mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-12 17:08:14 +00:00
feat: 兼容磁贴布局功能 to #34464969
This commit is contained in:
parent
0360572476
commit
d8a39683d9
@ -24,7 +24,7 @@ export class BemTools extends Component<{ host: BuiltinSimulatorHost }> {
|
||||
}
|
||||
return (
|
||||
<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} />
|
||||
{ engineConfig.get('enableReactiveContainer') && <BorderContainer key="reactive-container-border" host={host} /> }
|
||||
<InsertionView key="insertion" host={host} />
|
||||
@ -38,4 +38,4 @@ export class BemTools extends Component<{ host: BuiltinSimulatorHost }> {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 { ISimulatorHost, Component, NodeInstance, ComponentInstance, DropContainer } from '../simulator';
|
||||
import {
|
||||
ISimulatorHost,
|
||||
Component,
|
||||
NodeInstance,
|
||||
ComponentInstance,
|
||||
DropContainer,
|
||||
} from '../simulator';
|
||||
import Viewport from './viewport';
|
||||
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 {
|
||||
AssetLevel,
|
||||
@ -23,7 +37,6 @@ import {
|
||||
LocateEvent,
|
||||
isDragAnyObject,
|
||||
isDragNodeObject,
|
||||
LocationData,
|
||||
isLocationData,
|
||||
LocationChildrenDetail,
|
||||
LocationDetailType,
|
||||
@ -36,7 +49,12 @@ import {
|
||||
} from '../designer';
|
||||
import { parseMetadata } from './utils/parse-metadata';
|
||||
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 clipboard from '../designer/clipboard';
|
||||
import { LiveEditing } from './live-editing/live-editing';
|
||||
@ -80,7 +98,10 @@ const defaultSimulatorUrl = (() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_, prefix = '', dev] = /^(.+?)(\/js)?\/?$/.exec(publicPath) || [];
|
||||
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') {
|
||||
urls = [`${prefix}/react-simulator-renderer.css`, `${prefix}/react-simulator-renderer.js`];
|
||||
} else {
|
||||
@ -106,12 +127,16 @@ const defaultRaxSimulatorUrl = (() => {
|
||||
|
||||
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
|
||||
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(
|
||||
AssetType.JSText,
|
||||
'window.PropTypes=parent.PropTypes;React.PropTypes=parent.PropTypes; window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;',
|
||||
),
|
||||
|
||||
];
|
||||
|
||||
const defaultRaxEnvironment = [
|
||||
@ -217,7 +242,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
|
||||
get(key: string): any {
|
||||
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];
|
||||
}
|
||||
@ -225,7 +253,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
/**
|
||||
* 有 Renderer 进程连接进来,设置同步机制
|
||||
*/
|
||||
connect(renderer: BuiltinSimulatorRenderer, fn: (context: { dispose: () => void; firstRun: boolean }) => void) {
|
||||
connect(
|
||||
renderer: BuiltinSimulatorRenderer,
|
||||
fn: (context: { dispose: () => void; firstRun: boolean }) => void,
|
||||
) {
|
||||
this._renderer = renderer;
|
||||
return autorun(fn as any, true);
|
||||
}
|
||||
@ -294,7 +325,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
* library:String umd包直接导出的name
|
||||
*/
|
||||
buildLibrary(library) {
|
||||
library = library || this.get('library') as LibraryItem[];
|
||||
library = library || (this.get('library') as LibraryItem[]);
|
||||
const libraryAsset: AssetList = [];
|
||||
const libraryExportList = [];
|
||||
|
||||
@ -305,7 +336,9 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
this.asyncLibraryMap[item.package] = item;
|
||||
}
|
||||
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) {
|
||||
libraryAsset.push(item.urls);
|
||||
@ -337,7 +370,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
const vendors = [
|
||||
// required & use once
|
||||
assetBundle(
|
||||
this.get('environment') || (this.renderEnv === 'rax' ? defaultRaxEnvironment : defaultEnvironment),
|
||||
this.get('environment') ||
|
||||
(this.renderEnv === 'rax' ? defaultRaxEnvironment : defaultEnvironment),
|
||||
AssetLevel.Environment,
|
||||
),
|
||||
// required & use once
|
||||
@ -349,7 +383,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
assetBundle(this.theme, AssetLevel.Theme),
|
||||
// required & use once
|
||||
assetBundle(
|
||||
this.get('simulatorUrl') || (this.renderEnv === 'rax' ? defaultRaxSimulatorUrl : defaultSimulatorUrl),
|
||||
this.get('simulatorUrl') ||
|
||||
(this.renderEnv === 'rax' ? defaultRaxSimulatorUrl : defaultSimulatorUrl),
|
||||
AssetLevel.Runtime,
|
||||
),
|
||||
];
|
||||
@ -357,7 +392,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
// wait 准备 iframe 内容、依赖库注入
|
||||
const renderer = await createSimulator(this, iframe, vendors);
|
||||
|
||||
|
||||
// TODO: !!! thinkof reload onloa
|
||||
|
||||
// wait 业务组件被第一次消费,否则会渲染出错
|
||||
@ -366,7 +400,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
// wait 运行时上下文
|
||||
await this.injectionConsumer.waitFirstConsume();
|
||||
|
||||
|
||||
if (Object.keys(this.asyncLibraryMap).length > 0) {
|
||||
// 加载异步Library
|
||||
await renderer.loadAsyncLibrary(this.asyncLibraryMap);
|
||||
@ -384,7 +417,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
focusTracker.mount(this._contentWindow);
|
||||
clipboard.injectCopyPaster(this._contentDocument);
|
||||
|
||||
|
||||
// TODO: dispose the bindings
|
||||
}
|
||||
|
||||
@ -437,15 +469,19 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
if (!node.isInited) return;
|
||||
// 静音状态不触发事件,通常是非局部更新操作
|
||||
if (this.mutedActivityEvent) return;
|
||||
this.postEvent('activity', {
|
||||
type: 'modified',
|
||||
payload: {
|
||||
schema: node.export(TransformStage.Render, { bypassChildren: true }),
|
||||
oldValue,
|
||||
newValue,
|
||||
prop,
|
||||
this.postEvent(
|
||||
'activity',
|
||||
{
|
||||
type: 'modified',
|
||||
payload: {
|
||||
schema: node.export(TransformStage.Render, { bypassChildren: true }),
|
||||
oldValue,
|
||||
newValue,
|
||||
prop,
|
||||
},
|
||||
},
|
||||
}, { doc: this.currentDocument });
|
||||
{ doc: this.currentDocument },
|
||||
);
|
||||
});
|
||||
// editor.on('node.add', ({ node }) => {
|
||||
// console.log('add node', node);
|
||||
@ -501,20 +537,82 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
} else if (!downEvent.metaKey) {
|
||||
return;
|
||||
}
|
||||
// stop response document focus event
|
||||
downEvent.stopPropagation();
|
||||
downEvent.preventDefault();
|
||||
|
||||
// FIXME: dirty fix remove label-for fro liveEditing
|
||||
(downEvent.target as HTMLElement).removeAttribute('for');
|
||||
|
||||
const nodeInst = this.getNodeInstanceFromElement(downEvent.target as Element);
|
||||
const node = getClosestClickableNode(nodeInst?.node || documentModel?.rootNode, downEvent);
|
||||
// 如果找不到可点击的节点, 直接返回
|
||||
if (!node) {
|
||||
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()) {
|
||||
// // 对于未注册组件直接返回
|
||||
// return;
|
||||
@ -522,8 +620,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
const isLeftButton = downEvent.which === 1 || downEvent.button === 0;
|
||||
const checkSelect = (e: MouseEvent) => {
|
||||
doc.removeEventListener('mouseup', checkSelect, true);
|
||||
// 鼠标是否移动
|
||||
if (!isShaken(downEvent, e)) {
|
||||
// 鼠标是否移动 ? - 鼠标抖动应该也需要支持选中事件,偶尔点击不能选中,磁帖块移除shaken检测
|
||||
if (!isShaken(downEvent, e) || isRGLNode) {
|
||||
let { id } = node;
|
||||
designer.activeTracker.track({ node, instance: nodeInst?.instance });
|
||||
if (isMulti && !isRootNode(node) && selection.has(id)) {
|
||||
@ -531,10 +629,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
} else {
|
||||
// TODO: 避免选中 Page 组件,默认选中第一个子节点;新增规则 或 判断 Live 模式
|
||||
if (node.isPage() && node.getChildren()?.notEmpty() && this.designMode === 'live') {
|
||||
const firstChildId = node
|
||||
.getChildren()
|
||||
?.get(0)
|
||||
?.getId();
|
||||
const firstChildId = node.getChildren()?.get(0)?.getId();
|
||||
if (firstChildId) id = firstChildId;
|
||||
}
|
||||
selection.select(id);
|
||||
@ -552,7 +647,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (isLeftButton && !isRootNode(node)) {
|
||||
let nodes: Node[] = [node];
|
||||
let ignoreUpSelected = false;
|
||||
@ -571,13 +665,16 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
} else {
|
||||
// will clear current selection & select dragment in dragstart
|
||||
}
|
||||
designer.dragon.boost(
|
||||
{
|
||||
type: DragObjectType.Node,
|
||||
nodes,
|
||||
},
|
||||
downEvent,
|
||||
);
|
||||
if (!isRGLNode) {
|
||||
designer.dragon.boost(
|
||||
{
|
||||
type: DragObjectType.Node,
|
||||
nodes,
|
||||
},
|
||||
downEvent,
|
||||
true,
|
||||
);
|
||||
}
|
||||
if (ignoreUpSelected) {
|
||||
// multi select mode has add selected, should return
|
||||
return;
|
||||
@ -636,6 +733,20 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
private disableHovering?: () => 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;
|
||||
}
|
||||
|
||||
const rootElement = this.findDOMNodes(nodeInst.instance, node.componentMeta.rootSelector)?.find(
|
||||
const rootElement = this.findDOMNodes(
|
||||
nodeInst.instance,
|
||||
node.componentMeta.rootSelector,
|
||||
)?.find(
|
||||
(item) =>
|
||||
// 可能是 [null];
|
||||
item && item.contains(targetElement),
|
||||
@ -847,7 +961,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
@ -974,7 +1091,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
* @see ISimulator
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
scrollToNode(node: Node, detail?: any/* , tryTimes = 0 */) {
|
||||
scrollToNode(node: Node, detail?: any /* , tryTimes = 0 */) {
|
||||
this.tryScrollAgain = null;
|
||||
if (this.sensing) {
|
||||
// actived sensor
|
||||
@ -1096,7 +1213,12 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
*/
|
||||
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;
|
||||
return (
|
||||
e.globalY >= rect.top &&
|
||||
e.globalY <= rect.bottom &&
|
||||
e.globalX >= rect.left &&
|
||||
e.globalX <= rect.right
|
||||
);
|
||||
}
|
||||
|
||||
private sensing = false;
|
||||
@ -1153,7 +1275,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
|
||||
const { container, instance: containerInstance } = dropContainer;
|
||||
|
||||
const edge = this.computeComponentInstanceRect(containerInstance, container.componentMeta.rootSelector);
|
||||
const edge = this.computeComponentInstanceRect(
|
||||
containerInstance,
|
||||
container.componentMeta.rootSelector,
|
||||
);
|
||||
|
||||
if (!edge) {
|
||||
return null;
|
||||
@ -1205,10 +1330,15 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
const instances = this.getComponentInstances(node);
|
||||
const inst = instances
|
||||
? 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]
|
||||
: null;
|
||||
const rect = inst ? this.computeComponentInstanceRect(inst, node.componentMeta.rootSelector) : null;
|
||||
const rect = inst
|
||||
? this.computeComponentInstanceRect(inst, node.componentMeta.rootSelector)
|
||||
: null;
|
||||
|
||||
if (!rect) {
|
||||
continue;
|
||||
@ -1340,7 +1470,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
if (nodeInstance.node === container) {
|
||||
instance = nodeInstance.instance;
|
||||
} else {
|
||||
instance = this.getClosestNodeInstance(nodeInstance.instance as any, container.id)?.instance;
|
||||
instance = this.getClosestNodeInstance(
|
||||
nodeInstance.instance as any,
|
||||
container.id,
|
||||
)?.instance;
|
||||
}
|
||||
} else {
|
||||
instance = this.getComponentInstances(container)?.[0];
|
||||
@ -1377,7 +1510,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}/* else if (res === DRILL_DOWN) {
|
||||
} /* else if (res === DRILL_DOWN) {
|
||||
if (!upward) {
|
||||
container = container.parent;
|
||||
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 document = this.project.currentDocument!;
|
||||
if (!children || children.isEmpty()) {
|
||||
|
||||
@ -13,7 +13,14 @@ import {
|
||||
} from '@ali/lowcode-types';
|
||||
import { megreAssets, AssetsJson } from '@ali/lowcode-utils';
|
||||
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 { INodeSelector, Component } from '../simulator';
|
||||
import { Scroller, IScrollable } from './scroller';
|
||||
@ -125,7 +132,7 @@ export class Designer {
|
||||
} else if (isDragNodeDataObject(dragObject)) {
|
||||
// process nodeData
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
@ -213,7 +220,11 @@ export class Designer {
|
||||
}
|
||||
const { currentSelection } = this;
|
||||
// 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;
|
||||
if (rootNodeChildrens.length > 0) {
|
||||
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;
|
||||
if (!activedDoc) {
|
||||
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 {
|
||||
target: activedDoc.rootNode as ParentalNode,
|
||||
};
|
||||
@ -355,7 +372,10 @@ export class Designer {
|
||||
if (props.suspensed !== this.props.suspensed && props.suspensed != null) {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
@ -477,7 +497,10 @@ export class Designer {
|
||||
return this.props?.globalComponentActions || null;
|
||||
}
|
||||
|
||||
getComponentMeta(componentName: string, generateMetadata?: () => ComponentMetadata | null): ComponentMeta {
|
||||
getComponentMeta(
|
||||
componentName: string,
|
||||
generateMetadata?: () => ComponentMetadata | null,
|
||||
): ComponentMeta {
|
||||
if (this._componentMetasMap.has(componentName)) {
|
||||
return this._componentMetasMap.get(componentName)!;
|
||||
}
|
||||
@ -558,4 +581,8 @@ export class Designer {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -4,7 +4,7 @@ import { NodeSchema } from '@ali/lowcode-types';
|
||||
import { setNativeSelection, cursor } from '@ali/lowcode-utils';
|
||||
import { DropLocation } from './location';
|
||||
import { Node, DocumentModel } from '../document';
|
||||
import { ISimulatorHost, isSimulatorHost } from '../simulator';
|
||||
import { ISimulatorHost, isSimulatorHost, NodeInstance, ComponentInstance } from '../simulator';
|
||||
import { Designer } from './designer';
|
||||
|
||||
export interface LocateEvent {
|
||||
@ -72,6 +72,14 @@ export interface ISensor {
|
||||
* 取消激活
|
||||
*/
|
||||
deactiveSensor(): void;
|
||||
/**
|
||||
* 获取节点实例
|
||||
*/
|
||||
getNodeInstanceFromElement(e: Element | null): NodeInstance<ComponentInstance> | null;
|
||||
/**
|
||||
* 获取RGL相关信息
|
||||
*/
|
||||
getRGLObject(nodeInst: NodeInstance): any;
|
||||
}
|
||||
|
||||
export type DragObject = DragNodeObject | DragNodeDataObject | DragAnyObject;
|
||||
@ -126,7 +134,9 @@ export function isShaken(e1: MouseEvent | DragEvent, e2: MouseEvent | DragEvent)
|
||||
if (e1.target !== e2.target) {
|
||||
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 {
|
||||
@ -199,6 +209,8 @@ export class Dragon {
|
||||
|
||||
@obx.ref private _dragging = false;
|
||||
|
||||
@obx.ref private _canDrop = false;
|
||||
|
||||
get dragging(): boolean {
|
||||
return this._dragging;
|
||||
}
|
||||
@ -239,17 +251,26 @@ export class Dragon {
|
||||
* @param dragObject 拖拽对象
|
||||
* @param boostEvent 拖拽初始时事件
|
||||
*/
|
||||
boost(dragObject: DragObject, boostEvent: MouseEvent | DragEvent) {
|
||||
boost(dragObject: DragObject, boostEvent: MouseEvent | DragEvent, isFromRGLNode?: boolean) {
|
||||
const { designer } = this;
|
||||
const masterSensors = this.getMasterSensors();
|
||||
const handleEvents = makeEventsHandler(boostEvent, masterSensors);
|
||||
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);
|
||||
let lastSensor: ISensor | undefined;
|
||||
|
||||
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) => {
|
||||
if (e.keyCode === 27) {
|
||||
designer.clearLocation();
|
||||
@ -304,13 +325,31 @@ export class Dragon {
|
||||
|
||||
const locateEvent = createLocateEvent(e);
|
||||
const sensor = chooseSensor(locateEvent);
|
||||
if (sensor) {
|
||||
sensor.fixEvent(locateEvent);
|
||||
sensor.locate(locateEvent);
|
||||
} else {
|
||||
this._canDrop = !!sensor?.locate(locateEvent);
|
||||
const { isRGL, rglNode } = getRGLObject(e);
|
||||
if (isRGL) {
|
||||
// 禁止原生响应
|
||||
if (!isFromRGLNode && this._canDrop) {
|
||||
this.emitter.emit('rgl.add.placeholder', {
|
||||
rglNode,
|
||||
node: locateEvent.dragObject.nodes[0],
|
||||
event: e,
|
||||
});
|
||||
}
|
||||
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 = () => {
|
||||
@ -363,6 +402,20 @@ export class Dragon {
|
||||
|
||||
// end-tail drag process
|
||||
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 */
|
||||
if (e && isDragEvent(e)) {
|
||||
e.preventDefault();
|
||||
@ -461,7 +514,10 @@ export class Dragon {
|
||||
const chooseSensor = (e: LocateEvent) => {
|
||||
// this.sensors will change on dragstart
|
||||
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) {
|
||||
// TODO: enter some area like componentspanel cancel
|
||||
if (lastSensor) {
|
||||
@ -547,7 +603,7 @@ export class Dragon {
|
||||
}
|
||||
|
||||
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 ============
|
||||
|
||||
@ -195,6 +195,10 @@ interface EngineOptions {
|
||||
* 关闭画布自动渲染,在资产包多重异步加载的场景有效,默认值:false
|
||||
*/
|
||||
disableAutoRender?: boolean;
|
||||
/**
|
||||
* 关闭拖拽组件时的虚线响应,性能考虑,默认值:false
|
||||
*/
|
||||
disableDetecting?: boolean;
|
||||
/**
|
||||
* Vision-polyfill settings
|
||||
*/
|
||||
@ -203,7 +207,7 @@ interface EngineOptions {
|
||||
disableCompatibleReducer?: boolean;
|
||||
// 是否开启在 render 阶段开启 filter reducer,默认值:false
|
||||
enableFilterReducerInRenderStage?: boolean;
|
||||
}
|
||||
};
|
||||
[key: string]: any;
|
||||
}
|
||||
export async function init(container?: Element, options?: EngineOptions) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user