feat: 兼容磁贴布局功能 to #34464969

This commit is contained in:
jianhui.fjh 2021-06-10 17:32:07 +08:00
parent 0360572476
commit d8a39683d9
5 changed files with 301 additions and 77 deletions

View File

@ -24,7 +24,7 @@ export class BemTools extends Component<{ host: BuiltinSimulatorHost }> {
} }
return ( return (
<div className="lc-bem-tools" style={{ transform: `translate(${-scrollX * scale}px,${-scrollY * scale}px)` }}> <div className="lc-bem-tools" style={{ transform: `translate(${-scrollX * scale}px,${-scrollY * scale}px)` }}>
<BorderDetecting key="hovering" host={host} /> { !engineConfig.get('disableDetecting') && <BorderDetecting key="hovering" host={host} /> }
<BorderSelecting key="selecting" host={host} /> <BorderSelecting key="selecting" host={host} />
{ engineConfig.get('enableReactiveContainer') && <BorderContainer key="reactive-container-border" host={host} /> } { engineConfig.get('enableReactiveContainer') && <BorderContainer key="reactive-container-border" host={host} /> }
<InsertionView key="insertion" host={host} /> <InsertionView key="insertion" host={host} />

View File

@ -1,9 +1,23 @@
import { obx, autorun, computed, getPublicPath, hotkey, focusTracker, engineConfig } from '@ali/lowcode-editor-core'; import {
obx,
autorun,
computed,
getPublicPath,
hotkey,
focusTracker,
engineConfig,
} from '@ali/lowcode-editor-core';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { ISimulatorHost, Component, NodeInstance, ComponentInstance, DropContainer } from '../simulator'; import {
ISimulatorHost,
Component,
NodeInstance,
ComponentInstance,
DropContainer,
} from '../simulator';
import Viewport from './viewport'; import Viewport from './viewport';
import { createSimulator } from './create-simulator'; import { createSimulator } from './create-simulator';
import { Node, ParentalNode, isNode, contains, isRootNode } from '../document'; import { Node, ParentalNode, contains, isRootNode } from '../document';
import ResourceConsumer from './resource-consumer'; import ResourceConsumer from './resource-consumer';
import { import {
AssetLevel, AssetLevel,
@ -23,7 +37,6 @@ import {
LocateEvent, LocateEvent,
isDragAnyObject, isDragAnyObject,
isDragNodeObject, isDragNodeObject,
LocationData,
isLocationData, isLocationData,
LocationChildrenDetail, LocationChildrenDetail,
LocationDetailType, LocationDetailType,
@ -36,7 +49,12 @@ import {
} from '../designer'; } from '../designer';
import { parseMetadata } from './utils/parse-metadata'; import { parseMetadata } from './utils/parse-metadata';
import { getClosestClickableNode } from './utils/clickable'; import { getClosestClickableNode } from './utils/clickable';
import { ComponentMetadata, ComponentSchema, TransformStage, ActivityData } from '@ali/lowcode-types'; import {
ComponentMetadata,
ComponentSchema,
TransformStage,
ActivityData,
} from '@ali/lowcode-types';
import { BuiltinSimulatorRenderer } from './renderer'; import { BuiltinSimulatorRenderer } from './renderer';
import clipboard from '../designer/clipboard'; import clipboard from '../designer/clipboard';
import { LiveEditing } from './live-editing/live-editing'; import { LiveEditing } from './live-editing/live-editing';
@ -80,7 +98,10 @@ const defaultSimulatorUrl = (() => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, prefix = '', dev] = /^(.+?)(\/js)?\/?$/.exec(publicPath) || []; const [_, prefix = '', dev] = /^(.+?)(\/js)?\/?$/.exec(publicPath) || [];
if (dev) { if (dev) {
urls = [`${prefix}/css/react-simulator-renderer.css`, `${prefix}/js/react-simulator-renderer.js`]; urls = [
`${prefix}/css/react-simulator-renderer.css`,
`${prefix}/js/react-simulator-renderer.js`,
];
} else if (process.env.NODE_ENV === 'production') { } else if (process.env.NODE_ENV === 'production') {
urls = [`${prefix}/react-simulator-renderer.css`, `${prefix}/react-simulator-renderer.js`]; urls = [`${prefix}/react-simulator-renderer.css`, `${prefix}/react-simulator-renderer.js`];
} else { } else {
@ -106,12 +127,16 @@ const defaultRaxSimulatorUrl = (() => {
const defaultEnvironment = [ const defaultEnvironment = [
// https://g.alicdn.com/mylib/??react/16.11.0/umd/react.production.min.js,react-dom/16.8.6/umd/react-dom.production.min.js,prop-types/15.7.2/prop-types.min.js // https://g.alicdn.com/mylib/??react/16.11.0/umd/react.production.min.js,react-dom/16.8.6/umd/react-dom.production.min.js,prop-types/15.7.2/prop-types.min.js
assetItem(AssetType.JSText, 'window.React=parent.React;window.ReactDOM=parent.ReactDOM;window.__is_simulator_env__=true;', undefined, 'react'), assetItem(
AssetType.JSText,
'window.React=parent.React;window.ReactDOM=parent.ReactDOM;window.__is_simulator_env__=true;',
undefined,
'react',
),
assetItem( assetItem(
AssetType.JSText, AssetType.JSText,
'window.PropTypes=parent.PropTypes;React.PropTypes=parent.PropTypes; window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;', 'window.PropTypes=parent.PropTypes;React.PropTypes=parent.PropTypes; window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;',
), ),
]; ];
const defaultRaxEnvironment = [ const defaultRaxEnvironment = [
@ -217,7 +242,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
get(key: string): any { get(key: string): any {
if (key === 'device') { if (key === 'device') {
return this.designer?.editor?.get('deviceMapper')?.transform?.(this._props.device) || this._props.device; return (
this.designer?.editor?.get('deviceMapper')?.transform?.(this._props.device) ||
this._props.device
);
} }
return this._props[key]; return this._props[key];
} }
@ -225,7 +253,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/** /**
* Renderer * Renderer
*/ */
connect(renderer: BuiltinSimulatorRenderer, fn: (context: { dispose: () => void; firstRun: boolean }) => void) { connect(
renderer: BuiltinSimulatorRenderer,
fn: (context: { dispose: () => void; firstRun: boolean }) => void,
) {
this._renderer = renderer; this._renderer = renderer;
return autorun(fn as any, true); return autorun(fn as any, true);
} }
@ -294,7 +325,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
* libraryString umd包直接导出的name * libraryString umd包直接导出的name
*/ */
buildLibrary(library) { buildLibrary(library) {
library = library || this.get('library') as LibraryItem[]; library = library || (this.get('library') as LibraryItem[]);
const libraryAsset: AssetList = []; const libraryAsset: AssetList = [];
const libraryExportList = []; const libraryExportList = [];
@ -305,7 +336,9 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
this.asyncLibraryMap[item.package] = item; this.asyncLibraryMap[item.package] = item;
} }
if (item.exportName && item.library) { if (item.exportName && item.library) {
libraryExportList.push(`Object.defineProperty(window,'${item.exportName}',{get:()=>window.${item.library}});`); libraryExportList.push(
`Object.defineProperty(window,'${item.exportName}',{get:()=>window.${item.library}});`,
);
} }
if (item.urls) { if (item.urls) {
libraryAsset.push(item.urls); libraryAsset.push(item.urls);
@ -337,7 +370,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
const vendors = [ const vendors = [
// required & use once // required & use once
assetBundle( assetBundle(
this.get('environment') || (this.renderEnv === 'rax' ? defaultRaxEnvironment : defaultEnvironment), this.get('environment') ||
(this.renderEnv === 'rax' ? defaultRaxEnvironment : defaultEnvironment),
AssetLevel.Environment, AssetLevel.Environment,
), ),
// required & use once // required & use once
@ -349,7 +383,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
assetBundle(this.theme, AssetLevel.Theme), assetBundle(this.theme, AssetLevel.Theme),
// required & use once // required & use once
assetBundle( assetBundle(
this.get('simulatorUrl') || (this.renderEnv === 'rax' ? defaultRaxSimulatorUrl : defaultSimulatorUrl), this.get('simulatorUrl') ||
(this.renderEnv === 'rax' ? defaultRaxSimulatorUrl : defaultSimulatorUrl),
AssetLevel.Runtime, AssetLevel.Runtime,
), ),
]; ];
@ -357,7 +392,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
// wait 准备 iframe 内容、依赖库注入 // wait 准备 iframe 内容、依赖库注入
const renderer = await createSimulator(this, iframe, vendors); const renderer = await createSimulator(this, iframe, vendors);
// TODO: !!! thinkof reload onloa // TODO: !!! thinkof reload onloa
// wait 业务组件被第一次消费,否则会渲染出错 // wait 业务组件被第一次消费,否则会渲染出错
@ -366,7 +400,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
// wait 运行时上下文 // wait 运行时上下文
await this.injectionConsumer.waitFirstConsume(); await this.injectionConsumer.waitFirstConsume();
if (Object.keys(this.asyncLibraryMap).length > 0) { if (Object.keys(this.asyncLibraryMap).length > 0) {
// 加载异步Library // 加载异步Library
await renderer.loadAsyncLibrary(this.asyncLibraryMap); await renderer.loadAsyncLibrary(this.asyncLibraryMap);
@ -384,7 +417,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
focusTracker.mount(this._contentWindow); focusTracker.mount(this._contentWindow);
clipboard.injectCopyPaster(this._contentDocument); clipboard.injectCopyPaster(this._contentDocument);
// TODO: dispose the bindings // TODO: dispose the bindings
} }
@ -437,15 +469,19 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
if (!node.isInited) return; if (!node.isInited) return;
// 静音状态不触发事件,通常是非局部更新操作 // 静音状态不触发事件,通常是非局部更新操作
if (this.mutedActivityEvent) return; if (this.mutedActivityEvent) return;
this.postEvent('activity', { this.postEvent(
type: 'modified', 'activity',
payload: { {
schema: node.export(TransformStage.Render, { bypassChildren: true }), type: 'modified',
oldValue, payload: {
newValue, schema: node.export(TransformStage.Render, { bypassChildren: true }),
prop, oldValue,
newValue,
prop,
},
}, },
}, { doc: this.currentDocument }); { doc: this.currentDocument },
);
}); });
// editor.on('node.add', ({ node }) => { // editor.on('node.add', ({ node }) => {
// console.log('add node', node); // console.log('add node', node);
@ -501,20 +537,82 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
} else if (!downEvent.metaKey) { } else if (!downEvent.metaKey) {
return; return;
} }
// stop response document focus event
downEvent.stopPropagation();
downEvent.preventDefault();
// FIXME: dirty fix remove label-for fro liveEditing // FIXME: dirty fix remove label-for fro liveEditing
(downEvent.target as HTMLElement).removeAttribute('for'); (downEvent.target as HTMLElement).removeAttribute('for');
const nodeInst = this.getNodeInstanceFromElement(downEvent.target as Element); const nodeInst = this.getNodeInstanceFromElement(downEvent.target as Element);
const node = getClosestClickableNode(nodeInst?.node || documentModel?.rootNode, downEvent); const node = getClosestClickableNode(nodeInst?.node || documentModel?.rootNode, downEvent);
// 如果找不到可点击的节点, 直接返回 // 如果找不到可点击的节点, 直接返回
if (!node) { if (!node) {
return; return;
} }
const isRGLNode = node?.getParent()?.isRGLContainer;
const rglNode = node?.getParent();
if (isRGLNode) {
// 如果拖拽的是磁铁块的右下角handle则直接跳过
if (downEvent.target.classList.contains('react-resizable-handle')) return;
isMulti = false;
designer.dragon.emitter.emit('rgl.switch', {
action: 'start',
rglNode,
});
const judgeEnterOtherRGL = (e: MouseEvent) => {
const _nodeInst = this.getNodeInstanceFromElement(e.target as Element);
const { isRGL: _isRGL, rglNode: _rglNode } = this.getRGLObject(
_nodeInst as NodeInstance,
);
const status = !!(
_isRGL &&
_rglNode?.id !== rglNode?.id &&
_rglNode?.getParent() !== node &&
nodeInst?.node !== _nodeInst?.node
);
return { status, rglNode: _rglNode };
};
const move = (e: MouseEvent) => {
if (!isShaken(downEvent, e)) {
if (nodeInst.instance && nodeInst.instance.style) {
nodeInst.instance.style.pointerEvents = 'none';
}
}
const { status, rglNode: _rglNode } = judgeEnterOtherRGL(e);
if (status) {
designer.dragon.emitter.emit('rgl.add.placeholder', {
rglNode: _rglNode,
node,
event: e,
fromRglNode: rglNode,
});
} else {
designer.dragon.emitter.emit('rgl.remove.placeholder');
}
};
const over = (e: MouseEvent) => {
const { status, rglNode: _rglNode } = judgeEnterOtherRGL(e);
if (status) {
designer.dragon.emitter.emit('rgl.drop', {
rglNode: _rglNode,
node,
fromRglNode: rglNode,
});
}
designer.dragon.emitter.emit('rgl.remove.placeholder');
if (nodeInst.instance && nodeInst.instance.style) {
nodeInst.instance.style.pointerEvents = '';
}
designer.dragon.emitter.emit('rgl.switch', {
action: 'end',
rglNode,
});
doc.removeEventListener('mouseup', over, true);
doc.removeEventListener('mousemove', move, true);
};
doc.addEventListener('mouseup', over, true);
doc.addEventListener('mousemove', move, true);
} else {
// stop response document focus event
downEvent.stopPropagation();
downEvent.preventDefault();
}
// if (!node?.isValidComponent()) { // if (!node?.isValidComponent()) {
// // 对于未注册组件直接返回 // // 对于未注册组件直接返回
// return; // return;
@ -522,8 +620,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
const isLeftButton = downEvent.which === 1 || downEvent.button === 0; const isLeftButton = downEvent.which === 1 || downEvent.button === 0;
const checkSelect = (e: MouseEvent) => { const checkSelect = (e: MouseEvent) => {
doc.removeEventListener('mouseup', checkSelect, true); doc.removeEventListener('mouseup', checkSelect, true);
// 鼠标是否移动 // 鼠标是否移动 ? - 鼠标抖动应该也需要支持选中事件偶尔点击不能选中磁帖块移除shaken检测
if (!isShaken(downEvent, e)) { if (!isShaken(downEvent, e) || isRGLNode) {
let { id } = node; let { id } = node;
designer.activeTracker.track({ node, instance: nodeInst?.instance }); designer.activeTracker.track({ node, instance: nodeInst?.instance });
if (isMulti && !isRootNode(node) && selection.has(id)) { if (isMulti && !isRootNode(node) && selection.has(id)) {
@ -531,10 +629,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
} else { } else {
// TODO: 避免选中 Page 组件,默认选中第一个子节点;新增规则 或 判断 Live 模式 // TODO: 避免选中 Page 组件,默认选中第一个子节点;新增规则 或 判断 Live 模式
if (node.isPage() && node.getChildren()?.notEmpty() && this.designMode === 'live') { if (node.isPage() && node.getChildren()?.notEmpty() && this.designMode === 'live') {
const firstChildId = node const firstChildId = node.getChildren()?.get(0)?.getId();
.getChildren()
?.get(0)
?.getId();
if (firstChildId) id = firstChildId; if (firstChildId) id = firstChildId;
} }
selection.select(id); selection.select(id);
@ -552,7 +647,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
} }
} }
}; };
if (isLeftButton && !isRootNode(node)) { if (isLeftButton && !isRootNode(node)) {
let nodes: Node[] = [node]; let nodes: Node[] = [node];
let ignoreUpSelected = false; let ignoreUpSelected = false;
@ -571,13 +665,16 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
} else { } else {
// will clear current selection & select dragment in dragstart // will clear current selection & select dragment in dragstart
} }
designer.dragon.boost( if (!isRGLNode) {
{ designer.dragon.boost(
type: DragObjectType.Node, {
nodes, type: DragObjectType.Node,
}, nodes,
downEvent, },
); downEvent,
true,
);
}
if (ignoreUpSelected) { if (ignoreUpSelected) {
// multi select mode has add selected, should return // multi select mode has add selected, should return
return; return;
@ -636,6 +733,20 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
private disableHovering?: () => void; private disableHovering?: () => void;
private disableDetecting?: () => void; private disableDetecting?: () => void;
/**
* node实例的磁贴相关信息
*/
getRGLObject(nodeInst: NodeInstance) {
const isContainerNode = nodeInst?.node.isContainer();
const isEmptyNode = nodeInst?.node?.isEmpty();
const isRGLContainerNode = nodeInst?.node?.isRGLContainer;
const isRGLNode = nodeInst?.node?.getParent()?.isRGLContainer;
const isRGL = isRGLContainerNode || (isRGLNode && (!isContainerNode || !isEmptyNode));
let rglNode = isRGLContainerNode ? nodeInst?.node : isRGL ? nodeInst?.node?.getParent() : {};
return { isContainerNode, isEmptyNode, isRGLContainerNode, isRGLNode, isRGL, rglNode };
}
/** /**
* *
*/ */
@ -694,7 +805,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
return; return;
} }
const rootElement = this.findDOMNodes(nodeInst.instance, node.componentMeta.rootSelector)?.find( const rootElement = this.findDOMNodes(
nodeInst.instance,
node.componentMeta.rootSelector,
)?.find(
(item) => (item) =>
// 可能是 [null]; // 可能是 [null];
item && item.contains(targetElement), item && item.contains(targetElement),
@ -847,7 +961,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/** /**
* @see ISimulator * @see ISimulator
*/ */
getClosestNodeInstance(from: ComponentInstance, specId?: string): NodeInstance<ComponentInstance> | null { getClosestNodeInstance(
from: ComponentInstance,
specId?: string,
): NodeInstance<ComponentInstance> | null {
return this.renderer?.getClosestNodeInstance(from, specId) || null; return this.renderer?.getClosestNodeInstance(from, specId) || null;
} }
@ -974,7 +1091,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
* @see ISimulator * @see ISimulator
*/ */
/* istanbul ignore next */ /* istanbul ignore next */
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) {
// actived sensor // actived sensor
@ -1096,7 +1213,12 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
*/ */
isEnter(e: LocateEvent): boolean { isEnter(e: LocateEvent): boolean {
const rect = this.viewport.bounds; const rect = this.viewport.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 = false; private sensing = false;
@ -1153,7 +1275,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
const { container, instance: containerInstance } = dropContainer; const { container, instance: containerInstance } = dropContainer;
const edge = this.computeComponentInstanceRect(containerInstance, container.componentMeta.rootSelector); const edge = this.computeComponentInstanceRect(
containerInstance,
container.componentMeta.rootSelector,
);
if (!edge) { if (!edge) {
return null; return null;
@ -1205,10 +1330,15 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
const instances = this.getComponentInstances(node); const instances = this.getComponentInstances(node);
const inst = instances const inst = instances
? instances.length > 1 ? instances.length > 1
? instances.find((_inst) => this.getClosestNodeInstance(_inst, container.id)?.instance === containerInstance) ? instances.find(
(_inst) =>
this.getClosestNodeInstance(_inst, container.id)?.instance === containerInstance,
)
: instances[0] : instances[0]
: null; : null;
const rect = inst ? this.computeComponentInstanceRect(inst, node.componentMeta.rootSelector) : null; const rect = inst
? this.computeComponentInstanceRect(inst, node.componentMeta.rootSelector)
: null;
if (!rect) { if (!rect) {
continue; continue;
@ -1340,7 +1470,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
if (nodeInstance.node === container) { if (nodeInstance.node === container) {
instance = nodeInstance.instance; instance = nodeInstance.instance;
} else { } else {
instance = this.getClosestNodeInstance(nodeInstance.instance as any, container.id)?.instance; instance = this.getClosestNodeInstance(
nodeInstance.instance as any,
container.id,
)?.instance;
} }
} else { } else {
instance = this.getComponentInstances(container)?.[0]; instance = this.getComponentInstances(container)?.[0];
@ -1377,7 +1510,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
} else { } else {
return null; return null;
} }
}/* else if (res === DRILL_DOWN) { } /* else if (res === DRILL_DOWN) {
if (!upward) { if (!upward) {
container = container.parent; container = container.parent;
instance = this.getClosestNodeInstance(dropContainer.instance, container.id)?.instance; instance = this.getClosestNodeInstance(dropContainer.instance, container.id)?.instance;
@ -1462,7 +1595,11 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/** /**
* *
*/ */
getNearByContainer({ container, instance }: DropContainer, drillDownExcludes: Set<Node>, e: LocateEvent) { getNearByContainer(
{ container, instance }: DropContainer,
drillDownExcludes: Set<Node>,
e: LocateEvent,
) {
const { children } = container; const { children } = container;
const document = this.project.currentDocument!; const document = this.project.currentDocument!;
if (!children || children.isEmpty()) { if (!children || children.isEmpty()) {

View File

@ -13,7 +13,14 @@ import {
} from '@ali/lowcode-types'; } from '@ali/lowcode-types';
import { megreAssets, AssetsJson } from '@ali/lowcode-utils'; import { megreAssets, AssetsJson } from '@ali/lowcode-utils';
import { Project } from '../project'; import { Project } from '../project';
import { Node, DocumentModel, insertChildren, isRootNode, ParentalNode, TransformStage } from '../document'; import {
Node,
DocumentModel,
insertChildren,
isRootNode,
ParentalNode,
TransformStage,
} from '../document';
import { ComponentMeta } from '../component-meta'; import { ComponentMeta } from '../component-meta';
import { INodeSelector, Component } from '../simulator'; import { INodeSelector, Component } from '../simulator';
import { Scroller, IScrollable } from './scroller'; import { Scroller, IScrollable } from './scroller';
@ -125,7 +132,7 @@ export class Designer {
} else if (isDragNodeDataObject(dragObject)) { } else if (isDragNodeDataObject(dragObject)) {
// process nodeData // process nodeData
const nodeData = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data]; const nodeData = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data];
const isNotNodeSchema = nodeData.find(item => !isNodeSchema(item)); const isNotNodeSchema = nodeData.find((item) => !isNodeSchema(item));
if (isNotNodeSchema) { if (isNotNodeSchema) {
return; return;
} }
@ -213,7 +220,11 @@ export class Designer {
} }
const { currentSelection } = this; const { currentSelection } = this;
// TODO: 避免选中 Page 组件,默认选中第一个子节点;新增规则 或 判断 Live 模式 // TODO: 避免选中 Page 组件,默认选中第一个子节点;新增规则 或 判断 Live 模式
if (currentSelection && currentSelection.selected.length === 0 && this.simulatorProps?.designMode === 'live') { if (
currentSelection &&
currentSelection.selected.length === 0 &&
this.simulatorProps?.designMode === 'live'
) {
const rootNodeChildrens = this.currentDocument.getRoot().getChildren().children; const rootNodeChildrens = this.currentDocument.getRoot().getChildren().children;
if (rootNodeChildrens.length > 0) { if (rootNodeChildrens.length > 0) {
currentSelection.select(rootNodeChildrens[0].id); currentSelection.select(rootNodeChildrens[0].id);
@ -301,12 +312,18 @@ export class Designer {
/** /**
* *
*/ */
getSuitableInsertion(insertNode?: Node | NodeSchema | NodeSchema[]): { target: ParentalNode; index?: number } | null { getSuitableInsertion(
insertNode?: Node | NodeSchema | NodeSchema[],
): { target: ParentalNode; index?: number } | null {
const activedDoc = this.project.currentDocument; const activedDoc = this.project.currentDocument;
if (!activedDoc) { if (!activedDoc) {
return null; return null;
} }
if (Array.isArray(insertNode) && isNodeSchema(insertNode[0]) && this.getComponentMeta(insertNode[0].componentName).isModal) { if (
Array.isArray(insertNode) &&
isNodeSchema(insertNode[0]) &&
this.getComponentMeta(insertNode[0].componentName).isModal
) {
return { return {
target: activedDoc.rootNode as ParentalNode, target: activedDoc.rootNode as ParentalNode,
}; };
@ -355,7 +372,10 @@ export class Designer {
if (props.suspensed !== this.props.suspensed && props.suspensed != null) { if (props.suspensed !== this.props.suspensed && props.suspensed != null) {
this.suspensed = props.suspensed; this.suspensed = props.suspensed;
} }
if (props.componentMetadatas !== this.props.componentMetadatas && props.componentMetadatas != null) { if (
props.componentMetadatas !== this.props.componentMetadatas &&
props.componentMetadatas != null
) {
this.buildComponentMetasMap(props.componentMetadatas); this.buildComponentMetasMap(props.componentMetadatas);
} }
} else { } else {
@ -477,7 +497,10 @@ export class Designer {
return this.props?.globalComponentActions || null; return this.props?.globalComponentActions || null;
} }
getComponentMeta(componentName: string, generateMetadata?: () => ComponentMetadata | null): ComponentMeta { getComponentMeta(
componentName: string,
generateMetadata?: () => ComponentMetadata | null,
): ComponentMeta {
if (this._componentMetasMap.has(componentName)) { if (this._componentMetasMap.has(componentName)) {
return this._componentMetasMap.get(componentName)!; return this._componentMetasMap.get(componentName)!;
} }
@ -558,4 +581,8 @@ export class Designer {
} }
export type PropsReducerContext = { stage: TransformStage }; export type PropsReducerContext = { stage: TransformStage };
export type PropsReducer = (props: CompositeObject, node: Node, ctx?: PropsReducerContext) => CompositeObject; export type PropsReducer = (
props: CompositeObject,
node: Node,
ctx?: PropsReducerContext,
) => CompositeObject;

View File

@ -4,7 +4,7 @@ import { NodeSchema } from '@ali/lowcode-types';
import { setNativeSelection, cursor } from '@ali/lowcode-utils'; import { setNativeSelection, cursor } from '@ali/lowcode-utils';
import { DropLocation } from './location'; import { DropLocation } from './location';
import { Node, DocumentModel } from '../document'; import { Node, DocumentModel } from '../document';
import { ISimulatorHost, isSimulatorHost } from '../simulator'; import { ISimulatorHost, isSimulatorHost, NodeInstance, ComponentInstance } from '../simulator';
import { Designer } from './designer'; import { Designer } from './designer';
export interface LocateEvent { export interface LocateEvent {
@ -72,6 +72,14 @@ export interface ISensor {
* *
*/ */
deactiveSensor(): void; deactiveSensor(): void;
/**
*
*/
getNodeInstanceFromElement(e: Element | null): NodeInstance<ComponentInstance> | null;
/**
* RGL相关信息
*/
getRGLObject(nodeInst: NodeInstance): any;
} }
export type DragObject = DragNodeObject | DragNodeDataObject | DragAnyObject; export type DragObject = DragNodeObject | DragNodeDataObject | DragAnyObject;
@ -126,7 +134,9 @@ export function isShaken(e1: MouseEvent | DragEvent, e2: MouseEvent | DragEvent)
if (e1.target !== e2.target) { if (e1.target !== e2.target) {
return true; return true;
} }
return Math.pow(e1.clientY - e2.clientY, 2) + Math.pow(e1.clientX - e2.clientX, 2) > SHAKE_DISTANCE; return (
Math.pow(e1.clientY - e2.clientY, 2) + Math.pow(e1.clientX - e2.clientX, 2) > SHAKE_DISTANCE
);
} }
function isInvalidPoint(e: any, last: any): boolean { function isInvalidPoint(e: any, last: any): boolean {
@ -199,6 +209,8 @@ export class Dragon {
@obx.ref private _dragging = false; @obx.ref private _dragging = false;
@obx.ref private _canDrop = false;
get dragging(): boolean { get dragging(): boolean {
return this._dragging; return this._dragging;
} }
@ -239,17 +251,26 @@ export class Dragon {
* @param dragObject * @param dragObject
* @param boostEvent * @param boostEvent
*/ */
boost(dragObject: DragObject, boostEvent: MouseEvent | DragEvent) { boost(dragObject: DragObject, boostEvent: MouseEvent | DragEvent, isFromRGLNode?: boolean) {
const { designer } = this; const { designer } = this;
const masterSensors = this.getMasterSensors(); const masterSensors = this.getMasterSensors();
const handleEvents = makeEventsHandler(boostEvent, masterSensors); const handleEvents = makeEventsHandler(boostEvent, masterSensors);
const newBie = !isDragNodeObject(dragObject); const newBie = !isDragNodeObject(dragObject);
const forceCopyState = isDragNodeObject(dragObject) && dragObject.nodes.some((node) => node.isSlot()); const forceCopyState =
isDragNodeObject(dragObject) && dragObject.nodes.some((node) => node.isSlot());
const isBoostFromDragAPI = isDragEvent(boostEvent); const isBoostFromDragAPI = isDragEvent(boostEvent);
let lastSensor: ISensor | undefined; let lastSensor: ISensor | undefined;
this._dragging = false; this._dragging = false;
const getRGLObject = (e: MouseEvent | DragEvent) => {
const locateEvent = createLocateEvent(e);
const sensor = chooseSensor(locateEvent);
if (!sensor || !sensor.getNodeInstanceFromElement) return {};
const nodeInst = sensor.getNodeInstanceFromElement(e.target as Element);
return sensor.getRGLObject(nodeInst as NodeInstance);
};
const checkesc = (e: KeyboardEvent) => { const checkesc = (e: KeyboardEvent) => {
if (e.keyCode === 27) { if (e.keyCode === 27) {
designer.clearLocation(); designer.clearLocation();
@ -304,13 +325,31 @@ export class Dragon {
const locateEvent = createLocateEvent(e); const locateEvent = createLocateEvent(e);
const sensor = chooseSensor(locateEvent); const sensor = chooseSensor(locateEvent);
if (sensor) { this._canDrop = !!sensor?.locate(locateEvent);
sensor.fixEvent(locateEvent); const { isRGL, rglNode } = getRGLObject(e);
sensor.locate(locateEvent); if (isRGL) {
} else { // 禁止原生响应
if (!isFromRGLNode && this._canDrop) {
this.emitter.emit('rgl.add.placeholder', {
rglNode,
node: locateEvent.dragObject.nodes[0],
event: e,
});
}
designer.clearLocation(); designer.clearLocation();
this.clearState();
this.emitter.emit('drag', locateEvent);
} else {
if (!isFromRGLNode && this._canDrop) {
this.emitter.emit('rgl.remove.placeholder');
}
if (sensor) {
sensor.fixEvent(locateEvent);
} else {
designer.clearLocation();
}
this.emitter.emit('drag', locateEvent);
} }
this.emitter.emit('drag', locateEvent);
}; };
const dragstart = () => { const dragstart = () => {
@ -363,6 +402,20 @@ export class Dragon {
// end-tail drag process // end-tail drag process
const over = (e?: any) => { const over = (e?: any) => {
// 发送drop事件
if (e) {
const { isRGL, rglNode } = getRGLObject(e);
if (isRGL && !isFromRGLNode && this._canDrop) {
const tarNode = dragObject.nodes[0];
this.emitter.emit('rgl.drop', {
rglNode,
node: tarNode,
});
const { selection } = designer.project.currentDocument;
selection.select(tarNode.id);
}
}
/* istanbul ignore next */ /* istanbul ignore next */
if (e && isDragEvent(e)) { if (e && isDragEvent(e)) {
e.preventDefault(); e.preventDefault();
@ -461,7 +514,10 @@ export class Dragon {
const chooseSensor = (e: LocateEvent) => { const chooseSensor = (e: LocateEvent) => {
// this.sensors will change on dragstart // this.sensors will change on dragstart
const sensors: ISensor[] = (masterSensors as ISensor[]).concat(this.sensors); const sensors: ISensor[] = (masterSensors as ISensor[]).concat(this.sensors);
let sensor = e.sensor && e.sensor.isEnter(e) ? 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) {
// TODO: enter some area like componentspanel cancel // TODO: enter some area like componentspanel cancel
if (lastSensor) { if (lastSensor) {
@ -547,7 +603,7 @@ export class Dragon {
} }
private getSimulators() { private getSimulators() {
return new Set(this.designer.project.documents.map(doc => doc.simulator)); return new Set(this.designer.project.documents.map((doc) => doc.simulator));
} }
// #region ======== drag and drop helpers ============ // #region ======== drag and drop helpers ============

View File

@ -195,6 +195,10 @@ interface EngineOptions {
* false * false
*/ */
disableAutoRender?: boolean; disableAutoRender?: boolean;
/**
* 线false
*/
disableDetecting?: boolean;
/** /**
* Vision-polyfill settings * Vision-polyfill settings
*/ */
@ -203,7 +207,7 @@ interface EngineOptions {
disableCompatibleReducer?: boolean; disableCompatibleReducer?: boolean;
// 是否开启在 render 阶段开启 filter reducer默认值false // 是否开启在 render 阶段开启 filter reducer默认值false
enableFilterReducerInRenderStage?: boolean; enableFilterReducerInRenderStage?: boolean;
} };
[key: string]: any; [key: string]: any;
} }
export async function init(container?: Element, options?: EngineOptions) { export async function init(container?: Element, options?: EngineOptions) {