mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-18 18:46:19 +08:00
refactor: redesign element transform manager interface (#11679)
### Change - Rename `ElementTransformManager` -> `InteractivityManager` - Now you can `event.on` and `action.onXXX` method to extend interactivity behaviour. The old approach of overriding methods directly is deprecated.
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
export type SupportedEvents =
|
||||
| 'click'
|
||||
| 'dblclick'
|
||||
| 'pointerdown'
|
||||
| 'pointerenter'
|
||||
| 'pointerleave'
|
||||
| 'pointermove'
|
||||
| 'pointerup';
|
||||
@@ -0,0 +1,141 @@
|
||||
import { type Container, createIdentifier } from '@blocksuite/global/di';
|
||||
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
||||
import { Extension } from '@blocksuite/store';
|
||||
|
||||
import type { PointerEventState } from '../../../event/index.js';
|
||||
import type { GfxController } from '../../controller.js';
|
||||
import { GfxControllerIdentifier } from '../../identifiers.js';
|
||||
import type { SupportedEvents } from '../event.js';
|
||||
import type {
|
||||
DragExtensionInitializeContext,
|
||||
ExtensionDragEndContext,
|
||||
ExtensionDragMoveContext,
|
||||
ExtensionDragStartContext,
|
||||
} from '../types/drag.js';
|
||||
|
||||
export const InteractivityExtensionIdentifier =
|
||||
createIdentifier<InteractivityExtension>('interactivity-extension');
|
||||
|
||||
export class InteractivityExtension extends Extension {
|
||||
static key: string;
|
||||
|
||||
get std() {
|
||||
return this.gfx.std;
|
||||
}
|
||||
|
||||
event: Omit<InteractivityEventAPI, 'emit'> = new InteractivityEventAPI();
|
||||
|
||||
action: Omit<InteractivityActionAPI, 'emit'> = new InteractivityActionAPI();
|
||||
|
||||
constructor(protected readonly gfx: GfxController) {
|
||||
super();
|
||||
}
|
||||
|
||||
mounted() {}
|
||||
|
||||
/**
|
||||
* Override this method should call `super.unmounted()`
|
||||
*/
|
||||
unmounted() {
|
||||
this.event.destroy();
|
||||
this.action.destroy();
|
||||
}
|
||||
|
||||
static override setup(di: Container) {
|
||||
if (!this.key) {
|
||||
throw new BlockSuiteError(
|
||||
ErrorCode.ValueNotExists,
|
||||
'key is not defined in the InteractivityExtension'
|
||||
);
|
||||
}
|
||||
|
||||
di.add(
|
||||
this as unknown as { new (gfx: GfxController): InteractivityExtension },
|
||||
[GfxControllerIdentifier]
|
||||
);
|
||||
di.addImpl(InteractivityExtensionIdentifier(this.key), provider =>
|
||||
provider.get(this)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class InteractivityEventAPI {
|
||||
private readonly _handlersMap = new Map<
|
||||
SupportedEvents,
|
||||
((evt: PointerEventState) => void)[]
|
||||
>();
|
||||
|
||||
on(eventName: SupportedEvents, handler: (evt: PointerEventState) => void) {
|
||||
const handlers = this._handlersMap.get(eventName) ?? [];
|
||||
handlers.push(handler);
|
||||
this._handlersMap.set(eventName, handlers);
|
||||
|
||||
return () => {
|
||||
const idx = handlers.indexOf(handler);
|
||||
|
||||
if (idx > -1) {
|
||||
handlers.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
emit(eventName: SupportedEvents, evt: PointerEventState) {
|
||||
const handlers = this._handlersMap.get(eventName);
|
||||
if (!handlers) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const handler of handlers) {
|
||||
handler(evt);
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._handlersMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
type ActionContextMap = {
|
||||
dragInitialize: {
|
||||
context: DragExtensionInitializeContext;
|
||||
returnType: {
|
||||
onDragStart?: (context: ExtensionDragStartContext) => void;
|
||||
onDragMove?: (context: ExtensionDragMoveContext) => void;
|
||||
onDragEnd?: (context: ExtensionDragEndContext) => void;
|
||||
clear?: () => void;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export class InteractivityActionAPI {
|
||||
private readonly _handlers: Partial<{
|
||||
dragInitialize: Parameters<InteractivityActionAPI['onDragInitialize']>[0];
|
||||
}> = {};
|
||||
|
||||
onDragInitialize(
|
||||
handler: (
|
||||
ctx: ActionContextMap['dragInitialize']['context']
|
||||
) => ActionContextMap['dragInitialize']['returnType']
|
||||
) {
|
||||
this._handlers['dragInitialize'] = handler;
|
||||
|
||||
return () => {
|
||||
delete this._handlers['dragInitialize'];
|
||||
};
|
||||
}
|
||||
|
||||
emit<K extends keyof ActionContextMap>(
|
||||
event: K,
|
||||
context: ActionContextMap[K]['context']
|
||||
): ActionContextMap[K]['returnType'] | undefined {
|
||||
const handler = this._handlers[event];
|
||||
|
||||
return handler?.(context);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
for (const key in this._handlers) {
|
||||
delete this._handlers[key as keyof typeof this._handlers];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { Bound } from '@blocksuite/global/gfx';
|
||||
import last from 'lodash-es/last';
|
||||
|
||||
import type { PointerEventState } from '../../event';
|
||||
import type { GfxController } from '../controller.js';
|
||||
import type { GfxElementModelView } from '../view/view.js';
|
||||
|
||||
export class GfxViewEventManager {
|
||||
private _currentStackedElm: GfxElementModelView[] = [];
|
||||
|
||||
private _callInReverseOrder(
|
||||
callback: (view: GfxElementModelView) => void,
|
||||
arr = this._currentStackedElm
|
||||
) {
|
||||
for (let i = arr.length - 1; i >= 0; i--) {
|
||||
const view = arr[i];
|
||||
|
||||
callback(view);
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private readonly gfx: GfxController) {}
|
||||
|
||||
click(_evt: PointerEventState): void {
|
||||
last(this._currentStackedElm)?.dispatch('click', _evt);
|
||||
}
|
||||
|
||||
dblClick(_evt: PointerEventState): void {
|
||||
last(this._currentStackedElm)?.dispatch('dblclick', _evt);
|
||||
}
|
||||
|
||||
pointerDown(_evt: PointerEventState): void {
|
||||
last(this._currentStackedElm)?.dispatch('pointerdown', _evt);
|
||||
}
|
||||
|
||||
pointerMove(_evt: PointerEventState): void {
|
||||
const [x, y] = this.gfx.viewport.toModelCoord(_evt.x, _evt.y);
|
||||
const hoveredElmViews = this.gfx.grid
|
||||
.search(new Bound(x - 5, y - 5, 10, 10), {
|
||||
filter: ['canvas', 'local'],
|
||||
})
|
||||
.map(model => this.gfx.view.get(model)) as GfxElementModelView[];
|
||||
const currentStackedViews = new Set(this._currentStackedElm);
|
||||
const visited = new Set<GfxElementModelView>();
|
||||
|
||||
this._callInReverseOrder(view => {
|
||||
if (currentStackedViews.has(view)) {
|
||||
visited.add(view);
|
||||
view.dispatch('pointermove', _evt);
|
||||
} else {
|
||||
view.dispatch('pointerenter', _evt);
|
||||
}
|
||||
}, hoveredElmViews);
|
||||
this._callInReverseOrder(
|
||||
view => !visited.has(view) && view.dispatch('pointerleave', _evt)
|
||||
);
|
||||
this._currentStackedElm = hoveredElmViews;
|
||||
}
|
||||
|
||||
pointerUp(_evt: PointerEventState): void {
|
||||
last(this._currentStackedElm)?.dispatch('pointerup', _evt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export { InteractivityExtension } from './extension/base.js';
|
||||
export { GfxViewEventManager } from './gfx-view-event-handler.js';
|
||||
export { InteractivityIdentifier, InteractivityManager } from './manager.js';
|
||||
export type {
|
||||
DragExtensionInitializeContext,
|
||||
DragInitializationOption,
|
||||
ExtensionDragEndContext,
|
||||
ExtensionDragMoveContext,
|
||||
ExtensionDragStartContext,
|
||||
} from './types/drag.js';
|
||||
export type {
|
||||
DragEndContext,
|
||||
DragMoveContext,
|
||||
DragStartContext,
|
||||
GfxViewTransformInterface,
|
||||
SelectedContext,
|
||||
} from './types/view.js';
|
||||
@@ -0,0 +1,291 @@
|
||||
import { type ServiceIdentifier } from '@blocksuite/global/di';
|
||||
import { DisposableGroup } from '@blocksuite/global/disposable';
|
||||
import { Bound, Point } from '@blocksuite/global/gfx';
|
||||
|
||||
import type { PointerEventState } from '../../event/state/pointer.js';
|
||||
import { GfxExtension, GfxExtensionIdentifier } from '../extension.js';
|
||||
import type { GfxModel } from '../model/model.js';
|
||||
import type { SupportedEvents } from './event.js';
|
||||
import {
|
||||
type InteractivityActionAPI,
|
||||
type InteractivityEventAPI,
|
||||
InteractivityExtensionIdentifier,
|
||||
} from './extension/base.js';
|
||||
import { GfxViewEventManager } from './gfx-view-event-handler.js';
|
||||
import type {
|
||||
DragExtensionInitializeContext,
|
||||
DragInitializationOption,
|
||||
ExtensionDragEndContext,
|
||||
ExtensionDragMoveContext,
|
||||
ExtensionDragStartContext,
|
||||
} from './types/drag.js';
|
||||
|
||||
type ExtensionPointerHandler = Exclude<
|
||||
SupportedEvents,
|
||||
'pointerleave' | 'pointerenter'
|
||||
>;
|
||||
|
||||
export const InteractivityIdentifier = GfxExtensionIdentifier(
|
||||
'interactivity-manager'
|
||||
) as ServiceIdentifier<InteractivityManager>;
|
||||
|
||||
const CAMEL_CASE_MAP: {
|
||||
[key in ExtensionPointerHandler]: keyof GfxViewEventManager;
|
||||
} = {
|
||||
click: 'click',
|
||||
dblclick: 'dblClick',
|
||||
pointerdown: 'pointerDown',
|
||||
pointermove: 'pointerMove',
|
||||
pointerup: 'pointerUp',
|
||||
};
|
||||
|
||||
export class InteractivityManager extends GfxExtension {
|
||||
static override key = 'interactivity-manager';
|
||||
|
||||
private readonly _disposable = new DisposableGroup();
|
||||
|
||||
private canvasEventHandler = new GfxViewEventManager(this.gfx);
|
||||
|
||||
override mounted(): void {
|
||||
this.canvasEventHandler = new GfxViewEventManager(this.gfx);
|
||||
this.interactExtensions.forEach(ext => {
|
||||
ext.mounted();
|
||||
});
|
||||
}
|
||||
|
||||
override unmounted(): void {
|
||||
this._disposable.dispose();
|
||||
this.interactExtensions.forEach(ext => {
|
||||
ext.unmounted();
|
||||
});
|
||||
}
|
||||
|
||||
get interactExtensions() {
|
||||
return this.std.provider.getAll(InteractivityExtensionIdentifier);
|
||||
}
|
||||
|
||||
get keyboard() {
|
||||
return this.gfx.keyboard;
|
||||
}
|
||||
|
||||
private _safeExecute(fn: () => void, errorMessage: string) {
|
||||
try {
|
||||
fn();
|
||||
} catch (e) {
|
||||
console.error(errorMessage, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the event to canvas elements
|
||||
* @param eventName
|
||||
* @param evt
|
||||
*/
|
||||
dispatch(eventName: ExtensionPointerHandler, evt: PointerEventState) {
|
||||
const handlerName = CAMEL_CASE_MAP[eventName];
|
||||
|
||||
this.canvasEventHandler[handlerName](evt);
|
||||
|
||||
const extensions = this.interactExtensions;
|
||||
|
||||
extensions.forEach(ext => {
|
||||
(ext.event as InteractivityEventAPI).emit(eventName, evt);
|
||||
});
|
||||
}
|
||||
|
||||
dispatchOnSelected(evt: PointerEventState) {
|
||||
const { raw } = evt;
|
||||
const { gfx } = this;
|
||||
const [x, y] = gfx.viewport.toModelCoordFromClientCoord([raw.x, raw.y]);
|
||||
const picked = this.gfx.getElementInGroup(x, y);
|
||||
|
||||
const tryGetLockedAncestor = (e: GfxModel) => {
|
||||
if (e?.isLockedByAncestor()) {
|
||||
return e.groups.findLast(group => group.isLocked()) ?? e;
|
||||
}
|
||||
return e;
|
||||
};
|
||||
|
||||
if (picked) {
|
||||
const lockedElement = tryGetLockedAncestor(picked);
|
||||
const multiSelect = raw.shiftKey;
|
||||
const view = gfx.view.get(lockedElement);
|
||||
const context = {
|
||||
selected: multiSelect ? !gfx.selection.has(picked.id) : true,
|
||||
multiSelect,
|
||||
event: raw,
|
||||
position: Point.from([x, y]),
|
||||
fallback: lockedElement !== picked,
|
||||
};
|
||||
|
||||
const selected = view?.onSelected(context);
|
||||
return selected ?? true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
initializeDrag(options: DragInitializationOption) {
|
||||
let cancelledByExt = false;
|
||||
|
||||
const context: DragExtensionInitializeContext = {
|
||||
/**
|
||||
* The elements that are being dragged
|
||||
*/
|
||||
elements: options.movingElements,
|
||||
|
||||
preventDefault: () => {
|
||||
cancelledByExt = true;
|
||||
},
|
||||
|
||||
dragStartPos: Point.from(
|
||||
this.gfx.viewport.toModelCoordFromClientCoord([
|
||||
options.event.x,
|
||||
options.event.y,
|
||||
])
|
||||
),
|
||||
};
|
||||
const extension = this.interactExtensions;
|
||||
const activeExtensionHandlers = Array.from(
|
||||
extension.values().map(ext => {
|
||||
return (ext.action as InteractivityActionAPI).emit(
|
||||
'dragInitialize',
|
||||
context
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
if (cancelledByExt) {
|
||||
activeExtensionHandlers.forEach(handler => handler?.clear?.());
|
||||
return;
|
||||
}
|
||||
|
||||
const host = this.std.host;
|
||||
const { event } = options;
|
||||
const internal = {
|
||||
elements: context.elements.map(model => {
|
||||
return {
|
||||
view: this.gfx.view.get(model)!,
|
||||
originalBound: Bound.deserialize(model.xywh),
|
||||
model: model,
|
||||
};
|
||||
}),
|
||||
dragStartPos: Point.from(
|
||||
this.gfx.viewport.toModelCoordFromClientCoord([event.x, event.y])
|
||||
),
|
||||
};
|
||||
let dragLastPos = internal.dragStartPos;
|
||||
let lastEvent = event;
|
||||
|
||||
const viewportWatcher = this.gfx.viewport.viewportMoved.subscribe(() => {
|
||||
onDragMove(lastEvent as PointerEvent);
|
||||
});
|
||||
const onDragMove = (event: PointerEvent) => {
|
||||
dragLastPos = Point.from(
|
||||
this.gfx.viewport.toModelCoordFromClientCoord([event.x, event.y])
|
||||
);
|
||||
|
||||
const moveContext: ExtensionDragMoveContext = {
|
||||
...internal,
|
||||
event,
|
||||
dragLastPos,
|
||||
dx: dragLastPos.x - internal.dragStartPos.x,
|
||||
dy: dragLastPos.y - internal.dragStartPos.y,
|
||||
};
|
||||
|
||||
// If shift key is pressed, restrict the movement to one direction
|
||||
if (this.keyboard.shiftKey$.peek()) {
|
||||
const angle = Math.abs(Math.atan2(moveContext.dy, moveContext.dx));
|
||||
const direction =
|
||||
angle < Math.PI / 4 || angle > 3 * (Math.PI / 4) ? 'dy' : 'dx';
|
||||
|
||||
moveContext[direction] = 0;
|
||||
}
|
||||
|
||||
this._safeExecute(() => {
|
||||
activeExtensionHandlers.forEach(handler =>
|
||||
handler?.onDragMove?.(moveContext)
|
||||
);
|
||||
}, 'Error while executing extension `onDragMove`');
|
||||
|
||||
internal.elements.forEach(element => {
|
||||
const { view, originalBound } = element;
|
||||
|
||||
view.onDragMove({
|
||||
currentBound: originalBound,
|
||||
dx: moveContext.dx,
|
||||
dy: moveContext.dy,
|
||||
elements: internal.elements,
|
||||
});
|
||||
});
|
||||
};
|
||||
const onDragEnd = (event: PointerEvent) => {
|
||||
host.removeEventListener('pointermove', onDragMove, false);
|
||||
host.removeEventListener('pointerup', onDragEnd, false);
|
||||
viewportWatcher.unsubscribe();
|
||||
|
||||
dragLastPos = Point.from(
|
||||
this.gfx.viewport.toModelCoordFromClientCoord([event.x, event.y])
|
||||
);
|
||||
const endContext: ExtensionDragEndContext = {
|
||||
...internal,
|
||||
event,
|
||||
dragLastPos,
|
||||
dx: dragLastPos.x - internal.dragStartPos.x,
|
||||
dy: dragLastPos.y - internal.dragStartPos.y,
|
||||
};
|
||||
|
||||
this._safeExecute(() => {
|
||||
activeExtensionHandlers.forEach(handler =>
|
||||
handler?.onDragEnd?.(endContext)
|
||||
);
|
||||
}, 'Error while executing extension `onDragEnd` handler');
|
||||
|
||||
this.std.store.transact(() => {
|
||||
internal.elements.forEach(element => {
|
||||
const { view, originalBound } = element;
|
||||
|
||||
view.onDragEnd({
|
||||
currentBound: originalBound.moveDelta(endContext.dx, endContext.dy),
|
||||
dx: endContext.dx,
|
||||
dy: endContext.dy,
|
||||
elements: internal.elements,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this._safeExecute(() => {
|
||||
activeExtensionHandlers.forEach(handler => handler?.clear?.());
|
||||
}, 'Error while executing extension `clear` handler');
|
||||
|
||||
options.onDragEnd?.();
|
||||
};
|
||||
const listenEvent = () => {
|
||||
host.addEventListener('pointermove', onDragMove, false);
|
||||
host.addEventListener('pointerup', onDragEnd, false);
|
||||
};
|
||||
const dragStart = () => {
|
||||
internal.elements.forEach(({ view, originalBound }) => {
|
||||
view.onDragStart({
|
||||
currentBound: originalBound,
|
||||
elements: internal.elements,
|
||||
});
|
||||
});
|
||||
|
||||
const dragStartContext: ExtensionDragStartContext = {
|
||||
...internal,
|
||||
event: event as PointerEvent,
|
||||
dragLastPos,
|
||||
};
|
||||
|
||||
this._safeExecute(() => {
|
||||
activeExtensionHandlers.forEach(handler =>
|
||||
handler?.onDragStart?.(dragStartContext)
|
||||
);
|
||||
}, 'Error while executing extension `onDragStart` handler');
|
||||
};
|
||||
|
||||
listenEvent();
|
||||
dragStart();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import type { Bound } from '@blocksuite/global/gfx';
|
||||
|
||||
import type { GfxBlockComponent } from '../../../view';
|
||||
import type { GfxModel } from '../../model/model';
|
||||
import type { GfxElementModelView } from '../../view/view';
|
||||
|
||||
export type DragInitializationOption = {
|
||||
movingElements: GfxModel[];
|
||||
event: PointerEvent | MouseEvent;
|
||||
onDragEnd?: () => void;
|
||||
};
|
||||
|
||||
export type DragExtensionInitializeContext = {
|
||||
/**
|
||||
* The elements that are being dragged.
|
||||
* The extension can modify this array to add or remove dragging elements.
|
||||
*/
|
||||
elements: GfxModel[];
|
||||
|
||||
/**
|
||||
* Prevent the default drag behavior. The following drag events will not be triggered.
|
||||
*/
|
||||
preventDefault: () => void;
|
||||
|
||||
/**
|
||||
* The start position of the drag in model space.
|
||||
*/
|
||||
dragStartPos: Readonly<{
|
||||
x: number;
|
||||
y: number;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type ExtensionBaseEvent = {
|
||||
/**
|
||||
* The elements that respond to the event.
|
||||
*/
|
||||
elements: {
|
||||
view: GfxBlockComponent | GfxElementModelView;
|
||||
originalBound: Bound;
|
||||
model: GfxModel;
|
||||
}[];
|
||||
|
||||
/**
|
||||
* The mouse event
|
||||
*/
|
||||
event: PointerEvent;
|
||||
|
||||
/**
|
||||
* The start position of the drag in model space.
|
||||
*/
|
||||
dragStartPos: Readonly<{
|
||||
x: number;
|
||||
y: number;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* The last position of the drag in model space.
|
||||
*/
|
||||
dragLastPos: Readonly<{
|
||||
x: number;
|
||||
y: number;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type ExtensionDragStartContext = ExtensionBaseEvent;
|
||||
|
||||
export type ExtensionDragMoveContext = ExtensionBaseEvent & {
|
||||
dx: number;
|
||||
dy: number;
|
||||
};
|
||||
|
||||
export type ExtensionDragEndContext = ExtensionDragMoveContext;
|
||||
@@ -0,0 +1,77 @@
|
||||
import type { Bound, IPoint } from '@blocksuite/global/gfx';
|
||||
|
||||
import type { GfxBlockComponent } from '../../../view/element/gfx-block-component.js';
|
||||
import type { GfxModel } from '../../model/model.js';
|
||||
import type { GfxElementModelView } from '../../view/view.js';
|
||||
|
||||
export type DragStartContext = {
|
||||
/**
|
||||
* The elements that are being dragged
|
||||
*/
|
||||
elements: {
|
||||
view: GfxBlockComponent | GfxElementModelView;
|
||||
originalBound: Bound;
|
||||
model: GfxModel;
|
||||
}[];
|
||||
|
||||
/**
|
||||
* The bound of element when drag starts
|
||||
*/
|
||||
currentBound: Bound;
|
||||
};
|
||||
|
||||
export type DragMoveContext = DragStartContext & {
|
||||
/**
|
||||
* The delta x of current drag position compared to the start position in model coordinate.
|
||||
*/
|
||||
dx: number;
|
||||
|
||||
/**
|
||||
* The delta y of current drag position compared to the start position in model coordinate.
|
||||
*/
|
||||
dy: number;
|
||||
};
|
||||
|
||||
export type DragEndContext = DragMoveContext;
|
||||
|
||||
export type SelectedContext = {
|
||||
/**
|
||||
* The selected state of the element
|
||||
*/
|
||||
selected: boolean;
|
||||
|
||||
/**
|
||||
* Whether is multi-select, usually triggered by shift key
|
||||
*/
|
||||
multiSelect: boolean;
|
||||
|
||||
/**
|
||||
* The pointer event that triggers the selection
|
||||
*/
|
||||
event: PointerEvent;
|
||||
|
||||
/**
|
||||
* The model position of the event pointer
|
||||
*/
|
||||
position: IPoint;
|
||||
|
||||
/**
|
||||
* If the current selection is a fallback selection, like selecting the element inside a group, the group will be selected instead
|
||||
*/
|
||||
fallback: boolean;
|
||||
};
|
||||
|
||||
export type GfxViewTransformInterface = {
|
||||
onDragStart: (context: DragStartContext) => void;
|
||||
onDragMove: (context: DragMoveContext) => void;
|
||||
onDragEnd: (context: DragEndContext) => void;
|
||||
onRotate: (context: {}) => void;
|
||||
onResize: (context: {}) => void;
|
||||
|
||||
/**
|
||||
* When the element is selected by the pointer
|
||||
* @param context
|
||||
* @returns
|
||||
*/
|
||||
onSelected: (context: SelectedContext) => void;
|
||||
};
|
||||
Reference in New Issue
Block a user