mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-14 13:03:07 +00:00
drag ok
This commit is contained in:
parent
ada3fe12ec
commit
4d4cfab271
@ -1,22 +0,0 @@
|
||||
辅助类
|
||||
对齐线
|
||||
插入指示 insertion 竖线 横线 插入块 禁止插入块
|
||||
幽灵替身 ghost
|
||||
聚焦编辑指示
|
||||
|
||||
|
||||
插入指示 insertion 竖线 横线 插入块 禁止插入块
|
||||
|
||||
竖线:红色,绿色
|
||||
横线:红色,绿色
|
||||
插入块:透明绿色,透明红色
|
||||
|
||||
投放指示线
|
||||
|
||||
cover
|
||||
|
||||
轮廓服务
|
||||
悬停指示线 xray mode?
|
||||
选中指示线
|
||||
投放指示线
|
||||
透视线 x-ray
|
||||
@ -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 <div className="embed-editor-toolbar" ref={shell => embedEditor.mount(shell)} />;
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
<div className="lc-auxiliary" style={{ transform: `translate(${-scrollX * scale}px,${-scrollY * scale}px)` }}>
|
||||
<OutlineHovering key="hovering" />
|
||||
<OutlineSelecting key="selecting" />
|
||||
<InsertionView key="insertion" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
.my-insertion {
|
||||
.lc-insertion {
|
||||
position: absolute;
|
||||
top: -1.5px;
|
||||
left: 0;
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -75,7 +75,7 @@ export class OutlineHovering extends Component {
|
||||
if (!current) {
|
||||
return <Fragment />;
|
||||
}
|
||||
const instances = host.getComponentInstance(current);
|
||||
const instances = host.getComponentInstances(current);
|
||||
if (!instances || instances.length < 1) {
|
||||
return <Fragment />;
|
||||
}
|
||||
@ -83,7 +83,7 @@ export class OutlineHovering extends Component {
|
||||
if (instances.length === 1) {
|
||||
return (
|
||||
<OutlineHoveringInstance
|
||||
key="line-s"
|
||||
key="line-h"
|
||||
title={current.title}
|
||||
scale={this.scale}
|
||||
scrollX={this.scrollX}
|
||||
@ -96,7 +96,7 @@ export class OutlineHovering extends Component {
|
||||
<Fragment>
|
||||
{instances.map((inst, i) => (
|
||||
<OutlineHoveringInstance
|
||||
key={`line-${i}`}
|
||||
key={`line-h-${i}`}
|
||||
title={current.title}
|
||||
scale={this.scale}
|
||||
scrollX={this.scrollX}
|
||||
|
||||
@ -70,7 +70,7 @@ export class OutlineSelecting extends Component {
|
||||
return (
|
||||
<Fragment>
|
||||
{selecting.map(node => {
|
||||
const instances = this.host.getComponentInstance(node);
|
||||
const instances = this.host.getComponentInstances(node);
|
||||
if (!instances || instances.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -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<SimulatorProps> {
|
||||
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<SimulatorProps> {
|
||||
}
|
||||
|
||||
@obx.ref _props: SimulatorProps = {};
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
setProps(props: SimulatorProps) {
|
||||
this._props = props;
|
||||
}
|
||||
@ -196,7 +214,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
// 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<SimulatorProps> {
|
||||
// cause edit
|
||||
doc.addEventListener('dblclick', (e: MouseEvent) => {
|
||||
// TODO:
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -271,8 +288,9 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
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<SimulatorProps> {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
setSuspense(suspended: boolean) {
|
||||
if (suspended) {
|
||||
if (this.disableHovering) {
|
||||
@ -312,35 +333,61 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
}
|
||||
}
|
||||
|
||||
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<ReactInstance> | 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<SimulatorProps> {
|
||||
}
|
||||
|
||||
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<SimulatorProps> {
|
||||
}
|
||||
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<Element | Text> | 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<SimulatorProps> {
|
||||
}
|
||||
|
||||
// #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<SimulatorProps> {
|
||||
} 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<SimulatorProps> {
|
||||
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<SimulatorProps> {
|
||||
}
|
||||
}
|
||||
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<SimulatorProps> {
|
||||
}
|
||||
}
|
||||
|
||||
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<INode | NodeData> = dragTarget.nodes || (dragTarget as NodeDatasDragTarget).data;
|
||||
checkNesting(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean {
|
||||
const items: Array<Node | NodeSchema> = 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<INode | NodeData> = dragTarget.nodes || (dragTarget as NodeDatasDragTarget).data;
|
||||
checkDropTarget(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean {
|
||||
const items: Array<Node | NodeSchema> = 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);
|
||||
}
|
||||
|
||||
@ -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<ReactInstance> | null {
|
||||
return getClosestNodeInstance(from, nodeId);
|
||||
}
|
||||
|
||||
findDOMNodes(instance: ReactInstance): Array<Element | Text> | 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<ReactInstance> | 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<ReactInstance> | 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);
|
||||
}
|
||||
|
||||
@ -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<Element | Text>) {
|
||||
if (fiber) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 } {
|
||||
|
||||
@ -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<Element | Text> | null {
|
||||
if (!this.simulator) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isElement(instance)) {
|
||||
return [instance];
|
||||
}
|
||||
|
||||
return this.simulator.findDOMNodes(instance);
|
||||
}
|
||||
|
||||
private _opened: boolean = true;
|
||||
private _suspensed: boolean = false;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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 === '';
|
||||
}
|
||||
|
||||
@ -54,8 +54,8 @@ export default class Node {
|
||||
protected _directives?: Props<Node>;
|
||||
protected _extras?: Props<Node>;
|
||||
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<Node> | 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;
|
||||
}
|
||||
|
||||
@ -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<string, Prop> | null = null;
|
||||
@obx.val private _items: Prop[] | null = null;
|
||||
@obx.val private _maps: Map<string, Prop> | null = null;
|
||||
@computed private get items(): Prop[] | null {
|
||||
let _items: any;
|
||||
untracked(() => {
|
||||
|
||||
@ -11,7 +11,7 @@ export type UNSET = typeof UNSET;
|
||||
export default class Props<O = any> implements IPropParent {
|
||||
readonly id = uniqueId('props');
|
||||
@obx.val private items: Prop[] = [];
|
||||
@obx.ref private get maps(): Map<string, Prop> {
|
||||
@computed private get maps(): Map<string, Prop> {
|
||||
const maps = new Map();
|
||||
if (this.items.length > 0) {
|
||||
this.items.forEach(prop => {
|
||||
@ -36,7 +36,7 @@ export default class Props<O = any> implements IPropParent {
|
||||
/**
|
||||
* 元素个数
|
||||
*/
|
||||
get size() {
|
||||
@computed get size() {
|
||||
return this.items.length;
|
||||
}
|
||||
|
||||
|
||||
@ -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<Prop> = new Set();
|
||||
@obx.ref private get maps(): Map<string, Prop> {
|
||||
@computed private get maps(): Map<string, Prop> {
|
||||
const maps = new Map();
|
||||
if (this.space.size > 0) {
|
||||
this.space.forEach(prop => {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -21,7 +21,6 @@ export default class Hovering {
|
||||
}
|
||||
|
||||
hover(node: Node | null) {
|
||||
console.info(node);
|
||||
this._current = node;
|
||||
}
|
||||
|
||||
|
||||
@ -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<ClientRect | DOMRect> & {
|
||||
export interface CanvasPoint {
|
||||
canvasX: number;
|
||||
canvasY: number;
|
||||
}
|
||||
|
||||
export type Rects = DOMRect[] & {
|
||||
elements: Array<Element | Text>;
|
||||
};
|
||||
|
||||
export type Rect = (ClientRect | DOMRect) & {
|
||||
export type Rect = DOMRect & {
|
||||
elements: Array<Element | Text>;
|
||||
computed?: boolean;
|
||||
};
|
||||
|
||||
@ -100,16 +100,6 @@ export interface ISimulator<P = object> extends ISensor {
|
||||
*/
|
||||
clearState(): void;
|
||||
|
||||
/**
|
||||
* 在模拟器拖拽定位
|
||||
*/
|
||||
locate(e: LocateEvent): any;
|
||||
|
||||
/**
|
||||
* 给 event 打补丁,添加 canvasX, globalX 等信息,用于拖拽
|
||||
*/
|
||||
fixEvent(e: LocateEvent): LocateEvent;
|
||||
|
||||
// #endregion
|
||||
|
||||
/**
|
||||
@ -128,13 +118,15 @@ export interface ISimulator<P = object> 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<T = ComponentInstance> {
|
||||
nodeId: string;
|
||||
instance: ComponentInstance;
|
||||
instance: T;
|
||||
node?: Node | null;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user