diff --git a/packages/designer/src/builtins/simulator/host/create-simulator.ts b/packages/designer/src/builtins/simulator/host/create-simulator.ts index c1959faed..e3412afd4 100644 --- a/packages/designer/src/builtins/simulator/host/create-simulator.ts +++ b/packages/designer/src/builtins/simulator/host/create-simulator.ts @@ -1,7 +1,8 @@ // NOTE: 仅用作类型标注,切勿作为实体使用 import { SimulatorRenderer } from '../renderer/renderer'; import { SimulatorHost } from './host'; -import { AssetLevel, AssetLevels, AssetList, isAssetBundle, isAssetItem, isCSSUrl, AssetType, assetItem } from '../utils/asset'; +import { AssetLevel, AssetLevels, AssetList, isAssetBundle, isAssetItem, AssetType, assetItem } from '../utils/asset'; +import { isCSSUrl } from '../../../utils/is-css-url'; export function createSimulator(host: SimulatorHost, iframe: HTMLIFrameElement, vendors: AssetList = []): Promise { const win: any = iframe.contentWindow; diff --git a/packages/designer/src/builtins/simulator/host/host.ts b/packages/designer/src/builtins/simulator/host/host.ts index 56a571eab..b66dc85ff 100644 --- a/packages/designer/src/builtins/simulator/host/host.ts +++ b/packages/designer/src/builtins/simulator/host/host.ts @@ -12,7 +12,7 @@ import { LocationData } from '../../../designer/helper/location'; import { NodeData } from '../../../designer/schema'; import { ComponentDescriptionSpec } from '../../../designer/component-config'; import { ReactInstance } from 'react'; -import { setNativeSelection } from '../../../utils/navtive-selection'; +import { setNativeSelection } from '../../../designer/helper/navtive-selection'; import cursor from '../../../designer/helper/cursor'; export interface SimulatorProps { diff --git a/packages/designer/src/builtins/simulator/renderer/renderer.ts b/packages/designer/src/builtins/simulator/renderer/renderer.ts index 22380d8d2..a9328551e 100644 --- a/packages/designer/src/builtins/simulator/renderer/renderer.ts +++ b/packages/designer/src/builtins/simulator/renderer/renderer.ts @@ -4,66 +4,14 @@ import { host } from './host'; import SimulatorRendererView from './renderer-view'; import { computed, obx } from '@recore/obx'; import { RootSchema, NpmInfo } from '../../../designer/schema'; -import { isElement, getClientRects } from '../../../utils/dom'; +import { getClientRects } from '../../../utils/get-client-rects'; import { Asset } from '../utils/asset'; import loader from '../utils/loader'; import { ComponentDescriptionSpec } from '../../../designer/component-config'; -import { findDOMNodes } from '../utils/react'; +import { reactFindDOMNodes } from '../utils/react-find-dom-nodes'; import { isESModule } from '../../../utils/is-es-module'; import { NodeInstance } from '../../../designer/simulator'; - -let REACT_KEY = ''; -function cacheReactKey(el: Element): Element { - if (REACT_KEY !== '') { - return el; - } - REACT_KEY = Object.keys(el).find(key => key.startsWith('__reactInternalInstance$')) || ''; - if (!REACT_KEY && (el as HTMLElement).parentElement) { - return cacheReactKey((el as HTMLElement).parentElement!); - } - return el; -} - -const SYMBOL_VNID = Symbol('_LCNodeId'); - -function getClosestNodeInstance(element: Element): NodeInstance | null { - let el: any = element; - if (el) { - el = cacheReactKey(el); - } - while (el) { - if (SYMBOL_VNID in el) { - return { - nodeId: el[SYMBOL_VNID], - instance: el, - }; - } - // get fiberNode from element - if (el[REACT_KEY]) { - return getNodeInstance(el[REACT_KEY]); - } - el = el.parentElement; - } - return null; -} - -function getNodeInstance(fiberNode: any): NodeInstance | null { - const instance = fiberNode.stateNode; - if (instance && SYMBOL_VNID in instance) { - return { - nodeId: instance[SYMBOL_VNID], - instance, - }; - } - return getNodeInstance(fiberNode.return); -} - -function checkInstanceMounted(instance: any): boolean { - if (isElement(instance)) { - return instance.parentElement != null; - } - return true; -} +import { isElement } from '../../../utils/is-element'; export class SimulatorRenderer { readonly isSimulatorRenderer = true; @@ -205,7 +153,7 @@ export class SimulatorRenderer { } findDOMNodes(instance: ReactInstance): Array | null { - return findDOMNodes(instance); + return reactFindDOMNodes(instance); } getClientRects(element: Element | Text) { @@ -297,4 +245,58 @@ function buildComponents(componentsMap: { [componentName: string]: ComponentDesc return components; } + +let REACT_KEY = ''; +function cacheReactKey(el: Element): Element { + if (REACT_KEY !== '') { + return el; + } + REACT_KEY = Object.keys(el).find(key => key.startsWith('__reactInternalInstance$')) || ''; + if (!REACT_KEY && (el as HTMLElement).parentElement) { + return cacheReactKey((el as HTMLElement).parentElement!); + } + return el; +} + +const SYMBOL_VNID = Symbol('_LCNodeId'); + +function getClosestNodeInstance(element: Element): NodeInstance | null { + let el: any = element; + if (el) { + el = cacheReactKey(el); + } + while (el) { + if (SYMBOL_VNID in el) { + return { + nodeId: el[SYMBOL_VNID], + instance: el, + }; + } + // get fiberNode from element + if (el[REACT_KEY]) { + return getNodeInstance(el[REACT_KEY]); + } + el = el.parentElement; + } + return null; +} + +function getNodeInstance(fiberNode: any): NodeInstance | null { + const instance = fiberNode.stateNode; + if (instance && SYMBOL_VNID in instance) { + return { + nodeId: instance[SYMBOL_VNID], + instance, + }; + } + return getNodeInstance(fiberNode.return); +} + +function checkInstanceMounted(instance: any): boolean { + if (isElement(instance)) { + return instance.parentElement != null; + } + return true; +} + export default new SimulatorRenderer(); diff --git a/packages/designer/src/builtins/simulator/utils/asset.ts b/packages/designer/src/builtins/simulator/utils/asset.ts index d419f66ac..36082de9e 100644 --- a/packages/designer/src/builtins/simulator/utils/asset.ts +++ b/packages/designer/src/builtins/simulator/utils/asset.ts @@ -57,10 +57,6 @@ export function isAssetBundle(obj: any): obj is AssetBundle { return obj && obj.type === AssetType.Bundle; } -export function isCSSUrl(url: string): boolean { - return /\.css$/.test(url); -} - export function assetBundle(assets?: Asset | AssetList | null, level?: AssetLevel): AssetBundle | null { if (!assets) { return null; diff --git a/packages/designer/src/utils/create-defer.ts b/packages/designer/src/builtins/simulator/utils/create-defer.ts similarity index 100% rename from packages/designer/src/utils/create-defer.ts rename to packages/designer/src/builtins/simulator/utils/create-defer.ts diff --git a/packages/designer/src/builtins/simulator/utils/loader.ts b/packages/designer/src/builtins/simulator/utils/loader.ts index da321c26b..58fd31d82 100644 --- a/packages/designer/src/builtins/simulator/utils/loader.ts +++ b/packages/designer/src/builtins/simulator/utils/loader.ts @@ -1,6 +1,7 @@ import { load, evaluate } from './script'; import StylePoint from './style'; -import { Asset, AssetLevel, AssetLevels, AssetType, AssetList, isAssetBundle, isAssetItem, assetItem, isCSSUrl, AssetItem } from './asset'; +import { Asset, AssetLevel, AssetLevels, AssetType, AssetList, isAssetBundle, isAssetItem, assetItem, AssetItem } from './asset'; +import { isCSSUrl } from '../../../utils/is-css-url'; function parseAssetList(scripts: any, styles: any, assets: AssetList, level?: AssetLevel) { for (let asset of assets) { diff --git a/packages/designer/src/builtins/simulator/utils/react.ts b/packages/designer/src/builtins/simulator/utils/react-find-dom-nodes.ts similarity index 78% rename from packages/designer/src/builtins/simulator/utils/react.ts rename to packages/designer/src/builtins/simulator/utils/react-find-dom-nodes.ts index 3623c8e0d..f2bcf9664 100644 --- a/packages/designer/src/builtins/simulator/utils/react.ts +++ b/packages/designer/src/builtins/simulator/utils/react-find-dom-nodes.ts @@ -1,5 +1,6 @@ import { ReactInstance } from 'react'; -import { isDOMNode, isElement } from '../../../utils/dom'; +import { isElement } from '../../../utils/is-element'; +import { isDOMNode } from '../../../utils/is-dom-node'; const FIBER_KEY = '_reactInternalFiber'; @@ -18,7 +19,7 @@ function elementsFromFiber(fiber: any, elements: Array) { } } -export function findDOMNodes(elem: ReactInstance | null): Array | null { +export function reactFindDOMNodes(elem: ReactInstance | null): Array | null { if (!elem) { return null; } diff --git a/packages/designer/src/builtins/simulator/utils/script.ts b/packages/designer/src/builtins/simulator/utils/script.ts index 97e1001aa..81841ff6d 100644 --- a/packages/designer/src/builtins/simulator/utils/script.ts +++ b/packages/designer/src/builtins/simulator/utils/script.ts @@ -1,4 +1,4 @@ -import { createDefer } from '../../../utils/create-defer'; +import { createDefer } from './create-defer'; export function evaluate(script: string) { const scriptEl = document.createElement('script'); diff --git a/packages/designer/src/builtins/simulator/utils/style.ts b/packages/designer/src/builtins/simulator/utils/style.ts index cd29c793a..74f4eddd4 100644 --- a/packages/designer/src/builtins/simulator/utils/style.ts +++ b/packages/designer/src/builtins/simulator/utils/style.ts @@ -1,4 +1,4 @@ -import { createDefer } from '../../../utils/create-defer'; +import { createDefer } from './create-defer'; export default class StylePoint { private lastContent: string | undefined; diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts index 31656d56d..9fcc34bf6 100644 --- a/packages/designer/src/designer/designer.ts +++ b/packages/designer/src/designer/designer.ts @@ -56,6 +56,7 @@ export default class Designer { }); this.dragon.onDrag(e => { + console.info(e); if (this.props?.onDrag) { this.props.onDrag(e); } diff --git a/packages/designer/src/designer/document/document-model.ts b/packages/designer/src/designer/document/document-model.ts index 7e2076d17..72319414a 100644 --- a/packages/designer/src/designer/document/document-model.ts +++ b/packages/designer/src/designer/document/document-model.ts @@ -7,7 +7,7 @@ import { ISimulator, ComponentInstance, Component, NodeInstance } from '../simul import { computed, obx } from '@recore/obx'; import Location from '../helper/location'; import { ComponentConfig } from '../component-config'; -import { isElement } from '../../utils/dom'; +import { isElement } from '../../utils/is-element'; export default class DocumentModel { /** diff --git a/packages/designer/src/designer/helper/dragon.ts b/packages/designer/src/designer/helper/dragon.ts index 4fe4ba5a0..bb8b17419 100644 --- a/packages/designer/src/designer/helper/dragon.ts +++ b/packages/designer/src/designer/helper/dragon.ts @@ -6,7 +6,7 @@ import { NodeData } from '../schema'; import { ISimulator, isSimulator } from '../simulator'; import Node from '../document/node/node'; import Designer from '../designer'; -import { setNativeSelection } from '../../utils/navtive-selection'; +import { setNativeSelection } from './navtive-selection'; import cursor from './cursor'; export interface LocateEvent { @@ -135,8 +135,22 @@ export function setShaken(e: any) { e.shaken = true; } -function isFromTopDocument(e: MouseEvent) { - return e.view!.document === document; +function getSourceSensor(dragObject: DragObject): ISimulator | null { + if (!isDragNodeObject(dragObject)) { + return null; + } + return dragObject.nodes[0]?.document.simulator || null; +} + +function makeSimulatorListener(masterSensors: ISimulator[]): (fn: (sdoc: Document) => void) => void { + return (fn: (sdoc: Document) => void) => { + masterSensors.forEach(sim => { + const sdoc = sim.contentDocument; + if (sdoc) { + fn(sdoc); + } + }); + }; } export default class Dragon { @@ -167,12 +181,12 @@ export default class Dragon { } // Get a new node to be dragged - const dragTarget = boost(e); - if (!dragTarget) { + const dragObject = boost(e); + if (!dragObject) { return; } - this.boost(dragTarget, e); + this.boost(dragObject, e); }; shell.addEventListener('mousedown', mousedown as any); return () => { @@ -180,24 +194,18 @@ export default class Dragon { }; } - getMasterSensors(): ISimulator[] { - return this.designer.project.documents.map(doc => { - if (doc.actived && doc.simulator?.sensorAvailable) { - return doc.simulator; - } - return null; - }).filter(Boolean) as any; - } - boost(dragObject: DragObject, boostEvent: MouseEvent) { const doc = document; - const isFromTop = isFromTopDocument(boostEvent); + const sourceDoc = boostEvent.view?.document; const masterSensors = this.getMasterSensors(); + const listenSimulators = !sourceDoc || sourceDoc === doc ? makeSimulatorListener(masterSensors) : null; + const alwaysListen = listenSimulators ? doc : sourceDoc!; const designer = this.designer; const newBie = dragObject.type !== DragObjectType.Node; let lastSensor: ISensor | undefined; this._dragging = false; + // 禁用默认的文稿拖选 this.setNativeSelection(false); @@ -216,10 +224,6 @@ export default class Dragon { } }; - // period one fix: - // get evt source-sensor - // get globalX and globalY source-sensor - const drag = (e: MouseEvent) => { checkcopy(e); @@ -241,10 +245,12 @@ export default class Dragon { } this.setDraggingState(true); // ESC cancel drag - doc.addEventListener('keydown', checkesc, false); - if (isFromTop) { - // topDoc.addEventListener('keydown', checkesc, false); - } + alwaysListen.addEventListener('keydown', checkesc, false); + listenSimulators && + listenSimulators(sdoc => { + sdoc.addEventListener('keydown', checkesc, false); + }); + this.emitter.emit('dragstart', locateEvent); }; @@ -281,26 +287,21 @@ export default class Dragon { this.clearState(); - if (isFromTop) { - doc.removeEventListener('mousemove', move, true); - doc.removeEventListener('mouseup', over, true); - doc.removeEventListener('mousedown', over, true); - doc.removeEventListener('keydown', checkesc, false); - doc.removeEventListener('keydown', checkcopy as any, false); - doc.removeEventListener('keyup', checkcopy as any, false); - } else { - masterSensors.forEach(item => { - const odoc = item.contentDocument; - if (odoc) { - odoc.removeEventListener('mousemove', move, true); - odoc.removeEventListener('mouseup', over, true); - odoc.removeEventListener('mousedown', over, true); - odoc.removeEventListener('keydown', checkesc, false); - odoc.removeEventListener('keydown', checkcopy as any, false); - odoc.removeEventListener('keyup', checkcopy as any, false); - } + alwaysListen.removeEventListener('mousemove', move, true); + alwaysListen.removeEventListener('mouseup', over, true); + alwaysListen.removeEventListener('mousedown', over, true); + alwaysListen.removeEventListener('keydown', checkesc, false); + alwaysListen.removeEventListener('keydown', checkcopy as any, false); + alwaysListen.removeEventListener('keyup', checkcopy as any, false); + listenSimulators && + listenSimulators(sdoc => { + sdoc.removeEventListener('mousemove', move, true); + sdoc.removeEventListener('mouseup', over, true); + sdoc.removeEventListener('mousedown', over, true); + sdoc.removeEventListener('keydown', checkesc, false); + sdoc.removeEventListener('keydown', checkcopy as any, false); + sdoc.removeEventListener('keyup', checkcopy as any, false); }); - } if (exception) { throw exception; } @@ -348,13 +349,6 @@ export default class Dragon { return evt; }; - function getSourceSensor(dragObject: DragObject): ISimulator | null { - if (!isDragNodeObject(dragObject)) { - return null; - } - return dragObject.nodes[0]?.document.simulator || null; - } - const sourceSensor = getSourceSensor(dragObject); const sensors: ISensor[] = (masterSensors as ISensor[]).concat(this.sensors); const chooseSensor = (e: LocateEvent) => { @@ -380,27 +374,42 @@ export default class Dragon { return sensor; }; - doc.addEventListener('mousemove', move, true); - doc.addEventListener('mouseup', over, true); - doc.addEventListener('mousedown', over, true); - if (isFromTop) {/* - topDoc.addEventListener('mousemove', move, true); - topDoc.addEventListener('mouseup', over, true); - topDoc.addEventListener('mousedown', over, true); - */ - } + alwaysListen.addEventListener('mousemove', move, true); + alwaysListen.addEventListener('mouseup', over, true); + alwaysListen.addEventListener('mousedown', over, true); + listenSimulators && + listenSimulators(sdoc => { + // alwaysListen = global document + // listen others simulator iframe + sdoc.addEventListener('mousemove', move, true); + sdoc.addEventListener('mouseup', over, true); + sdoc.addEventListener('mousedown', over, true); + }); + + // future think: drag things from browser-out or a iframe-pane if (!newBie) { - doc.addEventListener('keydown', checkcopy as any, false); - doc.addEventListener('keyup', checkcopy as any, false); - if (isFromTop) {/* - topDoc.addEventListener('keydown', checkcopy as any, false); - topDoc.addEventListener('keyup', checkcopy as any, false); - */ - } + alwaysListen.addEventListener('keydown', checkcopy as any, false); + alwaysListen.addEventListener('keyup', checkcopy as any, false); + listenSimulators && + listenSimulators(sdoc => { + sdoc.addEventListener('keydown', checkcopy as any, false); + sdoc.addEventListener('keyup', checkcopy as any, false); + }); } } + private getMasterSensors(): ISimulator[] { + return this.designer.project.documents + .map(doc => { + if (doc.actived && doc.simulator?.sensorAvailable) { + return doc.simulator; + } + return null; + }) + .filter(Boolean) as any; + } + // #region ======== drag and drop helpers ============ private setNativeSelection(enableFlag: boolean) { setNativeSelection(enableFlag); @@ -447,7 +456,6 @@ export default class Dragon { } // #endregion - /** * 添加投放感应区 */ diff --git a/packages/designer/src/utils/hotkey.ts b/packages/designer/src/designer/helper/hotkey.ts similarity index 100% rename from packages/designer/src/utils/hotkey.ts rename to packages/designer/src/designer/helper/hotkey.ts diff --git a/packages/designer/src/utils/navtive-selection.ts b/packages/designer/src/designer/helper/navtive-selection.ts similarity index 100% rename from packages/designer/src/utils/navtive-selection.ts rename to packages/designer/src/designer/helper/navtive-selection.ts diff --git a/packages/designer/src/designer/helper/scroller.ts b/packages/designer/src/designer/helper/scroller.ts index ecef91f4b..a901ab10b 100644 --- a/packages/designer/src/designer/helper/scroller.ts +++ b/packages/designer/src/designer/helper/scroller.ts @@ -1,4 +1,4 @@ -import { isElement } from '../../utils/dom'; +import { isElement } from '../../utils/is-element'; export class ScrollTarget { get left() { diff --git a/packages/designer/src/utils/dom.ts b/packages/designer/src/utils/get-client-rects.ts similarity index 52% rename from packages/designer/src/utils/dom.ts rename to packages/designer/src/utils/get-client-rects.ts index f723945d0..1629b20c9 100644 --- a/packages/designer/src/utils/dom.ts +++ b/packages/designer/src/utils/get-client-rects.ts @@ -1,10 +1,4 @@ -export function isDOMNode(node: any): node is Element | Text { - return node.nodeType && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE); -} - -export function isElement(node: any): node is Element { - return node.nodeType === Node.ELEMENT_NODE; -} +import { isElement } from './is-element'; // a range for test TextNode clientRect const cycleRange = document.createRange(); diff --git a/packages/designer/src/utils/is-dom-node.ts b/packages/designer/src/utils/is-dom-node.ts new file mode 100644 index 000000000..1d84eaa02 --- /dev/null +++ b/packages/designer/src/utils/is-dom-node.ts @@ -0,0 +1,3 @@ +export function isDOMNode(node: any): node is Element | Text { + return node.nodeType && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE); +} diff --git a/packages/designer/src/utils/is-element.ts b/packages/designer/src/utils/is-element.ts new file mode 100644 index 000000000..7cb030814 --- /dev/null +++ b/packages/designer/src/utils/is-element.ts @@ -0,0 +1,3 @@ +export function isElement(node: any): node is Element { + return node.nodeType === Node.ELEMENT_NODE; +}