This commit is contained in:
kangwei 2020-02-19 09:03:53 +08:00
parent ada3fe12ec
commit 4d4cfab271
23 changed files with 561 additions and 476 deletions

View File

@ -1,22 +0,0 @@
辅助类
对齐线
插入指示 insertion 竖线 横线 插入块 禁止插入块
幽灵替身 ghost
聚焦编辑指示
插入指示 insertion 竖线 横线 插入块 禁止插入块
竖线:红色,绿色
横线:红色,绿色
插入块:透明绿色,透明红色
投放指示线
cover
轮廓服务
悬停指示线 xray mode?
选中指示线
投放指示线
透视线 x-ray

View File

@ -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)} />;
}
}

View File

@ -3,9 +3,10 @@ import { Component } from 'react';
import { OutlineHovering } from './outline-hovering'; import { OutlineHovering } from './outline-hovering';
import { SimulatorContext } from '../context'; import { SimulatorContext } from '../context';
import { SimulatorHost } from '../host'; import { SimulatorHost } from '../host';
import { OutlineSelecting } from './outline-selecting';
import { InsertionView } from './insertion';
import './auxiliary.less'; import './auxiliary.less';
import './outlines.less'; import './outlines.less';
import { OutlineSelecting } from './outline-selecting';
@observer @observer
export class AuxiliaryView extends Component { 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)` }}> <div className="lc-auxiliary" style={{ transform: `translate(${-scrollX * scale}px,${-scrollY * scale}px)` }}>
<OutlineHovering key="hovering" /> <OutlineHovering key="hovering" />
<OutlineSelecting key="selecting" /> <OutlineSelecting key="selecting" />
<InsertionView key="insertion" />
</div> </div>
); );
} }

View File

@ -1,4 +1,4 @@
.my-insertion { .lc-insertion {
position: absolute; position: absolute;
top: -1.5px; top: -1.5px;
left: 0; left: 0;

View File

@ -1,34 +1,35 @@
import { Component } from 'react'; import { Component } from 'react';
import { observer } from '@ali/recore'; import { computed } from '@recore/obx';
import { getCurrentDocument } from '../../globals'; 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 './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 { interface InsertionData {
edge?: Rect; edge?: DOMRect;
insertType?: string; insertType?: string;
vertical?: boolean; vertical?: boolean;
nearRect?: Rect; nearRect?: Rect;
coverRect?: Rect; coverRect?: DOMRect;
} }
/** /**
* (INode) * (INode)
*/ */
function processChildrenDetail( function processChildrenDetail(
doc: DocumentContext, sim: ISimulator,
target: INodeParent, target: NodeParent,
detail: LocationChildrenDetail, detail: LocationChildrenDetail,
): InsertionData { ): InsertionData {
const edge = doc.computeRect(target); let edge = detail.edge || null;
if (edge) {
edge = sim.computeRect(target);
}
if (!edge) { if (!edge) {
return {}; return {};
} }
@ -38,15 +39,9 @@ function processChildrenDetail(
insertType: 'before', insertType: 'before',
}; };
if (isConditionFlow(target)) {
ret.insertType = 'cover';
ret.coverRect = edge;
return ret;
}
if (detail.near) { if (detail.near) {
const { node, pos, rect, align } = 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.vertical = align ? align === 'V' : isVertical(ret.nearRect);
ret.insertType = pos; ret.insertType = pos;
return ret; return ret;
@ -55,10 +50,10 @@ function processChildrenDetail(
// from outline-tree: has index, but no near // from outline-tree: has index, but no near
// TODO: think of shadowNode & ConditionFlow // TODO: think of shadowNode & ConditionFlow
const { index } = detail; const { index } = detail;
let nearNode = getChildAt(target, index); let nearNode = target.children.get(index);
if (!nearNode) { if (!nearNode) {
// index = 0, eg. nochild, // index = 0, eg. nochild,
nearNode = getChildAt(target, index > 0 ? index - 1 : 0); nearNode = target.children.get(index > 0 ? index - 1 : 0);
if (!nearNode) { if (!nearNode) {
ret.insertType = 'cover'; ret.insertType = 'cover';
ret.coverRect = edge; ret.coverRect = edge;
@ -67,7 +62,7 @@ function processChildrenDetail(
ret.insertType = 'after'; ret.insertType = 'after';
} }
if (nearNode) { if (nearNode) {
ret.nearRect = doc.computeRect(nearNode); ret.nearRect = sim.computeRect(nearNode);
ret.vertical = isVertical(ret.nearRect); ret.vertical = isVertical(ret.nearRect);
} }
return ret; return ret;
@ -77,44 +72,56 @@ function processChildrenDetail(
* detail "坐标" * detail "坐标"
*/ */
function processDetail({ target, detail, document }: Location): InsertionData { function processDetail({ target, detail, document }: Location): InsertionData {
const sim = document.simulator;
if (!sim) {
return {};
}
if (isLocationChildrenDetail(detail)) { if (isLocationChildrenDetail(detail)) {
return processChildrenDetail(document, target, detail); return processChildrenDetail(sim, target, detail);
} else { } else {
// TODO: others... // 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 } : {}; return edge ? { edge, insertType: 'cover', coverRect: edge } : {};
} }
} }
@observer @observer
export class InsertionView extends Component { export class InsertionView extends Component {
static contextType = SimulatorContext;
@computed get host(): SimulatorHost {
return this.context;
}
shouldComponentUpdate() { shouldComponentUpdate() {
return false; return false;
} }
render() { render() {
const doc = getCurrentDocument(); const loc = this.host.document.dropLocation;
if (!doc || !doc.dropLocation) { if (!loc) {
return null; return null;
} }
const { scale, scrollTarget } = doc.viewport; const { scale, scrollX, scrollY } = this.host.viewport;
const sx = scrollTarget!.left; const { edge, insertType, coverRect, nearRect, vertical } = processDetail(loc);
const sy = scrollTarget!.top;
const { edge, insertType, coverRect, nearRect, vertical } = processDetail(doc.dropLocation);
if (!edge) { if (!edge) {
return null; return null;
} }
let className = 'my-insertion'; let className = 'lc-insertion';
const style: any = {}; const style: any = {};
let x: number; let x: number;
let y: number; let y: number;
if (insertType === 'cover') { if (insertType === 'cover') {
className += ' cover'; className += ' cover';
x = (coverRect!.left + sx) * scale; x = (coverRect!.left + scrollX) * scale;
y = (coverRect!.top + sy) * scale; y = (coverRect!.top + scrollY) * scale;
style.width = coverRect!.width * scale; style.width = coverRect!.width * scale;
style.height = coverRect!.height * scale; style.height = coverRect!.height * scale;
} else { } else {
@ -123,12 +130,12 @@ export class InsertionView extends Component {
} }
if (vertical) { if (vertical) {
className += ' vertical'; className += ' vertical';
x = ((insertType === 'before' ? nearRect.left : nearRect.right) + sx) * scale; x = ((insertType === 'before' ? nearRect.left : nearRect.right) + scrollX) * scale;
y = (nearRect.top + sy) * scale; y = (nearRect.top + scrollY) * scale;
style.height = nearRect!.height * scale; style.height = nearRect!.height * scale;
} else { } else {
x = (nearRect.left + sx) * scale; x = (nearRect.left + scrollX) * scale;
y = ((insertType === 'before' ? nearRect.top : nearRect.bottom) + sy) * scale; y = ((insertType === 'before' ? nearRect.top : nearRect.bottom) + scrollY) * scale;
style.width = nearRect.width * scale; style.width = nearRect.width * scale;
} }
} }

View File

@ -75,7 +75,7 @@ export class OutlineHovering extends Component {
if (!current) { if (!current) {
return <Fragment />; return <Fragment />;
} }
const instances = host.getComponentInstance(current); const instances = host.getComponentInstances(current);
if (!instances || instances.length < 1) { if (!instances || instances.length < 1) {
return <Fragment />; return <Fragment />;
} }
@ -83,7 +83,7 @@ export class OutlineHovering extends Component {
if (instances.length === 1) { if (instances.length === 1) {
return ( return (
<OutlineHoveringInstance <OutlineHoveringInstance
key="line-s" key="line-h"
title={current.title} title={current.title}
scale={this.scale} scale={this.scale}
scrollX={this.scrollX} scrollX={this.scrollX}
@ -96,7 +96,7 @@ export class OutlineHovering extends Component {
<Fragment> <Fragment>
{instances.map((inst, i) => ( {instances.map((inst, i) => (
<OutlineHoveringInstance <OutlineHoveringInstance
key={`line-${i}`} key={`line-h-${i}`}
title={current.title} title={current.title}
scale={this.scale} scale={this.scale}
scrollX={this.scrollX} scrollX={this.scrollX}

View File

@ -70,7 +70,7 @@ export class OutlineSelecting extends Component {
return ( return (
<Fragment> <Fragment>
{selecting.map(node => { {selecting.map(node => {
const instances = this.host.getComponentInstance(node); const instances = this.host.getComponentInstances(node);
if (!instances || instances.length < 1) { if (!instances || instances.length < 1) {
return null; return null;
} }

View File

@ -1,19 +1,38 @@
import { obx, autorun, computed } from '@recore/obx'; 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 Viewport from './viewport';
import { createSimulator } from './create-simulator'; import { createSimulator } from './create-simulator';
import { SimulatorRenderer } from '../renderer/renderer'; 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 DocumentModel from '../../../designer/document/document-model';
import ResourceConsumer from './resource-consumer'; import ResourceConsumer from './resource-consumer';
import { AssetLevel, Asset, assetBundle, assetItem, AssetType } from '../utils/asset'; import { AssetLevel, Asset, assetBundle, assetItem, AssetType } from '../utils/asset';
import { DragObjectType, isShaken, LocateEvent, DragNodeObject, DragNodeDataObject } from '../../../designer/helper/dragon'; import {
import { LocationData } from '../../../designer/helper/location'; DragObjectType,
import { NodeData } from '../../../designer/schema'; 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 { ComponentDescriptionSpec } from '../../../designer/component-config';
import { ReactInstance } from 'react'; import { ReactInstance } from 'react';
import { setNativeSelection } from '../../../designer/helper/navtive-selection'; import { setNativeSelection } from '../../../designer/helper/navtive-selection';
import cursor from '../../../designer/helper/cursor'; import cursor from '../../../designer/helper/cursor';
import { isRootNode } from '../../../designer/document/node/root-node';
export interface SimulatorProps { export interface SimulatorProps {
// 从 documentModel 上获取 // 从 documentModel 上获取
@ -53,15 +72,11 @@ const defaultDepends = [
export class SimulatorHost implements ISimulator<SimulatorProps> { export class SimulatorHost implements ISimulator<SimulatorProps> {
readonly isSimulator = true; readonly isSimulator = true;
constructor(readonly document: DocumentModel) {} constructor(readonly document: DocumentModel) {}
readonly designer = this.document.designer; readonly designer = this.document.designer;
private _sensorAvailable: boolean = true;
get sensorAvailable(): boolean {
return this._sensorAvailable;
}
@computed get device(): string | undefined { @computed get device(): string | undefined {
// 根据 device 不同来做画布外框样式变化 渲染时可选择不同组件 // 根据 device 不同来做画布外框样式变化 渲染时可选择不同组件
// renderer 依赖 // renderer 依赖
@ -92,6 +107,9 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
} }
@obx.ref _props: SimulatorProps = {}; @obx.ref _props: SimulatorProps = {};
/**
* @see ISimulator
*/
setProps(props: SimulatorProps) { setProps(props: SimulatorProps) {
this._props = props; this._props = props;
} }
@ -196,7 +214,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
// TODO: think of lock when edit a node // TODO: think of lock when edit a node
// 事件路由 // 事件路由
doc.addEventListener('mousedown', (downEvent: MouseEvent) => { doc.addEventListener('mousedown', (downEvent: MouseEvent) => {
const nodeInst = documentModel.getNodeInstanceFromElement(downEvent.target as Element); const nodeInst = this.getNodeInstanceFromElement(downEvent.target as Element);
if (!nodeInst?.node) { if (!nodeInst?.node) {
selection.clear(); selection.clear();
return; return;
@ -256,7 +274,6 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
// cause edit // cause edit
doc.addEventListener('dblclick', (e: MouseEvent) => { doc.addEventListener('dblclick', (e: MouseEvent) => {
// TODO: // TODO:
}); });
} }
@ -271,8 +288,9 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
if (!hovering.enable) { if (!hovering.enable) {
return; return;
} }
const nodeInst = this.document.getNodeInstanceFromElement(e.target as Element); const nodeInst = this.getNodeInstanceFromElement(e.target as Element);
// TODO: enhance only hover one instance // TODO: enhance only hover one instance
console.info(nodeInst);
hovering.hover(nodeInst?.node || null); hovering.hover(nodeInst?.node || null);
e.stopPropagation(); e.stopPropagation();
}; };
@ -298,6 +316,9 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
}; };
} }
/**
* @see ISimulator
*/
setSuspense(suspended: boolean) { setSuspense(suspended: boolean) {
if (suspended) { if (suspended) {
if (this.disableHovering) { 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 { describeComponent(component: Component): ComponentDescriptionSpec {
throw new Error('Method not implemented.'); 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 { getComponentContext(node: Node): object {
throw new Error('Method not implemented.'); 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 renderer = this.renderer!;
const elements = renderer.findDOMNodes(instance); const elements = renderer.findDOMNodes(instance);
if (!elements) { if (!elements) {
@ -348,10 +395,12 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
} }
let rects: DOMRect[] | undefined; 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) { while (true) {
if (!rects || rects.length < 1) { if (!rects || rects.length < 1) {
const elem = elements.pop(); const elem = elems.pop();
if (!elem) { if (!elem) {
break; break;
} }
@ -372,30 +421,62 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
} }
if (rect.left < last.x) { if (rect.left < last.x) {
last.x = rect.left; last.x = rect.left;
computed = true;
} }
if (rect.top < last.y) { if (rect.top < last.y) {
last.y = rect.top; last.y = rect.top;
computed = true;
} }
if (rect.right > last.r) { if (rect.right > last.r) {
last.r = rect.right; last.r = rect.right;
computed = true;
} }
if (rect.bottom > last.b) { if (rect.bottom > last.b) {
last.b = rect.bottom; last.b = rect.bottom;
computed = true;
} }
} }
if (last) { 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; return null;
} }
/**
* @see ISimulator
*/
findDOMNodes(instance: ReactInstance): Array<Element | Text> | null { findDOMNodes(instance: ReactInstance): Array<Element | Text> | null {
return this._renderer?.findDOMNodes(instance) || 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; private tryScrollAgain: number | null = null;
/**
* @see ISimulator
*/
scrollToNode(node: Node, detail?: any, tryTimes = 0) { scrollToNode(node: Node, detail?: any, tryTimes = 0) {
this.tryScrollAgain = null; this.tryScrollAgain = null;
if (this.sensing) { if (this.sensing) {
@ -451,80 +532,251 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
} }
// #region ========= drag and drop helpers ============= // #region ========= drag and drop helpers =============
/**
* @see ISimulator
*/
setNativeSelection(enableFlag: boolean) { setNativeSelection(enableFlag: boolean) {
setNativeSelection(enableFlag); this.renderer?.setNativeSelection(enableFlag);
} }
/**
* @see ISimulator
*/
setDraggingState(state: boolean) { setDraggingState(state: boolean) {
cursor.setDragging(state); this.renderer?.setDraggingState(state);
} }
/**
* @see ISimulator
*/
setCopyState(state: boolean) { setCopyState(state: boolean) {
cursor.setCopy(state); this.renderer?.setCopyState(state);
} }
/**
* @see ISimulator
*/
clearState() { clearState() {
cursor.release(); this.renderer?.clearState();
} }
private _sensorAvailable: boolean = true;
/**
* @see ISensor
*/
get sensorAvailable(): boolean {
return this._sensorAvailable;
}
/**
* @see ISensor
*/
fixEvent(e: LocateEvent): LocateEvent { fixEvent(e: LocateEvent): LocateEvent {
/*
if (e.fixed) { if (e.fixed) {
return e; 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; return e;
} }
/**
* @see ISensor
*/
isEnter(e: LocateEvent): boolean { isEnter(e: LocateEvent): boolean {
return false; /* const rect = this.viewport.bounds;
const rect = this.bounds;
return e.globalY >= rect.top && e.globalY <= rect.bottom && e.globalX >= rect.left && e.globalX <= rect.right; return e.globalY >= rect.top && e.globalY <= rect.bottom && e.globalX >= rect.left && e.globalX <= rect.right;
*/
} }
private sensing: boolean = false; private sensing: boolean = false;
/**
* @see ISensor
*/
deactiveSensor() { deactiveSensor() {
this.sensing = false; this.sensing = false;
this.scroller.cancel(); 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 { getDropTarget(e: LocateEvent): NodeParent | LocationData | null {
/* const { target, dragObject } = e;
const { target, dragTarget } = e; const isAny = isDragAnyObject(dragObject);
const isAny = isAnyDragTarget(dragTarget);
let container: any; let container: any;
if (target) { if (target) {
const ref = this.document.getNodeFromElement(target as Element); const ref = this.getNodeInstanceFromElement(target);
if (ref) { if (ref?.node) {
container = ref; e.targetInstance = ref.instance;
e.targetNode = ref.node;
container = ref.node;
} else if (isAny) { } else if (isAny) {
return null; return null;
} else { } else {
container = this.document.view; container = this.document.rootNode;
} }
} else if (isAny) { } else if (isAny) {
return null; return null;
} else { } else {
container = this.document.view; container = this.document.rootNode;
} }
if (!isElementNode(container) && !isRootNode(container)) { if (!isNodeParent(container) && !isRootNode(container)) {
container = container.parent; container = container.parent;
} }
// use spec container to accept specialData
if (isAny) { if (isAny) {
while (container) { // TODO: use spec container to accept specialData
if (isRootNode(container)) {
return null;
}
const locationData = this.acceptAnyData(container, e);
if (locationData) {
return locationData;
}
container = container.parent;
}
return null; return null;
} }
@ -546,7 +798,8 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
} else { } else {
container = container.parent; container = container.parent;
} }
} else if (res === AT_CHILD) { }
/* else if (res === AT_CHILD) {
if (!upward) { if (!upward) {
upward = container.parent; upward = container.parent;
} }
@ -555,31 +808,30 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
container = upward; container = upward;
upward = null; upward = null;
} }
} else if (isNode(res)) { }*/
else if (isNode(res)) {
console.info('res', res);
container = res; container = res;
upward = null; upward = null;
} }
}*/ }
return null; return null;
} }
acceptNodes(container: Node, e: LocateEvent) { acceptNodes(container: NodeParent, e: LocateEvent) {
/* const { dragObject } = e;
const { dragTarget } = e;
if (isRootNode(container)) { 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 (!config.isContainer) {
if (!proto.isContainer && !acceptable) {
return false; return false;
} }
// check is contains, get common parent // check is contains, get common parent
if (isNodesDragTarget(dragTarget)) { if (isDragNodeObject(dragObject)) {
const nodes = dragTarget.nodes; const nodes = dragObject.nodes;
let i = nodes.length; let i = nodes.length;
let p: any = container; let p: any = container;
while (i-- > 0) { while (i-- > 0) {
@ -588,41 +840,16 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
} }
} }
if (p !== container) { if (p !== container) {
return p || this.document.view; return p || this.document.rootNode;
} }
} }
// first use accept return this.checkNesting(container, dragObject as any);
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);
*/
} }
/*
getNearByContainer(container: NodeParent, e: LocateEvent) { getNearByContainer(container: NodeParent, e: LocateEvent) {
/*
const children = container.children; const children = container.children;
if (!children || children.length < 1) { if (!children || children.length < 1) {
return null; return null;
@ -656,195 +883,76 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
} }
} }
return nearBy;*/ return nearBy;
} }
*/
locate(e: LocateEvent): any { checkNesting(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean {
/* const items: Array<Node | NodeSchema> = dragObject.nodes || (dragObject as DragNodeDataObject).data;
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;
return items.every(item => this.checkNestingDown(dropTarget, item)); return items.every(item => this.checkNestingDown(dropTarget, item));
*/
} }
checkDropTarget(dropTarget: Node, dragTarget: DragNodeObject | DragNodeDataObject): boolean { checkDropTarget(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean {
return false; const items: Array<Node | NodeSchema> = dragObject.nodes || (dragObject as DragNodeDataObject).data;
/*
const items: Array<INode | NodeData> = dragTarget.nodes || (dragTarget as NodeDatasDragTarget).data;
return items.every(item => this.checkNestingUp(dropTarget, item)); return items.every(item => this.checkNestingUp(dropTarget, item));
*/
} }
checkNestingUp(parent: NodeParent, target: NodeData | Node): boolean { checkNestingUp(parent: NodeParent, target: NodeSchema | Node): boolean {
/* if (isNode(target) || isNodeSchema(target)) {
if (isElementNode(target) || isElementData(target)) { const config = isNode(target) ? target.componentConfig : this.designer.getComponentConfig(target.componentName);
const proto = isElementNode(target) if (config) {
? target.prototype return config.checkNestingUp(target, parent);
: this.document.getPrototypeByTagNameOrURI(target.tagName, target.uri);
if (proto) {
return proto.checkNestingUp(target, parent);
} }
}*/ }
return true; return true;
} }
checkNestingDown(parent: NodeParent, target: NodeData | Node): boolean { checkNestingDown(parent: NodeParent, target: NodeSchema | Node): boolean {
/* const config = parent.componentConfig;
const proto = parent.prototype; return config.checkNestingDown(parent, target) && this.checkNestingUp(parent, target);
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;
} }
// #endregion // #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);
}

View File

@ -8,10 +8,12 @@ import { getClientRects } from '../../../utils/get-client-rects';
import { Asset } from '../utils/asset'; import { Asset } from '../utils/asset';
import loader from '../utils/loader'; import loader from '../utils/loader';
import { ComponentDescriptionSpec } from '../../../designer/component-config'; 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 { isESModule } from '../../../utils/is-es-module';
import { NodeInstance } from '../../../designer/simulator'; import { NodeInstance } from '../../../designer/simulator';
import { isElement } from '../../../utils/is-element'; import { isElement } from '../../../utils/is-element';
import cursor from '../../../designer/helper/cursor';
import { setNativeSelection } from '../../../designer/helper/navtive-selection';
export class SimulatorRenderer { export class SimulatorRenderer {
readonly isSimulatorRenderer = true; readonly isSimulatorRenderer = true;
@ -133,7 +135,9 @@ export class SimulatorRenderer {
let instances = this.instancesMap.get(id); let instances = this.instancesMap.get(id);
if (instances) { if (instances) {
instances = instances.filter(checkInstanceMounted); instances = instances.filter(checkInstanceMounted);
instances.push(instance); if (!instances.includes(instance)) {
instances.push(instance);
}
instancesMap.set(id, instances); instancesMap.set(id, instances);
} else { } else {
instancesMap.set(id, [instance]); instancesMap.set(id, [instance]);
@ -144,12 +148,12 @@ export class SimulatorRenderer {
this.ctxMap.set(id, ctx); this.ctxMap.set(id, ctx);
} }
getComponentInstance(id: string): ReactInstance[] | null { getComponentInstances(id: string): ReactInstance[] | null {
return this.instancesMap.get(id) || null; return this.instancesMap.get(id) || null;
} }
getClosestNodeInstance(element: Element): NodeInstance | null { getClosestNodeInstance(from: ReactInstance, nodeId?: string): NodeInstance<ReactInstance> | null {
return getClosestNodeInstance(element); return getClosestNodeInstance(from, nodeId);
} }
findDOMNodes(instance: ReactInstance): Array<Element | Text> | null { findDOMNodes(instance: ReactInstance): Array<Element | Text> | null {
@ -160,6 +164,28 @@ export class SimulatorRenderer {
return getClientRects(element); 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; private _running: boolean = false;
run() { run() {
if (this._running) { if (this._running) {
@ -260,34 +286,44 @@ function cacheReactKey(el: Element): Element {
const SYMBOL_VNID = Symbol('_LCNodeId'); const SYMBOL_VNID = Symbol('_LCNodeId');
function getClosestNodeInstance(element: Element): NodeInstance | null { function getClosestNodeInstance(from: ReactInstance, specId?: string): NodeInstance<ReactInstance> | null {
let el: any = element; let el: any = from;
if (el) { if (el) {
el = cacheReactKey(el); if (isElement(el)) {
el = cacheReactKey(el);
} else {
return getNodeInstance(el[FIBER_KEY], specId);
}
} }
while (el) { while (el) {
if (SYMBOL_VNID in el) { if (SYMBOL_VNID in el) {
return { const nodeId = el[SYMBOL_VNID];
nodeId: el[SYMBOL_VNID], if (!specId || specId === nodeId) {
instance: el, return {
}; nodeId: nodeId,
instance: el,
};
}
} }
// get fiberNode from element // get fiberNode from element
if (el[REACT_KEY]) { if (el[REACT_KEY]) {
return getNodeInstance(el[REACT_KEY]); return getNodeInstance(el[REACT_KEY], specId);
} }
el = el.parentElement; el = el.parentElement;
} }
return null; return null;
} }
function getNodeInstance(fiberNode: any): NodeInstance | null { function getNodeInstance(fiberNode: any, specId?: string): NodeInstance<ReactInstance> | null {
const instance = fiberNode.stateNode; const instance = fiberNode.stateNode;
if (instance && SYMBOL_VNID in instance) { if (instance && SYMBOL_VNID in instance) {
return { const nodeId = instance[SYMBOL_VNID];
nodeId: instance[SYMBOL_VNID], if (!specId || specId === nodeId) {
instance, return {
}; nodeId: nodeId,
instance: instance,
};
}
} }
return getNodeInstance(fiberNode.return); return getNodeInstance(fiberNode.return);
} }

View File

@ -2,7 +2,7 @@ import { ReactInstance } from 'react';
import { isElement } from '../../../utils/is-element'; import { isElement } from '../../../utils/is-element';
import { isDOMNode } from '../../../utils/is-dom-node'; import { isDOMNode } from '../../../utils/is-dom-node';
const FIBER_KEY = '_reactInternalFiber'; export const FIBER_KEY = '_reactInternalFiber';
function elementsFromFiber(fiber: any, elements: Array<Element | Text>) { function elementsFromFiber(fiber: any, elements: Array<Element | Text>) {
if (fiber) { if (fiber) {

View File

@ -280,7 +280,7 @@ export class ComponentConfig {
} }
private _isContainer?: boolean; private _isContainer?: boolean;
get isContainer(): boolean { get isContainer(): boolean {
return this._isContainer!; return this._isContainer! || this.isRootComponent();
} }
private _isModal?: boolean; private _isModal?: boolean;
get 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) { set spec(spec: ComponentDescriptionSpec) {
this._spec = spec; this._spec = spec;
this.parseSpec(spec); this.parseSpec(spec);

View File

@ -56,7 +56,7 @@ export default class Designer {
}); });
this.dragon.onDrag(e => { this.dragon.onDrag(e => {
console.info(e); console.info('dropLocation', this._dropLocation);
if (this.props?.onDrag) { if (this.props?.onDrag) {
this.props.onDrag(e); this.props.onDrag(e);
} }
@ -247,12 +247,9 @@ export default class Designer {
return this._componentConfigsMap.get(componentName)!; return this._componentConfigsMap.get(componentName)!;
} }
const config = new ComponentConfig({ return new ComponentConfig({
componentName, componentName,
}); });
this._componentConfigsMap.set(componentName, config);
return config;
} }
get componentsMap(): { [key: string]: ComponentDescriptionSpec } { get componentsMap(): { [key: string]: ComponentDescriptionSpec } {

View File

@ -223,59 +223,16 @@ export default class DocumentModel {
// TODO: emit simulator mounted // TODO: emit simulator mounted
} }
/**
* simulator
*/
getComponentInstance(node: Node): ComponentInstance[] | null {
if (this.simulator) {
this.simulator.getComponentInstance(node);
}
return null;
}
getComponent(componentName: string): any { getComponent(componentName: string): any {
return this.simulator!.getComponent(componentName); 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 // TODO: guess componentConfig from component by simulator
return this.designer.getComponentConfig(componentName); 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 _opened: boolean = true;
private _suspensed: boolean = false; private _suspensed: boolean = false;

View File

@ -1,8 +1,9 @@
import Node, { NodeParent } from './node'; import Node, { NodeParent } from './node';
import { NodeData } from '../../schema'; import { NodeData } from '../../schema';
import { obx, computed } from '@recore/obx';
export default class NodeChildren { export default class NodeChildren {
private children: Node[]; @obx.val private children: Node[];
constructor(readonly owner: NodeParent, childrenData: NodeData | NodeData[]) { constructor(readonly owner: NodeParent, childrenData: NodeData | NodeData[]) {
this.children = (Array.isArray(childrenData) ? childrenData : [childrenData]).map(child => { this.children = (Array.isArray(childrenData) ? childrenData : [childrenData]).map(child => {
const node = this.owner.document.createNode(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; return this.children.length;
} }
/** /**
* *
*/ */
isEmpty() { @computed isEmpty() {
return this.size < 1; return this.size < 1;
} }

View File

@ -1,10 +1,10 @@
import { obx } from '@recore/obx'; import { obx, computed } from '@recore/obx';
import { JSExpression, isJSExpression } from '../../schema'; import { JSExpression, isJSExpression } from '../../schema';
export default class NodeContent { export default class NodeContent {
@obx.ref private _value: string | JSExpression = ''; @obx.ref private _value: string | JSExpression = '';
get value(): string | JSExpression { @computed get value(): string | JSExpression {
return this._value; return this._value;
} }
@ -15,7 +15,7 @@ export default class NodeContent {
/** /**
* *
*/ */
get code() { @computed get code() {
if (isJSExpression(this._value)) { if (isJSExpression(this._value)) {
return this._value.value; return this._value.value;
} }
@ -70,14 +70,14 @@ export default class NodeContent {
/** /**
* *
*/ */
isJSExpression(): boolean { @computed isJSExpression(): boolean {
return isJSExpression(this._value); return isJSExpression(this._value);
} }
/** /**
* *
*/ */
isEmpty() { @computed isEmpty() {
if (isJSExpression(this._value)) { if (isJSExpression(this._value)) {
return this._value.value === ''; return this._value.value === '';
} }

View File

@ -54,8 +54,8 @@ export default class Node {
protected _directives?: Props<Node>; protected _directives?: Props<Node>;
protected _extras?: Props<Node>; protected _extras?: Props<Node>;
protected _children: NodeChildren | NodeContent; protected _children: NodeChildren | NodeContent;
private _parent: NodeParent | null = null; @obx.ref private _parent: NodeParent | null = null;
private _zLevel = 0; @obx.ref private _zLevel = 0;
get props(): Props<Node> | undefined { get props(): Props<Node> | undefined {
return this._props; return this._props;
} }
@ -165,15 +165,18 @@ export default class Node {
/** /**
* *
*/ */
@obx.ref get component(): Component { @obx.ref get component(): Component | null {
return this.document.getComponent(this.componentName); if (this.isNodeParent) {
return this.document.getComponent(this.componentName);
}
return null;
} }
/** /**
* *
*/ */
@obx.ref get componentConfig(): ComponentConfig { @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 { @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) { if (!this.parent) {
return -1; return -1;
} }

View File

@ -125,7 +125,7 @@ export default class Prop implements IPropParent {
* *
* JSExpresion | JSSlot * JSExpresion | JSSlot
*/ */
isContainJSExpression(): boolean { @computed isContainJSExpression(): boolean {
const type = this._type; const type = this._type;
if (type === 'expression') { if (type === 'expression') {
return true; return true;
@ -142,12 +142,12 @@ export default class Prop implements IPropParent {
/** /**
* JSON * JSON
*/ */
isJSON() { @computed isJSON() {
return !this.isContainJSExpression(); return !this.isContainJSExpression();
} }
private _items: Prop[] | null = null; @obx.val private _items: Prop[] | null = null;
private _maps: Map<string, Prop> | null = null; @obx.val private _maps: Map<string, Prop> | null = null;
@computed private get items(): Prop[] | null { @computed private get items(): Prop[] | null {
let _items: any; let _items: any;
untracked(() => { untracked(() => {

View File

@ -11,7 +11,7 @@ export type UNSET = typeof UNSET;
export default class Props<O = any> implements IPropParent { export default class Props<O = any> implements IPropParent {
readonly id = uniqueId('props'); readonly id = uniqueId('props');
@obx.val private items: Prop[] = []; @obx.val private items: Prop[] = [];
@obx.ref private get maps(): Map<string, Prop> { @computed private get maps(): Map<string, Prop> {
const maps = new Map(); const maps = new Map();
if (this.items.length > 0) { if (this.items.length > 0) {
this.items.forEach(prop => { 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; return this.items.length;
} }

View File

@ -1,10 +1,10 @@
import { obx, autorun, untracked } from '@recore/obx'; import { obx, autorun, untracked, computed } from '@recore/obx';
import Prop, { IPropParent } from './prop'; import Prop, { IPropParent } from './prop';
export type PendingItem = Prop[]; export type PendingItem = Prop[];
export default class StashSpace implements IPropParent { export default class StashSpace implements IPropParent {
@obx.val private space: Set<Prop> = new Set(); @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(); const maps = new Map();
if (this.space.size > 0) { if (this.space.size > 0) {
this.space.forEach(prop => { this.space.forEach(prop => {

View File

@ -2,8 +2,8 @@ import { EventEmitter } from 'events';
import { obx } from '@recore/obx'; import { obx } from '@recore/obx';
import Location from './location'; import Location from './location';
import DocumentModel from '../document/document-model'; import DocumentModel from '../document/document-model';
import { NodeData } from '../schema'; import { NodeData, NodeSchema } from '../schema';
import { ISimulator, isSimulator } from '../simulator'; import { ISimulator, isSimulator, ComponentInstance } from '../simulator';
import Node from '../document/node/node'; import Node from '../document/node/node';
import Designer from '../designer'; import Designer from '../designer';
import { setNativeSelection } from './navtive-selection'; import { setNativeSelection } from './navtive-selection';
@ -43,11 +43,14 @@ export interface LocateEvent {
/** /**
* *
*/ */
document?: DocumentModel; documentModel?: DocumentModel;
/** /**
* canvasX,canvasY, * canvasX,canvasY,
*/ */
fixed?: true; fixed?: true;
targetNode?: Node;
targetInstance?: ComponentInstance;
} }
/** /**
@ -89,8 +92,8 @@ export interface DragNodeObject {
} }
export interface DragNodeDataObject { export interface DragNodeDataObject {
type: DragObjectType.NodeData; type: DragObjectType.NodeData;
data: NodeData | NodeData[]; data: NodeSchema | NodeSchema[];
maps?: { [tagName: string]: string }; maps?: { [componentName: string]: string };
thumbnail?: string; thumbnail?: string;
description?: string; description?: string;
[extra: string]: any; [extra: string]: any;
@ -279,7 +282,7 @@ export default class Dragon {
if (this._dragging) { if (this._dragging) {
this._dragging = false; this._dragging = false;
try { try {
this.emitter.emit('dragend', { dragTarget: dragObject, copy: this.isCopyState() }); this.emitter.emit('dragend', { dragObject, copy: this.isCopyState() });
} catch (ex) { } catch (ex) {
exception = ex; exception = ex;
} }
@ -339,6 +342,8 @@ export default class Dragon {
const g = srcSim.viewport.toGlobalPoint(e); const g = srcSim.viewport.toGlobalPoint(e);
evt.globalX = g.clientX; evt.globalX = g.clientX;
evt.globalY = g.clientY; evt.globalY = g.clientY;
evt.canvasX = e.clientX;
evt.canvasY = e.clientY;
evt.sensor = srcSim; evt.sensor = srcSim;
} else { } else {
// this condition will not happen, just make sure ts ok // this condition will not happen, just make sure ts ok
@ -352,10 +357,12 @@ export default class Dragon {
const sourceSensor = getSourceSensor(dragObject); const sourceSensor = getSourceSensor(dragObject);
const sensors: ISensor[] = (masterSensors as ISensor[]).concat(this.sensors); const sensors: ISensor[] = (masterSensors as ISensor[]).concat(this.sensors);
const chooseSensor = (e: LocateEvent) => { 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 (!sensor) {
if (lastSensor) { if (lastSensor) {
sensor = lastSensor; sensor = lastSensor;
} else if (e.sensor) {
sensor = e.sensor;
} else if (sourceSensor) { } else if (sourceSensor) {
sensor = sourceSensor; sensor = sourceSensor;
} }

View File

@ -21,7 +21,6 @@ export default class Hovering {
} }
hover(node: Node | null) { hover(node: Node | null) {
console.info(node);
this._current = node; this._current = node;
} }

View File

@ -14,6 +14,7 @@ export enum LocationDetailType {
export interface LocationChildrenDetail { export interface LocationChildrenDetail {
type: LocationDetailType.Children; type: LocationDetailType.Children;
index: number; index: number;
edge?: DOMRect;
near?: { near?: {
node: ComponentNode; node: ComponentNode;
pos: 'before' | 'after'; pos: 'before' | 'after';
@ -36,11 +37,16 @@ export interface Point {
clientY: number; clientY: number;
} }
export type Rects = Array<ClientRect | DOMRect> & { export interface CanvasPoint {
canvasX: number;
canvasY: number;
}
export type Rects = DOMRect[] & {
elements: Array<Element | Text>; elements: Array<Element | Text>;
}; };
export type Rect = (ClientRect | DOMRect) & { export type Rect = DOMRect & {
elements: Array<Element | Text>; elements: Array<Element | Text>;
computed?: boolean; computed?: boolean;
}; };

View File

@ -100,16 +100,6 @@ export interface ISimulator<P = object> extends ISensor {
*/ */
clearState(): void; clearState(): void;
/**
*
*/
locate(e: LocateEvent): any;
/**
* event canvasX, globalX
*/
fixEvent(e: LocateEvent): LocateEvent;
// #endregion // #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; 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; computeComponentInstanceRect(instance: ComponentInstance): DOMRect | null;
@ -150,9 +142,9 @@ export function isSimulator(obj: any): obj is ISimulator {
return obj && obj.isSimulator; return obj && obj.isSimulator;
} }
export interface NodeInstance { export interface NodeInstance<T = ComponentInstance> {
nodeId: string; nodeId: string;
instance: ComponentInstance; instance: T;
node?: Node | null; node?: Node | null;
} }