diff --git a/packages/designer/src/builtin-simulator/bem-tools/index.tsx b/packages/designer/src/builtin-simulator/bem-tools/index.tsx
index 97ce673dc..6551633e8 100644
--- a/packages/designer/src/builtin-simulator/bem-tools/index.tsx
+++ b/packages/designer/src/builtin-simulator/bem-tools/index.tsx
@@ -24,7 +24,7 @@ export class BemTools extends Component<{ host: BuiltinSimulatorHost }> {
}
return (
-
+ { !engineConfig.get('disableDetecting') && }
{ engineConfig.get('enableReactiveContainer') && }
@@ -38,4 +38,4 @@ export class BemTools extends Component<{ host: BuiltinSimulatorHost }> {
);
}
-}
+}
\ No newline at end of file
diff --git a/packages/designer/src/builtin-simulator/host.ts b/packages/designer/src/builtin-simulator/host.ts
index 0eb5c2429..3520bf05d 100644
--- a/packages/designer/src/builtin-simulator/host.ts
+++ b/packages/designer/src/builtin-simulator/host.ts
@@ -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 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 ISimulatorHostwindow.${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 0) {
// 加载异步Library
await renderer.loadAsyncLibrary(this.asyncLibraryMap);
@@ -384,7 +417,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost {
// console.log('add node', node);
@@ -501,20 +537,82 @@ export class BuiltinSimulatorHost implements ISimulatorHost {
+ 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 {
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 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
// 可能是 [null];
item && item.contains(targetElement),
@@ -847,7 +961,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost | null {
+ getClosestNodeInstance(
+ from: ComponentInstance,
+ specId?: string,
+ ): NodeInstance | null {
return this.renderer?.getClosestNodeInstance(from, specId) || null;
}
@@ -974,7 +1091,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost= 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 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, e: LocateEvent) {
+ getNearByContainer(
+ { container, instance }: DropContainer,
+ drillDownExcludes: Set,
+ e: LocateEvent,
+ ) {
const { children } = container;
const document = this.project.currentDocument!;
if (!children || children.isEmpty()) {
diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts
index bab9d20ab..3d885088e 100644
--- a/packages/designer/src/designer/designer.ts
+++ b/packages/designer/src/designer/designer.ts
@@ -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;
diff --git a/packages/designer/src/designer/dragon.ts b/packages/designer/src/designer/dragon.ts
index b53b6cf20..c336039ea 100644
--- a/packages/designer/src/designer/dragon.ts
+++ b/packages/designer/src/designer/dragon.ts
@@ -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 | 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 ============
diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts
index a4b1c21ab..752c7066f 100644
--- a/packages/engine/src/engine-core.ts
+++ b/packages/engine/src/engine-core.ts
@@ -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) {