diff --git a/packages/designer/auxilary-bak/README.md b/packages/designer/auxilary-bak/README.md deleted file mode 100644 index bd959599f..000000000 --- a/packages/designer/auxilary-bak/README.md +++ /dev/null @@ -1,22 +0,0 @@ -辅助类 - 对齐线 - 插入指示 insertion 竖线 横线 插入块 禁止插入块 - 幽灵替身 ghost - 聚焦编辑指示 - - -插入指示 insertion 竖线 横线 插入块 禁止插入块 - -竖线:红色,绿色 -横线:红色,绿色 -插入块:透明绿色,透明红色 - -投放指示线 - -cover - -轮廓服务 - 悬停指示线 xray mode? - 选中指示线 - 投放指示线 - 透视线 x-ray diff --git a/packages/designer/auxilary-bak/embed-editor-toolbar.tsx b/packages/designer/auxilary-bak/embed-editor-toolbar.tsx deleted file mode 100644 index e8d6d8fac..000000000 --- a/packages/designer/auxilary-bak/embed-editor-toolbar.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Component } from 'react'; -import embedEditor from '../../globals/embed-editor'; - -export default class EmbedEditorToolbar extends Component { - shouldComponentUpdate() { - return false; - } - - render() { - return
embedEditor.mount(shell)} />; - } -} diff --git a/packages/designer/src/builtins/simulator/host/auxilary/auxiliary.tsx b/packages/designer/src/builtins/simulator/host/auxilary/auxiliary.tsx index ea5dbfcfd..57e6e7d35 100644 --- a/packages/designer/src/builtins/simulator/host/auxilary/auxiliary.tsx +++ b/packages/designer/src/builtins/simulator/host/auxilary/auxiliary.tsx @@ -3,9 +3,10 @@ import { Component } from 'react'; import { OutlineHovering } from './outline-hovering'; import { SimulatorContext } from '../context'; import { SimulatorHost } from '../host'; +import { OutlineSelecting } from './outline-selecting'; +import { InsertionView } from './insertion'; import './auxiliary.less'; import './outlines.less'; -import { OutlineSelecting } from './outline-selecting'; @observer export class AuxiliaryView extends Component { @@ -22,6 +23,7 @@ export class AuxiliaryView extends Component {
+
); } diff --git a/packages/designer/auxilary-bak/insertion.less b/packages/designer/src/builtins/simulator/host/auxilary/insertion.less similarity index 95% rename from packages/designer/auxilary-bak/insertion.less rename to packages/designer/src/builtins/simulator/host/auxilary/insertion.less index 871210b7b..b5cb19f50 100644 --- a/packages/designer/auxilary-bak/insertion.less +++ b/packages/designer/src/builtins/simulator/host/auxilary/insertion.less @@ -1,4 +1,4 @@ -.my-insertion { +.lc-insertion { position: absolute; top: -1.5px; left: 0; diff --git a/packages/designer/auxilary-bak/insertion.tsx b/packages/designer/src/builtins/simulator/host/auxilary/insertion.tsx similarity index 57% rename from packages/designer/auxilary-bak/insertion.tsx rename to packages/designer/src/builtins/simulator/host/auxilary/insertion.tsx index cc09d6ec4..99790f044 100644 --- a/packages/designer/auxilary-bak/insertion.tsx +++ b/packages/designer/src/builtins/simulator/host/auxilary/insertion.tsx @@ -1,34 +1,35 @@ import { Component } from 'react'; -import { observer } from '@ali/recore'; -import { getCurrentDocument } from '../../globals'; +import { computed } from '@recore/obx'; +import { observer } from '@recore/core-obx'; +import { SimulatorContext } from '../context'; +import { SimulatorHost } from '../host'; +import Location, { Rect, isLocationChildrenDetail, LocationChildrenDetail, isVertical } from '../../../../designer/helper/location'; +import { ISimulator } from '../../../../designer/simulator'; +import { NodeParent } from '../../../../designer/document/node/node'; import './insertion.less'; -import Location, { isLocationChildrenDetail, isVertical, LocationChildrenDetail, Rect } from '../../document/location'; -import { isConditionFlow } from '../../document/node/condition-flow'; -import { getChildAt, INodeParent } from '../../document/node'; -import DocumentContext from '../../document/document-context'; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function processPropDetail() { - // return { insertType: 'cover', coverEdge: ? }; -} interface InsertionData { - edge?: Rect; + edge?: DOMRect; insertType?: string; vertical?: boolean; nearRect?: Rect; - coverRect?: Rect; + coverRect?: DOMRect; } /** * 处理拖拽子节点(INode)情况 */ function processChildrenDetail( - doc: DocumentContext, - target: INodeParent, + sim: ISimulator, + target: NodeParent, detail: LocationChildrenDetail, ): InsertionData { - const edge = doc.computeRect(target); + let edge = detail.edge || null; + + if (edge) { + edge = sim.computeRect(target); + } + if (!edge) { return {}; } @@ -38,15 +39,9 @@ function processChildrenDetail( insertType: 'before', }; - if (isConditionFlow(target)) { - ret.insertType = 'cover'; - ret.coverRect = edge; - return ret; - } - if (detail.near) { const { node, pos, rect, align } = detail.near; - ret.nearRect = rect || doc.computeRect(node); + ret.nearRect = rect || sim.computeRect(node); ret.vertical = align ? align === 'V' : isVertical(ret.nearRect); ret.insertType = pos; return ret; @@ -55,10 +50,10 @@ function processChildrenDetail( // from outline-tree: has index, but no near // TODO: think of shadowNode & ConditionFlow const { index } = detail; - let nearNode = getChildAt(target, index); + let nearNode = target.children.get(index); if (!nearNode) { // index = 0, eg. nochild, - nearNode = getChildAt(target, index > 0 ? index - 1 : 0); + nearNode = target.children.get(index > 0 ? index - 1 : 0); if (!nearNode) { ret.insertType = 'cover'; ret.coverRect = edge; @@ -67,7 +62,7 @@ function processChildrenDetail( ret.insertType = 'after'; } if (nearNode) { - ret.nearRect = doc.computeRect(nearNode); + ret.nearRect = sim.computeRect(nearNode); ret.vertical = isVertical(ret.nearRect); } return ret; @@ -77,44 +72,56 @@ function processChildrenDetail( * 将 detail 信息转换为页面"坐标"信息 */ function processDetail({ target, detail, document }: Location): InsertionData { + const sim = document.simulator; + if (!sim) { + return {}; + } if (isLocationChildrenDetail(detail)) { - return processChildrenDetail(document, target, detail); + return processChildrenDetail(sim, target, detail); } else { // TODO: others... - const edge = document.computeRect(target); + const instances = sim.getComponentInstances(target); + if (!instances) { + return {}; + } + const edge = sim.computeComponentInstanceRect(instances[0]); return edge ? { edge, insertType: 'cover', coverRect: edge } : {}; } } @observer export class InsertionView extends Component { + static contextType = SimulatorContext; + + @computed get host(): SimulatorHost { + return this.context; + } + shouldComponentUpdate() { return false; } render() { - const doc = getCurrentDocument(); - if (!doc || !doc.dropLocation) { + const loc = this.host.document.dropLocation; + if (!loc) { return null; } - const { scale, scrollTarget } = doc.viewport; - const sx = scrollTarget!.left; - const sy = scrollTarget!.top; + const { scale, scrollX, scrollY } = this.host.viewport; + const { edge, insertType, coverRect, nearRect, vertical } = processDetail(loc); - const { edge, insertType, coverRect, nearRect, vertical } = processDetail(doc.dropLocation); if (!edge) { return null; } - let className = 'my-insertion'; + let className = 'lc-insertion'; const style: any = {}; let x: number; let y: number; if (insertType === 'cover') { className += ' cover'; - x = (coverRect!.left + sx) * scale; - y = (coverRect!.top + sy) * scale; + x = (coverRect!.left + scrollX) * scale; + y = (coverRect!.top + scrollY) * scale; style.width = coverRect!.width * scale; style.height = coverRect!.height * scale; } else { @@ -123,12 +130,12 @@ export class InsertionView extends Component { } if (vertical) { className += ' vertical'; - x = ((insertType === 'before' ? nearRect.left : nearRect.right) + sx) * scale; - y = (nearRect.top + sy) * scale; + x = ((insertType === 'before' ? nearRect.left : nearRect.right) + scrollX) * scale; + y = (nearRect.top + scrollY) * scale; style.height = nearRect!.height * scale; } else { - x = (nearRect.left + sx) * scale; - y = ((insertType === 'before' ? nearRect.top : nearRect.bottom) + sy) * scale; + x = (nearRect.left + scrollX) * scale; + y = ((insertType === 'before' ? nearRect.top : nearRect.bottom) + scrollY) * scale; style.width = nearRect.width * scale; } } diff --git a/packages/designer/src/builtins/simulator/host/auxilary/outline-hovering.tsx b/packages/designer/src/builtins/simulator/host/auxilary/outline-hovering.tsx index a56109e1f..2e33073ec 100644 --- a/packages/designer/src/builtins/simulator/host/auxilary/outline-hovering.tsx +++ b/packages/designer/src/builtins/simulator/host/auxilary/outline-hovering.tsx @@ -75,7 +75,7 @@ export class OutlineHovering extends Component { if (!current) { return ; } - const instances = host.getComponentInstance(current); + const instances = host.getComponentInstances(current); if (!instances || instances.length < 1) { return ; } @@ -83,7 +83,7 @@ export class OutlineHovering extends Component { if (instances.length === 1) { return ( {instances.map((inst, i) => ( {selecting.map(node => { - const instances = this.host.getComponentInstance(node); + const instances = this.host.getComponentInstances(node); if (!instances || instances.length < 1) { return null; } diff --git a/packages/designer/src/builtins/simulator/host/host.ts b/packages/designer/src/builtins/simulator/host/host.ts index b66dc85ff..ba816c96d 100644 --- a/packages/designer/src/builtins/simulator/host/host.ts +++ b/packages/designer/src/builtins/simulator/host/host.ts @@ -1,19 +1,38 @@ import { obx, autorun, computed } from '@recore/obx'; -import { ISimulator, ComponentInstance, Component, NodeInstance } from '../../../designer/simulator'; +import { ISimulator, Component, NodeInstance } from '../../../designer/simulator'; import Viewport from './viewport'; import { createSimulator } from './create-simulator'; import { SimulatorRenderer } from '../renderer/renderer'; -import Node, { NodeParent } from '../../../designer/document/node/node'; +import Node, { NodeParent, isNodeParent, isNode, contains } from '../../../designer/document/node/node'; import DocumentModel from '../../../designer/document/document-model'; import ResourceConsumer from './resource-consumer'; import { AssetLevel, Asset, assetBundle, assetItem, AssetType } from '../utils/asset'; -import { DragObjectType, isShaken, LocateEvent, DragNodeObject, DragNodeDataObject } from '../../../designer/helper/dragon'; -import { LocationData } from '../../../designer/helper/location'; -import { NodeData } from '../../../designer/schema'; +import { + DragObjectType, + isShaken, + LocateEvent, + DragNodeObject, + DragNodeDataObject, + isDragAnyObject, + isDragNodeObject, +} from '../../../designer/helper/dragon'; +import { + LocationData, + isLocationData, + LocationChildrenDetail, + LocationDetailType, + isChildInline, + isRowContainer, + getRectTarget, + Rect, + CanvasPoint, +} from '../../../designer/helper/location'; +import { isNodeSchema, NodeSchema } from '../../../designer/schema'; import { ComponentDescriptionSpec } from '../../../designer/component-config'; import { ReactInstance } from 'react'; import { setNativeSelection } from '../../../designer/helper/navtive-selection'; import cursor from '../../../designer/helper/cursor'; +import { isRootNode } from '../../../designer/document/node/root-node'; export interface SimulatorProps { // 从 documentModel 上获取 @@ -53,15 +72,11 @@ const defaultDepends = [ export class SimulatorHost implements ISimulator { readonly isSimulator = true; + constructor(readonly document: DocumentModel) {} readonly designer = this.document.designer; - private _sensorAvailable: boolean = true; - get sensorAvailable(): boolean { - return this._sensorAvailable; - } - @computed get device(): string | undefined { // 根据 device 不同来做画布外框样式变化 渲染时可选择不同组件 // renderer 依赖 @@ -92,6 +107,9 @@ export class SimulatorHost implements ISimulator { } @obx.ref _props: SimulatorProps = {}; + /** + * @see ISimulator + */ setProps(props: SimulatorProps) { this._props = props; } @@ -196,7 +214,7 @@ export class SimulatorHost implements ISimulator { // TODO: think of lock when edit a node // 事件路由 doc.addEventListener('mousedown', (downEvent: MouseEvent) => { - const nodeInst = documentModel.getNodeInstanceFromElement(downEvent.target as Element); + const nodeInst = this.getNodeInstanceFromElement(downEvent.target as Element); if (!nodeInst?.node) { selection.clear(); return; @@ -256,7 +274,6 @@ export class SimulatorHost implements ISimulator { // cause edit doc.addEventListener('dblclick', (e: MouseEvent) => { // TODO: - }); } @@ -271,8 +288,9 @@ export class SimulatorHost implements ISimulator { if (!hovering.enable) { return; } - const nodeInst = this.document.getNodeInstanceFromElement(e.target as Element); + const nodeInst = this.getNodeInstanceFromElement(e.target as Element); // TODO: enhance only hover one instance + console.info(nodeInst); hovering.hover(nodeInst?.node || null); e.stopPropagation(); }; @@ -298,6 +316,9 @@ export class SimulatorHost implements ISimulator { }; } + /** + * @see ISimulator + */ setSuspense(suspended: boolean) { if (suspended) { if (this.disableHovering) { @@ -312,35 +333,61 @@ export class SimulatorHost implements ISimulator { } } - setDesignMode(mode: string): void { - throw new Error('Method not implemented.'); - } - + /** + * @see ISimulator + */ describeComponent(component: Component): ComponentDescriptionSpec { throw new Error('Method not implemented.'); } - getComponent(componentName: string): Component { - throw new Error('Method not implemented.'); + /** + * @see ISimulator + */ + getComponent(componentName: string): Component | null { + return null; } - getComponentInstance(node: Node): ReactInstance[] | null { - return this._renderer?.getComponentInstance(node.id) || null; + /** + * @see ISimulator + */ + getComponentInstances(node: Node): ReactInstance[] | null { + return this._renderer?.getComponentInstances(node.id) || null; } - getComponentInstanceId(instance: ReactInstance) { - - } + /** + * @see ISimulator + */ + getComponentInstanceId(instance: ReactInstance) {} + /** + * @see ISimulator + */ getComponentContext(node: Node): object { throw new Error('Method not implemented.'); } - getClosestNodeInstance(elem: Element): NodeInstance | null { - return this.renderer?.getClosestNodeInstance(elem) || null; + /** + * @see ISimulator + */ + getClosestNodeInstance(from: ReactInstance, specId?: string): NodeInstance | null { + return this.renderer?.getClosestNodeInstance(from, specId) || null; } - computeComponentInstanceRect(instance: ReactInstance): DOMRect | null { + /** + * @see ISimulator + */ + computeRect(node: Node): Rect | null { + const instances = this.getComponentInstances(node); + if (!instances) { + return null; + } + return this.computeComponentInstanceRect(instances[0]); + } + + /** + * @see ISimulator + */ + computeComponentInstanceRect(instance: ReactInstance): Rect | null { const renderer = this.renderer!; const elements = renderer.findDOMNodes(instance); if (!elements) { @@ -348,10 +395,12 @@ export class SimulatorHost implements ISimulator { } let rects: DOMRect[] | undefined; - let last: { x: number; y: number; r: number; b: number; } | undefined; + let last: { x: number; y: number; r: number; b: number } | undefined; + let computed = false; + const elems = elements.slice(); while (true) { if (!rects || rects.length < 1) { - const elem = elements.pop(); + const elem = elems.pop(); if (!elem) { break; } @@ -372,30 +421,62 @@ export class SimulatorHost implements ISimulator { } if (rect.left < last.x) { last.x = rect.left; + computed = true; } if (rect.top < last.y) { last.y = rect.top; + computed = true; } if (rect.right > last.r) { last.r = rect.right; + computed = true; } if (rect.bottom > last.b) { last.b = rect.bottom; + computed = true; } } if (last) { - return new DOMRect(last.x, last.y, last.r - last.x, last.b - last.y); + const r: any = new DOMRect(last.x, last.y, last.r - last.x, last.b - last.y); + r.elements = elements; + r.computed = computed; + return r; } return null; } + /** + * @see ISimulator + */ findDOMNodes(instance: ReactInstance): Array | null { return this._renderer?.findDOMNodes(instance) || null; } + /** + * 通过 DOM 节点获取节点,依赖 simulator 的接口 + */ + getNodeInstanceFromElement(target: Element | null): NodeInstance | null { + if (!target) { + return null; + } + + const nodeIntance = this.getClosestNodeInstance(target); + if (!nodeIntance) { + return null; + } + const node = this.document.getNode(nodeIntance.nodeId); + return { + ...nodeIntance, + node, + }; + } + private tryScrollAgain: number | null = null; + /** + * @see ISimulator + */ scrollToNode(node: Node, detail?: any, tryTimes = 0) { this.tryScrollAgain = null; if (this.sensing) { @@ -451,80 +532,251 @@ export class SimulatorHost implements ISimulator { } // #region ========= drag and drop helpers ============= + /** + * @see ISimulator + */ setNativeSelection(enableFlag: boolean) { - setNativeSelection(enableFlag); + this.renderer?.setNativeSelection(enableFlag); } + /** + * @see ISimulator + */ setDraggingState(state: boolean) { - cursor.setDragging(state); + this.renderer?.setDraggingState(state); } + /** + * @see ISimulator + */ setCopyState(state: boolean) { - cursor.setCopy(state); + this.renderer?.setCopyState(state); } + /** + * @see ISimulator + */ clearState() { - cursor.release(); + this.renderer?.clearState(); } + private _sensorAvailable: boolean = true; + /** + * @see ISensor + */ + get sensorAvailable(): boolean { + return this._sensorAvailable; + } + + /** + * @see ISensor + */ fixEvent(e: LocateEvent): LocateEvent { - /* if (e.fixed) { return e; } - if (!e.target || e.originalEvent.view!.document !== this.contentDocument) { - e.target = this.contentDocument!.elementFromPoint(e.canvasX, e.canvasY); - }*/ + + const notMyEvent = e.originalEvent.view?.document !== this.contentDocument; + // fix canvasX canvasY : 当前激活文档画布坐标系 + if (notMyEvent || !('canvasX' in e) || !('canvasY' in e)) { + const l = this.viewport.toLocalPoint({ + clientX: e.globalX, + clientY: e.globalY, + }); + e.canvasX = l.clientX; + e.canvasY = l.clientY; + } + + // fix target : 浏览器事件响应目标 + if (!e.target || notMyEvent) { + e.target = this.contentDocument!.elementFromPoint(e.canvasX!, e.canvasY!); + } + + // documentModel : 目标文档 + e.documentModel = this.document; + + // 事件已订正 + e.fixed = true; return e; } + /** + * @see ISensor + */ isEnter(e: LocateEvent): boolean { - return false; /* - const rect = this.bounds; + const rect = this.viewport.bounds; return e.globalY >= rect.top && e.globalY <= rect.bottom && e.globalX >= rect.left && e.globalX <= rect.right; - */ } private sensing: boolean = false; + /** + * @see ISensor + */ deactiveSensor() { this.sensing = false; this.scroller.cancel(); } - // ========= drag location logic start ========== + // ========= drag location logic: hepler for locate ========== + + /** + * @see ISensor + */ + locate(e: LocateEvent): any { + this.sensing = true; + this.scroller.scrolling(e); + const dropTarget = this.getDropTarget(e); + console.info('aa', dropTarget); + if (!dropTarget) { + return null; + } + + if (isLocationData(dropTarget)) { + return this.designer.createLocation(dropTarget); + } + + const target = dropTarget; + const targetInstance = e.targetInstance as ReactInstance; + + const parentInstance = this.getClosestNodeInstance(targetInstance, target.id); + const edge = this.computeComponentInstanceRect(parentInstance?.instance as any); + + if (!edge) { + return null; + } + + const children = target.children; + + const detail: LocationChildrenDetail = { + type: LocationDetailType.Children, + index: 0, + edge, + }; + + const locationData = { + target, + detail, + }; + + if (!children || children.size < 1 || !edge) { + return this.designer.createLocation(locationData); + } + + let nearRect = null; + let nearIndex = 0; + let nearNode = null; + let nearDistance = null; + let minTop = null; + let maxBottom = null; + + for (let i = 0, l = children.size; i < l; i++) { + let node = children.get(i)!; + let index = i; + const instances = this.getComponentInstances(node); + const inst = instances + ? instances.length > 1 + ? instances.find(inst => { + return this.getClosestNodeInstance(inst, target.id)?.instance === targetInstance; + }) + : instances[0] + : null; + const rect = inst ? this.computeComponentInstanceRect(inst) : null; + + if (!rect) { + continue; + } + + const distance = isPointInRect(e as any, rect) ? 0 : distanceToRect(e as any, rect); + + if (distance === 0) { + nearDistance = distance; + nearNode = node; + nearIndex = index; + nearRect = rect; + break; + } + + // 标记子节点最顶 + if (minTop === null || rect.top < minTop) { + minTop = rect.top; + } + // 标记子节点最底 + if (maxBottom === null || rect.bottom > maxBottom) { + maxBottom = rect.bottom; + } + + if (nearDistance === null || distance < nearDistance) { + nearDistance = distance; + nearNode = node; + nearIndex = index; + nearRect = rect; + } + } + + detail.index = nearIndex; + + if (nearNode && nearRect) { + const el = getRectTarget(nearRect); + const inline = el ? isChildInline(el) : false; + const row = el ? isRowContainer(el.parentElement!) : false; + const vertical = inline || row; + // TODO: fix type + const near: any = { + node: nearNode, + pos: 'before', + align: vertical ? 'V' : 'H', + }; + detail.near = near; + if (isNearAfter(e as any, nearRect, vertical)) { + near.pos = 'after'; + detail.index = nearIndex + 1; + } + if (!row && nearDistance !== 0) { + const edgeDistance = distanceToEdge(e as any, edge); + if (edgeDistance.distance < nearDistance!) { + const nearAfter = edgeDistance.nearAfter; + if (minTop == null) { + minTop = edge.top; + } + if (maxBottom == null) { + maxBottom = edge.bottom; + } + near.rect = new DOMRect(edge.left, minTop, edge.width, maxBottom - minTop); + near.align = 'H'; + near.pos = nearAfter ? 'after' : 'before'; + detail.index = nearAfter ? children.size : 0; + } + } + } + + return this.designer.createLocation(locationData); + } + getDropTarget(e: LocateEvent): NodeParent | LocationData | null { - /* - const { target, dragTarget } = e; - const isAny = isAnyDragTarget(dragTarget); + const { target, dragObject } = e; + const isAny = isDragAnyObject(dragObject); let container: any; + if (target) { - const ref = this.document.getNodeFromElement(target as Element); - if (ref) { - container = ref; + const ref = this.getNodeInstanceFromElement(target); + if (ref?.node) { + e.targetInstance = ref.instance; + e.targetNode = ref.node; + container = ref.node; } else if (isAny) { return null; } else { - container = this.document.view; + container = this.document.rootNode; } } else if (isAny) { return null; } else { - container = this.document.view; + container = this.document.rootNode; } - if (!isElementNode(container) && !isRootNode(container)) { + if (!isNodeParent(container) && !isRootNode(container)) { container = container.parent; } - // use spec container to accept specialData if (isAny) { - while (container) { - if (isRootNode(container)) { - return null; - } - const locationData = this.acceptAnyData(container, e); - if (locationData) { - return locationData; - } - container = container.parent; - } + // TODO: use spec container to accept specialData return null; } @@ -546,7 +798,8 @@ export class SimulatorHost implements ISimulator { } else { container = container.parent; } - } else if (res === AT_CHILD) { + } + /* else if (res === AT_CHILD) { if (!upward) { upward = container.parent; } @@ -555,31 +808,30 @@ export class SimulatorHost implements ISimulator { container = upward; upward = null; } - } else if (isNode(res)) { + }*/ + else if (isNode(res)) { + console.info('res', res); container = res; upward = null; } - }*/ + } return null; } - acceptNodes(container: Node, e: LocateEvent) { - /* - const { dragTarget } = e; + acceptNodes(container: NodeParent, e: LocateEvent) { + const { dragObject } = e; if (isRootNode(container)) { - return this.checkDropTarget(container, dragTarget as any); + return this.checkDropTarget(container, dragObject as any); } - const proto = container.prototype; + const config = container.componentConfig; - const acceptable: boolean = this.isAcceptable(container); - if (!proto.isContainer && !acceptable) { + if (!config.isContainer) { return false; } - // check is contains, get common parent - if (isNodesDragTarget(dragTarget)) { - const nodes = dragTarget.nodes; + if (isDragNodeObject(dragObject)) { + const nodes = dragObject.nodes; let i = nodes.length; let p: any = container; while (i-- > 0) { @@ -588,41 +840,16 @@ export class SimulatorHost implements ISimulator { } } if (p !== container) { - return p || this.document.view; + return p || this.document.rootNode; } } - // first use accept - if (acceptable) { - const view: any = this.document.getView(container); - if (view && '$accept' in view) { - if (view.$accept === false) { - return false; - } - if (view.$accept === AT_CHILD || view.$accept === '@CHILD') { - return AT_CHILD; - } - if (typeof view.$accept === 'function') { - const ret = view.$accept(container, e); - if (ret || ret === false) { - return ret; - } - } - } - if (proto.acceptable) { - const ret = proto.accept(container, e); - if (ret || ret === false) { - return ret; - } - } - } - - return this.checkNesting(container, dragTarget as any); - */ + return this.checkNesting(container, dragObject as any); } + /* getNearByContainer(container: NodeParent, e: LocateEvent) { - /* + const children = container.children; if (!children || children.length < 1) { return null; @@ -656,195 +883,76 @@ export class SimulatorHost implements ISimulator { } } - return nearBy;*/ + return nearBy; } + */ - locate(e: LocateEvent): any { - /* - this.sensing = true; - this.scroller.scrolling(e); - const dropTarget = this.getDropTarget(e); - if (!dropTarget) { - return null; - } - - if (isLocationData(dropTarget)) { - return this.document.createLocation(dropTarget); - } - - const target = dropTarget; - - const edge = this.document.computeRect(target); - - const children = target.children; - - const detail: LocationChildrenDetail = { - type: LocationDetailType.Children, - index: 0, - }; - - const locationData = { - target, - detail, - }; - - if (!children || children.length < 1 || !edge) { - return this.document.createLocation(locationData); - } - - let nearRect = null; - let nearIndex = 0; - let nearNode = null; - let nearDistance = null; - let top = null; - let bottom = null; - - for (let i = 0, l = children.length; i < l; i++) { - let node = children[i]; - let index = i; - if (hasConditionFlow(node)) { - node = node.conditionFlow; - index = node.index; - // skip flow items - i = index + (node as any).length - 1; - } - const rect = this.document.computeRect(node); - - if (!rect) { - continue; - } - - const distance = isPointInRect(e, rect) ? 0 : distanceToRect(e, rect); - - if (distance === 0) { - nearDistance = distance; - nearNode = node; - nearIndex = index; - nearRect = rect; - break; - } - - // TODO: 忘记为什么这么处理了,记得添加注释 - if (top === null || rect.top < top) { - top = rect.top; - } - if (bottom === null || rect.bottom > bottom) { - bottom = rect.bottom; - } - - if (nearDistance === null || distance < nearDistance) { - nearDistance = distance; - nearNode = node; - nearIndex = index; - nearRect = rect; - } - } - - detail.index = nearIndex; - - if (nearNode && nearRect) { - const el = getRectTarget(nearRect); - const inline = el ? isChildInline(el) : false; - const row = el ? isRowContainer(el.parentElement!) : false; - const vertical = inline || row; - // TODO: fix type - const near: any = { - node: nearNode, - pos: 'before', - align: vertical ? 'V' : 'H', - }; - detail.near = near; - if (isNearAfter(e, nearRect, vertical)) { - near.pos = 'after'; - detail.index = nearIndex + (isConditionFlow(nearNode) ? nearNode.length : 1); - } - if (!row && nearDistance !== 0) { - const edgeDistance = distanceToEdge(e, edge); - if (edgeDistance.distance < nearDistance!) { - const nearAfter = edgeDistance.nearAfter; - if (top == null) { - top = edge.top; - } - if (bottom == null) { - bottom = edge.bottom; - } - near.rect = new DOMRect(edge.left, top, edge.width, bottom - top); - near.align = 'H'; - near.pos = nearAfter ? 'after' : 'before'; - detail.index = nearAfter ? children.length : 0; - } - } - } - - return this.document.createLocation(locationData); - */ - } - - isAcceptable(container: NodeParent): boolean { - return false; - /* - const proto = container.prototype; - const view: any = this.getComponentInstance(container); - if (view && '$accept' in view) { - return true; - } - return proto.acceptable;*/ - } - - acceptAnyData(container: Node, e: LocateEvent | MouseEvent | KeyboardEvent) { - /* - const proto = container.prototype; - const view: any = this.document.getView(container); - // use view instance method: $accept - if (view && typeof view.$accept === 'function') { - // should return LocationData - return view.$accept(container, e); - } - // use prototype method: accept - return proto.accept(container, e);*/ - } - - checkNesting(dropTarget: Node, dragTarget: DragNodeObject | DragNodeDataObject): boolean { - return false; - /* - const items: Array = dragTarget.nodes || (dragTarget as NodeDatasDragTarget).data; + checkNesting(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean { + const items: Array = dragObject.nodes || (dragObject as DragNodeDataObject).data; return items.every(item => this.checkNestingDown(dropTarget, item)); - */ } - checkDropTarget(dropTarget: Node, dragTarget: DragNodeObject | DragNodeDataObject): boolean { - return false; - /* - const items: Array = dragTarget.nodes || (dragTarget as NodeDatasDragTarget).data; + checkDropTarget(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean { + const items: Array = dragObject.nodes || (dragObject as DragNodeDataObject).data; return items.every(item => this.checkNestingUp(dropTarget, item)); - */ } - checkNestingUp(parent: NodeParent, target: NodeData | Node): boolean { - /* - if (isElementNode(target) || isElementData(target)) { - const proto = isElementNode(target) - ? target.prototype - : this.document.getPrototypeByTagNameOrURI(target.tagName, target.uri); - if (proto) { - return proto.checkNestingUp(target, parent); + checkNestingUp(parent: NodeParent, target: NodeSchema | Node): boolean { + if (isNode(target) || isNodeSchema(target)) { + const config = isNode(target) ? target.componentConfig : this.designer.getComponentConfig(target.componentName); + if (config) { + return config.checkNestingUp(target, parent); } - }*/ + } return true; } - checkNestingDown(parent: NodeParent, target: NodeData | Node): boolean { - /* - const proto = parent.prototype; - if (isConditionFlow(parent)) { - return parent.children.every( - child => proto.checkNestingDown(parent, child) && this.checkNestingUp(parent, child), - ); - } else { - return proto.checkNestingDown(parent, target) && this.checkNestingUp(parent, target); - }*/ - return false; + checkNestingDown(parent: NodeParent, target: NodeSchema | Node): boolean { + const config = parent.componentConfig; + return config.checkNestingDown(parent, target) && this.checkNestingUp(parent, target); } // #endregion } + + +function isPointInRect(point: CanvasPoint, rect: Rect) { + return ( + point.canvasY >= rect.top && + point.canvasY <= rect.bottom && + (point.canvasX >= rect.left && point.canvasX <= rect.right) + ); +} + +function distanceToRect(point: CanvasPoint, rect: Rect) { + let minX = Math.min(Math.abs(point.canvasX - rect.left), Math.abs(point.canvasX - rect.right)); + let minY = Math.min(Math.abs(point.canvasY - rect.top), Math.abs(point.canvasY - rect.bottom)); + if (point.canvasX >= rect.left && point.canvasX <= rect.right) { + minX = 0; + } + if (point.canvasY >= rect.top && point.canvasY <= rect.bottom) { + minY = 0; + } + + return Math.sqrt(minX ** 2 + minY ** 2); +} + +function distanceToEdge(point: CanvasPoint, rect: Rect) { + const distanceTop = Math.abs(point.canvasY - rect.top); + const distanceBottom = Math.abs(point.canvasY - rect.bottom); + + return { + distance: Math.min(distanceTop, distanceBottom), + nearAfter: distanceBottom < distanceTop, + }; +} + +function isNearAfter(point: CanvasPoint, rect: Rect, inline: boolean) { + if (inline) { + return ( + Math.abs(point.canvasX - rect.left) + Math.abs(point.canvasY - rect.top) > + Math.abs(point.canvasX - rect.right) + Math.abs(point.canvasY - rect.bottom) + ); + } + return Math.abs(point.canvasY - rect.top) > Math.abs(point.canvasY - rect.bottom); +} diff --git a/packages/designer/src/builtins/simulator/renderer/renderer.ts b/packages/designer/src/builtins/simulator/renderer/renderer.ts index a9328551e..15974c6e7 100644 --- a/packages/designer/src/builtins/simulator/renderer/renderer.ts +++ b/packages/designer/src/builtins/simulator/renderer/renderer.ts @@ -8,10 +8,12 @@ import { getClientRects } from '../../../utils/get-client-rects'; import { Asset } from '../utils/asset'; import loader from '../utils/loader'; import { ComponentDescriptionSpec } from '../../../designer/component-config'; -import { reactFindDOMNodes } from '../utils/react-find-dom-nodes'; +import { reactFindDOMNodes, FIBER_KEY } from '../utils/react-find-dom-nodes'; import { isESModule } from '../../../utils/is-es-module'; import { NodeInstance } from '../../../designer/simulator'; import { isElement } from '../../../utils/is-element'; +import cursor from '../../../designer/helper/cursor'; +import { setNativeSelection } from '../../../designer/helper/navtive-selection'; export class SimulatorRenderer { readonly isSimulatorRenderer = true; @@ -133,7 +135,9 @@ export class SimulatorRenderer { let instances = this.instancesMap.get(id); if (instances) { instances = instances.filter(checkInstanceMounted); - instances.push(instance); + if (!instances.includes(instance)) { + instances.push(instance); + } instancesMap.set(id, instances); } else { instancesMap.set(id, [instance]); @@ -144,12 +148,12 @@ export class SimulatorRenderer { this.ctxMap.set(id, ctx); } - getComponentInstance(id: string): ReactInstance[] | null { + getComponentInstances(id: string): ReactInstance[] | null { return this.instancesMap.get(id) || null; } - getClosestNodeInstance(element: Element): NodeInstance | null { - return getClosestNodeInstance(element); + getClosestNodeInstance(from: ReactInstance, nodeId?: string): NodeInstance | null { + return getClosestNodeInstance(from, nodeId); } findDOMNodes(instance: ReactInstance): Array | null { @@ -160,6 +164,28 @@ export class SimulatorRenderer { return getClientRects(element); } + setNativeSelection(enableFlag: boolean) { + setNativeSelection(enableFlag); + } + /** + * @see ISimulator + */ + setDraggingState(state: boolean) { + cursor.setDragging(state); + } + /** + * @see ISimulator + */ + setCopyState(state: boolean) { + cursor.setCopy(state); + } + /** + * @see ISimulator + */ + clearState() { + cursor.release(); + } + private _running: boolean = false; run() { if (this._running) { @@ -260,34 +286,44 @@ function cacheReactKey(el: Element): Element { const SYMBOL_VNID = Symbol('_LCNodeId'); -function getClosestNodeInstance(element: Element): NodeInstance | null { - let el: any = element; +function getClosestNodeInstance(from: ReactInstance, specId?: string): NodeInstance | null { + let el: any = from; if (el) { - el = cacheReactKey(el); + if (isElement(el)) { + el = cacheReactKey(el); + } else { + return getNodeInstance(el[FIBER_KEY], specId); + } } while (el) { if (SYMBOL_VNID in el) { - return { - nodeId: el[SYMBOL_VNID], - instance: el, - }; + const nodeId = el[SYMBOL_VNID]; + if (!specId || specId === nodeId) { + return { + nodeId: nodeId, + instance: el, + }; + } } // get fiberNode from element if (el[REACT_KEY]) { - return getNodeInstance(el[REACT_KEY]); + return getNodeInstance(el[REACT_KEY], specId); } el = el.parentElement; } return null; } -function getNodeInstance(fiberNode: any): NodeInstance | null { +function getNodeInstance(fiberNode: any, specId?: string): NodeInstance | null { const instance = fiberNode.stateNode; if (instance && SYMBOL_VNID in instance) { - return { - nodeId: instance[SYMBOL_VNID], - instance, - }; + const nodeId = instance[SYMBOL_VNID]; + if (!specId || specId === nodeId) { + return { + nodeId: nodeId, + instance: instance, + }; + } } return getNodeInstance(fiberNode.return); } diff --git a/packages/designer/src/builtins/simulator/utils/react-find-dom-nodes.ts b/packages/designer/src/builtins/simulator/utils/react-find-dom-nodes.ts index f2bcf9664..9783804de 100644 --- a/packages/designer/src/builtins/simulator/utils/react-find-dom-nodes.ts +++ b/packages/designer/src/builtins/simulator/utils/react-find-dom-nodes.ts @@ -2,7 +2,7 @@ import { ReactInstance } from 'react'; import { isElement } from '../../../utils/is-element'; import { isDOMNode } from '../../../utils/is-dom-node'; -const FIBER_KEY = '_reactInternalFiber'; +export const FIBER_KEY = '_reactInternalFiber'; function elementsFromFiber(fiber: any, elements: Array) { if (fiber) { diff --git a/packages/designer/src/designer/component-config.ts b/packages/designer/src/designer/component-config.ts index fde51b636..64bb76fe0 100644 --- a/packages/designer/src/designer/component-config.ts +++ b/packages/designer/src/designer/component-config.ts @@ -280,7 +280,7 @@ export class ComponentConfig { } private _isContainer?: boolean; get isContainer(): boolean { - return this._isContainer!; + return this._isContainer! || this.isRootComponent(); } private _isModal?: boolean; get isModal(): boolean { @@ -354,6 +354,10 @@ export class ComponentConfig { } } + isRootComponent() { + return this.componentName === 'Page' || this.componentName === 'Block' || this.componentName === 'Component'; + } + set spec(spec: ComponentDescriptionSpec) { this._spec = spec; this.parseSpec(spec); diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts index 9fcc34bf6..2ed1ecfc3 100644 --- a/packages/designer/src/designer/designer.ts +++ b/packages/designer/src/designer/designer.ts @@ -56,7 +56,7 @@ export default class Designer { }); this.dragon.onDrag(e => { - console.info(e); + console.info('dropLocation', this._dropLocation); if (this.props?.onDrag) { this.props.onDrag(e); } @@ -247,12 +247,9 @@ export default class Designer { return this._componentConfigsMap.get(componentName)!; } - const config = new ComponentConfig({ + return new ComponentConfig({ componentName, }); - - this._componentConfigsMap.set(componentName, config); - return config; } get componentsMap(): { [key: string]: ComponentDescriptionSpec } { diff --git a/packages/designer/src/designer/document/document-model.ts b/packages/designer/src/designer/document/document-model.ts index 72319414a..cb80d77a3 100644 --- a/packages/designer/src/designer/document/document-model.ts +++ b/packages/designer/src/designer/document/document-model.ts @@ -223,59 +223,16 @@ export default class DocumentModel { // TODO: emit simulator mounted } - /** - * 根据节点取得视图实例,在循环等场景会有多个,依赖 simulator 的接口 - */ - getComponentInstance(node: Node): ComponentInstance[] | null { - if (this.simulator) { - this.simulator.getComponentInstance(node); - } - return null; - } - getComponent(componentName: string): any { return this.simulator!.getComponent(componentName); } - getComponentConfig(component: Component, componentName: string): ComponentConfig { + getComponentConfig(componentName: string, component?: Component | null): ComponentConfig { // TODO: guess componentConfig from component by simulator return this.designer.getComponentConfig(componentName); } - /** - * 通过 DOM 节点获取节点,依赖 simulator 的接口 - */ - getNodeInstanceFromElement(target: Element | null): NodeInstance | null { - if (!this.simulator || !target) { - return null; - } - const nodeIntance = this.simulator.getClosestNodeInstance(target); - if (!nodeIntance) { - return null; - } - const node = this.getNode(nodeIntance.nodeId); - return { - ...nodeIntance, - node, - }; - } - - /** - * 获得到的结果是一个数组 - * 表示一个实例对应多个外层 DOM 节点,依赖 simulator 的接口 - */ - getDOMNodes(instance: ComponentInstance): Array | null { - if (!this.simulator) { - return null; - } - - if (isElement(instance)) { - return [instance]; - } - - return this.simulator.findDOMNodes(instance); - } private _opened: boolean = true; private _suspensed: boolean = false; diff --git a/packages/designer/src/designer/document/node/node-children.ts b/packages/designer/src/designer/document/node/node-children.ts index 58bff9747..b29fa4b22 100644 --- a/packages/designer/src/designer/document/node/node-children.ts +++ b/packages/designer/src/designer/document/node/node-children.ts @@ -1,8 +1,9 @@ import Node, { NodeParent } from './node'; import { NodeData } from '../../schema'; +import { obx, computed } from '@recore/obx'; export default class NodeChildren { - private children: Node[]; + @obx.val private children: Node[]; constructor(readonly owner: NodeParent, childrenData: NodeData | NodeData[]) { this.children = (Array.isArray(childrenData) ? childrenData : [childrenData]).map(child => { const node = this.owner.document.createNode(child); @@ -22,14 +23,14 @@ export default class NodeChildren { /** * 元素个数 */ - get size(): number { + @computed get size(): number { return this.children.length; } /** * 是否空 */ - isEmpty() { + @computed isEmpty() { return this.size < 1; } diff --git a/packages/designer/src/designer/document/node/node-content.ts b/packages/designer/src/designer/document/node/node-content.ts index af146a15b..436681830 100644 --- a/packages/designer/src/designer/document/node/node-content.ts +++ b/packages/designer/src/designer/document/node/node-content.ts @@ -1,10 +1,10 @@ -import { obx } from '@recore/obx'; +import { obx, computed } from '@recore/obx'; import { JSExpression, isJSExpression } from '../../schema'; export default class NodeContent { @obx.ref private _value: string | JSExpression = ''; - get value(): string | JSExpression { + @computed get value(): string | JSExpression { return this._value; } @@ -15,7 +15,7 @@ export default class NodeContent { /** * 获得表达式值 */ - get code() { + @computed get code() { if (isJSExpression(this._value)) { return this._value.value; } @@ -70,14 +70,14 @@ export default class NodeContent { /** * 是否表达式 */ - isJSExpression(): boolean { + @computed isJSExpression(): boolean { return isJSExpression(this._value); } /** * 是否空值 */ - isEmpty() { + @computed isEmpty() { if (isJSExpression(this._value)) { return this._value.value === ''; } diff --git a/packages/designer/src/designer/document/node/node.ts b/packages/designer/src/designer/document/node/node.ts index abb6c16d1..5101ab34b 100644 --- a/packages/designer/src/designer/document/node/node.ts +++ b/packages/designer/src/designer/document/node/node.ts @@ -54,8 +54,8 @@ export default class Node { protected _directives?: Props; protected _extras?: Props; protected _children: NodeChildren | NodeContent; - private _parent: NodeParent | null = null; - private _zLevel = 0; + @obx.ref private _parent: NodeParent | null = null; + @obx.ref private _zLevel = 0; get props(): Props | undefined { return this._props; } @@ -165,15 +165,18 @@ export default class Node { /** * 节点组件类 */ - @obx.ref get component(): Component { - return this.document.getComponent(this.componentName); + @obx.ref get component(): Component | null { + if (this.isNodeParent) { + return this.document.getComponent(this.componentName); + } + return null; } /** * 节点组件描述 */ @obx.ref get componentConfig(): ComponentConfig { - return this.document.getComponentConfig(this.component, this.componentName); + return this.document.getComponentConfig(this.componentName, this.component); } @obx.ref get propsData(): PropsMap | PropsList | null { @@ -258,7 +261,7 @@ export default class Node { /** * 获取节点在父容器中的索引 */ - get index(): number { + @computed get index(): number { if (!this.parent) { return -1; } diff --git a/packages/designer/src/designer/document/node/props/prop.ts b/packages/designer/src/designer/document/node/props/prop.ts index 274c56d90..281d941d1 100644 --- a/packages/designer/src/designer/document/node/props/prop.ts +++ b/packages/designer/src/designer/document/node/props/prop.ts @@ -125,7 +125,7 @@ export default class Prop implements IPropParent { * 值是否包含表达式 * 包含 JSExpresion | JSSlot 等值 */ - isContainJSExpression(): boolean { + @computed isContainJSExpression(): boolean { const type = this._type; if (type === 'expression') { return true; @@ -142,12 +142,12 @@ export default class Prop implements IPropParent { /** * 是否简单 JSON 数据 */ - isJSON() { + @computed isJSON() { return !this.isContainJSExpression(); } - private _items: Prop[] | null = null; - private _maps: Map | null = null; + @obx.val private _items: Prop[] | null = null; + @obx.val private _maps: Map | null = null; @computed private get items(): Prop[] | null { let _items: any; untracked(() => { diff --git a/packages/designer/src/designer/document/node/props/props.ts b/packages/designer/src/designer/document/node/props/props.ts index 62e30d433..091b094a0 100644 --- a/packages/designer/src/designer/document/node/props/props.ts +++ b/packages/designer/src/designer/document/node/props/props.ts @@ -11,7 +11,7 @@ export type UNSET = typeof UNSET; export default class Props implements IPropParent { readonly id = uniqueId('props'); @obx.val private items: Prop[] = []; - @obx.ref private get maps(): Map { + @computed private get maps(): Map { const maps = new Map(); if (this.items.length > 0) { this.items.forEach(prop => { @@ -36,7 +36,7 @@ export default class Props implements IPropParent { /** * 元素个数 */ - get size() { + @computed get size() { return this.items.length; } diff --git a/packages/designer/src/designer/document/node/props/stash-space.ts b/packages/designer/src/designer/document/node/props/stash-space.ts index 14cb36e18..e38d14015 100644 --- a/packages/designer/src/designer/document/node/props/stash-space.ts +++ b/packages/designer/src/designer/document/node/props/stash-space.ts @@ -1,10 +1,10 @@ -import { obx, autorun, untracked } from '@recore/obx'; +import { obx, autorun, untracked, computed } from '@recore/obx'; import Prop, { IPropParent } from './prop'; export type PendingItem = Prop[]; export default class StashSpace implements IPropParent { @obx.val private space: Set = new Set(); - @obx.ref private get maps(): Map { + @computed private get maps(): Map { const maps = new Map(); if (this.space.size > 0) { this.space.forEach(prop => { diff --git a/packages/designer/src/designer/helper/dragon.ts b/packages/designer/src/designer/helper/dragon.ts index bb8b17419..b19e5ceb9 100644 --- a/packages/designer/src/designer/helper/dragon.ts +++ b/packages/designer/src/designer/helper/dragon.ts @@ -2,8 +2,8 @@ import { EventEmitter } from 'events'; import { obx } from '@recore/obx'; import Location from './location'; import DocumentModel from '../document/document-model'; -import { NodeData } from '../schema'; -import { ISimulator, isSimulator } from '../simulator'; +import { NodeData, NodeSchema } from '../schema'; +import { ISimulator, isSimulator, ComponentInstance } from '../simulator'; import Node from '../document/node/node'; import Designer from '../designer'; import { setNativeSelection } from './navtive-selection'; @@ -43,11 +43,14 @@ export interface LocateEvent { /** * 激活或目标文档 */ - document?: DocumentModel; + documentModel?: DocumentModel; /** * 事件订正标识,初始构造时,从发起端构造,缺少 canvasX,canvasY, 需要经过订正才有 */ fixed?: true; + + targetNode?: Node; + targetInstance?: ComponentInstance; } /** @@ -89,8 +92,8 @@ export interface DragNodeObject { } export interface DragNodeDataObject { type: DragObjectType.NodeData; - data: NodeData | NodeData[]; - maps?: { [tagName: string]: string }; + data: NodeSchema | NodeSchema[]; + maps?: { [componentName: string]: string }; thumbnail?: string; description?: string; [extra: string]: any; @@ -279,7 +282,7 @@ export default class Dragon { if (this._dragging) { this._dragging = false; try { - this.emitter.emit('dragend', { dragTarget: dragObject, copy: this.isCopyState() }); + this.emitter.emit('dragend', { dragObject, copy: this.isCopyState() }); } catch (ex) { exception = ex; } @@ -339,6 +342,8 @@ export default class Dragon { const g = srcSim.viewport.toGlobalPoint(e); evt.globalX = g.clientX; evt.globalY = g.clientY; + evt.canvasX = e.clientX; + evt.canvasY = e.clientY; evt.sensor = srcSim; } else { // this condition will not happen, just make sure ts ok @@ -352,10 +357,12 @@ export default class Dragon { const sourceSensor = getSourceSensor(dragObject); const sensors: ISensor[] = (masterSensors as ISensor[]).concat(this.sensors); const chooseSensor = (e: LocateEvent) => { - let sensor = e.sensor || sensors.find(s => s.sensorAvailable && s.isEnter(e)); + let sensor = e.sensor && e.sensor.isEnter(e) ? e.sensor : sensors.find(s => s.sensorAvailable && s.isEnter(e)); if (!sensor) { if (lastSensor) { sensor = lastSensor; + } else if (e.sensor) { + sensor = e.sensor; } else if (sourceSensor) { sensor = sourceSensor; } diff --git a/packages/designer/src/designer/helper/hovering.ts b/packages/designer/src/designer/helper/hovering.ts index 7af2493c3..215435c76 100644 --- a/packages/designer/src/designer/helper/hovering.ts +++ b/packages/designer/src/designer/helper/hovering.ts @@ -21,7 +21,6 @@ export default class Hovering { } hover(node: Node | null) { - console.info(node); this._current = node; } diff --git a/packages/designer/src/designer/helper/location.ts b/packages/designer/src/designer/helper/location.ts index 22eac5dc3..c8e06de92 100644 --- a/packages/designer/src/designer/helper/location.ts +++ b/packages/designer/src/designer/helper/location.ts @@ -14,6 +14,7 @@ export enum LocationDetailType { export interface LocationChildrenDetail { type: LocationDetailType.Children; index: number; + edge?: DOMRect; near?: { node: ComponentNode; pos: 'before' | 'after'; @@ -36,11 +37,16 @@ export interface Point { clientY: number; } -export type Rects = Array & { +export interface CanvasPoint { + canvasX: number; + canvasY: number; +} + +export type Rects = DOMRect[] & { elements: Array; }; -export type Rect = (ClientRect | DOMRect) & { +export type Rect = DOMRect & { elements: Array; computed?: boolean; }; diff --git a/packages/designer/src/designer/simulator.ts b/packages/designer/src/designer/simulator.ts index f508920dd..5a5ee0502 100644 --- a/packages/designer/src/designer/simulator.ts +++ b/packages/designer/src/designer/simulator.ts @@ -100,16 +100,6 @@ export interface ISimulator

extends ISensor { */ clearState(): void; - /** - * 在模拟器拖拽定位 - */ - locate(e: LocateEvent): any; - - /** - * 给 event 打补丁,添加 canvasX, globalX 等信息,用于拖拽 - */ - fixEvent(e: LocateEvent): LocateEvent; - // #endregion /** @@ -128,13 +118,15 @@ export interface ISimulator

extends ISensor { /** * 根据节点获取节点的组件实例 */ - getComponentInstance(node: Node): ComponentInstance[] | null; + getComponentInstances(node: Node): ComponentInstance[] | null; /** * 根据节点获取节点的组件运行上下文 */ getComponentContext(node: Node): object | null; - getClosestNodeInstance(elem: Element): NodeInstance | null; + getClosestNodeInstance(from: ComponentInstance, specId?: string): NodeInstance | null; + + computeRect(node: Node): DOMRect | null; computeComponentInstanceRect(instance: ComponentInstance): DOMRect | null; @@ -150,9 +142,9 @@ export function isSimulator(obj: any): obj is ISimulator { return obj && obj.isSimulator; } -export interface NodeInstance { +export interface NodeInstance { nodeId: string; - instance: ComponentInstance; + instance: T; node?: Node | null; }