diff --git a/docs/docs/api/model/node-children.md b/docs/docs/api/model/node-children.md index 219e6bbc1..5507fcbfb 100644 --- a/docs/docs/api/model/node-children.md +++ b/docs/docs/api/model/node-children.md @@ -62,11 +62,11 @@ delete(node: IPublicModelNode): boolean; ```typescript /** - * 删除指定节点 - * delete the node + * 插入一个节点 + * insert the node * @param node */ -delete(node: IPublicModelNode): boolean; +insert(node: IPublicModelNode): boolean; ``` 相关类型:[IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts) diff --git a/docs/docs/api/model/window.md b/docs/docs/api/model/window.md index 3cc8b5d5e..9db39996f 100644 --- a/docs/docs/api/model/window.md +++ b/docs/docs/api/model/window.md @@ -45,6 +45,8 @@ sidebar_position: 12 关联模型 [IPublicModelEditorView](./editor-view) +**@since v1.1.7** + ### editorViews 窗口所有视图 @@ -53,6 +55,7 @@ sidebar_position: 12 关联模型 [IPublicModelEditorView](./editor-view) +**@since v1.1.7** ## 方法 @@ -90,3 +93,15 @@ onChangeViewType(fn: (viewName: string) => void): IPublicTypeDisposable; ``` 相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) + +### onSave + +窗口视图保存事件 + +``` +onSave(fn: () => void): IPublicTypeDisposable; +``` + +相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) + +**@since v1.1.7** diff --git a/docs/docs/guide/expand/editor/graph.md b/docs/docs/guide/expand/editor/graph.md new file mode 100644 index 000000000..2d5127054 --- /dev/null +++ b/docs/docs/guide/expand/editor/graph.md @@ -0,0 +1,155 @@ +--- +title: 图编排扩展 +sidebar_position: 9 +--- +## 项目运行 +### 前置准备 +1. 参考 https://lowcode-engine.cn/site/docs/guide/quickStart/start +2. 参考至Demo下载 https://lowcode-engine.cn/site/docs/guide/quickStart/start#%E4%B8%8B%E8%BD%BD-demo +### 选择demo-graph-x6 +在根目录下执行: +```bash +cd demo-graph-x6 +``` +### 安装依赖 +在 lowcode-demo/demo-graph-x6目录下执行: +```bash +npm install +``` +### 启动Demo +在 lowcode-demo/demo-graph-x6 目录下执行: +```bash +npm run start +``` +之后就可以通过 http://localhost:5556/ 来访问我们的 DEMO 了。 + +## 认识Demo +这里的Demo即通过图编排引擎加入了几个简单的物料而来,已经是可以面向真是用户的产品界面。 +![image.png](https://img.alicdn.com/imgextra/i1/O1CN016TbCI31hM2sJy8qkR_!!6000000004262-2-tps-5120-2726.png) +### 区域组成 +#### 顶部:操作区​ +- 右侧:保存到本地、重置页面、自定义按钮 +#### 顶部:工具区 +- 左侧:删除、撤销、重做、放大、缩小 +#### 左侧:面板与操作区​ +- 物料面板:可以查找节点,并在此拖动节点到编辑器画布中 +#### 中部:可视化页面编辑画布区域​ +- 点击节点/边在右侧面板中能够显示出对应组件的属性配置选项 +- 拖拽修改节点的排列顺序 +#### 右侧:组件级别配置​ +- 选中的组件:从页面开始一直到当前选中的节点/边位置,点击对应的名称可以切换到对应的节点上 +- 选中组件的配置:属性:节点的基础属性值设置 + +> 每个区域的组成都可以被替换和自定义来生成开发者需要的业务产品。 + +## 目录介绍 +![image.png](https://img.alicdn.com/imgextra/i3/O1CN01Luc8gr1tLq5QTbpb9_!!6000000005886-0-tps-832-1522.jpg) + +- public:与其他demo保持一致,均是lowcode engine所必要依赖 +- src + - plugins::自定义插件,完成了x6的切面回调处理功能 + - services:mock数据,真实场景中可能为异步获取数据 + +## 开发插件 +```typescript +function pluginX6DesignerExtension(ctx: IPublicModelPluginContext) { + return { + init() { + // 获取 x6 designer 内置插件的导出 api + const x6Designer = ctx.plugins['plugin-x6-designer'] as IDesigner; + + x6Designer.onNodeRender((model, node) => { + // @ts-ignore + // 自定义 node 渲染逻辑 + const { name, title } = model.propsData; + node.attr('text/textWrap/text', title || name); + }); + + x6Designer.onEdgeRender((model, edge) => { + // @ts-ignore + const { source, target, sourcePortId, targetPortId } = model.propsData; + console.log(sourcePortId, targetPortId); + requestAnimationFrame(() => { + edge.setSource({ cell: source, port: sourcePortId }); + edge.setTarget({ cell: target, port: targetPortId }); + }); + + // https://x6.antv.vision/zh/docs/tutorial/intermediate/edge-labels x6 标签模块 + // appendLabel 会触发 onEdgeLabelRender + edge.appendLabel({ + markup: Markup.getForeignObjectMarkup(), + attrs: { + fo: { + width: 120, + height: 30, + x: -60, + y: -15, + }, + }, + }); + }); + + x6Designer.onEdgeLabelRender((args) => { + const { selectors } = args + const content = selectors.foContent as HTMLDivElement + if (content) { + ReactDOM.render(
自定义 react 标签
, content) + } + }) + } + } +} + +pluginX6DesignerExtension.pluginName = 'plugin-x6-designer-extension'; + +export default pluginX6DesignerExtension; +``` +x6Designer为图实例暴露出来的一些接口,可基于此进行一些图的必要插件的封装,整个插件的封装完全follow低代码引擎的插件,详情可参考 https://lowcode-engine.cn/site/docs/guide/expand/editor/pluginWidget + +## 开发物料 +```bash +npm init @alilc/element your-material-demo +``` +![image.png](https://img.alicdn.com/imgextra/i3/O1CN01DCCqO82ADuhS8ztCt_!!6000000008170-2-tps-546-208.png) + +仓库初始化完成 +![image.png](https://img.alicdn.com/imgextra/i2/O1CN01qK2rUe1JNpdqbdhoW_!!6000000001017-0-tps-5120-2830.jpg) + +接下来即可编写物料内容了 +图物料与低代码的dom场景存在画布的差异,因此暂不支持物料单独调试,须通过项目demo进行物料调试 + +### 资产描述 +```bash +npm run lowcode:build +``` +如果物料是个React组件,则在执行上述命令时会自动生成对应的meta.ts,但图物料很多时候并非一个React组件,因此须手动生产meta.ts + +可参考: https://github.com/alibaba/lowcode-materials/blob/main/packages/graph-x6-materials/lowcode/send-email/meta.ts +同时会自动生成物料描述文件 + +### 物料调试 +#### 物料侧 +物料想要支持被项目动态inject调试,须在build.lowcode.js中加入 +```javascript +[ + '@alilc/build-plugin-alt', + { + type: 'component', + inject: true, + library + }, +] +``` +![image.png](https://img.alicdn.com/imgextra/i4/O1CN01HyXfL12992sDkOmOg_!!6000000008024-0-tps-5120-2824.jpg) + +本地启动 +```bash +npm run lowcode:dev +``` +#### 项目侧 +通过@alilc/lce-graph-core加载物料的天然支持了debug,因此无须特殊处理。 +若项目中自行加载,则参考 https://lowcode-engine.cn/site/docs/guide/expand/editor/cli +项目访问地址后拼接query "?debug"即可进入物料调试 +![image.png](https://img.alicdn.com/imgextra/i2/O1CN01ke58hT1aRoYJzkutk_!!6000000003327-2-tps-5120-2790.png) + + diff --git a/docs/docs/guide/expand/editor/pluginContextMenu.md b/docs/docs/guide/expand/editor/pluginContextMenu.md index e9dbdf3c4..cd293b73e 100644 --- a/docs/docs/guide/expand/editor/pluginContextMenu.md +++ b/docs/docs/guide/expand/editor/pluginContextMenu.md @@ -18,8 +18,7 @@ import { Icon, Message } from '@alifd/next'; const addHelloAction = (ctx: IPublicModelPluginContext) => { return { async init() { - const { addBuiltinComponentAction } = ctx.material; - addBuiltinComponentAction({ + ctx.material.addBuiltinComponentAction({ name: 'hello', content: { icon: , @@ -54,8 +53,7 @@ import { IPublicModelPluginContext } from '@alilc/lowcode-types'; const removeCopyAction = (ctx: IPublicModelPluginContext) => { return { async init() { - const { removeBuiltinComponentAction } = ctx.material; - removeBuiltinComponentAction('copy'); + ctx.material.removeBuiltinComponentAction('copy'); } } }; diff --git a/docs/package.json b/docs/package.json index 80f146513..efac589fa 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-engine-docs", - "version": "1.0.28", + "version": "1.0.30", "description": "低代码引擎版本化文档", "license": "MIT", "files": [ diff --git a/lerna.json b/lerna.json index dd7f87e45..67ffb648e 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "4.0.0", - "version": "1.1.6", + "version": "1.1.7", "npmClient": "yarn", "useWorkspaces": true, "packages": [ diff --git a/modules/code-generator/src/parser/SchemaParser.ts b/modules/code-generator/src/parser/SchemaParser.ts index c6263877c..b4f7424ce 100644 --- a/modules/code-generator/src/parser/SchemaParser.ts +++ b/modules/code-generator/src/parser/SchemaParser.ts @@ -106,6 +106,11 @@ function processChildren(schema: IPublicTypeNodeSchema): void { } } +function getInternalDep(internalDeps: Record, depName: string) { + const dep = internalDeps[depName]; + return (dep && dep.type !== InternalDependencyType.PAGE) ? dep : null; +} + export class SchemaParser implements ISchemaParser { validate(schema: IPublicTypeProjectSchema): boolean { if (SUPPORT_SCHEMA_VERSION_LIST.indexOf(schema.version) < 0) { @@ -221,12 +226,11 @@ export class SchemaParser implements ISchemaParser { } }); - // 分析容器内部组件依赖 containers.forEach((container) => { const depNames = this.getComponentNames(container); // eslint-disable-next-line no-param-reassign container.deps = uniqueArray(depNames, (i: string) => i) - .map((depName) => internalDeps[depName] || compDeps[depName]) + .map((depName) => getInternalDep(internalDeps, depName) || compDeps[depName]) .filter(Boolean); // container.deps = Object.keys(compDeps).map((depName) => compDeps[depName]); }); diff --git a/modules/code-generator/src/postprocessor/prettier/index.ts b/modules/code-generator/src/postprocessor/prettier/index.ts index b4c3188f3..075fc66e7 100644 --- a/modules/code-generator/src/postprocessor/prettier/index.ts +++ b/modules/code-generator/src/postprocessor/prettier/index.ts @@ -20,7 +20,7 @@ const factory: PostProcessorFactory = (config?: ProcessorConfig const codePrettier: PostProcessor = (content: string, fileType: string) => { let parser: prettier.BuiltInParserName | any; - if (fileType === 'js' || fileType === 'jsx') { + if (fileType === 'js' || fileType === 'jsx' || fileType === 'ts' || fileType === 'tsx') { parser = 'babel'; } else if (fileType === 'json') { parser = 'json-stringify'; diff --git a/modules/code-generator/src/utils/expressionParser.ts b/modules/code-generator/src/utils/expressionParser.ts index cbc950bbc..15320b963 100644 --- a/modules/code-generator/src/utils/expressionParser.ts +++ b/modules/code-generator/src/utils/expressionParser.ts @@ -167,7 +167,7 @@ export function parseExpressionGetKeywords(expr: string | null | undefined): str ], }); - const addIdentifierIfNeeded = (x: Record | number | null | undefined) => { + const addIdentifierIfNeeded = (x: Node | null | undefined) => { if (typeof x === 'object' && isIdentifier(x) && JS_KEYWORDS.includes(x.name)) { keywordVars.add(x.name); } @@ -189,7 +189,7 @@ export function parseExpressionGetKeywords(expr: string | null | undefined): str addIdentifierIfNeeded(item); }); } else { - addIdentifierIfNeeded(fieldValue as Record | null); + addIdentifierIfNeeded(fieldValue as any); } } }); @@ -217,7 +217,7 @@ export function parseExpressionGetGlobalVariables( const ast = parser.parse(`!(${expr});`); const addUndeclaredIdentifierIfNeeded = ( - x: Record | number | null | undefined, + x: Node | null | undefined, path: NodePath, ) => { if (typeof x === 'object' && isIdentifier(x) && !path.scope.hasBinding(x.name)) { @@ -241,7 +241,7 @@ export function parseExpressionGetGlobalVariables( addUndeclaredIdentifierIfNeeded(item, path); }); } else { - addUndeclaredIdentifierIfNeeded(fieldValue as Record | null, path); + addUndeclaredIdentifierIfNeeded(fieldValue as any, path); } } }); diff --git a/modules/code-generator/src/utils/nodeToJSX.ts b/modules/code-generator/src/utils/nodeToJSX.ts index ad79288ba..d29f28762 100644 --- a/modules/code-generator/src/utils/nodeToJSX.ts +++ b/modules/code-generator/src/utils/nodeToJSX.ts @@ -19,6 +19,7 @@ import { executeFunctionStack } from './aopHelper'; import { encodeJsxStringNode } from './encodeJsxAttrString'; import { unwrapJsExprQuoteInJsx } from './jsxHelpers'; import { transformThis2Context } from '../core/jsx/handlers/transformThis2Context'; +import { isValidIdentifier } from './validate'; function mergeNodeGeneratorConfig( cfg1: NodeGeneratorConfig, @@ -126,11 +127,13 @@ function generateAttrs( if (props) { if (!Array.isArray(props)) { Object.keys(props).forEach((propName: string) => { - pieces = pieces.concat(generateAttr(propName, props[propName] as IPublicTypeCompositeValue, scope, config)); + if (isValidIdentifier(propName)) { + pieces = pieces.concat(generateAttr(propName, props[propName] as IPublicTypeCompositeValue, scope, config)); + } }); } else { props.forEach((prop) => { - if (prop.name && !prop.spread) { + if (prop.name && isValidIdentifier(prop.name) && !prop.spread) { pieces = pieces.concat(generateAttr(prop.name, prop.value, scope, config)); } diff --git a/modules/code-generator/tests/plugins/jsx/__snapshots__/p0-condition-at-root.test.ts.snap b/modules/code-generator/tests/plugins/jsx/__snapshots__/p0-condition-at-root.test.ts.snap index 391e492f6..8bce3cc10 100644 --- a/modules/code-generator/tests/plugins/jsx/__snapshots__/p0-condition-at-root.test.ts.snap +++ b/modules/code-generator/tests/plugins/jsx/__snapshots__/p0-condition-at-root.test.ts.snap @@ -281,3 +281,75 @@ Object { }, } `; + +exports[`condition at root invalid attr name should not be generated 1`] = ` +Object { + "chunks": Array [ + Object { + "content": " + const __$$context = this._context || this; + const { state } = __$$context; + return Hello world!; + ", + "fileType": "jsx", + "linkAfter": Array [ + "ReactComponentClassRenderStart", + "ReactComponentClassRenderPre", + ], + "name": "ReactComponentClassRenderJSX", + "type": "string", + }, + Object { + "content": " + function __$$eval(expr) { + try { + return expr(); + } catch (error) { + + } + } + + function __$$evalArray(expr) { + const res = __$$eval(expr); + return Array.isArray(res) ? res : []; + } + + + function __$$createChildContext(oldContext, ext) { + const childContext = { + ...oldContext, + ...ext, + }; + childContext.__proto__ = oldContext; + return childContext; + } + ", + "fileType": "jsx", + "linkAfter": Array [ + "CommonFileExport", + ], + "name": "CommonCustomContent", + "type": "string", + }, + ], + "contextData": Object {}, + "depNames": Array [], + "ir": Object { + "children": Array [ + Object { + "children": "Hello world!", + "componentName": "Text", + "props": Object { + "a": 1, + "a.b": 2, + }, + }, + ], + "componentName": "Page", + "condition": null, + "containerType": "Page", + "fileName": "test", + "moduleName": "test", + }, +} +`; diff --git a/modules/code-generator/tests/plugins/jsx/p0-condition-at-root.test.ts b/modules/code-generator/tests/plugins/jsx/p0-condition-at-root.test.ts index 984c84830..31e73d63e 100644 --- a/modules/code-generator/tests/plugins/jsx/p0-condition-at-root.test.ts +++ b/modules/code-generator/tests/plugins/jsx/p0-condition-at-root.test.ts @@ -83,4 +83,22 @@ describe('condition at root', () => { }); expect(result).toMatchSnapshot(); }); + + test('invalid attr name should not be generated', async () => { + const containerIr: IContainerInfo = { + containerType: 'Page', + moduleName: 'test', + componentName: 'Page', + fileName: 'test', + condition: null, + children: [{ componentName: 'Text', children: 'Hello world!', props: { 'a': 1, 'a.b': 2 } }], + }; + const result = await jsx()({ + ir: containerIr, + contextData: {}, + chunks: [], + depNames: [], + }); + expect(result).toMatchSnapshot(); + }) }); diff --git a/packages/designer/jest.config.js b/packages/designer/jest.config.js index 82c76fad8..1ecebd938 100644 --- a/packages/designer/jest.config.js +++ b/packages/designer/jest.config.js @@ -19,6 +19,7 @@ const jestConfig = { // testMatch: ['**/setting-field.test.ts'], // testMatch: ['**/node.test.ts'], // testMatch: ['**/builtin-hotkey.test.ts'], + // testMatch: ['**/selection.test.ts'], transformIgnorePatterns: [ `/node_modules/(?!${esModules})/`, ], diff --git a/packages/designer/package.json b/packages/designer/package.json index e3b5ca968..4deaa2721 100644 --- a/packages/designer/package.json +++ b/packages/designer/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-designer", - "version": "1.1.6", + "version": "1.1.7", "description": "Designer for Ali LowCode Engine", "main": "lib/index.js", "module": "es/index.js", @@ -15,9 +15,10 @@ }, "license": "MIT", "dependencies": { - "@alilc/lowcode-editor-core": "1.1.6", - "@alilc/lowcode-types": "1.1.6", - "@alilc/lowcode-utils": "1.1.6", + "@alilc/build-plugin-lce": "^0.0.4-beta.2", + "@alilc/lowcode-editor-core": "1.1.7", + "@alilc/lowcode-types": "1.1.7", + "@alilc/lowcode-utils": "1.1.7", "classnames": "^2.2.6", "react": "^16", "react-dom": "^16.7.0", diff --git a/packages/designer/src/builtin-simulator/renderer.ts b/packages/designer/src/builtin-simulator/renderer.ts index bd94f24f6..15664757b 100644 --- a/packages/designer/src/builtin-simulator/renderer.ts +++ b/packages/designer/src/builtin-simulator/renderer.ts @@ -1,29 +1,7 @@ import { Component } from '../simulator'; -import { IPublicTypeComponentInstance, IPublicTypeNodeInstance, Asset, IPublicTypeComponentSchema, IPublicTypeProjectSchema, IPublicTypeLowCodeComponent } from '@alilc/lowcode-types'; +import { IPublicTypeComponentInstance, IPublicTypeSimulatorRenderer } from '@alilc/lowcode-types'; -export interface BuiltinSimulatorRenderer { - readonly isSimulatorRenderer: true; - autoRepaintNode?: boolean; - components: Record; - rerender: () => void; - createComponent(schema: IPublicTypeProjectSchema): Component | null; - getComponent(componentName: string): Component; - getClosestNodeInstance( - from: IPublicTypeComponentInstance, - nodeId?: string, - ): IPublicTypeNodeInstance | null; - findDOMNodes(instance: IPublicTypeComponentInstance): Array | null; - getClientRects(element: Element | Text): DOMRect[]; - setNativeSelection(enableFlag: boolean): void; - setDraggingState(state: boolean): void; - setCopyState(state: boolean): void; - loadAsyncLibrary(asyncMap: { [index: string]: any }): void; - clearState(): void; - stopAutoRepaintNode(): void; - enableAutoRepaintNode(): void; - run(): void; - load(asset: Asset): Promise; -} +export type BuiltinSimulatorRenderer = IPublicTypeSimulatorRenderer; export function isSimulatorRenderer(obj: any): obj is BuiltinSimulatorRenderer { return obj && obj.isSimulatorRenderer; diff --git a/packages/designer/src/document/document-model.ts b/packages/designer/src/document/document-model.ts index 2d39ed115..9521fef38 100644 --- a/packages/designer/src/document/document-model.ts +++ b/packages/designer/src/document/document-model.ts @@ -839,13 +839,18 @@ export class DocumentModel implements IDocumentModel { } // 合并外界传入的自定义渲染的组件 if (Array.isArray(extraComps)) { - extraComps.forEach(c => { - if (c && !exsitingMap[c]) { - const m = this.getComponentMeta(c); - if (m && m.npm?.package) { + extraComps.forEach((componentName) => { + if (componentName && !exsitingMap[componentName]) { + const meta = this.getComponentMeta(componentName); + if (meta?.npm?.package) { componentsMap.push({ - ...m?.npm, - componentName: c, + ...meta?.npm, + componentName, + }); + } else { + componentsMap.push({ + devMode: 'lowCode', + componentName, }); } } diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index 456595b04..b0e509bce 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -159,6 +159,9 @@ export interface IBaseNode return !!this.getExtraProp('isLocked')?.getValue(); } + canSelect(): boolean { + const onSelectHook = this.componentMeta?.advanced?.callbacks?.onSelectHook; + const canSelect = typeof onSelectHook === 'function' ? onSelectHook(this.internalToShellNode()!) : true; + return canSelect; + } + /** * 选择当前节点 */ diff --git a/packages/designer/src/document/node/props/prop.ts b/packages/designer/src/document/node/props/prop.ts index 37017d72d..01b2dc26b 100644 --- a/packages/designer/src/document/node/props/prop.ts +++ b/packages/designer/src/document/node/props/prop.ts @@ -148,15 +148,13 @@ export class Prop implements IProp, IPropParent { @obx.shallow private _items: IProp[] | null = null; - @obx.shallow private _maps: Map | null = null; - /** - * 作为 _maps 的一层缓存机制,主要是复用部分已存在的 Prop,保持响应式关系,比如: + * 作为一层缓存机制,主要是复用部分已存在的 Prop,保持响应式关系,比如: * 当前 Prop#_value 值为 { a: 1 },当调用 setValue({ a: 2 }) 时,所有原来的子 Prop 均被销毁, * 导致假如外部有 mobx reaction(常见于 observer),此时响应式链路会被打断, * 因为 reaction 监听的是原 Prop(a) 的 _value,而不是新 Prop(a) 的 _value。 */ - private _prevMaps: Map | null = null; + @obx.shallow private _maps: Map | null = null; /** * 构造 items 属性,同时构造 maps 属性 @@ -171,8 +169,8 @@ export class Prop implements IProp, IPropParent { data.forEach((item: any, idx: number) => { items = items || []; let prop; - if (this._prevMaps?.has(idx.toString())) { - prop = this._prevMaps.get(idx.toString())!; + if (this._maps?.has(idx.toString())) { + prop = this._maps.get(idx.toString())!; prop.setValue(item); } else { prop = new Prop(this, item, idx); @@ -187,8 +185,8 @@ export class Prop implements IProp, IPropParent { const keys = Object.keys(data); for (const key of keys) { let prop: IProp; - if (this._prevMaps?.has(key)) { - prop = this._prevMaps.get(key)!; + if (this._maps?.has(key)) { + prop = this._maps.get(key)!; prop.setValue(data[key]); } else { prop = new Prop(this, data[key], key); @@ -419,8 +417,6 @@ export class Prop implements IProp, IPropParent { items.forEach((prop) => prop.purge()); } this._items = null; - this._prevMaps = this._maps; - this._maps = null; if (this._type !== 'slot' && this._slotNode) { this._slotNode.remove(); this._slotNode = undefined; diff --git a/packages/designer/src/document/selection.ts b/packages/designer/src/document/selection.ts index 93ec04ffe..6147e188d 100644 --- a/packages/designer/src/document/selection.ts +++ b/packages/designer/src/document/selection.ts @@ -32,6 +32,12 @@ export class Selection implements ISelection { return; } + const node = this.doc.getNode(id); + + if (!node?.canSelect()) { + return; + } + this._selected = [id]; this.emitter.emit('selectionchange', this._selected); } @@ -40,7 +46,18 @@ export class Selection implements ISelection { * 批量选中 */ selectAll(ids: string[]) { - this._selected = ids; + const selectIds: string[] = []; + + ids.forEach(d => { + const node = this.doc.getNode(d); + + if (node?.canSelect()) { + selectIds.push(d); + } + }); + + this._selected = selectIds; + this.emitter.emit('selectionchange', this._selected); } diff --git a/packages/designer/tests/document/node/props/prop.test.ts b/packages/designer/tests/document/node/props/prop.test.ts index 177bc5247..58c24a4ea 100644 --- a/packages/designer/tests/document/node/props/prop.test.ts +++ b/packages/designer/tests/document/node/props/prop.test.ts @@ -379,7 +379,6 @@ describe('Prop 类测试', () => { prop.dispose(); expect(prop._items).toBeNull(); - expect(prop._maps).toBeNull(); }); }); diff --git a/packages/designer/tests/document/selection.test.ts b/packages/designer/tests/document/selection.test.ts index b8b5ad434..0af22b5ce 100644 --- a/packages/designer/tests/document/selection.test.ts +++ b/packages/designer/tests/document/selection.test.ts @@ -122,7 +122,7 @@ describe('选择区测试', () => { selectionChangeHandler.mockClear(); }); - it('dispose 方法', () => { + it('selectAll 包含不存在的 id', () => { const project = new Project(designer, { componentsTree: [ formSchema, @@ -135,14 +135,7 @@ describe('选择区测试', () => { selection.selectAll(['form', 'node_k1ow3cbj', 'form2']); - const selectionChangeHandler = jest.fn(); - selection.onSelectionChange(selectionChangeHandler); - selection.dispose(); - - expect(selectionChangeHandler).toHaveBeenCalledTimes(1); - expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form', 'node_k1ow3cbj']); expect(selection.selected).toEqual(['form', 'node_k1ow3cbj']); - selectionChangeHandler.mockClear(); }); it('dispose 方法 - 选中的节点没有被删除的', () => { diff --git a/packages/editor-core/package.json b/packages/editor-core/package.json index 2701199d5..efb73bb2c 100644 --- a/packages/editor-core/package.json +++ b/packages/editor-core/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-editor-core", - "version": "1.1.6", + "version": "1.1.7", "description": "Core Api for Ali lowCode engine", "license": "MIT", "main": "lib/index.js", @@ -14,8 +14,8 @@ }, "dependencies": { "@alifd/next": "^1.19.16", - "@alilc/lowcode-types": "1.1.6", - "@alilc/lowcode-utils": "1.1.6", + "@alilc/lowcode-types": "1.1.7", + "@alilc/lowcode-utils": "1.1.7", "classnames": "^2.2.6", "debug": "^4.1.1", "intl-messageformat": "^9.3.1", diff --git a/packages/editor-skeleton/package.json b/packages/editor-skeleton/package.json index ff9c9a782..9199e1cb3 100644 --- a/packages/editor-skeleton/package.json +++ b/packages/editor-skeleton/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-editor-skeleton", - "version": "1.1.6", + "version": "1.1.7", "description": "alibaba lowcode editor skeleton", "main": "lib/index.js", "module": "es/index.js", @@ -19,10 +19,10 @@ ], "dependencies": { "@alifd/next": "^1.20.12", - "@alilc/lowcode-designer": "1.1.6", - "@alilc/lowcode-editor-core": "1.1.6", - "@alilc/lowcode-types": "1.1.6", - "@alilc/lowcode-utils": "1.1.6", + "@alilc/lowcode-designer": "1.1.7", + "@alilc/lowcode-editor-core": "1.1.7", + "@alilc/lowcode-types": "1.1.7", + "@alilc/lowcode-utils": "1.1.7", "classnames": "^2.2.6", "react": "^16.8.1", "react-dom": "^16.8.1" diff --git a/packages/editor-skeleton/src/components/settings/settings-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-pane.tsx index 9cc8d9cae..1d651bb5a 100644 --- a/packages/editor-skeleton/src/components/settings/settings-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-pane.tsx @@ -1,6 +1,6 @@ import { Component, MouseEvent, Fragment, ReactNode } from 'react'; import { shallowIntl, observer, obx, engineConfig, runInAction } from '@alilc/lowcode-editor-core'; -import { createContent, isJSSlot, isSetterConfig } from '@alilc/lowcode-utils'; +import { createContent, isJSSlot, isSetterConfig, shouldUseVariableSetter } from '@alilc/lowcode-utils'; import { Skeleton, Stage } from '@alilc/lowcode-editor-skeleton'; import { IPublicApiSetters, IPublicTypeCustomView, IPublicTypeDynamicProps } from '@alilc/lowcode-types'; import { ISettingEntry, IComponentMeta, ISettingField, isSettingField, ISettingTopEntry } from '@alilc/lowcode-designer'; @@ -155,23 +155,29 @@ class SettingFieldView extends Component { + const treeNode = this.getTreeNodeById(node.id); + treeNode?.setHidden(!visible); + }); } setNodeSelected(nodeId: string): void { diff --git a/packages/plugin-outline-pane/src/index.tsx b/packages/plugin-outline-pane/src/index.tsx index 95b219a4b..180b424ea 100644 --- a/packages/plugin-outline-pane/src/index.tsx +++ b/packages/plugin-outline-pane/src/index.tsx @@ -4,7 +4,7 @@ import { IPublicModelPluginContext, IPublicModelDocumentModel } from '@alilc/low import { MasterPaneName, BackupPaneName } from './helper/consts'; import { TreeMaster } from './controllers/tree-master'; import { PaneController } from './controllers/pane-controller'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; export function OutlinePaneContext(props: { treeMaster?: TreeMaster; @@ -19,9 +19,11 @@ export function OutlinePaneContext(props: { }) { const treeMaster = props.treeMaster || new TreeMaster(props.pluginContext, props.options); const [masterPaneController, setMasterPaneController] = useState(new PaneController(props.paneName || MasterPaneName, treeMaster)); - treeMaster.onPluginContextChange(() => { - setMasterPaneController(new PaneController(props.paneName || MasterPaneName, treeMaster)); - }); + useEffect(() => { + return treeMaster.onPluginContextChange(() => { + setMasterPaneController(new PaneController(props.paneName || MasterPaneName, treeMaster)); + }); + }, []); return ( { + this.setState({ + visible: !hidden, + }); + }); } render() { @@ -132,7 +138,7 @@ export default class TreeTitle extends PureComponent<{ data-id={treeNode.id} onClick={() => { if (isModal) { - if (node.visible) { + if (this.state.visible) { node.document?.modalNodesManager?.setInvisible(node); } else { node.document?.modalNodesManager?.setVisible(node); @@ -144,7 +150,7 @@ export default class TreeTitle extends PureComponent<{ } }} > - {isModal && node.visible && ( + {isModal && this.state.visible && (
{ node.document?.modalNodesManager?.setInvisible(node); }} @@ -152,7 +158,7 @@ export default class TreeTitle extends PureComponent<{
)} - {isModal && !node.visible && ( + {isModal && !this.state.visible && (
{ node.document?.modalNodesManager?.setVisible(node); }} diff --git a/packages/rax-renderer/package.json b/packages/rax-renderer/package.json index 489cf1409..3910afe41 100644 --- a/packages/rax-renderer/package.json +++ b/packages/rax-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-rax-renderer", - "version": "1.1.6", + "version": "1.1.7", "description": "Rax renderer for Ali lowCode engine", "main": "lib/index.js", "module": "es/index.js", @@ -30,8 +30,8 @@ "build": "build-scripts build" }, "dependencies": { - "@alilc/lowcode-renderer-core": "1.1.6", - "@alilc/lowcode-utils": "1.1.6", + "@alilc/lowcode-renderer-core": "1.1.7", + "@alilc/lowcode-utils": "1.1.7", "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 4f8bc7ad1..0e29b1091 100644 --- a/packages/rax-simulator-renderer/package.json +++ b/packages/rax-simulator-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-rax-simulator-renderer", - "version": "1.1.6", + "version": "1.1.7", "description": "rax simulator renderer for alibaba lowcode designer", "main": "lib/index.js", "module": "es/index.js", @@ -13,10 +13,10 @@ "build:umd": "build-scripts build --config build.umd.json" }, "dependencies": { - "@alilc/lowcode-designer": "1.1.6", - "@alilc/lowcode-rax-renderer": "1.1.6", - "@alilc/lowcode-types": "1.1.6", - "@alilc/lowcode-utils": "1.1.6", + "@alilc/lowcode-designer": "1.1.7", + "@alilc/lowcode-rax-renderer": "1.1.7", + "@alilc/lowcode-types": "1.1.7", + "@alilc/lowcode-utils": "1.1.7", "classnames": "^2.2.6", "driver-universal": "^3.1.3", "history": "^5.0.0", diff --git a/packages/react-renderer/package.json b/packages/react-renderer/package.json index 744c1cac0..451ef1ee3 100644 --- a/packages/react-renderer/package.json +++ b/packages/react-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-react-renderer", - "version": "1.1.6", + "version": "1.1.7", "description": "react renderer for ali lowcode engine", "main": "lib/index.js", "module": "es/index.js", @@ -22,7 +22,7 @@ ], "dependencies": { "@alifd/next": "^1.21.16", - "@alilc/lowcode-renderer-core": "1.1.6" + "@alilc/lowcode-renderer-core": "1.1.7" }, "devDependencies": { "@alib/build-scripts": "^0.1.18", diff --git a/packages/react-simulator-renderer/package.json b/packages/react-simulator-renderer/package.json index ad7673b62..adb5526c7 100644 --- a/packages/react-simulator-renderer/package.json +++ b/packages/react-simulator-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-react-simulator-renderer", - "version": "1.1.6", + "version": "1.1.7", "description": "react simulator renderer for alibaba lowcode designer", "main": "lib/index.js", "module": "es/index.js", @@ -17,10 +17,10 @@ "test:cov": "build-scripts test --config build.test.json --jest-coverage" }, "dependencies": { - "@alilc/lowcode-designer": "1.1.6", - "@alilc/lowcode-react-renderer": "1.1.6", - "@alilc/lowcode-types": "1.1.6", - "@alilc/lowcode-utils": "1.1.6", + "@alilc/lowcode-designer": "1.1.7", + "@alilc/lowcode-react-renderer": "1.1.7", + "@alilc/lowcode-types": "1.1.7", + "@alilc/lowcode-utils": "1.1.7", "classnames": "^2.2.6", "mobx": "^6.3.0", "mobx-react": "^7.2.0", diff --git a/packages/renderer-core/jest.config.js b/packages/renderer-core/jest.config.js index 2489490fa..e3267c517 100644 --- a/packages/renderer-core/jest.config.js +++ b/packages/renderer-core/jest.config.js @@ -12,6 +12,7 @@ const jestConfig = { // testMatch: ['(/tests?/.*(test))\\.[jt]s$'], // testMatch: ['**/*/base.test.tsx'], // testMatch: ['**/utils/common.test.ts'], + // testMatch: ['**/*/leaf.test.tsx'], transformIgnorePatterns: [ `/node_modules/(?!${esModules})/`, ], diff --git a/packages/renderer-core/package.json b/packages/renderer-core/package.json index 1a6fd6090..ce633dd54 100644 --- a/packages/renderer-core/package.json +++ b/packages/renderer-core/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-renderer-core", - "version": "1.1.6", + "version": "1.1.7", "description": "renderer core", "license": "MIT", "main": "lib/index.js", @@ -16,8 +16,8 @@ }, "dependencies": { "@alilc/lowcode-datasource-engine": "^1.0.0", - "@alilc/lowcode-types": "1.1.6", - "@alilc/lowcode-utils": "1.1.6", + "@alilc/lowcode-types": "1.1.7", + "@alilc/lowcode-utils": "1.1.7", "classnames": "^2.2.6", "debug": "^4.1.1", "fetch-jsonp": "^1.1.3", @@ -32,7 +32,7 @@ "devDependencies": { "@alib/build-scripts": "^0.1.18", "@alifd/next": "^1.26.0", - "@alilc/lowcode-designer": "1.1.6", + "@alilc/lowcode-designer": "1.1.7", "@babel/plugin-transform-typescript": "^7.16.8", "@testing-library/react": "^11.2.2", "@types/classnames": "^2.2.11", diff --git a/packages/renderer-core/src/hoc/leaf.tsx b/packages/renderer-core/src/hoc/leaf.tsx index 98b267475..432f22a5e 100644 --- a/packages/renderer-core/src/hoc/leaf.tsx +++ b/packages/renderer-core/src/hoc/leaf.tsx @@ -521,16 +521,11 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, { } get hasChildren(): boolean { - let { children } = this.props; - if (this.state.childrenInState) { - children = this.state.nodeChildren; + if (!this.state.childrenInState) { + return 'children' in this.props; } - if (Array.isArray(children)) { - return Boolean(children && children.length); - } - - return Boolean(children); + return true; } get children(): any { @@ -576,7 +571,11 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, { delete compProps.__inner__; - return engine.createElement(Comp, compProps, this.hasChildren ? this.children : null); + if (this.hasChildren) { + return engine.createElement(Comp, compProps, this.children); + } + + return engine.createElement(Comp, compProps); } } diff --git a/packages/renderer-core/tests/hoc/leaf.test.tsx b/packages/renderer-core/tests/hoc/leaf.test.tsx index d4a65e578..c21a10be9 100644 --- a/packages/renderer-core/tests/hoc/leaf.test.tsx +++ b/packages/renderer-core/tests/hoc/leaf.test.tsx @@ -504,6 +504,41 @@ describe('onChildrenChange', () => { DivNode.emitChildrenChange(); makeSnapshot(component); }); + + it('children is 0', () => { + DivNode.schema.children = 0 + DivNode.emitChildrenChange(); + const componentInstance = component.root; + expect(componentInstance.findByType(components.Div).props.children).toEqual(0); + }); + + it('children is false', () => { + DivNode.schema.children = false + DivNode.emitChildrenChange(); + const componentInstance = component.root; + expect(componentInstance.findByType(components.Div).props.children).toEqual(false); + }); + + it('children is []', () => { + DivNode.schema.children = [] + DivNode.emitChildrenChange(); + const componentInstance = component.root; + expect(componentInstance.findByType(components.Div).props.children).toEqual([]); + }); + + it('children is null', () => { + DivNode.schema.children = null + DivNode.emitChildrenChange(); + const componentInstance = component.root; + expect(componentInstance.findByType(components.Div).props.children).toEqual(null); + }); + + it('children is undefined', () => { + DivNode.schema.children = undefined; + DivNode.emitChildrenChange(); + const componentInstance = component.root; + expect(componentInstance.findByType(components.Div).props.children).toEqual(undefined); + }); }); describe('not render leaf', () => { diff --git a/packages/shell/package.json b/packages/shell/package.json index 521befb2e..9ec85ec5c 100644 --- a/packages/shell/package.json +++ b/packages/shell/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-shell", - "version": "1.1.6", + "version": "1.1.7", "description": "Shell Layer for AliLowCodeEngine", "main": "lib/index.js", "module": "es/index.js", @@ -13,12 +13,12 @@ }, "license": "MIT", "dependencies": { - "@alilc/lowcode-designer": "1.1.6", - "@alilc/lowcode-editor-core": "1.1.6", - "@alilc/lowcode-editor-skeleton": "1.1.6", - "@alilc/lowcode-types": "1.1.6", - "@alilc/lowcode-utils": "1.1.6", - "@alilc/lowcode-workspace": "1.1.6", + "@alilc/lowcode-designer": "1.1.7", + "@alilc/lowcode-editor-core": "1.1.7", + "@alilc/lowcode-editor-skeleton": "1.1.7", + "@alilc/lowcode-types": "1.1.7", + "@alilc/lowcode-utils": "1.1.7", + "@alilc/lowcode-workspace": "1.1.7", "classnames": "^2.2.6", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.5", diff --git a/packages/shell/src/api/setters.ts b/packages/shell/src/api/setters.ts index 56f42d18b..7750a5152 100644 --- a/packages/shell/src/api/setters.ts +++ b/packages/shell/src/api/setters.ts @@ -47,11 +47,11 @@ export class Setters implements IPublicApiSetters { * 获取已注册的所有 settersMap * @returns */ - getSettersMap(): Map { + }> => { return this[settersSymbol].getSettersMap(); - } + }; /** * 注册一个 setter diff --git a/packages/shell/src/model/active-tracker.ts b/packages/shell/src/model/active-tracker.ts index e6170b7a6..32d4c04eb 100644 --- a/packages/shell/src/model/active-tracker.ts +++ b/packages/shell/src/model/active-tracker.ts @@ -13,7 +13,13 @@ export class ActiveTracker implements IPublicModelActiveTracker { } get target() { - const { node: innerNode, detail, instance } = this[activeTrackerSymbol]._target; + const _target = this[activeTrackerSymbol]._target; + + if (!_target) { + return null; + } + + const { node: innerNode, detail, instance } = _target; const publicNode = ShellNode.create(innerNode); return { node: publicNode!, diff --git a/packages/shell/src/model/window.ts b/packages/shell/src/model/window.ts index f539e9567..3e6bd6450 100644 --- a/packages/shell/src/model/window.ts +++ b/packages/shell/src/model/window.ts @@ -43,6 +43,10 @@ export class Window implements IPublicModelWindow { return await this[windowSymbol].save(); } + onSave(fn: () => void) { + return this[windowSymbol].onSave(fn); + } + get currentEditorView() { if (this[windowSymbol].editorView) { return new EditorView(this[windowSymbol].editorView).toProxy() as any; diff --git a/packages/types/package.json b/packages/types/package.json index b92d2efb6..0b05893ca 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-types", - "version": "1.1.6", + "version": "1.1.7", "description": "Types for Ali lowCode engine", "files": [ "es", diff --git a/packages/types/src/shell/model/active-tracker.ts b/packages/types/src/shell/model/active-tracker.ts index 2538a2601..ac116a947 100644 --- a/packages/types/src/shell/model/active-tracker.ts +++ b/packages/types/src/shell/model/active-tracker.ts @@ -6,7 +6,7 @@ export interface IPublicModelActiveTracker { /** * @since 1.1.7 */ - target: IPublicTypeActiveTarget; + target: IPublicTypeActiveTarget | null; onChange(fn: (target: IPublicTypeActiveTarget) => void): () => void; diff --git a/packages/types/src/shell/model/plugin-context.ts b/packages/types/src/shell/model/plugin-context.ts index ea6fb7102..1f3d3b5e8 100644 --- a/packages/types/src/shell/model/plugin-context.ts +++ b/packages/types/src/shell/model/plugin-context.ts @@ -13,7 +13,7 @@ import { IPublicApiWorkspace, } from '../api'; import { IPublicEnumPluginRegisterLevel } from '../enum'; -import { IPublicModelEngineConfig, IPublicModelEditorView } from './'; +import { IPublicModelEngineConfig, IPublicModelWindow } from './'; export interface IPublicModelPluginContext { @@ -108,7 +108,7 @@ export interface IPublicModelPluginContext { */ get registerLevel(): IPublicEnumPluginRegisterLevel; - get editorWindow(): IPublicModelEditorView; + get editorWindow(): IPublicModelWindow; } /** diff --git a/packages/types/src/shell/model/window.ts b/packages/types/src/shell/model/window.ts index a33be6ceb..95ab738bc 100644 --- a/packages/types/src/shell/model/window.ts +++ b/packages/types/src/shell/model/window.ts @@ -42,4 +42,10 @@ export interface IPublicModelWindow< /** 窗口视图变更事件 */ onChangeViewType(fn: (viewName: string) => void): IPublicTypeDisposable; + + /** + * 窗口视图保存事件 + * @since 1.1.7 + */ + onSave(fn: () => void): IPublicTypeDisposable; } \ No newline at end of file diff --git a/packages/types/src/shell/type/index.ts b/packages/types/src/shell/type/index.ts index b5dad9bb6..7232ffbbd 100644 --- a/packages/types/src/shell/type/index.ts +++ b/packages/types/src/shell/type/index.ts @@ -90,3 +90,4 @@ export * from './editor-view-config'; export * from './hotkey-callback-config'; export * from './hotkey-callbacks'; export * from './scrollable'; +export * from './simulator-renderer'; diff --git a/packages/types/src/shell/type/metadata.ts b/packages/types/src/shell/type/metadata.ts index 3122ab7d7..573be1689 100644 --- a/packages/types/src/shell/type/metadata.ts +++ b/packages/types/src/shell/type/metadata.ts @@ -195,6 +195,9 @@ export interface IPublicTypeCallbacks { onMoveHook?: (currentNode: IPublicModelNode) => boolean; // thinkof 限制性拖拽 onHoverHook?: (currentNode: IPublicModelNode) => boolean; + + /** 选中 hook,如果返回值是 false,可以控制组件不可被选中 */ + onSelectHook?: (currentNode: IPublicModelNode) => boolean; onChildMoveHook?: (childNode: IPublicModelNode, currentNode: IPublicModelNode) => boolean; // events diff --git a/packages/types/src/shell/type/simulator-renderer.ts b/packages/types/src/shell/type/simulator-renderer.ts new file mode 100644 index 000000000..14aa16ab8 --- /dev/null +++ b/packages/types/src/shell/type/simulator-renderer.ts @@ -0,0 +1,32 @@ +import { Asset } from '../../assets'; +import { + IPublicTypeNodeInstance, + IPublicTypeProjectSchema, + IPublicTypeComponentSchema, +} from './'; + +export interface IPublicTypeSimulatorRenderer { + readonly isSimulatorRenderer: true; + autoRepaintNode?: boolean; + components: Record; + rerender: () => void; + createComponent( + schema: IPublicTypeProjectSchema, + ): Component | null; + getComponent(componentName: string): Component; + getClosestNodeInstance( + from: ComponentInstance, + nodeId?: string, + ): IPublicTypeNodeInstance | null; + findDOMNodes(instance: ComponentInstance): Array | null; + getClientRects(element: Element | Text): DOMRect[]; + setNativeSelection(enableFlag: boolean): void; + setDraggingState(state: boolean): void; + setCopyState(state: boolean): void; + loadAsyncLibrary(asyncMap: { [index: string]: any }): void; + clearState(): void; + stopAutoRepaintNode(): void; + enableAutoRepaintNode(): void; + run(): void; + load(asset: Asset): Promise; +} diff --git a/packages/utils/package.json b/packages/utils/package.json index d52f6deef..6a0880b00 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-utils", - "version": "1.1.6", + "version": "1.1.7", "description": "Utils for Ali lowCode engine", "files": [ "lib", @@ -14,7 +14,7 @@ }, "dependencies": { "@alifd/next": "^1.19.16", - "@alilc/lowcode-types": "1.1.6", + "@alilc/lowcode-types": "1.1.7", "lodash": "^4.17.21", "mobx": "^6.3.0", "react": "^16" diff --git a/packages/utils/src/misc.ts b/packages/utils/src/misc.ts index 4510e8643..89f121065 100644 --- a/packages/utils/src/misc.ts +++ b/packages/utils/src/misc.ts @@ -81,6 +81,7 @@ const stageList = [ 'init', 'upgrade', ]; + /** * 兼容原来的数字版本的枚举对象 * @param stage @@ -108,4 +109,18 @@ export function deprecate(fail: any, message: string, alterative?: string) { export function isRegExp(obj: any): obj is RegExp { return obj && obj.test && obj.exec && obj.compile; +} + +/** + * The prop supportVariable SHOULD take precedence over default global supportVariable. + * @param propSupportVariable prop supportVariable + * @param globalSupportVariable global supportVariable + * @returns + */ +export function shouldUseVariableSetter( + propSupportVariable: boolean | undefined, + globalSupportVariable: boolean, +) { + if (propSupportVariable === false) return false; + return propSupportVariable || globalSupportVariable; } \ No newline at end of file diff --git a/packages/utils/test/src/misc.test.ts b/packages/utils/test/src/misc.test.ts new file mode 100644 index 000000000..8873dc4c8 --- /dev/null +++ b/packages/utils/test/src/misc.test.ts @@ -0,0 +1,9 @@ +import { shouldUseVariableSetter } from '../../src/misc'; + +it('shouldUseVariableSetter', () => { + expect(shouldUseVariableSetter(false, true)).toBeFalsy(); + expect(shouldUseVariableSetter(true, true)).toBeTruthy(); + expect(shouldUseVariableSetter(true, false)).toBeTruthy(); + expect(shouldUseVariableSetter(undefined, false)).toBeFalsy(); + expect(shouldUseVariableSetter(undefined, true)).toBeTruthy(); +}); \ No newline at end of file diff --git a/packages/workspace/package.json b/packages/workspace/package.json index 52a45567d..569d65203 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-workspace", - "version": "1.1.6", + "version": "1.1.7", "description": "Shell Layer for AliLowCodeEngine", "main": "lib/index.js", "module": "es/index.js", @@ -15,11 +15,11 @@ }, "license": "MIT", "dependencies": { - "@alilc/lowcode-designer": "1.1.6", - "@alilc/lowcode-editor-core": "1.1.6", - "@alilc/lowcode-editor-skeleton": "1.1.6", - "@alilc/lowcode-types": "1.1.6", - "@alilc/lowcode-utils": "1.1.6", + "@alilc/lowcode-designer": "1.1.7", + "@alilc/lowcode-editor-core": "1.1.7", + "@alilc/lowcode-editor-skeleton": "1.1.7", + "@alilc/lowcode-types": "1.1.7", + "@alilc/lowcode-utils": "1.1.7", "classnames": "^2.2.6", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.5", diff --git a/packages/workspace/src/window.ts b/packages/workspace/src/window.ts index 0e37b4986..6fb706632 100644 --- a/packages/workspace/src/window.ts +++ b/packages/workspace/src/window.ts @@ -77,7 +77,18 @@ export class EditorWindow implements IEditorWindow { const saveResult = await this.editorViews.get(name)?.save(); value[name] = saveResult; } - return await this.resource.save(value); + const result = await this.resource.save(value); + this.emitter.emit('handle.save'); + + return result; + } + + onSave(fn: () => void) { + this.emitter.on('handle.save', fn); + + return () => { + this.emitter.off('handle.save', fn); + }; } async init() {