diff --git a/lerna.json b/lerna.json index 92a6ed85d..87bc9fc56 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "2.11.0", - "version": "1.0.55", + "version": "1.0.56", "npmClient": "tnpm", "registry": "http://registry.npm.alibaba-inc.com", "useWorkspaces": true, diff --git a/package.json b/package.json index ad2023cce..d0e32422d 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,9 @@ "clean": "rm -rf ./packages/*/lib ./packages/*/es ./packages/*/dist ./packages/*/build", "lint": "eslint --ext .ts,.tsx,.js,.jsx ./ --quiet", "lint:fix": "eslint --ext .ts,.tsx,.js,.jsx ./ --quiet --fix", - "pub": "tnpm run watchdog:build && lerna publish patch --force-publish --exact", - "pub:prepatch": "tnpm run watchdog:build && lerna publish prepatch --force-publish --exact --dist-tag beta --preid beta", - "pub:prerelease": "tnpm run watchdog:build && lerna publish prerelease --force-publish --exact --dist-tag beta --preid beta", + "pub": "tnpm run watchdog:build && lerna publish patch --force-publish --exact --no-changelog", + "pub:prepatch": "tnpm run watchdog:build && lerna publish prepatch --force-publish --exact --dist-tag beta --preid beta --no-changelog", + "pub:prerelease": "tnpm run watchdog:build && lerna publish prerelease --force-publish --exact --dist-tag beta --preid beta --no-changelog", "setup": "./scripts/setup.sh", "setup:skip-build": "./scripts/setup-skip-build.sh", "start": "./scripts/start.sh", diff --git a/packages/designer/jest.config.js b/packages/designer/jest.config.js index 1a6063f1a..0c79d473b 100644 --- a/packages/designer/jest.config.js +++ b/packages/designer/jest.config.js @@ -6,7 +6,7 @@ module.exports = { // // '^.+\\.(ts|tsx)$': 'ts-jest', // // '^.+\\.(js|jsx)$': 'babel-jest', // }, - // testMatch: ['**/node.add.test.ts'], + // testMatch: ['**/setting-prop-entry.test.ts'], // testMatch: ['(/tests?/.*(test))\\.[jt]s$'], transformIgnorePatterns: [ `/node_modules/(?!${esModules})/`, @@ -22,6 +22,7 @@ module.exports = { '!src/builtin-simulator/utils/**', '!src/plugin/sequencify.ts', '!src/document/node/exclusive-group.ts', + '!src/document/node/props/value-to-source.ts', '!**/node_modules/**', '!**/vendor/**', ], diff --git a/packages/designer/package.json b/packages/designer/package.json index b80b5c458..87b357c24 100644 --- a/packages/designer/package.json +++ b/packages/designer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-designer", - "version": "1.0.55", + "version": "1.0.56", "description": "Designer for Ali LowCode Engine", "main": "lib/index.js", "module": "es/index.js", @@ -14,9 +14,9 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-editor-core": "1.0.55", - "@ali/lowcode-types": "1.0.55", - "@ali/lowcode-utils": "1.0.55", + "@ali/lowcode-editor-core": "1.0.56", + "@ali/lowcode-types": "1.0.56", + "@ali/lowcode-utils": "1.0.56", "classnames": "^2.2.6", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.5", 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 4cb8199df..efe00d474 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, isLowCodeComponent } 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 _node = _nodeInst?.node; + if (!_node) return { status: false }; + const { isRGL: _isRGL, rglNode: _rglNode } = _node.getRGL(); + const status = !!( + _isRGL && + _rglNode?.id !== rglNode?.id && + _rglNode?.getParent() !== node && + _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; + /** * 设置悬停处理 */ @@ -694,11 +795,14 @@ export class BuiltinSimulatorHost implements ISimulatorHost // 可能是 [null]; item && item.contains(targetElement), @@ -851,7 +955,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost | null { + getClosestNodeInstance( + from: ComponentInstance, + specId?: string, + ): NodeInstance | null { return this.renderer?.getClosestNodeInstance(from, specId) || null; } @@ -978,7 +1085,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; @@ -1157,7 +1269,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; @@ -1344,7 +1464,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/component-meta.ts b/packages/designer/src/component-meta.ts index 0fe273601..f8cc3a335 100644 --- a/packages/designer/src/component-meta.ts +++ b/packages/designer/src/component-meta.ts @@ -23,6 +23,7 @@ import { IconRemove } from './icons/remove'; import { IconClone } from './icons/clone'; import { ReactElement } from 'react'; import { IconHidden } from './icons/hidden'; +import EventEmitter from 'events'; function ensureAList(list?: string | string[]): string[] | null { if (!list) { @@ -66,6 +67,8 @@ export class ComponentMeta { private _npm?: NpmInfo; + private emitter: EventEmitter = new EventEmitter(); + get npm() { return this._npm; } @@ -178,10 +181,10 @@ export class ComponentMeta { this._title = typeof title === 'string' ? { - type: 'i18n', - 'en-US': this.componentName, - 'zh-CN': title, - } + type: 'i18n', + 'en-US': this.componentName, + 'zh-CN': title, + } : title; } @@ -229,6 +232,11 @@ export class ComponentMeta { this._isContainer = false; this._isModal = false; } + this.emitter.emit('metadata_change'); + } + + refreshMetadata() { + this.parseMetadata(this.getMetadata()); } private transformMetadata(metadta: ComponentMetadata): TransformedComponentMetadata { @@ -297,6 +305,13 @@ export class ComponentMeta { return true; } + onMetadataChange(fn: (args: any) => void): () => void { + this.emitter.on('metadata_change', fn); + return () => { + this.emitter.removeListener('metadata_change', fn); + }; + } + // compatiable vision prototype?: any; } @@ -436,6 +451,21 @@ const builtinComponentActions: ComponentAction[] = [ if (parent) { const newNode = doc.insertNode(parent, node, index + 1, true); newNode.select(); + const { isRGL, rglNode } = node.getRGL(); + if (isRGL) { + // 复制layout信息 + let layout = rglNode.getPropValue('layout') || []; + let curLayout = layout.filter((item) => item.i === node.getPropValue('fieldId')); + if (curLayout && curLayout[0]) { + layout.push({ + ...curLayout[0], + i: newNode.getPropValue('fieldId'), + }); + rglNode.setPropValue('layout', layout); + // 如果是磁贴块复制,则需要滚动到影响位置 + setTimeout(() => newNode.document.simulator?.scrollToNode(newNode), 10); + } + } } }, }, diff --git a/packages/designer/src/designer/builtin-hotkey.ts b/packages/designer/src/designer/builtin-hotkey.ts index 1ea18c9ad..73cc409b4 100644 --- a/packages/designer/src/designer/builtin-hotkey.ts +++ b/packages/designer/src/designer/builtin-hotkey.ts @@ -6,7 +6,9 @@ import clipboard from './clipboard'; function isInLiveEditing() { if (globalContext.has(Editor)) { - return Boolean(globalContext.get(Editor).get('designer')?.project?.simulator?.liveEditing?.editing); + return Boolean( + globalContext.get(Editor).get('designer')?.project?.simulator?.liveEditing?.editing, + ); } } 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..eb08e7b4a 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,10 @@ export interface ISensor { * 取消激活 */ deactiveSensor(): void; + /** + * 获取节点实例 + */ + getNodeInstanceFromElement(e: Element | null): NodeInstance | null; } export type DragObject = DragNodeObject | DragNodeDataObject | DragAnyObject; @@ -126,7 +130,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 +205,8 @@ export class Dragon { @obx.ref private _dragging = false; + @obx.ref private _canDrop = false; + get dragging(): boolean { return this._dragging; } @@ -239,17 +247,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 getRGL = (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 nodeInst?.node?.getRGL() || {}; + }; + const checkesc = (e: KeyboardEvent) => { if (e.keyCode === 27) { designer.clearLocation(); @@ -301,9 +318,26 @@ export class Dragon { return; } lastArrive = e; - const locateEvent = createLocateEvent(e); const sensor = chooseSensor(locateEvent); + const { isRGL, rglNode } = getRGL(e); + if (isRGL) { + this._canDrop = !!sensor?.locate(locateEvent); + if (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); + return; + } + } else { + this._canDrop = false; + this.emitter.emit('rgl.remove.placeholder'); + } if (sensor) { sensor.fixEvent(locateEvent); sensor.locate(locateEvent); @@ -363,6 +397,23 @@ export class Dragon { // end-tail drag process const over = (e?: any) => { + // 发送drop事件 + if (e) { + const { isRGL, rglNode } = getRGL(e); + if (isRGL && this._canDrop) { + const tarNode = dragObject.nodes[0]; + if (rglNode.id !== tarNode.id) { + // 避免死循环 + 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 +512,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 +601,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/designer/src/designer/setting/setting-prop-entry.ts b/packages/designer/src/designer/setting/setting-prop-entry.ts index 351af3cfe..5a8a369d1 100644 --- a/packages/designer/src/designer/setting/setting-prop-entry.ts +++ b/packages/designer/src/designer/setting/setting-prop-entry.ts @@ -118,6 +118,7 @@ export class SettingPropEntry implements SettingEntry { * 1 类似值,比如数组长度一样 * 2 单一植 */ + /* istanbul ignore next */ @computed get valueState(): number { if (this.type !== 'field') { const { getValue } = this.extraProps; @@ -155,7 +156,6 @@ export class SettingPropEntry implements SettingEntry { try { return getValue ? getValue(this, val) : val; } catch (e) { - // todo: add log console.warn(e); return val; } @@ -175,7 +175,7 @@ export class SettingPropEntry implements SettingEntry { try { setValue(this, val); } catch (e) { - // todo: add log + /* istanbul ignore next */ console.warn(e); } } @@ -198,7 +198,7 @@ export class SettingPropEntry implements SettingEntry { try { setValue(this, undefined); } catch (e) { - // todo: add log + /* istanbul ignore next */ console.warn(e); } } @@ -320,7 +320,7 @@ export class SettingPropEntry implements SettingEntry { return; } const v = this.getValue(); - if (isJSExpression(v)) { + if (this.isUseVariable()) { this.setValue(v.mock); } else { this.setValue({ diff --git a/packages/designer/src/designer/setting/setting-top-entry.ts b/packages/designer/src/designer/setting/setting-top-entry.ts index ab09d355c..6e03cc66a 100644 --- a/packages/designer/src/designer/setting/setting-top-entry.ts +++ b/packages/designer/src/designer/setting/setting-top-entry.ts @@ -80,6 +80,8 @@ export class SettingTopEntry implements SettingEntry { // clear fields this.setupItems(); + + this.setupEvents(); } private setupComponentMeta() { @@ -120,6 +122,12 @@ export class SettingTopEntry implements SettingEntry { } } + private setupEvents() { + this.componentMeta?.onMetadataChange(() => { + this.setupItems(); + }); + } + /** * 获取当前属性值 */ diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index d2dede1bc..0bc849f1a 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -110,7 +110,7 @@ export class Node { /** * @deprecated */ - private _addons: { [key: string]: { exportData: () => any; isProp: boolean; } } = {}; + private _addons: { [key: string]: { exportData: () => any; isProp: boolean } } = {}; @obx.ref private _parent: ParentalNode | null = null; @@ -156,8 +156,6 @@ export class Node { return this.componentMeta.icon; } - readonly settingEntry: SettingTopEntry; - private isInited = false; constructor(readonly document: DocumentModel, nodeSchema: Schema, options: any = {}) { @@ -168,16 +166,17 @@ export class Node { this.props = new Props(this, { children: isDOMText(children) || isJSExpression(children) ? children : '', }); - this.settingEntry = this.document.designer.createSettingEntry([this]); } else { // 这里 props 被初始化两次,一次 new,一次 import,new 的实例需要给 propsReducer 的钩子去使用, // import 是为了使用钩子返回的值,并非完全幂等的操作,部分行为执行两次会有 bug, // 所以在 props 里会对 new / import 做一些区别化的解析 this.props = new Props(this, props, extras); - this.settingEntry = this.document.designer.createSettingEntry([this]); this._children = new NodeChildren(this as ParentalNode, this.initialChildren(children)); this._children.internalInitParent(); - this.props.import(this.upgradeProps(this.initProps(props || {})), this.upgradeProps(extras || {})); + this.props.import( + this.upgradeProps(this.initProps(props || {})), + this.upgradeProps(extras || {}), + ); this.setupAutoruns(); } @@ -185,6 +184,14 @@ export class Node { this.emitter = new EventEmitter(); } + _settingEntry: SettingTopEntry; + + get settingEntry(): SettingTopEntry { + if (this._settingEntry) return this._settingEntry; + this._settingEntry = this.document.designer.createSettingEntry([this]); + return this._settingEntry; + } + private initProps(props: any): any { return this.document.designer.transformProps(props, this, TransformStage.Init); } @@ -221,6 +228,16 @@ export class Node { return children || []; } + private _isRGLContainer = false; + + set isRGLContainer(status: boolean) { + this._isRGLContainer = status; + } + + get isRGLContainer(): boolean { + return !!this._isRGLContainer; + } + isContainer(): boolean { return this.isParental() && this.componentMeta.isContainer; } @@ -335,7 +352,11 @@ export class Node { /** * 移除当前节点 */ - remove(useMutator = true, purge = true, options: NodeRemoveOptions = { suppressRemoveEvent: false }) { + remove( + useMutator = true, + purge = true, + options: NodeRemoveOptions = { suppressRemoveEvent: false }, + ) { if (this.parent) { if (!options.suppressRemoveEvent) { this.document.designer.editor?.emit('node.remove.topLevel', { @@ -616,15 +637,21 @@ export class Node { import(data: Schema, checkId = false) { const { componentName, id, children, props, ...extras } = data; if (this.isSlot()) { - foreachReverse(this.children, (subNode: Node) => { - subNode.remove(true, true); - }, (iterable, idx) => (iterable as NodeChildren).get(idx)); + foreachReverse( + this.children, + (subNode: Node) => { + subNode.remove(true, true); + }, + (iterable, idx) => (iterable as NodeChildren).get(idx), + ); } if (this.isParental()) { this.props.import(props, extras); (this._children as NodeChildren).import(children, checkId); } else { - this.props.get('children', true)!.setValue(isDOMText(children) || isJSExpression(children) ? children : ''); + this.props + .get('children', true)! + .setValue(isDOMText(children) || isJSExpression(children) ? children : ''); } } @@ -792,7 +819,8 @@ export class Node { * 是否可执行某action */ canPerformAction(action: string): boolean { - const availableActions = this.componentMeta?.availableActions?.map((action) => action.name) || []; + const availableActions = + this.componentMeta?.availableActions?.map((action) => action.name) || []; return availableActions.indexOf(action) >= 0; } @@ -854,7 +882,11 @@ export class Node { return this.children?.onChange(fn); } - mergeChildren(remover: () => any, adder: (children: Node[]) => NodeData[] | null, sorter: () => any) { + mergeChildren( + remover: () => any, + adder: (children: Node[]) => NodeData[] | null, + sorter: () => any, + ) { this.children?.mergeChildren(remover, adder, sorter); } @@ -907,6 +939,21 @@ export class Node { return this.document; } + /** + * 获取磁贴相关信息 + */ + getRGL() { + const isContainerNode = this.isContainer(); + const isEmptyNode = this.isEmpty(); + const isRGLContainerNode = this.isRGLContainer; + const isRGLNode = this.getParent()?.isRGLContainer; + const isRGL = isRGLContainerNode || (isRGLNode && (!isContainerNode || !isEmptyNode)); + let rglNode = isRGLContainerNode ? this : isRGL ? this?.getParent() : {}; + return { isContainerNode, isEmptyNode, isRGLContainerNode, isRGLNode, isRGL, rglNode }; + } + + /** + /** * @deprecated */ @@ -933,9 +980,11 @@ export class Node { return { container: dropElement, ref }; } const rootCanDropIn = this.componentMeta?.prototype?.options?.canDropIn; - if (rootCanDropIn === undefined - || rootCanDropIn === true - || (typeof rootCanDropIn === 'function' && rootCanDropIn(node))) { + if ( + rootCanDropIn === undefined || + rootCanDropIn === true || + (typeof rootCanDropIn === 'function' && rootCanDropIn(node)) + ) { return { container: this, ref }; } // 假如最后找不到合适位置,返回 null 阻止继续插入节点 @@ -944,9 +993,11 @@ export class Node { const canDropIn = this.componentMeta?.prototype?.options?.canDropIn; if (this.isContainer()) { - if (canDropIn === undefined || + if ( + canDropIn === undefined || (typeof canDropIn === 'boolean' && canDropIn) || - (typeof canDropIn === 'function' && canDropIn(node))) { + (typeof canDropIn === 'function' && canDropIn(node)) + ) { return { container: this, ref }; } } @@ -1044,6 +1095,10 @@ export function isRootNode(node: Node): node is RootNode { return node && node.isRoot(); } +export function isLowCodeComponent(node: Node): boolean { + return node.componentMeta?.getMetadata().devMode === 'lowcode'; +} + export function getZLevelTop(child: Node, zLevel: number): Node | null { let l = child.zLevel; if (l < zLevel || zLevel < 0) { @@ -1113,7 +1168,12 @@ export function comparePosition(node1: Node, node2: Node): PositionNO { return PositionNO.BeforeOrAfter; } -export function insertChild(container: ParentalNode, thing: Node | NodeData, at?: number | null, copy?: boolean): Node { +export function insertChild( + container: ParentalNode, + thing: Node | NodeData, + at?: number | null, + copy?: boolean, +): Node { let node: Node; if (isNode(thing) && (copy || thing.isSlot())) { thing = thing.export(TransformStage.Clone); diff --git a/packages/designer/src/document/node/props/prop.ts b/packages/designer/src/document/node/props/prop.ts index 79a3564f9..f8237e8f0 100644 --- a/packages/designer/src/document/node/props/prop.ts +++ b/packages/designer/src/document/node/props/prop.ts @@ -98,7 +98,7 @@ export class Prop implements IPropParent { return this.export(TransformStage.Serilize); } - export(stage: TransformStage = TransformStage.Save): CompositeValue | UNSET { + export(stage: TransformStage = TransformStage.Save): CompositeValue { stage = compatStage(stage); const type = this._type; if (stage === TransformStage.Render && this.key === '___condition___') { @@ -110,7 +110,6 @@ export class Prop implements IPropParent { } if (type === 'unset') { - // return UNSET; @康为 之后 review 下这块改造 return undefined; } @@ -147,10 +146,6 @@ export class Prop implements IPropParent { const maps: any = {}; this.items!.forEach((prop, key) => { const v = prop.export(stage); - // if (v !== UNSET) { - // maps[prop.key == null ? key : prop.key] = v; - // } - // @康为 之后 review 下这块改造 maps[prop.key == null ? key : prop.key] = v; }); return maps; @@ -161,12 +156,9 @@ export class Prop implements IPropParent { return this._value; } return this.items!.map((prop) => { - const v = prop.export(stage); - return v === UNSET ? undefined : v; + return prop.export(stage); }); } - - return undefined; } private _code: string | null = null; @@ -246,7 +238,7 @@ export class Prop implements IPropParent { } else { this._type = 'map'; } - } else { + } /* istanbul ignore next */ else { this._type = 'expression'; this._value = { type: 'JSExpression', @@ -267,11 +259,7 @@ export class Prop implements IPropParent { } @computed getValue(): CompositeValue { - const v = this.export(TransformStage.Serilize); - if (v === UNSET) { - return undefined; - } - return v; + return this.export(TransformStage.Serilize); } private dispose() { @@ -563,7 +551,7 @@ export class Prop implements IPropParent { items.push(prop); maps.set(key, prop); } - } else { + } /* istanbul ignore next */ else { return null; } diff --git a/packages/designer/src/document/node/props/props.ts b/packages/designer/src/document/node/props/props.ts index 2d24e477d..1e900710f 100644 --- a/packages/designer/src/document/node/props/props.ts +++ b/packages/designer/src/document/node/props/props.ts @@ -161,8 +161,9 @@ export class Props implements IPropParent { /** * @deprecated */ + /* istanbul ignore next */ private transformToStatic(props: any) { - let transducers = this.owner.componentMeta.prototype?.options?.transducers; + let transducers = this.owner.componentMeta?.prototype?.options?.transducers; if (!transducers) { return props; } diff --git a/packages/designer/tests/builtin-simulator/host.test.ts b/packages/designer/tests/builtin-simulator/host.test.ts index 9b4121267..ab96b20ec 100644 --- a/packages/designer/tests/builtin-simulator/host.test.ts +++ b/packages/designer/tests/builtin-simulator/host.test.ts @@ -408,7 +408,7 @@ describe('Host 测试', () => { host.setupContextMenu(); host.getNodeInstanceFromElement = () => { return { - node: { componentMeta: { componentName: 'Button' } }, + node: { componentMeta: { componentName: 'Button', getMetadata() { return {} } } }, }; }; const mockFn = jest.fn(); @@ -428,7 +428,7 @@ describe('Host 测试', () => { dispatchEvent() {}, }; - // 非法分支测试 + // 非法分支测试 host.mountContentFrame(); expect(host._iframe).toBeUndefined(); diff --git a/packages/designer/tests/designer/setting/setting-prop-entry.test.ts b/packages/designer/tests/designer/setting/setting-prop-entry.test.ts index 655dea54c..e5bb5f6c5 100644 --- a/packages/designer/tests/designer/setting/setting-prop-entry.test.ts +++ b/packages/designer/tests/designer/setting/setting-prop-entry.test.ts @@ -1,8 +1,11 @@ +// @ts-nocheck import set from 'lodash/set'; import cloneDeep from 'lodash/cloneDeep'; import '../../fixtures/window'; import { Editor } from '@ali/lowcode-editor-core'; import { Project } from '../../../src/project/project'; +import { SettingTopEntry } from '../../../src/designer/setting/setting-top-entry'; +import { SettingPropEntry } from '../../../src/designer/setting/setting-prop-entry'; import { Node } from '../../../src/document/node/node'; import { Designer } from '../../../src/designer/designer'; import formSchema from '../../../fixtures/schema/form'; @@ -28,6 +31,119 @@ describe('setting-prop-entry 测试', () => { doc = null; }); + describe('纯粹的 UnitTest', () => { + let mockNode: Node; + let mockTopEntry: SettingTopEntry; + beforeEach(() => { + mockNode = new Node(designer.currentDocument, { + componentName: 'Button', + props: { + a: 'str', + b: 222, + obj: { + x: 1, + }, + jse: { + type: 'JSExpression', + value: 'state.a', + mock: 111, + } + }, + }); + mockTopEntry = new SettingTopEntry(editor, [mockNode]); + }); + afterEach(() => { + mockNode = null; + mockTopEntry = null; + }); + + it('常规方法', () => { + // type: group 类型 + const prop = new SettingPropEntry(mockTopEntry, 'xGroup', 'group'); + expect(prop.setKey('xxx')).toBeUndefined(); + expect(prop.remove()).toBeUndefined(); + + const prop2 = new SettingPropEntry(mockTopEntry, '#xGroup'); + expect(prop2.setKey('xxx')).toBeUndefined(); + expect(prop2.remove()).toBeUndefined(); + + expect(prop.getVariableValue()).toBe(''); + }); + + it('setValue / getValue / onValueChange', () => { + // 获取已有的 prop + const prop1 = mockTopEntry.getProp('a'); + prop1.extraProps = { + getValue: (prop, val) => `prefix ${val}`, + setValue: (prop, val) => { prop.setValue(`modified ${val}`, false, false, { disableMutator: true }) }, + defaultValue: 'default', + }; + + expect(prop1.getDefaultValue()).toBe('default'); + expect(prop1.getValue()).toBe('prefix str'); + + // disableMutator: true + prop1.setValue('bbb', false, false, { disableMutator: true }); + expect(prop1.getValue()).toBe('prefix bbb'); + + // disableMutator: false + prop1.setValue('bbb'); + expect(prop1.getValue()).toBe('prefix modified bbb'); + + const mockFn3 = jest.fn(); + const prop2 = mockTopEntry.getProp('obj'); + const prop3 = prop2.get('x'); + const offFn = prop3.onValueChange(mockFn3); + expect(prop3.getValue()).toBe(1); + prop3.setValue(2); + expect(mockFn3).toHaveBeenCalled(); + + offFn(); + prop3.setValue(3); + mockFn3.mockClear(); + expect(mockFn3).toHaveBeenCalledTimes(0); + + const prop4 = mockTopEntry.getProp('b'); + prop4.extraProps = { + getValue: () => { throw 'error'; }, + }; + expect(prop4.getValue()).toBe(222); + }); + + it('clearValue', () => { + const prop1 = mockTopEntry.getProp('a'); + prop1.clearValue(); + expect(prop1.getValue()).toBeUndefined(); + + const mockFn = jest.fn(); + prop1.extraProps = { + setValue: mockFn, + }; + prop1.clearValue(); + expect(mockFn).toHaveBeenCalled(); + }); + + it('getVariableValue/ setUseVariable / isUseVariable / getMockOrValue', () => { + const prop1 = mockTopEntry.getProp('jse'); + + expect(prop1.isUseVariable()).toBeTruthy(); + expect(prop1.useVariable).toBeTruthy(); + + expect(prop1.getMockOrValue()).toEqual(111); + expect(prop1.getVariableValue()).toEqual('state.a'); + + prop1.setUseVariable(false); + expect(prop1.getValue()).toEqual(111); + prop1.setUseVariable(true); + expect(prop1.getValue()).toEqual({ + type: 'JSExpression', + value: '', + mock: 111, + }); + prop1.setUseVariable(true); + }); + }); + describe('node 构造函数生成 settingEntry', () => { it('常规方法测试', () => { const divNode = doc?.getNode('div'); @@ -81,9 +197,7 @@ describe('setting-prop-entry 测试', () => { expect(divNode?.getProp('behavior').getValue()).toBeUndefined(); }); - it.skip('type: group 场景测试', () => { - - }); + it.skip('type: group 场景测试', () => {}); it('JSExpression 类型的 prop', () => { designer.createComponentMeta(divMeta); diff --git a/packages/designer/tests/designer/setting/setting-top-entry.test.ts b/packages/designer/tests/designer/setting/setting-top-entry.test.ts index 94fdbab90..b0dc27012 100644 --- a/packages/designer/tests/designer/setting/setting-top-entry.test.ts +++ b/packages/designer/tests/designer/setting/setting-top-entry.test.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import set from 'lodash/set'; import cloneDeep from 'lodash/cloneDeep'; import '../../fixtures/window'; @@ -75,6 +76,31 @@ describe('setting-top-entry 测试', () => { settingEntry.getValue(); }); + it('onMetadataChange', () => { + designer.createComponentMeta(divMeta); + designer.project.open(settingSchema); + const { currentDocument } = designer.project; + const divNode = currentDocument?.getNode('div') as Node; + + const { settingEntry } = divNode!; + const mockFn = jest.fn(); + settingEntry.componentMeta.onMetadataChange(mockFn); + settingEntry.componentMeta.refreshMetadata(); + expect(mockFn).toHaveBeenCalled(); + }); + + it.skip('setupItems - customView', () => { + designer.createComponentMeta(divMeta); + designer.project.open(settingSchema); + const { currentDocument } = designer.project; + const divNode = currentDocument?.getNode('div') as Node; + + const { settingEntry } = divNode; + // 模拟将第一个配置变成 react funcional component + settingEntry.componentMeta.getMetadata().combined[0].items[0] = props => props.xx; + settingEntry.setupItems(); + }); + it('清理方法测试', () => { designer.createComponentMeta(divMeta); designer.project.open(settingSchema); diff --git a/packages/designer/tests/document/node/node.add.test.ts b/packages/designer/tests/document/node/node.add.test.ts index 200fe524f..c9dd53d1f 100644 --- a/packages/designer/tests/document/node/node.add.test.ts +++ b/packages/designer/tests/document/node/node.add.test.ts @@ -64,6 +64,10 @@ describe('schema 生成节点模型测试', () => { const exportSchema = currentDocument?.export(1); expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt); + nodesMap.forEach(node => { + // 触发 getter + node.settingEntry; + }); expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt); }); diff --git a/packages/designer/tests/document/node/props/prop.test.ts b/packages/designer/tests/document/node/props/prop.test.ts index 00ab084e5..3c01de7ec 100644 --- a/packages/designer/tests/document/node/props/prop.test.ts +++ b/packages/designer/tests/document/node/props/prop.test.ts @@ -1,16 +1,32 @@ +// @ts-nocheck import '../../../fixtures/window'; -import { set, delayObxTick } from '../../../utils'; -import { Editor } from '@ali/lowcode-editor-core'; -import { Props } from '../../../../src/document/node/props/props'; +import { delayObxTick } from '../../../utils'; +import { Editor, engineConfig } from '@ali/lowcode-editor-core'; import { Designer } from '../../../../src/designer/designer'; -import { Project } from '../../../../src/project/project'; import { DocumentModel } from '../../../../src/document/document-model'; import { Prop, isProp, isValidArrayIndex } from '../../../../src/document/node/props/prop'; import { TransformStage } from '@ali/lowcode-types'; - +const slotNodeImportMockFn = jest.fn(); +const slotNodeRemoveMockFn = jest.fn(); const mockedOwner = { componentName: 'Div', + addSlot() {}, + document: { + createNode(schema) { + return { + ...schema, + addSlot() {}, + internalSetSlotFor() {}, + import: slotNodeImportMockFn, + export() { + return schema; + }, + remove: slotNodeRemoveMockFn, + }; + }, + designer: {}, + }, }; const mockedPropsInst = { @@ -32,7 +48,19 @@ describe('Prop 类测试', () => { numProp = new Prop(mockedPropsInst, 1, 'numProp'); nullProp = new Prop(mockedPropsInst, null, 'nullProp'); expProp = new Prop(mockedPropsInst, { type: 'JSExpression', value: 'state.haha' }, 'expProp'); - // slotProp = new Prop(mockedPropsInst, { type: 'JSSlot', value: [{ componentName: 'Button' }] }, 'slotProp'); + slotProp = new Prop( + mockedPropsInst, + { + type: 'JSSlot', + title: '测试 slot', + name: 'testSlot', + params: { a: 1 }, + value: [{ componentName: 'Button' }], + }, + 'slotProp', + ); + slotNodeImportMockFn.mockClear(); + slotNodeRemoveMockFn.mockClear(); }); afterEach(() => { boolProp.purge(); @@ -40,7 +68,7 @@ describe('Prop 类测试', () => { numProp.purge(); nullProp.purge(); expProp.purge(); - // slotProp.purge(); + slotProp.purge(); }); it('consturctor / getProps / getNode', () => { @@ -60,6 +88,7 @@ describe('Prop 类测试', () => { expect(numProp.set()).toBeNull(); expect(numProp.has()).toBeFalsy(); + expect(numProp.path).toEqual(['numProp']); }); it('getValue / getAsString / setValue', () => { @@ -121,7 +150,28 @@ describe('Prop 类测试', () => { expect( new Prop(mockedPropsInst, false, '___condition___').export(TransformStage.Render), ).toBeTruthy(); - // console.log(slotProp.export(TransformStage.Render)); + engineConfig.set('enableCondition', true); + expect( + new Prop(mockedPropsInst, false, '___condition___').export(TransformStage.Render), + ).toBeFalsy(); + expect(slotProp.export(TransformStage.Render)).toEqual({ + type: 'JSSlot', + params: { a: 1 }, + value: { + componentName: 'Slot', + title: '测试 slot', + name: 'testSlot', + params: { a: 1 }, + children: [{ componentName: 'Button' }], + }, + }); + expect(slotProp.export(TransformStage.Save)).toEqual({ + type: 'JSSlot', + params: { a: 1 }, + value: [{ componentName: 'Button' }], + title: '测试 slot', + name: 'testSlot', + }); }); it('compare', () => { @@ -145,6 +195,21 @@ describe('Prop 类测试', () => { boolProp.purge(); }); + it('slot', () => { + // 更新 slot + slotProp.setValue({ + type: 'JSSlot', + value: [{ + componentName: 'Form', + }] + }); + expect(slotNodeImportMockFn).toBeCalled(); + + // 节点类型转换 + slotProp.setValue(true); + expect(slotNodeRemoveMockFn).toBeCalled(); + }); + it('迭代器 / map / forEach', () => { const mockedFn = jest.fn(); for (const item of strProp) { @@ -153,13 +218,13 @@ describe('Prop 类测试', () => { expect(mockedFn).not.toHaveBeenCalled(); mockedFn.mockClear(); - strProp.forEach(item => { + strProp.forEach((item) => { mockedFn(); }); expect(mockedFn).not.toHaveBeenCalled(); mockedFn.mockClear(); - strProp.map(item => { + strProp.map((item) => { return mockedFn(); }); expect(mockedFn).not.toHaveBeenCalled(); @@ -201,7 +266,6 @@ describe('Prop 类测试', () => { z2: 'str', }); - expect(prop.getPropValue('a')).toBe(1); prop.setPropValue('a', 2); expect(prop.getPropValue('a')).toBe(2); @@ -283,13 +347,13 @@ describe('Prop 类测试', () => { expect(mockedFn).toHaveBeenCalledTimes(5); mockedFn.mockClear(); - prop.forEach(item => { + prop.forEach((item) => { mockedFn(); }); expect(mockedFn).toHaveBeenCalledTimes(5); mockedFn.mockClear(); - prop.map(item => { + prop.map((item) => { return mockedFn(); }); expect(mockedFn).toHaveBeenCalledTimes(5); @@ -297,6 +361,7 @@ describe('Prop 类测试', () => { }); it('dispose', () => { + prop.items; prop.dispose(); expect(prop._items).toBeNull(); @@ -321,9 +386,15 @@ describe('Prop 类测试', () => { expect(prop.get(2).getValue()).toBe('haha'); expect(prop.getAsString()).toBe(''); + + prop.unset(); + prop.set(0, true); + expect(prop.set('x', 'invalid')).toBeNull(); + expect(prop.get(0).getValue()).toBeTruthy(); }); it('export', () => { + expect(prop.export()).toEqual([1, true, 'haha']); // 触发构造 prop.items; expect(prop.export()).toEqual([1, true, 'haha']); @@ -351,18 +422,22 @@ describe('Prop 类测试', () => { const designer = new Designer({ editor }); const doc = new DocumentModel(designer.project, { componentName: 'Page', - children: [{ - id: 'div', - componentName: 'Div', - }], + children: [ + { + id: 'div', + componentName: 'Div', + }, + ], }); const div = doc.getNode('div'); const slotProp = new Prop(div?.getProps(), { type: 'JSSlot', - value: [{ - componentName: 'Button', - }], + value: [ + { + componentName: 'Button', + }, + ], }); expect(slotProp.slotNode?.componentName).toBe('Slot'); diff --git a/packages/designer/tests/document/node/props/props.test.ts b/packages/designer/tests/document/node/props/props.test.ts index 56597ecc2..eb49568a5 100644 --- a/packages/designer/tests/document/node/props/props.test.ts +++ b/packages/designer/tests/document/node/props/props.test.ts @@ -1,4 +1,4 @@ - +// @ts-nocheck import '../../../fixtures/window'; import { set, delayObxTick } from '../../../utils'; import { Editor } from '@ali/lowcode-editor-core'; diff --git a/packages/designer/tests/document/node/props/value-to-source.test.ts b/packages/designer/tests/document/node/props/value-to-source.test.ts index 2694211e8..43ed19b2a 100644 --- a/packages/designer/tests/document/node/props/value-to-source.test.ts +++ b/packages/designer/tests/document/node/props/value-to-source.test.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import '../../../fixtures/silent-console'; import { getSource, valueToSource } from '../../../../src/document/node/props/value-to-source'; diff --git a/packages/designer/tests/project/project.test.ts b/packages/designer/tests/project/project.test.ts index 1fa17f8e4..6f2ddddb5 100644 --- a/packages/designer/tests/project/project.test.ts +++ b/packages/designer/tests/project/project.test.ts @@ -59,6 +59,10 @@ describe('schema 生成节点模型测试', () => { const exportSchema = currentDocument?.export(1); expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt); + nodesMap.forEach(node => { + // 触发 getter + node.settingEntry; + }); expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt); }); @@ -77,6 +81,10 @@ describe('schema 生成节点模型测试', () => { const exportSchema = currentDocument?.export(1); expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt); + nodesMap.forEach(node => { + // 触发 getter + node.settingEntry; + }); expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt); }); diff --git a/packages/editor-core/package.json b/packages/editor-core/package.json index afca8834e..02912993e 100644 --- a/packages/editor-core/package.json +++ b/packages/editor-core/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-core", - "version": "1.0.55", + "version": "1.0.56", "description": "Core Api for Ali lowCode engine", "license": "MIT", "main": "lib/index.js", @@ -13,8 +13,8 @@ "build": "build-scripts build --skip-demo" }, "dependencies": { - "@ali/lowcode-types": "1.0.55", - "@ali/lowcode-utils": "1.0.55", + "@ali/lowcode-types": "1.0.56", + "@ali/lowcode-utils": "1.0.56", "@alifd/next": "^1.19.16", "@recore/obx": "^1.0.9", "@recore/obx-react": "^1.0.8", diff --git a/packages/editor-preset-general/package.json b/packages/editor-preset-general/package.json index 281e2c9b0..d5a669f1e 100644 --- a/packages/editor-preset-general/package.json +++ b/packages/editor-preset-general/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-preset-general", - "version": "1.0.55", + "version": "1.0.56", "description": "Ali General Editor Preset", "main": "lib/index.js", "private": true, @@ -14,12 +14,12 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-editor-core": "1.0.55", - "@ali/lowcode-editor-skeleton": "1.0.55", - "@ali/lowcode-plugin-designer": "1.0.55", - "@ali/lowcode-plugin-outline-pane": "1.0.55", - "@ali/lowcode-types": "1.0.55", - "@ali/lowcode-utils": "1.0.55", + "@ali/lowcode-editor-core": "1.0.56", + "@ali/lowcode-editor-skeleton": "1.0.56", + "@ali/lowcode-plugin-designer": "1.0.56", + "@ali/lowcode-plugin-outline-pane": "1.0.56", + "@ali/lowcode-types": "1.0.56", + "@ali/lowcode-utils": "1.0.56", "@alifd/next": "^1.19.12", "@alife/theme-lowcode-dark": "^0.1.0", "@alife/theme-lowcode-light": "^0.1.0", diff --git a/packages/editor-skeleton/package.json b/packages/editor-skeleton/package.json index a64ac2e17..fe517ad60 100644 --- a/packages/editor-skeleton/package.json +++ b/packages/editor-skeleton/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-skeleton", - "version": "1.0.55", + "version": "1.0.56", "description": "alibaba lowcode editor skeleton", "main": "lib/index.js", "module": "es/index.js", @@ -17,10 +17,10 @@ "editor" ], "dependencies": { - "@ali/lowcode-designer": "1.0.55", - "@ali/lowcode-editor-core": "1.0.55", - "@ali/lowcode-types": "1.0.55", - "@ali/lowcode-utils": "1.0.55", + "@ali/lowcode-designer": "1.0.56", + "@ali/lowcode-editor-core": "1.0.56", + "@ali/lowcode-types": "1.0.56", + "@ali/lowcode-utils": "1.0.56", "@ali/ve-icons": "latest", "@ali/ve-less-variables": "^2.0.0", "@alifd/next": "^1.20.12", diff --git a/packages/engine/package.json b/packages/engine/package.json index 3c6576546..4e346c917 100644 --- a/packages/engine/package.json +++ b/packages/engine/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-engine", - "version": "1.0.55", + "version": "1.0.56", "description": "Universal API for AliLowCode engine", "main": "lib/engine-core.js", "module": "es/engine-core.js", @@ -18,14 +18,14 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-designer": "1.0.55", - "@ali/lowcode-editor-core": "1.0.55", + "@ali/lowcode-designer": "1.0.56", + "@ali/lowcode-editor-core": "1.0.56", "@ali/lowcode-editor-setters": "1.0.30", - "@ali/lowcode-editor-skeleton": "1.0.55", + "@ali/lowcode-editor-skeleton": "1.0.56", "@ali/lowcode-engine-ext": "^1.0.0", - "@ali/lowcode-plugin-designer": "1.0.55", - "@ali/lowcode-plugin-outline-pane": "1.0.55", - "@ali/lowcode-utils": "1.0.55", + "@ali/lowcode-plugin-designer": "1.0.56", + "@ali/lowcode-plugin-outline-pane": "1.0.56", + "@ali/lowcode-utils": "1.0.56", "@ali/ve-i18n-util": "^2.0.0", "@ali/ve-icons": "^4.1.9", "@ali/ve-less-variables": "2.0.3", diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index a4b1c21ab..9ba16ed12 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -195,6 +195,14 @@ interface EngineOptions { * 关闭画布自动渲染,在资产包多重异步加载的场景有效,默认值:false */ disableAutoRender?: boolean; + /** + * 关闭拖拽组件时的虚线响应,性能考虑,默认值:false + */ + disableDetecting?: boolean; + /** + * 定制画布中点击被忽略的 selectors,默认值:(selectors) => selectors + */ + customizeIgnoreSelectors?: (defaultIgnoreSelectors: string[]) => string[]; /** * Vision-polyfill settings */ @@ -203,7 +211,7 @@ interface EngineOptions { disableCompatibleReducer?: boolean; // 是否开启在 render 阶段开启 filter reducer,默认值:false enableFilterReducerInRenderStage?: boolean; - } + }; [key: string]: any; } export async function init(container?: Element, options?: EngineOptions) { diff --git a/packages/ignitor/package.json b/packages/ignitor/package.json index a82250e27..31d7b9097 100644 --- a/packages/ignitor/package.json +++ b/packages/ignitor/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-ignitor", - "version": "1.0.55", + "version": "1.0.56", "description": "点火器,bootstrap lce project", "main": "lib/index.js", "private": true, diff --git a/packages/plugin-designer/package.json b/packages/plugin-designer/package.json index f4a868f2d..935375040 100644 --- a/packages/plugin-designer/package.json +++ b/packages/plugin-designer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-designer", - "version": "1.0.55", + "version": "1.0.56", "description": "alibaba lowcode editor designer plugin", "files": [ "es", @@ -18,7 +18,7 @@ ], "author": "xiayang.xy", "dependencies": { - "@ali/lowcode-editor-core": "1.0.55", + "@ali/lowcode-editor-core": "1.0.56", "react": "^16.8.1", "react-dom": "^16.8.1" }, diff --git a/packages/plugin-outline-pane/package.json b/packages/plugin-outline-pane/package.json index 727c13a0f..d33f7821f 100644 --- a/packages/plugin-outline-pane/package.json +++ b/packages/plugin-outline-pane/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-outline-pane", - "version": "1.0.55", + "version": "1.0.56", "description": "Outline pane for Ali lowCode engine", "files": [ "es", @@ -12,10 +12,10 @@ "build": "build-scripts build --skip-demo" }, "dependencies": { - "@ali/lowcode-designer": "1.0.55", - "@ali/lowcode-editor-core": "1.0.55", - "@ali/lowcode-types": "1.0.55", - "@ali/lowcode-utils": "1.0.55", + "@ali/lowcode-designer": "1.0.56", + "@ali/lowcode-editor-core": "1.0.56", + "@ali/lowcode-types": "1.0.56", + "@ali/lowcode-utils": "1.0.56", "@alifd/next": "^1.19.16", "classnames": "^2.2.6", "react": "^16", diff --git a/packages/rax-renderer/package.json b/packages/rax-renderer/package.json index 70f4cc679..3f59ed409 100644 --- a/packages/rax-renderer/package.json +++ b/packages/rax-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-rax-renderer", - "version": "1.0.55", + "version": "1.0.56", "description": "Rax renderer for Ali lowCode engine", "main": "lib/index.js", "module": "es/index.js", @@ -30,8 +30,8 @@ "build": "build-scripts build" }, "dependencies": { - "@ali/lowcode-renderer-core": "1.0.55", - "@ali/lowcode-utils": "1.0.55", + "@ali/lowcode-renderer-core": "1.0.56", + "@ali/lowcode-utils": "1.0.56", "rax-find-dom-node": "^1.0.1" }, "devDependencies": { diff --git a/packages/rax-simulator-renderer/package.json b/packages/rax-simulator-renderer/package.json index 50084cabf..e62863647 100644 --- a/packages/rax-simulator-renderer/package.json +++ b/packages/rax-simulator-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-rax-simulator-renderer", - "version": "1.0.55", + "version": "1.0.56", "description": "rax simulator renderer for alibaba lowcode designer", "main": "lib/index.js", "module": "es/index.js", @@ -11,10 +11,10 @@ "cloud-build": "build-scripts build --skip-demo" }, "dependencies": { - "@ali/lowcode-designer": "1.0.55", - "@ali/lowcode-rax-renderer": "1.0.55", - "@ali/lowcode-types": "1.0.55", - "@ali/lowcode-utils": "1.0.55", + "@ali/lowcode-designer": "1.0.56", + "@ali/lowcode-rax-renderer": "1.0.56", + "@ali/lowcode-types": "1.0.56", + "@ali/lowcode-utils": "1.0.56", "@ali/recore-rax": "^1.2.4", "@ali/vu-css-style": "^1.0.2", "@recore/obx": "^1.0.8", @@ -55,6 +55,6 @@ "publishConfig": { "registry": "https://registry.npm.alibaba-inc.com" }, - "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-rax-simulator-renderer@1.0.54/build/index.html", + "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-rax-simulator-renderer@1.0.55/build/index.html", "gitHead": "3bfd7df92985ec6c9d2ccb8ba95d7b0829fa2b1d" } diff --git a/packages/react-renderer/package.json b/packages/react-renderer/package.json index 5514c39a2..75918c668 100644 --- a/packages/react-renderer/package.json +++ b/packages/react-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-react-renderer", - "version": "1.0.55", + "version": "1.0.56", "description": "react renderer for ali lowcode engine", "main": "lib/index.js", "module": "es/index.js", @@ -23,7 +23,7 @@ "react" ], "dependencies": { - "@ali/lowcode-renderer-core": "1.0.55", + "@ali/lowcode-renderer-core": "1.0.56", "@alifd/next": "^1.21.16" }, "devDependencies": { diff --git a/packages/react-simulator-renderer/package.json b/packages/react-simulator-renderer/package.json index aa942e06c..16b44f27b 100644 --- a/packages/react-simulator-renderer/package.json +++ b/packages/react-simulator-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-react-simulator-renderer", - "version": "1.0.55", + "version": "1.0.56", "description": "react simulator renderer for alibaba lowcode designer", "main": "lib/index.js", "module": "es/index.js", @@ -14,10 +14,10 @@ "build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --skip-demo" }, "dependencies": { - "@ali/lowcode-designer": "1.0.55", - "@ali/lowcode-react-renderer": "1.0.55", - "@ali/lowcode-types": "1.0.55", - "@ali/lowcode-utils": "1.0.55", + "@ali/lowcode-designer": "1.0.56", + "@ali/lowcode-react-renderer": "1.0.56", + "@ali/lowcode-types": "1.0.56", + "@ali/lowcode-utils": "1.0.56", "@ali/vu-css-style": "^1.0.2", "@recore/obx": "^1.0.8", "@recore/obx-react": "^1.0.7", diff --git a/packages/renderer-core/package.json b/packages/renderer-core/package.json index 3f28ae2ba..efaaa5862 100644 --- a/packages/renderer-core/package.json +++ b/packages/renderer-core/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-renderer-core", - "version": "1.0.55", + "version": "1.0.56", "description": "renderer core", "license": "MIT", "main": "lib/index.js", diff --git a/packages/types/package.json b/packages/types/package.json index a2df7bc5c..96a876dd4 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-types", - "version": "1.0.55", + "version": "1.0.56", "description": "Types for Ali lowCode engine", "files": [ "es", diff --git a/packages/utils/package.json b/packages/utils/package.json index b9af19e92..2641ee7da 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-utils", - "version": "1.0.55", + "version": "1.0.56", "description": "Utils for Ali lowCode engine", "files": [ "es", @@ -12,7 +12,7 @@ "build": "build-scripts build --skip-demo" }, "dependencies": { - "@ali/lowcode-types": "1.0.55", + "@ali/lowcode-types": "1.0.56", "@alifd/next": "^1.19.16", "lodash.get": "^4.4.2", "react": "^16" diff --git a/packages/vision-polyfill/package.json b/packages/vision-polyfill/package.json index d3099ab8d..234ad6655 100644 --- a/packages/vision-polyfill/package.json +++ b/packages/vision-polyfill/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-vision-polyfill", - "version": "1.0.55", + "version": "1.0.56", "description": "Vision Polyfill for Ali lowCode engine", "main": "lib/index.js", "private": true, @@ -18,10 +18,10 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-designer": "1.0.55", - "@ali/lowcode-editor-core": "1.0.55", - "@ali/lowcode-editor-skeleton": "1.0.55", - "@ali/lowcode-utils": "1.0.55", + "@ali/lowcode-designer": "1.0.56", + "@ali/lowcode-editor-core": "1.0.56", + "@ali/lowcode-editor-skeleton": "1.0.56", + "@ali/lowcode-utils": "1.0.56", "@ali/ve-i18n-util": "^2.0.0", "@ali/ve-icons": "^4.1.9", "@ali/ve-less-variables": "2.0.3", diff --git a/tsconfig.json b/tsconfig.json index 556a1fea8..fce8ef03d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -37,5 +37,5 @@ }, "outDir": "lib" }, - "exclude": ["**/test", "**/lib", "**/es", "node_modules"] + "exclude": ["**/tests/*", "**/*.test.ts", "**/lib", "**/es", "node_modules"] }