diff --git a/.github/workflows/check base branch.yml b/.github/workflows/check base branch.yml new file mode 100644 index 000000000..cef996c75 --- /dev/null +++ b/.github/workflows/check base branch.yml @@ -0,0 +1,33 @@ +name: Check Base Branch + +on: + pull_request: + types: [opened] + +jobs: + code-review: + name: Check + runs-on: ubuntu-latest + + steps: + # 判断用户是否有写仓库权限 + - name: 'Check User Permission' + uses: 'lannonbr/repo-permission-check-action@2.0.0' + with: + permission: 'write' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: 'Check base branch name is develop or not' + if: github.event.pull_request.base.ref != 'develop' # check the target branch if it's master + uses: actions-cool/issues-helper@v2 + with: + actions: 'create-comment' + token: ${{ secrets.GITHUB_TOKEN }} + issue-number: ${{ github.event.pull_request.number }} + body: | + 感谢你的 PR,根据引擎的 [研发协作流程](https://lowcode-engine.cn/site/docs/participate/flow),请将目标合入分支设置为 **develop**。 + + Thanks in advance, according to the [Contribution Guideline](https://lowcode-engine.cn/site/docs/participate/flow), please set the base branch to **develop**. + + @${{ github.event.pull_request.user.login }} \ No newline at end of file diff --git a/.github/workflows/cov packages.yml b/.github/workflows/cov packages.yml index 499750282..7f92e1009 100644 --- a/.github/workflows/cov packages.yml +++ b/.github/workflows/cov packages.yml @@ -73,7 +73,7 @@ jobs: package-manager: yarn annotations: none -cov-utils: + cov-utils: runs-on: ubuntu-latest # skip fork's PR, otherwise it fails while making a comment if: ${{ github.event.pull_request.head.repo.full_name == 'alibaba/lowcode-engine' }} @@ -91,6 +91,6 @@ cov-utils: - uses: ArtiomTr/jest-coverage-report-action@v2 with: working-directory: packages/utils - test-script: npm test + test-script: npm test -- --jest-ci --jest-json --jest-coverage --jest-testLocationInResults --jest-outputFile=report.json package-manager: yarn annotations: none \ No newline at end of file diff --git a/.github/workflows/help wanted.yml b/.github/workflows/help wanted.yml index 94927ad28..619d08b93 100644 --- a/.github/workflows/help wanted.yml +++ b/.github/workflows/help wanted.yml @@ -1,4 +1,4 @@ -name: Issue Reply +name: Help Wanted on: issues: diff --git a/.github/workflows/insufficient information.yml b/.github/workflows/insufficient information.yml index 15885043a..c49e133f1 100644 --- a/.github/workflows/insufficient information.yml +++ b/.github/workflows/insufficient information.yml @@ -1,4 +1,4 @@ -name: Issue Reply +name: Insufficient Info on: issues: diff --git a/.github/workflows/test modules.yml b/.github/workflows/test modules.yml index 9410626e8..b2464cc40 100644 --- a/.github/workflows/test modules.yml +++ b/.github/workflows/test modules.yml @@ -1,4 +1,4 @@ -name: lint & test +name: Lint & Test (Mods) on: push: diff --git a/.github/workflows/test packages.yml b/.github/workflows/test packages.yml index 484ef849a..4ee9b4156 100644 --- a/.github/workflows/test packages.yml +++ b/.github/workflows/test packages.yml @@ -1,4 +1,4 @@ -name: lint & test +name: Lint & Test (Pkgs) on: push: @@ -43,7 +43,7 @@ jobs: - name: test run: cd packages/designer && npm test - editor-skeleton: + test-editor-skeleton: runs-on: ubuntu-latest steps: - name: checkout @@ -57,4 +57,52 @@ jobs: run: npm i && npm run setup:skip-build - name: test - run: cd packages/editor-skeleton && npm test \ No newline at end of file + run: cd packages/editor-skeleton && npm test + + test-renderer-core: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + + - uses: actions/setup-node@v2 + with: + node-version: '14' + + - name: install + run: npm i && npm run setup:skip-build + + - name: test + run: cd packages/renderer-core && npm test + + test-react-simulator-renderer: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + + - uses: actions/setup-node@v2 + with: + node-version: '14' + + - name: install + run: npm i && npm run setup:skip-build + + - name: test + run: cd packages/react-simulator-renderer && npm test + + test-utils: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + + - uses: actions/setup-node@v2 + with: + node-version: '14' + + - name: install + run: npm i && npm run setup:skip-build + + - name: test + run: cd packages/utils && npm test \ No newline at end of file diff --git a/docs/docs/api/common.md b/docs/docs/api/common.md index e5bfa8629..966e2277f 100644 --- a/docs/docs/api/common.md +++ b/docs/docs/api/common.md @@ -95,6 +95,17 @@ common.utils.startTransaction(() => { }, IPublicEnumTransitionType.repaint); ``` +#### getConvertedExtraKey + +props key 转化工具 + +```typescript +getConvertedExtraKey(key: string): string + +``` + +**@since v1.0.17** + #### createIntl i18n 相关工具 ```typescript @@ -144,4 +155,4 @@ const { intl, getLocale, setLocale } = common.utils.createIntl({ * get Workbench Component */ get Workbench(): Component; -``` \ No newline at end of file +``` diff --git a/docs/docs/api/config.md b/docs/docs/api/config.md index ea4c7dbfc..9294b9d28 100644 --- a/docs/docs/api/config.md +++ b/docs/docs/api/config.md @@ -105,6 +105,17 @@ getPreference(): IPublicModelPreference; **@since v1.1.0** +示例 + +```javascript +import { config } from '@alilc/lowcode-engine'; + +const panelName = 'outline-master-pane'; + +// 设置大纲树面板钉住,在大纲树下次重新打开时生效 +config.getPreference().set(`${panelName}-pinned-status-isFloat`, false, 'skeleton') +``` + ## 事件 ### onceGot diff --git a/docs/docs/api/material.md b/docs/docs/api/material.md index b52ad8cb2..5a5502fda 100644 --- a/docs/docs/api/material.md +++ b/docs/docs/api/material.md @@ -245,6 +245,7 @@ material.getComponentMeta('Input'); ``` #### getComponentMetasMap + 获取所有已注册的物料元数据 ```typescript @@ -264,6 +265,15 @@ import { material } from '@alilc/lowcode-engine'; material.getComponentMetasMap(); ``` +#### refreshComponentMetasMap + +刷新 componentMetasMap,可触发模拟器里的 components 重新构建 + +**@since v1.1.7** + +```typescript + refreshComponentMetasMap(): void; +``` ### 物料元数据管道函数 #### registerMetadataTransducer diff --git a/docs/docs/api/model/editor-view.md b/docs/docs/api/model/editor-view.md new file mode 100644 index 000000000..a3cc83e1f --- /dev/null +++ b/docs/docs/api/model/editor-view.md @@ -0,0 +1,21 @@ +--- +title: EditorView +sidebar_position: 12 +--- + +> **[@experimental](./#experimental)**
+> **@types** [IPublicModelEditorView](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/editor-view.ts)
+> **@since** v1.1.7 + +窗口编辑视图 + +## 类型定义 + +``` +import { IPublicModelPluginContext } from "./plugin-context"; + +export interface IPublicModelEditorView extends IPublicModelPluginContext {}; + +``` + +相关类型定义: [IPublicModelPluginContext](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/plugin-context.ts) 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 f102c0cab..9db39996f 100644 --- a/docs/docs/api/model/window.md +++ b/docs/docs/api/model/window.md @@ -38,6 +38,25 @@ sidebar_position: 12 关联模型 [IPublicModelResource](./resource) +### currentEditorView +窗口当前视图 + +`@type {IPublicModelEditorView}` + +关联模型 [IPublicModelEditorView](./editor-view) + +**@since v1.1.7** + +### editorViews + +窗口所有视图 + +`@type {IPublicModelEditorView[]}` + +关联模型 [IPublicModelEditorView](./editor-view) + +**@since v1.1.7** + ## 方法 ### importSchema @@ -74,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/api/setters.md b/docs/docs/api/setters.md index 92d24f4e1..cc7b6d429 100644 --- a/docs/docs/api/setters.md +++ b/docs/docs/api/setters.md @@ -145,7 +145,7 @@ export default class AltStringSetter extends React.PureComponent 每个区域的组成都可以被替换和自定义来生成开发者需要的业务产品。 + +## 目录介绍 +![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/package.json b/docs/package.json index c05ace310..efac589fa 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-engine-docs", - "version": "1.0.25", + "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/package.json b/modules/code-generator/package.json index 97d945469..f830b23ed 100644 --- a/modules/code-generator/package.json +++ b/modules/code-generator/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-code-generator", - "version": "1.1.0", + "version": "1.1.2", "description": "出码引擎 for LowCode Engine", "license": "MIT", "main": "lib/index.js", @@ -145,4 +145,4 @@ "registry": "https://registry.npmjs.org/" }, "repository": "git@github.com:alibaba/lowcode-engine.git" -} \ No newline at end of file +} diff --git a/modules/code-generator/src/index.ts b/modules/code-generator/src/index.ts index 0c8fdf384..da9b12d87 100644 --- a/modules/code-generator/src/index.ts +++ b/modules/code-generator/src/index.ts @@ -9,7 +9,7 @@ import { createModuleBuilder } from './generator/ModuleBuilder'; import { createDiskPublisher } from './publisher/disk'; import { createZipPublisher } from './publisher/zip'; import createIceJsProjectBuilder, { plugins as icejsPlugins } from './solutions/icejs'; -import createIce3JsProjectBuilder, { plugins as icejs3Plugins } from './solutions/icejs3'; +import createIceJs3ProjectBuilder, { plugins as icejs3Plugins } from './solutions/icejs3'; import createRaxAppProjectBuilder, { plugins as raxPlugins } from './solutions/rax-app'; // 引入说明 @@ -42,7 +42,7 @@ export default { createModuleBuilder, solutions: { icejs: createIceJsProjectBuilder, - icejs3: createIce3JsProjectBuilder, + icejs3: createIceJs3ProjectBuilder, rax: createRaxAppProjectBuilder, }, solutionParts: { diff --git a/modules/code-generator/src/parser/SchemaParser.ts b/modules/code-generator/src/parser/SchemaParser.ts index 3108fee47..b4f7424ce 100644 --- a/modules/code-generator/src/parser/SchemaParser.ts +++ b/modules/code-generator/src/parser/SchemaParser.ts @@ -32,7 +32,7 @@ import { import { SUPPORT_SCHEMA_VERSION_LIST } from '../const'; import { getErrorMessage } from '../utils/errors'; -import { handleSubNodes, isValidContainerType } from '../utils/schema'; +import { handleSubNodes, isValidContainerType, ContainerType } from '../utils/schema'; import { uniqueArray } from '../utils/common'; import { componentAnalyzer } from '../analyzer/componentAnalyzer'; import { ensureValidClassName } from '../utils/validate'; @@ -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) { @@ -161,7 +166,8 @@ export class SchemaParser implements ISchemaParser { ...subRoot, componentName: getRootComponentName(subRoot.componentName, compDeps), containerType: subRoot.componentName, - moduleName: ensureValidClassName(changeCase.pascalCase(subRoot.fileName)), + moduleName: ensureValidClassName(subRoot.componentName === ContainerType.Component ? + subRoot.fileName : changeCase.pascalCase(subRoot.fileName)), }; return container; }); @@ -220,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/plugins/common/styleImport.ts b/modules/code-generator/src/plugins/common/styleImport.ts index c340900ea..23e129302 100644 --- a/modules/code-generator/src/plugins/common/styleImport.ts +++ b/modules/code-generator/src/plugins/common/styleImport.ts @@ -21,6 +21,7 @@ const pluginFactory: BuilderComponentPluginFactory = () => { if (ir && ir.deps && ir.deps.length > 0) { let lowcodeMaterialsStyleAdded = false; + let fusionUIStyleAdded = false; let nextStyleAddedMap: Record = {}; ir.deps.forEach((dep: any) => { if (dep.package === '@alifd/next' && !nextStyleAddedMap[dep.exportName]) { @@ -41,6 +42,15 @@ const pluginFactory: BuilderComponentPluginFactory = () => { linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport], }); lowcodeMaterialsStyleAdded = true; + } else if (dep.package === '@alifd/fusion-ui' && !fusionUIStyleAdded) { + chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: COMMON_CHUNK_NAME.InternalDepsImport, + content: 'import \'@alifd/fusion-ui/lib/style\';', + linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport], + }); + fusionUIStyleAdded = true; } }); } diff --git a/modules/code-generator/src/plugins/component/react/containerClass.ts b/modules/code-generator/src/plugins/component/react/containerClass.ts index b4c474b38..eab6cbebe 100644 --- a/modules/code-generator/src/plugins/component/react/containerClass.ts +++ b/modules/code-generator/src/plugins/component/react/containerClass.ts @@ -48,7 +48,7 @@ const pluginFactory: BuilderComponentPluginFactory = () => { type: ChunkType.STRING, fileType: FileType.JSX, name: CLASS_DEFINE_CHUNK_NAME.InsVar, - content: `static displayName = '${changeCase.pascalCase(ir.moduleName)}';`, + content: `static displayName = '${ir.moduleName}';`, linkAfter: [ CLASS_DEFINE_CHUNK_NAME.Start, ], diff --git a/modules/code-generator/src/plugins/component/react/jsx.ts b/modules/code-generator/src/plugins/component/react/jsx.ts index 7cf5bd9de..588b356ad 100644 --- a/modules/code-generator/src/plugins/component/react/jsx.ts +++ b/modules/code-generator/src/plugins/component/react/jsx.ts @@ -58,7 +58,7 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => generateCompositeType( { type: 'JSFunction', - value: input.value || 'null', + value: input.value || 'function () {}', }, Scope.createRootScope(), ), 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/standalone.ts b/modules/code-generator/src/standalone.ts index 2f15515cf..23d16e61f 100644 --- a/modules/code-generator/src/standalone.ts +++ b/modules/code-generator/src/standalone.ts @@ -8,7 +8,8 @@ import './polyfills/buffer'; import { createProjectBuilder } from './generator/ProjectBuilder'; import { createModuleBuilder } from './generator/ModuleBuilder'; import { createZipPublisher } from './publisher/zip'; -import createIceJsProjectBuilder, { plugins as reactPlugins } from './solutions/icejs'; +import createIceJsProjectBuilder, { plugins as icejsPlugins } from './solutions/icejs'; +import createIceJs3ProjectBuilder, { plugins as icejs3Plugins } from './solutions/icejs3'; import createRaxAppProjectBuilder, { plugins as raxPlugins } from './solutions/rax-app'; // 引入说明 @@ -18,6 +19,7 @@ import { COMMON_CHUNK_NAME, CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from ' // 引入通用插件组 import esmodule from './plugins/common/esmodule'; import requireUtils from './plugins/common/requireUtils'; +import styleImport from './plugins/common/styleImport'; import css from './plugins/component/style/css'; import constants from './plugins/project/constants'; @@ -32,6 +34,7 @@ import * as CONSTANTS from './const'; // 引入内置解决方案模块 import icejs from './plugins/project/framework/icejs'; +import icejs3 from './plugins/project/framework/icejs3'; import rax from './plugins/project/framework/rax'; export default { @@ -39,10 +42,12 @@ export default { createModuleBuilder, solutions: { icejs: createIceJsProjectBuilder, + icejs3: createIceJs3ProjectBuilder, rax: createRaxAppProjectBuilder, }, solutionParts: { icejs, + icejs3, rax, }, publishers: { @@ -50,6 +55,7 @@ export default { }, plugins: { common: { + /** * 处理 ES Module * @deprecated please use esModule @@ -57,12 +63,7 @@ export default { esmodule, esModule: esmodule, requireUtils, - }, - react: { - ...reactPlugins, - }, - rax: { - ...raxPlugins, + styleImport, }, style: { css, @@ -72,6 +73,22 @@ export default { i18n, utils, }, + icejs: { + ...icejsPlugins, + }, + icejs3: { + ...icejs3Plugins, + }, + rax: { + ...raxPlugins, + }, + + /** + * @deprecated please use icejs + */ + react: { + ...icejsPlugins, + }, }, postprocessor: { prettier, diff --git a/modules/code-generator/src/utils/schema.ts b/modules/code-generator/src/utils/schema.ts index 831b38965..f9529945e 100644 --- a/modules/code-generator/src/utils/schema.ts +++ b/modules/code-generator/src/utils/schema.ts @@ -147,4 +147,10 @@ export function isValidContainerType(schema: IPublicTypeNodeSchema) { 'Component', 'Block', ].includes(schema.componentName); +} + +export const enum ContainerType { + Page = 'Page', + Component = 'Component', + Block = 'Block', } \ No newline at end of file diff --git a/package.json b/package.json index 02585e563..6a650c49b 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "yarn": "^1.22.17", "rimraf": "^3.0.2", "@types/react-router": "5.1.18", - "build-plugin-component": "^1.12.0", + "@alilc/build-plugin-lce": "^0.0.3", "babel-jest": "^26.5.2", "@alilc/lowcode-test-mate": "^1.0.1" }, diff --git a/packages/designer/build.json b/packages/designer/build.json index bd5cf18dd..3e9260055 100644 --- a/packages/designer/build.json +++ b/packages/designer/build.json @@ -1,5 +1,5 @@ { "plugins": [ - "build-plugin-component" + "@alilc/build-plugin-lce" ] } diff --git a/packages/designer/build.test.json b/packages/designer/build.test.json index 45f0dbdfd..10d18109b 100644 --- a/packages/designer/build.test.json +++ b/packages/designer/build.test.json @@ -1,6 +1,6 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", "@alilc/lowcode-test-mate/plugin/index.ts" ], "babelPlugins": [ diff --git a/packages/designer/package.json b/packages/designer/package.json index 61190a3be..cb73cdeb9 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", @@ -9,15 +9,15 @@ "es" ], "scripts": { - "build": "build-scripts build --skip-demo", + "build": "build-scripts build", "test": "build-scripts test --config build.test.json", "test:cov": "build-scripts test --config build.test.json --jest-coverage" }, "license": "MIT", "dependencies": { - "@alilc/lowcode-editor-core": "1.1.6", - "@alilc/lowcode-types": "1.1.6", - "@alilc/lowcode-utils": "1.1.6", + "@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/bem-tools/border-resizing.tsx b/packages/designer/src/builtin-simulator/bem-tools/border-resizing.tsx index 29401ca6b..4b3c5c31a 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/border-resizing.tsx +++ b/packages/designer/src/builtin-simulator/bem-tools/border-resizing.tsx @@ -1,10 +1,10 @@ import React, { Component, Fragment } from 'react'; import DragResizeEngine from './drag-resize-engine'; -import { observer, computed, globalContext } from '@alilc/lowcode-editor-core'; +import { observer, computed } from '@alilc/lowcode-editor-core'; import classNames from 'classnames'; import { SimulatorContext } from '../context'; import { BuiltinSimulatorHost } from '../host'; -import { OffsetObserver, Designer } from '../../designer'; +import { OffsetObserver, Designer, INode } from '../../designer'; import { Node } from '../../document'; import { normalizeTriggers } from '../../utils/misc'; @@ -135,7 +135,7 @@ export class BoxResizingInstance extends Component<{ // this.hoveringCapture.setBoundary(this.outline); this.willBind(); - const resize = (e: MouseEvent, direction: string, node: any, moveX: number, moveY: number) => { + const resize = (e: MouseEvent, direction: string, node: INode, moveX: number, moveY: number) => { const { advanced } = node.componentMeta; if ( advanced.callbacks && @@ -149,7 +149,7 @@ export class BoxResizingInstance extends Component<{ } }; - const resizeStart = (e: MouseEvent, direction: string, node: any) => { + const resizeStart = (e: MouseEvent, direction: string, node: INode) => { const { advanced } = node.componentMeta; if ( advanced.callbacks && @@ -161,7 +161,7 @@ export class BoxResizingInstance extends Component<{ } }; - const resizeEnd = (e: MouseEvent, direction: string, node: any) => { + const resizeEnd = (e: MouseEvent, direction: string, node: INode) => { const { advanced } = node.componentMeta; if ( advanced.callbacks && @@ -172,8 +172,7 @@ export class BoxResizingInstance extends Component<{ advanced.callbacks.onResizeEnd(e, cbNode); } - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = node.document?.designer.editor; const npm = node?.componentMeta?.npm; const selected = [npm?.package, npm?.componentName].filter((item) => !!item).join('-') || diff --git a/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx b/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx index 75b4a3488..0e9d734b6 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx +++ b/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx @@ -9,7 +9,7 @@ import { ComponentType, } from 'react'; import classNames from 'classnames'; -import { observer, computed, Tip, globalContext } from '@alilc/lowcode-editor-core'; +import { observer, computed, Tip } from '@alilc/lowcode-editor-core'; import { createIcon, isReactComponent, isActionContentObject } from '@alilc/lowcode-utils'; import { IPublicTypeActionContentObject } from '@alilc/lowcode-types'; import { BuiltinSimulatorHost } from '../host'; @@ -131,8 +131,7 @@ function createAction(content: ReactNode | ComponentType | IPublicTypeActio className="lc-borders-action" onClick={() => { action && action(node.internalToShellNode()!); - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = node.document?.designer.editor; const npm = node?.componentMeta?.npm; const selected = [npm?.package, npm?.componentName].filter((item) => !!item).join('-') || diff --git a/packages/designer/src/builtin-simulator/create-simulator.ts b/packages/designer/src/builtin-simulator/create-simulator.ts index e46582180..15ab3507b 100644 --- a/packages/designer/src/builtin-simulator/create-simulator.ts +++ b/packages/designer/src/builtin-simulator/create-simulator.ts @@ -20,7 +20,9 @@ export function createSimulator( ): Promise { const win: any = iframe.contentWindow; const doc = iframe.contentDocument!; + const innerPlugins = host.designer.editor.get('innerPlugins'); + win.AliLowCodeEngine = innerPlugins._getLowCodePluginContext({}); win.LCSimulatorHost = host; win._ = window._; diff --git a/packages/designer/src/builtin-simulator/host-view.tsx b/packages/designer/src/builtin-simulator/host-view.tsx index a7943ea9f..21e007930 100644 --- a/packages/designer/src/builtin-simulator/host-view.tsx +++ b/packages/designer/src/builtin-simulator/host-view.tsx @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { observer, globalContext } from '@alilc/lowcode-editor-core'; +import { observer } from '@alilc/lowcode-editor-core'; import { BuiltinSimulatorHost, BuiltinSimulatorProps } from './host'; import { BemTools } from './bem-tools'; import { Project } from '../project'; @@ -76,8 +76,7 @@ class Content extends Component<{ host: BuiltinSimulatorHost }> { private dispose?: () => void; componentDidMount() { - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = this.props.host.designer.editor; const onEnableEvents = (type: boolean) => { this.setState({ disabledEvents: type, diff --git a/packages/designer/src/builtin-simulator/live-editing/live-editing.ts b/packages/designer/src/builtin-simulator/live-editing/live-editing.ts index e92e266ee..c8594d701 100644 --- a/packages/designer/src/builtin-simulator/live-editing/live-editing.ts +++ b/packages/designer/src/builtin-simulator/live-editing/live-editing.ts @@ -1,6 +1,6 @@ -import { obx, globalContext } from '@alilc/lowcode-editor-core'; +import { obx } from '@alilc/lowcode-editor-core'; import { IPublicTypePluginConfig, IPublicTypeLiveTextEditingConfig } from '@alilc/lowcode-types'; -import { Node, Prop } from '../../document'; +import { INode, Prop } from '../../document'; const EDITOR_KEY = 'data-setter-prop'; @@ -17,7 +17,7 @@ function defaultSaveContent(content: string, prop: Prop) { } export interface EditingTarget { - node: Node; + node: INode; rootElement: HTMLElement; event: MouseEvent; } @@ -47,13 +47,16 @@ export class LiveEditing { @obx.ref private _editing: Prop | null = null; + private _dispose?: () => void; + + private _save?: () => void; + apply(target: EditingTarget) { const { node, event, rootElement } = target; const targetElement = event.target as HTMLElement; const { liveTextEditing } = node.componentMeta; - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = node.document?.designer.editor; const npm = node?.componentMeta?.npm; const selected = [npm?.package, npm?.componentName].filter((item) => !!item).join('-') || node?.componentMeta?.componentName || ''; @@ -166,10 +169,6 @@ export class LiveEditing { return this._editing; } - private _dispose?: () => void; - - private _save?: () => void; - saveAndDispose() { if (this._save) { this._save(); diff --git a/packages/designer/src/builtin-simulator/node-selector/index.tsx b/packages/designer/src/builtin-simulator/node-selector/index.tsx index 09c8f1262..07ae754aa 100644 --- a/packages/designer/src/builtin-simulator/node-selector/index.tsx +++ b/packages/designer/src/builtin-simulator/node-selector/index.tsx @@ -1,6 +1,6 @@ import { Overlay } from '@alifd/next'; import React, { MouseEvent } from 'react'; -import { Title, globalContext } from '@alilc/lowcode-editor-core'; +import { Title } from '@alilc/lowcode-editor-core'; import { canClickNode } from '@alilc/lowcode-utils'; import './index.less'; @@ -66,8 +66,7 @@ export default class InstanceNodeSelector extends React.Component !!item).join('-') || diff --git a/packages/designer/src/component-actions.ts b/packages/designer/src/component-actions.ts index 793bbdc20..a7a690a44 100644 --- a/packages/designer/src/component-actions.ts +++ b/packages/designer/src/component-actions.ts @@ -56,8 +56,8 @@ export class ComponentActions { const { isRGL, rglNode } = node.getRGL(); if (isRGL) { // 复制 layout 信息 - let layout = rglNode.getPropValue('layout') || []; - let curLayout = layout.filter((item) => item.i === node.getPropValue('fieldId')); + const layout = rglNode.getPropValue('layout') || []; + const curLayout = layout.filter((item) => item.i === node.getPropValue('fieldId')); if (curLayout && curLayout[0]) { layout.push({ ...curLayout[0], diff --git a/packages/designer/src/component-meta.ts b/packages/designer/src/component-meta.ts index 4b0a613cb..00c495621 100644 --- a/packages/designer/src/component-meta.ts +++ b/packages/designer/src/component-meta.ts @@ -60,6 +60,8 @@ export function buildFilter(rule?: string | string[] | RegExp | IPublicTypeNesti export interface IComponentMeta extends IPublicModelComponentMeta { prototype?: any; + liveTextEditing?: IPublicTypeLiveTextEditingConfig[]; + get rootSelector(): string | undefined; setMetadata(metadata: IPublicTypeComponentMetadata): void; diff --git a/packages/designer/src/designer/active-tracker.ts b/packages/designer/src/designer/active-tracker.ts index 27a7090f8..74d865673 100644 --- a/packages/designer/src/designer/active-tracker.ts +++ b/packages/designer/src/designer/active-tracker.ts @@ -7,6 +7,8 @@ import { import { isNode } from '@alilc/lowcode-utils'; export interface IActiveTracker extends Omit< IPublicModelActiveTracker, 'track' | 'onChange' > { + _target: ActiveTarget | INode; + track(originalTarget: ActiveTarget | INode): void; onChange(fn: (target: ActiveTarget) => void): () => void; @@ -17,10 +19,10 @@ export interface ActiveTarget extends Omit< IPublicTypeActiveTarget, 'node' > { } export class ActiveTracker implements IActiveTracker { - private emitter: IEventBus = createModuleEventBus('ActiveTracker'); - @obx.ref private _target?: ActiveTarget | INode; + private emitter: IEventBus = createModuleEventBus('ActiveTracker'); + track(originalTarget: ActiveTarget | INode) { let target = originalTarget; if (isNode(originalTarget)) { diff --git a/packages/designer/src/document/document-model.ts b/packages/designer/src/document/document-model.ts index c55e7a8e4..9521fef38 100644 --- a/packages/designer/src/document/document-model.ts +++ b/packages/designer/src/document/document-model.ts @@ -141,13 +141,17 @@ export interface IDocumentModel extends Omit { - 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/history.ts b/packages/designer/src/document/history.ts index d190a9a9c..ca288c03a 100644 --- a/packages/designer/src/document/history.ts +++ b/packages/designer/src/document/history.ts @@ -1,6 +1,7 @@ -import { reaction, untracked, globalContext, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core'; +import { reaction, untracked, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core'; import { IPublicTypeNodeSchema, IPublicModelHistory, IPublicTypeDisposable } from '@alilc/lowcode-types'; import { Logger } from '@alilc/lowcode-utils'; +import { IDocumentModel } from '../designer'; const logger = new Logger({ level: 'warn', bizName: 'history' }); @@ -37,10 +38,12 @@ export class History implements IHistory { return this.session.data; } + private timeGap: number = 1000; + constructor( dataFn: () => T | null, private redoer: (data: T) => void, - private timeGap: number = 1000, + private document?: IDocumentModel, ) { this.session = new Session(0, null, this.timeGap); this.records = [this.session]; @@ -130,8 +133,7 @@ export class History implements IHistory { } const cursor = this.session.cursor - 1; this.go(cursor); - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = this.document?.designer.editor; if (!editor) { return; } @@ -144,8 +146,7 @@ export class History implements IHistory { } const cursor = this.session.cursor + 1; this.go(cursor); - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = this.document?.designer.editor; if (!editor) { return; } diff --git a/packages/designer/src/document/node/node-children.ts b/packages/designer/src/document/node/node-children.ts index 1b6df5866..4fb1c3d22 100644 --- a/packages/designer/src/document/node/node-children.ts +++ b/packages/designer/src/document/node/node-children.ts @@ -1,4 +1,4 @@ -import { obx, computed, globalContext, makeObservable, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core'; +import { obx, computed, makeObservable, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core'; import { Node, INode } from './node'; import { IPublicTypeNodeData, IPublicModelNodeChildren, IPublicEnumTransformStage, IPublicTypeDisposable } from '@alilc/lowcode-types'; import { shallowEqual, compatStage, isNodeSchema } from '@alilc/lowcode-utils'; @@ -16,12 +16,12 @@ export interface INodeChildren extends Omit, 'isEmpty' | 'notEmpty' > { + children: INode[]; + get owner(): INode; get length(): number; - children: INode[]; - unlinkChild(node: INode): void; /** @@ -239,11 +239,8 @@ export class NodeChildren implements INodeChildren { } const { document } = node; /* istanbul ignore next */ - if (globalContext.has('editor')) { - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); - editor.eventBus.emit('node.remove', { node, index: i }); - } + const editor = node.document?.designer.editor; + editor?.eventBus.emit('node.remove', { node, index: i }); document?.unlinkNode(node); document?.selection.remove(node.id); document?.destroyNode(node); @@ -281,14 +278,11 @@ export class NodeChildren implements INodeChildren { const i = children.map(d => d.id).indexOf(node.id); if (node.parent) { - if (globalContext.has('editor')) { - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); - editor.eventBus.emit('node.remove.topLevel', { - node, - index: node.index, - }); - } + const editor = node.document?.designer.editor; + editor?.eventBus.emit('node.remove.topLevel', { + node, + index: node.index, + }); } if (i < 0) { @@ -317,11 +311,8 @@ export class NodeChildren implements INodeChildren { }); this.emitter.emit('insert', node); /* istanbul ignore next */ - if (globalContext.has('editor')) { - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); - editor.eventBus.emit('node.add', { node }); - } + const editor = node.document?.designer.editor; + editor?.eventBus.emit('node.add', { node }); if (useMutator) { this.reportModified(node, this.owner, { type: 'insert' }); } diff --git a/packages/designer/src/document/node/props/prop.ts b/packages/designer/src/document/node/props/prop.ts index b1fd1aec4..37017d72d 100644 --- a/packages/designer/src/document/node/props/prop.ts +++ b/packages/designer/src/document/node/props/prop.ts @@ -16,6 +16,8 @@ export type UNSET = typeof UNSET; export interface IProp extends Omit, 'exportSchema' | 'node'>, IPropParent { + spread: boolean; + key: string | number | undefined; readonly props: IProps; @@ -42,6 +44,8 @@ export interface IProp extends Omit { + return this.items!.map((prop) => { return prop?.export(stage); }); - if (values.every((val) => val === undefined)) { - return undefined; - } - return values; } } diff --git a/packages/designer/src/plugin/plugin-context.ts b/packages/designer/src/plugin/plugin-context.ts index f43134c75..94e296fd1 100644 --- a/packages/designer/src/plugin/plugin-context.ts +++ b/packages/designer/src/plugin/plugin-context.ts @@ -17,6 +17,8 @@ import { IPublicTypePluginDeclaration, IPublicApiCanvas, IPublicApiWorkspace, + IPublicEnumPluginRegisterLevel, + IPublicModelWindow, } from '@alilc/lowcode-types'; import { IPluginContextOptions, @@ -41,6 +43,8 @@ export default class PluginContext implements pluginEvent: IPublicApiEvent; canvas: IPublicApiCanvas; workspace: IPublicApiWorkspace; + registerLevel: IPublicEnumPluginRegisterLevel; + editorWindow: IPublicModelWindow; constructor( options: IPluginContextOptions, diff --git a/packages/designer/src/plugin/plugin-manager.ts b/packages/designer/src/plugin/plugin-manager.ts index 449afebdf..071bfa9d1 100644 --- a/packages/designer/src/plugin/plugin-manager.ts +++ b/packages/designer/src/plugin/plugin-manager.ts @@ -143,11 +143,10 @@ export class LowCodePluginManager implements ILowCodePluginManager { } async delete(pluginName: string): Promise { - const idx = this.plugins.findIndex((plugin) => plugin.name === pluginName); - if (idx === -1) return false; - const plugin = this.plugins[idx]; + const plugin = this.plugins.find(({ name }) => name === pluginName); + if (!plugin) return false; await plugin.destroy(); - + const idx = this.plugins.indexOf(plugin); this.plugins.splice(idx, 1); return this.pluginsMap.delete(pluginName); } diff --git a/packages/designer/src/plugin/plugin-types.ts b/packages/designer/src/plugin/plugin-types.ts index f76e20827..ac08d7d0c 100644 --- a/packages/designer/src/plugin/plugin-types.ts +++ b/packages/designer/src/plugin/plugin-types.ts @@ -16,6 +16,8 @@ import { IPublicApiWorkspace, IPublicTypePluginMeta, IPublicTypePluginRegisterOptions, + IPublicModelWindow, + IPublicEnumPluginRegisterLevel, } from '@alilc/lowcode-types'; import PluginContext from './plugin-context'; @@ -56,6 +58,8 @@ export interface ILowCodePluginContextPrivate { set pluginEvent(event: IPublicApiEvent); set canvas(canvas: IPublicApiCanvas); set workspace(workspace: IPublicApiWorkspace); + set editorWindow(window: IPublicModelWindow); + set registerLevel(level: IPublicEnumPluginRegisterLevel); } export interface ILowCodePluginContextApiAssembler { assembleApis( diff --git a/packages/designer/src/project/project.ts b/packages/designer/src/project/project.ts index 0675190b2..75e621d5e 100644 --- a/packages/designer/src/project/project.ts +++ b/packages/designer/src/project/project.ts @@ -1,7 +1,7 @@ import { obx, computed, makeObservable, action, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core'; import { IDesigner } from '../designer'; import { DocumentModel, isDocumentModel } from '../document'; -import type { IDocumentModel } from "../document"; +import type { IDocumentModel } from '../document'; import { IPublicTypeComponentsMap, IPublicEnumTransformStage, @@ -317,13 +317,13 @@ export class Project implements IProject { doc = this.createDocument(); return doc.open(); } - if (typeof doc === 'string') { - const got = this.documents.find((item) => item.fileName === doc || item.id === doc); + if (typeof doc === 'string' || typeof doc === 'number') { + const got = this.documents.find((item) => item.fileName === String(doc) || String(item.id) === String(doc)); if (got) { return got.open(); } - const data = this.data.componentsTree.find((data) => data.fileName === doc); + const data = this.data.componentsTree.find((data) => data.fileName === String(doc)); if (data) { doc = this.createDocument(data); return doc.open(); diff --git a/packages/designer/tests/builtin-simulator/host.test.ts b/packages/designer/tests/builtin-simulator/host.test.ts index 61ae61742..57e74be57 100644 --- a/packages/designer/tests/builtin-simulator/host.test.ts +++ b/packages/designer/tests/builtin-simulator/host.test.ts @@ -1,3 +1,4 @@ +import { IPublicTypePluginMeta } from './../../../../lib/packages/types/src/shell/type/plugin-meta.d'; import '../fixtures/window'; import { Editor, @@ -22,6 +23,7 @@ import { BuiltinSimulatorHost } from '../../src/builtin-simulator/host'; import { fireEvent } from '@testing-library/react'; import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory'; import { Setters, Workspace } from '@alilc/lowcode-shell'; +import { ILowCodePluginContextApiAssembler, ILowCodePluginContextPrivate, LowCodePluginManager } from '@alilc/lowcode-designer'; describe('Host 测试', () => { let editor: Editor; @@ -32,10 +34,20 @@ describe('Host 测试', () => { beforeAll(() => { editor = new Editor(); - const innerWorkspace = new InnerWorkspace(); + const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + assembleApis: (context: ILowCodePluginContextPrivate, pluginName: string, meta: IPublicTypePluginMeta) => { + context.project = project; + const eventPrefix = meta?.eventPrefix || 'common'; + context.workspace = workspace; + }, + }; + const innerPlugins = new LowCodePluginManager(pluginContextApiAssembler); + const innerWorkspace = new InnerWorkspace(() => {}, {}); const workspace = new Workspace(innerWorkspace); editor.set('innerHotkey', new InnerHotkey()) editor.set('setters', new Setters(new InnerSetters())); + editor.set('innerPlugins' as any, innerPlugins); !globalContext.has(Editor) && globalContext.register(editor, Editor); !globalContext.has('workspace') && globalContext.register(innerWorkspace, 'workspace'); }); diff --git a/packages/designer/tests/document/node/props/prop.test.ts b/packages/designer/tests/document/node/props/prop.test.ts index 932733b1a..177bc5247 100644 --- a/packages/designer/tests/document/node/props/prop.test.ts +++ b/packages/designer/tests/document/node/props/prop.test.ts @@ -435,7 +435,7 @@ describe('Prop 类测试', () => { it('should return undefined when all items are undefined', () => { prop = new Prop(mockPropsInst, [undefined, undefined], '___loopArgs___'); - expect(prop.getValue()).toBeUndefined(); + expect(prop.getValue()).toEqual([undefined, undefined]); }); it('迭代器 / map / forEach', () => { diff --git a/packages/editor-core/build.json b/packages/editor-core/build.json index 40b17de79..c1ebc2c86 100644 --- a/packages/editor-core/build.json +++ b/packages/editor-core/build.json @@ -1,6 +1,6 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", "build-plugin-fusion", "./build.plugin.js" ] diff --git a/packages/editor-core/package.json b/packages/editor-core/package.json index c4272a0ab..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", @@ -10,12 +10,12 @@ "es" ], "scripts": { - "build": "build-scripts build --skip-demo" + "build": "build-scripts build" }, "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-core/src/config.ts b/packages/editor-core/src/config.ts index ef889e727..c325c2bb8 100644 --- a/packages/editor-core/src/config.ts +++ b/packages/editor-core/src/config.ts @@ -145,6 +145,16 @@ const VALID_ENGINE_OPTIONS = { type: 'function', description: '配置指定节点为根组件', }, + enableAutoOpenFirstWindow: { + type: 'boolean', + description: '应用级设计模式下,自动打开第一个窗口', + default: true, + }, + enableWorkspaceMode: { + type: 'boolean', + description: '是否开启应用级设计模式', + default: false, + }, }; const getStrictModeValue = (engineOptions: IPublicTypeEngineOptions, defaultValue: boolean): boolean => { diff --git a/packages/editor-core/src/di/setter.ts b/packages/editor-core/src/di/setter.ts index 4139c5939..437d9a89e 100644 --- a/packages/editor-core/src/di/setter.ts +++ b/packages/editor-core/src/di/setter.ts @@ -1,8 +1,7 @@ import { ReactNode } from 'react'; -import { IPublicTypeCustomView, IPublicTypeRegisteredSetter } from '@alilc/lowcode-types'; +import { IPublicApiSetters, IPublicTypeCustomView, IPublicTypeRegisteredSetter } from '@alilc/lowcode-types'; import { createContent, isCustomView } from '@alilc/lowcode-utils'; - const settersMap = new Map(); @@ -44,13 +43,17 @@ function getInitialFromSetter(setter: any) { ) || null; // eslint-disable-line } -export class Setters { - constructor(readonly viewName: string = 'global') {} +export interface ISetters extends IPublicApiSetters { +} + +export class Setters implements ISetters { settersMap = new Map(); + constructor(readonly viewName: string = 'global') {} + getSetter = (type: string): IPublicTypeRegisteredSetter | null => { return this.settersMap.get(type) || null; }; diff --git a/packages/editor-skeleton/build.json b/packages/editor-skeleton/build.json index 77627cdf9..d0aec1038 100644 --- a/packages/editor-skeleton/build.json +++ b/packages/editor-skeleton/build.json @@ -1,9 +1,9 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", "build-plugin-fusion", ["build-plugin-moment-locales", { "locales": ["zh-cn"] }] ] -} \ No newline at end of file +} diff --git a/packages/editor-skeleton/build.test.json b/packages/editor-skeleton/build.test.json index 45f0dbdfd..10d18109b 100644 --- a/packages/editor-skeleton/build.test.json +++ b/packages/editor-skeleton/build.test.json @@ -1,6 +1,6 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", "@alilc/lowcode-test-mate/plugin/index.ts" ], "babelPlugins": [ diff --git a/packages/editor-skeleton/package.json b/packages/editor-skeleton/package.json index 7e64d8166..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", @@ -11,7 +11,7 @@ ], "scripts": { "test": "build-scripts test --config build.test.json", - "build": "build-scripts build --skip-demo" + "build": "build-scripts build" }, "keywords": [ "lowcode", @@ -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/popup/index.tsx b/packages/editor-skeleton/src/components/popup/index.tsx index 68897d61a..1367723a1 100644 --- a/packages/editor-skeleton/src/components/popup/index.tsx +++ b/packages/editor-skeleton/src/components/popup/index.tsx @@ -4,6 +4,22 @@ import { uniqueId } from '@alilc/lowcode-utils'; import { IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core'; import './style.less'; +export interface PopupExtProps { + width?: number; + hasMask?: boolean; + trigger?: ReactNode; + canCloseByOutSideClick?: boolean + className?: string; + safeNode?: string[]; +} + +interface PopupProps extends PopupExtProps{ + content?: ReactNode, + title?: ReactNode, + actionKey?: string +} + + export const PopupContext = createContext({} as any); export class PopupPipe { @@ -11,7 +27,7 @@ export class PopupPipe { private currentId?: string; - create(props?: object): { + create(props?: PopupExtProps): { send: (content: ReactNode, title: ReactNode) => void; show: (target: Element) => void; } { @@ -45,13 +61,13 @@ export class PopupPipe { }; } - private popup(props: object, target?: Element) { + private popup(props: PopupProps, target?: Element) { Promise.resolve().then(() => { this.emitter.emit('popupchange', props, target); }); } - onPopupChange(fn: (props: object, target?: Element) => void): () => void { + onPopupChange(fn: (props: PopupProps, target?: Element) => void): () => void { this.emitter.on('popupchange', fn); return () => { this.emitter.removeListener('popupchange', fn); @@ -86,18 +102,23 @@ export default class PopupService extends Component<{ } } +interface StateType extends PopupProps { + visible?: boolean, + offsetX?: number, + pos?: {top: number, height: number} +} export class PopupContent extends PureComponent<{ safeId?: string; popupContainer?: string }> { static contextType = PopupContext; popupContainerId = uniqueId('popupContainer'); - state: any = { + state: StateType = { visible: false, offsetX: -300, }; private dispose = (this.context as PopupPipe).onPopupChange((props, target) => { - const state: any = { + const state: StateType = { ...props, visible: true, }; @@ -132,7 +153,7 @@ export class PopupContent extends PureComponent<{ safeId?: string; popupContaine }; render() { - const { content, visible, title, actionKey, pos, offsetX } = this.state; + const { content, visible, title, actionKey, pos, offsetX, width = 360, hasMask = false, canCloseByOutSideClick = true, safeNode = [] } = this.state; if (!visible) { return null; } @@ -146,10 +167,10 @@ export class PopupContent extends PureComponent<{ safeId?: string; popupContaine return ( { if (avoidLaterHidden) { return; @@ -160,11 +181,11 @@ export class PopupContent extends PureComponent<{ safeId?: string; popupContaine }} trigger={
} triggerType="click" - canCloseByOutSideClick + canCloseByOutSideClick={canCloseByOutSideClick} animation={false} onClose={this.onClose} id={this.props.safeId} - safeNode={id} + safeNode={[id, ...safeNode]} closeable container={this.props.popupContainer} > diff --git a/packages/editor-skeleton/src/components/settings/settings-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-pane.tsx index 46b188acc..1d651bb5a 100644 --- a/packages/editor-skeleton/src/components/settings/settings-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-pane.tsx @@ -1,14 +1,13 @@ -import { Component, MouseEvent, Fragment } from 'react'; -import { shallowIntl, observer, obx, engineConfig, runInAction, globalContext } from '@alilc/lowcode-editor-core'; -import { createContent, isJSSlot, isSetterConfig } from '@alilc/lowcode-utils'; +import { Component, MouseEvent, Fragment, ReactNode } from 'react'; +import { shallowIntl, observer, obx, engineConfig, runInAction } from '@alilc/lowcode-editor-core'; +import { createContent, isJSSlot, isSetterConfig, shouldUseVariableSetter } from '@alilc/lowcode-utils'; import { Skeleton, Stage } from '@alilc/lowcode-editor-skeleton'; -import { IPublicTypeCustomView } from '@alilc/lowcode-types'; +import { IPublicApiSetters, IPublicTypeCustomView, IPublicTypeDynamicProps } from '@alilc/lowcode-types'; import { ISettingEntry, IComponentMeta, ISettingField, isSettingField, ISettingTopEntry } from '@alilc/lowcode-designer'; import { createField } from '../field'; import PopupService, { PopupPipe } from '../popup'; import { SkeletonContext } from '../../context'; import { intl } from '../../locale'; -import { Setters } from '@alilc/lowcode-shell'; function isStandardComponent(componentMeta: IComponentMeta | null) { if (!componentMeta) return false; @@ -40,7 +39,7 @@ class SettingFieldView extends Component { @@ -112,7 +111,9 @@ class SettingFieldView extends Component | IPublicTypeDynamicProps = {}; let setterType: any; let initialValue: any = null; @@ -154,23 +155,29 @@ class SettingFieldView extends Component { const { field } = this.props; const { extraProps } = field; const { display } = extraProps; - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); - const { stages } = editor.get('skeleton') as Skeleton; + const editor = this.props.field.designer?.editor; + const { stages } = editor?.get('skeleton') as Skeleton; // const items = field.items; let stageName; @@ -343,15 +349,15 @@ class SettingGroupView extends Component { } } -export function createSettingFieldView(item: ISettingField | IPublicTypeCustomView, field: ISettingEntry, index?: number) { - if (isSettingField(item)) { - if (item.isGroup) { - return ; +export function createSettingFieldView(field: ISettingField | IPublicTypeCustomView, fieldEntry: ISettingEntry, index?: number) { + if (isSettingField(field)) { + if (field.isGroup) { + return ; } else { - return ; + return ; } } else { - return createContent(item, { key: index, field }); + return createContent(field, { key: index, field: fieldEntry }); } } diff --git a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx index 0bbef4bf2..800719588 100644 --- a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx @@ -53,8 +53,7 @@ export class SettingsPrimaryPane extends Component { } // 抛出事件,对于有些需要 panel 插件随着 度变化进行再次渲染的,由panel插件内部监听事件实现 - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = this.props.panel.skeleton.editor; editor?.eventBus.emit('dockpane.drag', width); } onDragChange(type: 'start' | 'end') { - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = this.props.panel.skeleton.editor; editor?.eventBus.emit('dockpane.dragchange', type); // builtinSimulator 屏蔽掉 鼠标事件 editor?.eventBus.emit('designer.builtinSimulator.disabledEvents', type === 'start'); @@ -187,8 +185,7 @@ export class TitledPanelView extends Component<{ panel: Panel; area?: string }> if (!panel.inited) { return null; } - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = panel.skeleton.editor; const panelName = area ? `${area}-${panel.name}` : panel.name; editor?.eventBus.emit('skeleton.panel.toggle', { name: panelName || '', @@ -250,8 +247,7 @@ export class PanelView extends Component<{ if (!panel.inited) { return null; } - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = panel.skeleton.editor; const panelName = area ? `${area}-${panel.name}` : panel.name; editor?.eventBus.emit('skeleton.panel.toggle', { name: panelName || '', diff --git a/packages/editor-skeleton/src/skeleton.ts b/packages/editor-skeleton/src/skeleton.ts index 180859f1d..616e7f017 100644 --- a/packages/editor-skeleton/src/skeleton.ts +++ b/packages/editor-skeleton/src/skeleton.ts @@ -56,7 +56,8 @@ export interface ISkeleton extends Omit { editor: IEditor; @@ -101,6 +102,8 @@ export interface ISkeleton extends Omit): IWidget | Widget | Panel | Stage | Dock | PanelDock | undefined; } export class Skeleton { @@ -440,7 +443,7 @@ export class Skeleton { return restConfig; } - add(config: IPublicTypeSkeletonConfig, extraConfig?: Record) { + add(config: IPublicTypeSkeletonConfig, extraConfig?: Record): IWidget | Widget | Panel | Stage | Dock | PanelDock | undefined { const parsedConfig = { ...this.parseConfig(config), ...extraConfig, diff --git a/packages/editor-skeleton/src/widget/panel.ts b/packages/editor-skeleton/src/widget/panel.ts index feec08da7..b35af6a21 100644 --- a/packages/editor-skeleton/src/widget/panel.ts +++ b/packages/editor-skeleton/src/widget/panel.ts @@ -211,6 +211,10 @@ export class Panel implements IWidget { this.setActive(false); } + disable() {} + + enable(): void {} + show() { this.setActive(true); } diff --git a/packages/engine/README-zh_CN.md b/packages/engine/README-zh_CN.md index b5256af9c..c99e98cf6 100644 --- a/packages/engine/README-zh_CN.md +++ b/packages/engine/README-zh_CN.md @@ -14,8 +14,8 @@ [![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url] -[![codecov][codecov-image-url]][codecov-url] - +[![codecov][codecov-image-url]][codecov-url] [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/lowcode-workspace/awesome-lowcode-engine) + [![](https://img.shields.io/badge/LowCodeEngine-%E6%9F%A5%E7%9C%8B%E8%B4%A1%E7%8C%AE%E6%8E%92%E8%A1%8C%E6%A6%9C-orange)](https://opensource.alibaba.com/contribution_leaderboard/details?projectValue=lowcode-engine) [npm-image]: https://img.shields.io/npm/v/@alilc/lowcode-engine.svg?style=flat-square diff --git a/packages/engine/README.md b/packages/engine/README.md index 2d1254e4a..52bce8616 100644 --- a/packages/engine/README.md +++ b/packages/engine/README.md @@ -14,7 +14,7 @@ An enterprise-class low-code technology stack with scale-out design [![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url] -[![codecov][codecov-image-url]][codecov-url] +[![codecov][codecov-image-url]][codecov-url] [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/lowcode-workspace/awesome-lowcode-engine) [![](https://img.shields.io/badge/LowCodeEngine-Check%20Your%20Contribution-orange)](https://opensource.alibaba.com/contribution_leaderboard/details?projectValue=lowcode-engine) diff --git a/packages/engine/build.json b/packages/engine/build.json index e8c3e583d..405f2c005 100644 --- a/packages/engine/build.json +++ b/packages/engine/build.json @@ -1,6 +1,6 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", [ "build-plugin-fusion", { diff --git a/packages/engine/build.test.json b/packages/engine/build.test.json index a43d8eff9..9596d43e7 100644 --- a/packages/engine/build.test.json +++ b/packages/engine/build.test.json @@ -1,7 +1,7 @@ { "plugins": [ [ - "build-plugin-component", + "@alilc/build-plugin-lce", { "filename": "editor-preset-vision", "library": "LowcodeEditor", diff --git a/packages/engine/package.json b/packages/engine/package.json index 99b185db0..4a2d7f07a 100644 --- a/packages/engine/package.json +++ b/packages/engine/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-engine", - "version": "1.1.6", + "version": "1.1.7", "description": "An enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系", "main": "lib/engine-core.js", "module": "es/engine-core.js", @@ -12,22 +12,22 @@ "scripts": { "start": "build-scripts start", "version:update": "node ./scripts/version.js", - "build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --skip-demo", + "build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build", "build:umd": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --config build.umd.json", "test": "build-scripts test --config build.test.json --jest-passWithNoTests" }, "license": "MIT", "dependencies": { "@alifd/next": "^1.19.12", - "@alilc/lowcode-designer": "1.1.6", - "@alilc/lowcode-editor-core": "1.1.6", - "@alilc/lowcode-editor-skeleton": "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-engine-ext": "^1.0.0", - "@alilc/lowcode-plugin-designer": "1.1.6", - "@alilc/lowcode-plugin-outline-pane": "1.1.6", - "@alilc/lowcode-shell": "1.1.6", - "@alilc/lowcode-utils": "1.1.6", - "@alilc/lowcode-workspace": "1.1.6", + "@alilc/lowcode-plugin-designer": "1.1.7", + "@alilc/lowcode-plugin-outline-pane": "1.1.7", + "@alilc/lowcode-shell": "1.1.7", + "@alilc/lowcode-utils": "1.1.7", + "@alilc/lowcode-workspace": "1.1.7", "react": "^16.8.1", "react-dom": "^16.8.1" }, diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index b038df818..84dfd67aa 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -9,11 +9,16 @@ import { engineConfig, Setters as InnerSetters, Hotkey as InnerHotkey, + IEditor, } from '@alilc/lowcode-editor-core'; import { IPublicTypeEngineOptions, IPublicModelDocumentModel, IPublicTypePluginMeta, + IPublicTypeDisposable, + IPublicApiPlugins, + IPublicApiWorkspace, + IPublicEnumPluginRegisterLevel, } from '@alilc/lowcode-types'; import { Designer, @@ -21,6 +26,7 @@ import { ILowCodePluginContextPrivate, ILowCodePluginContextApiAssembler, PluginPreference, + IDesigner, } from '@alilc/lowcode-designer'; import { Skeleton as InnerSkeleton, @@ -29,6 +35,7 @@ import { import { Workspace as InnerWorkspace, Workbench as WorkSpaceWorkbench, + IWorkspace, } from '@alilc/lowcode-workspace'; import { @@ -60,18 +67,30 @@ export * from './modules/skeleton-types'; export * from './modules/designer-types'; export * from './modules/lowcode-types'; -async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: Plugins) { +async function registryInnerPlugin(designer: IDesigner, editor: IEditor, plugins: IPublicApiPlugins): Promise { // 注册一批内置插件 + const componentMetaParserPlugin = componentMetaParser(designer); + const defaultPanelRegistryPlugin = defaultPanelRegistry(editor); await plugins.register(OutlinePlugin, {}, { autoInit: true }); - await plugins.register(componentMetaParser(designer)); + await plugins.register(componentMetaParserPlugin); await plugins.register(setterRegistry, {}); - await plugins.register(defaultPanelRegistry(editor)); + await plugins.register(defaultPanelRegistryPlugin); await plugins.register(builtinHotkey); await plugins.register(registerDefaults, {}, { autoInit: true }); + + return () => { + plugins.delete(OutlinePlugin.pluginName); + plugins.delete(componentMetaParserPlugin.pluginName); + plugins.delete(setterRegistry.pluginName); + plugins.delete(defaultPanelRegistryPlugin.pluginName); + plugins.delete(defaultPanelRegistryPlugin.pluginName); + plugins.delete(builtinHotkey.pluginName); + plugins.delete(registerDefaults.pluginName); + }; } -const innerWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory); -const workspace = new Workspace(innerWorkspace); +const innerWorkspace: IWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory); +const workspace: IPublicApiWorkspace = new Workspace(innerWorkspace); const editor = new Editor(); globalContext.register(editor, Editor); globalContext.register(editor, 'editor'); @@ -120,6 +139,7 @@ const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = { context.plugins = plugins; context.logger = new Logger({ level: 'warn', bizName: `plugin:${pluginName}` }); context.workspace = workspace; + context.registerLevel = IPublicEnumPluginRegisterLevel.Default; }, }; @@ -139,8 +159,6 @@ export { logger, hotkey, common, - // 兼容原 editor 的事件功能 - event as editor, workspace, canvas, }; @@ -158,7 +176,7 @@ let engineContainer: HTMLElement; export const version = VERSION_PLACEHOLDER; engineConfig.set('ENGINE_VERSION', version); -registryInnerPlugin(designer, editor, plugins); +const pluginPromise = registryInnerPlugin(designer, editor, plugins); export async function init( container?: HTMLElement, @@ -183,10 +201,10 @@ export async function init( } engineConfig.setEngineOptions(engineOptions as any); - await plugins.init(pluginPreference as any); - const { Workbench } = common.skeletonCabin; if (options && options.enableWorkspaceMode) { + const disposeFun = await pluginPromise; + disposeFun && disposeFun(); render( createElement(WorkSpaceWorkbench, { workspace: innerWorkspace, @@ -196,12 +214,16 @@ export async function init( }), engineContainer, ); + innerWorkspace.enableAutoOpenFirstWindow = engineConfig.get('enableAutoOpenFirstWindow', true); innerWorkspace.setActive(true); + innerWorkspace.initWindow(); innerHotkey.activate(false); await innerWorkspace.plugins.init(pluginPreference); return; } + await plugins.init(pluginPreference as any); + render( createElement(Workbench, { skeleton: innerSkeleton, diff --git a/packages/engine/src/modules/classes.ts b/packages/engine/src/modules/classes.ts index 44aeed965..d68ea43de 100644 --- a/packages/engine/src/modules/classes.ts +++ b/packages/engine/src/modules/classes.ts @@ -10,6 +10,7 @@ import { Selection, Prop, SimulatorHost, + SkeletonItem, } from '@alilc/lowcode-shell'; import { Node as InnerNode } from '@alilc/lowcode-designer'; @@ -26,4 +27,5 @@ export default { Selection, Prop, SimulatorHost, + SkeletonItem, }; diff --git a/packages/engine/src/modules/symbols.ts b/packages/engine/src/modules/symbols.ts index 7faccff51..a0371abf9 100644 --- a/packages/engine/src/modules/symbols.ts +++ b/packages/engine/src/modules/symbols.ts @@ -11,6 +11,7 @@ import { designerCabinSymbol, propSymbol, simulatorHostSymbol, + skeletonItemSymbol, } from '@alilc/lowcode-shell'; export default { @@ -26,4 +27,5 @@ export default { designerCabinSymbol, propSymbol, simulatorHostSymbol, + skeletonItemSymbol, }; diff --git a/packages/ignitor/package.json b/packages/ignitor/package.json index 7fa6ab073..2448ce0fc 100644 --- a/packages/ignitor/package.json +++ b/packages/ignitor/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-ignitor", - "version": "1.1.6", + "version": "1.1.7", "description": "点火器,bootstrap lce project", "main": "lib/index.js", "private": true, diff --git a/packages/plugin-designer/build.json b/packages/plugin-designer/build.json index bd5cf18dd..3e9260055 100644 --- a/packages/plugin-designer/build.json +++ b/packages/plugin-designer/build.json @@ -1,5 +1,5 @@ { "plugins": [ - "build-plugin-component" + "@alilc/build-plugin-lce" ] } diff --git a/packages/plugin-designer/package.json b/packages/plugin-designer/package.json index 0f64b70f1..302d22491 100644 --- a/packages/plugin-designer/package.json +++ b/packages/plugin-designer/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-plugin-designer", - "version": "1.1.6", + "version": "1.1.7", "description": "alibaba lowcode editor designer plugin", "files": [ "es", @@ -10,7 +10,7 @@ "module": "es/index.js", "stylePath": "style.js", "scripts": { - "build": "build-scripts build --skip-demo" + "build": "build-scripts build" }, "keywords": [ "lowcode", @@ -18,9 +18,9 @@ ], "author": "xiayang.xy", "dependencies": { - "@alilc/lowcode-designer": "1.1.6", - "@alilc/lowcode-editor-core": "1.1.6", - "@alilc/lowcode-utils": "1.1.6", + "@alilc/lowcode-designer": "1.1.7", + "@alilc/lowcode-editor-core": "1.1.7", + "@alilc/lowcode-utils": "1.1.7", "react": "^16.8.1", "react-dom": "^16.8.1" }, diff --git a/packages/plugin-designer/src/index.tsx b/packages/plugin-designer/src/index.tsx index d40609c48..54eda1365 100644 --- a/packages/plugin-designer/src/index.tsx +++ b/packages/plugin-designer/src/index.tsx @@ -62,6 +62,11 @@ export default class DesignerPlugin extends PureComponent { + this.setState({ + locale, + }); + }); const { components, packages, extraEnvironment, utils } = assets; const state = { componentMetadatas: components || [], diff --git a/packages/plugin-outline-pane/build.json b/packages/plugin-outline-pane/build.json index e791d5b6b..d0aec1038 100644 --- a/packages/plugin-outline-pane/build.json +++ b/packages/plugin-outline-pane/build.json @@ -1,6 +1,6 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", "build-plugin-fusion", ["build-plugin-moment-locales", { "locales": ["zh-cn"] diff --git a/packages/plugin-outline-pane/package.json b/packages/plugin-outline-pane/package.json index ec4738cd7..b19196920 100644 --- a/packages/plugin-outline-pane/package.json +++ b/packages/plugin-outline-pane/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-plugin-outline-pane", - "version": "1.1.6", + "version": "1.1.7", "description": "Outline pane for Ali lowCode engine", "files": [ "es", @@ -9,14 +9,12 @@ "main": "lib/index.js", "module": "es/index.js", "scripts": { - "build": "build-scripts build --skip-demo" + "build": "build-scripts build" }, "dependencies": { "@alifd/next": "^1.19.16", - "@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-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/plugin-outline-pane/src/controllers/pane-controller.ts b/packages/plugin-outline-pane/src/controllers/pane-controller.ts index a02844ad1..e7c41892c 100644 --- a/packages/plugin-outline-pane/src/controllers/pane-controller.ts +++ b/packages/plugin-outline-pane/src/controllers/pane-controller.ts @@ -16,16 +16,15 @@ import { IPublicModelDropLocation, IPublicModelScroller, IPublicModelScrollTarget, - IPublicModelPluginContext, IPublicModelLocateEvent, } from '@alilc/lowcode-types'; import TreeNode from './tree-node'; import { IndentTrack } from '../helper/indent-track'; import DwellTimer from '../helper/dwell-timer'; -import { ITreeBoard, TreeMaster } from './tree-master'; +import { IOutlinePanelPluginContext, ITreeBoard, TreeMaster } from './tree-master'; export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicTypeScrollable { - private pluginContext: IPublicModelPluginContext; + private pluginContext: IOutlinePanelPluginContext; private treeMaster?: TreeMaster; @@ -100,8 +99,8 @@ export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicTy private _shell: HTMLDivElement | null = null; - constructor(at: string | symbol, pluginContext: IPublicModelPluginContext, treeMaster: TreeMaster) { - this.pluginContext = pluginContext; + constructor(at: string | symbol, treeMaster: TreeMaster) { + this.pluginContext = treeMaster.pluginContext; this.treeMaster = treeMaster; this.at = at; let inited = false; @@ -237,7 +236,7 @@ export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicTy let { node } = treeNode; if (isDragNodeObject(dragObject)) { const newNodes = operationalNodes; - let i = newNodes.length; + let i = newNodes?.length; let p: any = node; while (i-- > 0) { if (newNodes[i].contains(p)) { @@ -482,7 +481,7 @@ export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicTy const isSlotContainer = treeNode.hasSlots(); const isContainer = treeNode.isContainer(); - if (container.isSlot && !treeNode.expanded) { + if (container.isSlotNode && !treeNode.expanded) { // 未展开,直接定位到内部第一个节点 if (isSlotContainer) { detail.index = null; diff --git a/packages/plugin-outline-pane/src/controllers/tree-master.ts b/packages/plugin-outline-pane/src/controllers/tree-master.ts index 074ed3447..40162d808 100644 --- a/packages/plugin-outline-pane/src/controllers/tree-master.ts +++ b/packages/plugin-outline-pane/src/controllers/tree-master.ts @@ -1,30 +1,96 @@ import { isLocationChildrenDetail } from '@alilc/lowcode-utils'; -import { IPublicModelPluginContext, IPublicTypeActiveTarget, IPublicModelNode } from '@alilc/lowcode-types'; +import { IPublicModelPluginContext, IPublicTypeActiveTarget, IPublicModelNode, IPublicTypeDisposable, IPublicEnumPluginRegisterLevel } from '@alilc/lowcode-types'; import TreeNode from './tree-node'; import { Tree } from './tree'; +import EventEmitter from 'events'; +import { enUS, zhCN } from '../locale'; +import { ReactNode } from 'react'; export interface ITreeBoard { readonly at: string | symbol; scrollToNode(treeNode: TreeNode, detail?: any): void; } +enum EVENT_NAMES { + pluginContextChanged = 'pluginContextChanged', +} + +export interface IOutlinePanelPluginContext extends IPublicModelPluginContext { + extraTitle?: string; + intlNode(id: string, params?: object): ReactNode; + intl(id: string, params?: object): string; + getLocale(): string; +} + export class TreeMaster { - readonly pluginContext: IPublicModelPluginContext; + pluginContext: IOutlinePanelPluginContext; private boards = new Set(); private treeMap = new Map(); - constructor(pluginContext: IPublicModelPluginContext) { - this.pluginContext = pluginContext; + private disposeEvents: (IPublicTypeDisposable | undefined)[] = []; + + event = new EventEmitter(); + + constructor(pluginContext: IPublicModelPluginContext, readonly options: { + extraTitle?: string; + }) { + this.setPluginContext(pluginContext); + const { workspace } = this.pluginContext; + this.initEvent(); + if (pluginContext.registerLevel === IPublicEnumPluginRegisterLevel.Workspace) { + this.setPluginContext(workspace.window?.currentEditorView); + workspace.onWindowRendererReady(() => { + this.setPluginContext(workspace.window?.currentEditorView); + let dispose: IPublicTypeDisposable | undefined; + const windowViewTypeChangeEvent = () => { + dispose = workspace.window?.onChangeViewType(() => { + this.setPluginContext(workspace.window?.currentEditorView); + }); + }; + + windowViewTypeChangeEvent(); + + workspace.onChangeActiveWindow(() => { + windowViewTypeChangeEvent(); + this.setPluginContext(workspace.window?.currentEditorView); + dispose && dispose(); + }); + }); + } + } + + private setPluginContext(pluginContext: IPublicModelPluginContext | undefined | null) { + if (!pluginContext) { + return; + } + const { intl, intlNode, getLocale } = pluginContext.common.utils.createIntl({ + 'en-US': enUS, + 'zh-CN': zhCN, + }); + let _pluginContext: IOutlinePanelPluginContext = Object.assign(pluginContext, { + intl, + intlNode, + getLocale, + }); + _pluginContext.extraTitle = this.options && this.options['extraTitle']; + this.pluginContext = _pluginContext; + this.disposeEvent(); + this.initEvent(); + this.emitPluginContextChange(); + } + + private disposeEvent() { + this.disposeEvents.forEach(d => { + d && d(); + }); + } + + private initEvent() { let startTime: any; const { event, project, canvas } = this.pluginContext; - canvas.dragon?.onDragstart(() => { - startTime = Date.now() / 1000; - // needs? - this.toVision(); - }); - canvas.activeTracker?.onChange((target: IPublicTypeActiveTarget) => { + const setExpandByActiveTracker = (target: IPublicTypeActiveTarget) => { const { node, detail } = target; const tree = this.currentTree; if (!tree/* || node.document !== tree.document */) { @@ -39,29 +105,40 @@ export class TreeMaster { this.boards.forEach((board) => { board.scrollToNode(treeNode, detail); }); - }); - canvas.dragon?.onDragend(() => { - const endTime: any = Date.now() / 1000; - const nodes = project.currentDocument?.selection?.getNodes(); - event.emit('outlinePane.dragend', { - selected: nodes - ?.map((n) => { - if (!n) { - return; - } - const npm = n?.componentMeta?.npm; - return ( - [npm?.package, npm?.componentName].filter((item) => !!item).join('-') || n?.componentMeta?.componentName - ); - }) - .join('&'), - time: (endTime - startTime).toFixed(2), - }); - }); - project.onRemoveDocument((data: {id: string}) => { - const { id } = data; - this.treeMap.delete(id); - }); + }; + this.disposeEvents = [ + canvas.dragon?.onDragstart(() => { + startTime = Date.now() / 1000; + // needs? + this.toVision(); + }), + canvas.activeTracker?.onChange(setExpandByActiveTracker), + canvas.dragon?.onDragend(() => { + const endTime: any = Date.now() / 1000; + const nodes = project.currentDocument?.selection?.getNodes(); + event.emit('outlinePane.dragend', { + selected: nodes + ?.map((n) => { + if (!n) { + return; + } + const npm = n?.componentMeta?.npm; + return ( + [npm?.package, npm?.componentName].filter((item) => !!item).join('-') || n?.componentMeta?.componentName + ); + }) + .join('&'), + time: (endTime - startTime).toFixed(2), + }); + }), + project.onRemoveDocument((data: {id: string}) => { + const { id } = data; + this.treeMap.delete(id); + }), + ]; + if (canvas.activeTracker?.target) { + setExpandByActiveTracker(canvas.activeTracker?.target); + } } private toVision() { @@ -86,6 +163,14 @@ export class TreeMaster { // todo others purge } + onPluginContextChange(fn: () => void) { + this.event.on(EVENT_NAMES.pluginContextChanged, fn); + } + + emitPluginContextChange() { + this.event.emit(EVENT_NAMES.pluginContextChanged); + } + get currentTree(): Tree | null { const doc = this.pluginContext.project.getCurrentDocument(); if (doc) { @@ -93,7 +178,7 @@ export class TreeMaster { if (this.treeMap.has(id)) { return this.treeMap.get(id)!; } - const tree = new Tree(this.pluginContext); + const tree = new Tree(this); this.treeMap.set(id, tree); return tree; } diff --git a/packages/plugin-outline-pane/src/controllers/tree-node.ts b/packages/plugin-outline-pane/src/controllers/tree-node.ts index 62f373e2a..079ed77d6 100644 --- a/packages/plugin-outline-pane/src/controllers/tree-node.ts +++ b/packages/plugin-outline-pane/src/controllers/tree-node.ts @@ -2,12 +2,12 @@ import { IPublicTypeTitleContent, IPublicTypeLocationChildrenDetail, IPublicModelNode, - IPublicModelPluginContext, IPublicTypeDisposable, } from '@alilc/lowcode-types'; import { isI18nData, isLocationChildrenDetail } from '@alilc/lowcode-utils'; import EventEmitter from 'events'; import { Tree } from './tree'; +import { IOutlinePanelPluginContext } from './tree-master'; /** * 大纲树过滤结果 @@ -35,10 +35,12 @@ enum EVENT_NAMES { titleLabelChanged = 'titleLabelChanged', expandableChanged = 'expandableChanged', + + conditionChanged = 'conditionChanged', } export default class TreeNode { - readonly pluginContext: IPublicModelPluginContext; + readonly pluginContext: IOutlinePanelPluginContext; event = new EventEmitter(); private _node: IPublicModelNode; @@ -152,6 +154,10 @@ export default class TreeNode { return this.node.slots.map((node) => this.tree.getTreeNode(node)); } + get condition(): boolean { + return this.node.hasCondition() && !this.node.conditionGroup; + } + get children(): TreeNode[] | null { return this.node.children?.map((node) => this.tree.getTreeNode(node)) || null; } @@ -160,9 +166,9 @@ export default class TreeNode { return this._node; } - constructor(tree: Tree, node: IPublicModelNode, pluginContext: IPublicModelPluginContext) { + constructor(tree: Tree, node: IPublicModelNode) { this.tree = tree; - this.pluginContext = pluginContext; + this.pluginContext = tree.pluginContext; this._node = node; } @@ -203,6 +209,15 @@ export default class TreeNode { this.event.off(EVENT_NAMES.titleLabelChanged, fn); }; } + + onConditionChanged(fn: (treeNode: TreeNode) => void): IPublicTypeDisposable { + this.event.on(EVENT_NAMES.conditionChanged, fn); + + return () => { + this.event.off(EVENT_NAMES.conditionChanged, fn); + }; + } + onExpandableChanged(fn: (expandable: boolean) => void): IPublicTypeDisposable { this.event.on(EVENT_NAMES.expandableChanged, fn); return () => { @@ -221,11 +236,17 @@ export default class TreeNode { this.event.emit(EVENT_NAMES.titleLabelChanged, this.title); } + notifyConditionChanged(): void { + this.event.emit(EVENT_NAMES.conditionChanged, this.condition); + } + setHidden(flag: boolean) { if (this.node.conditionGroup) { return; } - this.node.visible = !flag; + if (this.node.visible !== !flag) { + this.node.visible = !flag; + } this.event.emit(EVENT_NAMES.hiddenChanged, flag); } diff --git a/packages/plugin-outline-pane/src/controllers/tree.ts b/packages/plugin-outline-pane/src/controllers/tree.ts index c0098794c..ab0d64b0f 100644 --- a/packages/plugin-outline-pane/src/controllers/tree.ts +++ b/packages/plugin-outline-pane/src/controllers/tree.ts @@ -1,12 +1,13 @@ import TreeNode from './tree-node'; -import { IPublicModelNode, IPublicModelPluginContext, IPublicTypePropChangeOptions } from '@alilc/lowcode-types'; +import { IPublicModelNode, IPublicTypePropChangeOptions } from '@alilc/lowcode-types'; +import { IOutlinePanelPluginContext, TreeMaster } from './tree-master'; export class Tree { private treeNodesMap = new Map(); readonly id: string | undefined; - readonly pluginContext: IPublicModelPluginContext; + readonly pluginContext: IOutlinePanelPluginContext; get root(): TreeNode | null { if (this.pluginContext.project.currentDocument?.focusNode) { @@ -15,8 +16,11 @@ export class Tree { return null; } - constructor(pluginContext: IPublicModelPluginContext) { - this.pluginContext = pluginContext; + readonly treeMaster: TreeMaster; + + constructor(treeMaster: TreeMaster) { + this.treeMaster = treeMaster; + this.pluginContext = treeMaster.pluginContext; const doc = this.pluginContext.project.currentDocument; this.id = doc?.id; @@ -31,8 +35,16 @@ export class Tree { if (key === '___title___') { const treeNode = this.getTreeNodeById(node.id); treeNode?.notifyTitleLabelChanged(); + } else if (key === '___condition___') { + const treeNode = this.getTreeNodeById(node.id); + treeNode?.notifyConditionChanged(); } }); + + doc?.onChangeNodeVisible((node: IPublicModelNode, visible: boolean) => { + const treeNode = this.getTreeNodeById(node.id); + treeNode?.setHidden(!visible); + }); } setNodeSelected(nodeId: string): void { @@ -51,7 +63,7 @@ export class Tree { return tnode; } - const treeNode = new TreeNode(this, node, this.pluginContext); + const treeNode = new TreeNode(this, node); this.treeNodesMap.set(node.id, treeNode); return treeNode; } diff --git a/packages/plugin-outline-pane/src/index.tsx b/packages/plugin-outline-pane/src/index.tsx index 701e46540..180b424ea 100644 --- a/packages/plugin-outline-pane/src/index.tsx +++ b/packages/plugin-outline-pane/src/index.tsx @@ -1,21 +1,43 @@ import { Pane } from './views/pane'; import { IconOutline } from './icons/outline'; import { IPublicModelPluginContext, IPublicModelDocumentModel } from '@alilc/lowcode-types'; -import { enUS, zhCN } from './locale'; import { MasterPaneName, BackupPaneName } from './helper/consts'; import { TreeMaster } from './controllers/tree-master'; import { PaneController } from './controllers/pane-controller'; +import { useState, useEffect } from 'react'; + +export function OutlinePaneContext(props: { + treeMaster?: TreeMaster; + + pluginContext: IPublicModelPluginContext; + + options: any; + + paneName: string; + + hideFilter?: boolean; +}) { + const treeMaster = props.treeMaster || new TreeMaster(props.pluginContext, props.options); + const [masterPaneController, setMasterPaneController] = useState(new PaneController(props.paneName || MasterPaneName, treeMaster)); + useEffect(() => { + return treeMaster.onPluginContextChange(() => { + setMasterPaneController(new PaneController(props.paneName || MasterPaneName, treeMaster)); + }); + }, []); + + return ( + + ); +} export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { - const { skeleton, config, common, event, canvas, project } = ctx; - const { intl, intlNode, getLocale } = common.utils.createIntl({ - 'en-US': enUS, - 'zh-CN': zhCN, - }); - ctx.intl = intl; - ctx.intlNode = intlNode; - ctx.getLocale = getLocale; - ctx.extraTitle = options && options['extraTitle']; + const { skeleton, config, canvas, project } = ctx; let isInFloatArea = true; const hasPreferenceForOutline = config.getPreference().contains('outline-pane-pinned-status-isFloat', 'skeleton'); @@ -26,9 +48,7 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { masterPane: false, backupPane: false, }; - const treeMaster = new TreeMaster(ctx); - let masterPaneController: PaneController | null = null; - let backupPaneController: PaneController | null = null; + const treeMaster = new TreeMaster(ctx, options); return { async init() { skeleton.add({ @@ -40,20 +60,9 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { name: MasterPaneName, props: { icon: IconOutline, - description: intlNode('Outline Tree'), - }, - content: (props: any) => { - masterPaneController = new PaneController(MasterPaneName, ctx, treeMaster); - return ( - - ); + description: treeMaster.pluginContext.intlNode('Outline Tree'), }, + content: OutlinePaneContext, }, panelProps: { area: isInFloatArea ? 'leftFloatArea' : 'leftFixedArea', @@ -62,6 +71,8 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { }, contentProps: { treeTitleExtra: config.get('treeTitleExtra'), + treeMaster, + paneName: MasterPaneName, }, }); @@ -72,16 +83,10 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { props: { hiddenWhenInit: true, }, - content: (props: any) => { - backupPaneController = new PaneController(BackupPaneName, ctx, treeMaster); - return ( - - ); + content: OutlinePaneContext, + contentProps: { + paneName: BackupPaneName, + treeMaster, }, }); diff --git a/packages/plugin-outline-pane/src/views/filter.tsx b/packages/plugin-outline-pane/src/views/filter.tsx index 166a5c8e2..8fd18ed25 100644 --- a/packages/plugin-outline-pane/src/views/filter.tsx +++ b/packages/plugin-outline-pane/src/views/filter.tsx @@ -5,11 +5,9 @@ import { Search, Checkbox, Balloon, Divider } from '@alifd/next'; import TreeNode from '../controllers/tree-node'; import { Tree } from '../controllers/tree'; import { matchTreeNode, FILTER_OPTIONS } from './filter-tree'; -import { IPublicModelPluginContext } from '@alilc/lowcode-types'; export default class Filter extends PureComponent<{ tree: Tree; - pluginContext: IPublicModelPluginContext; }, { keywords: string; filterOps: string[]; @@ -56,7 +54,7 @@ export default class Filter extends PureComponent<{ - {this.props.pluginContext.intlNode('Check All')} + {this.props.tree.pluginContext.intlNode('Check All')} - {this.props.pluginContext.intlNode(op.label)} + {this.props.tree.pluginContext.intlNode(op.label)} ))} diff --git a/packages/plugin-outline-pane/src/views/pane.tsx b/packages/plugin-outline-pane/src/views/pane.tsx index adaada786..4b807ca18 100644 --- a/packages/plugin-outline-pane/src/views/pane.tsx +++ b/packages/plugin-outline-pane/src/views/pane.tsx @@ -1,47 +1,75 @@ import React, { PureComponent } from 'react'; +import { Loading } from '@alifd/next'; import { PaneController } from '../controllers/pane-controller'; import TreeView from './tree'; import './style.less'; -import { IPublicModelPluginContext } from '@alilc/lowcode-types'; import Filter from './filter'; import { TreeMaster } from '../controllers/tree-master'; +import { Tree } from '../controllers/tree'; +import { IPublicTypeDisposable } from '@alilc/lowcode-types'; export class Pane extends PureComponent<{ - config: any; - pluginContext: IPublicModelPluginContext; treeMaster: TreeMaster; controller: PaneController; + hideFilter?: boolean; +}, { + tree: Tree | null; }> { private controller; - private treeMaster: TreeMaster; + + private simulatorRendererReadyDispose: IPublicTypeDisposable; + private changeDocumentDispose: IPublicTypeDisposable; + private removeDocumentDispose: IPublicTypeDisposable; constructor(props: any) { super(props); const { controller, treeMaster } = props; - this.treeMaster = treeMaster; this.controller = controller; + this.state = { + tree: treeMaster.currentTree, + }; + this.simulatorRendererReadyDispose = this.props.treeMaster.pluginContext?.project?.onSimulatorRendererReady(this.changeTree); + this.changeDocumentDispose = this.props.treeMaster.pluginContext?.project?.onChangeDocument(this.changeTree); + this.removeDocumentDispose = this.props.treeMaster.pluginContext?.project?.onRemoveDocument(this.changeTree); } + changeTree = () => { + this.setState({ + tree: this.props.treeMaster.currentTree, + }); + }; + componentWillUnmount() { this.controller.purge(); + this.simulatorRendererReadyDispose?.(); + this.changeDocumentDispose?.(); + this.removeDocumentDispose?.(); } render() { - const tree = this.treeMaster.currentTree; + const tree = this.state.tree; if (!tree) { return (
-

{this.props.pluginContext.intl('Initializing')}

+

+ +

); } return (
- -
this.controller.mount(shell)} className="lc-outline-tree-container"> - + { !this.props.hideFilter && } +
this.controller.mount(shell)} className={`lc-outline-tree-container ${ this.props.hideFilter ? 'lc-hidden-outline-filter' : '' }`}> +
); diff --git a/packages/plugin-outline-pane/src/views/style.less b/packages/plugin-outline-pane/src/views/style.less index cd133932b..f343fba17 100644 --- a/packages/plugin-outline-pane/src/views/style.less +++ b/packages/plugin-outline-pane/src/views/style.less @@ -14,10 +14,14 @@ overflow: auto; } + > .lc-outline-tree-container.lc-hidden-outline-filter { + top: 0; + } + > .lc-outline-filter { padding: 12px 16px; display: flex; - align-items: center; + align-items: stretch; justify-content: right; .lc-outline-filter-search-input { @@ -27,7 +31,6 @@ .lc-outline-filter-icon { background: #ebecf0; border: 1px solid #c4c6cf; - height: 28px; display: flex; align-items: center; border-radius: 0 2px 2px 0; diff --git a/packages/plugin-outline-pane/src/views/tree-branches.tsx b/packages/plugin-outline-pane/src/views/tree-branches.tsx index 210fe0af8..2e281071b 100644 --- a/packages/plugin-outline-pane/src/views/tree-branches.tsx +++ b/packages/plugin-outline-pane/src/views/tree-branches.tsx @@ -2,12 +2,11 @@ import { PureComponent } from 'react'; import classNames from 'classnames'; import TreeNode from '../controllers/tree-node'; import TreeNodeView from './tree-node'; -import { IPublicModelPluginContext, IPublicModelExclusiveGroup, IPublicTypeDisposable, IPublicTypeLocationChildrenDetail } from '@alilc/lowcode-types'; +import { IPublicModelExclusiveGroup, IPublicTypeDisposable, IPublicTypeLocationChildrenDetail } from '@alilc/lowcode-types'; export default class TreeBranches extends PureComponent<{ treeNode: TreeNode; isModal?: boolean; - pluginContext: IPublicModelPluginContext; expanded: boolean; treeChildren: TreeNode[] | null; }> { @@ -51,12 +50,11 @@ export default class TreeBranches extends PureComponent<{ return (
{ - !isModal && + !isModal && }
@@ -73,7 +71,6 @@ interface ITreeNodeChildrenState { class TreeNodeChildren extends PureComponent<{ treeNode: TreeNode; isModal?: boolean; - pluginContext: IPublicModelPluginContext; treeChildren: TreeNode[] | null; }, ITreeNodeChildrenState> { state: ITreeNodeChildrenState = { @@ -84,8 +81,8 @@ class TreeNodeChildren extends PureComponent<{ }; offLocationChanged: IPublicTypeDisposable | undefined; componentDidMount() { - const { treeNode, pluginContext } = this.props; - const { project } = pluginContext; + const { treeNode } = this.props; + const { project } = treeNode.pluginContext; const { filterWorking, matchSelf, keywords } = treeNode.filterReult; const { dropDetail } = treeNode; this.setState({ @@ -122,13 +119,14 @@ class TreeNodeChildren extends PureComponent<{ let groupContents: any[] = []; let currentGrp: IPublicModelExclusiveGroup; const { filterWorking, matchSelf, keywords } = this.state; - const Title = this.props.pluginContext.common.editorCabin.Title; + const Title = this.props.treeNode.pluginContext.common.editorCabin.Title; const endGroup = () => { if (groupContents.length > 0) { children.push(
+ {/* @ts-ignore */} ); + groupContents.push(<TreeNodeView key={child.id} treeNode={child} isModal={isModal} />); } else { if (index === dropIndex) { children.push(insertion); } - children.push(<TreeNodeView key={child.id} treeNode={child} isModal={isModal} pluginContext={this.props.pluginContext} />); + children.push(<TreeNodeView key={child.id} treeNode={child} isModal={isModal} />); } }); endGroup(); @@ -191,14 +189,13 @@ class TreeNodeChildren extends PureComponent<{ class TreeNodeSlots extends PureComponent<{ treeNode: TreeNode; - pluginContext: IPublicModelPluginContext; }> { render() { const { treeNode } = this.props; if (!treeNode.hasSlots()) { return null; } - const Title = this.props.pluginContext.common.editorCabin.Title; + const Title = this.props.treeNode.pluginContext.common.editorCabin.Title; return ( <div className={classNames('tree-node-slots', { @@ -207,10 +204,11 @@ class TreeNodeSlots extends PureComponent<{ data-id={treeNode.id} > <div className="tree-node-slots-title"> - <Title title={{ type: 'i18n', intl: this.props.pluginContext.intlNode('Slots') }} /> + {/* @ts-ignore */} + <Title title={{ type: 'i18n', intl: this.props.treeNode.pluginContext.intlNode('Slots') }} /> </div> {treeNode.slots.map(tnode => ( - <TreeNodeView key={tnode.id} treeNode={tnode} pluginContext={this.props.pluginContext} /> + <TreeNodeView key={tnode.id} treeNode={tnode} /> ))} </div> ); diff --git a/packages/plugin-outline-pane/src/views/tree-node.tsx b/packages/plugin-outline-pane/src/views/tree-node.tsx index 712dc20fa..882fe15b1 100644 --- a/packages/plugin-outline-pane/src/views/tree-node.tsx +++ b/packages/plugin-outline-pane/src/views/tree-node.tsx @@ -4,34 +4,56 @@ import TreeNode from '../controllers/tree-node'; import TreeTitle from './tree-title'; import TreeBranches from './tree-branches'; import { IconEyeClose } from '../icons/eye-close'; -import { IPublicModelPluginContext, IPublicModelModalNodesManager, IPublicTypeDisposable } from '@alilc/lowcode-types'; +import { IPublicModelModalNodesManager, IPublicTypeDisposable } from '@alilc/lowcode-types'; +import { IOutlinePanelPluginContext } from '../controllers/tree-master'; class ModalTreeNodeView extends PureComponent<{ treeNode: TreeNode; - pluginContext: IPublicModelPluginContext; +}, { + treeChildren: TreeNode[] | null; }> { private modalNodesManager: IPublicModelModalNodesManager | undefined | null; - readonly pluginContext: IPublicModelPluginContext; + readonly pluginContext: IOutlinePanelPluginContext; - constructor(props: any) { + constructor(props: { + treeNode: TreeNode; + }) { super(props); // 模态管理对象 - this.pluginContext = props.pluginContext; + this.pluginContext = props.treeNode.pluginContext; const { project } = this.pluginContext; this.modalNodesManager = project.currentDocument?.modalNodesManager; + this.state = { + treeChildren: this.rootTreeNode.children, + }; } hideAllNodes() { this.modalNodesManager?.hideModalNodes(); } - render() { + componentDidMount(): void { + const rootTreeNode = this.rootTreeNode; + rootTreeNode.onExpandableChanged(() => { + this.setState({ + treeChildren: rootTreeNode.children, + }); + }); + } + + get rootTreeNode() { const { treeNode } = this.props; // 当指定了新的根节点时,要从原始的根节点去获取模态节点 const { project } = this.pluginContext; const rootNode = project.currentDocument?.root; const rootTreeNode = treeNode.tree.getTreeNode(rootNode!); + + return rootTreeNode; + } + + render() { + const rootTreeNode = this.rootTreeNode; const { expanded } = rootTreeNode; const hasVisibleModalNode = !!this.modalNodesManager?.getVisibleModalNode(); @@ -49,10 +71,9 @@ class ModalTreeNodeView extends PureComponent<{ <div className="tree-pane-modal-content"> <TreeBranches treeNode={rootTreeNode} - treeChildren={rootTreeNode.children} + treeChildren={this.state.treeChildren} expanded={expanded} isModal - pluginContext={this.pluginContext} /> </div> </div> @@ -63,7 +84,6 @@ class ModalTreeNodeView extends PureComponent<{ export default class TreeNodeView extends PureComponent<{ treeNode: TreeNode; isModal?: boolean; - pluginContext: IPublicModelPluginContext; isRootNode?: boolean; }> { state: { @@ -114,8 +134,8 @@ export default class TreeNodeView extends PureComponent<{ } componentDidMount() { - const { treeNode, pluginContext } = this.props; - const { project } = pluginContext; + const { treeNode } = this.props; + const { project } = treeNode.pluginContext; const doc = project.currentDocument; @@ -158,14 +178,14 @@ export default class TreeNodeView extends PureComponent<{ } shouldShowModalTreeNode(): boolean { - const { treeNode, isRootNode, pluginContext } = this.props; + const { treeNode, isRootNode } = this.props; if (!isRootNode) { // 只在 当前树 的根节点展示模态节点 return false; } // 当指定了新的根节点时,要从原始的根节点去获取模态节点 - const { project } = pluginContext; + const { project } = treeNode.pluginContext; const rootNode = project.currentDocument?.root; const rootTreeNode = treeNode.tree.getTreeNode(rootNode!); const modalNodes = rootTreeNode.children?.filter((item) => { @@ -214,19 +234,16 @@ export default class TreeNodeView extends PureComponent<{ hidden={this.state.hidden} locked={this.state.locked} expandable={this.state.expandable} - pluginContext={this.props.pluginContext} /> {shouldShowModalTreeNode && <ModalTreeNodeView treeNode={treeNode} - pluginContext={this.props.pluginContext} /> } <TreeBranches treeNode={treeNode} isModal={false} expanded={this.state.expanded} - pluginContext={this.props.pluginContext} treeChildren={this.state.treeChildren} /> </div> diff --git a/packages/plugin-outline-pane/src/views/tree-title.tsx b/packages/plugin-outline-pane/src/views/tree-title.tsx index 6e1b14a46..6f35c6a61 100644 --- a/packages/plugin-outline-pane/src/views/tree-title.tsx +++ b/packages/plugin-outline-pane/src/views/tree-title.tsx @@ -1,7 +1,7 @@ import { KeyboardEvent, FocusEvent, Fragment, PureComponent } from 'react'; import classNames from 'classnames'; import { createIcon } from '@alilc/lowcode-utils'; -import { IPublicModelPluginContext, IPublicApiEvent } from '@alilc/lowcode-types'; +import { IPublicApiEvent } from '@alilc/lowcode-types'; import TreeNode from '../controllers/tree-node'; import { IconLock, IconUnlock, IconArrowRight, IconEyeClose, IconEye, IconCond, IconLoop, IconRadioActive, IconRadio, IconSetting } from '../icons'; @@ -23,11 +23,12 @@ export default class TreeTitle extends PureComponent<{ hidden: boolean; locked: boolean; expandable: boolean; - pluginContext: IPublicModelPluginContext; }> { state: { editing: boolean; title: string; + condition?: boolean; + visible?: boolean; } = { editing: false, title: '', @@ -53,7 +54,7 @@ export default class TreeTitle extends PureComponent<{ const { treeNode } = this.props; const value = (e.target as HTMLInputElement).value || ''; treeNode.setTitleLabel(value); - emitOutlineEvent(this.props.pluginContext.event, 'rename', treeNode, { value }); + emitOutlineEvent(this.props.treeNode.pluginContext.event, 'rename', treeNode, { value }); this.cancelEdit(); }; @@ -81,16 +82,28 @@ export default class TreeTitle extends PureComponent<{ this.setState({ editing: false, title: treeNode.titleLabel, + condition: treeNode.condition, }); treeNode.onTitleLabelChanged(() => { this.setState({ title: treeNode.titleLabel, }); }); + treeNode.onConditionChanged(() => { + this.setState({ + condition: treeNode.condition, + }); + }); + treeNode.onHiddenChanged((hidden: boolean) => { + this.setState({ + visible: !hidden, + }); + }); } render() { - const { treeNode, isModal, pluginContext } = this.props; + const { treeNode, isModal } = this.props; + const { pluginContext } = treeNode; const { editing } = this.state; const isCNode = !treeNode.isRoot(); const { node } = treeNode; @@ -125,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); @@ -137,7 +150,7 @@ export default class TreeTitle extends PureComponent<{ } }} > - {isModal && node.visible && ( + {isModal && this.state.visible && ( <div onClick={() => { node.document?.modalNodesManager?.setInvisible(node); }} @@ -145,7 +158,7 @@ export default class TreeTitle extends PureComponent<{ <IconRadioActive className="tree-node-modal-radio-active" /> </div> )} - {isModal && !node.visible && ( + {isModal && !this.state.visible && ( <div onClick={() => { node.document?.modalNodesManager?.setVisible(node); }} @@ -153,7 +166,7 @@ export default class TreeTitle extends PureComponent<{ <IconRadio className="tree-node-modal-radio" /> </div> )} - {isCNode && <ExpandBtn expandable={this.props.expandable} expanded={this.props.expanded} treeNode={treeNode} pluginContext={this.props.pluginContext} />} + {isCNode && <ExpandBtn expandable={this.props.expandable} expanded={this.props.expanded} treeNode={treeNode} />} <div className="tree-node-icon">{createIcon(treeNode.icon)}</div> <div className="tree-node-title-label"> {editing ? ( @@ -166,6 +179,7 @@ export default class TreeTitle extends PureComponent<{ /> ) : ( <Fragment> + {/* @ts-ignore */} <Title title={this.state.title} match={filterWorking && matchSelf} @@ -175,6 +189,7 @@ export default class TreeTitle extends PureComponent<{ {node.slotFor && ( <a className="tree-node-tag slot"> {/* todo: click redirect to prop */} + {/* @ts-ignore */} <Tip>{intlNode('Slot for {prop}', { prop: node.slotFor.key })}</Tip> </a> )} @@ -182,22 +197,24 @@ export default class TreeTitle extends PureComponent<{ <a className="tree-node-tag loop"> {/* todo: click todo something */} <IconLoop /> + {/* @ts-ignore */} <Tip>{intlNode('Loop')}</Tip> </a> )} - {node.hasCondition() && !node.conditionGroup && ( + {this.state.condition && ( <a className="tree-node-tag cond"> {/* todo: click todo something */} <IconCond /> + {/* @ts-ignore */} <Tip>{intlNode('Conditional')}</Tip> </a> )} </Fragment> )} </div> - {shouldShowHideBtn && <HideBtn hidden={this.props.hidden} treeNode={treeNode} pluginContext={this.props.pluginContext} />} - {shouldShowLockBtn && <LockBtn locked={this.props.locked} treeNode={treeNode} pluginContext={this.props.pluginContext} />} - {shouldEditBtn && <RenameBtn treeNode={treeNode} pluginContext={this.props.pluginContext} onClick={this.enableEdit} /> } + {shouldShowHideBtn && <HideBtn hidden={this.props.hidden} treeNode={treeNode} />} + {shouldShowLockBtn && <LockBtn locked={this.props.locked} treeNode={treeNode} />} + {shouldEditBtn && <RenameBtn treeNode={treeNode} onClick={this.enableEdit} /> } </div> ); @@ -206,11 +223,10 @@ export default class TreeTitle extends PureComponent<{ class RenameBtn extends PureComponent<{ treeNode: TreeNode; - pluginContext: IPublicModelPluginContext; onClick: (e: any) => void; }> { render() { - const { intl, common } = this.props.pluginContext; + const { intl, common } = this.props.treeNode.pluginContext; const Tip = common.editorCabin.Tip; return ( <div @@ -218,6 +234,7 @@ class RenameBtn extends PureComponent<{ onClick={this.props.onClick} > <IconSetting /> + {/* @ts-ignore */} <Tip>{intl('Rename')}</Tip> </div> ); @@ -226,12 +243,11 @@ class RenameBtn extends PureComponent<{ class LockBtn extends PureComponent<{ treeNode: TreeNode; - pluginContext: IPublicModelPluginContext; locked: boolean; }> { render() { const { treeNode, locked } = this.props; - const { intl, common } = this.props.pluginContext; + const { intl, common } = this.props.treeNode.pluginContext; const Tip = common.editorCabin.Tip; return ( <div @@ -242,6 +258,7 @@ class LockBtn extends PureComponent<{ }} > {locked ? <IconUnlock /> : <IconLock /> } + {/* @ts-ignore */} <Tip>{locked ? intl('Unlock') : intl('Lock')}</Tip> </div> ); @@ -251,24 +268,24 @@ class LockBtn extends PureComponent<{ class HideBtn extends PureComponent<{ treeNode: TreeNode; hidden: boolean; - pluginContext: IPublicModelPluginContext; }, { hidden: boolean; }> { render() { const { treeNode, hidden } = this.props; - const { intl, common } = this.props.pluginContext; + const { intl, common } = treeNode.pluginContext; const Tip = common.editorCabin.Tip; return ( <div className="tree-node-hide-btn" onClick={(e) => { e.stopPropagation(); - emitOutlineEvent(this.props.pluginContext.event, hidden ? 'show' : 'hide', treeNode); + emitOutlineEvent(treeNode.pluginContext.event, hidden ? 'show' : 'hide', treeNode); treeNode.setHidden(!hidden); }} > {hidden ? <IconEye /> : <IconEyeClose />} + {/* @ts-ignore */} <Tip>{hidden ? intl('Show') : intl('Hide')}</Tip> </div> ); @@ -277,7 +294,6 @@ class HideBtn extends PureComponent<{ class ExpandBtn extends PureComponent<{ treeNode: TreeNode; - pluginContext: IPublicModelPluginContext; expanded: boolean; expandable: boolean; }> { @@ -294,7 +310,7 @@ class ExpandBtn extends PureComponent<{ if (expanded) { e.stopPropagation(); } - emitOutlineEvent(this.props.pluginContext.event, expanded ? 'collapse' : 'expand', treeNode); + emitOutlineEvent(treeNode.pluginContext.event, expanded ? 'collapse' : 'expand', treeNode); treeNode.setExpanded(!expanded); }} > diff --git a/packages/plugin-outline-pane/src/views/tree.tsx b/packages/plugin-outline-pane/src/views/tree.tsx index 930c65cce..4bc502886 100644 --- a/packages/plugin-outline-pane/src/views/tree.tsx +++ b/packages/plugin-outline-pane/src/views/tree.tsx @@ -2,7 +2,7 @@ import { MouseEvent as ReactMouseEvent, PureComponent } from 'react'; import { isFormEvent, canClickNode, isShaken } from '@alilc/lowcode-utils'; import { Tree } from '../controllers/tree'; import TreeNodeView from './tree-node'; -import { IPublicEnumDragObjectType, IPublicModelPluginContext, IPublicModelNode } from '@alilc/lowcode-types'; +import { IPublicEnumDragObjectType, IPublicModelNode } from '@alilc/lowcode-types'; import TreeNode from '../controllers/tree-node'; function getTreeNodeIdByEvent(e: ReactMouseEvent, stop: Element): null | string { @@ -20,12 +20,21 @@ function getTreeNodeIdByEvent(e: ReactMouseEvent, stop: Element): null | string export default class TreeView extends PureComponent<{ tree: Tree; - pluginContext: IPublicModelPluginContext; }> { private shell: HTMLDivElement | null = null; + private ignoreUpSelected = false; + + private boostEvent?: MouseEvent; + + state: { + root: TreeNode | null; + } = { + root: null, + }; + private hover(e: ReactMouseEvent) { - const { project } = this.props.pluginContext; + const { project } = this.props.tree.pluginContext; const detecting = project.currentDocument?.detecting; if (detecting?.enable) { return; @@ -54,7 +63,7 @@ export default class TreeView extends PureComponent<{ return; } - const { project, event, canvas } = this.props.pluginContext; + const { project, event, canvas } = this.props.tree.pluginContext; const doc = project.currentDocument; const selection = doc?.selection; const focusNode = doc?.focusNode; @@ -109,10 +118,6 @@ export default class TreeView extends PureComponent<{ return tree.getTreeNodeById(id); } - private ignoreUpSelected = false; - - private boostEvent?: MouseEvent; - private onMouseDown = (e: ReactMouseEvent) => { if (isFormEvent(e.nativeEvent)) { return; @@ -127,7 +132,7 @@ export default class TreeView extends PureComponent<{ if (!canClickNode(node, e)) { return; } - const { project, canvas } = this.props.pluginContext; + const { project, canvas } = this.props.tree.pluginContext; const selection = project.currentDocument?.selection; const focusNode = project.currentDocument?.focusNode; @@ -166,22 +171,16 @@ export default class TreeView extends PureComponent<{ }; private onMouseLeave = () => { - const { pluginContext } = this.props; + const { pluginContext } = this.props.tree; const { project } = pluginContext; const doc = project.currentDocument; doc?.detecting.leave(); }; - state: { - root: TreeNode | null - } = { - root: null, - }; - componentDidMount() { - const { tree, pluginContext } = this.props; + const { tree } = this.props; const { root } = tree; - const { project } = pluginContext; + const { project } = tree.pluginContext; this.setState({ root }); const doc = project.currentDocument; doc?.onFocusNodeChanged(() => { @@ -208,7 +207,6 @@ export default class TreeView extends PureComponent<{ <TreeNodeView key={this.state.root?.id} treeNode={this.state.root} - pluginContext={this.props.pluginContext} isRootNode /> </div> 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/build.json b/packages/rax-simulator-renderer/build.json index b95a17aaf..e7ae1dcf7 100644 --- a/packages/rax-simulator-renderer/build.json +++ b/packages/rax-simulator-renderer/build.json @@ -1,3 +1,3 @@ { - "plugins": ["build-plugin-component", "./build.plugin.js"] + "plugins": ["@alilc/build-plugin-lce", "./build.plugin.js"] } diff --git a/packages/rax-simulator-renderer/package.json b/packages/rax-simulator-renderer/package.json index 76954ff36..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", @@ -9,14 +9,14 @@ "dist" ], "scripts": { - "build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --skip-demo", + "build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build", "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/build.json b/packages/react-renderer/build.json index e791d5b6b..d0aec1038 100644 --- a/packages/react-renderer/build.json +++ b/packages/react-renderer/build.json @@ -1,6 +1,6 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", "build-plugin-fusion", ["build-plugin-moment-locales", { "locales": ["zh-cn"] diff --git a/packages/react-renderer/build.test.json b/packages/react-renderer/build.test.json index dcdc891e9..9cc30d746 100644 --- a/packages/react-renderer/build.test.json +++ b/packages/react-renderer/build.test.json @@ -1,6 +1,6 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", "@alilc/lowcode-test-mate/plugin/index.ts" ] } diff --git a/packages/react-renderer/package.json b/packages/react-renderer/package.json index 862f69d01..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", @@ -12,7 +12,7 @@ "scripts": { "test": "build-scripts test --config build.test.json", "start": "build-scripts start", - "build": "build-scripts build --skip-demo", + "build": "build-scripts build", "build:umd": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --config build.umd.json" }, "keywords": [ @@ -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/build.json b/packages/react-simulator-renderer/build.json index b95a17aaf..e7ae1dcf7 100644 --- a/packages/react-simulator-renderer/build.json +++ b/packages/react-simulator-renderer/build.json @@ -1,3 +1,3 @@ { - "plugins": ["build-plugin-component", "./build.plugin.js"] + "plugins": ["@alilc/build-plugin-lce", "./build.plugin.js"] } diff --git a/packages/react-simulator-renderer/build.test.json b/packages/react-simulator-renderer/build.test.json index dcdc891e9..9cc30d746 100644 --- a/packages/react-simulator-renderer/build.test.json +++ b/packages/react-simulator-renderer/build.test.json @@ -1,6 +1,6 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", "@alilc/lowcode-test-mate/plugin/index.ts" ] } diff --git a/packages/react-simulator-renderer/package.json b/packages/react-simulator-renderer/package.json index fce8d866e..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", @@ -12,15 +12,15 @@ ], "scripts": { "test": "build-scripts test --config build.test.json", - "build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --skip-demo", + "build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build", "build:umd": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --config build.umd.json", "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/build.json b/packages/renderer-core/build.json index a8e42f154..9140815c5 100644 --- a/packages/renderer-core/build.json +++ b/packages/renderer-core/build.json @@ -1,7 +1,7 @@ { "plugins": [ [ - "build-plugin-component", + "@alilc/build-plugin-lce", { "babelPlugins": ["@babel/plugin-transform-typescript"] } diff --git a/packages/renderer-core/build.test.json b/packages/renderer-core/build.test.json index dcdc891e9..9cc30d746 100644 --- a/packages/renderer-core/build.test.json +++ b/packages/renderer-core/build.test.json @@ -1,6 +1,6 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", "@alilc/lowcode-test-mate/plugin/index.ts" ] } diff --git a/packages/renderer-core/jest.config.js b/packages/renderer-core/jest.config.js index 652a76e70..2489490fa 100644 --- a/packages/renderer-core/jest.config.js +++ b/packages/renderer-core/jest.config.js @@ -11,6 +11,7 @@ const jestConfig = { // }, // testMatch: ['(/tests?/.*(test))\\.[jt]s$'], // testMatch: ['**/*/base.test.tsx'], + // testMatch: ['**/utils/common.test.ts'], transformIgnorePatterns: [ `/node_modules/(?!${esModules})/`, ], diff --git a/packages/renderer-core/package.json b/packages/renderer-core/package.json index 84a63aa9a..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", @@ -10,14 +10,14 @@ "es" ], "scripts": { - "build": "build-scripts build --skip-demo", + "build": "build-scripts build", "test": "build-scripts test --config build.test.json", "test:cov": "build-scripts test --config build.test.json --jest-coverage" }, "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 5df988048..98b267475 100644 --- a/packages/renderer-core/src/hoc/leaf.tsx +++ b/packages/renderer-core/src/hoc/leaf.tsx @@ -102,6 +102,9 @@ function initRerenderEvent({ return; } cache.event.get(schema.id)?.dispose.forEach((disposeFn: any) => disposeFn && disposeFn()); + const debounceRerender = debounce(() => { + container.rerender(); + }, 20); cache.event.set(schema.id, { clear: false, leaf, @@ -111,21 +114,21 @@ function initRerenderEvent({ return; } __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onPropsChange make rerender`); - container.rerender(); + debounceRerender(); }), leaf?.onChildrenChange?.(() => { if (!container.autoRepaintNode) { return; } __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onChildrenChange make rerender`); - container.rerender(); + debounceRerender(); }) as Function, leaf?.onVisibleChange?.(() => { if (!container.autoRepaintNode) { return; } __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onVisibleChange make rerender`); - container.rerender(); + debounceRerender(); }), ], }); diff --git a/packages/renderer-core/src/renderer/base.tsx b/packages/renderer-core/src/renderer/base.tsx index 56f58599b..99a9a40cf 100644 --- a/packages/renderer-core/src/renderer/base.tsx +++ b/packages/renderer-core/src/renderer/base.tsx @@ -493,6 +493,11 @@ export default function baseRendererFactory(): IBaseRenderComponent { return schema.map((item, idy) => this.__createVirtualDom(item, scope, parentInfo, (item as IPublicTypeNodeSchema)?.__ctx?.lceKey ? '' : String(idy))); } + // @ts-expect-error 如果直接转换好了,可以返回 + if (schema.$$typeof) { + return schema; + } + const _children = getSchemaChildren(schema); if (!schema.componentName) { logger.error('The componentName in the schema is invalid, please check the schema: ', schema); @@ -510,11 +515,6 @@ export default function baseRendererFactory(): IBaseRenderComponent { schema.children = [text]; } - // @ts-expect-error 如果直接转换好了,可以返回 - if (schema.$$typeof) { - return schema; - } - if (!isSchema(schema)) { return null; } diff --git a/packages/renderer-core/src/renderer/component.tsx b/packages/renderer-core/src/renderer/component.tsx index 4be33f5c1..f9f6e8f96 100644 --- a/packages/renderer-core/src/renderer/component.tsx +++ b/packages/renderer-core/src/renderer/component.tsx @@ -47,11 +47,25 @@ export default function componentRendererFactory(): IBaseRenderComponent { return this.__renderComp(Component, this.__renderContextProvider({ compContext: this })); } + getComponentName() { + return this.props?.componentName; + } + /** 需要重载下面几个方法,如果在低代码组件中绑定了对应的生命周期时会出现死循环 */ - componentDidMount() {} - getSnapshotBeforeUpdate() {} - componentDidUpdate() {} - componentWillUnmount() {} - componentDidCatch() {} + componentDidMount() { + this.__debug(`componentDidMount - ${this.getComponentName()}`); + } + getSnapshotBeforeUpdate() { + this.__debug(`getSnapshotBeforeUpdate - ${this.getComponentName()}`); + } + componentDidUpdate() { + this.__debug(`componentDidUpdate - ${this.getComponentName()}`); + } + componentWillUnmount() { + this.__debug(`componentWillUnmount - ${this.getComponentName()}`); + } + componentDidCatch() { + this.__debug(`componentDidCatch - ${this.getComponentName()}`); + } }; } diff --git a/packages/renderer-core/src/renderer/page.tsx b/packages/renderer-core/src/renderer/page.tsx index 9ba49c723..16d55e01b 100644 --- a/packages/renderer-core/src/renderer/page.tsx +++ b/packages/renderer-core/src/renderer/page.tsx @@ -1,6 +1,9 @@ +import { getLogger } from '@alilc/lowcode-utils'; import baseRendererFactory from './base'; import { IBaseRendererProps, IBaseRenderComponent } from '../types'; +const logger = getLogger({ level: 'warn', bizName: 'renderer-core:page' }); + export default function pageRendererFactory(): IBaseRenderComponent { const BaseRenderer = baseRendererFactory(); return class PageRenderer extends BaseRenderer { @@ -29,6 +32,11 @@ export default function pageRendererFactory(): IBaseRenderComponent { super.componentDidUpdate?.(prevProps, _prevState, snapshot); } + setState(state: any, callback?: () => void) { + logger.info('page set state', state); + super.setState(state, callback); + } + render() { const { __schema, __components } = this.props; if (this.__checkSchema(__schema)) { diff --git a/packages/renderer-core/tests/hoc/leaf.test.tsx b/packages/renderer-core/tests/hoc/leaf.test.tsx index 94d50f87a..d4a65e578 100644 --- a/packages/renderer-core/tests/hoc/leaf.test.tsx +++ b/packages/renderer-core/tests/hoc/leaf.test.tsx @@ -83,7 +83,6 @@ beforeEach(() => { }); component = renderer.create( - // @ts-ignore <Div _leaf={DivNode}> <Text _leaf={TextNode} content="content"></Text> </Div> @@ -238,7 +237,6 @@ describe('mini unit render', () => { nodeMap.set(textSchema.id, TextNode); component = renderer.create( - // @ts-ignore <MiniRenderDiv _leaf={MiniRenderDivNode}> <Text _leaf={TextNode} content="content"></Text> </MiniRenderDiv> @@ -285,7 +283,6 @@ describe('mini unit render', () => { nodeMap.set(textSchema.id, TextNode); renderer.create( - // @ts-ignore <div> <Text _leaf={TextNode} content="content"></Text> </div> @@ -309,7 +306,6 @@ describe('mini unit render', () => { }); renderer.create( - // @ts-ignore <div> <Text _leaf={TextNode} content="content"></Text> </div> @@ -388,7 +384,6 @@ describe('mini unit render', () => { }; const component = renderer.create( - // @ts-ignore <MiniRenderDiv _leaf={MiniRenderDivNode}> <Text _leaf={TextNode} content="content"></Text> </MiniRenderDiv> @@ -428,7 +423,6 @@ describe('mini unit render', () => { nodeMap.set(miniRenderSchema.id, MiniRenderDivNode); component = renderer.create( - // @ts-ignore <MiniRenderDiv _leaf={MiniRenderDivNode}> <Text _leaf={TextNode} content="content"></Text> </MiniRenderDiv> @@ -491,7 +485,6 @@ describe('onVisibleChange', () => { describe('children', () => { it('this.props.children is array', () => { const component = renderer.create( - // @ts-ignore <Div _leaf={DivNode}> <Text _leaf={TextNode} content="content"></Text> <Text _leaf={TextNode} content="content"></Text> diff --git a/packages/renderer-core/tests/renderer/base.test.tsx b/packages/renderer-core/tests/renderer/base.test.tsx index 63c5cfbb2..3faa2bcf4 100644 --- a/packages/renderer-core/tests/renderer/base.test.tsx +++ b/packages/renderer-core/tests/renderer/base.test.tsx @@ -79,7 +79,6 @@ describe('Base Render methods', () => { // const originalUtils = jest.requireActual('../../src/utils'); // mockParseExpression.mockImplementation(originalUtils.parseExpression); const component = TestRenderer.create( - // @ts-ignore <RendererClass __schema={mockSchema} components={components as any} diff --git a/packages/renderer-core/tests/renderer/renderer.test.tsx b/packages/renderer-core/tests/renderer/renderer.test.tsx index 36ee4167e..081cede8a 100644 --- a/packages/renderer-core/tests/renderer/renderer.test.tsx +++ b/packages/renderer-core/tests/renderer/renderer.test.tsx @@ -13,7 +13,6 @@ function getComp(schema, comp = null, others = {}): Promise<{ }> { return new Promise((resolve, reject) => { const component = renderer.create( - // @ts-ignore <Renderer schema={schema} components={components as any} @@ -48,7 +47,6 @@ afterEach(() => { describe('Base Render', () => { it('renderComp', () => { const content = ( - // @ts-ignore <Renderer schema={schema as any} components={components as any} diff --git a/packages/renderer-core/tests/utils/common.test.ts b/packages/renderer-core/tests/utils/common.test.ts index 6fac55024..995e55642 100644 --- a/packages/renderer-core/tests/utils/common.test.ts +++ b/packages/renderer-core/tests/utils/common.test.ts @@ -374,7 +374,7 @@ describe('test parseThisRequiredExpression', () => { }; const fn = logger.error = jest.fn(); parseThisRequiredExpression(mockExpression, { state: { text: 'text' } }); - expect(fn).toBeCalledWith('parseExpression.error', new ReferenceError('state is not defined'), {"type": "JSExpression", "value": "state.text"}, {"state": {"text": "text"}}); + expect(fn).toBeCalledWith(' parseExpression.error', new ReferenceError('state is not defined'), {"type": "JSExpression", "value": "state.text"}, {"state": {"text": "text"}}); }); it('[success] JSExpression handle without this use scopeValue', () => { diff --git a/packages/shell/build.json b/packages/shell/build.json index bd5cf18dd..3e9260055 100644 --- a/packages/shell/build.json +++ b/packages/shell/build.json @@ -1,5 +1,5 @@ { "plugins": [ - "build-plugin-component" + "@alilc/build-plugin-lce" ] } diff --git a/packages/shell/build.test.json b/packages/shell/build.test.json index dcdc891e9..9cc30d746 100644 --- a/packages/shell/build.test.json +++ b/packages/shell/build.test.json @@ -1,6 +1,6 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", "@alilc/lowcode-test-mate/plugin/index.ts" ] } diff --git a/packages/shell/package.json b/packages/shell/package.json index 9b9cc0416..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", @@ -9,18 +9,16 @@ "es" ], "scripts": { - "build": "build-scripts build --skip-demo", - "test": "build-scripts test --config build.test.json", - "test:cov": "build-scripts test --config build.test.json --jest-coverage" + "build": "build-scripts build" }, "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/common.tsx b/packages/shell/src/api/common.tsx index 6a44dc82e..9f38cf109 100644 --- a/packages/shell/src/api/common.tsx +++ b/packages/shell/src/api/common.tsx @@ -1,4 +1,4 @@ -import { editorSymbol, skeletonSymbol, designerCabinSymbol, designerSymbol } from '../symbols'; +import { editorSymbol, skeletonSymbol, designerCabinSymbol, designerSymbol, settingFieldSymbol } from '../symbols'; import { isFormEvent as innerIsFormEvent, compatibleLegaoSchema as innerCompatibleLegaoSchema, @@ -25,6 +25,7 @@ import { IPublicTypeLocationDetailType as InnerLocationDetailType, IPublicApiCommonEditorCabin, IPublicModelDragon, + IPublicModelSettingField, } from '@alilc/lowcode-types'; import { SettingField as InnerSettingField, @@ -57,6 +58,9 @@ import { untracked as innerUntracked, computed as innerComputed, observer as innerObserver, + action as innerAction, + runInAction as innerRunInAction, + engineConfig as innerEngineConfig, } from '@alilc/lowcode-editor-core'; import { Dragon as ShellDragon } from '../model'; import { ReactNode } from 'react'; @@ -168,8 +172,8 @@ class SkeletonCabin implements IPublicApiCommonSkeletonCabin { /** * @deprecated */ - createSettingFieldView(item: any, field: any) { - return innerCreateSettingFieldView(item, field); + createSettingFieldView(field: IPublicModelSettingField, fieldEntry: any) { + return innerCreateSettingFieldView((field as any)[settingFieldSymbol] || field, fieldEntry); } /** @@ -301,6 +305,27 @@ class EditorCabin implements IPublicApiCommonEditorCabin { return innerObx; } + /** + * @deprecated + */ + get action() { + return innerAction; + } + + /** + * @deprecated + */ + get engineConfig() { + return innerEngineConfig; + } + + /** + * @deprecated + */ + get runInAction() { + return innerRunInAction; + } + /** * @deprecated */ diff --git a/packages/shell/src/api/material.ts b/packages/shell/src/api/material.ts index e3ebd20ec..ea9d6e01b 100644 --- a/packages/shell/src/api/material.ts +++ b/packages/shell/src/api/material.ts @@ -143,9 +143,16 @@ export class Material implements IPublicApiMaterial { * 在设计器辅助层增加一个扩展 action * @param action */ - addBuiltinComponentAction(action: IPublicTypeComponentAction) { + addBuiltinComponentAction = (action: IPublicTypeComponentAction) => { this[designerSymbol].componentActions.addBuiltinComponentAction(action); - } + }; + + /** + * 刷新 componentMetasMap,可触发模拟器里的 components 重新构建 + */ + refreshComponentMetasMap = () => { + this[designerSymbol].refreshComponentMetasMap(); + }; /** * 移除设计器辅助层的指定 action diff --git a/packages/shell/src/api/plugins.ts b/packages/shell/src/api/plugins.ts index e5a79edbd..b6f5e6371 100644 --- a/packages/shell/src/api/plugins.ts +++ b/packages/shell/src/api/plugins.ts @@ -66,8 +66,8 @@ export class Plugins implements IPublicApiPlugins { return this[pluginsSymbol].has(pluginName); } - delete(pluginName: string) { - this[pluginsSymbol].delete(pluginName); + async delete(pluginName: string) { + return await this[pluginsSymbol].delete(pluginName); } toProxy() { diff --git a/packages/shell/src/api/project.ts b/packages/shell/src/api/project.ts index ed45701b3..f005d0af0 100644 --- a/packages/shell/src/api/project.ts +++ b/packages/shell/src/api/project.ts @@ -18,6 +18,9 @@ import { import { DocumentModel as ShellDocumentModel } from '../model'; import { SimulatorHost } from './simulator-host'; import { editorSymbol, projectSymbol, simulatorHostSymbol, documentSymbol } from '../symbols'; +import { getLogger } from '@alilc/lowcode-utils'; + +const logger = getLogger({ level: 'warn', bizName: 'shell-project' }); const innerProjectSymbol = Symbol('innerProject'); export class Project implements IPublicApiProject { @@ -29,6 +32,10 @@ export class Project implements IPublicApiProject { } const workspace = globalContext.get('workspace'); if (workspace.isActive) { + if (!workspace.window?.innerProject) { + logger.error('project api 调用时机出现问题,请检查'); + return this[innerProjectSymbol]; + } return workspace.window.innerProject; } @@ -43,8 +50,8 @@ export class Project implements IPublicApiProject { this[innerProjectSymbol] = project; } - static create(project: InnerProject) { - return new Project(project); + static create(project: InnerProject, workspaceMode: boolean = false) { + return new Project(project, workspaceMode); } /** @@ -225,15 +232,15 @@ export class Project implements IPublicApiProject { */ setConfig<T extends keyof IPublicTypeAppConfig>(key: T, value: IPublicTypeAppConfig[T]): void; setConfig(value: IPublicTypeAppConfig): void; - setConfig(...params: any[]): void{ - if(params.length === 2) { + setConfig(...params: any[]): void { + if (params.length === 2) { const oldConfig = this[projectSymbol].get('config'); this[projectSymbol].set('config', { ...oldConfig, [params[0]]: params[1], - }) + }); } else { - this[projectSymbol].set('config', params[0]) + this[projectSymbol].set('config', params[0]); } } } diff --git a/packages/shell/src/api/setters.ts b/packages/shell/src/api/setters.ts index 553f32c4e..7750a5152 100644 --- a/packages/shell/src/api/setters.ts +++ b/packages/shell/src/api/setters.ts @@ -1,27 +1,36 @@ import { IPublicTypeCustomView, IPublicApiSetters, IPublicTypeRegisteredSetter } from '@alilc/lowcode-types'; -import { Setters as InnerSetters, globalContext } from '@alilc/lowcode-editor-core'; +import { ISetters, globalContext, untracked } from '@alilc/lowcode-editor-core'; import { ReactNode } from 'react'; +import { getLogger } from '@alilc/lowcode-utils'; const innerSettersSymbol = Symbol('setters'); const settersSymbol = Symbol('setters'); -export class Setters implements IPublicApiSetters { - readonly [innerSettersSymbol]: InnerSetters; +const logger = getLogger({ level: 'warn', bizName: 'shell-setters' }); - get [settersSymbol](): InnerSetters { +export class Setters implements IPublicApiSetters { + readonly [innerSettersSymbol]: ISetters; + + get [settersSymbol](): ISetters { if (this.workspaceMode) { return this[innerSettersSymbol]; } const workspace = globalContext.get('workspace'); if (workspace.isActive) { - return workspace.window.innerSetters; + return untracked(() => { + if (!workspace.window.innerSetters) { + logger.error('setter api 调用时机出现问题,请检查'); + return this[innerSettersSymbol]; + } + return workspace.window.innerSetters; + }); } return this[innerSettersSymbol]; } - constructor(innerSetters: InnerSetters, readonly workspaceMode = false) { + constructor(innerSetters: ISetters, readonly workspaceMode = false) { this[innerSettersSymbol] = innerSetters; } @@ -38,11 +47,11 @@ export class Setters implements IPublicApiSetters { * 获取已注册的所有 settersMap * @returns */ - getSettersMap(): Map<string, IPublicTypeRegisteredSetter & { + getSettersMap = (): Map<string, IPublicTypeRegisteredSetter & { type: string; - }> { + }> => { return this[settersSymbol].getSettersMap(); - } + }; /** * 注册一个 setter @@ -57,6 +66,9 @@ export class Setters implements IPublicApiSetters { return this[settersSymbol].registerSetter(typeOrMaps, setter); }; + /** + * @deprecated + */ createSetterContent = (setter: any, props: Record<string, any>): ReactNode => { return this[settersSymbol].createSetterContent(setter, props); }; diff --git a/packages/shell/src/api/skeleton.ts b/packages/shell/src/api/skeleton.ts index 928c55a0d..b48747598 100644 --- a/packages/shell/src/api/skeleton.ts +++ b/packages/shell/src/api/skeleton.ts @@ -4,10 +4,14 @@ import { SkeletonEvents, } from '@alilc/lowcode-editor-skeleton'; import { skeletonSymbol } from '../symbols'; -import { IPublicApiSkeleton, IPublicTypeDisposable, IPublicTypeSkeletonConfig, IPublicTypeWidgetConfigArea } from '@alilc/lowcode-types'; +import { IPublicApiSkeleton, IPublicModelSkeletonItem, IPublicTypeDisposable, IPublicTypeSkeletonConfig, IPublicTypeWidgetConfigArea } from '@alilc/lowcode-types'; +import { getLogger } from '@alilc/lowcode-utils'; +import { SkeletonItem } from '../model/skeleton-item'; const innerSkeletonSymbol = Symbol('skeleton'); +const logger = getLogger({ level: 'warn', bizName: 'shell-skeleton' }); + export class Skeleton implements IPublicApiSkeleton { private readonly [innerSkeletonSymbol]: ISkeleton; private readonly pluginName: string; @@ -18,6 +22,10 @@ export class Skeleton implements IPublicApiSkeleton { } const workspace = globalContext.get('workspace'); if (workspace.isActive) { + if (!workspace.window.innerSkeleton) { + logger.error('skeleton api 调用时机出现问题,请检查'); + return this[innerSkeletonSymbol]; + } return workspace.window.innerSkeleton; } @@ -39,12 +47,15 @@ export class Skeleton implements IPublicApiSkeleton { * @param extraConfig * @returns */ - add(config: IPublicTypeSkeletonConfig, extraConfig?: Record<string, any>) { + add(config: IPublicTypeSkeletonConfig, extraConfig?: Record<string, any>): IPublicModelSkeletonItem | undefined { const configWithName = { ...config, pluginName: this.pluginName, }; - return this[skeletonSymbol].add(configWithName, extraConfig); + const item = this[skeletonSymbol].add(configWithName, extraConfig); + if (item) { + return new SkeletonItem(item); + } } /** @@ -61,6 +72,10 @@ export class Skeleton implements IPublicApiSkeleton { skeleton[normalizeArea(area)].container?.remove(name); } + getAreaItems(areaName: IPublicTypeWidgetConfigArea): IPublicModelSkeletonItem[] { + return this[skeletonSymbol][normalizeArea(areaName)].container.items?.map(d => new SkeletonItem(d)); + } + /** * 显示面板 * @param name @@ -186,7 +201,7 @@ export class Skeleton implements IPublicApiSkeleton { } } -function normalizeArea(area: IPublicTypeWidgetConfigArea | undefined): 'leftArea' | 'rightArea' | 'topArea' | 'toolbar' | 'mainArea' | 'bottomArea' | 'leftFixedArea' | 'leftFloatArea' | 'stages' { +function normalizeArea(area: IPublicTypeWidgetConfigArea | undefined): 'leftArea' | 'rightArea' | 'topArea' | 'toolbar' | 'mainArea' | 'bottomArea' | 'leftFixedArea' | 'leftFloatArea' | 'stages' | 'subTopArea' { switch (area) { case 'leftArea': case 'left': @@ -213,6 +228,8 @@ function normalizeArea(area: IPublicTypeWidgetConfigArea | undefined): 'leftArea return 'leftFloatArea'; case 'stages': return 'stages'; + case 'subTopArea': + return 'subTopArea'; default: throw new Error(`${area} not supported`); } diff --git a/packages/shell/src/api/workspace.ts b/packages/shell/src/api/workspace.ts index 21015431f..8192ac67d 100644 --- a/packages/shell/src/api/workspace.ts +++ b/packages/shell/src/api/workspace.ts @@ -28,15 +28,22 @@ export class Workspace implements IPublicApiWorkspace { } get window() { + if (!this[workspaceSymbol].window) { + return null; + } return new ShellWindow(this[workspaceSymbol].window); } + onWindowRendererReady(fn: () => void): IPublicTypeDisposable { + return this[workspaceSymbol].onWindowRendererReady(fn); + } + registerResourceType(resourceTypeModel: IPublicTypeResourceType): void { this[workspaceSymbol].registerResourceType(resourceTypeModel); } - openEditorWindow(resourceName: string, title: string, extra: object, viewName?: string) { - this[workspaceSymbol].openEditorWindow(resourceName, title, extra, viewName); + async openEditorWindow(resourceName: string, title: string, extra: object, viewName?: string, sleep?: boolean): Promise<void> { + await this[workspaceSymbol].openEditorWindow(resourceName, title, extra, viewName, sleep); } openEditorWindowById(id: string) { @@ -66,4 +73,8 @@ export class Workspace implements IPublicApiWorkspace { onChangeActiveWindow(fn: () => void): IPublicTypeDisposable { return this[workspaceSymbol].onChangeActiveWindow(fn); } + + onChangeActiveEditorView(fn: () => void): IPublicTypeDisposable { + return this[workspaceSymbol].onChangeActiveEditorView(fn); + } } diff --git a/packages/shell/src/index.ts b/packages/shell/src/index.ts index 7017ca289..6f79c78bc 100644 --- a/packages/shell/src/index.ts +++ b/packages/shell/src/index.ts @@ -10,6 +10,8 @@ import { SettingTopEntry, Clipboard, SettingField, + Window, + SkeletonItem, } from './model'; import { Project, @@ -50,6 +52,7 @@ export { Selection, Setters, Hotkey, + Window, Skeleton, SettingField as SettingPropEntry, SettingTopEntry, @@ -64,4 +67,5 @@ export { SimulatorHost, Config, SettingField, + SkeletonItem, }; diff --git a/packages/shell/src/model/active-tracker.ts b/packages/shell/src/model/active-tracker.ts index 41871aacb..32d4c04eb 100644 --- a/packages/shell/src/model/active-tracker.ts +++ b/packages/shell/src/model/active-tracker.ts @@ -12,6 +12,22 @@ export class ActiveTracker implements IPublicModelActiveTracker { this[activeTrackerSymbol] = innerTracker; } + get target() { + const _target = this[activeTrackerSymbol]._target; + + if (!_target) { + return null; + } + + const { node: innerNode, detail, instance } = _target; + const publicNode = ShellNode.create(innerNode); + return { + node: publicNode!, + detail, + instance, + }; + } + onChange(fn: (target: IPublicTypeActiveTarget) => void): () => void { if (!fn) { return () => {}; diff --git a/packages/shell/src/model/document-model.ts b/packages/shell/src/model/document-model.ts index 68252e3a5..06f3cebcd 100644 --- a/packages/shell/src/model/document-model.ts +++ b/packages/shell/src/model/document-model.ts @@ -90,7 +90,7 @@ export class DocumentModel implements IPublicModelDocumentModel { * @returns */ get project(): IPublicApiProject { - return ShellProject.create(this[documentSymbol].project); + return ShellProject.create(this[documentSymbol].project, true); } /** diff --git a/packages/shell/src/model/editor-view.ts b/packages/shell/src/model/editor-view.ts new file mode 100644 index 000000000..d70a109b1 --- /dev/null +++ b/packages/shell/src/model/editor-view.ts @@ -0,0 +1,27 @@ +import { editorViewSymbol, pluginContextSymbol } from '../symbols'; +import { IPublicModelPluginContext } from '@alilc/lowcode-types'; +import { IViewContext } from '@alilc/lowcode-workspace'; + +export class EditorView { + [editorViewSymbol]: IViewContext; + + [pluginContextSymbol]: IPublicModelPluginContext; + + constructor(editorView: IViewContext) { + this[editorViewSymbol] = editorView; + this[pluginContextSymbol] = this[editorViewSymbol].innerPlugins._getLowCodePluginContext({ + pluginName: editorView.editorWindow + editorView.viewName, + }); + } + + toProxy() { + return new Proxy(this, { + get(target, prop, receiver) { + if ((target[pluginContextSymbol] as any)[prop as string]) { + return Reflect.get(target[pluginContextSymbol], prop, receiver); + } + return Reflect.get(target, prop, receiver); + }, + }); + } +} diff --git a/packages/shell/src/model/index.ts b/packages/shell/src/model/index.ts index cd481643e..a15d50b54 100644 --- a/packages/shell/src/model/index.ts +++ b/packages/shell/src/model/index.ts @@ -19,3 +19,5 @@ export * from './active-tracker'; export * from './plugin-instance'; export * from './window'; export * from './clipboard'; +export * from './editor-view'; +export * from './skeleton-item'; diff --git a/packages/shell/src/model/resource.ts b/packages/shell/src/model/resource.ts index b2b065398..43f28fbd8 100644 --- a/packages/shell/src/model/resource.ts +++ b/packages/shell/src/model/resource.ts @@ -25,6 +25,10 @@ export class Resource implements IPublicModelResource { return this[resourceSymbol].resourceType.name; } + get config() { + return this[resourceSymbol].config; + } + get type() { return this[resourceSymbol].resourceType.type; } diff --git a/packages/shell/src/model/skeleton-item.ts b/packages/shell/src/model/skeleton-item.ts new file mode 100644 index 000000000..b91e5a19a --- /dev/null +++ b/packages/shell/src/model/skeleton-item.ts @@ -0,0 +1,31 @@ +import { skeletonItemSymbol } from '../symbols'; +import { IPublicModelSkeletonItem } from '@alilc/lowcode-types'; +import { Dock, IWidget, Panel, PanelDock, Stage, Widget } from '@alilc/lowcode-editor-skeleton'; + +export class SkeletonItem implements IPublicModelSkeletonItem { + private [skeletonItemSymbol]: IWidget | Widget | Panel | Stage | Dock | PanelDock; + + constructor(skeletonItem: IWidget | Widget | Panel | Stage | Dock | PanelDock) { + this[skeletonItemSymbol] = skeletonItem; + } + + get name() { + return this[skeletonItemSymbol].name; + } + + disable() { + this[skeletonItemSymbol].disable?.(); + } + + enable() { + this[skeletonItemSymbol].enable?.(); + } + + hide() { + this[skeletonItemSymbol].hide(); + } + + show() { + this[skeletonItemSymbol].show(); + } +} \ No newline at end of file diff --git a/packages/shell/src/model/window.ts b/packages/shell/src/model/window.ts index b1263d541..3e6bd6450 100644 --- a/packages/shell/src/model/window.ts +++ b/packages/shell/src/model/window.ts @@ -2,6 +2,7 @@ import { windowSymbol } from '../symbols'; import { IPublicModelResource, IPublicModelWindow, IPublicTypeDisposable } from '@alilc/lowcode-types'; import { IEditorWindow } from '@alilc/lowcode-workspace'; import { Resource as ShellResource } from './resource'; +import { EditorView } from './editor-view'; export class Window implements IPublicModelWindow { private readonly [windowSymbol]: IEditorWindow; @@ -41,4 +42,19 @@ export class Window implements IPublicModelWindow { async save() { 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; + } + return null; + } + + get editorViews() { + return Array.from(this[windowSymbol].editorViews.values()).map(d => new EditorView(d).toProxy() as any); + } } diff --git a/packages/shell/src/symbols.ts b/packages/shell/src/symbols.ts index 4ba6ff236..b4ec01c7e 100644 --- a/packages/shell/src/symbols.ts +++ b/packages/shell/src/symbols.ts @@ -34,3 +34,6 @@ export const resourceSymbol = Symbol('resource'); export const clipboardSymbol = Symbol('clipboard'); export const configSymbol = Symbol('configSymbol'); export const conditionGroupSymbol = Symbol('conditionGroup'); +export const editorViewSymbol = Symbol('editorView'); +export const pluginContextSymbol = Symbol('pluginContext'); +export const skeletonItemSymbol = Symbol('skeletonItem'); \ No newline at end of file diff --git a/packages/types/build.json b/packages/types/build.json index bd5cf18dd..3e9260055 100644 --- a/packages/types/build.json +++ b/packages/types/build.json @@ -1,5 +1,5 @@ { "plugins": [ - "build-plugin-component" + "@alilc/build-plugin-lce" ] } diff --git a/packages/types/package.json b/packages/types/package.json index 6d9033886..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", @@ -9,7 +9,7 @@ "main": "lib/index.js", "module": "es/index.js", "scripts": { - "build": "build-scripts build --skip-demo" + "build": "build-scripts build" }, "dependencies": { "@alilc/lowcode-datasource-types": "^1.0.0", diff --git a/packages/types/src/shell/api/material.ts b/packages/types/src/shell/api/material.ts index 1e9f54996..d64455edd 100644 --- a/packages/types/src/shell/api/material.ts +++ b/packages/types/src/shell/api/material.ts @@ -122,4 +122,10 @@ export interface IPublicApiMaterial { * @param fn */ onChangeAssets(fn: () => void): IPublicTypeDisposable; + + /** + * 刷新 componentMetasMap,可触发模拟器里的 components 重新构建 + * @since v1.1.7 + */ + refreshComponentMetasMap(): void; } diff --git a/packages/types/src/shell/api/setters.ts b/packages/types/src/shell/api/setters.ts index 9ff2e8eda..011a9dcac 100644 --- a/packages/types/src/shell/api/setters.ts +++ b/packages/types/src/shell/api/setters.ts @@ -1,6 +1,9 @@ +import { ReactNode } from 'react'; + import { IPublicTypeRegisteredSetter, IPublicTypeCustomView } from '../type'; export interface IPublicApiSetters { + /** * 获取指定 setter * get setter by type @@ -29,4 +32,9 @@ export interface IPublicApiSetters { typeOrMaps: string | { [key: string]: IPublicTypeCustomView | IPublicTypeRegisteredSetter }, setter?: IPublicTypeCustomView | IPublicTypeRegisteredSetter | undefined ): void; + + /** + * @deprecated + */ + createSetterContent (setter: any, props: Record<string, any>): ReactNode; } diff --git a/packages/types/src/shell/api/skeleton.ts b/packages/types/src/shell/api/skeleton.ts index b22b3586c..13ba3468b 100644 --- a/packages/types/src/shell/api/skeleton.ts +++ b/packages/types/src/shell/api/skeleton.ts @@ -1,3 +1,4 @@ +import { IPublicModelSkeletonItem } from '../model'; import { IPublicTypeDisposable, IPublicTypeSkeletonConfig } from '../type'; export interface IPublicApiSkeleton { @@ -9,7 +10,7 @@ export interface IPublicApiSkeleton { * @param extraConfig * @returns */ - add(config: IPublicTypeSkeletonConfig, extraConfig?: Record<string, any>): any; + add(config: IPublicTypeSkeletonConfig, extraConfig?: Record<string, any>): IPublicModelSkeletonItem | undefined; /** * 移除一个面板实例 diff --git a/packages/types/src/shell/api/workspace.ts b/packages/types/src/shell/api/workspace.ts index 8c19846f7..8ae2434bc 100644 --- a/packages/types/src/shell/api/workspace.ts +++ b/packages/types/src/shell/api/workspace.ts @@ -10,7 +10,7 @@ export interface IPublicApiWorkspace< isActive: boolean; /** 当前设计器窗口 */ - window: ModelWindow; + window: ModelWindow | null; plugins: Plugins; @@ -30,7 +30,7 @@ export interface IPublicApiWorkspace< registerResourceType(resourceTypeModel: IPublicTypeResourceType): void; /** 打开视图窗口 */ - openEditorWindow(resourceName: string, title: string, extra: Object, viewName?: string): void; + openEditorWindow(resourceName: string, title: string, extra: Object, viewName?: string, sleep?: boolean): Promise<void>; /** 通过视图 id 打开窗口 */ openEditorWindowById(id: string): void; @@ -46,4 +46,16 @@ export interface IPublicApiWorkspace< /** active 窗口变更事件 */ onChangeActiveWindow(fn: () => void): IPublicTypeDisposable; + + /** + * active 视图变更事件 + * @since v1.1.7 + */ + onChangeActiveEditorView(fn: () => void): IPublicTypeDisposable; + + /** + * window 下的所有视图 renderer ready 事件 + * @since v1.1.7 + */ + onWindowRendererReady(fn: () => void): IPublicTypeDisposable; } \ No newline at end of file diff --git a/packages/types/src/shell/enum/index.ts b/packages/types/src/shell/enum/index.ts index 506055825..f3d558011 100644 --- a/packages/types/src/shell/enum/index.ts +++ b/packages/types/src/shell/enum/index.ts @@ -2,4 +2,5 @@ export * from './event-names'; export * from './transition-type'; export * from './transform-stage'; export * from './drag-object-type'; -export * from './prop-value-changed-type'; \ No newline at end of file +export * from './prop-value-changed-type'; +export * from './plugin-register-level'; \ No newline at end of file diff --git a/packages/types/src/shell/enum/plugin-register-level.ts b/packages/types/src/shell/enum/plugin-register-level.ts new file mode 100644 index 000000000..a0d9b746b --- /dev/null +++ b/packages/types/src/shell/enum/plugin-register-level.ts @@ -0,0 +1,6 @@ +export enum IPublicEnumPluginRegisterLevel { + Default = 'default', + Workspace = 'workspace', + Resource = 'resource', + EditorView = 'editorView', +} \ No newline at end of file diff --git a/packages/types/src/shell/model/active-tracker.ts b/packages/types/src/shell/model/active-tracker.ts index b0ceec11f..ac116a947 100644 --- a/packages/types/src/shell/model/active-tracker.ts +++ b/packages/types/src/shell/model/active-tracker.ts @@ -2,6 +2,12 @@ import { IPublicTypeActiveTarget } from '../type'; import { IPublicModelNode } from './node'; export interface IPublicModelActiveTracker { + + /** + * @since 1.1.7 + */ + target: IPublicTypeActiveTarget | null; + onChange(fn: (target: IPublicTypeActiveTarget) => void): () => void; track(node: IPublicModelNode): void; diff --git a/packages/types/src/shell/model/dragon.ts b/packages/types/src/shell/model/dragon.ts index 662eb6a00..917149faf 100644 --- a/packages/types/src/shell/model/dragon.ts +++ b/packages/types/src/shell/model/dragon.ts @@ -1,5 +1,5 @@ /* eslint-disable max-len */ -import { IPublicTypeDragNodeDataObject, IPublicTypeDragObject } from '../type'; +import { IPublicTypeDisposable, IPublicTypeDragNodeDataObject, IPublicTypeDragObject } from '../type'; import { IPublicModelDragObject, IPublicModelLocateEvent, IPublicModelNode } from './'; export interface IPublicModelDragon< @@ -19,7 +19,7 @@ export interface IPublicModelDragon< * @param func * @returns */ - onDragstart(func: (e: LocateEvent) => any): () => void; + onDragstart(func: (e: LocateEvent) => any): IPublicTypeDisposable; /** * 绑定 drag 事件 @@ -27,7 +27,7 @@ export interface IPublicModelDragon< * @param func * @returns */ - onDrag(func: (e: LocateEvent) => any): () => void; + onDrag(func: (e: LocateEvent) => any): IPublicTypeDisposable; /** * 绑定 dragend 事件 @@ -35,7 +35,7 @@ export interface IPublicModelDragon< * @param func * @returns */ - onDragend(func: (o: { dragObject: IPublicModelDragObject; copy?: boolean }) => any): () => void; + onDragend(func: (o: { dragObject: IPublicModelDragObject; copy?: boolean }) => any): IPublicTypeDisposable; /** * 设置拖拽监听的区域 shell,以及自定义拖拽转换函数 boost diff --git a/packages/types/src/shell/model/editor-view.ts b/packages/types/src/shell/model/editor-view.ts new file mode 100644 index 000000000..793417845 --- /dev/null +++ b/packages/types/src/shell/model/editor-view.ts @@ -0,0 +1,3 @@ +import { IPublicModelPluginContext } from './plugin-context'; + +export interface IPublicModelEditorView extends IPublicModelPluginContext {} \ No newline at end of file diff --git a/packages/types/src/shell/model/index.ts b/packages/types/src/shell/model/index.ts index b62fb76b9..1924f1121 100644 --- a/packages/types/src/shell/model/index.ts +++ b/packages/types/src/shell/model/index.ts @@ -30,3 +30,5 @@ export * from './sensor'; export * from './resource'; export * from './clipboard'; export * from './setting-field'; +export * from './editor-view'; +export * from './skeleton-item'; diff --git a/packages/types/src/shell/model/plugin-context.ts b/packages/types/src/shell/model/plugin-context.ts index 5d97b5472..1f3d3b5e8 100644 --- a/packages/types/src/shell/model/plugin-context.ts +++ b/packages/types/src/shell/model/plugin-context.ts @@ -12,18 +12,11 @@ import { IPublicApiPlugins, IPublicApiWorkspace, } from '../api'; -import { IPublicModelEngineConfig } from './'; +import { IPublicEnumPluginRegisterLevel } from '../enum'; +import { IPublicModelEngineConfig, IPublicModelWindow } from './'; export interface IPublicModelPluginContext { - /** - * 对于插件开发者来说,可以在 context 挂载自定义的内容,作为插件内全局上下文使用 - * - * for plugin developers, costom properties can be add to plugin context - * from inside plugin for convenience. - */ - [key: string]: any; - /** * 可通过该对象读取插件初始化配置 * by using this, init options can be accessed from inside plugin @@ -108,6 +101,14 @@ export interface IPublicModelPluginContext { * @tutorial https://lowcode-engine.cn/site/docs/api/workspace */ get workspace(): IPublicApiWorkspace; + + /** + * 插件注册层级 + * @since v1.1.7 + */ + get registerLevel(): IPublicEnumPluginRegisterLevel; + + get editorWindow(): IPublicModelWindow; } /** diff --git a/packages/types/src/shell/model/resource.ts b/packages/types/src/shell/model/resource.ts index e18f3d503..1f59845e6 100644 --- a/packages/types/src/shell/model/resource.ts +++ b/packages/types/src/shell/model/resource.ts @@ -18,6 +18,10 @@ export interface IBaseModelResource< get children(): Resource[]; get viewName(): string | undefined; + + get config(): { + disableBehaviors?: ('copy' | 'remove')[]; + } | undefined; } export type IPublicModelResource = IBaseModelResource<IPublicModelResource>; diff --git a/packages/types/src/shell/model/skeleton-item.ts b/packages/types/src/shell/model/skeleton-item.ts new file mode 100644 index 000000000..ca262ccbf --- /dev/null +++ b/packages/types/src/shell/model/skeleton-item.ts @@ -0,0 +1,6 @@ +/** + * @since 1.1.7 + */ +export interface IPublicModelSkeletonItem { + +} \ No newline at end of file diff --git a/packages/types/src/shell/model/window.ts b/packages/types/src/shell/model/window.ts index bb27ce317..95ab738bc 100644 --- a/packages/types/src/shell/model/window.ts +++ b/packages/types/src/shell/model/window.ts @@ -1,6 +1,7 @@ import { ReactElement } from 'react'; import { IPublicTypeDisposable, IPublicTypeNodeSchema } from '../type'; import { IPublicModelResource } from './resource'; +import { IPublicModelEditorView } from './editor-view'; export interface IPublicModelWindow< Resource = IPublicModelResource @@ -18,6 +19,18 @@ export interface IPublicModelWindow< /** 窗口资源类型 */ resource?: Resource; + /** + * 窗口当前视图 + * @since v1.1.7 + */ + currentEditorView: IPublicModelEditorView | null; + + /** + * 窗口全部视图实例 + * @since v1.1.7 + */ + editorViews: IPublicModelEditorView[]; + /** 当前窗口导入 schema */ importSchema(schema: IPublicTypeNodeSchema): void; @@ -29,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/engine-options.ts b/packages/types/src/shell/type/engine-options.ts index 195db8912..f17716653 100644 --- a/packages/types/src/shell/type/engine-options.ts +++ b/packages/types/src/shell/type/engine-options.ts @@ -2,6 +2,7 @@ import { RequestHandlersMap } from '@alilc/lowcode-datasource-types'; import { ComponentType } from 'react'; export interface IPublicTypeEngineOptions { + /** * 是否开启 condition 的能力,默认在设计器中不管 condition 是啥都正常展示 * when this is true, node that configured as conditional not renderring @@ -136,8 +137,10 @@ export interface IPublicTypeEngineOptions { * 与 react-renderer 的 appHelper 一致,https://lowcode-engine.cn/site/docs/guide/expand/runtime/renderer#apphelper */ appHelper?: { + /** 全局公共函数 */ utils?: Record<string, any>; + /** 全局常量 */ constants?: Record<string, any>; }; @@ -168,6 +171,12 @@ export interface IPublicTypeEngineOptions { * 开启应用级设计模式 */ enableWorkspaceMode?: boolean; + + /** + * @default true + * 应用级设计模式下,自动打开第一个窗口 + */ + enableAutoOpenFirstWindow?: boolean; } /** diff --git a/packages/types/src/shell/type/resource-list.ts b/packages/types/src/shell/type/resource-list.ts index 5831d7b50..7abdcc7b1 100644 --- a/packages/types/src/shell/type/resource-list.ts +++ b/packages/types/src/shell/type/resource-list.ts @@ -24,6 +24,10 @@ export interface IPublicResourceData { /** 资源子元素 */ children?: IPublicResourceData[]; + + config?: { + disableBehaviors?: ('copy' | 'remove')[]; + }; } export type IPublicResourceList = IPublicResourceData[]; \ No newline at end of file diff --git a/packages/types/src/shell/type/widget-base-config.ts b/packages/types/src/shell/type/widget-base-config.ts index f8268628b..edddf2d68 100644 --- a/packages/types/src/shell/type/widget-base-config.ts +++ b/packages/types/src/shell/type/widget-base-config.ts @@ -25,7 +25,7 @@ export interface IPublicTypePanelDockConfig extends IPublicTypeWidgetBaseConfig props?: IPublicTypePanelDockProps; /** 面板 name, 当没有 props.title 时, 会使用 name 作为标题 */ - name?: string; + name: string; } export interface IPublicTypePanelDockProps { diff --git a/packages/utils/build.json b/packages/utils/build.json index bd5cf18dd..3e9260055 100644 --- a/packages/utils/build.json +++ b/packages/utils/build.json @@ -1,5 +1,5 @@ { "plugins": [ - "build-plugin-component" + "@alilc/build-plugin-lce" ] } diff --git a/packages/utils/build.test.json b/packages/utils/build.test.json index dcdc891e9..9cc30d746 100644 --- a/packages/utils/build.test.json +++ b/packages/utils/build.test.json @@ -1,6 +1,6 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", "@alilc/lowcode-test-mate/plugin/index.ts" ] } diff --git a/packages/utils/jest.config.js b/packages/utils/jest.config.js index 0e05687d7..328ea622e 100644 --- a/packages/utils/jest.config.js +++ b/packages/utils/jest.config.js @@ -1,9 +1,20 @@ -module.exports = { +const fs = require('fs'); +const { join } = require('path'); +const pkgNames = fs.readdirSync(join('..')).filter(pkgName => !pkgName.startsWith('.')); + +const jestConfig = { moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], - collectCoverage: true, + collectCoverage: false, collectCoverageFrom: [ - 'src/**/*.{ts,tsx}', + 'src/**/*.ts', + '!src/**/*.d.ts', '!**/node_modules/**', '!**/vendor/**', ], }; + +// 只对本仓库内的 pkg 做 mapping +jestConfig.moduleNameMapper = {}; +jestConfig.moduleNameMapper[`^@alilc/lowcode\\-(${pkgNames.join('|')})$`] = '<rootDir>/../$1/src'; + +module.exports = jestConfig; \ No newline at end of file diff --git a/packages/utils/package.json b/packages/utils/package.json index 8043a9e10..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", @@ -10,11 +10,11 @@ "module": "es/index.js", "scripts": { "test": "build-scripts test --config build.test.json", - "build": "build-scripts build --skip-demo" + "build": "build-scripts build" }, "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/cursor.css b/packages/utils/src/cursor.css new file mode 100644 index 000000000..e13da656e --- /dev/null +++ b/packages/utils/src/cursor.css @@ -0,0 +1,19 @@ +html.lc-cursor-dragging, +html.lc-cursor-dragging * { + cursor: move !important; +} + +html.lc-cursor-x-resizing, +html.lc-cursor-x-resizing * { + cursor: col-resize; +} + +html.lc-cursor-y-resizing, +html.lc-cursor-y-resizing * { + cursor: row-resize; +} + +html.lc-cursor-copy, +html.lc-cursor-copy * { + cursor: copy !important; +} diff --git a/packages/utils/src/cursor.less b/packages/utils/src/cursor.less deleted file mode 100644 index 30c890862..000000000 --- a/packages/utils/src/cursor.less +++ /dev/null @@ -1,15 +0,0 @@ -html.lc-cursor-dragging, html.lc-cursor-dragging * { - cursor: move !important; -} - -html.lc-cursor-x-resizing, html.lc-cursor-x-resizing * { - cursor: col-resize; -} - -html.lc-cursor-y-resizing, html.lc-cursor-y-resizing * { - cursor: row-resize; -} - -html.lc-cursor-copy, html.lc-cursor-copy * { - cursor: copy !important; -} diff --git a/packages/utils/src/cursor.ts b/packages/utils/src/cursor.ts index fea4bce65..c12ec64b9 100644 --- a/packages/utils/src/cursor.ts +++ b/packages/utils/src/cursor.ts @@ -1,4 +1,4 @@ -import './cursor.less'; +import './cursor.css'; export class Cursor { private states = new Set<string>(); diff --git a/packages/utils/src/is-react.ts b/packages/utils/src/is-react.ts index 1f17f9afc..07568db98 100644 --- a/packages/utils/src/is-react.ts +++ b/packages/utils/src/is-react.ts @@ -3,6 +3,7 @@ import { cloneEnumerableProperty } from './clone-enumerable-property'; const hasSymbol = typeof Symbol === 'function' && Symbol.for; const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0; +const REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3; export function isReactClass(obj: any): obj is ComponentClass<any> { return obj && obj.prototype && (obj.prototype.isReactComponent || obj.prototype instanceof Component); @@ -16,8 +17,16 @@ function isForwardRefType(obj: any): boolean { return obj?.$$typeof && obj?.$$typeof === REACT_FORWARD_REF_TYPE; } +function isMemoType(obj: any): boolean { + return obj?.$$typeof && obj.$$typeof === REACT_MEMO_TYPE; +} + export function isReactComponent(obj: any): obj is ComponentType<any> { - return obj && (isReactClass(obj) || typeof obj === 'function' || isForwardRefType(obj)); + if (!obj) { + return false; + } + + return Boolean(isReactClass(obj) || typeof obj === 'function' || isForwardRefType(obj) || isMemoType(obj)); } export function wrapReactClass(view: FunctionComponent) { 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/build-components/buildComponents.test.ts b/packages/utils/test/src/build-components/buildComponents.test.ts index 5662aa12c..e854890da 100644 --- a/packages/utils/test/src/build-components/buildComponents.test.ts +++ b/packages/utils/test/src/build-components/buildComponents.test.ts @@ -309,8 +309,14 @@ describe('build-component', () => { )) .toEqual({ Button: { - componentName: 'Component', - schema: {}, + componentsMap: [], + componentsTree: [ + { + componentName: 'Component', + schema: {}, + } + ], + version: "", }, }); }) diff --git a/packages/utils/test/src/is-react.test.ts b/packages/utils/test/src/is-react.test.ts new file mode 100644 index 000000000..74c88c933 --- /dev/null +++ b/packages/utils/test/src/is-react.test.ts @@ -0,0 +1,38 @@ +import React from "react"; +import { isReactComponent, wrapReactClass } from "../../src/is-react"; + +class reactDemo extends React.Component { + +} + +const reactMemo = React.memo(reactDemo); + +const reactForwardRef = React.forwardRef((props, ref): any => { + return ''; +}); + +describe('is-react-ut', () => { + it('isReactComponent', () => { + expect(isReactComponent(null)).toBeFalsy(); + expect(isReactComponent(() => {})).toBeTruthy(); + expect(isReactComponent({ + $$typeof: Symbol.for('react.memo') + })).toBeTruthy(); + expect(isReactComponent({ + $$typeof: Symbol.for('react.forward_ref') + })).toBeTruthy(); + expect(isReactComponent(reactDemo)).toBeTruthy(); + expect(isReactComponent(reactMemo)).toBeTruthy(); + expect(isReactComponent(reactForwardRef)).toBeTruthy(); + + }); + + it('wrapReactClass', () => { + const wrap = wrapReactClass(() => {}); + expect(isReactComponent(wrap)).toBeTruthy(); + + const fun = () => {}; + fun.displayName = 'mock'; + expect(wrapReactClass(fun).displayName).toBe('mock'); + }) +}) \ 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/build.json b/packages/workspace/build.json index bd5cf18dd..3e9260055 100644 --- a/packages/workspace/build.json +++ b/packages/workspace/build.json @@ -1,5 +1,5 @@ { "plugins": [ - "build-plugin-component" + "@alilc/build-plugin-lce" ] } diff --git a/packages/workspace/build.test.json b/packages/workspace/build.test.json index dcdc891e9..9cc30d746 100644 --- a/packages/workspace/build.test.json +++ b/packages/workspace/build.test.json @@ -1,6 +1,6 @@ { "plugins": [ - "build-plugin-component", + "@alilc/build-plugin-lce", "@alilc/lowcode-test-mate/plugin/index.ts" ] } diff --git a/packages/workspace/package.json b/packages/workspace/package.json index c1b1e25a5..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", @@ -9,17 +9,17 @@ "es" ], "scripts": { - "build": "build-scripts build --skip-demo", + "build": "build-scripts build", "test": "build-scripts test --config build.test.json", "test:cov": "build-scripts test --config build.test.json --jest-coverage" }, "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/context/base-context.ts b/packages/workspace/src/context/base-context.ts index e74ae2ffb..b359a6139 100644 --- a/packages/workspace/src/context/base-context.ts +++ b/packages/workspace/src/context/base-context.ts @@ -30,6 +30,7 @@ import { Common, Logger, Workspace, + Window, Canvas, } from '@alilc/lowcode-shell'; import { @@ -43,6 +44,7 @@ import { IPublicApiProject, IPublicApiSetters, IPublicApiSkeleton, + IPublicEnumPluginRegisterLevel, IPublicModelPluginContext, IPublicTypePluginMeta, } from '@alilc/lowcode-types'; @@ -99,7 +101,7 @@ export class BasicContext implements IBasicContext { preference: IPluginPreferenceMananger; workspace: IWorkspace; - constructor(innerWorkspace: IWorkspace, viewName: string, public editorWindow?: IEditorWindow) { + constructor(innerWorkspace: IWorkspace, viewName: string, readonly registerLevel: IPublicEnumPluginRegisterLevel, public editorWindow?: IEditorWindow) { const editor = new Editor(viewName, true); const innerSkeleton = new InnerSkeleton(editor, viewName); @@ -164,6 +166,10 @@ export class BasicContext implements IBasicContext { context.plugins = plugins; context.logger = new Logger({ level: 'warn', bizName: `plugin:${pluginName}` }); context.canvas = canvas; + if (editorWindow) { + context.editorWindow = new Window(editorWindow); + } + context.registerLevel = registerLevel; }, }; diff --git a/packages/workspace/src/context/view-context.ts b/packages/workspace/src/context/view-context.ts index 38a9e570f..ff4c12eee 100644 --- a/packages/workspace/src/context/view-context.ts +++ b/packages/workspace/src/context/view-context.ts @@ -1,12 +1,18 @@ import { computed, makeObservable, obx } from '@alilc/lowcode-editor-core'; -import { IPublicEditorViewConfig, IPublicTypeEditorView } from '@alilc/lowcode-types'; +import { IPublicEditorViewConfig, IPublicEnumPluginRegisterLevel, IPublicTypeEditorView } from '@alilc/lowcode-types'; import { flow } from 'mobx'; import { IWorkspace } from '../workspace'; -import { BasicContext } from './base-context'; +import { BasicContext, IBasicContext } from './base-context'; import { IEditorWindow } from '../window'; import { getWebviewPlugin } from '../inner-plugins/webview'; -export class Context extends BasicContext { +export interface IViewContext extends IBasicContext { + editorWindow: IEditorWindow; + + viewName: string; +} + +export class Context extends BasicContext implements IViewContext { viewName = 'editor-view'; instance: IPublicEditorViewConfig; @@ -17,10 +23,6 @@ export class Context extends BasicContext { @obx isInit: boolean = false; - @computed get active() { - return this._activate; - } - init = flow(function* (this: Context) { if (this.viewType === 'webview') { const url = yield this.instance?.url?.(); @@ -34,7 +36,7 @@ export class Context extends BasicContext { }); constructor(public workspace: IWorkspace, public editorWindow: IEditorWindow, public editorView: IPublicTypeEditorView, options: Object | undefined) { - super(workspace, editorView.viewName, editorWindow); + super(workspace, editorView.viewName, IPublicEnumPluginRegisterLevel.EditorView, editorWindow); this.viewType = editorView.viewType || 'editor'; this.viewName = editorView.viewName; this.instance = editorView(this.innerPlugins._getLowCodePluginContext({ @@ -43,6 +45,18 @@ export class Context extends BasicContext { makeObservable(this); } + @computed get active() { + return this._activate; + } + + onSimulatorRendererReady = (): Promise<void> => { + return new Promise((resolve) => { + this.project.onSimulatorRendererReady(() => { + resolve(); + }); + }); + }; + setActivate = (_activate: boolean) => { this._activate = _activate; this.innerHotkey.activate(this._activate); diff --git a/packages/workspace/src/index.ts b/packages/workspace/src/index.ts index 1f3b77a7a..6c437fad0 100644 --- a/packages/workspace/src/index.ts +++ b/packages/workspace/src/index.ts @@ -4,3 +4,4 @@ export * from './window'; export * from './layouts/workbench'; export { Resource } from './resource'; export type { IResource } from './resource'; +export type { IViewContext } from './context/view-context'; diff --git a/packages/workspace/src/layouts/workbench.less b/packages/workspace/src/layouts/workbench.less index 95574871a..c8c89d6f0 100644 --- a/packages/workspace/src/layouts/workbench.less +++ b/packages/workspace/src/layouts/workbench.less @@ -368,6 +368,9 @@ body { right: 0; bottom: 0; left: 0; + flex-direction: column; + display: flex; + align-content: stretch; } .engine-actionitem { diff --git a/packages/workspace/src/layouts/workbench.tsx b/packages/workspace/src/layouts/workbench.tsx index fe5ef846f..0c69f9717 100644 --- a/packages/workspace/src/layouts/workbench.tsx +++ b/packages/workspace/src/layouts/workbench.tsx @@ -47,7 +47,7 @@ export class Workbench extends Component<{ { workspace.windows.map(d => ( <WindowView - active={d.id === workspace.window.id} + active={d.id === workspace.window?.id} window={d} key={d.id} /> diff --git a/packages/workspace/src/resource.ts b/packages/workspace/src/resource.ts index 10cb7f0a1..4b695d036 100644 --- a/packages/workspace/src/resource.ts +++ b/packages/workspace/src/resource.ts @@ -1,5 +1,5 @@ import { ISkeleton } from '@alilc/lowcode-editor-skeleton'; -import { IPublicTypeEditorView, IPublicResourceData, IPublicResourceTypeConfig, IBaseModelResource } from '@alilc/lowcode-types'; +import { IPublicTypeEditorView, IPublicResourceData, IPublicResourceTypeConfig, IBaseModelResource, IPublicEnumPluginRegisterLevel } from '@alilc/lowcode-types'; import { Logger } from '@alilc/lowcode-utils'; import { BasicContext, IBasicContext } from './context/base-context'; import { ResourceType, IResourceType } from './resource-type'; @@ -39,7 +39,7 @@ export class Resource implements IResource { } get viewName() { - return this.resourceData.viewName || (this.resourceData as any).viewType; + return this.resourceData.viewName || (this.resourceData as any).viewType || this.defaultViewType; } get description() { @@ -70,12 +70,14 @@ export class Resource implements IResource { return this.context.innerSkeleton; } - get children(): IResource[] { - return this.resourceData?.children?.map(d => new Resource(d, this.workspace.getResourceType(d.resourceName || this.resourceType.name), this.workspace)) || []; + children: IResource[]; + + get config() { + return this.resourceData.config; } constructor(readonly resourceData: IPublicResourceData, readonly resourceType: IResourceType, readonly workspace: IWorkspace) { - this.context = new BasicContext(workspace, `resource-${resourceData.resourceName || resourceType.name}`); + this.context = new BasicContext(workspace, `resource-${resourceData.resourceName || resourceType.name}`, IPublicEnumPluginRegisterLevel.Resource); this.resourceTypeInstance = resourceType.resourceTypeModel(this.context.innerPlugins._getLowCodePluginContext({ pluginName: '', }), this.options); @@ -88,6 +90,7 @@ export class Resource implements IResource { if (!resourceType) { logger.error(`resourceType[${resourceType}] is unValid.`); } + this.children = this.resourceData?.children?.map(d => new Resource(d, this.workspace.getResourceType(d.resourceName || this.resourceType.name), this.workspace)) || []; } async init() { diff --git a/packages/workspace/src/window.ts b/packages/workspace/src/window.ts index 96707dcb8..6fb706632 100644 --- a/packages/workspace/src/window.ts +++ b/packages/workspace/src/window.ts @@ -1,23 +1,31 @@ import { uniqueId } from '@alilc/lowcode-utils'; import { createModuleEventBus, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core'; -import { Context } from './context/view-context'; +import { Context, IViewContext } from './context/view-context'; import { IWorkspace } from './workspace'; import { IResource } from './resource'; -import { IPublicTypeDisposable } from '../../types/es/shell/type/disposable'; -import { IPublicModelWindow } from '@alilc/lowcode-types'; +import { IPublicModelWindow, IPublicTypeDisposable } from '@alilc/lowcode-types'; interface IWindowCOnfig { title: string | undefined; options?: Object; viewType?: string | undefined; + sleep?: boolean; } -export interface IEditorWindow extends Omit<IPublicModelWindow<IResource>, 'changeViewType'> { +export interface IEditorWindow extends Omit<IPublicModelWindow<IResource>, 'changeViewType' | 'currentEditorView' | 'editorViews'> { readonly resource: IResource; - editorViews: Map<string, Context>; + editorViews: Map<string, IViewContext>; + + editorView: IViewContext; changeViewType: (name: string, ignoreEmit?: boolean) => void; + + initReady: boolean; + + sleep?: boolean; + + init(): void; } export class EditorWindow implements IEditorWindow { @@ -36,11 +44,13 @@ export class EditorWindow implements IEditorWindow { @obx initReady = false; + sleep: boolean | undefined; + constructor(readonly resource: IResource, readonly workspace: IWorkspace, private config: IWindowCOnfig) { makeObservable(this); - this.init(); this.title = config.title; this.icon = resource.icon; + this.sleep = config.sleep; } async importSchema(schema: any) { @@ -59,20 +69,39 @@ export class EditorWindow implements IEditorWindow { async save() { const value: any = {}; const editorViews = this.resource.editorViews; + if (!editorViews) { + return; + } for (let i = 0; i < editorViews.length; i++) { const name = editorViews[i].viewName; 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() { await this.initViewTypes(); await this.execViewTypesInit(); + Promise.all(Array.from(this.editorViews.values()).map((d) => d.onSimulatorRendererReady)).then(() => { + this.workspace.emitWindowRendererReady(); + }); this.url = await this.resource.url(); this.setDefaultViewType(); this.initReady = true; + this.workspace.checkWindowQueue(); + this.sleep = false; } initViewTypes = async () => { @@ -134,10 +163,15 @@ export class EditorWindow implements IEditorWindow { return; } + this.editorView.setActivate(true); + if (!ignoreEmit) { this.emitter.emit('window.change.view.type', name); + + if (this.id === this.workspace.window.id) { + this.workspace.emitChangeActiveEditorView(); + } } - this.editorView.setActivate(true); }; get project() { @@ -168,6 +202,10 @@ export class EditorWindow implements IEditorWindow { return this.editorView?.designer; } + get plugins() { + return this.editorView?.plugins; + } + get innerPlugins() { return this.editorView?.innerPlugins; } diff --git a/packages/workspace/src/workspace.ts b/packages/workspace/src/workspace.ts index d959c55eb..46c8c4a0e 100644 --- a/packages/workspace/src/workspace.ts +++ b/packages/workspace/src/workspace.ts @@ -1,6 +1,6 @@ import { IDesigner, ILowCodePluginManager, LowCodePluginManager } from '@alilc/lowcode-designer'; -import { createModuleEventBus, Editor, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core'; -import { IPublicApiPlugins, IPublicApiWorkspace, IPublicResourceList, IPublicTypeResourceType, IShellModelFactory } from '@alilc/lowcode-types'; +import { createModuleEventBus, Editor, IEditor, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core'; +import { IPublicApiPlugins, IPublicApiWorkspace, IPublicEnumPluginRegisterLevel, IPublicResourceList, IPublicTypeDisposable, IPublicTypeResourceType, IShellModelFactory } from '@alilc/lowcode-types'; import { BasicContext } from './context/base-context'; import { EditorWindow } from './window'; import type { IEditorWindow } from './window'; @@ -11,6 +11,10 @@ enum EVENT { CHANGE_WINDOW = 'change_window', CHANGE_ACTIVE_WINDOW = 'change_active_window', + + WINDOW_RENDER_READY = 'window_render_ready', + + CHANGE_ACTIVE_EDITOR_VIEW = 'change_active_editor_view', } const CHANGE_EVENT = 'resource.list.change'; @@ -19,10 +23,12 @@ export interface IWorkspace extends Omit<IPublicApiWorkspace< LowCodePluginManager, IEditorWindow >, 'resourceList' | 'plugins'> { - readonly registryInnerPlugin: (designer: IDesigner, editor: Editor, plugins: IPublicApiPlugins) => Promise<void>; + readonly registryInnerPlugin: (designer: IDesigner, editor: Editor, plugins: IPublicApiPlugins) => Promise<IPublicTypeDisposable>; readonly shellModelFactory: IShellModelFactory; + enableAutoOpenFirstWindow: boolean; + window: IEditorWindow; plugins: ILowCodePluginManager; @@ -30,11 +36,25 @@ export interface IWorkspace extends Omit<IPublicApiWorkspace< getResourceList(): IResource[]; getResourceType(resourceName: string): IResourceType; + + checkWindowQueue(): void; + + emitWindowRendererReady(): void; + + initWindow(): void; + + setActive(active: boolean): void; + + onChangeActiveEditorView(fn: () => void): IPublicTypeDisposable; + + emitChangeActiveEditorView(): void; } export class Workspace implements IWorkspace { context: BasicContext; + enableAutoOpenFirstWindow: boolean; + private emitter: IEventBus = createModuleEventBus('workspace'); private _isActive = false; @@ -69,33 +89,47 @@ export class Workspace implements IWorkspace { @obx.ref window: IEditorWindow; + windowQueue: { + name: string; + title: string; + options: Object; + viewType?: string; + }[] = []; + constructor( - readonly registryInnerPlugin: (designer: IDesigner, editor: Editor, plugins: IPublicApiPlugins) => Promise<void>, + readonly registryInnerPlugin: (designer: IDesigner, editor: IEditor, plugins: IPublicApiPlugins) => Promise<IPublicTypeDisposable>, readonly shellModelFactory: any, ) { - this.init(); + this.context = new BasicContext(this, '', IPublicEnumPluginRegisterLevel.Workspace); makeObservable(this); } - init() { - this.initWindow(); - this.context = new BasicContext(this, ''); - } - - initWindow() { - if (!this.defaultResourceType) { + checkWindowQueue() { + if (!this.windowQueue || !this.windowQueue.length) { return; } - const title = this.defaultResourceType.name; + + const windowInfo = this.windowQueue.shift(); + if (windowInfo) { + this.openEditorWindow(windowInfo.name, windowInfo.title, windowInfo.options, windowInfo.viewType); + } + } + + async initWindow() { + if (!this.defaultResourceType || this.enableAutoOpenFirstWindow === false) { + return; + } + const resourceName = this.defaultResourceType.name; const resource = new Resource({ - resourceName: title, + resourceName, options: {}, }, this.defaultResourceType, this); this.window = new EditorWindow(resource, this, { - title, + title: resource.title, }); + await this.window.init(); this.editorWindowMap.set(this.window.id, this.window); - this.windows.push(this.window); + this.windows = [...this.windows, this.window]; this.emitChangeWindow(); this.emitChangeActiveWindow(); } @@ -108,7 +142,7 @@ export class Workspace implements IWorkspace { const resourceType = new ResourceType(resourceTypeModel); this.resourceTypeMap.set(resourceTypeModel.resourceName, resourceType); - if (!this.window && this.defaultResourceType) { + if (!this.window && this.defaultResourceType && this._isActive) { this.initWindow(); } } @@ -129,6 +163,17 @@ export class Workspace implements IWorkspace { }; } + onWindowRendererReady(fn: () => void): IPublicTypeDisposable { + this.emitter.on(EVENT.WINDOW_RENDER_READY, fn); + return () => { + this.emitter.off(EVENT.WINDOW_RENDER_READY, fn); + }; + } + + emitWindowRendererReady() { + this.emitter.emit(EVENT.WINDOW_RENDER_READY); + } + getResourceType(resourceName: string): IResourceType { return this.resourceTypeMap.get(resourceName)!; } @@ -145,29 +190,44 @@ export class Workspace implements IWorkspace { } private remove(index: number) { + if (index < 0) { + return; + } const window = this.windows[index]; this.windows.splice(index, 1); if (this.window === window) { this.window = this.windows[index] || this.windows[index + 1] || this.windows[index - 1]; + if (this.window.sleep) { + this.window.init(); + } this.emitChangeActiveWindow(); } this.emitChangeWindow(); } - removeEditorWindow(resourceName: string) { - const index = this.windows.findIndex(d => (d.resource?.name === resourceName && d.title)); + removeEditorWindow(resourceName: string, title: string) { + const index = this.windows.findIndex(d => (d.resource?.name === resourceName && d.title === title)); this.remove(index); } - openEditorWindowById(id: string) { + async openEditorWindowById(id: string) { const window = this.editorWindowMap.get(id); if (window) { this.window = window; + if (window.sleep) { + await window.init(); + } this.emitChangeActiveWindow(); } } - openEditorWindow(name: string, title: string, options: Object, viewType?: string) { + async openEditorWindow(name: string, title: string, options: Object, viewType?: string, sleep?: boolean) { + if (this.window && !this.window?.initReady && !sleep) { + this.windowQueue.push({ + name, title, options, viewType, + }); + return; + } const resourceType = this.resourceTypeMap.get(name); if (!resourceType) { console.error(`${name} resourceType is not available`); @@ -176,6 +236,11 @@ export class Workspace implements IWorkspace { const filterWindows = this.windows.filter(d => (d.resource?.name === name && d.resource.title == title)); if (filterWindows && filterWindows.length) { this.window = filterWindows[0]; + if (!sleep && this.window.sleep) { + await this.window.init(); + } else { + this.checkWindowQueue(); + } this.emitChangeActiveWindow(); return; } @@ -184,13 +249,18 @@ export class Workspace implements IWorkspace { title, options, }, resourceType, this); - this.window = new EditorWindow(resource, this, { + const window = new EditorWindow(resource, this, { title, options, viewType, + sleep, }); - this.windows = [...this.windows, this.window]; - this.editorWindowMap.set(this.window.id, this.window); + this.windows = [...this.windows, window]; + this.editorWindowMap.set(window.id, window); + if (!sleep) { + this.window = window; + await this.window.init(); + } this.emitChangeWindow(); this.emitChangeActiveWindow(); } @@ -202,12 +272,24 @@ export class Workspace implements IWorkspace { }; } + onChangeActiveEditorView(fn: () => void) { + this.emitter.on(EVENT.CHANGE_ACTIVE_EDITOR_VIEW, fn); + return () => { + this.emitter.removeListener(EVENT.CHANGE_ACTIVE_EDITOR_VIEW, fn); + }; + } + + emitChangeActiveEditorView() { + this.emitter.emit(EVENT.CHANGE_ACTIVE_EDITOR_VIEW); + } + emitChangeWindow() { this.emitter.emit(EVENT.CHANGE_WINDOW); } emitChangeActiveWindow() { this.emitter.emit(EVENT.CHANGE_ACTIVE_WINDOW); + this.emitChangeActiveEditorView(); } onChangeActiveWindow(fn: () => void) {