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

feat(): Multi select target on events #10365

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

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

## [next]

- feat(): Multi select target on events [#10365](https://github.com/fabricjs/fabric.js/pull/10365)

## [6.5.3]

- fix(ColorMatrix): Restore correct alpha for JS colorMatrix filter [#10313](https://github.com/fabricjs/fabric.js/pull/10313)
Expand Down
16 changes: 9 additions & 7 deletions src/EventTypeDefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,18 @@ type CanvasModificationEvents = {
'object:modified': ModifiedEvent;
};

interface SimpleEventHandler<T extends Event = TPointerEvent>
extends TEvent<T> {
target?: FabricObject;
multiSelectTarget?: FabricObject;
subTargets: FabricObject[];
}

export interface TPointerEventInfo<E extends TPointerEvent = TPointerEvent>
extends TEvent<E> {
target?: FabricObject;
subTargets?: FabricObject[];
multiSelecttarget?: FabricObject;
subTargets: FabricObject[];
transform?: Transform | null;
/**
* @deprecated
Expand All @@ -173,12 +181,6 @@ export interface TPointerEventInfo<E extends TPointerEvent = TPointerEvent>
viewportPoint: Point;
}

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

interface InEvent {
previousTarget?: FabricObject;
}
Expand Down
26 changes: 17 additions & 9 deletions src/canvas/Canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,7 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
options: CanvasEvents[`mouse:${T}`] = {
e,
target,
multiSelectTarget: this._multiSelectTarget,
subTargets: targets,
...getEventPoints(this, e),
transform: this._currentTransform,
Expand Down Expand Up @@ -1099,7 +1100,11 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
* @private
*/
_resetTransformEventData() {
this._target = this._pointer = this._absolutePointer = undefined;
this._multiSelectTarget =
this._target =
this._pointer =
this._absolutePointer =
undefined;
}

/**
Expand All @@ -1110,7 +1115,8 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
_cacheTransformEventData(e: TPointerEvent) {
// reset in order to avoid stale caching
this._resetTransformEventData();
this._pointer = this.getViewportPoint(e);
const pointer = this.getViewportPoint(e);
this._pointer = pointer;
this._absolutePointer = sendPointToPlane(
this._pointer,
undefined,
Expand All @@ -1119,6 +1125,14 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
this._target = this._currentTransform
? this._currentTransform.target
: this.findTarget(e);
// in case we have a multi selection as a target, search additional targets
this._multiSelectTarget = isActiveSelection(this._target)
? // first search active objects for a target to remove
this.searchPossibleTargets(this._target.getObjects(), pointer) ||
// if not found, search under active selection for a target to add
// `prevActiveObjects` will be searched but we already know they will not be found
this.searchPossibleTargets(this._objects, pointer)
: undefined;
}

/**
Expand Down Expand Up @@ -1413,13 +1427,7 @@ export class Canvas extends SelectableCanvas implements CanvasOptions {
if (isAS) {
const prevActiveObjects = activeObject.getObjects();
if (target === activeObject) {
const pointer = this.getViewportPoint(e);
target =
// first search active objects for a target to remove
this.searchPossibleTargets(prevActiveObjects, pointer) ||
// if not found, search under active selection for a target to add
// `prevActiveObjects` will be searched but we already know they will not be found
this.searchPossibleTargets(this._objects, pointer);
target = this._multiSelectTarget;
// if nothing is found bail out
if (!target || !target.selectable) {
return false;
Expand Down
10 changes: 10 additions & 0 deletions src/canvas/SelectableCanvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,16 @@ export class SelectableCanvas<EventSpec extends CanvasEvents = CanvasEvents>
*/
protected declare _target?: FabricObject;

/**
* During a mouse event we may need the target multiple times in multiple functions.
* _multiSelectTarget holds a reference to the target that is inside or outside the multi selection
* and that may be the object added or removed from the multi selection.
* This reference is valid for the lifespan of the event.
* Every fabricJS mouse event create and delete the cache every time.
* @type {FabricObject}
*/
protected declare _multiSelectTarget?: FabricObject;

static ownDefaults = canvasDefaults;

static getDefaults(): Record<string, any> {
Expand Down
14 changes: 4 additions & 10 deletions src/shapes/Path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,27 +390,21 @@ export class Path<

/**
* Creates an instance of Path from an SVG <path> element
* @static
* @memberOf Path
* @param {HTMLElement} element to parse
* @param {Partial<PathProps>} [options] Options object
*/
static async fromElement(
element: HTMLElement,
options: Partial<PathProps>,
cssRules?: CSSRules,
) {
const { d, ...parsedAttributes } = parseAttributes(
): Promise<Path> {
const { left: _left, top: _top, ...otherOptions } = options;
const { d, left, top, ...parsedAttributes } = parseAttributes(
element,
this.ATTRIBUTE_NAMES,
cssRules,
);
return new this(d, {
...parsedAttributes,
...options,
// we pass undefined to instruct the constructor to position the object using the bbox
left: undefined,
top: undefined,
...otherOptions,
});
}
}
Expand Down
Loading