diff --git a/.eslintrc.js b/.eslintrc.js index 1455cdfd4..1ec3834e6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -41,5 +41,16 @@ module.exports = { '@typescript-eslint/dot-notation': 0, // for lint performance '@typescript-eslint/restrict-plus-operands': 0, // for lint performance 'no-unexpected-multiline': 1, + 'no-multiple-empty-lines': ['error', { "max": 1 }], + 'lines-around-comment': ['error', { + "beforeBlockComment": true, + "afterBlockComment": false, + "afterLineComment": false, + "allowBlockStart": true, + }], + "@typescript-eslint/member-ordering": [ + "error", + { "default": ["signature", "field", "constructor", "method"] } + ], } -}; +}; \ No newline at end of file diff --git a/docs/docs/api/index.md b/docs/docs/api/index.md index 2bd735c84..993c39e88 100644 --- a/docs/docs/api/index.md +++ b/docs/docs/api/index.md @@ -44,3 +44,7 @@ sidebar_position: 0 2. 事件(events)的命名格式为:on[Will|Did]VerbNoun?,参考 [https://code.visualstudio.com/api/references/vscode-api#events](https://code.visualstudio.com/api/references/vscode-api#events) 3. 基于 Disposable 模式,对于事件的绑定、快捷键的绑定函数,返回值则是解绑函数 4. 对于属性的导出,统一用 .xxx 的 getter 模式,(尽量)不使用 .getXxx() + +## experimental + +说明此模块处于公测阶段, API 可能会发生改变. \ No newline at end of file diff --git a/docs/docs/api/model/detecting.md b/docs/docs/api/model/detecting.md index 2cc0ea18d..15e1cfca7 100644 --- a/docs/docs/api/model/detecting.md +++ b/docs/docs/api/model/detecting.md @@ -9,30 +9,65 @@ sidebar_position: 6 画布节点悬停模型 -## 方法签名 -### capture - -capture(id: string) - -hover 指定节点 - -### release - -release(id: string) - -hover 离开指定节点 - -### leave - -leave() - -清空 hover 态 +## 变量 ### current + 当前 hover 的节点 +`@type {IPublicModelNode | null}` + +相关类型:[IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts) + **@since v1.0.16** +### enable + +是否启用 + +`@type {boolean}` + + +## 方法签名 +### capture + +hover 指定节点 + +```typescript +/** + * hover 指定节点 + * capture node with nodeId + * @param id 节点 id + */ +capture(id: string): void; +``` + +### release + +hover 离开指定节点 + +```typescript +/** + * hover 离开指定节点 + * release node with nodeId + * @param id 节点 id + */ +release(id: string): void; +``` + +### leave + +清空 hover 态 + +```typescript +/** + * 清空 hover 态 + * clear all hover state + */ +leave(): void; +``` + +## 事件 ### onDetectingChange hover 节点变化事件 @@ -42,6 +77,11 @@ hover 节点变化事件 * set callback which will be called when hovering object changed. * @since v1.1.0 */ -onDetectingChange(fn: (node: IPublicModelNode) => void): () => void; +onDetectingChange(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable; ``` + +相关类型: +- [IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts) +- [IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) + **@since v1.1.0** \ No newline at end of file diff --git a/docs/docs/api/model/document-model.md b/docs/docs/api/model/document-model.md index c7b35c4d6..9e5064371 100644 --- a/docs/docs/api/model/document-model.md +++ b/docs/docs/api/model/document-model.md @@ -334,7 +334,7 @@ onChangeNodeVisible(fn: (node: IPublicModelNode, visible: boolean) => void): voi ### onChangeNodeChildren -onChangeNodeChildren(fn: (info?: IPublicOnChangeOptions) => void) +onChangeNodeChildren(fn: (info?: IPublicTypeOnChangeOptions) => void) 当前 document 的节点 children 变更事件 diff --git a/docs/docs/api/model/history.md b/docs/docs/api/model/history.md index 5864febea..db6800850 100644 --- a/docs/docs/api/model/history.md +++ b/docs/docs/api/model/history.md @@ -12,47 +12,113 @@ sidebar_position: 5 ## 方法签名 ### go -go(cursor: number) - 历史记录跳转到指定位置 +```typescript +/** + * 历史记录跳转到指定位置 + * go to a specific history + * @param cursor + */ +go(cursor: number): void; +``` + ### back -back() - 历史记录后退 +```typescript +/** + * 历史记录后退 + * go backward in history + */ +back(): void; +``` + ### forward forward() 历史记录前进 + +```typescript +/** + * 历史记录前进 + * go forward in history + */ +forward(): void; +``` + ### savePoint -savePoint() - 保存当前状态 -### isSavePoint -isSavePoint() +```typescript +/** + * 保存当前状态 + * do save current change as a record in history + */ +savePoint(): void; +``` + +### isSavePoint 当前是否是「保存点」,即是否有状态变更但未保存 +```typescript +/** + * 当前是否是「保存点」,即是否有状态变更但未保存 + * check if there is unsaved change for history + */ +isSavePoint(): boolean; +``` + ### getState -getState() - 获取 state,判断当前是否为「可回退」、「可前进」的状态 +```typescript +/** + * 获取 state,判断当前是否为「可回退」、「可前进」的状态 + * get flags in number which indicat current change state + * + * | 1 | 1 | 1 | + * | -------- | -------- | -------- | + * | modified | redoable | undoable | + * eg: + * 7 means : modified && redoable && undoable + * 5 means : modified && undoable + */ +getState(): number; +``` + ## 事件 ### onChangeState -onChangeState(func: () => any) - 监听 state 变更事件 +```typescript +/** + * 监听 state 变更事件 + * monitor on stateChange event + * @param func + */ +onChangeState(func: () => any): IPublicTypeDisposable; +``` + +相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) + ### onChangeCursor -onChangeCursor(func: () => any) - 监听历史记录游标位置变更事件 + +```typescript +/** + * 监听历史记录游标位置变更事件 + * monitor on cursorChange event + * @param func + */ +onChangeCursor(func: () => any): IPublicTypeDisposable; +``` + +相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) \ No newline at end of file diff --git a/docs/docs/api/model/plugin-instance.md b/docs/docs/api/model/plugin-instance.md index 9ca872a4f..8f764c68b 100644 --- a/docs/docs/api/model/plugin-instance.md +++ b/docs/docs/api/model/plugin-instance.md @@ -1,5 +1,5 @@ --- -title: plugin-instance +title: PluginInstance sidebar_position: 12 --- diff --git a/docs/docs/api/model/prop.md b/docs/docs/api/model/prop.md index b7832ef9f..e619cdc33 100644 --- a/docs/docs/api/model/prop.md +++ b/docs/docs/api/model/prop.md @@ -15,39 +15,94 @@ sidebar_position: 3 id +`@type {string}` + ### key key 值 +`@type {string | number | undefined}` + ### path 返回当前 prop 的路径 +`@type {string[]}` + ### node 返回所属的节点实例 +`@type {IPublicModelNode | null}` + +相关类型:[IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts) + +### slotNode + +当本 prop 代表一个 Slot 时,返回对应的 slotNode + +`@type {IPublicModelNode | undefined | null}` + +相关类型:[IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts) + + ## 方法签名 ### setValue -setValue(val: CompositeValue) - 设置值 +```typescript +/** + * 设置值 + * set value for this prop + * @param val + */ +setValue(val: IPublicTypeCompositeValue): void; +``` + +相关类型:[IPublicTypeCompositeValue](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/composite-value.ts) + ### getValue -getValue() - 获取值 +```typescript +/** + * 获取值 + * get value of this prop + */ +getValue(): any; +``` + ### remove + 移除值 +```typescript +/** + * 移除值 + * remove value of this prop + * @since v1.0.16 + */ +remove(): void; +``` + **@since v1.0.16** ### exportSchema -exportSchema(stage: IPublicEnumTransformStage = IPublicEnumTransformStage.Render) +导出值 -导出值 \ No newline at end of file +```typescript +/** + * 导出值 + * export schema + * @param stage + */ +exportSchema(stage: IPublicEnumTransformStage): IPublicTypeCompositeValue; +``` + +相关类型: +- [IPublicEnumTransformStage](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/enum/transform-stage.ts) +- [IPublicTypeCompositeValue](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/composite-value.ts) \ No newline at end of file diff --git a/docs/docs/api/model/props.md b/docs/docs/api/model/props.md index 5956a14b7..f76919d49 100644 --- a/docs/docs/api/model/props.md +++ b/docs/docs/api/model/props.md @@ -13,46 +13,148 @@ sidebar_position: 4 ### id id + +`@type {string}` + + ### path 返回当前 props 的路径 + +`@type {string[]}` + + ### node 返回当前属性集所属的节点实例 +`@type {IPublicModelNode | null}` + +相关类型:[IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts) + ## 方法签名 ### getProp -getProp(path: string): Prop | null - 获取指定 path 的属性模型实例 +```typescript +/** + * 获取指定 path 的属性模型实例 + * get prop by path + * @param path 属性路径,支持 a / a.b / a.0 等格式 + */ +getProp(path: string): IPublicModelProp | null; +``` + +相关类型:[IPublicModelProp](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/prop.ts) + ### getPropValue -getPropValue(path: string) - 获取指定 path 的属性模型实例值 +```typescript +/** + * 获取指定 path 的属性模型实例值 + * get value of prop by path + * @param path 属性路径,支持 a / a.b / a.0 等格式 + */ +getPropValue(path: string): any; +``` + ### getExtraProp -getExtraProp(path: string): Prop | null - 获取指定 path 的属性模型实例,注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 +```typescript +/** + * 获取指定 path 的属性模型实例, + * 注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 + * get extra prop by path + * @param path 属性路径,支持 a / a.b / a.0 等格式 + */ +getExtraProp(path: string): IPublicModelProp | null; +``` + +相关类型:[IPublicModelProp](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/prop.ts) + ### getExtraPropValue -getExtraPropValue(path: string) - 获取指定 path 的属性模型实例值,注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 +```typescript +/** + * 获取指定 path 的属性模型实例值 + * 注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 + * get value of extra prop by path + * @param path 属性路径,支持 a / a.b / a.0 等格式 + */ +getExtraPropValue(path: string): any; +``` + ### setPropValue -setPropValue(path: string, value: CompositeValue) - 设置指定 path 的属性模型实例值 +```typescript +/** + * 设置指定 path 的属性模型实例值 + * set value of prop by path + * @param path 属性路径,支持 a / a.b / a.0 等格式 + * @param value 值 + */ +setPropValue(path: string, value: IPublicTypeCompositeValue): void; +``` + +相关类型:[IPublicTypeCompositeValue](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/composite-value.ts) + ### setExtraPropValue -setExtraPropValue(path: string, value: CompositeValue) +设置指定 path 的属性模型实例值 -设置指定 path 的属性模型实例值 \ No newline at end of file +```typescript +/** + * 设置指定 path 的属性模型实例值 + * set value of extra prop by path + * @param path 属性路径,支持 a / a.b / a.0 等格式 + * @param value 值 + */ +setExtraPropValue(path: string, value: IPublicTypeCompositeValue): void; +``` + +相关类型:[IPublicTypeCompositeValue](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/composite-value.ts) + + +### has + +当前 props 是否包含某 prop + +```typescript +/** + * 当前 props 是否包含某 prop + * check if the specified key is existing or not. + * @param key + * @since v1.1.0 + */ +has(key: string): boolean; +``` + +**@since v1.1.0** + +### add + +添加一个 prop + +```typescript +/** + * 添加一个 prop + * add a key with given value + * @param value + * @param key + * @since v1.1.0 + */ +add(value: IPublicTypeCompositeValue, key?: string | number | undefined): any; +``` + +相关类型:[IPublicTypeCompositeValue](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/composite-value.ts) + +**@since v1.1.0** \ No newline at end of file diff --git a/docs/docs/api/model/resource.md b/docs/docs/api/model/resource.md new file mode 100644 index 000000000..33a6e3119 --- /dev/null +++ b/docs/docs/api/model/resource.md @@ -0,0 +1,46 @@ +--- +title: Resource +sidebar_position: 12 +--- + +> **[@experimental](./#experimental)**
+> **@types** [IPublicModelWindow](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/resource.ts)
+> **@since** v1.1.0 + +## 变量 + +### title + +资源标题 + +`@type {string}` + +### name + +资源名字 + +`@type {string}` + +### type + +资源类型 + +`@type {string}` + +### category + +资源分类 + +`@type {string}` + +### icon + +资源 icon + +`@type {ReactElement}` + +### options + +资源配置信息 + +`@type {Object}` \ No newline at end of file diff --git a/docs/docs/api/model/selection.md b/docs/docs/api/model/selection.md index 77b33fa60..529a3e5a2 100644 --- a/docs/docs/api/model/selection.md +++ b/docs/docs/api/model/selection.md @@ -14,49 +14,110 @@ sidebar_position: 6 返回选中的节点 id +`@type {string[]}` + +### node +返回选中的节点(如多个节点只返回第一个) + +`@type {IPublicModelNode | null}` + +相关类型:[IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts) + +**@since v1.1.0** + ## 方法签名 ### select -select(id: string) - 选中指定节点(覆盖方式) +```typescript +/** +* 选中指定节点(覆盖方式) +* select node with id, this will override current selection +* @param id +*/ +select(id: string): void; +``` + ### selectAll -selectAll(ids: string[]) - 批量选中指定节点们 +```typescript +/** +* 批量选中指定节点们 +* select node with ids, this will override current selection +* +* @param ids +*/ +selectAll(ids: string[]): void; +``` + ### remove -remove(id: string) - **取消选中**选中的指定节点,不会删除组件 +```typescript +/** +* 移除选中的指定节点 +* remove node from selection with node id +* @param id +*/ +remove(id: string): void; +``` + ### clear -clear() - **取消选中**所有选中节点,不会删除组件 +```typescript +/** +* 清除所有选中节点 +* clear current selection +*/ +clear(): void; +``` + ### has -has(id: string) - 判断是否选中了指定节点 +```typescript +/** +* 判断是否选中了指定节点 +* check if node with specific id is selected +* @param id +*/ +has(id: string): boolean; +``` + ### add -add(id: string) - 选中指定节点(增量方式) +```typescript +/** +* 选中指定节点(增量方式) +* add node with specific id to selection +* @param id +*/ +add(id: string): void; +``` + ### getNodes -getNodes() - 获取选中的节点实例 +```typescript +/** +* 获取选中的节点实例 +* get selected nodes +*/ +getNodes(): IPublicModelNode[]; +``` + +相关类型:[IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts) + ### getTopNodes 获取选区的顶层节点 例如选中的节点为: @@ -67,19 +128,34 @@ getNodes() getNodes 返回的是 [DivA、ChildrenA、DivB],getTopNodes 返回的是 [DivA、DivB],其中 ChildrenA 由于是二层节点,getTopNodes 不会返回 +```typescript +/** +* 获取选区的顶层节点 +* get seleted top nodes +* for example: +* getNodes() returns [A, subA, B], then +* getTopNodes() will return [A, B], subA will be removed +* @since v1.0.16 +*/ +getTopNodes(includeRoot?: boolean): IPublicModelNode[]; +``` + **@since v1.0.16** +## 事件 ### onSelectionChange 注册 selection 变化事件回调 ```typescript /** - * 注册 selection 变化事件回调 - * set callback which will be called when selection is changed - * @since v1.1.0 - */ -onSelectionChange(fn: (ids: string[]) => void): () => void; +* 注册 selection 变化事件回调 +* set callback which will be called when selection is changed +* @since v1.1.0 +*/ +onSelectionChange(fn: (ids: string[]) => void): IPublicTypeDisposable; ``` +相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) + **@since v1.1.0** \ No newline at end of file diff --git a/docs/docs/api/model/window.md b/docs/docs/api/model/window.md index 6a5413883..f70c33e4d 100644 --- a/docs/docs/api/model/window.md +++ b/docs/docs/api/model/window.md @@ -3,6 +3,7 @@ title: Window sidebar_position: 12 --- +> **[@experimental](./#experimental)**
> **@types** [IPublicModelWindow](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/window.ts)
> **@since** v1.1.0 @@ -17,13 +18,25 @@ sidebar_position: 12 窗口唯一 id +`@type {string}` + ### title 窗口标题 -### resourceName +`@type {string}` -窗口资源名字 +### icon + +`@type {ReactElement}` + +### resource + +窗口对应资源 + +`@type {IPublicModelResource}` + +关联模型 [IPublicModelResource](./resource) ## 方法签名 diff --git a/docs/docs/api/plugins.md b/docs/docs/api/plugins.md index 1b20bdabe..9b1993b10 100644 --- a/docs/docs/api/plugins.md +++ b/docs/docs/api/plugins.md @@ -222,7 +222,7 @@ your-plugin/package.json } ``` 转换后的结构: -```json +```typescript const debug = (ctx: IPublicModelPluginContext, options: any) => { return {}; } diff --git a/docs/docs/api/workspace.md b/docs/docs/api/workspace.md index 5a9ad2a44..138693cfa 100644 --- a/docs/docs/api/workspace.md +++ b/docs/docs/api/workspace.md @@ -3,10 +3,10 @@ title: workspace - 应用级 API sidebar_position: 12 --- +> **[@experimental](./#experimental)**
> **@types** [IPublicApiWorkspace](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/workspace.ts)
> **@since** v1.1.0 - ## 模块简介 通过该模块可以开发应用级低代码设计器。 @@ -47,6 +47,16 @@ get window(): IPublicModelWindow[] 关联模型 [IPublicModelWindow](./model/window) +### resourceList + +当前设计器的资源列表数据 + +``` +get resourceList(): IPublicModelResource; +``` + +关联模型 [IPublicModelResource](./model/resource) + ## 方法签名 ### registerResourceType @@ -54,10 +64,10 @@ get window(): IPublicModelWindow[] ```typescript /** 注册资源 */ -registerResourceType(resourceName: string, resourceType: 'editor', options: IPublicResourceOptions): void; +registerResourceType(resourceTypeModel: IPublicTypeResourceType): void; ``` -相关类型:[IPublicResourceOptions](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/resource-options.ts) +相关类型:[IPublicTypeResourceType](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/resource-type.ts) ### onChangeWindows @@ -74,3 +84,55 @@ active 窗口变更事件 ```typescript function onChangeActiveWindow(fn: () => void): void; ``` + +### setResourceList + +设置设计器资源列表数据 + +```typescript +setResourceList(resourceList: IPublicResourceList) {} +``` + +相关类型:[IPublicResourceOptions](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/resource-options.ts) + +### onResourceListChange + +设计器资源列表数据变更事件 + +```typescript +onResourceListChange(fn: (resourceList: IPublicResourceList): void): (): void; +``` + +相关类型:[IPublicResourceOptions](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/resource-options.ts) + +### openEditorWindow + +打开视图窗口 + +```typescript +openEditorWindow(resourceName: string, title: string, options: Object, viewName?: string): void; +``` + +### openEditorWindowById + +通过视图 id 打开窗口 + +```typescript +openEditorWindowById(id: string): void; +``` + +### removeEditorWindow + +移除视图窗口 + +```typescript +removeEditorWindow(resourceName: string, title: string): void; +``` + +### removeEditorWindowById + +通过视图 id 移除窗口 + +```typescript +removeEditorWindowById(id: string): void; +``` \ No newline at end of file diff --git a/docs/docs/guide/design/editor.md b/docs/docs/guide/design/editor.md index b04cb4eb6..fcead2922 100644 --- a/docs/docs/guide/design/editor.md +++ b/docs/docs/guide/design/editor.md @@ -313,7 +313,7 @@ simulator-renderer 通过调用 host 的方法,将 schema、components 等参 - 被拖拽对象 - `DragObject` - 拖拽到的目标位置 - `DropLocation` -- 拖拽感应区 - `ISensor` +- 拖拽感应区 - `IPublicModelSensor` - 定位事件 - `LocateEvent` ##### Sensor diff --git a/docs/package.json b/docs/package.json index 797a1e3e0..4102563dc 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-engine-docs", - "version": "1.0.13", + "version": "1.0.14", "description": "低代码引擎版本化文档", "license": "MIT", "files": [ diff --git a/modules/code-generator/package.json b/modules/code-generator/package.json index 19c270f2a..97863584c 100644 --- a/modules/code-generator/package.json +++ b/modules/code-generator/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-code-generator", - "version": "1.0.7-beta.5", + "version": "1.0.7", "description": "出码引擎 for LowCode Engine", "license": "MIT", "main": "lib/index.js", diff --git a/modules/code-generator/src/generator/ProjectBuilder.ts b/modules/code-generator/src/generator/ProjectBuilder.ts index 8f5405882..819f37c5c 100644 --- a/modules/code-generator/src/generator/ProjectBuilder.ts +++ b/modules/code-generator/src/generator/ProjectBuilder.ts @@ -40,6 +40,11 @@ export interface ProjectBuilderInitOptions { inStrictMode?: boolean; /** 一些额外的上下文数据 */ extraContextData?: Record; + /** + * Hook which is used to customize original options, we can reorder/add/remove plugins/processors + * of the existing solution. + */ + customizeBuilderOptions?(originalOptions: ProjectBuilderInitOptions): ProjectBuilderInitOptions; } export class ProjectBuilder implements IProjectBuilder { @@ -67,16 +72,21 @@ export class ProjectBuilder implements IProjectBuilder { /** 一些额外的上下文数据 */ readonly extraContextData: IContextData; - constructor({ - template, - plugins, - postProcessors, - schemaParser = new SchemaParser(), - projectPreProcessors = [], - projectPostProcessors = [], - inStrictMode = false, - extraContextData = {}, - }: ProjectBuilderInitOptions) { + constructor(builderOptions: ProjectBuilderInitOptions) { + let customBuilderOptions = builderOptions; + if (typeof builderOptions.customizeBuilderOptions === 'function') { + customBuilderOptions = builderOptions.customizeBuilderOptions(builderOptions); + } + const { + template, + plugins, + postProcessors, + schemaParser = new SchemaParser(), + projectPreProcessors = [], + projectPostProcessors = [], + inStrictMode = false, + extraContextData = {}, + } = customBuilderOptions; this.template = template; this.plugins = plugins; this.postProcessors = postProcessors; diff --git a/modules/code-generator/src/parser/SchemaParser.ts b/modules/code-generator/src/parser/SchemaParser.ts index 8c563ba16..3108fee47 100644 --- a/modules/code-generator/src/parser/SchemaParser.ts +++ b/modules/code-generator/src/parser/SchemaParser.ts @@ -5,7 +5,7 @@ import changeCase from 'change-case'; import { IPublicTypeUtilItem, - NodeDataType, + IPublicTypeNodeDataType, IPublicTypeNodeSchema, IPublicTypeContainerSchema, IPublicTypeProjectSchema, @@ -81,7 +81,7 @@ function processChildren(schema: IPublicTypeNodeSchema): void { if (nodeProps.children) { if (!schema.children) { // eslint-disable-next-line no-param-reassign - schema.children = nodeProps.children as NodeDataType; + schema.children = nodeProps.children as IPublicTypeNodeDataType; } else { let _children: IPublicTypeNodeData[] = []; @@ -331,7 +331,7 @@ export class SchemaParser implements ISchemaParser { }; } - getComponentNames(children: NodeDataType): string[] { + getComponentNames(children: IPublicTypeNodeDataType): string[] { return handleSubNodes( children, { diff --git a/modules/code-generator/src/solutions/icejs.ts b/modules/code-generator/src/solutions/icejs.ts index 85ce7c1f5..1b3dec4af 100644 --- a/modules/code-generator/src/solutions/icejs.ts +++ b/modules/code-generator/src/solutions/icejs.ts @@ -91,6 +91,7 @@ export default function createIceJsProjectBuilder( packageJSON: [icejs.plugins.packageJSON()], }, postProcessors: [prettier()], + customizeBuilderOptions: options?.customizeBuilderOptions, }); } diff --git a/modules/code-generator/src/solutions/rax-app.ts b/modules/code-generator/src/solutions/rax-app.ts index c2b3adac5..f7e903835 100644 --- a/modules/code-generator/src/solutions/rax-app.ts +++ b/modules/code-generator/src/solutions/rax-app.ts @@ -71,6 +71,7 @@ export default function createRaxProjectBuilder( packageJSON: [raxApp.plugins.packageJSON(options)], }, postProcessors: [prettier()], + customizeBuilderOptions: options?.customizeBuilderOptions, }); } diff --git a/modules/code-generator/src/types/core.ts b/modules/code-generator/src/types/core.ts index b2c110823..39e4c32ba 100644 --- a/modules/code-generator/src/types/core.ts +++ b/modules/code-generator/src/types/core.ts @@ -5,7 +5,7 @@ import { IPublicTypeCompositeObject, ResultDir, ResultFile, - NodeDataType, + IPublicTypeNodeDataType, IPublicTypeProjectSchema, IPublicTypeJSExpression, IPublicTypeJSFunction, @@ -14,6 +14,7 @@ import { import { IParseResult } from './intermediate'; import { IScopeBindings } from '../utils/ScopeBindings'; +import type { ProjectBuilderInitOptions } from '../generator/ProjectBuilder'; export enum FileType { CSS = 'css', @@ -65,7 +66,10 @@ export interface ICodeStruct extends IBaseCodeStruct { /** 上下文数据,用来在插件之间共享一些数据 */ export interface IContextData extends IProjectBuilderOptions { - /** 是否使用了 Ref 的 API (this.$/this.$$) */ + + /** + * 是否使用了 Ref 的 API (this.$/this.$$) + * */ useRefApi?: boolean; /** @@ -108,6 +112,7 @@ export interface IModuleBuilder { * @interface ICodeGenerator */ export interface ICodeGenerator { + /** * 出码接口,把 Schema 转换成代码文件系统描述 * @@ -138,30 +143,36 @@ export interface IProjectPlugins { } export interface IProjectBuilderOptions { - /** 是否处于严格模式(默认: 否) */ + + /** 是否处于严格模式 (默认:否) */ inStrictMode?: boolean; /** * 是否要容忍对 JSExpression 求值时的异常 * 默认:true - * 注: 如果容忍异常,则会在求值时包裹 try-catch 块, + * 注:如果容忍异常,则会在求值时包裹 try-catch 块, * catch 到异常时默认会抛出一个 CustomEvent 事件里面包含异常信息和求值的表达式 */ tolerateEvalErrors?: boolean; /** * 容忍异常的时候的的错误处理语句块 - * 默认: 无 + * 默认:无 * 您可以设置为一个语句块,比如: * window.dispatchEvent(new CustomEvent('lowcode-eval-error', { error, expr })) * * 一般可以结合埋点监控模块用来监控求值异常 * - * 其中: + * 其中: * - error: 异常信息 * - expr: 求值的表达式 */ evalErrorsHandler?: string; + /** + * Hook which is used to customize original options, we can reorder/add/remove plugins/processors + * of the existing solution. + */ + customizeBuilderOptions?(originalOptions: ProjectBuilderInitOptions): ProjectBuilderInitOptions; } export interface IProjectBuilder { @@ -205,7 +216,7 @@ type CompositeTypeGenerator = | BaseGenerator | Array>; -export type NodeGenerator = (nodeItem: NodeDataType, scope: IScope) => T; +export type NodeGenerator = (nodeItem: IPublicTypeNodeDataType, scope: IScope) => T; // FIXME: 在新的实现中,添加了第一参数 this: CustomHandlerSet 作为上下文。究其本质 // scopeBindings?: IScopeBindings; diff --git a/modules/code-generator/src/utils/nodeToJSX.ts b/modules/code-generator/src/utils/nodeToJSX.ts index bb03523c0..73dba862c 100644 --- a/modules/code-generator/src/utils/nodeToJSX.ts +++ b/modules/code-generator/src/utils/nodeToJSX.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; import { pipe } from 'fp-ts/function'; -import { IPublicTypeNodeSchema, isNodeSchema, NodeDataType, IPublicTypeCompositeValue } from '@alilc/lowcode-types'; +import { IPublicTypeNodeSchema, isNodeSchema, IPublicTypeNodeDataType, IPublicTypeCompositeValue } from '@alilc/lowcode-types'; import { IScope, @@ -365,7 +365,7 @@ export function generateReactExprInJS( const handleChildren = (v: string[]) => v.join(''); export function createNodeGenerator(cfg: NodeGeneratorConfig = {}): NodeGenerator { - const generateNode = (nodeItem: NodeDataType, scope: IScope): string => { + const generateNode = (nodeItem: IPublicTypeNodeDataType, scope: IScope): string => { if (_.isArray(nodeItem)) { const resList = nodeItem.map((n) => generateNode(n, scope)); return handleChildren(resList); @@ -390,7 +390,7 @@ export function createNodeGenerator(cfg: NodeGeneratorConfig = {}): NodeGenerato return `{${valueStr}}`; }; - return (nodeItem: NodeDataType, scope: IScope) => unwrapJsExprQuoteInJsx(generateNode(nodeItem, scope)); + return (nodeItem: IPublicTypeNodeDataType, scope: IScope) => unwrapJsExprQuoteInJsx(generateNode(nodeItem, scope)); } const defaultReactGeneratorConfig: NodeGeneratorConfig = { diff --git a/packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx b/packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx index 710ba3e53..f316e1c7a 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx +++ b/packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx @@ -6,7 +6,6 @@ import { getClosestNode } from '@alilc/lowcode-utils'; import { intl } from '../../locale'; import { BuiltinSimulatorHost } from '../host'; - export class BorderDetectingInstance extends PureComponent<{ title: IPublicTypeTitleContent; rect: DOMRect | null; @@ -77,11 +76,9 @@ export class BorderDetecting extends Component<{ host: BuiltinSimulatorHost }> { const { host } = this.props; const { current } = this; - const canHoverHook = current?.componentMeta.getMetadata()?.configure.advanced?.callbacks?.onHoverHook; const canHover = (canHoverHook && typeof canHoverHook === 'function') ? canHoverHook(current.internalToShellNode()) : true; - if (!canHover || !current || host.viewport.scrolling || host.liveEditing.editing) { return null; } diff --git a/packages/designer/src/builtin-simulator/host.ts b/packages/designer/src/builtin-simulator/host.ts index 5dee3f246..2171050c8 100644 --- a/packages/designer/src/builtin-simulator/host.ts +++ b/packages/designer/src/builtin-simulator/host.ts @@ -57,7 +57,7 @@ import { IPublicEnumTransitionType, IPublicEnumDragObjectType, IPublicTypeDragNodeObject, - NodeInstance, + IPublicTypeNodeInstance, IPublicTypeComponentInstance, IPublicTypeLocationChildrenDetail, IPublicTypeLocationDetailType, @@ -173,7 +173,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost | null { + ): IPublicTypeNodeInstance | null { return this.renderer?.getClosestNodeInstance(from, specId) || null; } @@ -1028,7 +1027,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost | null { + getNodeInstanceFromElement(target: Element | null): IPublicTypeNodeInstance | null { if (!target) { return null; } @@ -1111,14 +1110,14 @@ export class BuiltinSimulatorHost implements ISimulatorHost | undefined; + let nodeInstance: IPublicTypeNodeInstance | undefined; if (target) { const ref = this.getNodeInstanceFromElement(target); 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 a054d1723..e92e266ee 100644 --- a/packages/designer/src/builtin-simulator/live-editing/live-editing.ts +++ b/packages/designer/src/builtin-simulator/live-editing/live-editing.ts @@ -214,7 +214,6 @@ function selectRange(doc: Document, range: Range) { } } - function queryPropElement(rootElement: HTMLElement, targetElement: HTMLElement, selector?: string) { if (!selector) { return null; diff --git a/packages/designer/src/builtin-simulator/renderer.ts b/packages/designer/src/builtin-simulator/renderer.ts index e2c92f3b6..be32b6be6 100644 --- a/packages/designer/src/builtin-simulator/renderer.ts +++ b/packages/designer/src/builtin-simulator/renderer.ts @@ -1,5 +1,5 @@ import { Component } from '../simulator'; -import { IPublicTypeNodeSchema, IPublicTypeComponentInstance, NodeInstance } from '@alilc/lowcode-types'; +import { IPublicTypeNodeSchema, IPublicTypeComponentInstance, IPublicTypeNodeInstance } from '@alilc/lowcode-types'; export interface BuiltinSimulatorRenderer { readonly isSimulatorRenderer: true; @@ -8,7 +8,7 @@ export interface BuiltinSimulatorRenderer { getClosestNodeInstance( from: IPublicTypeComponentInstance, nodeId?: string, - ): NodeInstance | null; + ): IPublicTypeNodeInstance | null; findDOMNodes(instance: IPublicTypeComponentInstance): Array | null; getClientRects(element: Element | Text): DOMRect[]; setNativeSelection(enableFlag: boolean): void; diff --git a/packages/designer/src/designer/clipboard.ts b/packages/designer/src/designer/clipboard.ts index 03546f11e..a43b51b0e 100644 --- a/packages/designer/src/designer/clipboard.ts +++ b/packages/designer/src/designer/clipboard.ts @@ -18,23 +18,8 @@ function getDataFromPasteEvent(event: ClipboardEvent) { }; } } catch (error) { - /* - const html = clipboardData.getData('text/html'); - if (html !== '') { - // TODO: clear the html - return { - code: '
', - maps: {}, - }; - } - */ // TODO: open the parser implement return { }; - /* - return { - code: clipboardData.getData('text/plain'), - maps: {}, - }; */ } } diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts index 999dc84a5..6e4b3c63e 100644 --- a/packages/designer/src/designer/designer.ts +++ b/packages/designer/src/designer/designer.ts @@ -13,10 +13,12 @@ import { IShellModelFactory, IPublicModelDragObject, IPublicModelScrollable, - IDesigner, IPublicModelScroller, IPublicTypeLocationData, IPublicEnumTransformStage, + IPublicModelDragon, + IPublicModelActiveTracker, + IPublicModelDropLocation, } from '@alilc/lowcode-types'; import { megreAssets, IPublicTypeAssetsJson, isNodeSchema, isDragNodeObject, isDragNodeDataObject, isLocationChildrenDetail, Logger } from '@alilc/lowcode-utils'; import { Project } from '../project'; @@ -37,12 +39,14 @@ import { ComponentActions } from '../component-actions'; const logger = new Logger({ level: 'warn', bizName: 'designer' }); export interface DesignerProps { + [key: string]: any; editor: IPublicModelEditor; shellModelFactory: IShellModelFactory; className?: string; style?: object; defaultSchema?: IPublicTypeProjectSchema; hotkeys?: object; + viewName?: string; simulatorProps?: object | ((document: DocumentModel) => object); simulatorComponent?: ComponentType; dragGhostComponent?: ComponentType; @@ -53,13 +57,23 @@ export interface DesignerProps { onDragstart?: (e: ILocateEvent) => void; onDrag?: (e: ILocateEvent) => void; onDragend?: (e: { dragObject: IPublicModelDragObject; copy: boolean }, loc?: DropLocation) => void; - viewName?: string; - [key: string]: any; } +export interface IDesigner { + get dragon(): IPublicModelDragon; + get activeTracker(): IPublicModelActiveTracker; + createScroller(scrollable: IPublicModelScrollable): IPublicModelScroller; + + /** + * 创建插入位置,考虑放到 dragon 中 + */ + createLocation(locationData: IPublicTypeLocationData): IPublicModelDropLocation; +} export class Designer implements IDesigner { - dragon: Dragon; + public dragon: Dragon; + + public viewName: string | undefined; readonly componentActions = new ComponentActions(); @@ -75,6 +89,24 @@ export class Designer implements IDesigner { readonly shellModelFactory: IShellModelFactory; + private _dropLocation?: DropLocation; + + private propsReducers = new Map(); + + private _lostComponentMetasMap = new Map(); + + private props?: DesignerProps; + + private oobxList: OffsetObserver[] = []; + + @obx.ref private _componentMetasMap = new Map(); + + @obx.ref private _simulatorComponent?: ComponentType; + + @obx.ref private _simulatorProps?: object | ((project: Project) => object); + + @obx.ref private _suspensed = false; + get currentDocument() { return this.project.currentDocument; } @@ -87,8 +119,6 @@ export class Designer implements IDesigner { return this.currentDocument?.selection; } - viewName: string | undefined; - constructor(props: DesignerProps) { makeObservable(this); const { editor, viewName, shellModelFactory } = props; @@ -222,8 +252,6 @@ export class Designer implements IDesigner { this.editor.eventBus.emit(`designer.${event}`, ...args); } - private _dropLocation?: DropLocation; - get dropLocation() { return this._dropLocation; } @@ -258,8 +286,6 @@ export class Designer implements IDesigner { return new Scroller(scrollable); } - private oobxList: OffsetObserver[] = []; - createOffsetObserver(nodeInstance: INodeSelector): OffsetObserver | null { const oobx = createOffsetObserver(nodeInstance); this.clearOobxList(); @@ -330,8 +356,6 @@ export class Designer implements IDesigner { return { target, index }; } - private props?: DesignerProps; - setProps(nextProps: DesignerProps) { const props = this.props ? { ...this.props, ...nextProps } : nextProps; if (this.props) { @@ -409,14 +433,10 @@ export class Designer implements IDesigner { return this.props?.[key]; } - @obx.ref private _simulatorComponent?: ComponentType; - @computed get simulatorComponent(): ComponentType | undefined { return this._simulatorComponent; } - @obx.ref private _simulatorProps?: object | ((project: Project) => object); - @computed get simulatorProps(): object { if (typeof this._simulatorProps === 'function') { return this._simulatorProps(this.project); @@ -439,8 +459,6 @@ export class Designer implements IDesigner { }; } - @obx.ref private _suspensed = false; - get suspensed(): boolean { return this._suspensed; } @@ -461,16 +479,15 @@ export class Designer implements IDesigner { this.project.load(schema); } - @obx.ref private _componentMetasMap = new Map(); - - private _lostComponentMetasMap = new Map(); - buildComponentMetasMap(metas: IPublicTypeComponentMetadata[]) { metas.forEach((data) => this.createComponentMeta(data)); } - createComponentMeta(data: IPublicTypeComponentMetadata): ComponentMeta { + createComponentMeta(data: IPublicTypeComponentMetadata): ComponentMeta | null { const key = data.componentName; + if (!key) { + return null; + } let meta = this._componentMetasMap.get(key); if (meta) { meta.setMetadata(data); @@ -540,8 +557,6 @@ export class Designer implements IDesigner { return maps; } - private propsReducers = new Map(); - transformProps(props: IPublicTypeCompositeObject | IPublicTypePropsList, node: Node, stage: IPublicEnumTransformStage) { if (Array.isArray(props)) { // current not support, make this future diff --git a/packages/designer/src/designer/detecting.ts b/packages/designer/src/designer/detecting.ts index d3d14106c..b7ada8138 100644 --- a/packages/designer/src/designer/detecting.ts +++ b/packages/designer/src/designer/detecting.ts @@ -2,8 +2,13 @@ import { makeObservable, obx, IEventBus, createModuleEventBus } from '@alilc/low import { IPublicModelDetecting, IPublicModelNode, IPublicModelDocumentModel } from '@alilc/lowcode-types'; const DETECTING_CHANGE_EVENT = 'detectingChange'; -export interface IDetecting extends IPublicModelDetecting { +export interface IDetecting extends Omit< IPublicModelDetecting, 'capture' | 'release' | 'leave' > { + capture(node: IPublicModelNode | null): void; + + release(node: IPublicModelNode | null): void; + + leave(document: IPublicModelDocumentModel | undefined): void; } export class Detecting implements IDetecting { diff --git a/packages/designer/src/designer/dragon.ts b/packages/designer/src/designer/dragon.ts index 963c187bd..29a67a9ab 100644 --- a/packages/designer/src/designer/dragon.ts +++ b/packages/designer/src/designer/dragon.ts @@ -8,7 +8,7 @@ import { IPublicModelNode, IPublicModelDragon, IPublicModelLocateEvent, - ISensor, + IPublicModelSensor, } from '@alilc/lowcode-types'; import { setNativeSelection, cursor } from '@alilc/lowcode-utils'; import { Node } from '../document'; @@ -22,7 +22,7 @@ export interface ILocateEvent extends IPublicModelLocateEvent { /** * 激活的感应器 */ - sensor?: ISensor; + sensor?: IPublicModelSensor; } @@ -52,6 +52,7 @@ export function isLocateEvent(e: any): e is ILocateEvent { } const SHAKE_DISTANCE = 4; + /** * mouse shake check */ @@ -103,16 +104,16 @@ export interface IDragon extends IPublicModelDragon { * Drag-on 拖拽引擎 */ export class Dragon implements IDragon { - private sensors: ISensor[] = []; + private sensors: IPublicModelSensor[] = []; key = Math.random(); /** * current active sensor, 可用于感应区高亮 */ - @obx.ref private _activeSensor: ISensor | undefined; + @obx.ref private _activeSensor: IPublicModelSensor | undefined; - get activeSensor(): ISensor | undefined { + get activeSensor(): IPublicModelSensor | undefined { return this._activeSensor; } @@ -173,7 +174,7 @@ export class Dragon implements IDragon { const forceCopyState = isDragNodeObject(dragObject) && dragObject.nodes.some((node: Node | IPublicModelNode) => (typeof node.isSlot === 'function' ? node.isSlot() : node.isSlot)); const isBoostFromDragAPI = isDragEvent(boostEvent); - let lastSensor: ISensor | undefined; + let lastSensor: IPublicModelSensor | undefined; this._dragging = false; @@ -462,7 +463,7 @@ export class Dragon implements IDragon { /* istanbul ignore next */ const chooseSensor = (e: ILocateEvent) => { // this.sensors will change on dragstart - const sensors: ISensor[] = this.sensors.concat(masterSensors as ISensor[]); + const sensors: IPublicModelSensor[] = this.sensors.concat(masterSensors as IPublicModelSensor[]); let sensor = e.sensor && e.sensor.isEnter(e) ? e.sensor diff --git a/packages/designer/src/document/document-model.ts b/packages/designer/src/document/document-model.ts index 679931bc7..8bccde2ef 100644 --- a/packages/designer/src/document/document-model.ts +++ b/packages/designer/src/document/document-model.ts @@ -8,25 +8,24 @@ import { IPublicTypeDragNodeObject, IPublicTypeDragNodeDataObject, IPublicModelDocumentModel, - IPublicModelSelection, IPublicModelHistory, IPublicModelModalNodesManager, IPublicModelNode, IPublicApiProject, IPublicModelDropLocation, IPublicEnumTransformStage, - IPublicOnChangeOptions, - EDITOR_EVENT, + IPublicTypeOnChangeOptions, } from '@alilc/lowcode-types'; import { Project } from '../project'; import { ISimulatorHost } from '../simulator'; import { ComponentMeta } from '../component-meta'; import { IDropLocation, Designer } from '../designer'; import { Node, insertChildren, insertChild, isNode, RootNode, INode } from './node/node'; -import { Selection } from './selection'; +import { Selection, ISelection } from './selection'; import { History } from './history'; import { ModalNodesManager } from './node'; import { uniqueId, isPlainObject, compatStage, isJSExpression, isDOMText, isNodeSchema, isDragNodeObject, isDragNodeDataObject } from '@alilc/lowcode-utils'; +import { EDITOR_EVENT } from '../types'; export type GetDataType = T extends undefined ? NodeType extends { @@ -35,8 +34,20 @@ export type GetDataType = T extends undefined ? R : any : T; -export interface IDocumentModel extends IPublicModelDocumentModel { +export interface IDocumentModel extends Omit< IPublicModelDocumentModel, 'selection' > { + + readonly designer: Designer; + + /** + * 选区控制 + */ + readonly selection: ISelection; + + /** + * 根据 id 获取节点 + */ + getNode(id: string): INode | null; } export class DocumentModel implements IDocumentModel { @@ -53,7 +64,7 @@ export class DocumentModel implements IDocumentModel { /** * 选区控制 */ - readonly selection: IPublicModelSelection = new Selection(this); + readonly selection: ISelection = new Selection(this); /** * 操作记录控制 @@ -116,16 +127,85 @@ export class DocumentModel implements IDocumentModel { @obx.ref private _drillDownNode: Node | null = null; - drillDown(node: Node | null) { - this._drillDownNode = node; - } - private _modalNode?: INode; private _blank?: boolean; private inited = false; + @obx.shallow private willPurgeSpace: Node[] = []; + + get modalNode() { + return this._modalNode; + } + + get currentRoot() { + return this.modalNode || this.focusNode; + } + + @obx.shallow private activeNodes?: Node[]; + + @obx.ref private _dropLocation: IDropLocation | null = null; + + set dropLocation(loc: IPublicModelDropLocation | null) { + this._dropLocation = loc; + // pub event + this.designer.editor.eventBus.emit( + 'document.dropLocation.changed', + { document: this, location: loc }, + ); + } + + /** + * 投放插入位置标记 + */ + get dropLocation() { + return this._dropLocation; + } + + /** + * 导出 schema 数据 + */ + get schema(): IPublicTypeRootSchema { + return this.rootNode?.schema as any; + } + + @obx.ref private _opened = false; + + @obx.ref private _suspensed = false; + + /** + * 是否为非激活状态 + */ + get suspensed(): boolean { + return this._suspensed || !this._opened; + } + + /** + * 与 suspensed 相反,是否为激活状态,这个函数可能用的更多一点 + */ + get active(): boolean { + return !this._suspensed; + } + + /** + * @deprecated 兼容 + */ + get actived(): boolean { + return this.active; + } + + /** + * 是否打开 + */ + get opened() { + return this._opened; + } + + get root() { + return this.rootNode; + } + constructor(project: Project, schema?: IPublicTypeRootSchema) { makeObservable(this); this.project = project; @@ -160,6 +240,10 @@ export class DocumentModel implements IDocumentModel { this.inited = true; } + drillDown(node: Node | null) { + this._drillDownNode = node; + } + onChangeNodeVisible(fn: (node: IPublicModelNode, visible: boolean) => void): () => void { this.designer.editor?.eventBus.on(EDITOR_EVENT.NODE_CHILDREN_CHANGE, fn); @@ -168,7 +252,7 @@ export class DocumentModel implements IDocumentModel { }; } - onChangeNodeChildren(fn: (info: IPublicOnChangeOptions) => void): () => void { + onChangeNodeChildren(fn: (info: IPublicTypeOnChangeOptions) => void): () => void { this.designer.editor?.eventBus.on(EDITOR_EVENT.NODE_VISIBLE_CHANGE, fn); return () => { @@ -176,16 +260,6 @@ export class DocumentModel implements IDocumentModel { }; } - @obx.shallow private willPurgeSpace: Node[] = []; - - get modalNode() { - return this._modalNode; - } - - get currentRoot() { - return this.modalNode || this.focusNode; - } - addWillPurge(node: Node) { this.willPurgeSpace.push(node); } @@ -202,7 +276,7 @@ export class DocumentModel implements IDocumentModel { } /** - * 生成唯一id + * 生成唯一 id */ nextId(possibleId: string | undefined) { let id = possibleId; @@ -216,7 +290,7 @@ export class DocumentModel implements IDocumentModel { /** * 根据 id 获取节点 */ - getNode(id: string): Node | null { + getNode(id: string): INode | null { return this._nodesMap.get(id) || null; } @@ -235,8 +309,6 @@ export class DocumentModel implements IDocumentModel { return node ? !node.isPurged : false; } - @obx.shallow private activeNodes?: Node[]; - /** * 根据 schema 创建一个节点 */ @@ -336,25 +408,6 @@ export class DocumentModel implements IDocumentModel { this._nodesMap.delete(node.id); } - @obx.ref private _dropLocation: IDropLocation | null = null; - - - set dropLocation(loc: IPublicModelDropLocation | null) { - this._dropLocation = loc; - // pub event - this.designer.editor.eventBus.emit( - 'document.dropLocation.changed', - { document: this, location: loc }, - ); - } - - /** - * 投放插入位置标记 - */ - get dropLocation() { - return this._dropLocation; - } - /** * 包裹当前选区中的节点 */ @@ -377,13 +430,6 @@ export class DocumentModel implements IDocumentModel { return null; } - /** - * 导出 schema 数据 - */ - get schema(): IPublicTypeRootSchema { - return this.rootNode?.schema as any; - } - @action import(schema: IPublicTypeRootSchema, checkId = false) { const drillDownNodeId = this._drillDownNode?.id; @@ -448,38 +494,6 @@ export class DocumentModel implements IDocumentModel { ); } - @obx.ref private _opened = false; - - @obx.ref private _suspensed = false; - - /** - * 是否为非激活状态 - */ - get suspensed(): boolean { - return this._suspensed || !this._opened; - } - - /** - * 与 suspensed 相反,是否为激活状态,这个函数可能用的更多一点 - */ - get active(): boolean { - return !this._suspensed; - } - - /** - * @deprecated 兼容 - */ - get actived(): boolean { - return this.active; - } - - /** - * 是否打开 - */ - get opened() { - return this._opened; - } - /** * 切换激活,只有打开的才能激活 * 不激活,打开之后切换到另外一个时发生,比如 tab 视图,切换到另外一个标签页 @@ -616,10 +630,6 @@ export class DocumentModel implements IDocumentModel { return this.history; } - get root() { - return this.rootNode; - } - /** * @deprecated */ diff --git a/packages/designer/src/document/history.ts b/packages/designer/src/document/history.ts index fa62bc5cd..5d52f0dd3 100644 --- a/packages/designer/src/document/history.ts +++ b/packages/designer/src/document/history.ts @@ -1,5 +1,8 @@ import { reaction, untracked, globalContext, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core'; import { IPublicTypeNodeSchema, IPublicModelHistory } from '@alilc/lowcode-types'; +import { Logger } from '@alilc/lowcode-utils'; + +const logger = new Logger({ level: 'warn', bizName: 'history' }); export interface Serialization { serialize(data: K): T; @@ -30,11 +33,15 @@ export class History implements IHistory { }, }; - setSerialization(serialization: Serialization) { - this.currentSerialization = serialization; + get hotData() { + return this.session.data; } - constructor(dataFn: () => T | null, private redoer: (data: T) => void, private timeGap: number = 1000) { + constructor( + dataFn: () => T | null, + private redoer: (data: T) => void, + private timeGap: number = 1000, + ) { this.session = new Session(0, null, this.timeGap); this.records = [this.session]; @@ -68,8 +75,8 @@ export class History implements IHistory { }, { fireImmediately: true }); } - get hotData() { - return this.session.data; + setSerialization(serialization: Serialization) { + this.currentSerialization = serialization; } isSavePoint(): boolean { @@ -84,16 +91,18 @@ export class History implements IHistory { this.asleep = false; } - go(cursor: number) { + go(originalCursor: number) { this.session.end(); - const currentCursor = this.session.cursor; + let cursor = originalCursor; cursor = +cursor; if (cursor < 0) { cursor = 0; } else if (cursor >= this.records.length) { cursor = this.records.length - 1; } + + const currentCursor = this.session.cursor; if (cursor === currentCursor) { return; } @@ -106,7 +115,7 @@ export class History implements IHistory { this.redoer(this.currentSerialization.unserialize(hotData)); this.emitter.emit('cursor', hotData); } catch (e) /* istanbul ignore next */ { - console.error(e); + logger.error(e); } this.wakeup(); @@ -174,6 +183,7 @@ export class History implements IHistory { } return state; } + /** * 监听 state 变更事件 * @param func @@ -209,6 +219,7 @@ export class History implements IHistory { this.emitter.removeAllListeners(); this.records = []; } + /** * * @deprecated diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index 67eb817bf..ef837a329 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -6,30 +6,33 @@ import { IPublicTypePropsList, IPublicTypeNodeData, IPublicTypeI18nData, - SlotSchema, + IPublicTypeSlotSchema, IPublicTypePageSchema, IPublicTypeComponentSchema, - NodeStatus, IPublicTypeCompositeValue, GlobalEvent, IPublicTypeComponentAction, IPublicModelNode, IPublicModelExclusiveGroup, IPublicEnumTransformStage, - EDITOR_EVENT, } from '@alilc/lowcode-types'; import { compatStage, isDOMText, isJSExpression } from '@alilc/lowcode-utils'; import { SettingTopEntry } from '@alilc/lowcode-designer'; import { Props, getConvertedExtraKey } from './props/props'; -import { DocumentModel } from '../document-model'; +import { DocumentModel, IDocumentModel } from '../document-model'; import { NodeChildren, INodeChildren } from './node-children'; import { Prop } from './props/prop'; import { ComponentMeta } from '../../component-meta'; import { ExclusiveGroup, isExclusiveGroup } from './exclusive-group'; import { includeSlot, removeSlot } from '../../utils/slot'; import { foreachReverse } from '../../utils/tree'; -import { NodeRemoveOptions } from '../../types'; +import { NodeRemoveOptions, EDITOR_EVENT } from '../../types'; +export interface NodeStatus { + locking: boolean; + pseudo: boolean; + inPlaceEditing: boolean; +} export interface INode extends IPublicModelNode { @@ -52,6 +55,21 @@ export interface INode extends IPublicModelNode { unlinkSlot(slotNode: Node): void; didDropOut(dragment: Node): void; + + /** + * 导出 schema + */ + export(stage: IPublicEnumTransformStage, options?: any): IPublicTypeNodeSchema; + + get document(): IDocumentModel; + + emitPropChange(val: IPublicTypePropChangeOptions): void; + + import(data: IPublicTypeNodeSchema, checkId?: boolean): void; + + internalSetSlotFor(slotFor: Prop | null | undefined): void; + + addSlot(slotNode: INode): void; } /** @@ -186,7 +204,7 @@ export class Node isInited = false; - constructor(readonly document: DocumentModel, nodeSchema: Schema, options: any = {}) { + constructor(readonly document: IDocumentModel, nodeSchema: Schema, options: any = {}) { makeObservable(this); const { componentName, id, children, props, ...extras } = nodeSchema; this.id = document.nextId(id); @@ -518,7 +536,7 @@ export class Node return this.props.export(IPublicEnumTransformStage.Serilize).props || null; } - @obx.shallow _slots: Node[] = []; + @obx.shallow _slots: INode[] = []; hasSlots() { return this._slots.length > 0; @@ -882,7 +900,7 @@ export class Node return false; } - addSlot(slotNode: Node) { + addSlot(slotNode: INode) { const slotName = slotNode?.getExtraProp('name')?.getAsString(); // 一个组件下的所有 slot,相同 slotName 的 slot 应该是唯一的 if (includeSlot(this, slotName)) { @@ -1235,14 +1253,13 @@ function ensureNode(node: any, document: DocumentModel): Node { return nodeInstance; } - export interface LeafNode extends Node { readonly children: null; } export type IPublicTypePropChangeOptions = Omit; -export type SlotNode = Node; +export type SlotNode = Node; export type PageNode = Node; export type ComponentNode = Node; export type RootNode = PageNode | ComponentNode; diff --git a/packages/designer/src/document/node/props/prop.ts b/packages/designer/src/document/node/props/prop.ts index cef83f52e..642783588 100644 --- a/packages/designer/src/document/node/props/prop.ts +++ b/packages/designer/src/document/node/props/prop.ts @@ -1,9 +1,9 @@ import { untracked, computed, obx, engineConfig, action, makeObservable, mobx, runInAction } from '@alilc/lowcode-editor-core'; -import { IPublicTypeCompositeValue, GlobalEvent, IPublicTypeJSSlot, SlotSchema, IPublicEnumTransformStage } from '@alilc/lowcode-types'; +import { IPublicTypeCompositeValue, GlobalEvent, IPublicTypeJSSlot, IPublicTypeSlotSchema, IPublicEnumTransformStage, IPublicModelProp } from '@alilc/lowcode-types'; import { uniqueId, isPlainObject, hasOwnProperty, compatStage, isJSExpression, isJSSlot } from '@alilc/lowcode-utils'; import { valueToSource } from './value-to-source'; -import { Props } from './props'; -import { SlotNode, Node } from '../node'; +import { Props, IProps, IPropParent } from './props'; +import { SlotNode, INode } from '../node'; // import { TransformStage } from '../transform-stage'; const { set: mobxSet, isObservableArray } = mobx; @@ -11,19 +11,25 @@ export const UNSET = Symbol.for('unset'); // eslint-disable-next-line no-redeclare export type UNSET = typeof UNSET; -export interface IPropParent { - delete(prop: Prop): void; +export interface IProp extends Omit { + readonly props: Props; - readonly owner: Node; - readonly path: string[]; + + readonly owner: INode; + + delete(prop: Prop): void; + + export(stage: IPublicEnumTransformStage): IPublicTypeCompositeValue; + + getNode(): INode; } export type ValueTypes = 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot'; -export class Prop implements IPropParent { +export class Prop implements IProp, IPropParent { readonly isProp = true; - readonly owner: Node; + readonly owner: INode; /** * 键值 @@ -39,6 +45,150 @@ export class Prop implements IPropParent { readonly options: any; + readonly id = uniqueId('prop$'); + + @obx.ref private _type: ValueTypes = 'unset'; + + /** + * 属性类型 + */ + get type(): ValueTypes { + return this._type; + } + + @obx private _value: any = UNSET; + + /** + * 属性值 + */ + @computed get value(): IPublicTypeCompositeValue | UNSET { + return this.export(IPublicEnumTransformStage.Serilize); + } + + private _code: string | null = null; + + /** + * 获得表达式值 + */ + @computed get code() { + if (isJSExpression(this.value)) { + return this.value.value; + } + // todo: JSFunction ... + if (this.type === 'slot') { + return JSON.stringify(this._slotNode!.export(IPublicEnumTransformStage.Save)); + } + return this._code != null ? this._code : JSON.stringify(this.value); + } + + /** + * 设置表达式值 + */ + set code(code: string) { + if (isJSExpression(this._value)) { + this.setValue({ + ...this._value, + value: code, + }); + this._code = code; + return; + } + + try { + const v = JSON.parse(code); + this.setValue(v); + this._code = code; + return; + } catch (e) { + // ignore + } + + this.setValue({ + type: 'JSExpression', + value: code, + mock: this._value, + }); + this._code = code; + } + + private _slotNode?: INode; + + get slotNode(): INode | undefined | null { + return this._slotNode; + } + + @obx.shallow private _items: Prop[] | null = null; + + @obx.shallow private _maps: Map | null = null; + + /** + * 作为 _maps 的一层缓存机制,主要是复用部分已存在的 Prop,保持响应式关系,比如: + * 当前 Prop#_value 值为 { a: 1 },当调用 setValue({ a: 2 }) 时,所有原来的子 Prop 均被销毁, + * 导致假如外部有 mobx reaction(常见于 observer),此时响应式链路会被打断, + * 因为 reaction 监听的是原 Prop(a) 的 _value,而不是新 Prop(a) 的 _value。 + */ + private _prevMaps: Map | null = null; + + /** + * 构造 items 属性,同时构造 maps 属性 + */ + private get items(): Prop[] | null { + if (this._items) return this._items; + return runInAction(() => { + let items: Prop[] | null = null; + if (this._type === 'list') { + const data = this._value; + data.forEach((item: any, idx: number) => { + items = items || []; + items.push(new Prop(this, item, idx)); + }); + this._maps = null; + } else if (this._type === 'map') { + const data = this._value; + const maps = new Map(); + const keys = Object.keys(data); + for (const key of keys) { + let prop: Prop; + if (this._prevMaps?.has(key)) { + prop = this._prevMaps.get(key)!; + prop.setValue(data[key]); + } else { + prop = new Prop(this, data[key], key); + } + items = items || []; + items.push(prop); + maps.set(key, prop); + } + this._maps = maps; + } else { + items = null; + this._maps = null; + } + this._items = items; + return this._items; + }); + } + + @computed private get maps(): Map | null { + if (!this.items) { + return null; + } + return this._maps; + } + + get path(): string[] { + return (this.parent.path || []).concat(this.key as string); + } + + /** + * 元素个数 + */ + get size(): number { + return this.items?.length || 0; + } + + private purged = false; + constructor( public parent: IPropParent, value: IPublicTypeCompositeValue | UNSET = UNSET, @@ -88,26 +238,6 @@ export class Prop implements IPropParent { this.get(propName, false)?.unset(); } - readonly id = uniqueId('prop$'); - - @obx.ref private _type: ValueTypes = 'unset'; - - /** - * 属性类型 - */ - get type(): ValueTypes { - return this._type; - } - - @obx private _value: any = UNSET; - - /** - * 属性值 - */ - @computed get value(): IPublicTypeCompositeValue | UNSET { - return this.export(IPublicEnumTransformStage.Serilize); - } - export(stage: IPublicEnumTransformStage = IPublicEnumTransformStage.Save): IPublicTypeCompositeValue { stage = compatStage(stage); const type = this._type; @@ -180,52 +310,6 @@ export class Prop implements IPropParent { } } - private _code: string | null = null; - - /** - * 获得表达式值 - */ - @computed get code() { - if (isJSExpression(this.value)) { - return this.value.value; - } - // todo: JSFunction ... - if (this.type === 'slot') { - return JSON.stringify(this._slotNode!.export(IPublicEnumTransformStage.Save)); - } - return this._code != null ? this._code : JSON.stringify(this.value); - } - - /** - * 设置表达式值 - */ - set code(code: string) { - if (isJSExpression(this._value)) { - this.setValue({ - ...this._value, - value: code, - }); - this._code = code; - return; - } - - try { - const v = JSON.parse(code); - this.setValue(v); - this._code = code; - return; - } catch (e) { - // ignore - } - - this.setValue({ - type: 'JSExpression', - value: code, - mock: this._value, - }); - this._code = code; - } - getAsString(): string { if (this.type === 'literal') { return this._value ? String(this._value) : ''; @@ -305,19 +389,13 @@ export class Prop implements IPropParent { } } - private _slotNode?: SlotNode; - - get slotNode() { - return this._slotNode; - } - @action setAsSlot(data: IPublicTypeJSSlot) { this._type = 'slot'; - let slotSchema: SlotSchema; + let slotSchema: IPublicTypeSlotSchema; // 当 data.value 的结构为 { componentName: 'Slot' } 时,复用部分 slotSchema 数据 if ((isPlainObject(data.value) && data.value?.componentName === 'Slot')) { - const value = data.value as SlotSchema; + const value = data.value as IPublicTypeSlotSchema; slotSchema = { componentName: 'Slot', title: value.title || value.props?.slotTitle, @@ -325,7 +403,7 @@ export class Prop implements IPropParent { name: value.name || value.props?.slotName, params: value.params || value.props?.slotParams, children: data.value, - } as SlotSchema; + } as IPublicTypeSlotSchema; } else { slotSchema = { componentName: 'Slot', @@ -342,8 +420,10 @@ export class Prop implements IPropParent { } else { const { owner } = this.props; this._slotNode = owner.document.createNode(slotSchema); - owner.addSlot(this._slotNode); - this._slotNode.internalSetSlotFor(this); + if (this._slotNode) { + owner.addSlot(this._slotNode); + this._slotNode.internalSetSlotFor(this); + } } } @@ -389,69 +469,6 @@ export class Prop implements IPropParent { return this.code === other.code ? 0 : 2; } - @obx.shallow private _items: Prop[] | null = null; - - @obx.shallow private _maps: Map | null = null; - - /** - * 作为 _maps 的一层缓存机制,主要是复用部分已存在的 Prop,保持响应式关系,比如: - * 当前 Prop#_value 值为 { a: 1 },当调用 setValue({ a: 2 }) 时,所有原来的子 Prop 均被销毁, - * 导致假如外部有 mobx reaction(常见于 observer),此时响应式链路会被打断, - * 因为 reaction 监听的是原 Prop(a) 的 _value,而不是新 Prop(a) 的 _value。 - */ - private _prevMaps: Map | null = null; - - get path(): string[] { - return (this.parent.path || []).concat(this.key as string); - } - - /** - * 构造 items 属性,同时构造 maps 属性 - */ - private get items(): Prop[] | null { - if (this._items) return this._items; - return runInAction(() => { - let items: Prop[] | null = null; - if (this._type === 'list') { - const data = this._value; - data.forEach((item: any, idx: number) => { - items = items || []; - items.push(new Prop(this, item, idx)); - }); - this._maps = null; - } else if (this._type === 'map') { - const data = this._value; - const maps = new Map(); - const keys = Object.keys(data); - for (const key of keys) { - let prop: Prop; - if (this._prevMaps?.has(key)) { - prop = this._prevMaps.get(key)!; - prop.setValue(data[key]); - } else { - prop = new Prop(this, data[key], key); - } - items = items || []; - items.push(prop); - maps.set(key, prop); - } - this._maps = maps; - } else { - items = null; - this._maps = null; - } - this._items = items; - return this._items; - }); - } - - @computed private get maps(): Map | null { - if (!this.items) { - return null; - } - return this._maps; - } - /** * 获取某个属性 * @param createIfNone 当没有的时候,是否创建一个 @@ -544,13 +561,6 @@ export class Prop implements IPropParent { } } - /** - * 元素个数 - */ - get size(): number { - return this.items?.length || 0; - } - /** * 添加值到列表 * @@ -640,8 +650,6 @@ export class Prop implements IPropParent { return hasOwnProperty(this._value, key); } - private purged = false; - /** * 回收销毁 */ diff --git a/packages/designer/src/document/node/props/props.ts b/packages/designer/src/document/node/props/props.ts index 897b54f17..56c1e7711 100644 --- a/packages/designer/src/document/node/props/props.ts +++ b/packages/designer/src/document/node/props/props.ts @@ -1,8 +1,8 @@ import { computed, makeObservable, obx, action } from '@alilc/lowcode-editor-core'; -import { IPublicTypePropsMap, IPublicTypePropsList, IPublicTypeCompositeValue, IPublicEnumTransformStage } from '@alilc/lowcode-types'; +import { IPublicTypePropsMap, IPublicTypePropsList, IPublicTypeCompositeValue, IPublicEnumTransformStage, IPublicModelProps } from '@alilc/lowcode-types'; import { uniqueId, compatStage } from '@alilc/lowcode-utils'; -import { Prop, IPropParent, UNSET } from './prop'; -import { Node } from '../node'; +import { Prop, IProp, UNSET } from './prop'; +import { INode, Node } from '../node'; // import { TransformStage } from '../transform-stage'; interface ExtrasObject { @@ -23,7 +23,30 @@ export function getConvertedExtraKey(key: string): string { export function getOriginalExtraKey(key: string): string { return key.replace(new RegExp(`${EXTRA_KEY_PREFIX}`, 'g'), ''); } -export class Props implements IPropParent { + +export interface IPropParent { + + readonly props: Props; + + readonly owner: INode; + + get path(): string[]; + + delete(prop: Prop): void; + +} + +export interface IProps extends Omit { + + /** + * 获取 props 对应的 node + */ + getNode(): INode; + + getProp(path: string): IProp | null; +} + +export class Props implements IProps, IPropParent { readonly id = uniqueId('props'); @obx.shallow private items: Prop[] = []; @@ -46,7 +69,7 @@ export class Props implements IPropParent { return this; } - readonly owner: Node; + readonly owner: INode; /** * 元素个数 @@ -57,7 +80,9 @@ export class Props implements IPropParent { @obx type: 'map' | 'list' = 'map'; - constructor(owner: Node, value?: IPublicTypePropsMap | IPublicTypePropsList | null, extras?: ExtrasObject) { + private purged = false; + + constructor(owner: INode, value?: IPublicTypePropsMap | IPublicTypePropsList | null, extras?: ExtrasObject) { makeObservable(this); this.owner = owner; if (Array.isArray(value)) { @@ -196,7 +221,7 @@ export class Props implements IPropParent { } /** - * 获取某个属性, 如果不存在,临时获取一个待写入 + * 获取某个属性,如果不存在,临时获取一个待写入 * @param createIfNone 当没有的时候,是否创建一个 */ @action @@ -323,8 +348,6 @@ export class Props implements IPropParent { }); } - private purged = false; - /** * 回收销毁 */ diff --git a/packages/designer/src/document/selection.ts b/packages/designer/src/document/selection.ts index 22b8b4ecd..bcbf778ea 100644 --- a/packages/designer/src/document/selection.ts +++ b/packages/designer/src/document/selection.ts @@ -1,10 +1,20 @@ import { obx, makeObservable, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core'; -import { Node, comparePosition, PositionNO } from './node/node'; +import { Node, INode, comparePosition, PositionNO } from './node/node'; import { DocumentModel } from './document-model'; import { IPublicModelSelection } from '@alilc/lowcode-types'; -export interface ISelection extends IPublicModelSelection { +export interface ISelection extends Omit< IPublicModelSelection, 'getNodes' | 'getTopNodes' > { + /** + * 获取选中的节点实例 + * @returns + */ + getNodes(): INode[]; + + /** + * 获取顶层选区节点,场景:拖拽时,建立蒙层,只蒙在最上层 + */ + getTopNodes(includeRoot?: boolean): INode[]; } export class Selection implements ISelection { diff --git a/packages/designer/src/simulator.ts b/packages/designer/src/simulator.ts index baa0fd793..642e5ad67 100644 --- a/packages/designer/src/simulator.ts +++ b/packages/designer/src/simulator.ts @@ -1,5 +1,5 @@ import { ComponentType } from 'react'; -import { IPublicTypeComponentMetadata, IPublicTypeNodeSchema, IPublicModelScrollable, IPublicTypeComponentInstance, ISensor, NodeInstance } from '@alilc/lowcode-types'; +import { IPublicTypeComponentMetadata, IPublicTypeNodeSchema, IPublicModelScrollable, IPublicTypeComponentInstance, IPublicModelSensor, IPublicTypeNodeInstance } from '@alilc/lowcode-types'; import { Point, ScrollTarget, ILocateEvent } from './designer'; import { BuiltinSimulatorRenderer } from './builtin-simulator/renderer'; import { Node, INode } from './document'; @@ -11,6 +11,7 @@ export const AutoFit = '100%'; export interface IScrollable extends IPublicModelScrollable { } export interface IViewport extends IScrollable { + /** * 视口大小 */ @@ -32,22 +33,27 @@ export interface IViewport extends IScrollable { * 视口矩形维度 */ readonly bounds: DOMRect; + /** * 内容矩形维度 */ readonly contentBounds: DOMRect; + /** * 视口滚动对象 */ readonly scrollTarget?: ScrollTarget; + /** * 是否滚动中 */ readonly scrolling: boolean; + /** * 内容当前滚动 X */ readonly scrollX: number; + /** * 内容当前滚动 Y */ @@ -72,8 +78,9 @@ export interface DropContainer { /** * 模拟器控制进程协议 */ -export interface ISimulatorHost

extends ISensor { +export interface ISimulatorHost

extends IPublicModelSensor { readonly isSimulator: true; + /** * 获得边界维度等信息 */ @@ -104,14 +111,17 @@ export interface ISimulatorHost

extends ISensor { * 设置文字拖选 */ setNativeSelection(enableFlag: boolean): void; + /** * 设置拖拽态 */ setDraggingState(state: boolean): void; + /** * 设置拷贝态 */ setCopyState(state: boolean): void; + /** * 清除所有态:拖拽态、拷贝态 */ @@ -128,24 +138,28 @@ export interface ISimulatorHost

extends ISensor { * 描述组件 */ generateComponentMetadata(componentName: string): IPublicTypeComponentMetadata; + /** * 根据组件信息获取组件类 */ getComponent(componentName: string): Component | any; + /** * 根据节点获取节点的组件实例 */ getComponentInstances(node: Node): IPublicTypeComponentInstance[] | null; + /** * 根据 schema 创建组件类 */ createComponent(schema: IPublicTypeNodeSchema): Component | null; + /** * 根据节点获取节点的组件运行上下文 */ getComponentContext(node: Node): object | null; - getClosestNodeInstance(from: IPublicTypeComponentInstance, specId?: string): NodeInstance | null; + getClosestNodeInstance(from: IPublicTypeComponentInstance, specId?: string): IPublicTypeNodeInstance | null; computeRect(node: Node): DOMRect | null; @@ -158,6 +172,7 @@ export interface ISimulatorHost

extends ISensor { postEvent(evtName: string, evtData: any): void; rerender(): void; + /** * 销毁 */ diff --git a/packages/designer/src/types/index.ts b/packages/designer/src/types/index.ts index e18f97145..50fd82bcd 100644 --- a/packages/designer/src/types/index.ts +++ b/packages/designer/src/types/index.ts @@ -11,4 +11,10 @@ export const utils = { getNodeSchemaById, }; +export enum EDITOR_EVENT { + NODE_CHILDREN_CHANGE = 'node.children.change', + + NODE_VISIBLE_CHANGE = 'node.visible.change', +} + export type Utils = typeof utils; \ No newline at end of file diff --git a/packages/editor-core/src/editor.ts b/packages/editor-core/src/editor.ts index e837fc530..5fa6bb894 100644 --- a/packages/editor-core/src/editor.ts +++ b/packages/editor-core/src/editor.ts @@ -7,8 +7,8 @@ import { IPublicModelEditor, EditorConfig, PluginClassSet, - KeyType, - GetReturnType, + IPublicTypeEditorValueKey, + IPublicTypeEditorGetResult, HookConfig, IPublicTypeComponentDescription, IPublicTypeRemoteComponentDescription, @@ -61,10 +61,11 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor this.setMaxListeners(200); this.eventBus = new EventBus(this); } + /** * Ioc Container */ - @obx.shallow private context = new Map(); + @obx.shallow private context = new Map(); get locale() { return globalLocale.getLocale(); @@ -76,15 +77,15 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor get( keyOrType: KeyOrType, - ): GetReturnType | undefined { + ): IPublicTypeEditorGetResult | undefined { return this.context.get(keyOrType as any); } - has(keyOrType: KeyType): boolean { + has(keyOrType: IPublicTypeEditorValueKey): boolean { return this.context.has(keyOrType); } - set(key: KeyType, data: any): void | Promise { + set(key: IPublicTypeEditorValueKey, data: any): void | Promise { if (key === 'assets') { return this.setAssets(data); } @@ -172,7 +173,7 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor this.notifyGot('assets'); } - onceGot(keyOrType: KeyOrType): Promise> { + onceGot(keyOrType: KeyOrType): Promise> { const x = this.context.get(keyOrType); if (x !== undefined) { return Promise.resolve(x); @@ -182,9 +183,9 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor }); } - onGot( + onGot( keyOrType: KeyOrType, - fn: (data: GetReturnType) => void, + fn: (data: IPublicTypeEditorGetResult) => void, ): () => void { const x = this.context.get(keyOrType); if (x !== undefined) { @@ -198,7 +199,7 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor } } - register(data: any, key?: KeyType): void { + register(data: any, key?: IPublicTypeEditorValueKey): void { this.context.set(key || data, data); this.notifyGot(key || data); } @@ -273,7 +274,7 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor /* eslint-disable */ private waits = new Map< - KeyType, + IPublicTypeEditorValueKey, Array<{ once?: boolean; resolve: (data: any) => void; @@ -281,7 +282,7 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor >(); /* eslint-enable */ - private notifyGot(key: KeyType) { + private notifyGot(key: IPublicTypeEditorValueKey) { let waits = this.waits.get(key); if (!waits) { return; @@ -301,7 +302,7 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor } } - private setWait(key: KeyType, resolve: (data: any) => void, once?: boolean) { + private setWait(key: IPublicTypeEditorValueKey, resolve: (data: any) => void, once?: boolean) { const waits = this.waits.get(key); if (waits) { waits.push({ resolve, once }); @@ -310,7 +311,7 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor } } - private delWait(key: KeyType, fn: any) { + private delWait(key: IPublicTypeEditorValueKey, fn: any) { const waits = this.waits.get(key); if (!waits) { return; diff --git a/packages/editor-core/src/utils/preference.ts b/packages/editor-core/src/utils/preference.ts index 0ee852bd7..6f17a8f63 100644 --- a/packages/editor-core/src/utils/preference.ts +++ b/packages/editor-core/src/utils/preference.ts @@ -2,15 +2,12 @@ import store from 'store'; import { getLogger } from './logger'; import { IPublicModelPreference } from '@alilc/lowcode-types'; -const logger = getLogger({ level: 'log', bizName: 'Preference' }); +const logger = getLogger({ level: 'warn', bizName: 'Preference' }); const STORAGE_KEY_PREFIX = 'ale'; - /** * used to store user preferences, such as pinned status of a pannel. * save to local storage. - * - * @class PreferenceStore */ export default class Preference implements IPublicModelPreference { getStorageKey(key: string, module?: string): string { @@ -24,7 +21,7 @@ export default class Preference implements IPublicModelPreference { return; } const storageKey = this.getStorageKey(key, module); - logger.log('storageKey:', storageKey, 'set with value:', value); + logger.debug('storageKey:', storageKey, 'set with value:', value); store.set(storageKey, value); } @@ -35,16 +32,15 @@ export default class Preference implements IPublicModelPreference { } const storageKey = this.getStorageKey(key, module); const result = store.get(storageKey); - logger.log('storageKey:', storageKey, 'get with result:', result); + logger.debug('storageKey:', storageKey, 'get with result:', result); return result; } + /** * check if local storage contain certain key * * @param {string} key * @param {string} module - * @returns {boolean} - * @memberof Preference */ contains(key: string, module: string): boolean { if (!key || typeof key !== 'string' || key.length === 0) { diff --git a/packages/editor-skeleton/src/area.ts b/packages/editor-skeleton/src/area.ts index f0dac5757..e96103ad6 100644 --- a/packages/editor-skeleton/src/area.ts +++ b/packages/editor-skeleton/src/area.ts @@ -1,12 +1,20 @@ /* eslint-disable max-len */ import { obx, computed, makeObservable } from '@alilc/lowcode-editor-core'; import { Logger } from '@alilc/lowcode-utils'; -import { IPublicTypeWidgetBaseConfig, IArea } from '@alilc/lowcode-types'; +import { IPublicTypeWidgetBaseConfig } from '@alilc/lowcode-types'; import { WidgetContainer } from './widget/widget-container'; import { Skeleton } from './skeleton'; import { IWidget } from './widget/widget'; const logger = new Logger({ level: 'warn', bizName: 'skeleton:area' }); +export interface IArea { + isEmpty(): boolean; + add(config: T | C): T; + remove(config: T | string): number; + setVisible(flag: boolean): void; + hide(): void; + show(): void; +} export class Area implements IArea { @obx private _visible = true; diff --git a/packages/plugin-outline-pane/src/controllers/pane-controller.ts b/packages/plugin-outline-pane/src/controllers/pane-controller.ts index a368c8288..be3b6b9ce 100644 --- a/packages/plugin-outline-pane/src/controllers/pane-controller.ts +++ b/packages/plugin-outline-pane/src/controllers/pane-controller.ts @@ -9,7 +9,7 @@ import { import { IPublicModelDragObject, IPublicModelScrollable, - ISensor, + IPublicModelSensor, IPublicTypeLocationChildrenDetail, IPublicTypeLocationDetailType, IPublicModelNode, @@ -24,7 +24,7 @@ import { IndentTrack } from '../helper/indent-track'; import DwellTimer from '../helper/dwell-timer'; import { ITreeBoard, TreeMaster } from './tree-master'; -export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollable { +export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicModelScrollable { private pluginContext: IPublicModelPluginContext; private treeMaster?: TreeMaster; @@ -50,12 +50,12 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab setup(); } - /** -------------------- ISensor begin -------------------- */ + /** -------------------- IPublicModelSensor begin -------------------- */ private indentTrack = new IndentTrack(); /** - * @see ISensor + * @see IPublicModelSensor */ fixEvent(e: IPublicModelLocateEvent): IPublicModelLocateEvent { if (e.fixed) { @@ -77,7 +77,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab } /** - * @see ISensor + * @see IPublicModelSensor */ locate(e: IPublicModelLocateEvent): IPublicModelDropLocation | undefined | null { this.sensing = true; @@ -213,7 +213,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab } /** - * @see ISensor + * @see IPublicModelSensor */ isEnter(e: IPublicModelLocateEvent): boolean { if (!this._shell) { @@ -224,7 +224,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab } /** - * @see ISensor + * @see IPublicModelSensor */ deactiveSensor() { this.sensing = false; @@ -234,15 +234,15 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab } private _sensorAvailable = false; + /** - * @see ISensor + * @see IPublicModelSensor */ get sensorAvailable() { return this._sensorAvailable; } - /** -------------------- ISensor end -------------------- */ - + /** -------------------- IPublicModelSensor end -------------------- */ /** -------------------- ITreeBoard begin -------------------- */ @@ -564,7 +564,6 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab return this._scrollTarget; } - private scroller?: IPublicModelScroller; purge() { @@ -573,7 +572,6 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab this.treeMaster?.removeBoard(this); } - private _shell: HTMLDivElement | null = null; mount(shell: HTMLDivElement | null) { diff --git a/packages/rax-simulator-renderer/src/renderer.ts b/packages/rax-simulator-renderer/src/renderer.ts index 2e4369608..2dca4506e 100644 --- a/packages/rax-simulator-renderer/src/renderer.ts +++ b/packages/rax-simulator-renderer/src/renderer.ts @@ -1,5 +1,5 @@ import { BuiltinSimulatorRenderer, Component, DocumentModel, Node } from '@alilc/lowcode-designer'; -import { IPublicTypeComponentSchema, IPublicTypeNodeSchema, IPublicTypeNpmInfo, IPublicEnumTransformStage, NodeInstance } from '@alilc/lowcode-types'; +import { IPublicTypeComponentSchema, IPublicTypeNodeSchema, IPublicTypeNpmInfo, IPublicEnumTransformStage, IPublicTypeNodeInstance } from '@alilc/lowcode-types'; import { Asset, compatibleLegaoSchema, cursor, isElement, isESModule, isPlainObject, isReactComponent, setNativeSelection } from '@alilc/lowcode-utils'; import LowCodeRenderer from '@alilc/lowcode-rax-renderer'; import { computed, observable as obx, makeObservable, configure } from 'mobx'; @@ -94,7 +94,7 @@ function isValidDesignModeRaxComponentInstance( raxComponentInst: any, ): raxComponentInst is { props: { - _leaf: Exclude['node'], null | undefined>; + _leaf: Exclude['node'], null | undefined>; }; } { const leaf = raxComponentInst?.props?._leaf; @@ -370,6 +370,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { @computed get componentsMap(): any { return this._componentsMap; } + /** * 加载资源 */ @@ -396,7 +397,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { } } - getNodeInstance(dom: HTMLElement): NodeInstance | null { + getNodeInstance(dom: HTMLElement): IPublicTypeNodeInstance | null { const INTERNAL = '_internal'; let instance: any = dom; if (!isElement(instance)) { @@ -429,7 +430,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { return null; } - getClosestNodeInstance(from: any, nodeId?: string): NodeInstance | null { + getClosestNodeInstance(from: any, nodeId?: string): IPublicTypeNodeInstance | null { const el: any = from; if (el) { // if (isElement(el)) { @@ -510,7 +511,6 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { doc.getElementsByTagName('head')[0].appendChild(s); } - const renderer = this; const { componentsMap: components } = renderer; diff --git a/packages/react-simulator-renderer/src/renderer.ts b/packages/react-simulator-renderer/src/renderer.ts index faa438a24..e76bfb585 100644 --- a/packages/react-simulator-renderer/src/renderer.ts +++ b/packages/react-simulator-renderer/src/renderer.ts @@ -17,7 +17,7 @@ import { AssetLoader, getProjectUtils, } from '@alilc/lowcode-utils'; -import { IPublicTypeComponentSchema, IPublicEnumTransformStage, IPublicTypeNodeSchema, NodeInstance } from '@alilc/lowcode-types'; +import { IPublicTypeComponentSchema, IPublicEnumTransformStage, IPublicTypeNodeSchema, IPublicTypeNodeInstance } from '@alilc/lowcode-types'; // just use types import { BuiltinSimulatorRenderer, Component, DocumentModel, Node } from '@alilc/lowcode-designer'; import LowCodeRenderer from '@alilc/lowcode-react-renderer'; @@ -368,6 +368,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { * 画布是否自动监听事件来重绘节点 */ autoRepaintNode = true; + /** * 加载资源 */ @@ -399,7 +400,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { } } - getClosestNodeInstance(from: ReactInstance, nodeId?: string): NodeInstance | null { + getClosestNodeInstance(from: ReactInstance, nodeId?: string): IPublicTypeNodeInstance | null { return getClosestNodeInstance(from, nodeId); } @@ -557,7 +558,7 @@ const SYMBOL_VDID = Symbol('_LCDocId'); function getClosestNodeInstance( from: ReactInstance, specId?: string, - ): NodeInstance | null { + ): IPublicTypeNodeInstance | null { let el: any = from; if (el) { if (isElement(el)) { @@ -587,7 +588,7 @@ function getClosestNodeInstance( return null; } -function getNodeInstance(fiberNode: any, specId?: string): NodeInstance | null { +function getNodeInstance(fiberNode: any, specId?: string): IPublicTypeNodeInstance | null { const instance = fiberNode?.stateNode; if (instance && SYMBOL_VNID in instance) { const nodeId = instance[SYMBOL_VNID]; diff --git a/packages/shell/src/api/canvas.ts b/packages/shell/src/api/canvas.ts index 9e729467f..4436514fb 100644 --- a/packages/shell/src/api/canvas.ts +++ b/packages/shell/src/api/canvas.ts @@ -4,7 +4,6 @@ import { IPublicModelScrollTarget, IPublicModelScrollable, IPublicModelScroller, - IDesigner, IPublicTypeLocationData, IPublicModelEditor, IPublicModelDragon, @@ -12,13 +11,13 @@ import { } from '@alilc/lowcode-types'; import { ScrollTarget as InnerScrollTarget, + IDesigner, } from '@alilc/lowcode-designer'; import { editorSymbol, designerSymbol, nodeSymbol } from '../symbols'; import { Dragon } from '../model'; import { DropLocation } from '../model/drop-location'; import { ActiveTracker } from '../model/active-tracker'; - export class Canvas implements IPublicApiCanvas { private readonly [editorSymbol]: IPublicModelEditor; diff --git a/packages/shell/src/api/common.tsx b/packages/shell/src/api/common.tsx index c77b80610..ee87bea15 100644 --- a/packages/shell/src/api/common.tsx +++ b/packages/shell/src/api/common.tsx @@ -25,7 +25,6 @@ import { IPublicTypeLocationDetailType as InnerLocationDetailType, IPublicApiCommonEditorCabin, IPublicModelDragon, - IDesigner, } from '@alilc/lowcode-types'; import { SettingField as InnerSettingField, @@ -35,6 +34,7 @@ import { ScrollTarget as InnerScrollTarget, getConvertedExtraKey as innerGetConvertedExtraKey, getOriginalExtraKey as innerGetOriginalExtraKey, + IDesigner, } from '@alilc/lowcode-designer'; import { Skeleton as InnerSkeleton, @@ -63,6 +63,7 @@ import { ReactNode, Component } from 'react'; class DesignerCabin implements IPublicApiCommonDesignerCabin { private readonly [editorSymbol]: Editor; + /** * @deprecated */ @@ -240,6 +241,7 @@ class EditorCabin implements IPublicApiCommonEditorCabin { constructor(editor: Editor) { this[editorSymbol] = editor; } + /** * Title 组件 * @experimental unstable API, pay extra caution when trying to use this @@ -335,7 +337,6 @@ class EditorCabin implements IPublicApiCommonEditorCabin { } } - export class Common implements IPublicApiCommon { private readonly __designerCabin: any; private readonly __skeletonCabin: any; diff --git a/packages/shell/src/api/workspace.ts b/packages/shell/src/api/workspace.ts index 3440871c8..f32b403b1 100644 --- a/packages/shell/src/api/workspace.ts +++ b/packages/shell/src/api/workspace.ts @@ -1,8 +1,9 @@ -import { IPublicApiWorkspace } from '@alilc/lowcode-types'; +import { IPublicApiWorkspace, IPublicResourceList, IPublicTypeResourceType } from '@alilc/lowcode-types'; import { Workspace as InnerWorkSpace } from '@alilc/lowcode-workspace'; import { Plugins } from '@alilc/lowcode-shell'; import { Window } from '../model/window'; import { workspaceSymbol } from '../symbols'; +import { Resource } from '../model'; export class Workspace implements IPublicApiWorkspace { readonly [workspaceSymbol]: InnerWorkSpace; @@ -11,6 +12,18 @@ export class Workspace implements IPublicApiWorkspace { this[workspaceSymbol] = innerWorkspace; } + get resourceList() { + return this[workspaceSymbol].getResourceList().map(d => new Resource(d)); + } + + setResourceList(resourceList: IPublicResourceList) { + this[workspaceSymbol].setResourceList(resourceList); + } + + onResourceListChange(fn: (resourceList: IPublicResourceList) => void): () => void { + return this[workspaceSymbol].onResourceListChange(fn); + } + get isActive() { return this[workspaceSymbol].isActive; } @@ -19,12 +32,12 @@ export class Workspace implements IPublicApiWorkspace { return new Window(this[workspaceSymbol].window); } - registerResourceType(resourceName: string, resourceType: 'editor', options: any): void { - this[workspaceSymbol].registerResourceType(resourceName, resourceType, options); + registerResourceType(resourceTypeModel: IPublicTypeResourceType): void { + this[workspaceSymbol].registerResourceType(resourceTypeModel); } - openEditorWindow(resourceName: string, title: string, viewType?: string) { - this[workspaceSymbol].openEditorWindow(resourceName, title, viewType); + openEditorWindow(resourceName: string, title: string, extra: Object, viewName?: string) { + this[workspaceSymbol].openEditorWindow(resourceName, title, extra, viewName); } openEditorWindowById(id: string) { diff --git a/packages/shell/src/model/detecting.ts b/packages/shell/src/model/detecting.ts index 4bfa83614..f99423eae 100644 --- a/packages/shell/src/model/detecting.ts +++ b/packages/shell/src/model/detecting.ts @@ -1,10 +1,10 @@ -import { Node } from './node'; +import { Node as ShellNode } from './node'; import { Detecting as InnerDetecting, - DocumentModel as InnerDocumentModel, + IDocumentModel as InnerDocumentModel, } from '@alilc/lowcode-designer'; import { documentSymbol, detectingSymbol } from '../symbols'; -import { IPublicModelDetecting, IPublicModelNode } from '@alilc/lowcode-types'; +import { IPublicModelDetecting, IPublicModelNode, IPublicTypeDisposable } from '@alilc/lowcode-types'; export class Detecting implements IPublicModelDetecting { private readonly [documentSymbol]: InnerDocumentModel; @@ -26,7 +26,7 @@ export class Detecting implements IPublicModelDetecting { * 当前 hover 的节点 */ get current() { - return Node.create(this[detectingSymbol].current); + return ShellNode.create(this[detectingSymbol].current); } /** @@ -52,7 +52,7 @@ export class Detecting implements IPublicModelDetecting { this[detectingSymbol].leave(this[documentSymbol]); } - onDetectingChange(fn: (node: IPublicModelNode) => void): () => void { + onDetectingChange(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable { return this[detectingSymbol].onDetectingChange(fn); } } \ No newline at end of file diff --git a/packages/shell/src/model/document-model.ts b/packages/shell/src/model/document-model.ts index f771d62ec..256cbd960 100644 --- a/packages/shell/src/model/document-model.ts +++ b/packages/shell/src/model/document-model.ts @@ -3,14 +3,13 @@ import { DocumentModel as InnerDocumentModel, Node as InnerNode, isDragNodeObject, - IOnChangeOptions as InnerOnChangeOptions, } from '@alilc/lowcode-designer'; import { IPublicEnumTransformStage, IPublicTypeRootSchema, GlobalEvent, IPublicModelDocumentModel, - IPublicOnChangeOptions, + IPublicTypeOnChangeOptions, IPublicModelDragObject, IPublicTypeDragNodeObject, IPublicTypeDragNodeDataObject, @@ -45,6 +44,7 @@ export class DocumentModel implements IPublicModelDocumentModel { selection: IPublicModelSelection; detecting: IPublicModelDetecting; history: IPublicModelHistory; + /** * @deprecated use canvas API instead */ @@ -141,6 +141,7 @@ export class DocumentModel implements IPublicModelDocumentModel { set dropLocation(loc: IPublicModelDropLocation | null) { this[documentSymbol].dropLocation = loc; } + /** * 根据 nodeId 返回 Node 实例 * get node instance by nodeId @@ -297,8 +298,8 @@ export class DocumentModel implements IPublicModelDocumentModel { * 当前 document 的节点 children 变更事件 * @param fn */ - onChangeNodeChildren(fn: (info: IPublicOnChangeOptions) => void): void { - this[documentSymbol].onChangeNodeChildren((info?: IPublicOnChangeOptions) => { + onChangeNodeChildren(fn: (info: IPublicTypeOnChangeOptions) => void): void { + this[documentSymbol].onChangeNodeChildren((info?: IPublicTypeOnChangeOptions) => { if (!info) { return; } diff --git a/packages/shell/src/model/history.ts b/packages/shell/src/model/history.ts index c6fa962cf..e872847de 100644 --- a/packages/shell/src/model/history.ts +++ b/packages/shell/src/model/history.ts @@ -1,6 +1,6 @@ import { DocumentModel as InnerDocumentModel } from '@alilc/lowcode-designer'; import { historySymbol, documentSymbol } from '../symbols'; -import { IPublicModelHistory } from '@alilc/lowcode-types'; +import { IPublicModelHistory, IPublicTypeDisposable } from '@alilc/lowcode-types'; export class History implements IPublicModelHistory { private readonly [documentSymbol]: InnerDocumentModel; @@ -54,7 +54,7 @@ export class History implements IPublicModelHistory { * 获取 state,判断当前是否为「可回退」、「可前进」的状态 * @returns */ - getState(): any { + getState(): number { return this[historySymbol].getState(); } @@ -63,7 +63,7 @@ export class History implements IPublicModelHistory { * @param func * @returns */ - onChangeState(func: () => any): () => void { + onChangeState(func: () => any): IPublicTypeDisposable { return this[historySymbol].onStateChange(func); } @@ -72,7 +72,7 @@ export class History implements IPublicModelHistory { * @param func * @returns */ - onChangeCursor(func: () => any): () => void { + onChangeCursor(func: () => any): IPublicTypeDisposable { return this[historySymbol].onCursor(func); } } diff --git a/packages/shell/src/model/index.ts b/packages/shell/src/model/index.ts index 1e0a77775..d0e45cc94 100644 --- a/packages/shell/src/model/index.ts +++ b/packages/shell/src/model/index.ts @@ -13,4 +13,5 @@ export * from './prop'; export * from './props'; export * from './selection'; export * from './setting-prop-entry'; -export * from './setting-top-entry'; \ No newline at end of file +export * from './setting-top-entry'; +export * from './resource'; \ No newline at end of file diff --git a/packages/shell/src/model/node.ts b/packages/shell/src/model/node.ts index eca07bb24..d9f86c8a8 100644 --- a/packages/shell/src/model/node.ts +++ b/packages/shell/src/model/node.ts @@ -19,12 +19,12 @@ import { IPublicModelSettingTopEntry, IPublicModelExclusiveGroup, } from '@alilc/lowcode-types'; -import { Prop } from './prop'; -import { Props } from './props'; -import { DocumentModel } from './document-model'; -import { NodeChildren } from './node-children'; -import { ComponentMeta } from './component-meta'; -import { SettingTopEntry } from './setting-top-entry'; +import { Prop as ShellProp } from './prop'; +import { Props as ShellProps } from './props'; +import { DocumentModel as ShellDocumentModel } from './document-model'; +import { NodeChildren as ShellNodeChildren } from './node-children'; +import { ComponentMeta as ShellComponentMeta } from './component-meta'; +import { SettingTopEntry as ShellSettingTopEntry } from './setting-top-entry'; import { documentSymbol, nodeSymbol } from '../symbols'; import { ReactElement } from 'react'; @@ -36,27 +36,6 @@ export class Node implements IPublicModelNode { private _id: string; - constructor(node: IPublicModelNode) { - this[nodeSymbol] = node; - this[documentSymbol] = node.document; - - this._id = this[nodeSymbol].id; - } - - static create(node: IPublicModelNode | null | undefined): IPublicModelNode | null { - if (!node) { - return null; - } - // @ts-ignore 直接返回已挂载的 shell node 实例 - if (node[shellNodeSymbol]) { - return (node as any)[shellNodeSymbol]; - } - const shellNode = new Node(node); - // @ts-ignore 挂载 shell node 实例 - node[shellNodeSymbol] = shellNode; - return shellNode; - } - /** * 节点 id */ @@ -257,7 +236,7 @@ export class Node implements IPublicModelNode { * 节点的物料元数据 */ get componentMeta(): IPublicModelComponentMeta | null { - return ComponentMeta.create(this[nodeSymbol].componentMeta); + return ShellComponentMeta.create(this[nodeSymbol].componentMeta); } /** @@ -265,7 +244,7 @@ export class Node implements IPublicModelNode { * @returns */ get document(): IPublicModelDocumentModel | null { - return DocumentModel.create(this[documentSymbol]); + return ShellDocumentModel.create(this[documentSymbol]); } /** @@ -297,7 +276,7 @@ export class Node implements IPublicModelNode { * @returns */ get children(): IPublicModelNodeChildren | null { - return NodeChildren.create(this[nodeSymbol].children); + return ShellNodeChildren.create(this[nodeSymbol].children); } /** @@ -311,14 +290,14 @@ export class Node implements IPublicModelNode { * 当前节点为插槽节点时,返回节点对应的属性实例 */ get slotFor(): IPublicModelProp | null { - return Prop.create(this[nodeSymbol].slotFor); + return ShellProp.create(this[nodeSymbol].slotFor); } /** * 返回节点的属性集 */ get props(): IPublicModelProps | null { - return Props.create(this[nodeSymbol].props); + return ShellProps.create(this[nodeSymbol].props); } /** @@ -336,7 +315,28 @@ export class Node implements IPublicModelNode { } get settingEntry(): IPublicModelSettingTopEntry { - return SettingTopEntry.create(this[nodeSymbol].settingEntry as any); + return ShellSettingTopEntry.create(this[nodeSymbol].settingEntry as any); + } + + constructor(node: IPublicModelNode) { + this[nodeSymbol] = node; + this[documentSymbol] = node.document; + + this._id = this[nodeSymbol].id; + } + + static create(node: InnerNode | null | undefined): IPublicModelNode | null { + if (!node) { + return null; + } + // @ts-ignore 直接返回已挂载的 shell node 实例 + if (node[shellNodeSymbol]) { + return (node as any)[shellNodeSymbol]; + } + const shellNode = new Node(node); + // @ts-ignore 挂载 shell node 实例 + node[shellNodeSymbol] = shellNode; + return shellNode; } /** @@ -445,7 +445,7 @@ export class Node implements IPublicModelNode { * @returns */ getProp(path: string, createIfNone = true): IPublicModelProp | null { - return Prop.create(this[nodeSymbol].getProp(path, createIfNone)); + return ShellProp.create(this[nodeSymbol].getProp(path, createIfNone)); } /** @@ -465,7 +465,7 @@ export class Node implements IPublicModelNode { * @returns */ getExtraProp(path: string, createIfNone?: boolean): IPublicModelProp | null { - return Prop.create(this[nodeSymbol].getExtraProp(path, createIfNone)); + return ShellProp.create(this[nodeSymbol].getExtraProp(path, createIfNone)); } /** @@ -591,12 +591,13 @@ export class Node implements IPublicModelNode { remove(): void { this[nodeSymbol].remove(); } + /** * @deprecated * 设置为磁贴布局节点 */ set isRGLContainer(flag: boolean) { - this[nodeSymbol].isRGLContainer = flag; + this[nodeSymbol].isRGLContainerNode = flag; } /** @@ -605,14 +606,14 @@ export class Node implements IPublicModelNode { * @returns Boolean */ get isRGLContainer() { - return this[nodeSymbol].isRGLContainer; + return this[nodeSymbol].isRGLContainerNode; } /** * 设置为磁贴布局节点 */ set isRGLContainerNode(flag: boolean) { - this[nodeSymbol].isRGLContainer = flag; + this[nodeSymbol].isRGLContainerNode = flag; } /** @@ -620,7 +621,7 @@ export class Node implements IPublicModelNode { * @returns Boolean */ get isRGLContainerNode() { - return this[nodeSymbol].isRGLContainer; + return this[nodeSymbol].isRGLContainerNode; } internalToShellNode() { diff --git a/packages/shell/src/model/prop.ts b/packages/shell/src/model/prop.ts index 3630ced94..8d4ca7842 100644 --- a/packages/shell/src/model/prop.ts +++ b/packages/shell/src/model/prop.ts @@ -1,7 +1,7 @@ -import { IPropParent as InnerProp } from '@alilc/lowcode-designer'; +import { IProp as InnerProp } from '@alilc/lowcode-designer'; import { IPublicTypeCompositeValue, IPublicEnumTransformStage, IPublicModelProp, IPublicModelNode } from '@alilc/lowcode-types'; import { propSymbol } from '../symbols'; -import { Node } from './node'; +import { Node as ShellNode } from './node'; export class Prop implements IPublicModelProp { private readonly [propSymbol]: InnerProp; @@ -26,6 +26,7 @@ export class Prop implements IPublicModelProp { /** * key 值 + * get key of prop */ get key(): string | number | undefined { return this[propSymbol].key; @@ -34,7 +35,7 @@ export class Prop implements IPublicModelProp { /** * 返回当前 prop 的路径 */ - get path(): any[] { + get path(): string[] { return this[propSymbol].path; } @@ -42,14 +43,14 @@ export class Prop implements IPublicModelProp { * 返回所属的节点实例 */ get node(): IPublicModelNode | null { - return Node.create(this[propSymbol].getNode()); + return ShellNode.create(this[propSymbol].getNode()); } /** * return the slot node (only if the current prop represents a slot) */ get slotNode(): IPublicModelNode | null { - return Node.create(this[propSymbol].slotNode); + return ShellNode.create(this[propSymbol].slotNode); } /** diff --git a/packages/shell/src/model/props.ts b/packages/shell/src/model/props.ts index 5261ac69a..98d0ef143 100644 --- a/packages/shell/src/model/props.ts +++ b/packages/shell/src/model/props.ts @@ -1,8 +1,8 @@ -import { IPropParent as InnerProps, getConvertedExtraKey } from '@alilc/lowcode-designer'; +import { IProps as InnerProps, getConvertedExtraKey } from '@alilc/lowcode-designer'; import { IPublicTypeCompositeValue, IPublicModelProps, IPublicModelNode, IPublicModelProp } from '@alilc/lowcode-types'; import { propsSymbol } from '../symbols'; -import { Node } from './node'; -import { Prop } from './prop'; +import { Node as ShellNode } from './node'; +import { Prop as ShellProp } from './prop'; export class Props implements IPublicModelProps { private readonly [propsSymbol]: InnerProps; @@ -36,7 +36,7 @@ export class Props implements IPublicModelProps { * 返回所属的 node 实例 */ get node(): IPublicModelNode | null { - return Node.create(this[propsSymbol].getNode()); + return ShellNode.create(this[propsSymbol].getNode()); } /** @@ -45,7 +45,7 @@ export class Props implements IPublicModelProps { * @returns */ getProp(path: string): IPublicModelProp | null { - return Prop.create(this[propsSymbol].getProp(path)); + return ShellProp.create(this[propsSymbol].getProp(path)); } /** @@ -64,7 +64,7 @@ export class Props implements IPublicModelProps { * @returns */ getExtraProp(path: string): IPublicModelProp | null { - return Prop.create(this[propsSymbol].getProp(getConvertedExtraKey(path))); + return ShellProp.create(this[propsSymbol].getProp(getConvertedExtraKey(path))); } /** diff --git a/packages/shell/src/model/resource.ts b/packages/shell/src/model/resource.ts new file mode 100644 index 000000000..0ca3f2445 --- /dev/null +++ b/packages/shell/src/model/resource.ts @@ -0,0 +1,36 @@ +import { IPublicModelResource } from '@alilc/lowcode-types'; +import { Resource as InnerResource } from '@alilc/lowcode-workspace'; +import { resourceSymbol } from '../symbols'; +import { ResourceType } from './resource-type'; + +export class Resource implements IPublicModelResource { + readonly [resourceSymbol]: InnerResource; + + constructor(resource: InnerResource) { + this[resourceSymbol] = resource; + } + + get title() { + return this[resourceSymbol].title; + } + + get icon() { + return this[resourceSymbol].icon; + } + + get options() { + return this[resourceSymbol].options; + } + + get name() { + return this[resourceSymbol].resourceType.name; + } + + get type() { + return this[resourceSymbol].resourceType.type; + } + + get category() { + return this[resourceSymbol].category; + } +} \ No newline at end of file diff --git a/packages/shell/src/model/selection.ts b/packages/shell/src/model/selection.ts index 92f0453ee..ff2124bec 100644 --- a/packages/shell/src/model/selection.ts +++ b/packages/shell/src/model/selection.ts @@ -1,19 +1,17 @@ import { - DocumentModel as InnerDocumentModel, - Node as InnerNode, - Selection as InnerSelection, + IDocumentModel as InnerDocumentModel, + INode as InnerNode, + ISelection as InnerSelection, } from '@alilc/lowcode-designer'; -import { Node } from './node'; -import { selectionSymbol, documentSymbol } from '../symbols'; -import { IPublicModelSelection, IPublicModelNode } from '@alilc/lowcode-types'; +import { Node as ShellNode } from './node'; +import { selectionSymbol } from '../symbols'; +import { IPublicModelSelection, IPublicModelNode, IPublicTypeDisposable } from '@alilc/lowcode-types'; export class Selection implements IPublicModelSelection { private readonly [selectionSymbol]: InnerSelection; - private readonly [documentSymbol]: InnerDocumentModel; constructor(document: InnerDocumentModel) { this[selectionSymbol] = document.selection; - this[documentSymbol] = document; } /** @@ -83,8 +81,16 @@ export class Selection implements IPublicModelSelection { * 获取选中的节点实例 * @returns */ - getNodes(): Array { - return this[selectionSymbol].getNodes().map((node: InnerNode) => Node.create(node)); + getNodes(): IPublicModelNode[] { + const innerNodes = this[selectionSymbol].getNodes(); + const nodes: IPublicModelNode[] = []; + innerNodes.forEach((node: InnerNode) => { + const shellNode = ShellNode.create(node); + if (shellNode) { + nodes.push(shellNode); + } + }); + return nodes; } /** @@ -94,12 +100,19 @@ export class Selection implements IPublicModelSelection { * getTopNodes() will return [A, B], subA will be removed * @returns */ - getTopNodes(includeRoot: boolean = false): Array { - return this[selectionSymbol].getTopNodes(includeRoot).map((node: InnerNode) => Node.create(node)); + getTopNodes(includeRoot: boolean = false): IPublicModelNode[] { + const innerNodes = this[selectionSymbol].getTopNodes(includeRoot); + const nodes: IPublicModelNode[] = []; + innerNodes.forEach((node: InnerNode) => { + const shellNode = ShellNode.create(node); + if (shellNode) { + nodes.push(shellNode); + } + }); + return nodes; } - - onSelectionChange(fn: (ids: string[]) => void): () => void { + onSelectionChange(fn: (ids: string[]) => void): IPublicTypeDisposable { return this[selectionSymbol].onSelectionChange(fn); } } diff --git a/packages/shell/src/model/window.ts b/packages/shell/src/model/window.ts index fee783f53..8645e3e09 100644 --- a/packages/shell/src/model/window.ts +++ b/packages/shell/src/model/window.ts @@ -1,12 +1,13 @@ import { windowSymbol } from '../symbols'; -import { IPublicModelWindow } from '@alilc/lowcode-types'; +import { IPublicModelResource, IPublicModelWindow } from '@alilc/lowcode-types'; import { EditorWindow } from '@alilc/lowcode-workspace'; +import { Resource } from './resource'; export class Window implements IPublicModelWindow { private readonly [windowSymbol]: EditorWindow; get id() { - return this[windowSymbol].id; + return this[windowSymbol]?.id; } get title() { @@ -17,8 +18,8 @@ export class Window implements IPublicModelWindow { return this[windowSymbol].icon; } - get resourceName() { - return this[windowSymbol].resourceName; + get resource(): IPublicModelResource { + return new Resource(this[windowSymbol].resource); } constructor(editorWindow: EditorWindow) { diff --git a/packages/shell/src/symbols.ts b/packages/shell/src/symbols.ts index b87e1f24b..cd164f62a 100644 --- a/packages/shell/src/symbols.ts +++ b/packages/shell/src/symbols.ts @@ -28,4 +28,6 @@ export const hotkeySymbol = Symbol('hotkey'); export const pluginsSymbol = Symbol('plugins'); export const workspaceSymbol = Symbol('workspace'); export const windowSymbol = Symbol('window'); -export const pluginInstanceSymbol = Symbol('plugin-instance'); \ No newline at end of file +export const pluginInstanceSymbol = Symbol('plugin-instance'); +export const resourceTypeSymbol = Symbol('resourceType'); +export const resourceSymbol = Symbol('resource'); \ No newline at end of file diff --git a/packages/types/src/activity.ts b/packages/types/src/activity.ts index ee0ce859a..8549df7b3 100644 --- a/packages/types/src/activity.ts +++ b/packages/types/src/activity.ts @@ -7,7 +7,7 @@ export enum ActivityType { 'COMPOSITE' = 'composite', } -export interface IActivityPayload { +interface IActivityPayload { schema: IPublicTypeNodeSchema; location?: { parent: { @@ -20,6 +20,10 @@ export interface IActivityPayload { newValue: any; } +/** + * TODO: not sure if this is used anywhere + * @deprecated + */ export type ActivityData = { type: ActivityType; payload: IActivityPayload; diff --git a/packages/types/src/assets.ts b/packages/types/src/assets.ts index f7bdfa022..852ed0038 100644 --- a/packages/types/src/assets.ts +++ b/packages/types/src/assets.ts @@ -1,12 +1,3 @@ - -export interface AssetItem { - type: AssetType; - content?: string | null; - device?: string; - level?: AssetLevel; - id?: string; -} - export enum AssetLevel { // 环境依赖库 比如 react, react-dom Environment = 1, @@ -41,12 +32,20 @@ export enum AssetType { Bundle = 'bundle', } +export interface AssetItem { + type: AssetType; + content?: string | null; + device?: string; + level?: AssetLevel; + id?: string; +} + +export type AssetList = Array; + +export type Asset = AssetList | AssetBundle | AssetItem | URL; + export interface AssetBundle { type: AssetType.Bundle; level?: AssetLevel; assets?: Asset | AssetList | null; } - -export type Asset = AssetList | AssetBundle | AssetItem | URL; - -export type AssetList = Array; diff --git a/packages/types/src/designer.ts b/packages/types/src/designer.ts deleted file mode 100644 index 55f1c8e0b..000000000 --- a/packages/types/src/designer.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { IPublicModelNode, IPublicModelDragon, IPublicModelDropLocation, IPublicModelScroller, IPublicModelScrollable, IPublicTypeComponentInstance, IPublicTypeLocationData, IPublicModelActiveTracker } from './shell'; - - -export interface IPublicOnChangeOptions { - type: string; - node: IPublicModelNode; -} - - -export interface NodeInstance { - docId: string; - nodeId: string; - instance: T; - node?: Node | null; -} - -export interface IDesigner { - get dragon(): IPublicModelDragon; - get activeTracker(): IPublicModelActiveTracker; - createScroller(scrollable: IPublicModelScrollable): IPublicModelScroller; - /** - * 创建插入位置,考虑放到 dragon 中 - */ - createLocation(locationData: IPublicTypeLocationData): IPublicModelDropLocation; -} diff --git a/packages/types/src/dragon.ts b/packages/types/src/dragon.ts deleted file mode 100644 index 188397e0a..000000000 --- a/packages/types/src/dragon.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { NodeInstance } from './designer'; -import { - IPublicModelDocumentModel, - IPublicModelLocateEvent, - IPublicModelDropLocation, - IPublicTypeComponentInstance, -} from './shell'; -import { IPublicTypeDragObject } from './shell/type/drag-object'; - - -export interface LocateEvent { - readonly type: 'LocateEvent'; - /** - * 浏览器窗口坐标系 - */ - readonly globalX: number; - readonly globalY: number; - /** - * 原始事件 - */ - readonly originalEvent: MouseEvent | DragEvent; - /** - * 拖拽对象 - */ - readonly dragObject: IPublicTypeDragObject; - - /** - * 激活的感应器 - */ - sensor?: ISensor; - - // ======= 以下是 激活的 sensor 将填充的值 ======== - /** - * 浏览器事件响应目标 - */ - target?: Element | null; - /** - * 当前激活文档画布坐标系 - */ - canvasX?: number; - canvasY?: number; - /** - * 激活或目标文档 - */ - documentModel?: IPublicModelDocumentModel; - /** - * 事件订正标识,初始构造时,从发起端构造,缺少 canvasX,canvasY, 需要经过订正才有 - */ - fixed?: true; -} - - -/** - * 拖拽敏感板 - */ -export interface ISensor { - /** - * 是否可响应,比如面板被隐藏,可设置该值 false - */ - readonly sensorAvailable: boolean; - /** - * 给事件打补丁 - */ - fixEvent(e: IPublicModelLocateEvent): IPublicModelLocateEvent; - /** - * 定位并激活 - */ - locate(e: IPublicModelLocateEvent): IPublicModelDropLocation | undefined | null; - /** - * 是否进入敏感板区域 - */ - isEnter(e: IPublicModelLocateEvent): boolean; - /** - * 取消激活 - */ - deactiveSensor(): void; - - /** - * 获取节点实例 - */ - getNodeInstanceFromElement?: (e: Element | null) => NodeInstance | null; -} \ No newline at end of file diff --git a/packages/types/src/editor-skeleton.ts b/packages/types/src/editor-skeleton.ts deleted file mode 100644 index f04e89933..000000000 --- a/packages/types/src/editor-skeleton.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface IArea { - isEmpty(): boolean; - add(config: T | C): T; - remove(config: T | string): number; - setVisible(flag: boolean): void; - hide(): void; - show(): void; -} \ No newline at end of file diff --git a/packages/types/src/editor.ts b/packages/types/src/editor.ts index 1334f4439..3691a7f94 100644 --- a/packages/types/src/editor.ts +++ b/packages/types/src/editor.ts @@ -1,34 +1,6 @@ import { ReactNode, ComponentType } from 'react'; import { IPublicTypeNpmInfo, IPublicModelEditor } from './shell'; -export type KeyType = (new (...args: any[]) => any) | symbol | string; -export type ClassType = new (...args: any[]) => any; -export interface GetOptions { - forceNew?: boolean; - sourceCls?: ClassType; -} -export type GetReturnType = T extends undefined - ? ClsType extends { - prototype: infer R; - } - ? R - : any - : T; - -/** - * duck-typed power-di - * - * @see https://www.npmjs.com/package/power-di - */ -export interface PowerDIRegisterOptions { - /** default: true */ - singleton?: boolean; - /** if data a class, auto new a instance. - * if data a function, auto run(lazy). - * default: true */ - autoNew?: boolean; -} - export interface EditorConfig { skeleton?: SkeletonConfig; theme?: ThemeConfig; @@ -172,10 +144,4 @@ export interface PluginStatus { export interface PluginStatusSet { [key: string]: PluginStatus; -} - -export enum EDITOR_EVENT { - NODE_CHILDREN_CHANGE = 'node.children.change', - - NODE_VISIBLE_CHANGE = 'node.visible.change', } \ No newline at end of file diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 5e7217550..d14dc9b99 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,29 +1,10 @@ export * from '@alilc/lowcode-datasource-types'; export * from './editor'; -export * from './shell/type/field-extra-props'; -export * from './shell/type/i18n-map'; -export * from './shell/type/icon-config'; -export * from './shell/type/metadata'; -export * from './shell/type/npm'; -export * from './shell/type/prop-types'; -export * from './schema'; export * from './activity'; -export * from './shell/type/tip-config'; -export * from './shell/type/title-content'; -export * from './utils'; -export * from './shell/type/value-type'; -export * from './shell/type/setter-config'; -export * from './shell/model/setting-target'; -export * from './node'; -export * from './shell/enum/transform-stage'; export * from './code-intermediate'; export * from './code-result'; export * from './assets'; export * as GlobalEvent from './event'; -export * from './shell/type/props-transducer'; -export * from './editor-skeleton'; -export * from './designer'; -export * from './dragon'; export * from './shell'; export * from './shell-model-factory'; // TODO: remove this in future versions diff --git a/packages/types/src/node.ts b/packages/types/src/node.ts deleted file mode 100644 index 732a38c9c..000000000 --- a/packages/types/src/node.ts +++ /dev/null @@ -1,10 +0,0 @@ - -export interface NodeStatus { - locking: boolean; - pseudo: boolean; - inPlaceEditing: boolean; -} - -export interface LeafNode extends Node { - readonly children: null; -} diff --git a/packages/types/src/schema.ts b/packages/types/src/schema.ts deleted file mode 100644 index 0641b057c..000000000 --- a/packages/types/src/schema.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { IPublicTypeNodeSchema } from './shell/type/node-schema'; -import { IPublicTypeNodeData } from './shell/type/node-data'; - -export type NodeDataType = IPublicTypeNodeData | IPublicTypeNodeData[]; -/** - * Slot schema 描述 - */ -export interface SlotSchema extends IPublicTypeNodeSchema { - componentName: 'Slot'; - name?: string; - title?: string; - params?: string[]; - props?: { - slotTitle?: string; - slotName?: string; - slotParams?: string[]; - }; - children?: IPublicTypeNodeSchema[]; -} diff --git a/packages/types/src/shell/api/workspace.ts b/packages/types/src/shell/api/workspace.ts index b9cd29afa..ca4b3cc34 100644 --- a/packages/types/src/shell/api/workspace.ts +++ b/packages/types/src/shell/api/workspace.ts @@ -1,31 +1,46 @@ import { IPublicModelWindow } from '../model'; -import { IPublicResourceOptions } from '../type'; -import { IPublicApiPlugins } from '@alilc/lowcode-types'; +import { IPublicApiPlugins, IPublicModelResource, IPublicResourceList, IPublicTypeResourceType } from '@alilc/lowcode-types'; export interface IPublicApiWorkspace { + /** 是否启用 workspace 模式 */ isActive: boolean; /** 当前设计器窗口 */ window: IPublicModelWindow; - /** 注册资源 */ - registerResourceType(resourceName: string, resourceType: 'editor', options: IPublicResourceOptions): void; - - /** 打开视图窗口 */ - openEditorWindow(resourceName: string, title: string, viewType?: string): void; - - /** 移除窗口 */ - removeEditorWindow(resourceName: string, title: string): void; - plugins: IPublicApiPlugins; /** 当前设计器的编辑窗口 */ windows: IPublicModelWindow[]; + /** 获取资源树列表 */ + get resourceList(): IPublicModelResource[]; + + /** 设置资源树列表 */ + setResourceList(resourceList: IPublicResourceList): void; + + /** 资源树列表更新事件 */ + onResourceListChange(fn: (resourceList: IPublicResourceList) => void): () => void; + + /** 注册资源 */ + registerResourceType(resourceTypeModel: IPublicTypeResourceType): void; + + /** 打开视图窗口 */ + openEditorWindow(resourceName: string, title: string, extra: Object, viewName?: string): void; + + /** 通过视图 id 打开窗口 */ + openEditorWindowById(id: string): void; + + /** 移除视图窗口 */ + removeEditorWindow(resourceName: string, title: string): void; + + /** 通过视图 id 移除窗口 */ + removeEditorWindowById(id: string): void; + /** 窗口新增/删除的事件 */ - onChangeWindows: (fn: () => void) => void; + onChangeWindows(fn: () => void): void; /** active 窗口变更事件 */ - onChangeActiveWindow: (fn: () => void) => void; + onChangeActiveWindow(fn: () => void): void; } \ No newline at end of file diff --git a/packages/types/src/shell/model/detecting.ts b/packages/types/src/shell/model/detecting.ts index 8e82f7449..d828ace32 100644 --- a/packages/types/src/shell/model/detecting.ts +++ b/packages/types/src/shell/model/detecting.ts @@ -1,39 +1,46 @@ import { IPublicModelNode } from './'; +import { IPublicTypeDisposable } from '../type'; export interface IPublicModelDetecting { /** - * 控制大纲树 hover 时是否出现悬停效果 + * 是否启用 + * check if current detecting is enabled + * @since v1.1.0 */ get enable(): boolean; /** * 当前 hover 的节点 + * get current hovering node * @since v1.0.16 */ - get current(): any; + get current(): IPublicModelNode | null; /** * hover 指定节点 + * capture node with nodeId * @param id 节点 id */ - capture(id: string): any; + capture(id: string): void; /** * hover 离开指定节点 + * release node with nodeId * @param id 节点 id */ - release(id: string): any; + release(id: string): void; /** * 清空 hover 态 + * clear all hover state */ - leave(): any; + leave(): void; /** * hover 节点变化事件 * set callback which will be called when hovering object changed. * @since v1.1.0 */ - onDetectingChange(fn: (node: IPublicModelNode) => void): () => void; + onDetectingChange(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable; } diff --git a/packages/types/src/shell/model/document-model.ts b/packages/types/src/shell/model/document-model.ts index 48ad9cbd3..e43d409a9 100644 --- a/packages/types/src/shell/model/document-model.ts +++ b/packages/types/src/shell/model/document-model.ts @@ -2,7 +2,7 @@ import { IPublicTypeRootSchema, IPublicTypeDragNodeDataObject, IPublicTypeDragNo import { IPublicEnumTransformStage } from '../enum'; import { IPublicApiProject } from '../api'; import { IPublicModelDropLocation, IPublicModelDetecting, IPublicModelNode, IPublicModelSelection, IPublicModelHistory, IPublicModelModalNodesManager } from './'; -import { IPublicOnChangeOptions } from '@alilc/lowcode-types'; +import { IPublicTypeOnChangeOptions } from '@alilc/lowcode-types'; export interface IPublicModelDocumentModel { @@ -168,12 +168,11 @@ export interface IPublicModelDocumentModel { */ onChangeNodeVisible(fn: (node: IPublicModelNode, visible: boolean) => void): void; - /** * 当前 document 的节点 children 变更事件 * @param fn */ - onChangeNodeChildren(fn: (info: IPublicOnChangeOptions) => void): void; + onChangeNodeChildren(fn: (info: IPublicTypeOnChangeOptions) => void): void; /** * 当前 document 节点属性修改事件 @@ -210,7 +209,6 @@ export interface IPublicModelDocumentModel { */ set dropLocation(loc: IPublicModelDropLocation | null); - /** * 设置聚焦节点变化的回调 * triggered focused node is set mannually from plugin diff --git a/packages/types/src/shell/model/editor.ts b/packages/types/src/shell/model/editor.ts index b6045b910..4f7211387 100644 --- a/packages/types/src/shell/model/editor.ts +++ b/packages/types/src/shell/model/editor.ts @@ -3,27 +3,26 @@ import { EventEmitter } from 'events'; import StrictEventEmitter from 'strict-event-emitter-types'; import * as GlobalEvent from '../../event'; import { IPublicApiEvent } from '../api'; -import { GetOptions, GetReturnType, KeyType, PowerDIRegisterOptions } from '../../editor'; - +import { IPublicTypeEditorValueKey, IPublicTypeEditorGetOptions, IPublicTypeEditorGetResult, IPublicTypeEditorRegisterOptions } from '../type'; export interface IPublicModelEditor extends StrictEventEmitter { get: ( keyOrType: KeyOrType, - opt?: GetOptions - ) => GetReturnType | undefined; + opt?: IPublicTypeEditorGetOptions + ) => IPublicTypeEditorGetResult | undefined; - has: (keyOrType: KeyType) => boolean; + has: (keyOrType: IPublicTypeEditorValueKey) => boolean; - set: (key: KeyType, data: any) => void | Promise; + set: (key: IPublicTypeEditorValueKey, data: any) => void | Promise; - onceGot: (keyOrType: KeyOrType) => Promise>; + onceGot: (keyOrType: KeyOrType) => Promise>; - onGot: ( + onGot: ( keyOrType: KeyOrType, - fn: (data: GetReturnType) => void + fn: (data: IPublicTypeEditorGetResult) => void ) => () => void; - register: (data: any, key?: KeyType, options?: PowerDIRegisterOptions) => void; + register: (data: any, key?: IPublicTypeEditorValueKey, options?: IPublicTypeEditorRegisterOptions) => void; get eventBus(): IPublicApiEvent; } diff --git a/packages/types/src/shell/model/history.ts b/packages/types/src/shell/model/history.ts index 1c0020de3..9d75295ab 100644 --- a/packages/types/src/shell/model/history.ts +++ b/packages/types/src/shell/model/history.ts @@ -1,49 +1,62 @@ +import { IPublicTypeDisposable } from '../type'; + export interface IPublicModelHistory { /** * 历史记录跳转到指定位置 + * go to a specific history * @param cursor */ go(cursor: number): void; /** * 历史记录后退 + * go backward in history */ back(): void; /** * 历史记录前进 + * go forward in history */ forward(): void; /** * 保存当前状态 + * do save current change as a record in history */ savePoint(): void; /** * 当前是否是「保存点」,即是否有状态变更但未保存 - * @returns + * check if there is unsaved change for history */ isSavePoint(): boolean; /** * 获取 state,判断当前是否为「可回退」、「可前进」的状态 - * @returns + * get flags in number which indicat current change state + * + * | 1 | 1 | 1 | + * | -------- | -------- | -------- | + * | modified | redoable | undoable | + * eg. + * 7 means : modified && redoable && undoable + * 5 means : modified && undoable */ - getState(): any; + getState(): number; /** * 监听 state 变更事件 + * monitor on stateChange event * @param func - * @returns */ - onChangeState(func: () => any): () => void; + onChangeState(func: () => any): IPublicTypeDisposable; /** * 监听历史记录游标位置变更事件 + * monitor on cursorChange event * @param func - * @returns */ - onChangeCursor(func: () => any): () => void; + onChangeCursor(func: () => any): IPublicTypeDisposable; } diff --git a/packages/types/src/shell/model/index.ts b/packages/types/src/shell/model/index.ts index 3b6850959..67335e062 100644 --- a/packages/types/src/shell/model/index.ts +++ b/packages/types/src/shell/model/index.ts @@ -27,3 +27,5 @@ export * from './engine-config'; export * from './editor'; export * from './preference'; export * from './plugin-instance'; +export * from './sensor'; +export * from './resource'; diff --git a/packages/types/src/shell/model/node.ts b/packages/types/src/shell/model/node.ts index 2350b5291..70c92016b 100644 --- a/packages/types/src/shell/model/node.ts +++ b/packages/types/src/shell/model/node.ts @@ -4,6 +4,7 @@ import { IPublicEnumTransformStage } from '../enum'; import { IPublicModelNodeChildren, IPublicModelComponentMeta, IPublicModelProp, IPublicModelProps, IPublicModelSettingTopEntry, IPublicModelDocumentModel, IPublicModelExclusiveGroup } from './'; export interface IPublicModelNode { + /** * 节点 id * node id diff --git a/packages/types/src/shell/model/prop.ts b/packages/types/src/shell/model/prop.ts index 7cec09b22..7ac906762 100644 --- a/packages/types/src/shell/model/prop.ts +++ b/packages/types/src/shell/model/prop.ts @@ -3,6 +3,7 @@ import { IPublicTypeCompositeValue } from '../type'; import { IPublicModelNode } from './'; export interface IPublicModelProp { + /** * id */ @@ -10,50 +11,60 @@ export interface IPublicModelProp { /** * key 值 + * get key of prop */ get key(): string | number | undefined; /** * 返回当前 prop 的路径 + * get path of current prop */ - get path(): any[]; + get path(): string[]; /** * 返回所属的节点实例 + * get node instance, which this prop belongs to */ get node(): IPublicModelNode | null; /** + * 当本 prop 代表一个 Slot 时,返回对应的 slotNode * return the slot node (only if the current prop represents a slot) + * @since v1.1.0 */ - get slotNode(): IPublicModelNode | null; + get slotNode(): IPublicModelNode | undefined | null; /** - * judge if it is a prop or not + * 是否是 Prop , 固定返回 true + * check if it is a prop or not, and of course always return true + * @experimental */ get isProp(): boolean; /** * 设置值 + * set value for this prop * @param val */ setValue(val: IPublicTypeCompositeValue): void; /** * 获取值 - * @returns + * get value of this prop */ getValue(): any; /** * 移除值 + * remove value of this prop + * @since v1.0.16 */ remove(): void; /** * 导出值 + * export schema * @param stage - * @returns */ exportSchema(stage: IPublicEnumTransformStage): IPublicTypeCompositeValue; } diff --git a/packages/types/src/shell/model/props.ts b/packages/types/src/shell/model/props.ts index 8d01ade13..9c862a008 100644 --- a/packages/types/src/shell/model/props.ts +++ b/packages/types/src/shell/model/props.ts @@ -2,6 +2,7 @@ import { IPublicTypeCompositeValue } from '../type'; import { IPublicModelNode, IPublicModelProp } from './'; export interface IPublicModelProps { + /** * id */ @@ -9,8 +10,9 @@ export interface IPublicModelProps { /** * 返回当前 props 的路径 + * return path of current props */ - get path(): any[]; + get path(): string[]; /** * 返回所属的 node 实例 @@ -19,62 +21,64 @@ export interface IPublicModelProps { /** * 获取指定 path 的属性模型实例 + * get prop by path * @param path 属性路径,支持 a / a.b / a.0 等格式 - * @returns */ getProp(path: string): IPublicModelProp | null; /** * 获取指定 path 的属性模型实例值 + * get value of prop by path * @param path 属性路径,支持 a / a.b / a.0 等格式 - * @returns */ getPropValue(path: string): any; /** * 获取指定 path 的属性模型实例, * 注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 + * get extra prop by path * @param path 属性路径,支持 a / a.b / a.0 等格式 - * @returns */ getExtraProp(path: string): IPublicModelProp | null; /** * 获取指定 path 的属性模型实例值 * 注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 + * get value of extra prop by path * @param path 属性路径,支持 a / a.b / a.0 等格式 - * @returns */ getExtraPropValue(path: string): any; /** * 设置指定 path 的属性模型实例值 + * set value of prop by path * @param path 属性路径,支持 a / a.b / a.0 等格式 * @param value 值 - * @returns */ setPropValue(path: string, value: IPublicTypeCompositeValue): void; /** * 设置指定 path 的属性模型实例值 + * set value of extra prop by path * @param path 属性路径,支持 a / a.b / a.0 等格式 * @param value 值 - * @returns */ setExtraPropValue(path: string, value: IPublicTypeCompositeValue): void; /** - * test if the specified key is existing or not. + * 当前 props 是否包含某 prop + * check if the specified key is existing or not. * @param key - * @returns + * @since v1.1.0 */ has(key: string): boolean; /** + * 添加一个 prop * add a key with given value * @param value * @param key - * @returns + * @since v1.1.0 */ add(value: IPublicTypeCompositeValue, key?: string | number | undefined): any; diff --git a/packages/types/src/shell/model/resource.ts b/packages/types/src/shell/model/resource.ts new file mode 100644 index 000000000..cfa48f189 --- /dev/null +++ b/packages/types/src/shell/model/resource.ts @@ -0,0 +1,16 @@ +import { ReactElement } from 'react'; +import { IPublicModelResourceType } from './resource-type'; + +export interface IPublicModelResource { + get title(): string | undefined; + + get icon(): ReactElement | undefined; + + get options(): Object; + + get name(): string | undefined; + + get type(): string | undefined; + + get category(): string | undefined; +} \ No newline at end of file diff --git a/packages/types/src/shell/model/selection.ts b/packages/types/src/shell/model/selection.ts index bfc0b75ce..3b2fdb2f5 100644 --- a/packages/types/src/shell/model/selection.ts +++ b/packages/types/src/shell/model/selection.ts @@ -1,66 +1,76 @@ import { IPublicModelNode } from './'; +import { IPublicTypeDisposable } from '../type'; export interface IPublicModelSelection { /** * 返回选中的节点 id + * get ids of selected nodes */ get selected(): string[]; /** - * return selected Node instance + * 返回选中的节点(如多个节点只返回第一个) + * return selected Node instance,return the first one if multiple nodes are selected + * @since v1.1.0 */ get node(): IPublicModelNode | null; /** * 选中指定节点(覆盖方式) + * select node with id, this will override current selection * @param id */ select(id: string): void; /** * 批量选中指定节点们 + * select node with ids, this will override current selection + * * @param ids */ selectAll(ids: string[]): void; /** * 移除选中的指定节点 + * remove node from selection with node id * @param id */ remove(id: string): void; /** * 清除所有选中节点 + * clear current selection */ clear(): void; /** * 判断是否选中了指定节点 + * check if node with specific id is selected * @param id - * @returns */ has(id: string): boolean; /** * 选中指定节点(增量方式) + * add node with specific id to selection * @param id */ add(id: string): void; /** * 获取选中的节点实例 - * @returns + * get selected nodes */ getNodes(): IPublicModelNode[]; /** * 获取选区的顶层节点 + * get seleted top nodes * for example: * getNodes() returns [A, subA, B], then * getTopNodes() will return [A, B], subA will be removed * @since v1.0.16 - * @returns */ getTopNodes(includeRoot?: boolean): IPublicModelNode[]; @@ -69,5 +79,5 @@ export interface IPublicModelSelection { * set callback which will be called when selection is changed * @since v1.1.0 */ - onSelectionChange(fn: (ids: string[]) => void): () => void; + onSelectionChange(fn: (ids: string[]) => void): IPublicTypeDisposable; } diff --git a/packages/types/src/shell/model/sensor.ts b/packages/types/src/shell/model/sensor.ts new file mode 100644 index 000000000..405cd7866 --- /dev/null +++ b/packages/types/src/shell/model/sensor.ts @@ -0,0 +1,42 @@ +import { IPublicTypeNodeInstance } from '../type/node-instance'; +import { + IPublicModelLocateEvent, + IPublicModelDropLocation, + IPublicTypeComponentInstance, +} from '..'; + +/** + * 拖拽敏感板 + */ +export interface IPublicModelSensor { + + /** + * 是否可响应,比如面板被隐藏,可设置该值 false + */ + readonly sensorAvailable: boolean; + + /** + * 给事件打补丁 + */ + fixEvent(e: IPublicModelLocateEvent): IPublicModelLocateEvent; + + /** + * 定位并激活 + */ + locate(e: IPublicModelLocateEvent): IPublicModelDropLocation | undefined | null; + + /** + * 是否进入敏感板区域 + */ + isEnter(e: IPublicModelLocateEvent): boolean; + + /** + * 取消激活 + */ + deactiveSensor(): void; + + /** + * 获取节点实例 + */ + getNodeInstanceFromElement?: (e: Element | null) => IPublicTypeNodeInstance | null; +} diff --git a/packages/types/src/shell/model/window.ts b/packages/types/src/shell/model/window.ts index 1502f2a3c..741ac1c8e 100644 --- a/packages/types/src/shell/model/window.ts +++ b/packages/types/src/shell/model/window.ts @@ -1,6 +1,21 @@ +import { ReactElement } from 'react'; import { IPublicTypeNodeSchema } from '../type'; +import { IPublicModelResource } from './resource'; export interface IPublicModelWindow { + + /** 窗口 id */ + id: string; + + /** 窗口标题 */ + title?: string; + + /** 窗口 icon */ + icon?: ReactElement; + + /** 窗口资源类型 */ + resource?: IPublicModelResource; + /** 当前窗口导入 schema */ importSchema(schema: IPublicTypeNodeSchema): void; @@ -9,13 +24,4 @@ export interface IPublicModelWindow { /** 调用当前窗口视图保存钩子 */ save(): Promise; - - /** 窗口 id */ - id: string; - - /** 窗口标题 */ - title?: string; - - /** 窗口资源名字 */ - resourceName?: string; } \ No newline at end of file diff --git a/packages/types/src/shell/type/editor-get-options.ts b/packages/types/src/shell/type/editor-get-options.ts new file mode 100644 index 000000000..ed5477057 --- /dev/null +++ b/packages/types/src/shell/type/editor-get-options.ts @@ -0,0 +1,5 @@ + +export interface IPublicTypeEditorGetOptions { + forceNew?: boolean; + sourceCls?: new (...args: any[]) => any; +} diff --git a/packages/types/src/shell/type/editor-get-result.ts b/packages/types/src/shell/type/editor-get-result.ts new file mode 100644 index 000000000..af3639ac0 --- /dev/null +++ b/packages/types/src/shell/type/editor-get-result.ts @@ -0,0 +1,4 @@ + +export type IPublicTypeEditorGetResult = T extends undefined ? ClsType extends { + prototype: infer R; +} ? R : any : T; diff --git a/packages/types/src/shell/type/editor-register-options.ts b/packages/types/src/shell/type/editor-register-options.ts new file mode 100644 index 000000000..385346581 --- /dev/null +++ b/packages/types/src/shell/type/editor-register-options.ts @@ -0,0 +1,19 @@ +/** + * duck-typed power-di + * + * @see https://www.npmjs.com/package/power-di + */ +export interface IPublicTypeEditorRegisterOptions { + + /** + * default: true + */ + singleton?: boolean; + + /** + * if data a class, auto new a instance. + * if data a function, auto run(lazy). + * default: true + */ + autoNew?: boolean; +} diff --git a/packages/types/src/shell/type/editor-value-key.ts b/packages/types/src/shell/type/editor-value-key.ts new file mode 100644 index 000000000..8c0d3c6c9 --- /dev/null +++ b/packages/types/src/shell/type/editor-value-key.ts @@ -0,0 +1,2 @@ + +export type IPublicTypeEditorValueKey = (new (...args: any[]) => any) | symbol | string; diff --git a/packages/types/src/shell/type/editor-view-config.ts b/packages/types/src/shell/type/editor-view-config.ts new file mode 100644 index 000000000..2b36a718a --- /dev/null +++ b/packages/types/src/shell/type/editor-view-config.ts @@ -0,0 +1,8 @@ +export interface IPublicEditorViewConfig { + + /** 视图初始化钩子 */ + init?: () => Promise; + + /** 资源保存时,会调用视图的钩子 */ + save?: () => Promise; +} \ No newline at end of file diff --git a/packages/types/src/shell/type/editor-view.ts b/packages/types/src/shell/type/editor-view.ts new file mode 100644 index 000000000..2357a48f5 --- /dev/null +++ b/packages/types/src/shell/type/editor-view.ts @@ -0,0 +1,12 @@ +import { IPublicEditorViewConfig } from './editor-view-config'; + +export interface IPublicTypeEditorView { + + /** 资源名字 */ + viewName: string; + + /** 资源类型 */ + viewType?: 'editor' | 'webview'; + + (ctx: any, options: any): IPublicEditorViewConfig; +} \ No newline at end of file diff --git a/packages/types/src/shell/type/index.ts b/packages/types/src/shell/type/index.ts index c1f70b605..f66094f3d 100644 --- a/packages/types/src/shell/type/index.ts +++ b/packages/types/src/shell/type/index.ts @@ -1,6 +1,6 @@ // this folder contains all interfaces/types working as type definition // - some exists as type TypeName -// - some althought exists as interfaces , but there won`t be a class implements them. +// - some althought exists as interfaces , but there won`t be any class implements them. // all of above cases will with prefix IPublicType, eg. IPublicTypeSomeName export * from './location'; export * from './active-target'; @@ -73,5 +73,17 @@ export * from './tip-config'; export * from './widget-config-area'; export * from './hotkey-callback'; export * from './plugin-register-options'; -export * from './resource-options'; -export * from './engine-options'; \ No newline at end of file +export * from './resource-list'; +export * from './engine-options'; +export * from './on-change-options'; +export * from './slot-schema'; +export * from './node-data-type'; +export * from './node-instance'; +export * from './editor-value-key'; +export * from './editor-get-options'; +export * from './editor-get-result'; +export * from './editor-register-options'; +export * from './editor-view'; +export * from './resource-type'; +export * from './resource-type-config'; +export * from './editor-view-config'; \ No newline at end of file diff --git a/packages/types/src/shell/type/node-data-type.ts b/packages/types/src/shell/type/node-data-type.ts new file mode 100644 index 000000000..d7f68041a --- /dev/null +++ b/packages/types/src/shell/type/node-data-type.ts @@ -0,0 +1,3 @@ +import { IPublicTypeNodeData } from './node-data'; + +export type IPublicTypeNodeDataType = IPublicTypeNodeData | IPublicTypeNodeData[]; diff --git a/packages/types/src/shell/type/node-instance.ts b/packages/types/src/shell/type/node-instance.ts new file mode 100644 index 000000000..e1d9789c8 --- /dev/null +++ b/packages/types/src/shell/type/node-instance.ts @@ -0,0 +1,8 @@ +import { IPublicTypeComponentInstance, IPublicModelNode } from '..'; + +export interface IPublicTypeNodeInstance { + docId: string; + nodeId: string; + instance: T; + node?: IPublicModelNode | null; +} diff --git a/packages/types/src/shell/type/on-change-options.ts b/packages/types/src/shell/type/on-change-options.ts new file mode 100644 index 000000000..a1b0c314d --- /dev/null +++ b/packages/types/src/shell/type/on-change-options.ts @@ -0,0 +1,6 @@ +import { IPublicModelNode } from '..'; + +export interface IPublicTypeOnChangeOptions { + type: string; + node: IPublicModelNode; +} diff --git a/packages/types/src/shell/type/resource-list.ts b/packages/types/src/shell/type/resource-list.ts new file mode 100644 index 000000000..e5fbcba7e --- /dev/null +++ b/packages/types/src/shell/type/resource-list.ts @@ -0,0 +1,10 @@ +export interface IPublicResourceData { + resourceName: string; + title: string; + category: string; + options: { + [key: string]: any; + }; +} + +export type IPublicResourceList = IPublicResourceData[]; \ No newline at end of file diff --git a/packages/types/src/shell/type/resource-options.ts b/packages/types/src/shell/type/resource-options.ts deleted file mode 100644 index e82db194c..000000000 --- a/packages/types/src/shell/type/resource-options.ts +++ /dev/null @@ -1,44 +0,0 @@ -export interface IPublicViewFunctions { - /** 视图初始化钩子 */ - init?: () => Promise; - /** 资源保存时,会调用视图的钩子 */ - save?: () => Promise; -} - -export interface IPublicEditorView { - /** 资源名字 */ - viewName: string; - /** 资源类型 */ - viewType?: 'editor' | 'webview'; - (ctx: any): IPublicViewFunctions; -} - -export interface IPublicResourceOptions { - /** 资源名字 */ - name: string; - - /** 资源描述 */ - description?: string; - - /** 资源 icon 标识 */ - icon?: React.ReactElement; - - /** 默认视图类型 */ - defaultViewType: string; - - /** 资源视图 */ - editorViews: IPublicEditorView[]; - - /** save 钩子 */ - save?: (schema: { - [viewName: string]: any; - }) => Promise; - - /** import 钩子 */ - import?: (schema: any) => Promise<{ - [viewName: string]: any; - }>; - - /** 默认标题 */ - defaultTitle?: string; -} \ No newline at end of file diff --git a/packages/types/src/shell/type/resource-type-config.ts b/packages/types/src/shell/type/resource-type-config.ts new file mode 100644 index 000000000..93534fea2 --- /dev/null +++ b/packages/types/src/shell/type/resource-type-config.ts @@ -0,0 +1,31 @@ +import { IPublicTypeEditorView } from './editor-view'; + +export interface IPublicResourceTypeConfig { + + /** 资源描述 */ + description?: string; + + /** 资源 icon 标识 */ + icon?: React.ReactElement; + + /** 默认视图类型 */ + defaultViewType: string; + + /** 资源视图 */ + editorViews: IPublicTypeEditorView[]; + + init?: () => void; + + /** save 钩子 */ + save?: (schema: { + [viewName: string]: any; + }) => Promise; + + /** import 钩子 */ + import?: (schema: any) => Promise<{ + [viewName: string]: any; + }>; + + /** 默认标题 */ + defaultTitle?: string; +} diff --git a/packages/types/src/shell/type/resource-type.ts b/packages/types/src/shell/type/resource-type.ts new file mode 100644 index 000000000..64ec85c79 --- /dev/null +++ b/packages/types/src/shell/type/resource-type.ts @@ -0,0 +1,10 @@ +import { IPublicModelPluginContext } from '../model'; +import { IPublicResourceTypeConfig } from './resource-type-config'; + +export interface IPublicTypeResourceType { + resourceName: string; + + resourceType: string; + + (ctx: IPublicModelPluginContext): IPublicResourceTypeConfig; +} \ No newline at end of file diff --git a/packages/types/src/shell/type/slot-schema.ts b/packages/types/src/shell/type/slot-schema.ts new file mode 100644 index 000000000..d4c1f5d97 --- /dev/null +++ b/packages/types/src/shell/type/slot-schema.ts @@ -0,0 +1,17 @@ +import { IPublicTypeNodeSchema } from './node-schema'; + +/** + * Slot schema 描述 + */ +export interface IPublicTypeSlotSchema extends IPublicTypeNodeSchema { + componentName: 'Slot'; + name?: string; + title?: string; + params?: string[]; + props?: { + slotTitle?: string; + slotName?: string; + slotParams?: string[]; + }; + children?: IPublicTypeNodeSchema[]; +} diff --git a/packages/types/src/shell/type/value-type.ts b/packages/types/src/shell/type/value-type.ts index 2be3cccb8..c0c012544 100644 --- a/packages/types/src/shell/type/value-type.ts +++ b/packages/types/src/shell/type/value-type.ts @@ -7,16 +7,19 @@ import { IPublicTypeNodeData, IPublicTypeCompositeValue, IPublicTypeNodeSchema } */ export interface IPublicTypeJSExpression { type: 'JSExpression'; + /** * 表达式字符串 */ value: string; + /** * 模拟值 * * @todo 待标准描述 */ mock?: any; + /** * 源码 * @@ -33,6 +36,7 @@ export interface IPublicTypeJSExpression { */ export interface IPublicTypeJSFunction { type: 'JSFunction'; + /** * 函数定义,或直接函数表达式 */ @@ -66,21 +70,34 @@ export interface IPublicTypeJSFunction { * 通常用于描述组件的某一个属性为 ReactNode 或 Function return ReactNode 的场景。 */ export interface IPublicTypeJSSlot { + + /** + * type + */ type: 'JSSlot'; + /** * @todo 待标准描述 */ title?: string; + + /** + * @todo 待标准描述 + */ + id?: string; + /** * 组件的某一个属性为 Function return ReactNode 时,函数的入参 * * 其子节点可以通过 this[参数名] 来获取对应的参数。 */ params?: string[]; + /** * 具体的值。 */ value?: IPublicTypeNodeData[] | IPublicTypeNodeData; + /** * @todo 待标准描述 */ diff --git a/packages/utils/src/schema.ts b/packages/utils/src/schema.ts index f82ca1a4c..58dfb3045 100644 --- a/packages/utils/src/schema.ts +++ b/packages/utils/src/schema.ts @@ -119,6 +119,10 @@ function getNodeSchemaFromPropsById(props: any, nodeId: string): IPublicTypeNode } } +/** + * TODO: not sure if this is used anywhere + * @deprecated + */ export function applyActivities(pivotSchema: IPublicTypeRootSchema, activities: any, options?: any): IPublicTypeRootSchema { let schema = { ...pivotSchema }; if (!Array.isArray(activities)) { diff --git a/packages/workspace/src/editor-view/context.ts b/packages/workspace/src/editor-view/context.ts index a845d36c1..b991532fb 100644 --- a/packages/workspace/src/editor-view/context.ts +++ b/packages/workspace/src/editor-view/context.ts @@ -1,5 +1,5 @@ import { makeObservable, obx } from '@alilc/lowcode-editor-core'; -import { IPublicEditorView, IPublicViewFunctions } from '@alilc/lowcode-types'; +import { IPublicEditorViewConfig, IPublicTypeEditorView } from '@alilc/lowcode-types'; import { flow } from 'mobx'; import { Workspace as InnerWorkspace } from '../workspace'; import { BasicContext } from '../base-context'; @@ -9,31 +9,12 @@ import { getWebviewPlugin } from '../inner-plugins/webview'; export class Context extends BasicContext { viewName = 'editor-view'; - instance: IPublicViewFunctions; + instance: IPublicEditorViewConfig; viewType: 'editor' | 'webview'; - constructor(public workspace: InnerWorkspace, public editorWindow: EditorWindow, public editorView: IPublicEditorView) { - super(workspace, editorView.viewName, editorWindow); - this.viewType = editorView.viewType || 'editor'; - this.viewName = editorView.viewName; - this.instance = editorView(this.innerPlugins._getLowCodePluginContext({ - pluginName: 'any', - })); - makeObservable(this); - } - @obx _activate = false; - setActivate = (_activate: boolean) => { - this._activate = _activate; - this.innerHotkey.activate(this._activate); - }; - - get active() { - return this._activate; - } - @obx isInit: boolean = false; init = flow(function* (this: any) { @@ -48,6 +29,25 @@ export class Context extends BasicContext { this.isInit = true; }); + constructor(public workspace: InnerWorkspace, public editorWindow: EditorWindow, public editorView: IPublicTypeEditorView, options: Object) { + super(workspace, editorView.viewName, editorWindow); + this.viewType = editorView.viewType || 'editor'; + this.viewName = editorView.viewName; + this.instance = editorView(this.innerPlugins._getLowCodePluginContext({ + pluginName: 'any', + }), options); + makeObservable(this); + } + + setActivate = (_activate: boolean) => { + this._activate = _activate; + this.innerHotkey.activate(this._activate); + }; + + get active() { + return this._activate; + } + async save() { return await this.instance?.save?.(); } diff --git a/packages/workspace/src/editor-window/context.ts b/packages/workspace/src/editor-window/context.ts index 2c1eee719..680647587 100644 --- a/packages/workspace/src/editor-window/context.ts +++ b/packages/workspace/src/editor-window/context.ts @@ -8,16 +8,16 @@ export class EditorWindow { id: string = uniqueId('window'); icon: React.ReactElement | undefined; - constructor(readonly resource: Resource, readonly workspace: Workspace, public title: string | undefined = '') { + @obx.ref editorView: Context; + + @obx editorViews: Map = new Map(); + + constructor(readonly resource: Resource, readonly workspace: Workspace, public title: string | undefined = '', private options: Object = {}) { makeObservable(this); this.init(); this.icon = resource.icon; } - get resourceName(): string { - return this.resource.options.name; - } - async importSchema(schema: any) { const newSchema = await this.resource.import(schema); @@ -72,16 +72,12 @@ export class EditorWindow { this.changeViewType(this.resource.defaultViewType); }; - @obx.ref editorView: Context; - - @obx editorViews: Map = new Map(); - initViewType = async (name: string) => { const viewInfo = this.resource.getEditorView(name); if (this.editorViews.get(name)) { return; } - const editorView = new Context(this.workspace, this, viewInfo as any); + const editorView = new Context(this.workspace, this, viewInfo as any, this.options); this.editorViews.set(name, editorView); }; diff --git a/packages/workspace/src/index.ts b/packages/workspace/src/index.ts index 91dc20a54..2f6da97a8 100644 --- a/packages/workspace/src/index.ts +++ b/packages/workspace/src/index.ts @@ -1,4 +1,4 @@ export { Workspace } from './workspace'; -export { Resource } from './resource'; export * from './editor-window/context'; export * from './layouts/workbench'; +export { Resource } from './resource'; diff --git a/packages/workspace/src/layouts/top-area.tsx b/packages/workspace/src/layouts/top-area.tsx index 457e928d2..cecaee2a7 100644 --- a/packages/workspace/src/layouts/top-area.tsx +++ b/packages/workspace/src/layouts/top-area.tsx @@ -48,9 +48,7 @@ class Contents extends Component<{ area: Area; itemClassName?: string }> { right.push(content); } }); - if (!center || !center.length) { - return null; - } + return (

{left}
diff --git a/packages/workspace/src/resource-type.ts b/packages/workspace/src/resource-type.ts new file mode 100644 index 000000000..b52183068 --- /dev/null +++ b/packages/workspace/src/resource-type.ts @@ -0,0 +1,14 @@ +import { IPublicTypeResourceType } from '@alilc/lowcode-types'; + +export class ResourceType { + constructor(readonly resourceTypeModel: IPublicTypeResourceType) { + } + + get name() { + return this.resourceTypeModel.resourceName; + } + + get type() { + return this.resourceTypeModel.resourceType; + } +} \ No newline at end of file diff --git a/packages/workspace/src/resource.ts b/packages/workspace/src/resource.ts index f881caf0f..9753339f0 100644 --- a/packages/workspace/src/resource.ts +++ b/packages/workspace/src/resource.ts @@ -1,49 +1,72 @@ -import { IPublicEditorView, IPublicResourceOptions } from '@alilc/lowcode-types'; +import { IPublicTypeEditorView, IPublicModelResource, IPublicResourceData, IPublicResourceTypeConfig } from '@alilc/lowcode-types'; +import { Logger } from '@alilc/lowcode-utils'; +import { BasicContext } from './base-context'; +import { ResourceType } from './resource-type'; +import { Workspace as InnerWorkSpace } from './workspace'; -export class Resource { - constructor(options: IPublicResourceOptions) { - if (options.editorViews) { - options.editorViews.forEach((d: any) => { +const logger = new Logger({ level: 'warn', bizName: 'workspace:resource' }); + +export class Resource implements IPublicModelResource { + resourceTypeInstance: IPublicResourceTypeConfig; + + editorViewMap: Map = new Map(); + + constructor(readonly resourceData: IPublicResourceData, readonly resourceType: ResourceType, workspace: InnerWorkSpace) { + this.resourceTypeInstance = resourceType.resourceTypeModel(new BasicContext(workspace, ''), {}); + if (this.resourceTypeInstance.editorViews) { + this.resourceTypeInstance.editorViews.forEach((d: any) => { this.editorViewMap.set(d.viewName, d); }); } - - this.options = options; + if (!resourceType) { + logger.error(`resourceType[${resourceType}] is unValid.`); + } } - options: IPublicResourceOptions; + get name() { + return this.resourceType.name; + } - editorViewMap: Map = new Map(); - - init(ctx: any) { - this.options.init(ctx); + get description() { + return this.resourceTypeInstance?.description; } get icon() { - return this.options.icon; + return this.resourceTypeInstance?.icon; + } + + get type() { + return this.resourceType.type; + } + + get title(): string | undefined { + return this.resourceData.title || this.resourceTypeInstance.defaultTitle; + } + + get options() { + return this.resourceData.options; + } + + get category() { + return this.resourceData?.category; } async import(schema: any) { - return await this.options.import?.(schema); + return await this.resourceTypeInstance.import?.(schema); + } + async save(value: any) { + return await this.resourceTypeInstance.save?.(value); + } + + get editorViews() { + return this.resourceTypeInstance.editorViews; + } + + get defaultViewType() { + return this.resourceTypeInstance.defaultViewType; } getEditorView(name: string) { return this.editorViewMap.get(name); } - - get defaultViewType() { - return this.options.defaultViewType || this.editorViewMap.keys().next().value; - } - - get editorViews() { - return Array.from(this.editorViewMap.values()); - } - - async save(value: any) { - return await this.options.save?.(value); - } - - get title() { - return this.options.defaultTitle; - } } \ No newline at end of file diff --git a/packages/workspace/src/workspace.ts b/packages/workspace/src/workspace.ts index 05d2e666a..c4462a71a 100644 --- a/packages/workspace/src/workspace.ts +++ b/packages/workspace/src/workspace.ts @@ -1,10 +1,11 @@ import { Designer } from '@alilc/lowcode-designer'; import { createModuleEventBus, Editor, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core'; import { Plugins } from '@alilc/lowcode-shell'; -import { IPublicApiWorkspace, IPublicResourceOptions } from '@alilc/lowcode-types'; +import { IPublicApiWorkspace, IPublicResourceList, IPublicTypeResourceType } from '@alilc/lowcode-types'; import { BasicContext } from './base-context'; import { EditorWindow } from './editor-window/context'; import { Resource } from './resource'; +import { ResourceType } from './resource-type'; enum event { ChangeWindow = 'change_window', @@ -12,8 +13,10 @@ enum event { ChangeActiveWindow = 'change_active_window', } +const CHANGE_EVENT = 'resource.list.change'; + export class Workspace implements IPublicApiWorkspace { - private context: BasicContext; + context: BasicContext; private emitter: IEventBus = createModuleEventBus('workspace'); @@ -25,6 +28,18 @@ export class Workspace implements IPublicApiWorkspace { return this.context.innerPlugins; } + private _isActive = false; + + windows: EditorWindow[] = []; + + editorWindowMap: Map = new Map(); + + @obx.ref window: EditorWindow; + + private resourceTypeMap: Map = new Map(); + + private resourceList: Resource[] = []; + constructor( readonly registryInnerPlugin: (designer: Designer, editor: Editor, plugins: Plugins) => Promise, readonly shellModelFactory: any, @@ -39,20 +54,18 @@ export class Workspace implements IPublicApiWorkspace { } initWindow() { - if (!this.defaultResource) { + if (!this.defaultResourceType) { return; } - const title = this.defaultResource.title; - this.window = new EditorWindow(this.defaultResource, this, title); + const title = this.defaultResourceType.name; + const resource = new Resource({}, this.defaultResourceType, this); + this.window = new EditorWindow(resource, this, title); this.editorWindowMap.set(this.window.id, this.window); this.windows.push(this.window); this.emitChangeWindow(); this.emitChangeActiveWindow(); } - - private _isActive = false; - get isActive() { return this._isActive; } @@ -61,36 +74,48 @@ export class Workspace implements IPublicApiWorkspace { this._isActive = value; } - windows: EditorWindow[] = []; + async registerResourceType(resourceTypeModel: IPublicTypeResourceType): Promise { + if (resourceTypeModel.resourceType === 'editor') { + const resourceType = new ResourceType(resourceTypeModel); + this.resourceTypeMap.set(resourceTypeModel.resourceName, resourceType); - editorWindowMap: Map = new Map(); - - @obx.ref window: EditorWindow; - - private resources: Map = new Map(); - - async registerResourceType(resourceName: string, resourceType: 'editor' | 'webview', options: IPublicResourceOptions): Promise { - if (resourceType === 'editor') { - const resource = new Resource(options); - this.resources.set(resourceName, resource); - - if (!this.window && this.defaultResource) { + if (!this.window && this.defaultResourceType) { this.initWindow(); } } } - get defaultResource(): Resource | null { - if (this.resources.size > 1) { - return this.resources.values().next().value; + getResourceList() { + return this.resourceList; + } + + setResourceList(resourceList: IPublicResourceList) { + this.resourceList = resourceList.map(d => new Resource(d, this.getResourceType(d.resourceName), this)); + this.emitter.emit(CHANGE_EVENT, resourceList); + } + + onResourceListChange(fn: (resourceList: IPublicResourceList) => void): () => void { + this.emitter.on(CHANGE_EVENT, fn); + return () => { + this.emitter.off(CHANGE_EVENT, fn); + }; + } + + getResourceType(resourceName: string): ResourceType { + return this.resourceTypeMap.get(resourceName)!; + } + + get defaultResourceType(): ResourceType | null { + if (this.resourceTypeMap.size >= 1) { + return Array.from(this.resourceTypeMap.values())[0]; } return null; } removeResourceType(resourceName: string) { - if (this.resources.has(resourceName)) { - this.resources.delete(resourceName); + if (this.resourceTypeMap.has(resourceName)) { + this.resourceTypeMap.delete(resourceName); } } @@ -110,7 +135,7 @@ export class Workspace implements IPublicApiWorkspace { } removeEditorWindow(resourceName: string, title: string) { - const index = this.windows.findIndex(d => (d.resourceName === resourceName && d.title)); + const index = this.windows.findIndex(d => (d.resource.name === resourceName && d.title)); this.remove(index); } @@ -122,19 +147,20 @@ export class Workspace implements IPublicApiWorkspace { } } - openEditorWindow(resourceName: string, title: string, viewType?: string) { - const resource = this.resources.get(resourceName); - if (!resource) { - console.error(`${resourceName} is not available`); + openEditorWindow(name: string, title: string, options: Object, viewType?: string) { + const resourceType = this.resourceTypeMap.get(name); + if (!resourceType) { + console.error(`${name} is not available`); return; } - const filterWindows = this.windows.filter(d => (d.resourceName === resourceName && d.title == title)); + const filterWindows = this.windows.filter(d => (d.resource.name === name && d.title == title)); if (filterWindows && filterWindows.length) { this.window = filterWindows[0]; this.emitChangeActiveWindow(); return; } - this.window = new EditorWindow(resource, this, title); + const resource = new Resource({}, resourceType, this); + this.window = new EditorWindow(resource, this, title, options); this.windows.push(this.window); this.editorWindowMap.set(this.window.id, this.window); this.emitChangeWindow();