mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-16 06:42:53 +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 { 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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
.my-insertion {
|
.lc-insertion {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -1.5px;
|
top: -1.5px;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
isEnter(e: LocateEvent): boolean {
|
/**
|
||||||
return false; /*
|
* @see ISensor
|
||||||
const rect = this.bounds;
|
|
||||||
return e.globalY >= rect.top && e.globalY <= rect.bottom && e.globalX >= rect.left && e.globalX <= rect.right;
|
|
||||||
*/
|
*/
|
||||||
|
isEnter(e: LocateEvent): boolean {
|
||||||
|
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;
|
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 {
|
|
||||||
/*
|
|
||||||
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 {
|
checkNesting(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean {
|
||||||
return false;
|
const items: Array<Node | NodeSchema> = dragObject.nodes || (dragObject as DragNodeDataObject).data;
|
||||||
/*
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
if (!instances.includes(instance)) {
|
||||||
instances.push(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,35 +286,45 @@ 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) {
|
||||||
|
if (isElement(el)) {
|
||||||
el = cacheReactKey(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) {
|
||||||
|
const nodeId = el[SYMBOL_VNID];
|
||||||
|
if (!specId || specId === nodeId) {
|
||||||
return {
|
return {
|
||||||
nodeId: el[SYMBOL_VNID],
|
nodeId: nodeId,
|
||||||
instance: el,
|
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) {
|
||||||
|
const nodeId = instance[SYMBOL_VNID];
|
||||||
|
if (!specId || specId === nodeId) {
|
||||||
return {
|
return {
|
||||||
nodeId: instance[SYMBOL_VNID],
|
nodeId: nodeId,
|
||||||
instance,
|
instance: instance,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return getNodeInstance(fiberNode.return);
|
return getNodeInstance(fiberNode.return);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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 } {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 === '';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
if (this.isNodeParent) {
|
||||||
return this.document.getComponent(this.componentName);
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(() => {
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 => {
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user