diff --git a/src/app/core/models/engine.ts b/src/app/core/models/engine.ts index ec462d2..5bd8221 100644 --- a/src/app/core/models/engine.ts +++ b/src/app/core/models/engine.ts @@ -1,11 +1,12 @@ import { Workbench } from '@/app/core/models/workbench'; import { IEngineProps } from '@/app/shared/types'; import { Cursor } from '@/app/core/models/cursor'; -import { Screen } from '@/app/core/models/screen'; +import { Screen, ScreenType } from '@/app/core/models/screen'; import { uid } from '@/app/shared/uid'; import { TreeNode } from '@/app/core/models/tree-node'; +import { Event } from '@/app/shared/event'; -export class Engine { +export class Engine extends Event { id: string; props: IEngineProps; @@ -18,7 +19,12 @@ export class Engine { screen: Screen; - constructor() { + constructor(props: IEngineProps) { + super(props); + this.props = { + ...Engine.defaultProps, + ...props + }; this.init(); this.id = uid(); } @@ -41,4 +47,23 @@ export class Engine { }); return results; } + + static defaultProps: IEngineProps = { + shortcuts: [], + effects: [], + drivers: [], + rootComponentName: 'Root', + sourceIdAttrName: 'data-designer-source-id', + nodeIdAttrName: 'data-designer-node-id', + contentEditableAttrName: 'data-content-editable', + contentEditableNodeIdAttrName: 'data-content-editable-node-id', + clickStopPropagationAttrName: 'data-click-stop-propagation', + nodeSelectionIdAttrName: 'data-designer-node-helpers-id', + nodeDragHandlerAttrName: 'data-designer-node-drag-handler', + screenResizeHandlerAttrName: 'data-designer-screen-resize-handler', + nodeResizeHandlerAttrName: 'data-designer-node-resize-handler', + outlineNodeIdAttrName: 'data-designer-outline-node-id', + nodeTranslateAttrName: 'data-designer-node-translate-handler', + defaultScreenType: ScreenType.PC + }; } diff --git a/src/app/core/models/move-helper.ts b/src/app/core/models/move-helper.ts index 1db2c86..0f1e735 100644 --- a/src/app/core/models/move-helper.ts +++ b/src/app/core/models/move-helper.ts @@ -2,6 +2,8 @@ import { Operation } from './operation'; import { TreeNode } from './tree-node'; import { Viewport } from './viewport'; import { IPoint, Rect } from '../../shared/coordinate'; +import { CursorDragType } from '@/app/core/models/cursor'; +import { DragNodeEvent } from '@/app/core/events'; export enum ClosestPosition { Before = 'BEFORE', @@ -63,4 +65,54 @@ export class MoveHelper { outlineClosestDirection: ClosestPosition = null; dragging = false; + + constructor(props: IMoveHelperProps) { + this.operation = props.operation; + this.rootNode = this.operation.tree; + } + + get cursor() { + return this.operation.engine.cursor; + } + + get viewport() { + return this.operation.workspace.viewport; + } + + get outline() { + return this.operation.workspace.outline; + } + + get hasDragNodes() { + return this.dragNodes.length > 0; + } + + get closestDirection() { + if (this.activeViewport === this.outline) { + return this.outlineClosestDirection; + } + return this.viewportClosestDirection; + } + + dragStart(props: IMoveHelperDragStartProps) { + const nodes = TreeNode.filterDraggable(props?.dragNodes); + if (nodes.length) { + this.dragNodes = nodes; + this.trigger( + new DragNodeEvent({ + target: this.operation.tree, + source: this.dragNodes + }) + ); + this.viewport.cacheElements(); + this.cursor.setDragType(CursorDragType.Move); + this.dragging = true; + } + } + + trigger(event: any) { + if (this.operation) { + return this.operation.dispatch(event); + } + } } diff --git a/src/app/core/models/operation.ts b/src/app/core/models/operation.ts index 635fa58..062657f 100644 --- a/src/app/core/models/operation.ts +++ b/src/app/core/models/operation.ts @@ -5,6 +5,8 @@ import { Selection } from './selection'; import { Hover } from './hover'; import { TransformHelper } from './transform-helper'; import { MoveHelper } from './move-helper'; +import { ICustomEvent } from '@/app/shared/event'; +import { isFn } from '@/app/shared/types'; export interface IOperation { tree?: ITreeNode; @@ -29,4 +31,9 @@ export class Operation { requests = { snapshot: null }; + + dispatch(event: ICustomEvent, callback?: () => void) { + if (this.workspace.dispatch(event) === false) return; + if (isFn(callback)) return callback(); + } } diff --git a/src/app/core/models/viewport.ts b/src/app/core/models/viewport.ts index 8184011..2cde9f3 100644 --- a/src/app/core/models/viewport.ts +++ b/src/app/core/models/viewport.ts @@ -1,5 +1,6 @@ import { Workspace } from './workspace'; import { Engine } from './engine'; +import { globalThisPolyfill } from '@/app/shared/globalThisPolyfill'; export interface IViewportProps { engine: Engine; @@ -50,4 +51,25 @@ export class Viewport { moveInsertionType: IViewportMoveInsertionType; nodeElementsStore: Record = {}; + + cacheElements() { + this.nodeElementsStore = {}; + this.viewportRoot?.querySelectorAll(`*[${this.nodeIdAttrName}]`).forEach((element: HTMLElement) => { + const id = element.getAttribute(this.nodeIdAttrName); + this.nodeElementsStore[id] = this.nodeElementsStore[id] || []; + this.nodeElementsStore[id].push(element); + }); + } + + get viewportRoot() { + return this.isIframe ? this.contentWindow?.document?.body : this.viewportElement; + } + + get isMaster() { + return this.contentWindow === globalThisPolyfill; + } + + get isIframe() { + return !!this.contentWindow?.frameElement && !this.isMaster; + } } diff --git a/src/app/core/models/workspace.ts b/src/app/core/models/workspace.ts index 4377f66..6b1d7b3 100644 --- a/src/app/core/models/workspace.ts +++ b/src/app/core/models/workspace.ts @@ -2,6 +2,8 @@ import { Engine } from '@/app/core/models/engine'; import { Viewport } from '@/app/core/models/viewport'; import { Operation } from '@/app/core/models/operation'; import { History } from '@/app/core/models/history'; +import { ICustomEvent } from '@/app/shared/event'; +import { IEngineContext } from '@/app/core/types'; export interface IWorkspace { id?: string; @@ -39,4 +41,17 @@ export class Workspace { history: History; props: IWorkspaceProps; + + getEventContext(): IEngineContext { + return { + workbench: this.engine.workbench, + workspace: this, + engine: this.engine, + viewport: this.viewport + }; + } + + dispatch(event: ICustomEvent) { + return this.engine.dispatch(event, this.getEventContext()); + } }