Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Observable types #8431

Merged
merged 35 commits into from
Nov 26, 2022
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7dd2dec
Update observable.mixin.ts
ShaMan123 Nov 9, 2022
853cdb4
types
ShaMan123 Nov 9, 2022
cabf258
genric observale
ShaMan123 Nov 9, 2022
9af645e
d
ShaMan123 Nov 9, 2022
a77e0fa
fix event data
ShaMan123 Nov 9, 2022
a80fa3d
Update observable.mixin.ts
ShaMan123 Nov 9, 2022
abe7c5b
Update __types__.ts
ShaMan123 Nov 9, 2022
a10df06
fixes
ShaMan123 Nov 9, 2022
ce34790
fix test
ShaMan123 Nov 9, 2022
8b58223
Update typedefs.ts
ShaMan123 Nov 9, 2022
ef73d35
Update CHANGELOG.md
ShaMan123 Nov 9, 2022
586350c
mv + generify
ShaMan123 Nov 9, 2022
f627e24
Update EventTypeDefs.ts
ShaMan123 Nov 9, 2022
2159c8b
extract to files
ShaMan123 Nov 9, 2022
1fc674a
cleanup
ShaMan123 Nov 9, 2022
8602748
rename
ShaMan123 Nov 9, 2022
356d1a4
rename
ShaMan123 Nov 9, 2022
e7a4ddc
mv event types to `EventTypeDefs`
ShaMan123 Nov 9, 2022
8dc94f1
Update rotate.ts
ShaMan123 Nov 9, 2022
6f2b877
rename
ShaMan123 Nov 9, 2022
4b2c1c6
minors
ShaMan123 Nov 9, 2022
ce006dc
merge master
asturur Nov 19, 2022
2f4c681
Merge branch 'master' into observable-types
ShaMan123 Nov 20, 2022
63c8ef0
text editing events
ShaMan123 Nov 20, 2022
5975caf
fix imports
ShaMan123 Nov 20, 2022
6509e20
optional unknown events
ShaMan123 Nov 20, 2022
c83c7c6
Merge branch 'master' into observable-types
ShaMan123 Nov 20, 2022
d3ff302
IText event spec
ShaMan123 Nov 20, 2022
6e6cdab
Merge branch 'master' into observable-types
asturur Nov 26, 2022
c1a0615
Update group.class.ts
ShaMan123 Nov 26, 2022
15e09bd
fix conflicts
ShaMan123 Nov 26, 2022
fca0374
Update static_canvas.class.ts
ShaMan123 Nov 26, 2022
152925d
Update EventTypeDefs.ts
ShaMan123 Nov 26, 2022
683188c
Revert "Update static_canvas.class.ts"
ShaMan123 Nov 26, 2022
c5ddb70
types for object selected/deselected/
asturur Nov 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [next]

- chore(TS): Observable types [#8431](https://github.com/fabricjs/fabric.js/pull/8431)
- chore(TS): migrate Rect [#8411](https://github.com/fabricjs/fabric.js/pull/8411)
- chore(TS): migrate Ellipse [#8408](https://github.com/fabricjs/fabric.js/pull/8408)
- chore(TS): migrate Triangle to TS [#8410](https://github.com/fabricjs/fabric.js/pull/8410)
Expand Down
222 changes: 222 additions & 0 deletions src/EventTypeDefs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
import type { Control } from './controls/control.class';
import type { Point } from './point.class';
import type { FabricObject } from './shapes/fabricObject.class';
import type { Group } from './shapes/group.class';
import type { TOriginX, TOriginY, TRadian } from './typedefs';
import type { saveObjectTransform } from './util/misc/objectTransforms';
import type { Canvas } from './__types__';

export type ModifierKey = 'altKey' | 'shiftKey' | 'ctrlKey';

export type TPointerEvent = MouseEvent | TouchEvent;

export type TransformAction<T extends Transform = Transform, R = void> = (
eventData: TPointerEvent,
transform: T,
x: number,
y: number
) => R;

export type TransformActionHandler<T extends Transform = Transform> =
TransformAction<T, boolean>;

export type ControlCallback<R = void> = (
eventData: TPointerEvent,
control: Control,
fabricObject: FabricObject
) => R;

export type ControlCursorCallback = ControlCallback<string>;

/**
* relative to target's containing coordinate plane
* both agree on every point
*/
export type Transform = {
target: FabricObject;
action: string;
actionHandler: TransformActionHandler;
corner: string;
scaleX: number;
scaleY: number;
skewX: number;
skewY: number;
offsetX: number;
offsetY: number;
originX: TOriginX;
originY: TOriginY;
ex: number;
ey: number;
lastX: number;
lastY: number;
theta: TRadian;
width: number;
height: number;
shiftKey: boolean;
altKey: boolean;
original: ReturnType<typeof saveObjectTransform>;
};

export type TEvent<E extends Event = TPointerEvent> = {
e: E;
};

export type BasicTransformEvent<E extends Event = TPointerEvent> = TEvent<E> & {
transform: Transform;
pointer: Point;
};

export type TModificationEvents =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to here are types from typedefs

| 'moving'
| 'scaling'
| 'rotating'
| 'skewing'
| 'resizing';

type ObjectModifiedEvents = Record<TModificationEvents, BasicTransformEvent> & {
modified: BasicTransformEvent | never;
};

type CanvasModifiedEvents = Record<
`object:${keyof ObjectModifiedEvents}`,
BasicTransformEvent & { target: FabricObject }
>;

export type TransformEvent<T extends Event = TPointerEvent> =
BasicTransformEvent<T> & {
target: FabricObject;
subTargets: FabricObject[];
button: number;
isClick: boolean;
pointer: Point;
absolutePointer: Point;
};

type SimpleEventHandler<T extends Event = TPointerEvent> = TEvent<T> & {
target: FabricObject;
subTargets: FabricObject[];
};

type InEvent = {
previousTarget?: FabricObject;
};

type OutEvent = {
nextTarget?: FabricObject;
};

type DragEventData = TEvent<DragEvent> & {
target: FabricObject;
subTargets?: FabricObject[];
dragSource?: FabricObject;
canDrop?: boolean;
dropTarget?: FabricObject;
};

type DropEventData = DragEventData & { pointer: Point };

type DnDEvents = {
dragstart: TEvent<DragEvent> & { target: FabricObject };
drag: DragEventData;
dragover: DragEventData;
dragenter: DragEventData & InEvent;
dragleave: DragEventData & OutEvent;
dragend: DragEventData;
'drop:before': DropEventData;
drop: DropEventData;
'drop:after': DropEventData;
};

type CanvasDnDEvents = DnDEvents & {
'drag:enter': DragEventData & InEvent;
'drag:leave': DragEventData & OutEvent;
};

type CanvasSelectionEvents = {
'selection:created': TEvent & {
selected: FabricObject[];
};
'selection:updated': TEvent & {
selected: FabricObject[];
deselected: FabricObject[];
};
'before:selection:cleared': Partial<TEvent> & {
deselected: FabricObject[];
};
'selection:cleared': Partial<TEvent> & {
deselected: FabricObject[];
};
};

type BeforeSuffix<T extends string> = `${T}:before`;
type WithBeforeSuffix<T extends string> = T | BeforeSuffix<T>;

type TPointerEvents<Prefix extends string, E = Record<string, never>> = Record<
`${Prefix}${
| WithBeforeSuffix<'down'>
| WithBeforeSuffix<'move'>
| WithBeforeSuffix<'up'>
| 'dblclick'}`,
TransformEvent & E
> &
Record<`${Prefix}wheel`, TransformEvent<WheelEvent> & E> &
Record<`${Prefix}over`, TransformEvent & InEvent & E> &
Record<`${Prefix}out`, TransformEvent & OutEvent & E>;

export type ObjectPointerEvents = TPointerEvents<'mouse'>;
export type CanvasPointerEvents = TPointerEvents<'mouse:'>;

export type ObjectEvents = ObjectPointerEvents &
DnDEvents &
ObjectModifiedEvents & {
// selection
selected: never;
deselected: never;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to me looks like that selected and deselected do get target and optionally 'e' ( if they happen by mouse )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recall that the object is fired without context
But need to double check

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As opposed to the events fired on canvas

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified and added e + target.


// tree
added: { target: Group | Canvas };
removed: { target: Group | Canvas };

// erasing
'erasing:end': { path: FabricObject };
};

export type CanvasEvents = CanvasPointerEvents &
CanvasDnDEvents &
CanvasModifiedEvents &
CanvasSelectionEvents & {
// tree
'object:added': { target: FabricObject };
'object:removed': { target: FabricObject };
'canvas:cleared': never;

// rendering
'before:render': { ctx: CanvasRenderingContext2D };
'after:render': { ctx: CanvasRenderingContext2D };

// brushes
'before:path:created': { path: FabricObject };
'path:created': { path: FabricObject };

// erasing
'erasing:start': never;
'erasing:end':
| never
| {
path: FabricObject;
targets: FabricObject[];
subTargets: FabricObject[];
drawables: {
backgroundImage?: FabricObject;
overlayImage?: FabricObject;
};
};

// IText
'text:selection:changed': { target: FabricObject };
'text:changed': { target: FabricObject };

// misc
'contextmenu:before': SimpleEventHandler<Event>;
contextmenu: SimpleEventHandler<Event>;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is useful, yes, but needs to be a suggestion and not a lockdown.
The developer needs to be able to do

rect.fire('whateveriwant', ({ ...myStuff }) => { })

I didn't try the branch, is this possible or locked?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TS will complain.
I can look into it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that if we accept string in addition to the event names TS won't show autocomplete.

5 changes: 3 additions & 2 deletions src/__types__.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CanvasEvents, ModifierKey } from './EventTypeDefs';
import type { Observable } from './mixins/observable.mixin';
import type { Point } from './point.class';
import { ModifierKey, TMat2D } from './typedefs';
import { TMat2D } from './typedefs';

/**
* @todo remove transient
Expand All @@ -19,6 +20,6 @@ export type StaticCanvas = Record<string, any> & {
br: Point;
};
getRetinaScaling(): number;
} & Observable;
} & Observable<CanvasEvents>;
export type Rect = any;
export type TObject = any;
3 changes: 2 additions & 1 deletion src/brushes/pencil_brush.class.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { fabric } from '../../HEADER';
import { ModifierKey, TEvent } from '../EventTypeDefs';
import { Point } from '../point.class';
import { TEvent, ModifierKey, PathData } from '../typedefs';
import { PathData } from '../typedefs';
import { getSmoothPathFromPoints, joinPath } from '../util/path';
import { Canvas } from '../__types__';
import { BaseBrush } from './base_brush.class';
Expand Down
17 changes: 11 additions & 6 deletions src/canvas.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { dragHandler, getActionFromCorner } from './controls/actions';
import { Point } from './point.class';
import { FabricObject } from './shapes/fabricObject.class';
import { Transform } from './typedefs';
import { Transform } from './EventTypeDefs';
import { saveObjectTransform } from './util/misc/objectTransforms';

(function (global) {
Expand Down Expand Up @@ -441,10 +441,12 @@ import { saveObjectTransform } from './util/misc/objectTransforms';
this._objectsToRender = undefined;
// removing active object should fire "selection:cleared" events
if (obj === this._activeObject) {
this.fire('before:selection:cleared', { target: obj });
this.fire('before:selection:cleared', { deselected: [obj] });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here i m thorn between deselected and maybeDeselected or something like that.
Typescript allow for self descovery of data structure, that means that we can care a bit less about consistency ( all events have target! ) and have names that make sense.
In this case deselected is past, while the before:selection:cleared can be interrupted and is not deselected yet.

Ideas?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get your point. A good point indeed

Copy link
Contributor Author

@ShaMan123 ShaMan123 Nov 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MaybeDeselcted is bad naming. Target or deselectionTarget are better

this._discardActiveObject();
this.fire('selection:cleared', { target: obj });
obj.fire('deselected');
this.fire('selection:cleared', { deselected: [obj] });
obj.fire('deselected', {
target: obj,
});
}
if (obj === this._hoveredTarget) {
this._hoveredTarget = null;
Expand Down Expand Up @@ -548,7 +550,7 @@ import { saveObjectTransform } from './util/misc/objectTransforms';
var ctx = this.contextTop;
this.clearContext(ctx);
this.renderTopLayer(ctx);
this.fire('after:render');
this.fire('after:render', { ctx });
return this;
},

Expand Down Expand Up @@ -1363,7 +1365,10 @@ import { saveObjectTransform } from './util/misc/objectTransforms';
var currentActives = this.getActiveObjects(),
activeObject = this.getActiveObject();
if (currentActives.length) {
this.fire('before:selection:cleared', { target: activeObject, e: e });
this.fire('before:selection:cleared', {
e,
deselected: [activeObject],
});
}
this._discardActiveObject(e);
this._fireSelectionEvents(currentActives, e);
Expand Down
2 changes: 1 addition & 1 deletion src/controls/changeWidth.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TransformActionHandler } from '../typedefs';
import { TransformActionHandler } from '../EventTypeDefs';
import { getLocalPoint, isTransformCentered } from './util';
import { wrapWithFireEvent } from './wrapWithFireEvent';
import { wrapWithFixedAnchor } from './wrapWithFixedAnchor';
Expand Down
9 changes: 4 additions & 5 deletions src/controls/control.class.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { fabric } from '../../HEADER';
import { halfPI } from '../constants';
import { Point } from '../point.class';
import type { FabricObject } from '../shapes/object.class';
import {
TDegree,
TMat2D,
TPointerEvent,
TransformAction,
TransformActionHandler,
} from '../typedefs';
} from '../EventTypeDefs';
import { Point } from '../point.class';
import type { FabricObject } from '../shapes/object.class';
import { TDegree, TMat2D } from '../typedefs';
import { cos } from '../util/misc/cos';
import { degreesToRadians } from '../util/misc/radiansDegreesConversion';
import { sin } from '../util/misc/sin';
Expand Down
2 changes: 1 addition & 1 deletion src/controls/drag.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TransformActionHandler } from '../typedefs';
import { TransformActionHandler } from '../EventTypeDefs';
import { fireEvent } from '../util/fireEvent';
import { commonEventInfo, isLocked } from './util';

Expand Down
7 changes: 4 additions & 3 deletions src/controls/rotate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @ts-nocheck

import { ControlCursorCallback, TransformActionHandler } from '../typedefs';
import {
ControlCursorCallback,
TransformActionHandler,
} from '../EventTypeDefs';
import { radiansToDegrees } from '../util/misc/radiansDegreesConversion';
import { isLocked, NOT_ALLOWED_CURSOR } from './util';
import { wrapWithFireEvent } from './wrapWithFireEvent';
Expand Down
6 changes: 3 additions & 3 deletions src/controls/scale.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { FabricObject } from '../shapes/fabricObject.class';
import {
ControlCursorCallback,
TAxis,
TPointerEvent,
Transform,
TransformActionHandler,
} from '../typedefs';
} from '../EventTypeDefs';
import type { FabricObject } from '../shapes/fabricObject.class';
import { TAxis } from '../typedefs';
import { Canvas } from '../__types__';
import {
findCornerQuadrant,
Expand Down
6 changes: 3 additions & 3 deletions src/controls/scaleSkew.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { FabricObject } from '../shapes/object.class';
import {
ControlCallback,
ControlCursorCallback,
TAxisKey,
TPointerEvent,
TransformActionHandler,
} from '../typedefs';
} from '../EventTypeDefs';
import type { FabricObject } from '../shapes/object.class';
import { TAxisKey } from '../typedefs';
import { Canvas } from '../__types__';
import { scaleCursorStyleHandler, scalingX, scalingY } from './scale';
import { skewCursorStyleHandler, skewHandlerX, skewHandlerY } from './skew';
Expand Down
9 changes: 4 additions & 5 deletions src/controls/skew.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { resolveOrigin } from '../mixins/object_origin.mixin';
import { Point } from '../point.class';
import {
ControlCursorCallback,
TAxis,
TAxisKey,
TPointerEvent,
Transform,
TransformActionHandler,
} from '../typedefs';
} from '../EventTypeDefs';
import { resolveOrigin } from '../mixins/object_origin.mixin';
import { Point } from '../point.class';
import { TAxis, TAxisKey } from '../typedefs';
import {
degreesToRadians,
radiansToDegrees,
Expand Down