From 163416fcfe291e133551cd75e484cc3ea6d0edc8 Mon Sep 17 00:00:00 2001 From: liujuping Date: Fri, 23 Dec 2022 20:16:12 +0800 Subject: [PATCH 01/14] feat: support the use of events in workspace mode to communicate in different views --- packages/editor-core/src/editor.ts | 2 ++ packages/engine/src/engine-core.ts | 5 +++-- packages/shell/src/api/event.ts | 21 ++++++++++----------- packages/workspace/src/base-context.ts | 6 +++--- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/editor-core/src/editor.ts b/packages/editor-core/src/editor.ts index e0a74cfeb..e837fc530 100644 --- a/packages/editor-core/src/editor.ts +++ b/packages/editor-core/src/editor.ts @@ -326,3 +326,5 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor } } } + +export const commonEvent = new EventBus(new EventEmitter()); \ No newline at end of file diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index 2d51e8cb7..1af53e9ba 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -5,6 +5,7 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { globalContext, Editor, + commonEvent, engineConfig, Setters as InnerSetters, Hotkey as InnerHotkey, @@ -87,7 +88,7 @@ editor.set('setters' as any, setters); editor.set('material', material); editor.set('innerHotkey', innerHotkey); const config = engineConfig; -const event = new Event(editor, { prefix: 'common' }); +const event = new Event(commonEvent, { prefix: 'common' }); const logger = new Logger({ level: 'warn', bizName: 'common' }); const common = new Common(editor, innerSkeleton); const canvas = new Canvas(editor); @@ -102,7 +103,7 @@ const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = { context.setters = setters; context.material = material; const eventPrefix = meta?.eventPrefix || 'common'; - context.event = new Event(editor, { prefix: eventPrefix }); + context.event = new Event(commonEvent, { prefix: eventPrefix }); context.config = config; context.common = common; context.canvas = canvas; diff --git a/packages/shell/src/api/event.ts b/packages/shell/src/api/event.ts index 9eaffbac1..9b7f765ab 100644 --- a/packages/shell/src/api/event.ts +++ b/packages/shell/src/api/event.ts @@ -1,6 +1,5 @@ -import { Editor as InnerEditor, globalContext } from '@alilc/lowcode-editor-core'; +import { Editor as InnerEditor, EventBus } from '@alilc/lowcode-editor-core'; import { getLogger, isPublicEventName, isPluginEventName } from '@alilc/lowcode-utils'; -import { editorSymbol } from '../symbols'; import { IPublicApiEvent, IPublicTypeDisposable } from '@alilc/lowcode-types'; const logger = getLogger({ level: 'warn', bizName: 'shell-event' }); @@ -9,10 +8,10 @@ type EventOptions = { prefix: string; }; -const innerEditorSymbol = Symbol('editor'); +const eventBusSymbol = Symbol('eventBus'); export class Event implements IPublicApiEvent { - private readonly [editorSymbol]: InnerEditor; + private readonly [eventBusSymbol]: EventBus; private readonly options: EventOptions; // TODO: @@ -21,8 +20,8 @@ export class Event implements IPublicApiEvent { */ readonly names = []; - constructor(editor: InnerEditor, options: EventOptions, public workspaceMode = false) { - this[editorSymbol] = editor; + constructor(eventBus: EventBus, options: EventOptions, public workspaceMode = false) { + this[eventBusSymbol] = eventBus; this.options = options; if (!this.options.prefix) { logger.warn('prefix is required while initializing Event'); @@ -36,7 +35,7 @@ export class Event implements IPublicApiEvent { */ on(event: string, listener: (...args: any[]) => void): IPublicTypeDisposable { if (isPluginEventName(event) || isPublicEventName(event)) { - return this[editorSymbol].eventBus.on(event, listener); + return this[eventBusSymbol].on(event, listener); } else { logger.warn(`fail to monitor on event ${event}, which is neither a engine public event nor a plugin event`); return () => {}; @@ -49,7 +48,7 @@ export class Event implements IPublicApiEvent { * @param listener 事件回调 */ off(event: string, listener: (...args: any[]) => void) { - this[editorSymbol].eventBus.off(event, listener); + this[eventBusSymbol].off(event, listener); } /** @@ -63,7 +62,7 @@ export class Event implements IPublicApiEvent { logger.warn('Event#emit has been forbidden while prefix is not specified'); return; } - this[editorSymbol].eventBus.emit(`${this.options.prefix}:${event}`, ...args); + this[eventBusSymbol].emit(`${this.options.prefix}:${event}`, ...args); } /** @@ -72,10 +71,10 @@ export class Event implements IPublicApiEvent { * @param args */ __internalEmit__(event: string, ...args: unknown[]) { - this[editorSymbol].emit(event, ...args); + this[eventBusSymbol].emit(event, ...args); } } export function getEvent(editor: InnerEditor, options: any = { prefix: 'common' }) { - return new Event(editor, options); + return new Event(editor.eventBus, options); } diff --git a/packages/workspace/src/base-context.ts b/packages/workspace/src/base-context.ts index daf97f35e..af606ddf0 100644 --- a/packages/workspace/src/base-context.ts +++ b/packages/workspace/src/base-context.ts @@ -4,6 +4,7 @@ import { Editor, engineConfig, Setters as InnerSetters, Hotkey as InnerHotkey, + commonEvent, } from '@alilc/lowcode-editor-core'; import { Designer, @@ -80,7 +81,7 @@ export class BasicContext { const material = new Material(editor, true); const project = new Project(innerProject, true); const config = engineConfig; - const event = new Event(editor, { prefix: 'common' }); + const event = new Event(commonEvent, { prefix: 'common' }); const logger = getLogger({ level: 'warn', bizName: 'common' }); const skeleton = new Skeleton(innerSkeleton, 'any', true); editor.set('setters', setters); @@ -114,8 +115,7 @@ export class BasicContext { context.setters = setters; context.material = material; const eventPrefix = meta?.eventPrefix || 'common'; - context.event = new Event(editor, { prefix: eventPrefix }); - context.event = event; + context.event = new Event(commonEvent, { prefix: eventPrefix }); context.config = config; context.common = common; context.plugins = plugins; From 56e9f04221283b501a35ca78e76974a556b5ebdd Mon Sep 17 00:00:00 2001 From: JackLian Date: Mon, 26 Dec 2022 10:36:13 +0800 Subject: [PATCH 02/14] fix: add some unconfigured i18n text --- .../builtin-simulator/bem-tools/border-detecting.tsx | 4 ++-- packages/designer/src/locale/en-US.json | 3 ++- packages/designer/src/locale/zh-CN.json | 3 ++- .../editor-skeleton/src/components/field/fields.tsx | 11 ++++++----- .../components/settings/settings-primary-pane.tsx | 9 +++++---- packages/editor-skeleton/src/locale/en-US.json | 8 +++++++- packages/editor-skeleton/src/locale/zh-CN.json | 12 +++++++++--- 7 files changed, 33 insertions(+), 17 deletions(-) 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 ebde9ef2d..710ba3e53 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx +++ b/packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import { computed, observer, Title } from '@alilc/lowcode-editor-core'; import { IPublicTypeTitleContent } from '@alilc/lowcode-types'; import { getClosestNode } from '@alilc/lowcode-utils'; - +import { intl } from '../../locale'; import { BuiltinSimulatorHost } from '../host'; @@ -37,7 +37,7 @@ export class BorderDetectingInstance extends PureComponent<{
{ - isLocked ? (<Title title="已锁定" className="lc-borders-status" />) : null + isLocked ? (<Title title={intl('locked')} className="lc-borders-status" />) : null } </div> ); diff --git a/packages/designer/src/locale/en-US.json b/packages/designer/src/locale/en-US.json index cb96a5bc7..50fca66ca 100644 --- a/packages/designer/src/locale/en-US.json +++ b/packages/designer/src/locale/en-US.json @@ -5,5 +5,6 @@ "lock": "Lock", "unlock": "Unlock", "Condition Group": "Condition Group", - "No opened document": "No opened document, open some document to editing" + "No opened document": "No opened document, open some document to editing", + "locked": "locked" } diff --git a/packages/designer/src/locale/zh-CN.json b/packages/designer/src/locale/zh-CN.json index 0caf4fef0..50d46dbf2 100644 --- a/packages/designer/src/locale/zh-CN.json +++ b/packages/designer/src/locale/zh-CN.json @@ -5,5 +5,6 @@ "lock": "锁定", "unlock": "解锁", "Condition Group": "条件组", - "No opened document": "没有打开的页面,请选择页面打开编辑" + "No opened document": "没有打开的页面,请选择页面打开编辑", + "locked": "已锁定" } diff --git a/packages/editor-skeleton/src/components/field/fields.tsx b/packages/editor-skeleton/src/components/field/fields.tsx index 7af58527d..84ac56f48 100644 --- a/packages/editor-skeleton/src/components/field/fields.tsx +++ b/packages/editor-skeleton/src/components/field/fields.tsx @@ -8,6 +8,7 @@ import { IPublicModelEditor, IPublicTypeTitleContent } from '@alilc/lowcode-type import { PopupPipe, PopupContext } from '../popup'; import './index.less'; import InlineTip from './inlinetip'; +import { intl } from '../../locale'; export interface FieldProps { className?: string; @@ -106,22 +107,22 @@ export class Field extends Component<FieldProps> { getTipContent(propName: string, tip?: any): any { let tipContent = ( <div> - <div>属性:{propName}</div> + <div>{intl('Attribute: ')}{propName}</div> </div> ); if (isObject(tip)) { tipContent = ( <div> - <div>属性:{propName}</div> - <div>说明:{(tip as any).content}</div> + <div>{intl('Attribute: ')}{propName}</div> + <div>{intl('Description: ')}{(tip as any).content}</div> </div> ); } else if (tip) { tipContent = ( <div> - <div>属性:{propName}</div> - <div>说明:{tip}</div> + <div>{intl('Attribute: ')}{propName}</div> + <div>{intl('Description: ')}{tip}</div> </div> ); } diff --git a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx index 8667f7e4c..50b18d50f 100644 --- a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx @@ -7,6 +7,7 @@ import { SettingsMain } from './main'; import { SettingsPane } from './settings-pane'; import { StageBox } from '../stage-box'; import { SkeletonContext } from '../../context'; +import { intl } from '../../locale'; import { createIcon, isSettingField } from '@alilc/lowcode-utils'; @observer @@ -137,7 +138,7 @@ export class SettingsPrimaryPane extends Component<{ engineEditor: Editor; confi return ( <div className="lc-settings-main"> <div className="lc-settings-notice"> - <p>请在左侧画布选中节点</p> + <p>{intl('Please select a node in canvas')}</p> </div> </div> ); @@ -148,7 +149,7 @@ export class SettingsPrimaryPane extends Component<{ engineEditor: Editor; confi return ( <div className="lc-settings-main"> <div className="lc-settings-notice"> - <p>该节点已被锁定,无法配置</p> + <p>{intl('Current node is locked')}</p> </div> </div> ); @@ -157,7 +158,7 @@ export class SettingsPrimaryPane extends Component<{ engineEditor: Editor; confi return ( <div className="lc-settings-main"> <div className="lc-settings-notice"> - <p>该组件暂无配置</p> + <p>{intl('No config found for this type of component')}</p> </div> </div> ); @@ -168,7 +169,7 @@ export class SettingsPrimaryPane extends Component<{ engineEditor: Editor; confi return ( <div className="lc-settings-main"> <div className="lc-settings-notice"> - <p>请选中同一类型节点编辑</p> + <p>{intl('Please select same kind of components')}</p> </div> </div> ); diff --git a/packages/editor-skeleton/src/locale/en-US.json b/packages/editor-skeleton/src/locale/en-US.json index 571c555d7..36abb6395 100644 --- a/packages/editor-skeleton/src/locale/en-US.json +++ b/packages/editor-skeleton/src/locale/en-US.json @@ -5,5 +5,11 @@ "Multiple Value, Click to Clear": "Multiple Value, Click to Clear", "Required": "Required", "Setted Value, Click to Clear": "Setted Value, Click to Clear", - "Multiple Value": "Multiple Value" + "Multiple Value": "Multiple Value", + "Attribute: ": "Attribute: ", + "Description: ": "Description: ", + "Please select a node in canvas": "Please select a node in canvas", + "Current node is locked": "Current node is locked", + "No config found for this type of component": "No config found for this type of component", + "Please select same kind of components": "Please select same kind of components" } diff --git a/packages/editor-skeleton/src/locale/zh-CN.json b/packages/editor-skeleton/src/locale/zh-CN.json index 0ed161004..c347ad20d 100644 --- a/packages/editor-skeleton/src/locale/zh-CN.json +++ b/packages/editor-skeleton/src/locale/zh-CN.json @@ -1,9 +1,15 @@ { - "Binded: {expr}": "已绑定: {expr}", + "Binded: {expr}": "已绑定:{expr}", "Variable Binding": "变量绑定", "Switch Setter": "切换设置器", - "Multiple Value, Click to Clear": "多种值, 点击清除", + "Multiple Value, Click to Clear": "多种值,点击清除", "Required": "必填项", "Setted Value, Click to Clear": "已设置值,点击清除", - "Multiple Value": "多种值" + "Multiple Value": "多种值", + "Attribute: ": "属性:", + "Description: ": "说明:", + "Please select a node in canvas": "请在左侧画布选中节点", + "Current node is locked": "该节点已被锁定,无法配置", + "No config found for this type of component": "该组件暂无配置", + "Please select same kind of components": "请选中同一类型节点编辑" } From 33ed67ab577e8870b2f4131b10d1abd6d6e7e98a Mon Sep 17 00:00:00 2001 From: JackLian <jack.lianjie@gmail.com> Date: Sun, 25 Dec 2022 13:49:05 +0800 Subject: [PATCH 03/14] =?UTF-8?q?docs:=20api=E6=96=87=E6=A1=A3=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/config/navbar.js | 22 +- docs/config/sidebars.js | 55 +- docs/docs/api/canvas.md | 86 ++ docs/docs/api/common.md | 130 ++- docs/docs/api/config.md | 109 +- docs/docs/api/event.md | 38 +- docs/docs/api/hotkey.md | 35 +- docs/docs/api/index.md | 6 +- docs/docs/api/init.md | 8 + docs/docs/api/logger.md | 93 +- docs/docs/api/material.md | 285 +++-- docs/docs/api/model/_category_.json | 6 + docs/docs/api/model/detecting.md | 47 + docs/docs/api/model/document-model.md | 150 +++ docs/docs/api/model/dragon.md | 129 ++ docs/docs/api/model/history.md | 58 + docs/docs/api/model/modal-nodes-manager.md | 48 + docs/docs/api/model/node-children.md | 123 ++ docs/docs/api/model/node.md | 250 ++++ docs/docs/api/model/prop.md | 53 + docs/docs/api/model/props.md | 58 + docs/docs/api/model/selection.md | 85 ++ docs/docs/api/plugins.md | 158 +-- docs/docs/api/project.md | 1038 ++++------------- docs/docs/api/setters.md | 228 +--- docs/docs/api/simulatorHost.md | 56 +- docs/docs/api/skeleton.md | 196 +++- docs/scripts/getDocsFromDir.js | 5 +- docs/scripts/sync-oss.js | 2 +- .../designer/src/plugin/plugin-manager.ts | 4 +- packages/designer/src/plugin/plugin-utils.ts | 4 +- packages/designer/src/project/project.ts | 1 - packages/editor-core/src/config.ts | 6 +- packages/editor-core/src/utils/preference.ts | 4 +- packages/shell/src/api/common.tsx | 32 +- packages/shell/src/api/material.ts | 6 +- packages/shell/src/api/plugins.ts | 4 +- packages/shell/src/api/project.ts | 9 +- packages/types/src/engine-config.ts | 17 - packages/types/src/shell/api/canvas.ts | 12 +- packages/types/src/shell/api/common.ts | 87 +- packages/types/src/shell/api/event.ts | 3 + packages/types/src/shell/api/hotkey.ts | 7 +- packages/types/src/shell/api/material.ts | 36 +- packages/types/src/shell/api/plugins.ts | 19 +- packages/types/src/shell/api/project.ts | 45 +- packages/types/src/shell/api/setters.ts | 3 + .../types/src/shell/api/simulator-host.ts | 14 +- packages/types/src/shell/api/skeleton.ts | 40 +- packages/types/src/shell/model/detecting.ts | 6 + packages/types/src/shell/model/dragon.ts | 12 +- .../types/src/shell/model/engine-config.ts | 17 +- packages/types/src/shell/model/index.ts | 1 + packages/types/src/shell/model/preference.ts | 17 + packages/types/src/shell/model/selection.ts | 6 + packages/types/src/shell/type/index.ts | 3 +- .../src/shell/type/plugin-register-options.ts | 13 + scripts/sync-oss.js | 2 +- 58 files changed, 2453 insertions(+), 1534 deletions(-) create mode 100644 docs/docs/api/canvas.md create mode 100644 docs/docs/api/model/_category_.json create mode 100644 docs/docs/api/model/detecting.md create mode 100644 docs/docs/api/model/document-model.md create mode 100644 docs/docs/api/model/dragon.md create mode 100644 docs/docs/api/model/history.md create mode 100644 docs/docs/api/model/modal-nodes-manager.md create mode 100644 docs/docs/api/model/node-children.md create mode 100644 docs/docs/api/model/node.md create mode 100644 docs/docs/api/model/prop.md create mode 100644 docs/docs/api/model/props.md create mode 100644 docs/docs/api/model/selection.md create mode 100644 packages/types/src/shell/model/preference.ts create mode 100644 packages/types/src/shell/type/plugin-register-options.ts diff --git a/docs/config/navbar.js b/docs/config/navbar.js index 0f0486398..20d5e5f90 100644 --- a/docs/config/navbar.js +++ b/docs/config/navbar.js @@ -33,12 +33,6 @@ module.exports = { position: 'left', label: 'FAQ', }, - { - type: 'doc', - docId: 'participate/index', - position: 'left', - label: '参与贡献', - }, { type: 'doc', docId: 'article/index', @@ -51,16 +45,6 @@ module.exports = { position: 'left', label: 'Demo 使用文档', }, - { - position: 'left', - href: 'https://developer.aliyun.com/ebook/7507', - label: '技术白皮书', - }, - { - position: 'left', - href: 'https://github.com/alibaba/lowcode-engine/releases', - label: '更新日志', - }, { to: '/community/issue', position: 'left', @@ -80,6 +64,12 @@ module.exports = { className: 'header-github-link', 'aria-label': 'GitHub repository', }, + { + type: 'doc', + docId: 'participate/index', + position: 'right', + label: '参与贡献', + }, { type: 'search', position: 'right', diff --git a/docs/config/sidebars.js b/docs/config/sidebars.js index e2de2d49c..95a79962f 100644 --- a/docs/config/sidebars.js +++ b/docs/config/sidebars.js @@ -22,10 +22,56 @@ module.exports = { * 根据当前目录自动生成导航配置 */ guide: [ - { - type: 'autogenerated', - dirName: 'guide', // '.' 即当前的文档文件夹 - }, + [ + { + type: 'category', + label: '入门', + collapsed: false, + items: getDocsFromDir('guide/quickStart'), + }, + { + type: 'category', + label: '创建编辑器', + collapsed: false, + items: getDocsFromDir('guide/create'), + }, + { + type: 'category', + label: '扩展编辑器', + collapsed: false, + items: getDocsFromDir('guide/expand/editor'), + }, + { + type: 'category', + label: '扩展运行时', + collapsed: false, + items: getDocsFromDir('guide/expand/runtime'), + }, + { + type: 'category', + label: '设计原理', + collapsed: false, + items: getDocsFromDir('guide/design'), + }, + { + type: 'category', + label: '附录', + collapsed: false, + items: [ + { + type: 'link', + label: '更新日志', + href: 'https://github.com/alibaba/lowcode-engine/releases', + }, + ...getDocsFromDir('guide/appendix'), + ], + }, + { + type: 'link', + label: '技术白皮书', + href: 'https://developer.aliyun.com/ebook/7507', + }, + ], ], api: [ { @@ -57,5 +103,4 @@ module.exports = { dirName: 'demoUsage', }, ], - // api: getDocsFromDir('api'), }; diff --git a/docs/docs/api/canvas.md b/docs/docs/api/canvas.md new file mode 100644 index 000000000..8340bd348 --- /dev/null +++ b/docs/docs/api/canvas.md @@ -0,0 +1,86 @@ +--- +title: cavas - 画布 API +sidebar_position: 12 +--- + +> **@types** [IPublicApiCanvas](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/canvas.ts)<br/> +> **@since** v1.1.0 + + +## 模块简介 + +通过该模块可以触达对画布拖拽相关的一些能力。 + +## 变量 + +### dragon + +获取拖拽操作对象的实例 + +```typescript +/** + * 获取拖拽操作对象的实例 + * get dragon instance, you can use this to obtain draging related abilities and lifecycle hooks + * @since v1.1.0 + */ +get dragon(): IPublicModelDragon | null; +``` +关联模型 [IPublicModelDragon](./model/dragon) + +### activeTracker + +获取活动追踪器实例 + +```typescript +/** + * 获取活动追踪器实例 + * get activeTracker instance, which is a singleton running in engine. + * it tracks document`s current focusing node/node[], and notify it`s subscribers that when + * focusing node/node[] changed. + * @since v1.1.0 + */ +get activeTracker(): IPublicModelActiveTracker | null; +``` + +## 方法签名 + +### createLocation +创建一个文档插入位置对象,该对象用来描述一个即将插入的节点在文档中的位置 + +```typescript +/** + * 创建一个文档插入位置对象,该对象用来描述一个即将插入的节点在文档中的位置 + * create a drop location for document, drop location describes a location in document + * @since v1.1.0 + */ +createLocation(locationData: IPublicTypeLocationData): IPublicModelDropLocation; +``` + +### createScroller +创建一个滚动控制器 Scroller,赋予一个视图滚动的基本能力, +```typescript +/** + * 创建一个滚动控制器 Scroller,赋予一个视图滚动的基本能力, + * a Scroller is a controller that gives a view (IPublicModelScrollable) the ability scrolling + * to some cordination by api scrollTo. + * + * when a scroller is inited, will need to pass is a scrollable, which has a scrollTarget. + * and when scrollTo(options: { left?: number; top?: number }) is called, scroller will + * move scrollTarget`s top-left corner to (options.left, options.top) that passed in. + * @since v1.1.0 + */ +createScroller(scrollable: IPublicModelScrollable): IPublicModelScroller; + +``` + +### createScrollTarget +创建一个 ScrollTarget,与 Scroller 一起发挥作用,详见 [createScroller](#createscroller) 中的描述 + +```typescript +/** + * 创建一个 ScrollTarget,与 Scroller 一起发挥作用,详见 createScroller 中的描述 + * this works with Scroller, refer to createScroller`s description + * @since v1.1.0 + */ +createScrollTarget(shell: HTMLDivElement): IPublicModelScrollTarget; +``` \ No newline at end of file diff --git a/docs/docs/api/common.md b/docs/docs/api/common.md index 39ee609e8..a6b21974d 100644 --- a/docs/docs/api/common.md +++ b/docs/docs/api/common.md @@ -2,34 +2,87 @@ title: common - 通用 API sidebar_position: 11 --- -# 模块简介 -通用模块里包含除了 9 大核心模块 API 之外的所有 API,比如通用 utils、面板扩展相关 等。 + +> **@types** [IPublicApiCommon](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/common.ts)<br/> +> **@since** v1.0.0 + + +## 模块简介 +通用模块里包含除了几大核心模块 API 之外的所有 API,比如通用 utils、面板扩展相关 等。 > 高能预警:之所以叫 skeletonCabin / designerCabin 跟兼容上一个版本的引擎有关系。若有必要,后面将用更有意义的命名空间来组织这些 API。 -# 变量(variables) -### utils +## 变量 +#### utils 通用 utils,详见下方方法签名 -### designerCabin -设计器扩展相关,详见下方方法签名 +相关类型:[IPublicApiCommonUtils](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/common.ts) -### skeletonCabin +#### skeletonCabin 面板扩展相关,详见下方方法签名 -# 方法签名(functions) -## utils -### isNodeSchema +## 方法签名 +### utils +#### isNodeSchema 是否为合法的 schema 结构 -### isFormEvent +```typscript +/** + * 是否为合法的 schema 结构 + * check if data is valid NodeSchema + * + * @param {*} data + * @returns {boolean} + */ +isNodeSchema(data: any): boolean; +``` + +#### isFormEvent 是否为表单事件类型 -### getNodeSchemaById -从 schema 结构中查找指定 id 节点 +```typescript +/** + * 是否为表单事件类型 + * check if e is a form event + * @param {(KeyboardEvent | MouseEvent)} e + * @returns {boolean} + */ +isFormEvent(e: KeyboardEvent | MouseEvent): boolean; +``` -### executeTransaction +#### getNodeSchemaById +从 schema 结构中查找指定 id 节点 +```typescript +/** + * 从 schema 结构中查找指定 id 节点 + * get node schema from a larger schema with node id + * @param {IPublicTypeNodeSchema} schema + * @param {string} nodeId + * @returns {(IPublicTypeNodeSchema | undefined)} + */ +getNodeSchemaById( + schema: IPublicTypeNodeSchema, + nodeId: string, + ): IPublicTypeNodeSchema | undefined; +``` +相关类型:[IPublicTypeNodeSchema](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/node-schema.ts) + +#### executeTransaction 批处理事务,用于优化特定场景的性能 -*引擎版本 >= 1.0.16 + +```typescript +/** + * 批处理事务,用于优化特定场景的性能 + * excute something in a transaction for performence + * + * @param {() => void} fn + * @param {IPublicEnumTransitionType} type + * @since v1.0.16 + */ +executeTransaction(fn: () => void, type: IPublicEnumTransitionType): void; +``` +**@since v1.0.16** + +##### 示例 ```typescript import { common } from '@alilc/lowcode-engine'; import { IPublicEnumTransitionType } from '@alilc/lowcode-types'; @@ -42,9 +95,33 @@ common.utils.startTransaction(() => { }, IPublicEnumTransitionType.repaint); ``` -### createIntl +#### createIntl i18n 相关工具 -*引擎版本 >= 1.0.17 +```typescript +/** + * i18n 相关工具 + * i18n tools + * + * @param {(string | object)} instance + * @returns {{ + * intlNode(id: string, params?: object): ReactNode; + * intl(id: string, params?: object): string; + * getLocale(): string; + * setLocale(locale: string): void; + * }} + * @since v1.0.17 + */ +createIntl(instance: string | object): { + intlNode(id: string, params?: object): ReactNode; + intl(id: string, params?: object): string; + getLocale(): string; + setLocale(locale: string): void; +}; +``` + +**@since v1.0.17** + +##### 示例 ```typescript import { common } from '@alilc/lowcode-engine'; import enUS from './en-US.json'; @@ -56,16 +133,15 @@ const { intl, getLocale, setLocale } = common.utils.createIntl({ }); ``` -## designerCabin -### isSettingField -是否是 SettingField 实例 -### TransformStage -转换类型枚举对象,包含 init / upgrade / render 等类型,参考 [TransformStage](https://github.com/alibaba/lowcode-engine/blob/4f4ac5115d18357a7399632860808f6cffc33fad/packages/types/src/transform-stage.ts#L1) -## -## skeletonCabin -### Workbench +### skeletonCabin +#### Workbench 编辑器框架 View -# 事件(events) -无 +```typescript +/** + * 编辑器框架 View + * get Workbench Component + */ +get Workbench(): Component; +``` \ No newline at end of file diff --git a/docs/docs/api/config.md b/docs/docs/api/config.md index 40d18eb3a..fee26d436 100644 --- a/docs/docs/api/config.md +++ b/docs/docs/api/config.md @@ -2,21 +2,29 @@ title: config - 配置 API sidebar_position: 8 --- + +> **@types** [IPublicModelEngineConfig](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/engine-config.ts)<br/> +> **@since** v1.0.0 + + ## 模块简介 配置模块,负责配置的读、写等操作。 -## -## 变量(variables) -无 -## -## 方法签名(functions) + +## 方法签名 ### get 获取指定 key 的值 -**类型定义** ```typescript -function get(key: string, defaultValue?: any): any +/** + * 获取指定 key 的值 + * get value by key + * @param key + * @param defaultValue + * @returns + */ +get(key: string, defaultValue?: any): any; ``` -**调用示例** +#### 示例 ```typescript import { config } from '@alilc/lowcode-engine'; @@ -26,11 +34,16 @@ config.get('keyB', { a: 1 }); ### set 设置指定 key 的值 -**类型定义** ```typescript -function set(key: string, value: any) +/** + * 设置指定 key 的值 + * set value for certain key + * @param key + * @param value + */ +set(key: string, value: any): void; ``` -**调用示例** +#### 示例 ```typescript import { config } from '@alilc/lowcode-engine'; @@ -40,40 +53,77 @@ config.set('keyC', 1); ### has 判断指定 key 是否有值 -**类型定义** ```typescript -function has(key: string): boolean +/** + * 判断指定 key 是否有值 + * check if config has certain key configed + * @param key + * @returns + */ +has(key: string): boolean; ``` -**调用示例** + +#### 示例 ```typescript import { config } from '@alilc/lowcode-engine'; config.has('keyD'); ``` -### + ### setConfig 批量设值,set 的对象版本 -**类型定义** ```typescript -function setConfig(config: { [key: string]: any }) +/** + * 批量设值,set 的对象版本 + * set multiple config key-values + * @param config + */ +setConfig(config: { [key: string]: any }): void; ``` -**调用示例** +#### 示例 ```typescript import { config } from '@alilc/lowcode-engine'; config.setConfig({ keyA: false, keyB: 2 }); ``` +### getPreference +获取全局 Preference 管理器,用于管理全局浏览器侧用户 Preference,如 Panel 是否钉住 + +```typescript +/** + * 获取全局 Preference, 用于管理全局浏览器侧用户 Preference,如 Panel 是否钉住 + * get global user preference manager, which can be use to store + * user`s preference in user localstorage, such as a panel is pinned or not. + * @returns {IPublicModelPreference} + * @since v1.1.0 + */ +getPreference(): IPublicModelPreference; +``` +相关类型:[IPublicModelPreference](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/preference.ts) + +**@since v1.1.0** + +## 事件 + ### onceGot 获取指定 key 的值,若此时还未赋值,则等待,若已有值,则直接返回值 注:此函数返回 Promise 实例 -**类型定义** + ```typescript -function onceGot(key: string): Promise<any> +/** + * 获取指定 key 的值,若此时还未赋值,则等待,若已有值,则直接返回值 + * 注:此函数返回 Promise 实例,只会执行(fullfill)一次 + * wait until value of certain key is set, will only be + * triggered once. + * @param key + * @returns + */ +onceGot(key: string): Promise<any>; ``` -**调用示例** +#### 示例 ```typescript import { config } from '@alilc/lowcode-engine'; @@ -88,11 +138,18 @@ const value = await config.onceGot('keyA'); ### onGot 获取指定 key 的值,函数回调模式,若多次被赋值,回调会被多次调用 -**类型定义** ```typescript -function onGot(key: string, fn: (data: any) => void): () => void + /** + * 获取指定 key 的值,函数回调模式,若多次被赋值,回调会被多次调用 + * set callback for event of value set for some key + * this will be called each time the value is set + * @param key + * @param fn + * @returns + */ + onGot(key: string, fn: (data: any) => void): () => void; ``` -**调用示例** +#### 示例 ```typescript import { config } from '@alilc/lowcode-engine'; @@ -102,6 +159,4 @@ config.onGot('keyA', (value) => { const.set('keyA', 1); // 'The value of keyA is 1' const.set('keyA', 2); // 'The value of keyA is 2' -``` -## 事件(events) -无 +``` \ No newline at end of file diff --git a/docs/docs/api/event.md b/docs/docs/api/event.md index b24c91054..be3c768e0 100644 --- a/docs/docs/api/event.md +++ b/docs/docs/api/event.md @@ -2,41 +2,63 @@ title: event - 事件 API sidebar_position: 7 --- + +> **@types** [IPublicApiEvent](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/event.ts)<br/> +> **@since** v1.0.0 + + ## 模块简介 负责事件处理 API,支持自定义监听事件、触发事件。 -## 方法签名(functions) +## 方法签名 ### on 监听事件 -**类型定义** ```typescript -function on(event: string, listener: (...args: unknown[]) => void): void; +/** + * 监听事件 + * add monitor to a event + * @param event 事件名称 + * @param listener 事件回调 + */ +on(event: string, listener: (...args: any[]) => void): IPublicTypeDisposable; ``` +相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) ### off 取消监听事件 -**类型定义** ```typescript -function off(event: string, listener: (...args: unknown[]) => void): void; +/** + * 取消监听事件 + * cancel a monitor from a event + * @param event 事件名称 + * @param listener 事件回调 + */ +off(event: string, listener: (...args: any[]) => void): void; ``` ### emit 触发事件 -**类型定义** - ```typescript -function emit(event: string, ...args: unknown[]): void; +/** + * 取消监听事件 + * cancel a monitor from a event + * @param event 事件名称 + * @param listener 事件回调 + */ +off(event: string, listener: (...args: any[]) => void): void; ``` ## 使用示例 ### 事件触发和监听 + ```typescript const eventName = 'eventName'; // 事件监听 +// 插件中发出的事件,默认以 `common` 为前缀,监听时需要注意下 event.on(`common:${eventName}`); // 触发事件 diff --git a/docs/docs/api/hotkey.md b/docs/docs/api/hotkey.md index cd0cf2944..a8c717342 100644 --- a/docs/docs/api/hotkey.md +++ b/docs/docs/api/hotkey.md @@ -2,29 +2,36 @@ title: hotkey - 快捷键 API sidebar_position: 5 --- + +> **@types** [IPublicApiHotkey](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/hotkey.ts)<br/> +> **@since** v1.0.0 + ## 模块简介 绑定快捷键 API,可以自定义项目快捷键使用。 -## 方法签名(functions) +## 方法签名 ### bind 绑定快捷键 -**类型定义** ```typescript -function bind( - combos: string[] | string, - callback: (e: KeyboardEvent, combo?: string) => any | false, - action?: string -): () => void; +/** + * 绑定快捷键 + * bind hotkey/hotkeys, + * @param combos 快捷键,格式如:['command + s'] 、['ctrl + shift + s'] 等 + * @param callback 回调函数 + * @param action + * @returns + */ +bind( + combos: string[] | string, + callback: IPublicTypeHotkeyCallback, + action?: string, + ): IPublicTypeDisposable; ``` +相关 types +- [IPublicTypeHotkeyCallback](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/hotkey-callback.ts) +- [IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) -**示例** -```typescript -hotkey.bind('command+s', (e) => { - e.preventDefault(); - // command+s 快捷键按下时需要执行的逻辑 -}); -``` ## 使用示例 ### 基础示例 diff --git a/docs/docs/api/index.md b/docs/docs/api/index.md index 212337a62..2bd735c84 100644 --- a/docs/docs/api/index.md +++ b/docs/docs/api/index.md @@ -5,7 +5,7 @@ sidebar_position: 0 引擎提供的公开 API 分为`命名空间`和`模型`两类,其中`命名空间`用于聚合一大类的 API,`模型`为各 API 涉及到的对象模型。 -### 命名空间 +## 命名空间 引擎直接提供以下几大类 API @@ -21,7 +21,7 @@ sidebar_position: 0 - logger 日志 API - init 初始化 API -### 模型 +## 模型 以下模型通过前面的 API 以返回值等形式间接透出。 - document-model 文档 @@ -37,7 +37,7 @@ sidebar_position: 0 - history 操作历史 -### API 设计约定 +## API 设计约定 一些 API 设计约定: 1. 所有 API 命名空间都按照 variables / functions / events 来组织 diff --git a/docs/docs/api/init.md b/docs/docs/api/init.md index 93ad133d3..fd0d4fff3 100644 --- a/docs/docs/api/init.md +++ b/docs/docs/api/init.md @@ -2,6 +2,10 @@ title: init - 初始化 API sidebar_position: 10 --- + +> **@since** v1.0.0 + + ## 模块简介 提供 init 等方法 ## 方法签名 @@ -14,6 +18,7 @@ function init(container?: Element, options?: EngineOptions): void ``` **初始化引擎的参数** + ```typescript interface EngineOptions { /** @@ -102,6 +107,9 @@ interface EngineOptions { [key: string]: any; } ``` +> 源码详见 [EngineOptions](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/engine-config.ts) + + ## 使用示例 ```typescript import { init } from '@alilc/lowcode-engine'; diff --git a/docs/docs/api/logger.md b/docs/docs/api/logger.md index 211065410..68681438b 100644 --- a/docs/docs/api/logger.md +++ b/docs/docs/api/logger.md @@ -2,48 +2,79 @@ title: logger - 日志 API sidebar_position: 9 --- -## 模块简介 -引擎日志模块,可以按照 **日志级别 **和** 业务类型 **两个维度来定制日志,参考 [zen-logger](https://web.npm.alibaba-inc.com/package/zen-logger) 实现进行封装。 -> 注:日志级别可以通过 url query 动态调整,详见下方使用示例。 -## 变量(variables) -无 -## 方法签名(functions) -### log / warn / error / info / debug +> **@types** [IPublicApiLogger](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/logger.ts)<br/> +> **@since** v1.0.0 + + +## 模块简介 +引擎日志模块,可以按照 **日志级别 **和** 业务类型 **两个维度来定制日志。 +> 注:日志级别可以通过 url query 动态调整,详见下方[查看示例](#查看示例)。<br/> +> 参考 [zen-logger](https://web.npm.alibaba-inc.com/package/zen-logger) 实现进行封装 + +## 方法签名 + 日志记录方法 -**类型定义** ```typescript -function log(args: any[]): void -function warn(args: any[]): void -function error(args: any[]): void -function info(args: any[]): void -function debug(args: any[]): void +/** + * debug info + */ +debug(...args: any | any[]): void; + +/** + * normal info output + */ +info(...args: any | any[]): void; + +/** + * warning info output + */ +warn(...args: any | any[]): void; + +/** + * error info output + */ +error(...args: any | any[]): void; + +/** + * log info output + */ +log(...args: any | any[]): void; ``` -**调用示例** + +## 输出示例 + ```typescript import { Logger } from '@alilc/lowcode-utils'; -const logger = new Logger({ level: 'warn', bizName: 'designer:pluginManager' }); +const logger = new Logger({ level: 'warn', bizName: 'myPlugin:moduleA' }); logger.log('Awesome Low-Code Engine'); ``` -## 事件(events) -无 -## 使用示例 -```typescript -import { Logger } from '@alilc/lowcode-utils'; +## 查看示例 -const logger = new Logger({ level: 'warn', bizName: 'designer:pluginManager' }); +开启查看方式: -// 若在 url query 中增加 `__logConf__` 可改变打印日志级别和限定业务类型日志 -// 默认:__logConf__=warn:* -logger.log('log'); // 不输出 -logger.warn('warn'); // 输出 -logger.error('error'); // 输出 - -// 比如:__logConf__=log:designer:pluginManager -logger.log('log'); // 输出 -logger.warn('warn'); // 输出 -logger.error('error'); // 输出 +- 方式 1:所有 logger 创建时会有默认输出的 level, 默认为 warn , 即只展示 warn , error +- 方式 2:url 上追加 __logConf__进行开启,示例如下 + +``` +https://lowcode-engine.cn/demo/demo-general/index.html?__logConf__=warn +// 开启所有 bizName的 warn 和 error + +https://lowcode-engine.cn/demo/demo-general/index.html?__logConf__=debug +// 开启所有 bizName的 debug, log, info, warn 和 error + +https://lowcode-engine.cn/demo/demo-general/index.html?__logConf__=log +// 开启所有 bizName的 log, info, warn 和 error + +https://lowcode-engine.cn/demo/demo-general/index.html?__logConf__=warn|* +// 同 __logConf__=warn + +https://lowcode-engine.cn/demo/demo-general/index.html?__logConf__=warn|bizName +// 开启 bizName 的 debug, log, info, warn 和 error + +https://lowcode-engine.cn/demo/demo-general/index.html?__logConf__=warn|partOfBizName +// 开启 bizName like '%partOfBizName%' 的 debug, log, info, warn 和 error ``` diff --git a/docs/docs/api/material.md b/docs/docs/api/material.md index f1df43e6c..c060b9bef 100644 --- a/docs/docs/api/material.md +++ b/docs/docs/api/material.md @@ -2,24 +2,44 @@ title: material - 物料 API sidebar_position: 2 --- -# 模块简介 + +> **@types** [IPublicApiMaterial](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/material.ts)<br/> +> **@since** v1.0.0 + + +## 模块简介 负责物料相关的 API,包括资产包、设计器辅助层、物料元数据和物料元数据管道函数。 -# 变量(variables) -## componentsMap +## 变量 +### componentsMap 获取组件 map 结构 +```typescript +/** + * 获取组件 map 结构 + * get map of components + */ +get componentsMap(): { [key: string]: IPublicTypeNpmInfo | ComponentType<any> | object } ; +``` +相关类型:[IPublicTypeNpmInfo](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/npm-info.ts) -# 方法签名(functions) -## 资产包 -### setAssets +## 方法签名 + +### 资产包 +#### setAssets 设置「[资产包](/site/docs/specs/lowcode-spec#2-协议结构)」结构 -**类型定义** ```typescript -function setAssets(assets: AssetsJson): void; +/** + * 设置「资产包」结构 + * set data for Assets + * @returns void + */ +setAssets(assets: IPublicTypeAssetsJson): void; ``` +相关类型:[IPublicTypeAssetsJson](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/assets-json.ts) -**示例** + +##### 示例 直接在项目中引用 npm 包 ```javascript import { material } from '@alilc/lowcode-engine'; @@ -28,7 +48,7 @@ import assets from '@alilc/mc-assets-<siteId>/assets.json'; material.setAssets(assets); ``` -通过物料中心接口动态引入资产包 +通过接口动态引入资产包 ```typescript import { material, plugins } from '@alilc/lowcode-engine'; import { IPublicModelPluginContext } from '@alilc/lowcode-types'; @@ -39,43 +59,54 @@ plugins.register((ctx: IPublicModelPluginContext) => { name: 'ext-assets', async init() { try { - // 将下述链接替换为您的物料即可。无论是通过 utils 从物料中心引入,还是通过其他途径如直接引入物料描述 - const res = await window.fetch('https://fusion.alicdn.com/assets/default@0.1.95/assets.json') - const assets = await res.text() - material.setAssets(assets) + // 将下述链接替换为您的物料描述地址即可。 + const res = await window.fetch('https://fusion.alicdn.com/assets/default@0.1.95/assets.json'); + const assets = await res.text(); + material.setAssets(assets); } catch (err) { - console.error(err) - } + console.error(err); + }; }, - } -}).catch(err => console.error(err)) + }; +}).catch(err => console.error(err)); ``` -### getAssets +#### getAssets 获取「资产包」结构 -**类型定义** + ```typescript -function getAssets(): AssetsJson; +/** + * 获取「资产包」结构 + * get AssetsJson data + * @returns IPublicTypeAssetsJson + */ +getAssets(): IPublicTypeAssetsJson; ``` +相关类型:[IPublicTypeAssetsJson](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/assets-json.ts) -**示例** +##### 示例 ```typescript import { material } from '@alilc/lowcode-engine'; material.getAssets(); ``` -### loadIncrementalAssets +#### loadIncrementalAssets 加载增量的「资产包」结构,该增量包会与原有的合并 -**类型定义** ```typescript -function loadIncrementalAssets(incrementalAssets: AssetsJson): void; +/** + * 加载增量的「资产包」结构,该增量包会与原有的合并 + * load Assets incrementally, and will merge this with exiting assets + * @param incrementalAssets + * @returns + */ +loadIncrementalAssets(incrementalAssets: IPublicTypeAssetsJson): void; ``` -说明:**该增量包会与原有的合并** +相关类型:[IPublicTypeAssetsJson](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/assets-json.ts) -**示例** +##### 示例 ```typescript import { material } from '@alilc/lowcode-engine'; import assets1 from '@alilc/mc-assets-<siteId>/assets.json'; @@ -84,57 +115,23 @@ import assets2 from '@alilc/mc-assets-<siteId>/assets.json'; material.setAssets(assets1); material.loadIncrementalAssets(assets2); ``` -## 设计器辅助层 -### addBuiltinComponentAction + +### 设计器辅助层 +#### addBuiltinComponentAction 在设计器辅助层增加一个扩展 action -**类型定义** + ```typescript -function addBuiltinComponentAction(action: IPublicTypeComponentAction): void; - -export interface IPublicTypeComponentAction { - /** - * behaviorName - */ - name: string; - /** - * 菜单名称 - */ - content: string | ReactNode | ActionContentObject; - /** - * 子集 - */ - items?: IPublicTypeComponentAction[]; - /** - * 显示与否 - * always: 无法禁用 - */ - condition?: boolean | ((currentNode: any) => boolean) | 'always'; - /** - * 显示在工具条上 - */ - important?: boolean; -} - -export interface ActionContentObject { - /** - * 图标 - */ - icon?: IconType; - /** - * 描述 - */ - title?: TipContent; - /** - * 执行动作 - */ - action?: (currentNode: any) => void; -} - -export type IconType = string | ReactElement | ComponentType<any> | IconConfig; +/** + * 在设计器辅助层增加一个扩展 action + * add an action button in canvas context menu area + * @param action + */ +addBuiltinComponentAction(action: IPublicTypeComponentAction): void; ``` +相关类型:[IPublicTypeComponentAction](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/component-action.ts) -**示例** +##### 示例 新增设计扩展位,并绑定事件 ```typescript import { material } from '@alilc/lowcode-engine'; @@ -154,14 +151,27 @@ material.addBuiltinComponentAction({ ``` ![image.png](https://img.alicdn.com/imgextra/i4/O1CN01jDbN7B1KfWVzJ16tw_!!6000000001191-2-tps-230-198.png) -### removeBuiltinComponentAction +#### removeBuiltinComponentAction 移除设计器辅助层的指定 action -**类型定义** + ```typescript -function removeBuiltinComponentAction(name: string): void; +/** + * 移除设计器辅助层的指定 action + * remove a builtin action button from canvas context menu area + * @param name + */ +removeBuiltinComponentAction(name: string): void; ``` -**示例** +##### 内置设计器辅助 name + +- remove:删除 +- hide:隐藏 +- copy:复制 +- lock:锁定,不可编辑 +- unlock:解锁,可编辑 + +##### 示例 ```typescript import { material } from '@alilc/lowcode-engine'; @@ -169,16 +179,25 @@ material.removeBuiltinComponentAction('myIconName'); ``` -### modifyBuiltinComponentAction +#### modifyBuiltinComponentAction 修改已有的设计器辅助层的指定 action -**类型定义** + ```typescript -function modifyBuiltinComponentAction( - actionName: string, - handle: (action: IPublicTypeComponentAction) => void -): void; +/** + * 修改已有的设计器辅助层的指定 action + * modify a builtin action button in canvas context menu area + * @param actionName + * @param handle + */ +modifyBuiltinComponentAction( + actionName: string, + handle: (action: IPublicTypeComponentAction) => void, + ): void; ``` -**内置设计器辅助 name** +相关类型:[IPublicTypeComponentAction](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/component-action.ts) + + +##### 内置设计器辅助 name - remove:删除 - hide:隐藏 @@ -188,7 +207,7 @@ function modifyBuiltinComponentAction( -**示例** +##### 示例 给原始的 remove 扩展时间添加执行前后的日志 ```typescript import { material } from '@alilc/lowcode-engine'; @@ -202,30 +221,43 @@ material.modifyBuiltinComponentAction('remove', (action) => { } }); ``` -### -## 物料元数据 -### getComponentMeta -获取指定名称的物料元数据 -**类型定义** -```typescript -function getComponentMeta(componentName: string): ComponentMeta; -``` -**示例** +### 物料元数据 +#### getComponentMeta +获取指定名称的物料元数据 + +```typescript +/** + * 获取指定名称的物料元数据 + * get component meta by component name + * @param componentName + * @returns + */ +getComponentMeta(componentName: string): IPublicModelComponentMeta | null; +``` +相关类型:[IPublicModelComponentMeta](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/component-meta.ts) + +##### 示例 ```typescript import { material } from '@alilc/lowcode-engine'; material.getComponentMeta('Input'); ``` -### getComponentMetasMap +#### getComponentMetasMap 获取所有已注册的物料元数据 -**类型定义** -```typescript -function getComponentMetasMap(): new Map<string, ComponentMeta>; -``` -**示例** +```typescript + /** + * 获取所有已注册的物料元数据 + * get map of all component metas + * @returns + */ + getComponentMetasMap(): Map<string, IPublicModelComponentMeta>; +``` +相关类型:[IPublicModelComponentMeta](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/component-meta.ts) + +##### 示例 ```typescript import { material } from '@alilc/lowcode-engine'; @@ -233,19 +265,27 @@ material.getComponentMetasMap(); ``` -## 物料元数据管道函数 -### registerMetadataTransducer +### 物料元数据管道函数 +#### registerMetadataTransducer 注册物料元数据管道函数,在物料信息初始化时执行。 -**类型定义** + ```typescript -function registerMetadataTransducer( - transducer: MetadataTransducer, // 管道函数 - level?: number, // 优先级 - id?: string | undefined, // id +/** + * 注册物料元数据管道函数,在物料信息初始化时执行。 + * register transducer to process component meta, which will be + * excuted during component meta`s initialization + * @param transducer + * @param level + * @param id + */ +registerMetadataTransducer( + transducer: IPublicTypeMetadataTransducer, + level?: number, + id?: string | undefined ): void; ``` -**示例** +##### 示例 给每一个组件的配置添加高级配置面板,其中有一个是否渲染配置项 ```typescript import { material } from '@alilc/lowcode-engine' @@ -290,28 +330,37 @@ function addonCombine(metadata: TransformedComponentMetadata) { material.registerMetadataTransducer(addonCombine, 1, 'parse-func'); ``` -### getRegisteredMetadataTransducers +#### getRegisteredMetadataTransducers 获取所有物料元数据管道函数 -**类型定义** + ```typescript -function getRegisteredMetadataTransducers(): IPublicTypeMetadataTransducer[]; +/** + * 获取所有物料元数据管道函数 + * get all registered metadata transducers + * @returns {IPublicTypeMetadataTransducer[]} + */ +getRegisteredMetadataTransducers(): IPublicTypeMetadataTransducer[]; ``` -**示例** +##### 示例 ```typescript import { material } from '@alilc/lowcode-engine' -material.getRegisteredMetadataTransducers('parse-func'); +material.getRegisteredMetadataTransducers(); ``` -## -# 事件(Event) +## 事件 ### onChangeAssets 监听 assets 变化的事件 -**类型定义** + ```typescript -function onChangeAssets(fn: () => void): void; +/** + * 监听 assets 变化的事件 + * add callback for assets changed event + * @param fn + */ +onChangeAssets(fn: () => void): void; ``` -**示例** +##### 示例 ```typescript import { material } from '@alilc/lowcode-engine'; diff --git a/docs/docs/api/model/_category_.json b/docs/docs/api/model/_category_.json new file mode 100644 index 000000000..5b1f74b36 --- /dev/null +++ b/docs/docs/api/model/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "模型定义 Models", + "position": 14, + "collapsed": false, + "collapsible": true +} diff --git a/docs/docs/api/model/detecting.md b/docs/docs/api/model/detecting.md new file mode 100644 index 000000000..2cc0ea18d --- /dev/null +++ b/docs/docs/api/model/detecting.md @@ -0,0 +1,47 @@ +--- +title: Detecting +sidebar_position: 6 +--- +> **@types** [IPublicModelDetecting](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/detecting.ts)<br/> +> **@since** v1.0.0 + +## 基本介绍 + +画布节点悬停模型 + +## 方法签名 +### capture + +capture(id: string) + +hover 指定节点 + +### release + +release(id: string) + +hover 离开指定节点 + +### leave + +leave() + +清空 hover 态 + +### current +当前 hover 的节点 + +**@since v1.0.16** + +### onDetectingChange +hover 节点变化事件 + +```typescript +/** + * hover 节点变化事件 + * set callback which will be called when hovering object changed. + * @since v1.1.0 + */ +onDetectingChange(fn: (node: IPublicModelNode) => void): () => void; +``` +**@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 new file mode 100644 index 000000000..68efe0575 --- /dev/null +++ b/docs/docs/api/model/document-model.md @@ -0,0 +1,150 @@ +--- +title: DocumentModel +sidebar_position: 0 +--- +> **@types** [IPublicModelDocumentModel](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/document-model.ts)<br/> +> **@since** v1.0.0 + +## 基本介绍 + +文档模型 + +## 变量 + +### selection + +画布节点选中区模型实例,具体方法参见 [画布节点选中区模型](./selection) + +### detecting + +画布节点 hover 区模型实例,具体方法参见 [画布节点悬停模型](./detecting) + +### history + +操作历史模型实例,具体方法参见 [操作历史模型](./history) +### canvas + +获取当前画布中的一些信息,比如拖拽时的 dropLocation + +### project + +获取当前文档模型所属的 project + +### root + +获取文档的根节点 + +### nodesMap + +获取文档下所有节点 + +### modalNodesManager + +参见 [模态节点管理](./modal-nodes-manager) + +## 方法签名 +### getNodeById + +getNodeById(nodeId: string) + +根据 nodeId 返回 [Node](./node) 实例 + +### importSchema + +importSchema(schema: RootSchema) + +导入 schema +### exportSchema + +exportSchema(stage: TransformStage = TransformStage.Render) + +导出 schema + +### insertNode + +insertNode( + parent: Node, + thing: Node, + at?: number | null | undefined, + copy?: boolean | undefined, + ) + +插入节点 +### createNode + +createNode(data: any) + +创建一个节点 +### removeNode + +removeNode(idOrNode: string | Node) + +移除指定节点/节点id + +### checkNesting +检查拖拽放置的目标节点是否可以放置该拖拽对象 + +**@since v1.0.16** + +```typescript +function checkNesting(dropTarget: Node, dragObject: DragNodeObject | DragNodeDataObject): boolean {} +``` + +## 事件 +### onAddNode + +onAddNode(fn: (node: Node) => void) + +当前 document 新增节点事件 + +```typescript +import { project } from '@alilc/lowcode-engine'; + +project.currentDocument.onAddNode((node) => { + console.log('node', node); +}) +``` + +### onRemoveNode + +onRemoveNode(fn: (node: Node) => void) + +当前 document 删除节点事件 + +### onChangeDetecting + +onChangeDetecting(fn: (node: Node) => void) + +当前 document 的 hover 变更事件 + +### onChangeSelection + +onChangeSelection(fn: (ids: string[]) => void) + +当前 document 的选中变更事件 + +### onChangeNodeVisible + +onChangeNodeVisible(fn: (node: Node, visible: boolean) => void) + +当前 document 的节点显隐状态变更事件 + +### onChangeNodeChildren + +onChangeNodeChildren(fn: (info?: IPublicOnChangeOptions) => void) + +当前 document 的节点 children 变更事件 + +### onChangeNodeProp +当前 document 节点属性修改事件 + +```typescript +onChangeNodeProp(fn: (info: IPublicTypePropChangeOptions) => void) +``` + +### onImportSchema +当前 document 导入新的 schema 事件 +版本 >= 1.0.15 +```typescript +onImportSchema(fn: (schema: any) => void) +``` \ No newline at end of file diff --git a/docs/docs/api/model/dragon.md b/docs/docs/api/model/dragon.md new file mode 100644 index 000000000..82884224b --- /dev/null +++ b/docs/docs/api/model/dragon.md @@ -0,0 +1,129 @@ +--- +title: Dragon +sidebar_position: 99 +--- +> **@types** [IPublicModelDragon](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/dragon.ts)<br/> +> **@since** v1.0.0 + +## 基本介绍 + +拖拽对象 + +### 对应接口 +```typescript +import { IPublicModelDragon } from '@alilc/lowcode-types'; +``` + +### 支持版本 + +**@since** v1.1.0 + +## 变量 + +### dragging + +是否正在拖动 + +```typescript +/** + * is dragging or not + */ +get dragging(): boolean; +``` + +## 方法签名 + +### onDragstart + +绑定 dragstart 事件 + +```typescript +/** + * 绑定 dragstart 事件 + * bind a callback function which will be called on dragging start + * @param func + * @returns + */ +onDragstart(func: (e: IPublicModelLocateEvent) => any): () => void; +``` + +### onDrag + +绑定 drag 事件 +```typescript +/** + * 绑定 drag 事件 + * bind a callback function which will be called on dragging + * @param func + * @returns + */ +onDrag(func: (e: IPublicModelLocateEvent) => any): () => void; +``` + +### onDragend + +绑定 dragend 事件 + +```typescript +/** + * 绑定 dragend 事件 + * bind a callback function which will be called on dragging end + * @param func + * @returns + */ +onDragend(func: (o: { dragObject: IPublicModelDragObject; copy?: boolean }) => any): () => void; +``` + +### from + +设置拖拽监听的区域 shell,以及自定义拖拽转换函数 boost + +```typescript +/** + * 设置拖拽监听的区域 shell,以及自定义拖拽转换函数 boost +* set a html element as shell to dragon as monitoring target, and +* set boost function which is used to transform a MouseEvent to type +* IPublicTypeDragNodeDataObject. + * @param shell 拖拽监听的区域 + * @param boost 拖拽转换函数 + */ +from(shell: Element, boost: (e: MouseEvent) => IPublicTypeDragNodeDataObject | null): any; +``` + +### boost + +发射拖拽对象 +```typescript +/** + * 发射拖拽对象 + * boost your dragObject for dragging(flying) + * + * @param dragObject 拖拽对象 + * @param boostEvent 拖拽初始时事件 + */ +boost(dragObject: IPublicTypeDragObject, boostEvent: MouseEvent | DragEvent, fromRglNode?: Node | IPublicModelNode): void; +``` + +### addSensor + +添加投放感应区 + +```typescript +/** + * 添加投放感应区 + * add sensor area + */ +addSensor(sensor: any): void; +``` + +### removeSensor + +移除投放感应 + +```typescript +/** + * 移除投放感应 + * remove sensor area + */ +removeSensor(sensor: any): void; +``` \ No newline at end of file diff --git a/docs/docs/api/model/history.md b/docs/docs/api/model/history.md new file mode 100644 index 000000000..5864febea --- /dev/null +++ b/docs/docs/api/model/history.md @@ -0,0 +1,58 @@ +--- +title: History +sidebar_position: 5 +--- +> **@types** [IPublicModelHistory](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/history.ts)<br/> +> **@since** v1.0.0 + +## 基本介绍 + +操作历史记录模型 + +## 方法签名 +### go + +go(cursor: number) + +历史记录跳转到指定位置 + +### back + +back() + +历史记录后退 + +### forward + +forward() + +历史记录前进 +### savePoint + +savePoint() + +保存当前状态 +### isSavePoint + +isSavePoint() + +当前是否是「保存点」,即是否有状态变更但未保存 + +### getState + +getState() + +获取 state,判断当前是否为「可回退」、「可前进」的状态 + +## 事件 +### onChangeState + +onChangeState(func: () => any) + +监听 state 变更事件 + +### onChangeCursor + +onChangeCursor(func: () => any) + +监听历史记录游标位置变更事件 diff --git a/docs/docs/api/model/modal-nodes-manager.md b/docs/docs/api/model/modal-nodes-manager.md new file mode 100644 index 000000000..ed89ce31b --- /dev/null +++ b/docs/docs/api/model/modal-nodes-manager.md @@ -0,0 +1,48 @@ +--- +title: ModalNodesManager +sidebar_position: 7 +--- +> **@types** [IPublicModelModalNodesManager](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/modal-nodes-manager.ts)<br/> +> **@since** v1.0.0 + +## 基本介绍 + +模态节点管理器模型 + +## 方法签名 + +### setNodes + +setNodes() + +设置模态节点,触发内部事件 + +### getModalNodes + +getModalNodes() + +获取模态节点(们) + +### getVisibleModalNode + +getVisibleModalNode() + +获取当前可见的模态节点 + +### hideModalNodes + +hideModalNodes() + +隐藏模态节点(们) + +### setVisible + +setVisible(node: Node) + +设置指定节点为可见态 + +### setInvisible + +setInvisible(node: Node) + +设置指定节点为不可见态 diff --git a/docs/docs/api/model/node-children.md b/docs/docs/api/model/node-children.md new file mode 100644 index 000000000..4c1b76b61 --- /dev/null +++ b/docs/docs/api/model/node-children.md @@ -0,0 +1,123 @@ +--- +title: NodeChildren +sidebar_position: 2 +--- +> **@types** [IPublicModelNodeChildren](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node-children.ts)<br/> +> **@since** v1.0.0 + +## 基本介绍 +节点孩子模型 + +## 变量 +### owner + +返回当前 children 实例所属的节点实例 + +### size + +children 内的节点实例数 + +### isEmpty + +是否为空 + +## 方法签名 +### delete + +delete(node: Node) + +删除指定节点 + +### insert + +insert(node: Node, at?: number | null) + +插入一个节点 + +### indexOf + +indexOf(node: Node) + +返回指定节点的下标 + +### splice + +splice(start: number, deleteCount: number, node?: Node) + +类似数组 splice 操作 + +### get + +get(index: number) + +返回指定下标的节点 + +### has + +has(node: Node) + +是否包含指定节点 + +### forEach + +forEach(fn: (node: Node, index: number) => void) + +类似数组的 forEach + +### map + +map<T\>(fn: (node: Node, index: number) => T[]) + +类似数组的 map + +### every + +every(fn: (node: Node, index: number) => boolean) + +类似数组的 every + +### some + +some(fn: (node: Node, index: number) => boolean) + +类似数组的 some + +### filter + +filter(fn: (node: Node, index: number) => boolean) + +类似数组的 filter + +### find + +find(fn: (node: Node, index: number) => boolean) + +类似数组的 find + +### reduce + +reduce(fn: (acc: any, cur: Node) => any, initialValue: any) + +类似数组的 reduce + +### importSchema + +importSchema(data?: NodeData | NodeData[]) + +导入 schema + +### exportSchema + +exportSchema(stage: IPulicEnumTransformStage = IPulicEnumTransformStage.Render) + +导出 schema + +### mergeChildren + +mergeChildren( + remover: (node: Node, idx: number) => boolean, + adder: (children: Node[]) => any, + sorter: (firstNode: Node, secondNode: Node) => number, + ) + +执行新增、删除、排序等操作 \ No newline at end of file diff --git a/docs/docs/api/model/node.md b/docs/docs/api/model/node.md new file mode 100644 index 000000000..cc031d040 --- /dev/null +++ b/docs/docs/api/model/node.md @@ -0,0 +1,250 @@ +--- +title: Node +sidebar_position: 1 +--- +> **@types** [IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts)<br/> +> **@since** v1.0.0 + +## 基本介绍 + +节点模型 + +## 变量 +### id + +节点 id + +### title + +节点标题 + +### isContainer + +是否为「容器型」节点 + +### isRoot + +是否为根节点 + +### isEmpty + +是否为空节点(无 children 或者 children 为空) + +### isPage + +是否为 Page 节点 + +### isComponent + +是否为 Component 节点 + +### isModal + +是否为「模态框」节点 + +### isSlot + +是否为插槽节点 + +### isParental + +是否为父类/分支节点 + +### isLeaf +是否为叶子节点 + +### isLocked +获取当前节点的锁定状态 + +**@since v1.0.16** + +### isRGLContainer +设置为磁贴布局节点,使用方式可参考:[磁贴布局在钉钉宜搭报表设计引擎中的实现](https://mp.weixin.qq.com/s/PSTut5ahAB8nlJ9kBpBaxw) + +**@since v1.0.16** + +### index + +下标 + +### icon + +图标 + +### zLevel + +节点所在树的层级深度,根节点深度为 0 + +### componentName + +节点 componentName + +### componentMeta + +节点的物料元数据,参见 物料元数据 + +### document + +获取节点所属的[文档模型](./document-model)对象 + +### prevSibling + +获取当前节点的前一个兄弟节点 + +### nextSibling + +获取当前节点的后一个兄弟节点 + +### parent + +获取当前节点的父亲节点 + +### children + +获取当前节点的孩子节点模型 + +### slots + +节点上挂载的插槽节点们 + +### slotFor + +当前节点为插槽节点时,返回节点对应的属性实例 + +### props + +返回节点的属性集 + +### propsData + +返回节点的属性集值 + +## 方法签名 +### getDOMNode + +getDOMNode() + +获取节点实例对应的 dom 节点 + +### getRect + +getRect() + +返回节点的尺寸、位置信息 + +### hasSlots + +hasSlots() + +是否有挂载插槽节点 + +### hasCondition + +hasCondition() + +是否设定了渲染条件 + +### hasLoop + +hasLoop() + +是否设定了循环数据 + +### getProp + +getProp(path: string): Prop | null + +获取指定 path 的属性模型实例 + +### getPropValue + +getPropValue(path: string) + +获取指定 path 的属性模型实例值 + +### getExtraProp + +getExtraProp(path: string): Prop | null + +获取指定 path 的属性模型实例,注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 + +### getExtraPropValue + +getExtraPropValue(path: string) + +获取指定 path 的属性模型实例,注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 + +### setPropValue + +setPropValue(path: string, value: CompositeValue) + +设置指定 path 的属性模型实例值 + +### setExtraPropValue + +setExtraPropValue(path: string, value: CompositeValue) + +设置指定 path 的属性模型实例值 + +### importSchema + +importSchema(data: NodeSchema) + +导入节点数据 + +### exportSchema + +exportSchema(stage: IPublicEnumTransformStage = IPublicEnumTransformStage.Render, options?: any) + +导出节点数据 + +### insertBefore + +insertBefore(node: Node, ref?: Node | undefined, useMutator?: boolean) + +在指定位置之前插入一个节点 + +### insertAfter + +insertAfter(node: Node, ref?: Node | undefined, useMutator?: boolean) + +在指定位置之后插入一个节点 + +### replaceChild + +replaceChild(node: Node, data: any) + +替换指定节点 + +### replaceWith + +replaceWith(schema: NodeSchema) + +将当前节点替换成指定节点描述 + +### select + +select() + +选中当前节点实例 + +### hover + +hover(flag = true) + +设置悬停态 + +### lock +设置节点锁定状态 + +```typescript +function lock(flag?: boolean){} +``` + +**@since v1.0.16** + +### remove + +remove() + +删除当前节点实例 \ No newline at end of file diff --git a/docs/docs/api/model/prop.md b/docs/docs/api/model/prop.md new file mode 100644 index 000000000..b7832ef9f --- /dev/null +++ b/docs/docs/api/model/prop.md @@ -0,0 +1,53 @@ +--- +title: Prop +sidebar_position: 3 +--- +> **@types** [IPublicModelProp](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/prop.ts)<br/> +> **@since** v1.0.0 + +## 基本介绍 + +属性模型 + +## 变量 + +### id + +id + +### key + +key 值 + +### path + +返回当前 prop 的路径 + +### node + +返回所属的节点实例 + +## 方法签名 + +### setValue + +setValue(val: CompositeValue) + +设置值 + +### getValue + +getValue() + +获取值 + +### remove +移除值 + +**@since v1.0.16** + +### exportSchema + +exportSchema(stage: IPublicEnumTransformStage = IPublicEnumTransformStage.Render) + +导出值 \ No newline at end of file diff --git a/docs/docs/api/model/props.md b/docs/docs/api/model/props.md new file mode 100644 index 000000000..5956a14b7 --- /dev/null +++ b/docs/docs/api/model/props.md @@ -0,0 +1,58 @@ +--- +title: Props +sidebar_position: 4 +--- +> **@types** [IPublicModelProps](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/props.ts)<br/> +> **@since** v1.0.0 + +## 基本介绍 + +属性集模型 + +## 变量 +### id + +id +### path + +返回当前 props 的路径 +### node + +返回当前属性集所属的节点实例 + +## 方法签名 +### getProp + +getProp(path: string): Prop | null + +获取指定 path 的属性模型实例 + +### getPropValue + +getPropValue(path: string) + +获取指定 path 的属性模型实例值 + +### getExtraProp + +getExtraProp(path: string): Prop | null + +获取指定 path 的属性模型实例,注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 + +### getExtraPropValue + +getExtraPropValue(path: string) + +获取指定 path 的属性模型实例值,注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 + +### setPropValue + +setPropValue(path: string, value: CompositeValue) + +设置指定 path 的属性模型实例值 + +### setExtraPropValue + +setExtraPropValue(path: string, value: CompositeValue) + +设置指定 path 的属性模型实例值 \ No newline at end of file diff --git a/docs/docs/api/model/selection.md b/docs/docs/api/model/selection.md new file mode 100644 index 000000000..77b33fa60 --- /dev/null +++ b/docs/docs/api/model/selection.md @@ -0,0 +1,85 @@ +--- +title: Selection +sidebar_position: 6 +--- +> **@types** [IPublicModelSelection](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/selection.ts)<br/> +> **@since** v1.0.0 + +## 基本介绍 + +画布节点选中模型 + +## 变量 +### selected + +返回选中的节点 id + +## 方法签名 +### select + +select(id: string) + +选中指定节点(覆盖方式) + +### selectAll + +selectAll(ids: string[]) + +批量选中指定节点们 + +### remove + +remove(id: string) + +**取消选中**选中的指定节点,不会删除组件 + +### clear + +clear() + +**取消选中**所有选中节点,不会删除组件 + +### has + +has(id: string) + +判断是否选中了指定节点 + +### add + +add(id: string) + +选中指定节点(增量方式) + +### getNodes + +getNodes() + +获取选中的节点实例 + +### getTopNodes +获取选区的顶层节点 +例如选中的节点为: + +- DivA + - ChildrenA +- DivB + +getNodes 返回的是 [DivA、ChildrenA、DivB],getTopNodes 返回的是 [DivA、DivB],其中 ChildrenA 由于是二层节点,getTopNodes 不会返回 + +**@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; +``` + +**@since v1.1.0** \ No newline at end of file diff --git a/docs/docs/api/plugins.md b/docs/docs/api/plugins.md index bf683b365..06b558369 100644 --- a/docs/docs/api/plugins.md +++ b/docs/docs/api/plugins.md @@ -2,25 +2,27 @@ title: plugins - 插件 API sidebar_position: 4 --- +> **@types** [IPublicApiPlugins](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/plugins.ts)<br/> +> **@since** v1.0.0 + ## 模块简介 插件管理器,提供编排模块中管理插件的能力。 -## 变量(variables) -无 -## 方法签名(functions) + +## 方法签名 ### register 注册插件 -#### 类型定义 ```typescript async function register( - pluginConfigCreator: (ctx: IPublicModelPluginContext) => IPublicTypePluginConfig, - options?: ILowCodeRegisterOptions, + plugin: IPublicTypePlugin, + options?: IPublicTypePluginRegisterOptions, ): Promise<void> ``` -pluginConfigCreator 是一个 IPublicTypePluginConfig 生成函数,IPublicTypePluginConfig 中包含了该插件的 init / destroy 等钩子函数,以及 exports 函数用于返回插件对外暴露的值。 +相关 types: +- [IPublicTypePlugin](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/plugin.ts) +- [IPublicTypePluginRegisterOptions](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/plugin-register-options.ts) -另外,pluginConfigCreator 还必须挂载 pluginName 字段,全局确保唯一,否则 register 时会报错,可以选择性挂载 meta 字段,用于描述插件的元数据信息,比如兼容的引擎版本、支持的参数配置、依赖插件声明等。 -> 注:pluginConfigCreator 挂载 pluginName / meta 可以通过低代码工具链的插件脚手架生成,写如 package.json 后将会自动注入到代码中,具体见 [插件元数据工程化示例](#RO9YY) +其中第一个参数 plugin 通过低代码工具链的插件脚手架生成编写模板,开发者可以参考[这个章节](/site/docs/guide/expand/editor/cli)进行创建 #### 简单示例 @@ -61,59 +63,59 @@ await plugins.register(builtinPluginRegistry); import { plugins } from '@alilc/lowcode-engine'; import { IPublicModelPluginContext } from '@alilc/lowcode-types'; -const pluginA = (ctx: IPublicModelPluginContext) => { +const PluginA = (ctx: IPublicModelPluginContext) => { return { async init() {}, exports() { return { x: 1, } }, }; } -pluginA.pluginName = 'pluginA'; +PluginA.pluginName = 'PluginA'; -const pluginB = (ctx: IPublicModelPluginContext) => { +const PluginB = (ctx: IPublicModelPluginContext) => { return { async init() { // 获取 pluginA 的导出值 - console.log(ctx.plugins.pluginA.x); // => 1 + console.log(ctx.plugins.PluginA.x); // => 1 }, }; } -pluginA.pluginName = 'pluginA'; -pluginB.pluginName = 'pluginB'; -pluginB.meta = { - dependencies: ['pluginA'], +PluginA.pluginName = 'pluginA'; +PluginB.pluginName = 'PluginB'; +PluginB.meta = { + dependencies: ['PluginA'], } -await plugins.register(pluginA); -await plugins.register(pluginB); +await plugins.register(PluginA); +await plugins.register(PluginB); ``` -> 注:ctx 是在插件 creator 中获取引擎 API 的上下文,具体定义参见 [IPublicModelPluginContext](#qEhTb) +> 注:ctx 是在插件中获取引擎 API 的唯一渠道,具体定义参见 [IPublicModelPluginContext](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/plugin-context.ts) + -#### #### 设置兼容引擎版本示例 ```typescript import { plugins } from '@alilc/lowcode-engine'; import { IPublicModelPluginContext } from '@alilc/lowcode-types'; -const builtinPluginRegistry = (ctx: IPublicModelPluginContext) => { +const BuiltinPluginRegistry = (ctx: IPublicModelPluginContext) => { return { async init() { ... }, }; } -builtinPluginRegistry.pluginName = 'builtinPluginRegistry'; -builtinPluginRegistry.meta = { +BuiltinPluginRegistry.pluginName = 'BuiltinPluginRegistry'; +BuiltinPluginRegistry.meta = { engines: { lowcodeEngine: '^1.0.0', // 插件需要配合 ^1.0.0 的引擎才可运行 }, } -await plugins.register(builtinPluginRegistry); +await plugins.register(BuiltinPluginRegistry); ``` #### 设置插件参数版本示例 ```typescript import { plugins } from '@alilc/lowcode-engine'; import { IPublicModelPluginContext } from '@alilc/lowcode-types'; -const builtinPluginRegistry = (ctx: IPublicModelPluginContext, options: any) => { +const BuiltinPluginRegistry = (ctx: IPublicModelPluginContext, options: any) => { return { async init() { // 1.0.4 之后的传值方式,通过 register(xxx, options) @@ -124,8 +126,8 @@ const builtinPluginRegistry = (ctx: IPublicModelPluginContext, options: any) => }, }; } -builtinPluginRegistry.pluginName = 'builtinPluginRegistry'; -builtinPluginRegistry.meta = { +BuiltinPluginRegistry.pluginName = 'BuiltinPluginRegistry'; +BuiltinPluginRegistry.meta = { preferenceDeclaration: { title: 'pluginA 的参数定义', properties: [ @@ -154,101 +156,14 @@ builtinPluginRegistry.meta = { } // 从 1.0.4 开始,支持直接在 pluginCreator 的第二个参数 options 获取入参 -await plugins.register(builtinPluginRegistry, { key1: 'abc', key5: 'willNotPassToPlugin' }); - -// 1.0.4 之前,通过 preference 来传递 / 获取值 -const preference = new Map(); -preference.set('builtinPluginRegistry', { - key1: 'abc', - key5: 'willNotPassToPlugin', // 因为 key5 不在插件声明可接受的参数里 -}); - -init(document.getElementById('lce'), engineOptions, preference); - +await plugins.register(BuiltinPluginRegistry, { key1: 'abc', key5: 'willNotPassToPlugin' }); ``` -### get -获取插件实例 +## 相关类型定义 -**类型定义** -```typescript -function get(pluginName: string): ILowCodePlugin | undefined -``` -**调用示例** -```typescript -import { plugins } from '@alilc/lowcode-engine'; +- [IPublicModelPluginContext](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/plugin-context.ts) +- [IPublicTypePluginConfig](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/plugin-config.ts) -plugins.get(builtinPluginRegistry); -``` -### -### getAll -获取所有插件实例 - -**类型定义** -```typescript -function getAll(): ILowCodePlugin[] -``` -**调用示例** -```typescript -import { plugins } from '@alilc/lowcode-engine'; - -plugins.getAll(); -``` -### -### has -判断是否已经加载了指定插件 -**类型定义** -```typescript -function has(pluginName: string): boolean -``` -**调用示例** -```typescript -import { plugins } from '@alilc/lowcode-engine'; - -plugins.has('builtinPluginRegistry'); -``` -### delete -删除指定插件 -**类型定义** -```typescript -async function delete(pluginName: string): Promise<boolean> -``` -**调用示例** -```typescript -import { plugins } from '@alilc/lowcode-engine'; - -plugins.delete('builtinPluginRegistry'); -``` -## -## 事件(events) -无 -## 相关模块 -### IPublicModelPluginContext -**类型定义** -```typescript -export interface IPublicModelPluginContext { - get skeleton(): IPublicApiSkeleton; - get hotkey(): IPublicApiHotkey; - get setters(): IPublicApiSetters; - get config(): IEngineConfig; - get material(): IPublicApiMaterial; - get event(): IPublicApiEvent; - get project(): IPublicApiProject; - get common(): IPublicApiCommon; - logger: IPublicApiLogger; - plugins: IPublicApiPlugins; - preference: IPluginPreferenceMananger; -} -``` -### IPublicTypePluginConfig -**类型定义** -```typescript -export interface IPublicTypePluginConfig { - init?(): void; - destroy?(): void; - exports?(): any; -} -``` ## 插件元数据工程转化示例 your-plugin/package.json ```json @@ -279,8 +194,3 @@ debug.meta = { preferenceDeclaration: { ... } }; ``` -### - -## 使用示例 - -更多示例参考:[https://github.com/alibaba/lowcode-demo/blob/058450edb584d92be6cb665b1f3a9646ba464ffa/src/universal/plugin.tsx#L36](https://github.com/alibaba/lowcode-demo/blob/058450edb584d92be6cb665b1f3a9646ba464ffa/src/universal/plugin.tsx#L36) diff --git a/docs/docs/api/project.md b/docs/docs/api/project.md index 64400cf2a..0a22cd920 100644 --- a/docs/docs/api/project.md +++ b/docs/docs/api/project.md @@ -2,880 +2,316 @@ title: project - 模型 API sidebar_position: 3 --- -# 模块简介 -引擎编排模块中包含多种模型,包括[项目模型(project)](#DADnF)、[文档模型(document-model)](#lp7xO)、[节点模型(node)](#m0cJS)、[节点孩子模型(node-children)](#W8seq)、[属性集模型(props)](#IJeRY)以及[属性模型(prop)](#w1diM)。 +## 模块简介 + +引擎编排模块中包含多种模型,包括: +- [文档模型 DocumentModel)](./model/document-model) +- [节点模型 Node](./model/node) +- [节点孩子模型 NodeChildren](./model/node-children) +- [属性模型 Prop](./model/prop) +- [属性集模型 Props](./model/props) + 他们的依赖关系如下图: ![image.png](https://img.alicdn.com/imgextra/i3/O1CN01B1bAZi1asNU3KaSUJ_!!6000000003385-2-tps-1650-1352.png) -在文档模型内部,又有一些引申模型,比如[历史操作(history)](#xvIKj)、[画布节点选中(selection)](#GtFkP)、[画布节点悬停(detecting)](#Tjt05)等。 -整个模型系统,以项目模型为最顶层的模型,其他模型实例均需要通过 project 来获得,比如 project.currentDocument 来获取当前的文档模型,project.currentDocument.nodesMap 来获取当前文档模型里所有的节点列表。 +在文档模型内部,又有一些引申模型,比如: +- [历史操作 History)](./model/history) +- [画布节点选中 Selection)](./model/selection) +- [画布节点悬停 Detecting)](./model/detecting) +- [模态节点管理器 ModalNodesManager](./model/modal_nodes_manager) -# 项目模型(Project) -## 变量(variables) +整个模型系统,以 project API 为入口,所有模型实例均需要通过 project 来获得,比如 project.currentDocument 来获取当前的文档模型,project.currentDocument.nodesMap 来获取当前文档模型里所有的节点列表。 + +下面来看看 project API 的具体介绍 + +## 变量 ### currentDocument 获取当前的 document 实例 + +```typescript +/** + * 获取当前的 document + * get current document + */ +get currentDocument(): IPublicModelDocumentModel | null; +``` + +相关类型:[IPublicModelDocumentModel](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/document-model.ts) + ### documents 获取当前 project 下所有 documents + +```typescript +/** + * 获取当前 project 下所有 documents + * get all documents of this project + * @returns + */ +get documents(): IPublicModelDocumentModel[]; +``` + +相关类型:[IPublicModelDocumentModel](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/document-model.ts) + ### simulatorHost 获取模拟器的 host -## -## 方法签名(functions) -### openDocument -openDocument(doc?: string | RootSchema | undefined) +```typescript +/** + * 获取模拟器的 host + * get simulator host + */ +get simulatorHost(): IPublicApiSimulatorHost | null; +``` + +相关类型:[IPublicApiSimulatorHost](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/simulator-host.ts) + + +## 方法签名 +### openDocument 打开一个 document +```typescript +/** + * 打开一个 document + * @param doc + * @returns + */ +openDocument(doc?: string | IPublicTypeRootSchema | undefined): IPublicModelDocumentModel | null; +``` + +相关类型: +- [IPublicApiSimulatorHost](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/simulator-host.ts) +- [IPublicTypeRootSchema](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/root-schema.ts) + ### createDocument -createDocument(data?: RootSchema): DocumentModel | null - 创建一个 document -### -### removeDocument -removeDocument(doc: DocumentModel) +```typescript +/** + * 创建一个 document + * create a document + * @param data + * @returns + */ +createDocument(data?: IPublicTypeRootSchema): IPublicModelDocumentModel | null; +``` + +相关类型: +- [IPublicApiSimulatorHost](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/simulator-host.ts) +- [IPublicTypeRootSchema](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/root-schema.ts) + +### removeDocument 删除一个 document +```typescript +/** + * 删除一个 document + * remove a document + * @param doc + */ +removeDocument(doc: IPublicModelDocumentModel): void; +``` + +相关类型:[IPublicApiSimulatorHost](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/simulator-host.ts) + ### getDocumentByFileName -getDocumentByFileName(fileName: string): DocumentModel | null - 根据 fileName 获取 document +```typescript +/** + * 根据 fileName 获取 document + * get a document by filename + * @param fileName + * @returns + */ +getDocumentByFileName(fileName: string): IPublicModelDocumentModel | null; +``` + +相关类型:[IPublicApiSimulatorHost](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/simulator-host.ts) ### getDocumentById -getDocumentById(id: string): DocumentModel | null - 根据 id 获取 document +```typescript +/** + * 根据 id 获取 document + * get a document by id + * @param id + * @returns + */ +getDocumentById(id: string): IPublicModelDocumentModel | null; +``` + +相关类型:[IPublicApiSimulatorHost](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/simulator-host.ts) + ### exportSchema -exportSchema() +导出 project schema -导出 project +```typescript +/** + * 导出 project + * export project to schema + * @returns + */ +exportSchema(stage: IPublicEnumTransformStage): IPublicTypeProjectSchema; +``` + +相关类型: +- [IPublicEnumTransformStage](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/enum/transform-stage.ts) +- [IPublicTypeProjectSchema](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/project-schema.ts) ### importSchema -importSchema(schema?: ProjectSchema) - 导入 project +```typescript +/** + * 导入 project schema + * import schema to project + * @param schema 待导入的 project 数据 + */ +importSchema(schema?: IPublicTypeProjectSchema): void; +``` +相关类型:[IPublicTypeProjectSchema](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/project-schema.ts) + ### getCurrentDocument - -getCurrentDocument(): DocumentModel | null - 获取当前的 document +```typescript +/** + * 获取当前的 document + * get current document + * @returns + */ +getCurrentDocument(): IPublicModelDocumentModel | null; +``` +相关类型:[IPublicModelDocumentModel](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/document-model.ts) + ### addPropsTransducer - -addPropsTransducer(transducer: PropsTransducer, stage: TransformStage) - 增加一个属性的管道处理函数 -**示例 1:在保存的时候删除每一个组件的 props.hidden** +```typescript +/** + * 增加一个属性的管道处理函数 + * add a transducer to process prop + * @param transducer + * @param stage + */ +addPropsTransducer( + transducer: IPublicTypePropsTransducer, + stage: IPublicEnumTransformStage, + ): void; +``` +相关类型: +- [IPublicTypePropsTransducer](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/props-transducer.ts) +- [IPublicEnumTransformStage](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/enum/transform-stage.ts) + +#### 示例 +在保存的时候删除每一个组件的 props.hidden ```typescript import { project } from '@alilc/lowcode-engine'; -import { CompositeObject, TransformStage, IPublicModelPluginContext } from '@alilc/lowcode-types'; +import { IPublicTypeCompositeObject, IPublicEnumTransformStage, IPublicModelPluginContext } from '@alilc/lowcode-types'; -export const deleteHiddenTransducer = (ctx: IPublicModelPluginContext) => { +export const DeleteHiddenTransducer = (ctx: IPublicModelPluginContext) => { return { - name: 'deleteHiddenTransducer', async init() { - project.addPropsTransducer((props: CompositeObject): CompositeObject => { + const { project } = ctx; + project.addPropsTransducer((props: IPublicTypeCompositeObject): IPublicTypeCompositeObject => { delete props.hidden; return props; - }, TransformStage.Save); + }, IPublicEnumTransformStage.Save); }, }; } -deleteHiddenTransducer.pluginName = 'deleteHiddenTransducer'; +DeleteHiddenTransducer.pluginName = 'DeleteHiddenTransducer'; ``` +### setI18n +设置多语言语料 + +```typescript +/** + * 设置多语言语料 + * 数据格式参考 https://github.com/alibaba/lowcode-engine/blob/main/specs/lowcode-spec.md#2434%E5%9B%BD%E9%99%85%E5%8C%96%E5%A4%9A%E8%AF%AD%E8%A8%80%E7%B1%BB%E5%9E%8Baa + * + * set I18n data for this project + * @param value object + * @since v1.0.17 + */ +setI18n(value: object): void; +``` + +**@since v1.0.17** + + +## 事件 + ### onRemoveDocument 绑定删除文档事件 -```typescript -function onRemoveDocument(fn: (data: { docId: string}) => void) {} -``` -*引擎版本>=1.0.16 -### setI18n -设置多语言语料 -```typescript -function setI18n(value: object) {} -``` -*引擎版本>=1.0.17 -## 事件(events) -### onChangeDocument -onChangeDocument(fn: (doc: DocumentModel) => void) +```typescript +/** + * 绑定删除文档事件 + * set callback for event onDocumentRemoved + * @param fn + * @since v1.0.16 + */ +onRemoveDocument(fn: (data: { id: string }) => void): IPublicTypeDisposable; +``` +相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) + +**@since v1.0.16** + +### onChangeDocument 当前 project 内的 document 变更事件 -### onSimulatorHostReady +```typescript +/** + * 当前 project 内的 document 变更事件 + * set callback for event onDocumentChanged + */ +onChangeDocument(fn: (doc: IPublicModelDocumentModel) => void): IPublicTypeDisposable; +``` -onSimulatorHostReady(fn: (host: SimulatorHost) => void) +相关类型: +- [IPublicModelDocumentModel](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/document-model.ts) +- [IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) + +### onSimulatorHostReady 当前 project 的模拟器 ready 事件 +```typescript +/** + * 当前 project 的模拟器 ready 事件 + * set callback for event onSimulatorHostReady + */ +onSimulatorHostReady(fn: (host: IPublicApiSimulatorHost) => void): IPublicTypeDisposable; +``` +相关类型: +- [IPublicApiSimulatorHost](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/simulator-host.ts) +- [IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) + ### onSimulatorRendererReady -onSimulatorRendererReady(fn: () => void) - 当前 project 的渲染器 ready 事件 -# -# 文档模型(DocumentModel) -## 变量(variables) -### selection - -画布节点选中区模型实例,具体方法参见 [画布节点选中区模型](#GtFkP) - -### detecting - -画布节点 hover 区模型实例,具体方法参见 [画布节点 hover 区模型](#Tjt05) - -### history - -操作历史模型实例,具体方法参见 [操作历史模型](#xvIKj) -### canvas - -获取当前画布中的一些信息,比如拖拽时的 dropLocation - -### project - -获取当前文档模型所属的 project - -### root - -获取文档的根节点 - -### nodesMap - -获取文档下所有节点 - -### modalNodesManager - -参见 [模态节点管理](#nNRF9) - -## 方法签名(functions) -### getNodeById - -getNodeById(nodeId: string) - -根据 nodeId 返回 [Node](#m0cJS) 实例 - -### importSchema - -importSchema(schema: RootSchema) - -导入 schema -### exportSchema - -exportSchema(stage: TransformStage = TransformStage.Render) - -导出 schema - -### insertNode - -insertNode( - parent: Node, - thing: Node, - at?: number | null | undefined, - copy?: boolean | undefined, - ) - -插入节点 -### createNode - -createNode(data: any) - -创建一个节点 -### removeNode - -removeNode(idOrNode: string | Node) - -移除指定节点/节点id - -### checkNesting -检查拖拽放置的目标节点是否可以放置该拖拽对象 -*引擎版本 > 1.0.16 -```typescript -function checkNesting(dropTarget: Node, dragObject: DragNodeObject | DragNodeDataObject): boolean {} -``` -## -## 事件(events) -### onAddNode - -onAddNode(fn: (node: Node) => void) - -当前 document 新增节点事件 ```typescript -import { project } from '@alilc/lowcode-engine'; - -project.currentDocument.onAddNode((node) => { - console.log('node', node); -}) +/** + * 当前 project 的渲染器 ready 事件 + * set callback for event onSimulatorRendererReady + */ +onSimulatorRendererReady(fn: () => void): IPublicTypeDisposable; ``` -### onRemoveNode - -onRemoveNode(fn: (node: Node) => void) - -当前 document 删除节点事件 - -### onChangeDetecting - -onChangeDetecting(fn: (node: Node) => void) - -当前 document 的 hover 变更事件 - -### onChangeSelection - -onChangeSelection(fn: (ids: string[]) => void) - -当前 document 的选中变更事件 - -### onChangeNodeVisible - -onChangeNodeVisible(fn: (node: Node, visible: boolean) => void) - -当前 document 的节点显隐状态变更事件 - -### onChangeNodeChildren - -onChangeNodeChildren(fn: (info?: IPublicOnChangeOptions) => void) - -当前 document 的节点 children 变更事件 - -### onChangeNodeProp -当前 document 节点属性修改事件 -```typescript -onChangeNodeProp(fn: (info: PropChangeOptions) => void) -``` - -### onImportSchema -当前 document 导入新的 schema 事件 -版本 >= 1.0.15 -```typescript -onImportSchema(fn: (schema: any) => void) -``` - -# 画布节点选中模型(Selection) -## 变量(variables) -### selected - -返回选中的节点 id - -## 方法签名(functions) -### select - -select(id: string) - -选中指定节点(覆盖方式) - -### selectAll - -selectAll(ids: string[]) - -批量选中指定节点们 - -### remove - -remove(id: string) - -**取消选中**选中的指定节点,不会删除组件 - -### clear - -clear() - -**取消选中**所有选中节点,不会删除组件 - -### has - -has(id: string) - -判断是否选中了指定节点 - -### add - -add(id: string) - -选中指定节点(增量方式) - -### getNodes - -getNodes() - -获取选中的节点实例 - -### getTopNodes -获取选区的顶层节点 -例如选中的节点为: - -- DivA - - ChildrenA -- DivB - -getNodes 返回的是 [DivA、ChildrenA、DivB],getTopNodes 返回的是 [DivA、DivB],其中 ChildrenA 由于是二层节点,getTopNodes 不会返回 - -*引擎版本 >= 1.0.16 - - -# 画布节点悬停模型(Detecting) -## 方法签名(functions) -### capture - -capture(id: string) - -hover 指定节点 - -### release - -release(id: string) - -hover 离开指定节点 - -### leave - -leave() - -清空 hover 态 - -### current -当前 hover 的节点 -*引擎版本 >= 1.0.16 - -# 操作历史记录模型(History) -## 方法签名(functions) -### go - -go(cursor: number) - -历史记录跳转到指定位置 - -### back - -back() - -历史记录后退 - -### forward - -forward() - -历史记录前进 -### savePoint - -savePoint() - -保存当前状态 -### isSavePoint - -isSavePoint() - -当前是否是「保存点」,即是否有状态变更但未保存 - -### getState - -getState() - -获取 state,判断当前是否为「可回退」、「可前进」的状态 - -## 事件(events) -### onChangeState - -onChangeState(func: () => any) - -监听 state 变更事件 - -### onChangeCursor - -onChangeCursor(func: () => any) - -监听历史记录游标位置变更事件 - -# 节点模型(Node) -## 变量(variables) -### id - -节点 id - -### title - -节点标题 - -### isContainer - -是否为「容器型」节点 - -### isRoot - -是否为根节点 - -### isEmpty - -是否为空节点(无 children 或者 children 为空) - -### isPage - -是否为 Page 节点 - -### isComponent - -是否为 Component 节点 - -### isModal - -是否为「模态框」节点 - -### isSlot - -是否为插槽节点 - -### isParental - -是否为父类/分支节点 - -### isLeaf -是否为叶子节点 - -### isLocked -获取当前节点的锁定状态 -*引擎版本>=1.0.16 - -### isRGLContainer -设置为磁贴布局节点,使用方式可参考:[磁贴布局在钉钉宜搭报表设计引擎中的实现](https://mp.weixin.qq.com/s/PSTut5ahAB8nlJ9kBpBaxw) -*引擎版本>=1.0.16 - -### index - -下标 - -### icon - -图标 - -### zLevel - -节点所在树的层级深度,根节点深度为 0 - -### componentName - -节点 componentName - -### componentMeta - -节点的物料元数据,参见 物料元数据 - -### document - -获取节点所属的[文档模型](#lp7xO)对象 - -### prevSibling - -获取当前节点的前一个兄弟节点 - -### nextSibling - -获取当前节点的后一个兄弟节点 - -### parent - -获取当前节点的父亲节点 - -### children - -获取当前节点的孩子节点模型 - -### slots - -节点上挂载的插槽节点们 - -### slotFor - -当前节点为插槽节点时,返回节点对应的属性实例 - -### props - -返回节点的属性集 - -### propsData - -返回节点的属性集值 - -## 方法签名(functions) -### getDOMNode - -getDOMNode() - -获取节点实例对应的 dom 节点 - -### getRect - -getRect() - -返回节点的尺寸、位置信息 - -### hasSlots - -hasSlots() - -是否有挂载插槽节点 - -### hasCondition - -hasCondition() - -是否设定了渲染条件 - -### hasLoop - -hasLoop() - -是否设定了循环数据 - -### getProp - -getProp(path: string): Prop | null - -获取指定 path 的属性模型实例 - -### getPropValue - -getPropValue(path: string) - -获取指定 path 的属性模型实例值 - -### getExtraProp - -getExtraProp(path: string): Prop | null - -获取指定 path 的属性模型实例,注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 - -### getExtraPropValue - -getExtraPropValue(path: string) - -获取指定 path 的属性模型实例,注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 - -### setPropValue - -setPropValue(path: string, value: CompositeValue) - -设置指定 path 的属性模型实例值 - -### setExtraPropValue - -setExtraPropValue(path: string, value: CompositeValue) - -设置指定 path 的属性模型实例值 - -### importSchema - -importSchema(data: NodeSchema) - -导入节点数据 - -### exportSchema - -exportSchema(stage: TransformStage = TransformStage.Render, options?: any) - -导出节点数据 - -### insertBefore - -insertBefore(node: Node, ref?: Node | undefined, useMutator?: boolean) - -在指定位置之前插入一个节点 - -### insertAfter - -insertAfter(node: Node, ref?: Node | undefined, useMutator?: boolean) - -在指定位置之后插入一个节点 - -### replaceChild - -replaceChild(node: Node, data: any) - -替换指定节点 - -### replaceWith - -replaceWith(schema: NodeSchema) - -将当前节点替换成指定节点描述 - -### select - -select() - -选中当前节点实例 - -### hover - -hover(flag = true) - -设置悬停态 - -### lock -设置节点锁定状态 -```typescript -function lock(flag?: boolean){} -``` -*引擎版本>=1.0.16 - -### remove - -remove() - -删除当前节点实例 - -# 节点孩子模型(NodeChildren) -## 变量(variables) -### owner - -返回当前 children 实例所属的节点实例 - -### size - -children 内的节点实例数 - -### isEmpty - -是否为空 - -## 方法签名(functions) -### delete - -delete(node: Node) - -删除指定节点 - -### insert - -insert(node: Node, at?: number | null) - -插入一个节点 - -### indexOf - -indexOf(node: Node) - -返回指定节点的下标 - -### splice - -splice(start: number, deleteCount: number, node?: Node) - -类似数组 splice 操作 - -### get - -get(index: number) - -返回指定下标的节点 - -### has - -has(node: Node) - -是否包含指定节点 - -### forEach - -forEach(fn: (node: Node, index: number) => void) - -类似数组的 forEach - -### map - -map<T\>(fn: (node: Node, index: number) => T[]) - -类似数组的 map - -### every - -every(fn: (node: Node, index: number) => boolean) - -类似数组的 every - -### some - -some(fn: (node: Node, index: number) => boolean) - -类似数组的 some - -### filter - -filter(fn: (node: Node, index: number) => boolean) - -类似数组的 filter - -### find - -find(fn: (node: Node, index: number) => boolean) - -类似数组的 find - -### reduce - -reduce(fn: (acc: any, cur: Node) => any, initialValue: any) - -类似数组的 reduce - -### importSchema - -importSchema(data?: NodeData | NodeData[]) - -导入 schema - -### exportSchema - -exportSchema(stage: TransformStage = TransformStage.Render) - -导出 schema - -### mergeChildren - -mergeChildren( - remover: (node: Node, idx: number) => boolean, - adder: (children: Node[]) => any, - sorter: (firstNode: Node, secondNode: Node) => number, - ) - -执行新增、删除、排序等操作 - -# 属性集模型(Props) -## 变量(variables) -### id - -id -### path - -返回当前 props 的路径 -### node - -返回当前属性集所属的节点实例 - -## 方法签名(functions) -### getProp - -getProp(path: string): Prop | null - -获取指定 path 的属性模型实例 - -### getPropValue - -getPropValue(path: string) - -获取指定 path 的属性模型实例值 - -### getExtraProp - -getExtraProp(path: string): Prop | null - -获取指定 path 的属性模型实例,注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 -### getExtraPropValue - -getExtraPropValue(path: string) - -获取指定 path 的属性模型实例值,注:导出时,不同于普通属性,该属性并不挂载在 props 之下,而是与 props 同级 -### - -### setPropValue - -setPropValue(path: string, value: CompositeValue) - -设置指定 path 的属性模型实例值 - -### setExtraPropValue - -setExtraPropValue(path: string, value: CompositeValue) - -设置指定 path 的属性模型实例值 - -# 属性模型(Prop) -## 变量(variables) -### id - -id - -### key - -key 值 - -### path - -返回当前 prop 的路径 - -### node - -返回所属的节点实例 - -## 方法签名(functions) -### setValue - -setValue(val: CompositeValue) - -设置值 - -### getValue - -getValue() - -获取值 - -### remove -移除值 -*引擎版本>=1.0.16 - -### exportSchema - -exportSchema(stage: TransformStage = TransformStage.Render) - -导出值 - -# 模态节点管理模型(ModalNodesManager) -## 方法签名(functions) -### getModalNodes - -getModalNodes() - -获取模态节点(们) - -### getVisibleModalNode - -getVisibleModalNode() - -获取当前可见的模态节点 - -### hideModalNodes - -hideModalNodes() - -隐藏模态节点(们) - -### setVisible - -setVisible(node: Node) - -设置指定节点为可见态 - -### setInvisible - -setInvisible(node: Node) - -设置指定节点为不可见态 - -### setNodes - -setNodes() - -设置模态节点,触发内部事件 +相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) diff --git a/docs/docs/api/setters.md b/docs/docs/api/setters.md index b36d7162d..3c5fd9f75 100644 --- a/docs/docs/api/setters.md +++ b/docs/docs/api/setters.md @@ -2,37 +2,64 @@ title: setters - 设置器 API sidebar_position: 6 --- +> **@types** [IPublicApiSetters](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/setters.ts)<br/> +> **@since** v1.0.0 + ## 模块简介 负责注册设置器、管理设置器的 API。注册自定义设置器之后可以在物料中进行使用。 -## 方法签名(functions) +## 方法签名 ### getSetter 获取指定 setter -**类型定义** ```typescript -function getSetter(type: string): RegisteredSetter; +/** + * 获取指定 setter + * get setter by type + * @param type + * @returns + */ +getSetter(type: string): IPublicTypeRegisteredSetter | null; ``` +相关类型:[IPublicTypeRegisteredSetter](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/registerd-setter.ts) ### getSettersMap 获取已注册的所有 settersMap -**类型定义** ```typescript -function getSettersMap(): Map<string, RegisteredSetter> +/** + * 获取已注册的所有 settersMap + * get map of all registered setters + * @returns + */ +getSettersMap(): Map<string, IPublicTypeRegisteredSetter & { + type: string; +}>; ``` +相关类型:[IPublicTypeRegisteredSetter](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/registerd-setter.ts) + ### registerSetter 注册一个 setter -**类型定义** ```typescript -function registerSetter( - typeOrMaps: string | { [key: string]: CustomView | RegisteredSetter }, - setter?: CustomView | RegisteredSetter | undefined, +/** + * 注册一个 setter + * register a setter + * @param typeOrMaps + * @param setter + * @returns + */ +registerSetter( + typeOrMaps: string | { [key: string]: IPublicTypeCustomView | IPublicTypeRegisteredSetter }, + setter?: IPublicTypeCustomView | IPublicTypeRegisteredSetter | undefined ): void; ``` +相关类型: +- [IPublicTypeRegisteredSetter](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/registerd-setter.ts) +- [IPublicTypeCustomView](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/custom-view.ts) + ## 使用示例 ### 注册官方内置 Setter 到设计器中 ```typescript @@ -40,7 +67,7 @@ import { setters, skeleton } from '@alilc/lowcode-engine'; import { setterMap, pluginMap } from '@alilc/lowcode-engine-ext'; import { IPublicModelPluginContext } from '@alilc/lowcode-types'; -const setterRegistry = (ctx: IPublicModelPluginContext) => { +const SetterRegistry = (ctx: IPublicModelPluginContext) => { return { name: 'ext-setters-registry', async init() { @@ -68,8 +95,8 @@ const setterRegistry = (ctx: IPublicModelPluginContext) => { }; } -setterRegistry.pluginName = 'setterRegistry'; -await plugins.register(setterRegistry); +SetterRegistry.pluginName = 'SetterRegistry'; +await plugins.register(SetterRegistry); ``` ### 开发自定义 Setter @@ -112,182 +139,13 @@ export default class AltStringSetter extends React.PureComponent<AltStringSetter } } ``` + 开发完毕之后,注册 AltStringSetter 到设计器中: + ```typescript import AltStringSetter from './AltStringSetter'; -const registerSetter = window.AliLowCodeEngine.setters.registerSetter; -registerSetter('AltStringSetter', AltStringSetter); -``` -注册之后,我们就可以在物料中使用了,其中核心配置如下: -```typescript -{ - "props": { - "isExtends": true, - "override": [ - { - "name": "type", - "setter": "AltStringSetter" - } - ] - } -} -``` -完整配置如下: -```typescript -{ - "componentName": "Message", - "title": "Message", - "props": [ - { - "name": "title", - "propType": "string", - "description": "标题", - "defaultValue": "标题" - }, - { - "name": "type", - "propType": { - "type": "oneOf", - "value": [ - "success", - "warning", - "error", - "notice", - "help", - "loading" - ] - }, - "description": "反馈类型", - "defaultValue": "success" - } - ], - "configure": { - "props": { - "isExtends": true, - "override": [ - { - "name": "type", - "setter": "AltStringSetter" - } - ] - } - } -} -``` -## 模块简介 -负责注册设置器、管理设置器的 API。注册自定义设置器之后可以在物料中进行使用。 - -## 方法签名(functions) -### getSetter -获取指定 setter - -**类型定义** -```typescript -function getSetter(type: string): RegisteredSetter; -``` - -### getSettersMap -获取已注册的所有 settersMap - -**类型定义** -```typescript -function getSettersMap(): Map<string, RegisteredSetter> -``` - -### registerSetter -注册一个 setter - -**类型定义** -```typescript -function registerSetter( - typeOrMaps: string | { [key: string]: CustomView | RegisteredSetter }, - setter?: CustomView | RegisteredSetter | undefined, -): void; -``` - -## 使用示例 -### 注册官方内置 Setter 到设计器中 -```typescript -import { setters, skeleton } from '@alilc/lowcode-engine'; -import { setterMap, pluginMap } from '@alilc/lowcode-engine-ext'; -import { IPublicModelPluginContext } from '@alilc/lowcode-types'; - -const setterRegistry = (ctx: IPublicModelPluginContext) => { - return { - name: 'ext-setters-registry', - async init() { - // 注册 setterMap - setters.registerSetter(setterMap); - // 注册插件 - // 注册事件绑定面板 - skeleton.add({ - area: 'centerArea', - type: 'Widget', - content: pluginMap.EventBindDialog, - name: 'eventBindDialog', - props: {}, - }); - - // 注册变量绑定面板 - skeleton.add({ - area: 'centerArea', - type: 'Widget', - content: pluginMap.VariableBindDialog, - name: 'variableBindDialog', - props: {}, - }); - }, - }; -} - -setterRegistry.pluginName = 'setterRegistry'; -await plugins.register(setterRegistry); -``` - -### 开发自定义 Setter -AltStringSetter 代码如下: -```typescript -import * as React from "react"; -import { Input } from "@alifd/next"; - -import "./index.scss"; -interface AltStringSetterProps { - // 当前值 - value: string; - // 默认值 - initialValue: string; - // setter 唯一输出 - onChange: (val: string) => void; - // AltStringSetter 特殊配置 - placeholder: string; -} -export default class AltStringSetter extends React.PureComponent<AltStringSetterProps> { - componentDidMount() { - const { onChange, value, defaultValue } = this.props; - if (value == undefined && defaultValue) { - onChange(defaultValue); - } - } - - // 声明 Setter 的 title - static displayName = 'AltStringSetter'; - - render() { - const { onChange, value, placeholder } = this.props; - return ( - <Input - value={value} - placeholder={placeholder || ""} - onChange={(val: any) => onChange(val)} - ></Input> - ); - } -} -``` -开发完毕之后,注册 AltStringSetter 到设计器中: -```typescript -import AltStringSetter from './AltStringSetter'; -const registerSetter = window.AliLowCodeEngine.setters.registerSetter; +import { setters } from '@alilc/lowcode-engine'; +const { registerSetter } = registerSetter; registerSetter('AltStringSetter', AltStringSetter); ``` 注册之后,我们就可以在物料中使用了,其中核心配置如下: diff --git a/docs/docs/api/simulatorHost.md b/docs/docs/api/simulatorHost.md index fc0009d3a..b30a61499 100644 --- a/docs/docs/api/simulatorHost.md +++ b/docs/docs/api/simulatorHost.md @@ -2,11 +2,25 @@ title: simulatorHost - 模拟器 API sidebar_position: 3 --- -# 模块简介 +> **@types** [IPublicApiSimulatorHost](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/simulator-host.ts)<br/> +> **@since** v1.0.0 + +## 模块简介 负责模拟器相关的 API,包括画布尺寸、语言等。 -# 方法(functions) -## set +## 方法 +### set +设置 host 配置值 +```typescript +/** + * 设置若干用于画布渲染的变量,比如画布大小、locale 等。 + * set config for simulator host, eg. device locale and so on. + * @param key + * @param value + */ +set(key: string, value: any): void; +``` +#### 示例 设置若干用于画布渲染的变量,比如画布大小、locale 等。 以设置画布大小为例: @@ -21,11 +35,41 @@ project.simulatorHost.set('deviceClassName', 'my-canvas-class'); project.simulatorHost.set('deviceStyle', { canvas: { width: '300px', backgroundColor: 'red' }, viewport: { width: '280px' } }); ``` -## get +### get 获取模拟器中设置的变量,比如画布大小、locale 等。 + ```typescript -project.simulatorHost.get('device'); +/** + * 获取模拟器中设置的变量,比如画布大小、locale 等。 + * set config value by key + * @param key + * @returns + */ +get(key: string): any; + ``` -## rerender +### rerender 刷新渲染画布 + +```typescript +/** + * 刷新渲染画布 + * make simulator render again + */ +rerender(): void; +``` + +### scrollToNode +滚动到指定节点 + +```typescript +/** + * 滚动到指定节点 + * scroll to specific node + * @param node + * @since v1.1.0 + */ +scrollToNode(node: IPublicModelNode): void; +``` +**@since v1.1.0** diff --git a/docs/docs/api/skeleton.md b/docs/docs/api/skeleton.md index 1ae573e7e..5ad82accf 100644 --- a/docs/docs/api/skeleton.md +++ b/docs/docs/api/skeleton.md @@ -2,6 +2,10 @@ title: skeleton - 面板 API sidebar_position: 1 --- +> **@types** [IPublicApiSkeleton](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/skeleton.ts)<br/> +> **@since** v1.0.0 + + ## 模块简介 面板 API 提供了面板扩展和管理的能力,如下图蓝色内容都是扩展出来的。 @@ -131,21 +135,24 @@ skeleton.add({ }); ``` -## 变量(variables) +## 方法签名 -无 -## 方法签名(functions) - -### 1. add - -```tsx -add(config: IWidgetBaseConfig & { - area?: string; -}, extraConfig?: object): IWidget | Panel; -``` +### add 往指定扩展区加入一块面板 +```typescript +/** + * 增加一个面板实例 + * add a new panel + * @param config + * @param extraConfig + * @returns + */ +add(config: IPublicTypeWidgetBaseConfig, extraConfig?: Record<string, any>): any; +``` + + IWidgetBaseConfig 定义如下: | 属性名 | 含义 | 备注 | @@ -160,74 +167,181 @@ IWidgetBaseConfig 定义如下: | index | 面板的位置,不传默认按插件注册顺序 | | -### 2. remove - -remove(config: IWidgetBaseConfig) +### remove 移除一个面板实例 -### 3. showPanel +```typescript +/** + * 移除一个面板实例 + * remove a panel + * @param config + * @returns + */ +remove(config: IPublicTypeWidgetBaseConfig): number | undefined; +``` -showPanel(name: string) + +### showPanel 展示指定 Panel 实例 -### 4. hidePanel +```typescript +/** + * 展示指定 Panel 实例 + * show panel by name + * @param name + */ +showPanel(name: string): void; +``` -hidePanel(name: string) +### hidePanel +隐藏面板 -### 5. showWidget +```typescript +/** + * 隐藏面板 + * hide panel by name + * @param name + */ +hidePanel(name: string): void; +``` -showWidget(name: string) +### showWidget 展示指定 Widget 实例 -### 6. hideWidget +```typescript +/** + * 展示指定 Widget 实例 + * show widget by name + * @param name + */ +showWidget(name: string): void; +``` -hideWidget(name: string) +### enableWidget +将 widget 启用。 +```typescript +/** + * 将 widget 启用 + * enable widget + * @param name + */ +enableWidget(name: string): void; +``` + +### hideWidget 隐藏指定 widget 实例。 -### 7. enableWidget +```typescript +/** + * 隐藏指定 widget 实例 + * hide widget by name + * @param name + */ +hideWidget(name: string): void; +``` -enableWidget(name: string) - -将 widget 启用。 - -注:该函数将会触发全局事件 'skeleton.widget.enable' - -### 8. disableWidget - -disableWidget(name: string) +### disableWidget 将 widget 禁用掉,禁用后,所有鼠标事件都会被禁止掉。 适用场景:在该面板还在进行初始化构造时,可以先禁止掉,防止用户点击报错,待初始化完成,重新启用。 -## 事件(events) -### 1. onShowPanel +```typescript +/** + * 将 widget 禁用掉,禁用后,所有鼠标事件都会被禁止掉。 + * disable widget,and make it not responding any click event. + * @param name + */ +disableWidget(name: string): void; +``` -onShowPanel(listener: (...args: unknown[]) => void) +### showArea +显示某个 Area + +```typescript +/** + * 显示某个 Area + * show area + * @param areaName name of area + */ +showArea(areaName: string): void; +``` + + +### hideArea +隐藏某个 Area + +```typescript +/** + * 隐藏某个 Area + * hide area + * @param areaName name of area + */ +hideArea(areaName: string): void; +``` +## 事件 +### onShowPanel 监听 Panel 实例显示事件 -### 2. onHidePanel +```typescript +/** + * 监听 panel 显示事件 + * set callback for panel shown event + * @param listener + * @returns + */ +onShowPanel(listener: (...args: any[]) => void): () => void; +``` -onHidePanel(listener: (...args: unknown[]) => void) +### onHidePanel 监听 Panel 实例隐藏事件 -### 3. onShowWidget +```typescript +/** + * 监听 Panel 实例隐藏事件 + * set callback for panel hidden event + * @param listener + * @returns + */ +onHidePanel(listener: (...args: any[]) => void): () => void; +``` -onShowWidget(listener: (...args: unknown[]) => void) + +### onShowWidget 监听 Widget 实例显示事件 -### 4. onHideWidget +```typescript +/** + * 监听 Widget 显示事件 + * set callback for widget shown event + * @param listener + * @returns + */ +onShowWidget(listener: (...args: any[]) => void): () => void; +``` -onHideWidget(listener: (...args: unknown[]) => void) + +### onHideWidget 监听 Widget 实例隐藏事件 + +```typescript +/** + * 监听 Widget 隐藏事件 + * set callback for widget hidden event + * @param listener + * @returns + */ +onHideWidget(listener: (...args: any[]) => void): () => void; +``` + ## 使用示例 ```typescript diff --git a/docs/scripts/getDocsFromDir.js b/docs/scripts/getDocsFromDir.js index 1d3236fe6..666d92c3c 100644 --- a/docs/scripts/getDocsFromDir.js +++ b/docs/scripts/getDocsFromDir.js @@ -9,7 +9,8 @@ module.exports = function getDocsFromDir(dir, cateList) { const docsDir = path.join(baseDir, dir); function getMarkdownOrder(filepath) { - return (matter(fs.readFileSync(filepath, 'utf-8')).data || {}).order || 100; + const data = matter(fs.readFileSync(filepath, 'utf-8')).data; + return (data || {}).sidebar_position || 100; } const docs = glob.sync('*.md?(x)', { @@ -26,7 +27,7 @@ module.exports = function getDocsFromDir(dir, cateList) { const orderA = getMarkdownOrder(a); const orderB = getMarkdownOrder(b); - return orderA - orderB; + return orderB - orderA; }) .map(filepath => { // /Users/xxx/site/docs/guide/basic/router.md => guide/basic/router diff --git a/docs/scripts/sync-oss.js b/docs/scripts/sync-oss.js index 407f113bd..8ab9402e8 100644 --- a/docs/scripts/sync-oss.js +++ b/docs/scripts/sync-oss.js @@ -5,7 +5,7 @@ const { version, name } = package; const options = { method: 'PUT', // 暂时使用 日常环境的 uipaas-node,上线后可切换成线上环境 https://uipaas-node.alibaba-inc.com - hostname: 'uipaas-node.alibaba.net', + hostname: 'uipaas-node.alibaba-inc.com', path: '/staticAssets/cdn/packages', headers: { 'Content-Type': 'application/json', diff --git a/packages/designer/src/plugin/plugin-manager.ts b/packages/designer/src/plugin/plugin-manager.ts index 456124397..449afebdf 100644 --- a/packages/designer/src/plugin/plugin-manager.ts +++ b/packages/designer/src/plugin/plugin-manager.ts @@ -15,7 +15,7 @@ import { invariant } from '../utils'; import sequencify from './sequencify'; import semverSatisfies from 'semver/functions/satisfies'; import { - ILowCodeRegisterOptions, + IPublicTypePluginRegisterOptions, IPublicTypePreferenceValueType, IPublicTypePlugin, } from '@alilc/lowcode-types'; @@ -65,7 +65,7 @@ export class LowCodePluginManager implements ILowCodePluginManager { async register( pluginModel: IPublicTypePlugin, options?: any, - registerOptions?: ILowCodeRegisterOptions, + registerOptions?: IPublicTypePluginRegisterOptions, ): Promise<void> { // registerOptions maybe in the second place if (isLowCodeRegisterOptions(options)) { diff --git a/packages/designer/src/plugin/plugin-utils.ts b/packages/designer/src/plugin/plugin-utils.ts index 21e142ab7..7d8ab8db9 100644 --- a/packages/designer/src/plugin/plugin-utils.ts +++ b/packages/designer/src/plugin/plugin-utils.ts @@ -1,5 +1,5 @@ import { isPlainObject } from 'lodash'; -import { ILowCodeRegisterOptions, IPublicTypePluginDeclaration } from '@alilc/lowcode-types'; +import { IPublicTypePluginRegisterOptions, IPublicTypePluginDeclaration } from '@alilc/lowcode-types'; export function isValidPreferenceKey( key: string, @@ -13,7 +13,7 @@ export function isValidPreferenceKey( }); } -export function isLowCodeRegisterOptions(opts: any): opts is ILowCodeRegisterOptions { +export function isLowCodeRegisterOptions(opts: any): opts is IPublicTypePluginRegisterOptions { return opts && ('autoInit' in opts || 'override' in opts); } diff --git a/packages/designer/src/project/project.ts b/packages/designer/src/project/project.ts index a056c37bf..08b96d7c4 100644 --- a/packages/designer/src/project/project.ts +++ b/packages/designer/src/project/project.ts @@ -5,7 +5,6 @@ import { IPublicTypeProjectSchema, IPublicTypeRootSchema, IPublicTypeComponentsMap, - TransformStage, IPublicApiProject, IPublicModelDocumentModel, IPublicEnumTransformStage, diff --git a/packages/editor-core/src/config.ts b/packages/editor-core/src/config.ts index d8d5ff3c9..344529690 100644 --- a/packages/editor-core/src/config.ts +++ b/packages/editor-core/src/config.ts @@ -3,7 +3,7 @@ import { isPlainObject } from '@alilc/lowcode-utils'; import { EngineOptions, IPublicModelEngineConfig, - IPreference, + IPublicModelPreference, } from '@alilc/lowcode-types'; import { getLogger } from './utils/logger'; import Preference from './utils/preference'; @@ -193,7 +193,7 @@ export class EngineConfig implements IPublicModelEngineConfig, IEngineConfigPriv * used to store preferences * */ - readonly preference: IPreference; + readonly preference: IPublicModelPreference; constructor(config?: { [key: string]: any }) { this.config = config || {}; @@ -349,7 +349,7 @@ export class EngineConfig implements IPublicModelEngineConfig, IEngineConfigPriv } } - getPreference(): IPreference { + getPreference(): IPublicModelPreference { return this.preference; } } diff --git a/packages/editor-core/src/utils/preference.ts b/packages/editor-core/src/utils/preference.ts index b5693a514..0ee852bd7 100644 --- a/packages/editor-core/src/utils/preference.ts +++ b/packages/editor-core/src/utils/preference.ts @@ -1,6 +1,6 @@ import store from 'store'; import { getLogger } from './logger'; -import { IPreference } from '@alilc/lowcode-types'; +import { IPublicModelPreference } from '@alilc/lowcode-types'; const logger = getLogger({ level: 'log', bizName: 'Preference' }); const STORAGE_KEY_PREFIX = 'ale'; @@ -12,7 +12,7 @@ const STORAGE_KEY_PREFIX = 'ale'; * * @class PreferenceStore */ -export default class Preference implements IPreference { +export default class Preference implements IPublicModelPreference { getStorageKey(key: string, module?: string): string { const moduleKey = module || '__inner__'; return `${STORAGE_KEY_PREFIX}_${moduleKey}.${key}`; diff --git a/packages/shell/src/api/common.tsx b/packages/shell/src/api/common.tsx index d1bd7afa0..c77b80610 100644 --- a/packages/shell/src/api/common.tsx +++ b/packages/shell/src/api/common.tsx @@ -17,13 +17,13 @@ import { IPublicTypeNodeSchema, IPublicEnumTransitionType, IPublicEnumTransformStage as InnerTransitionStage, - IPublicCommonDesignerCabin, - IPublicCommonSkeletonCabin, - IPublicCommonUtils, + IPublicApiCommonDesignerCabin, + IPublicApiCommonSkeletonCabin, + IPublicApiCommonUtils, IPublicApiCommon, IPublicEnumDragObjectType as InnerDragObjectType, IPublicTypeLocationDetailType as InnerLocationDetailType, - IPublicCommonEditorCabin, + IPublicApiCommonEditorCabin, IPublicModelDragon, IDesigner, } from '@alilc/lowcode-types'; @@ -61,7 +61,7 @@ import { import { Dragon } from '../model'; import { ReactNode, Component } from 'react'; -class DesignerCabin implements IPublicCommonDesignerCabin { +class DesignerCabin implements IPublicApiCommonDesignerCabin { private readonly [editorSymbol]: Editor; /** * @deprecated @@ -150,10 +150,9 @@ class DesignerCabin implements IPublicCommonDesignerCabin { get dragon(): IPublicModelDragon | null { return Dragon.create(this[designerSymbol].dragon); } - } -class SkeletonCabin implements IPublicCommonSkeletonCabin { +class SkeletonCabin implements IPublicApiCommonSkeletonCabin { private readonly [skeletonSymbol]: InnerSkeleton; constructor(skeleton: InnerSkeleton) { @@ -187,7 +186,7 @@ class SkeletonCabin implements IPublicCommonSkeletonCabin { } } -class Utils implements IPublicCommonUtils { +class Utils implements IPublicApiCommonUtils { isNodeSchema(data: any): data is IPublicTypeNodeSchema { return innerIsNodeSchema(data); } @@ -196,11 +195,17 @@ class Utils implements IPublicCommonUtils { return innerIsFormEvent(e); } + /** + * @deprecated this is a legacy api, do not use this if not using is already + */ compatibleLegaoSchema(props: any): any { return innerCompatibleLegaoSchema(props); } - getNodeSchemaById(schema: IPublicTypeNodeSchema, nodeId: string): IPublicTypeNodeSchema | undefined { + getNodeSchemaById( + schema: IPublicTypeNodeSchema, + nodeId: string, + ): IPublicTypeNodeSchema | undefined { return innerGetNodeSchemaById(schema, nodeId); } @@ -212,7 +217,10 @@ class Utils implements IPublicCommonUtils { return innerGetOriginalExtraKey(key); } - executeTransaction(fn: () => void, type: IPublicEnumTransitionType = IPublicEnumTransitionType.REPAINT): void { + executeTransaction( + fn: () => void, + type: IPublicEnumTransitionType = IPublicEnumTransitionType.REPAINT, + ): void { transactionManager.executeTransaction(fn, type); } @@ -226,7 +234,7 @@ class Utils implements IPublicCommonUtils { } } -class EditorCabin implements IPublicCommonEditorCabin { +class EditorCabin implements IPublicApiCommonEditorCabin { private readonly [editorSymbol]: Editor; constructor(editor: Editor) { @@ -234,6 +242,7 @@ class EditorCabin implements IPublicCommonEditorCabin { } /** * Title 组件 + * @experimental unstable API, pay extra caution when trying to use this */ get Title(): Component { return InnerTitle; @@ -241,6 +250,7 @@ class EditorCabin implements IPublicCommonEditorCabin { /** * Tip 组件 + * @experimental unstable API, pay extra caution when trying to use this */ get Tip(): Component { return InnerTip; diff --git a/packages/shell/src/api/material.ts b/packages/shell/src/api/material.ts index 584cfa1e5..e824290a5 100644 --- a/packages/shell/src/api/material.ts +++ b/packages/shell/src/api/material.ts @@ -15,10 +15,12 @@ import { IPublicApiMaterial, IPublicTypeMetadataTransducer, IPublicModelComponentMeta, + IPublicTypeNpmInfo, } from '@alilc/lowcode-types'; import { Workspace } from '@alilc/lowcode-workspace'; import { editorSymbol, designerSymbol } from '../symbols'; import { ComponentMeta } from '../model/component-meta'; +import { ComponentType } from 'react'; const innerEditorSymbol = Symbol('editor'); export class Material implements IPublicApiMaterial { @@ -47,7 +49,7 @@ export class Material implements IPublicApiMaterial { /** * 获取组件 map 结构 */ - get componentsMap() { + get componentsMap(): { [key: string]: IPublicTypeNpmInfo | ComponentType<any> | object } { return this[designerSymbol].componentsMap; } @@ -173,6 +175,6 @@ export class Material implements IPublicApiMaterial { // 设置 assets,经过 setAssets 赋值 this[editorSymbol].onGot('assets', fn); // 增量设置 assets,经过 loadIncrementalAssets 赋值 - this[editorSymbol].on('designer.incrementalAssetsReady', fn); + this[editorSymbol].eventBus.on('designer.incrementalAssetsReady', fn); } } diff --git a/packages/shell/src/api/plugins.ts b/packages/shell/src/api/plugins.ts index 60a66fb45..8a0a506e5 100644 --- a/packages/shell/src/api/plugins.ts +++ b/packages/shell/src/api/plugins.ts @@ -5,7 +5,7 @@ import { globalContext } from '@alilc/lowcode-editor-core'; import { IPublicApiPlugins, IPublicTypePlugin, - ILowCodeRegisterOptions, + IPublicTypePluginRegisterOptions, IPublicTypePreferenceValueType, } from '@alilc/lowcode-types'; import { pluginsSymbol } from '../symbols'; @@ -32,7 +32,7 @@ export class Plugins implements IPublicApiPlugins { async register( pluginModel: IPublicTypePlugin, options?: any, - registerOptions?: ILowCodeRegisterOptions, + registerOptions?: IPublicTypePluginRegisterOptions, ): Promise<void> { await this[pluginsSymbol].register(pluginModel, options, registerOptions); } diff --git a/packages/shell/src/api/project.ts b/packages/shell/src/api/project.ts index f083bc7e8..31c3277d4 100644 --- a/packages/shell/src/api/project.ts +++ b/packages/shell/src/api/project.ts @@ -13,6 +13,7 @@ import { IPublicTypePropsTransducer, IPublicEnumEventNames, IPublicEnumTransformStage, + IPublicTypeDisposable, } from '@alilc/lowcode-types'; @@ -163,7 +164,7 @@ export class Project implements IPublicApiProject { * @param fn * @returns */ - onRemoveDocument(fn: (data: { id: string}) => void): any { + onRemoveDocument(fn: (data: { id: string}) => void): IPublicTypeDisposable { return this[editorSymbol].eventBus.on( IPublicEnumEventNames.DESIGNER_DOCUMENT_REMOVE, (data: { id: string }) => fn(data), @@ -173,7 +174,7 @@ export class Project implements IPublicApiProject { /** * 当前 project 内的 document 变更事件 */ - onChangeDocument(fn: (doc: IPublicModelDocumentModel) => void) { + onChangeDocument(fn: (doc: IPublicModelDocumentModel) => void): IPublicTypeDisposable { const offFn = this[projectSymbol].onCurrentDocumentChange((originalDoc) => { fn(DocumentModel.create(originalDoc)!); }); @@ -186,7 +187,7 @@ export class Project implements IPublicApiProject { /** * 当前 project 的模拟器 ready 事件 */ - onSimulatorHostReady(fn: (host: IPublicApiSimulatorHost) => void) { + onSimulatorHostReady(fn: (host: IPublicApiSimulatorHost) => void): IPublicTypeDisposable { const offFn = this[projectSymbol].onSimulatorReady((simulator: BuiltinSimulatorHost) => { this[simulatorHostSymbol] = simulator; fn(SimulatorHost.create(simulator)!); @@ -200,7 +201,7 @@ export class Project implements IPublicApiProject { /** * 当前 project 的渲染器 ready 事件 */ - onSimulatorRendererReady(fn: () => void) { + onSimulatorRendererReady(fn: () => void): IPublicTypeDisposable { const offFn = this[projectSymbol].onRendererReady((renderer: any) => { this[simulatorRendererSymbol] = renderer; fn(); diff --git a/packages/types/src/engine-config.ts b/packages/types/src/engine-config.ts index eed9e021d..4c97433fb 100644 --- a/packages/types/src/engine-config.ts +++ b/packages/types/src/engine-config.ts @@ -138,20 +138,3 @@ export interface EngineOptions { */ enableWorkspaceMode?: boolean; } - -export interface IPreference { - /** - * set value from local storage by module and key - */ - set(key: string, value: any, module?: string): void; - - /** - * get value from local storage by module and key - */ - get(key: string, module: string): any; - - /** - * check if local storage contain certain key - */ - contains(key: string, module: string): boolean; -} diff --git a/packages/types/src/shell/api/canvas.ts b/packages/types/src/shell/api/canvas.ts index 0d1e3e88d..147d554ec 100644 --- a/packages/types/src/shell/api/canvas.ts +++ b/packages/types/src/shell/api/canvas.ts @@ -5,34 +5,44 @@ export interface IPublicApiCanvas { /** + * 创一个滚动控制器 Scroller,赋予一个视图滚动的基本能力, * a Scroller is a controller that gives a view (IPublicModelScrollable) the ability scrolling * to some cordination by api scrollTo. * - * when initing aaa scroller, will need to pass is a scrollable, which has a scrollTarget. + * when a scroller is inited, will need to pass is a scrollable, which has a scrollTarget. * and when scrollTo(options: { left?: number; top?: number }) is called, scroller will * move scrollTarget`s top-left corner to (options.left, options.top) that passed in. + * @since v1.1.0 */ createScroller(scrollable: IPublicModelScrollable): IPublicModelScroller; /** + * 创建一个 ScrollTarget,与 Scroller 一起发挥作用,详见 createScroller 中的描述 * this works with Scroller, refer to createScroller`s description + * @since v1.1.0 */ createScrollTarget(shell: HTMLDivElement): IPublicModelScrollTarget; /** + * 创建一个文档插入位置对象,该对象用来描述一个即将插入的节点在文档中的位置 * create a drop location for document, drop location describes a location in document + * @since v1.1.0 */ createLocation(locationData: IPublicTypeLocationData): IPublicModelDropLocation; /** + * 获取拖拽操作对象的实例 * get dragon instance, you can use this to obtain draging related abilities and lifecycle hooks + * @since v1.1.0 */ get dragon(): IPublicModelDragon | null; /** + * 获取活动追踪器实例 * get activeTracker instance, which is a singleton running in engine. * it tracks document`s current focusing node/node[], and notify it`s subscribers that when * focusing node/node[] changed. + * @since v1.1.0 */ get activeTracker(): IPublicModelActiveTracker | null; } diff --git a/packages/types/src/shell/api/common.ts b/packages/types/src/shell/api/common.ts index 8c1a7c4a0..ceb85067f 100644 --- a/packages/types/src/shell/api/common.ts +++ b/packages/types/src/shell/api/common.ts @@ -3,24 +3,65 @@ import { Component, ReactNode } from 'react'; import { IPublicTypeNodeSchema } from '../type'; import { IPublicEnumTransitionType } from '../enum'; -export interface IPublicCommonUtils { +export interface IPublicApiCommonUtils { + /** + * 是否为合法的 schema 结构 + * check if data is valid NodeSchema + * + * @param {*} data + * @returns {boolean} + */ isNodeSchema(data: any): boolean; + /** + * 是否为表单事件类型 + * check if e is a form event + * @param {(KeyboardEvent | MouseEvent)} e + * @returns {boolean} + */ isFormEvent(e: KeyboardEvent | MouseEvent): boolean; - compatibleLegaoSchema(props: any): any; - + /** + * 从 schema 结构中查找指定 id 节点 + * get node schema from a larger schema with node id + * @param {IPublicTypeNodeSchema} schema + * @param {string} nodeId + * @returns {(IPublicTypeNodeSchema | undefined)} + */ getNodeSchemaById( schema: IPublicTypeNodeSchema, nodeId: string, ): IPublicTypeNodeSchema | undefined; + // TODO: add comments getConvertedExtraKey(key: string): string; + // TODO: add comments getOriginalExtraKey(key: string): string; + /** + * 批处理事务,用于优化特定场景的性能 + * excute something in a transaction for performence + * + * @param {() => void} fn + * @param {IPublicEnumTransitionType} type + * @since v1.0.16 + */ executeTransaction(fn: () => void, type: IPublicEnumTransitionType): void; + /** + * i18n 相关工具 + * i18n tools + * + * @param {(string | object)} instance + * @returns {{ + * intlNode(id: string, params?: object): ReactNode; + * intl(id: string, params?: object): string; + * getLocale(): string; + * setLocale(locale: string): void; + * }} + * @since v1.0.17 + */ createIntl(instance: string | object): { intlNode(id: string, params?: object): ReactNode; intl(id: string, params?: object): string; @@ -28,33 +69,43 @@ export interface IPublicCommonUtils { setLocale(locale: string): void; }; } -export interface IPublicCommonSkeletonCabin { +export interface IPublicApiCommonSkeletonCabin { + /** + * 编辑器框架 View + * get Workbench Component + */ get Workbench(): Component; } -export interface IPublicCommonEditorCabin { +export interface IPublicApiCommonEditorCabin { + /** + * Title 组件 + * @experimental unstable API, pay extra caution when trying to use this + */ get Tip(): Component; + /** + * Tip 组件 + * @experimental unstable API, pay extra caution when trying to use this + */ get Title(): Component; } -export interface IPublicCommonDesignerCabin { - /** - * 是否是 SettingField 实例 - * - * @param {*} obj - * @returns {obj is SettingField} - * @memberof DesignerCabin - */ - isSettingField(obj: any): boolean; +export interface IPublicApiCommonDesignerCabin { } export interface IPublicApiCommon { - get utils(): IPublicCommonUtils; + get utils(): IPublicApiCommonUtils; - get designerCabin(): IPublicCommonDesignerCabin; + /** + * @deprecated + */ + get designerCabin(): IPublicApiCommonDesignerCabin; - get editorCabin(): IPublicCommonEditorCabin; + /** + * @experimental unstable API, pay extra caution when trying to use this + */ + get editorCabin(): IPublicApiCommonEditorCabin; - get skeletonCabin(): IPublicCommonSkeletonCabin; + get skeletonCabin(): IPublicApiCommonSkeletonCabin; } diff --git a/packages/types/src/shell/api/event.ts b/packages/types/src/shell/api/event.ts index c54bac649..18e76febe 100644 --- a/packages/types/src/shell/api/event.ts +++ b/packages/types/src/shell/api/event.ts @@ -4,6 +4,7 @@ export interface IPublicApiEvent { /** * 监听事件 + * add monitor to a event * @param event 事件名称 * @param listener 事件回调 */ @@ -12,6 +13,7 @@ export interface IPublicApiEvent { /** * 取消监听事件 + * cancel a monitor from a event * @param event 事件名称 * @param listener 事件回调 */ @@ -19,6 +21,7 @@ export interface IPublicApiEvent { /** * 触发事件 + * emit a message fot a event * @param event 事件名称 * @param args 事件参数 * @returns diff --git a/packages/types/src/shell/api/hotkey.ts b/packages/types/src/shell/api/hotkey.ts index 335379e97..c8da780af 100644 --- a/packages/types/src/shell/api/hotkey.ts +++ b/packages/types/src/shell/api/hotkey.ts @@ -5,10 +5,15 @@ export interface IPublicApiHotkey { /** * 绑定快捷键 + * bind hotkey/hotkeys, * @param combos 快捷键,格式如:['command + s'] 、['ctrl + shift + s'] 等 * @param callback 回调函数 * @param action * @returns */ - bind(combos: string[] | string, callback: IPublicTypeHotkeyCallback, action?: string): IPublicTypeDisposable; + bind( + combos: string[] | string, + callback: IPublicTypeHotkeyCallback, + action?: string, + ): IPublicTypeDisposable; } diff --git a/packages/types/src/shell/api/material.ts b/packages/types/src/shell/api/material.ts index 34396ddfd..deec23d52 100644 --- a/packages/types/src/shell/api/material.ts +++ b/packages/types/src/shell/api/material.ts @@ -1,31 +1,41 @@ -import { IPublicTypeAssetsJson, IPublicTypeMetadataTransducer, IPublicTypeComponentAction } from '../type'; +import { IPublicTypeAssetsJson, IPublicTypeMetadataTransducer, IPublicTypeComponentAction, IPublicTypeNpmInfo } from '../type'; import { IPublicModelComponentMeta } from '../model'; - +import { ComponentType } from 'react'; export interface IPublicApiMaterial { + /** + * 获取组件 map 结构 + * get map of components + */ + get componentsMap(): { [key: string]: IPublicTypeNpmInfo | ComponentType<any> | object } ; + /** * 设置「资产包」结构 - * @param assets - * @returns + * set data for Assets + * @returns void */ setAssets(assets: IPublicTypeAssetsJson): void; /** * 获取「资产包」结构 - * @returns + * get AssetsJson data + * @returns IPublicTypeAssetsJson */ - getAssets(): any; + getAssets(): IPublicTypeAssetsJson; /** * 加载增量的「资产包」结构,该增量包会与原有的合并 + * load Assets incrementally, and will merge this with exiting assets * @param incrementalAssets * @returns */ loadIncrementalAssets(incrementalAssets: IPublicTypeAssetsJson): void; /** - * 注册物料元数据管道函数 + * 注册物料元数据管道函数,在物料信息初始化时执行。 + * register transducer to process component meta, which will be + * excuted during component meta`s initialization * @param transducer * @param level * @param id @@ -38,12 +48,14 @@ export interface IPublicApiMaterial { /** * 获取所有物料元数据管道函数 - * @returns + * get all registered metadata transducers + * @returns {IPublicTypeMetadataTransducer[]} */ getRegisteredMetadataTransducers(): IPublicTypeMetadataTransducer[]; /** * 获取指定名称的物料元数据 + * get component meta by component name * @param componentName * @returns */ @@ -52,30 +64,33 @@ export interface IPublicApiMaterial { /** * test if the given object is a ComponentMeta instance or not * @param obj - * @returns + * @experiemental unstable API, pay extra caution when trying to use it */ isComponentMeta(obj: any): boolean; /** * 获取所有已注册的物料元数据 - * @returns + * get map of all component metas */ getComponentMetasMap(): Map<string, IPublicModelComponentMeta>; /** * 在设计器辅助层增加一个扩展 action + * add an action button in canvas context menu area * @param action */ addBuiltinComponentAction(action: IPublicTypeComponentAction): void; /** * 移除设计器辅助层的指定 action + * remove a builtin action button from canvas context menu area * @param name */ removeBuiltinComponentAction(name: string): void; /** * 修改已有的设计器辅助层的指定 action + * modify a builtin action button in canvas context menu area * @param actionName * @param handle */ @@ -86,6 +101,7 @@ export interface IPublicApiMaterial { /** * 监听 assets 变化的事件 + * add callback for assets changed event * @param fn */ onChangeAssets(fn: () => void): void; diff --git a/packages/types/src/shell/api/plugins.ts b/packages/types/src/shell/api/plugins.ts index b024a3699..2797ba071 100644 --- a/packages/types/src/shell/api/plugins.ts +++ b/packages/types/src/shell/api/plugins.ts @@ -1,5 +1,6 @@ import { IPublicTypePlugin } from '../model'; import { IPublicTypePreferenceValueType } from '../type'; +import { IPublicTypePluginRegisterOptions } from '../type/plugin-register-options'; export interface IPluginPreferenceMananger { // eslint-disable-next-line max-len @@ -9,28 +10,18 @@ export interface IPluginPreferenceMananger { ) => IPublicTypePreferenceValueType | undefined; } -export interface ILowCodeRegisterOptions { - /** - * Will enable plugin registered with auto-initialization immediately - * other than plugin-manager init all plugins at certain time. - * It is helpful when plugin register is later than plugin-manager initialization. - */ - autoInit?: boolean; - /** - * allow overriding existing plugin with same name when override === true - */ - override?: boolean; -} - export type PluginOptionsType = string | number | boolean | object; export interface IPublicApiPlugins { register( pluginModel: IPublicTypePlugin, options?: Record<string, PluginOptionsType>, - registerOptions?: ILowCodeRegisterOptions, + registerOptions?: IPublicTypePluginRegisterOptions, ): Promise<void>; + /** + * @deprecated use options instead + */ getPluginPreference( pluginName: string, ): Record<string, IPublicTypePreferenceValueType> | null | undefined; diff --git a/packages/types/src/shell/api/project.ts b/packages/types/src/shell/api/project.ts index ad71f1fb8..823159e7b 100644 --- a/packages/types/src/shell/api/project.ts +++ b/packages/types/src/shell/api/project.ts @@ -7,30 +7,34 @@ import { IPublicModelDocumentModel } from '../model'; export interface IPublicApiProject { /** * 获取当前的 document - * @returns + * get current document */ get currentDocument(): IPublicModelDocumentModel | null; /** - * 获取当前 project 下所有 documents - * @returns - */ + * 获取当前 project 下所有 documents + * get all documents of this project + * @returns + */ get documents(): IPublicModelDocumentModel[]; /** * 获取模拟器的 host + * get simulator host */ get simulatorHost(): IPublicApiSimulatorHost | null; /** - * 打开一个 document - * @param doc - * @returns - */ + * 打开一个 document + * open a document + * @param doc + * @returns + */ openDocument(doc?: string | IPublicTypeRootSchema | undefined): IPublicModelDocumentModel | null; /** * 创建一个 document + * create a document * @param data * @returns */ @@ -39,12 +43,14 @@ export interface IPublicApiProject { /** * 删除一个 document + * remove a document * @param doc */ removeDocument(doc: IPublicModelDocumentModel): void; /** * 根据 fileName 获取 document + * get a document by filename * @param fileName * @returns */ @@ -52,6 +58,7 @@ export interface IPublicApiProject { /** * 根据 id 获取 document + * get a document by id * @param id * @returns */ @@ -59,24 +66,28 @@ export interface IPublicApiProject { /** * 导出 project + * export project to schema * @returns */ exportSchema(stage: IPublicEnumTransformStage): IPublicTypeProjectSchema; /** - * 导入 project - * @param schema 待导入的 project 数据 - */ + * 导入 project schema + * import schema to project + * @param schema 待导入的 project 数据 + */ importSchema(schema?: IPublicTypeProjectSchema): void; /** * 获取当前的 document + * get current document * @returns */ getCurrentDocument(): IPublicModelDocumentModel | null; /** * 增加一个属性的管道处理函数 + * add a transducer to process prop * @param transducer * @param stage */ @@ -87,31 +98,37 @@ export interface IPublicApiProject { /** * 绑定删除文档事件 + * set callback for event onDocumentRemoved * @param fn - * @returns + * @since v1.0.16 */ - onRemoveDocument(fn: (data: { id: string }) => void): any; + onRemoveDocument(fn: (data: { id: string }) => void): IPublicTypeDisposable; /** * 当前 project 内的 document 变更事件 + * set callback for event onDocumentChanged */ onChangeDocument(fn: (doc: IPublicModelDocumentModel) => void): IPublicTypeDisposable; /** * 当前 project 的模拟器 ready 事件 + * set callback for event onSimulatorHostReady */ onSimulatorHostReady(fn: (host: IPublicApiSimulatorHost) => void): IPublicTypeDisposable; /** * 当前 project 的渲染器 ready 事件 + * set callback for event onSimulatorRendererReady */ onSimulatorRendererReady(fn: () => void): IPublicTypeDisposable; /** * 设置多语言语料 * 数据格式参考 https://github.com/alibaba/lowcode-engine/blob/main/specs/lowcode-spec.md#2434%E5%9B%BD%E9%99%85%E5%8C%96%E5%A4%9A%E8%AF%AD%E8%A8%80%E7%B1%BB%E5%9E%8Baa + * + * set I18n data for this project * @param value object - * @returns + * @since v1.0.17 */ setI18n(value: object): void; } diff --git a/packages/types/src/shell/api/setters.ts b/packages/types/src/shell/api/setters.ts index 25b57fe48..9ff2e8eda 100644 --- a/packages/types/src/shell/api/setters.ts +++ b/packages/types/src/shell/api/setters.ts @@ -3,6 +3,7 @@ import { IPublicTypeRegisteredSetter, IPublicTypeCustomView } from '../type'; export interface IPublicApiSetters { /** * 获取指定 setter + * get setter by type * @param type * @returns */ @@ -10,6 +11,7 @@ export interface IPublicApiSetters { /** * 获取已注册的所有 settersMap + * get map of all registered setters * @returns */ getSettersMap(): Map<string, IPublicTypeRegisteredSetter & { @@ -18,6 +20,7 @@ export interface IPublicApiSetters { /** * 注册一个 setter + * register a setter * @param typeOrMaps * @param setter * @returns diff --git a/packages/types/src/shell/api/simulator-host.ts b/packages/types/src/shell/api/simulator-host.ts index 971e78a83..1c0f5dec3 100644 --- a/packages/types/src/shell/api/simulator-host.ts +++ b/packages/types/src/shell/api/simulator-host.ts @@ -4,38 +4,48 @@ import { IPublicModelNode } from '../model'; export interface IPublicApiSimulatorHost { /** * 获取 contentWindow + * @experimental unstable api, pay extra caution when trying to use it */ get contentWindow(): Window | undefined; /** * 获取 contentDocument + * @experimental unstable api, pay extra caution when trying to use it */ get contentDocument(): Document | undefined; + /** + * @experimental unstable api, pay extra caution when trying to use it + */ get renderer(): any; /** - * 设置 host 配置值 + * 设置若干用于画布渲染的变量,比如画布大小、locale 等。 + * set config for simulator host, eg. device locale and so on. * @param key * @param value */ set(key: string, value: any): void; /** - * 获取 host 配置值 + * 获取模拟器中设置的变量,比如画布大小、locale 等。 + * set config value by key * @param key * @returns */ get(key: string): any; /** + * 滚动到指定节点 * scroll to specific node * @param node + * @since v1.1.0 */ scrollToNode(node: IPublicModelNode): void; /** * 刷新渲染画布 + * make simulator render again */ rerender(): void; } diff --git a/packages/types/src/shell/api/skeleton.ts b/packages/types/src/shell/api/skeleton.ts index c3a68c494..bfd81532a 100644 --- a/packages/types/src/shell/api/skeleton.ts +++ b/packages/types/src/shell/api/skeleton.ts @@ -3,6 +3,7 @@ import { IPublicTypeWidgetBaseConfig } from '../type'; export interface IPublicApiSkeleton { /** * 增加一个面板实例 + * add a new panel * @param config * @param extraConfig * @returns @@ -10,83 +11,96 @@ export interface IPublicApiSkeleton { add(config: IPublicTypeWidgetBaseConfig, extraConfig?: Record<string, any>): any; /** - * 移除一个面板实例 - * @param config - * @returns - */ + * 移除一个面板实例 + * remove a panel + * @param config + * @returns + */ remove(config: IPublicTypeWidgetBaseConfig): number | undefined; /** - * 显示面板 + * 展示指定 Panel 实例 + * show panel by name * @param name */ showPanel(name: string): void; /** * 隐藏面板 + * hide panel by name * @param name */ hidePanel(name: string): void; /** - * 显示 widget + * 展示指定 Widget 实例 + * show widget by name * @param name */ showWidget(name: string): void; /** - * enable widget + * 将 widget 启用 + * enable widget by name * @param name */ enableWidget(name: string): void; /** - * 隐藏 widget + * 隐藏指定 widget 实例 + * hide widget by name * @param name */ hideWidget(name: string): void; /** - * disable widget,不可点击 + * 将 widget 禁用掉,禁用后,所有鼠标事件都会被禁止掉。 + * disable widget,and make it not responding any click event. * @param name */ disableWidget(name: string): void; /** + * 显示某个 Area * show area * @param areaName name of area */ showArea(areaName: string): void; /** + * 隐藏某个 Area * hide area * @param areaName name of area */ hideArea(areaName: string): void; /** - * 监听 panel 显示事件 + * 监听 Panel 实例显示事件 + * set callback for panel shown event * @param listener * @returns */ onShowPanel(listener: (...args: any[]) => void): () => void; /** - * 监听 panel 隐藏事件 + * 监听 Panel 实例隐藏事件 + * set callback for panel hidden event * @param listener * @returns */ onHidePanel(listener: (...args: any[]) => void): () => void; /** - * 监听 widget 显示事件 + * 监听 Widget 显示事件 + * set callback for widget shown event * @param listener * @returns */ onShowWidget(listener: (...args: any[]) => void): () => void; /** - * 监听 widget 隐藏事件 + * 监听 Widget 隐藏事件 + * set callback for widget hidden event * @param listener * @returns */ diff --git a/packages/types/src/shell/model/detecting.ts b/packages/types/src/shell/model/detecting.ts index 9dc868d92..8e82f7449 100644 --- a/packages/types/src/shell/model/detecting.ts +++ b/packages/types/src/shell/model/detecting.ts @@ -9,6 +9,7 @@ export interface IPublicModelDetecting { /** * 当前 hover 的节点 + * @since v1.0.16 */ get current(): any; @@ -29,5 +30,10 @@ export interface IPublicModelDetecting { */ leave(): any; + /** + * hover 节点变化事件 + * set callback which will be called when hovering object changed. + * @since v1.1.0 + */ onDetectingChange(fn: (node: IPublicModelNode) => void): () => void; } diff --git a/packages/types/src/shell/model/dragon.ts b/packages/types/src/shell/model/dragon.ts index b9923b70b..6a6daf034 100644 --- a/packages/types/src/shell/model/dragon.ts +++ b/packages/types/src/shell/model/dragon.ts @@ -4,12 +4,14 @@ import { IPublicModelDragObject, IPublicModelLocateEvent, IPublicModelNode } fro export interface IPublicModelDragon { /** + * 是否正在拖动 * is dragging or not */ get dragging(): boolean; /** * 绑定 dragstart 事件 + * bind a callback function which will be called on dragging start * @param func * @returns */ @@ -17,6 +19,7 @@ export interface IPublicModelDragon { /** * 绑定 drag 事件 + * bind a callback function which will be called on dragging * @param func * @returns */ @@ -24,6 +27,7 @@ export interface IPublicModelDragon { /** * 绑定 dragend 事件 + * bind a callback function which will be called on dragging end * @param func * @returns */ @@ -32,13 +36,17 @@ export interface IPublicModelDragon { /** * 设置拖拽监听的区域 shell,以及自定义拖拽转换函数 boost + * set a html element as shell to dragon as monitoring target, and + * set boost function which is used to transform a MouseEvent to type + * IPublicTypeDragNodeDataObject. * @param shell 拖拽监听的区域 * @param boost 拖拽转换函数 */ from(shell: Element, boost: (e: MouseEvent) => IPublicTypeDragNodeDataObject | null): any; /** - * boost your dragObject for dragging(flying) 发射拖拽对象 + * 发射拖拽对象 + * boost your dragObject for dragging(flying) * * @param dragObject 拖拽对象 * @param boostEvent 拖拽初始时事件 @@ -47,11 +55,13 @@ export interface IPublicModelDragon { /** * 添加投放感应区 + * add sensor area */ addSensor(sensor: any): void; /** * 移除投放感应 + * remove sensor area */ removeSensor(sensor: any): void; } diff --git a/packages/types/src/shell/model/engine-config.ts b/packages/types/src/shell/model/engine-config.ts index 2a52203cf..99d3a9071 100644 --- a/packages/types/src/shell/model/engine-config.ts +++ b/packages/types/src/shell/model/engine-config.ts @@ -1,9 +1,10 @@ -import { IPreference } from '../../engine-config'; +import { IPublicModelPreference } from './'; export interface IPublicModelEngineConfig { /** * 判断指定 key 是否有值 + * check if config has certain key configed * @param key * @returns */ @@ -11,6 +12,7 @@ export interface IPublicModelEngineConfig { /** * 获取指定 key 的值 + * get value by key * @param key * @param defaultValue * @returns @@ -19,6 +21,7 @@ export interface IPublicModelEngineConfig { /** * 设置指定 key 的值 + * set value for certain key * @param key * @param value */ @@ -26,6 +29,7 @@ export interface IPublicModelEngineConfig { /** * 批量设值,set 的对象版本 + * set multiple config key-values * @param config */ setConfig(config: { [key: string]: any }): void; @@ -33,6 +37,8 @@ export interface IPublicModelEngineConfig { /** * 获取指定 key 的值,若此时还未赋值,则等待,若已有值,则直接返回值 * 注:此函数返回 Promise 实例,只会执行(fullfill)一次 + * wait until value of certain key is set, will only be + * triggered once. * @param key * @returns */ @@ -40,6 +46,8 @@ export interface IPublicModelEngineConfig { /** * 获取指定 key 的值,函数回调模式,若多次被赋值,回调会被多次调用 + * set callback for event of value set for some key + * this will be called each time the value is set * @param key * @param fn * @returns @@ -48,7 +56,10 @@ export interface IPublicModelEngineConfig { /** * 获取全局 Preference, 用于管理全局浏览器侧用户 Preference,如 Panel 是否钉住 - * @returns IPreference + * get global user preference manager, which can be use to store + * user`s preference in user localstorage, such as a panel is pinned or not. + * @returns {IPublicModelPreference} + * @since v1.1.0 */ - getPreference(): IPreference; + getPreference(): IPublicModelPreference; } diff --git a/packages/types/src/shell/model/index.ts b/packages/types/src/shell/model/index.ts index a11c1b2d1..c9d8f3a98 100644 --- a/packages/types/src/shell/model/index.ts +++ b/packages/types/src/shell/model/index.ts @@ -25,3 +25,4 @@ export * from './plugin-context'; export * from './setting-target'; export * from './engine-config'; export * from './editor'; +export * from './preference'; diff --git a/packages/types/src/shell/model/preference.ts b/packages/types/src/shell/model/preference.ts new file mode 100644 index 000000000..a58b76573 --- /dev/null +++ b/packages/types/src/shell/model/preference.ts @@ -0,0 +1,17 @@ + +export interface IPublicModelPreference { + /** + * set value from local storage by module and key + */ + set(key: string, value: any, module?: string): void; + + /** + * get value from local storage by module and key + */ + get(key: string, module: string): any; + + /** + * check if local storage contain certain key + */ + contains(key: string, module: string): boolean; +} diff --git a/packages/types/src/shell/model/selection.ts b/packages/types/src/shell/model/selection.ts index 8d48be8f0..bfc0b75ce 100644 --- a/packages/types/src/shell/model/selection.ts +++ b/packages/types/src/shell/model/selection.ts @@ -59,9 +59,15 @@ export interface IPublicModelSelection { * 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[]; + /** + * 注册 selection 变化事件回调 + * set callback which will be called when selection is changed + * @since v1.1.0 + */ onSelectionChange(fn: (ids: string[]) => void): () => void; } diff --git a/packages/types/src/shell/type/index.ts b/packages/types/src/shell/type/index.ts index ef80a138c..a42e65e3e 100644 --- a/packages/types/src/shell/type/index.ts +++ b/packages/types/src/shell/type/index.ts @@ -71,4 +71,5 @@ export * from './dynamic-props'; export * from './setter-config'; export * from './tip-config'; export * from './widget-config-area'; -export * from './hotkey-callback'; \ No newline at end of file +export * from './hotkey-callback'; +export * from './plugin-register-options'; \ No newline at end of file diff --git a/packages/types/src/shell/type/plugin-register-options.ts b/packages/types/src/shell/type/plugin-register-options.ts new file mode 100644 index 000000000..7d2377bbe --- /dev/null +++ b/packages/types/src/shell/type/plugin-register-options.ts @@ -0,0 +1,13 @@ + +export interface IPublicTypePluginRegisterOptions { + /** + * Will enable plugin registered with auto-initialization immediately + * other than plugin-manager init all plugins at certain time. + * It is helpful when plugin register is later than plugin-manager initialization. + */ + autoInit?: boolean; + /** + * allow overriding existing plugin with same name when override === true + */ + override?: boolean; +} diff --git a/scripts/sync-oss.js b/scripts/sync-oss.js index 422f7162d..4b67bdca9 100644 --- a/scripts/sync-oss.js +++ b/scripts/sync-oss.js @@ -5,7 +5,7 @@ const { version, name } = package; const options = { method: 'PUT', // 暂时使用 日常环境的 uipaas-node,上线后可切换成线上环境 https://uipaas-node.alibaba-inc.com - hostname: 'uipaas-node.alibaba.net', + hostname: 'uipaas-node.alibaba-inc.com', path: '/staticAssets/cdn/packages', headers: { 'Content-Type': 'application/json', From fadce950f91aaf2c1da076f5fe5d31c4fdc1dd02 Mon Sep 17 00:00:00 2001 From: liujuping <liujup@foxmail.com> Date: Mon, 26 Dec 2022 11:13:13 +0800 Subject: [PATCH 04/14] chore: eslint add no-unexpected-multiline rule --- .eslintrc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc.js b/.eslintrc.js index 5a8fe9e97..1455cdfd4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -40,5 +40,6 @@ module.exports = { "@typescript-eslint/no-useless-constructor": 0, '@typescript-eslint/dot-notation': 0, // for lint performance '@typescript-eslint/restrict-plus-operands': 0, // for lint performance + 'no-unexpected-multiline': 1, } }; From 840e70e3d01a91e63d0e628c3c14820148bc1f4b Mon Sep 17 00:00:00 2001 From: liujuping <liujup@foxmail.com> Date: Mon, 26 Dec 2022 12:07:19 +0800 Subject: [PATCH 05/14] docs: add workspace docs --- docs/docs/api/model/window.md | 23 +++++++++++++ docs/docs/api/workspace.md | 34 +++++++++++++++++++ packages/shell/src/model/window.ts | 4 +-- packages/types/src/shell/api/workspace.ts | 14 +++++++- packages/types/src/shell/model/window.ts | 15 +++++++- packages/types/src/shell/type/index.ts | 3 +- .../types/src/shell/type/resource-options.ts | 32 +++++++++++++++++ .../workspace/src/editor-window/context.ts | 2 +- packages/workspace/src/index.ts | 23 ++----------- 9 files changed, 123 insertions(+), 27 deletions(-) create mode 100644 docs/docs/api/model/window.md create mode 100644 docs/docs/api/workspace.md create mode 100644 packages/types/src/shell/type/resource-options.ts diff --git a/docs/docs/api/model/window.md b/docs/docs/api/model/window.md new file mode 100644 index 000000000..8c2fa1802 --- /dev/null +++ b/docs/docs/api/model/window.md @@ -0,0 +1,23 @@ +--- +title: Window +sidebar_position: 12 +--- + +> **@types** [IPublicModelWindow](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/window.ts)<br/> +> **@since** v1.1.0 + + +## 基本介绍 + +低代码设计器窗口模型 + +## 方法签名 + +### importSchema(schema: IPublicTypeNodeSchema) +当前窗口导入 schema + +### changeViewType(viewName: string) +修改当前窗口视图类型 + +### async save +调用当前窗口视图保存钩子 diff --git a/docs/docs/api/workspace.md b/docs/docs/api/workspace.md new file mode 100644 index 000000000..99d1fb6df --- /dev/null +++ b/docs/docs/api/workspace.md @@ -0,0 +1,34 @@ +--- +title: workspace - 应用级 API +sidebar_position: 12 +--- + +> **@types** [IPublicApiWorkspace](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/workspace.ts)<br/> +> **@since** v1.1.0 + + +## 模块简介 + +通过该模块可以开发应用级低代码设计器。 + +## 变量 + +### isActive + +是否启用 workspace 模式 + +### window + +当前设计器窗口模型 + +关联模型 [IPublicModelWindow](./model/window) + +## 方法签名 + +### registerResourceType +注册资源 + +```typescript +/** 注册资源 */ +registerResourceType(resourceName: string, resourceType: 'editor', options: IPublicResourceOptions): void; +``` diff --git a/packages/shell/src/model/window.ts b/packages/shell/src/model/window.ts index c909ee0fa..b471fc864 100644 --- a/packages/shell/src/model/window.ts +++ b/packages/shell/src/model/window.ts @@ -17,7 +17,7 @@ export class Window implements IPublicModelWindow { this[windowSymbol].changeViewType(viewName); } - save() { - return this[windowSymbol].save(); + async save() { + return await this[windowSymbol].save(); } } diff --git a/packages/types/src/shell/api/workspace.ts b/packages/types/src/shell/api/workspace.ts index d8aba8cf1..52db05825 100644 --- a/packages/types/src/shell/api/workspace.ts +++ b/packages/types/src/shell/api/workspace.ts @@ -1 +1,13 @@ -export interface IPublicApiWorkspace {} \ No newline at end of file +import { IPublicModelWindow } from '../model'; +import { IPublicResourceOptions } from '../type'; + +export interface IPublicApiWorkspace { + /** 是否启用 workspace 模式 */ + isActive: boolean; + + /** 当前设计器窗口 */ + window: IPublicModelWindow; + + /** 注册资源 */ + registerResourceType(resourceName: string, resourceType: 'editor', options: IPublicResourceOptions): void; +} \ No newline at end of file diff --git a/packages/types/src/shell/model/window.ts b/packages/types/src/shell/model/window.ts index e5430ab5a..4ffed5d07 100644 --- a/packages/types/src/shell/model/window.ts +++ b/packages/types/src/shell/model/window.ts @@ -1 +1,14 @@ -export interface IPublicModelWindow {} \ No newline at end of file +import { IPublicTypeNodeSchema } from '../type'; + +export interface IPublicModelWindow { + /** 当前窗口导入 schema */ + importSchema(schema: IPublicTypeNodeSchema): void; + + /** 修改当前窗口视图类型 */ + changeViewType(viewName: string): void; + + /** 调用当前窗口视图保存钩子 */ + save(): Promise<{ + [viewName: string]: IPublicTypeNodeSchema | any; + }>; +} \ 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 a42e65e3e..99a510292 100644 --- a/packages/types/src/shell/type/index.ts +++ b/packages/types/src/shell/type/index.ts @@ -72,4 +72,5 @@ export * from './setter-config'; export * from './tip-config'; export * from './widget-config-area'; export * from './hotkey-callback'; -export * from './plugin-register-options'; \ No newline at end of file +export * from './plugin-register-options'; +export * from './resource-options'; \ 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 new file mode 100644 index 000000000..63e8e4add --- /dev/null +++ b/packages/types/src/shell/type/resource-options.ts @@ -0,0 +1,32 @@ +export interface IPublicViewFunctions { + /** 视图初始化 */ + init?: () => Promise<void>; + /** 资源保存时调用视图的钩子 */ + save?: () => Promise<void>; +} + +export interface IPublicEditorView { + /** 资源名字 */ + viewName: string; + (ctx: any): IPublicViewFunctions; +} + +export interface IPublicResourceOptions { + /** 资源名字 */ + name: string; + + /** 资源描述 */ + description?: string; + + /** 默认视图类型 */ + defaultViewType: string; + + /** 资源视图 */ + editorViews: IPublicEditorView[]; + + /** save 钩子 */ + save?: () => Promise<void>; + + /** import 钩子 */ + import?: () => Promise<void>; +} \ No newline at end of file diff --git a/packages/workspace/src/editor-window/context.ts b/packages/workspace/src/editor-window/context.ts index 11e101720..31bbc88f3 100644 --- a/packages/workspace/src/editor-window/context.ts +++ b/packages/workspace/src/editor-window/context.ts @@ -26,7 +26,7 @@ export class EditorWindow { const saveResult = await this.editorViews.get(name)?.save(); value[name] = saveResult; } - this.resource.save(value); + return await this.resource.save(value); } async init() { diff --git a/packages/workspace/src/index.ts b/packages/workspace/src/index.ts index 6cd7dd5d3..7faad05f6 100644 --- a/packages/workspace/src/index.ts +++ b/packages/workspace/src/index.ts @@ -2,6 +2,7 @@ import { Editor } from '@alilc/lowcode-editor-core'; import { Skeleton as InnerSkeleton, } from '@alilc/lowcode-editor-skeleton'; +import { IPublicResourceOptions } from '@alilc/lowcode-types'; import { EditorWindow } from './editor-window/context'; import { Resource } from './resource'; @@ -35,7 +36,7 @@ export class Workspace { private resources: Map<string, Resource> = new Map(); - registerResourceType(resourceName: string, resourceType: 'editor' | 'webview', options: ResourceOptions): void { + registerResourceType(resourceName: string, resourceType: 'editor' | 'webview', options: IPublicResourceOptions): void { if (resourceType === 'editor') { const resource = new Resource(options); this.resources.set(resourceName, resource); @@ -62,23 +63,3 @@ export class Workspace { openEditorWindow() {} } - -export interface ResourceOptions { - description: string; - defaultViewType?: string; - editorViews?: EditorViewOptions[]; - init: (ctx: any) => Promise<void>; - dispose: (ctx: any) => Promise<void>; - import: (ctx: any) => Promise<any>; - save: (value: any) => Promise<any>; -} - -export interface ViewFunctions { - init: () => Promise<void>; - save: () => Promise<void>; -} - -export type EditorViewOptions = { - viewName: string; - (ctx: any): ViewFunctions; -}; From bcdec0989e5b10a1d33603931721bdc3f598da37 Mon Sep 17 00:00:00 2001 From: JackLian <jack.lianjie@gmail.com> Date: Mon, 26 Dec 2022 12:03:10 +0800 Subject: [PATCH 06/14] refactor: use callbacks to replace events --- docs/docs/api/model/document-model.md | 76 +++++++- .../designer/src/designer/active-tracker.ts | 3 +- packages/designer/src/designer/location.ts | 4 - .../designer/src/document/document-model.ts | 3 +- packages/editor-skeleton/src/skeleton.ts | 3 +- packages/engine/src/engine-core.ts | 2 +- .../src/controllers/pane-controller.ts | 72 ++++--- .../src/controllers/ric-shim.d.ts | 1 + .../src/controllers/tree-master.ts | 6 +- .../src/controllers/tree-node.ts | 12 +- .../src/controllers/tree.ts | 35 +++- .../src/helper/tree-title-extra.ts | 20 -- packages/plugin-outline-pane/src/index.tsx | 54 ++++-- .../plugin-outline-pane/src/views/pane.tsx | 18 +- .../src/views/root-tree-node.tsx | 179 ------------------ .../src/views/tree-branches.tsx | 46 +++-- .../src/views/tree-node.tsx | 178 ++++++++++++----- .../src/views/tree-title.tsx | 143 +++----------- .../plugin-outline-pane/src/views/tree.tsx | 28 +-- packages/shell/src/api/canvas.ts | 13 +- packages/shell/src/api/event.ts | 6 +- packages/shell/src/api/project.ts | 3 +- packages/shell/src/model/active-tracker.ts | 36 ++++ packages/shell/src/model/document-model.ts | 48 ++++- packages/shell/src/model/dragon.ts | 7 +- packages/types/src/shell/enum/event-names.ts | 6 - .../types/src/shell/model/active-tracker.ts | 3 + .../types/src/shell/model/document-model.ts | 50 ++++- .../types/src/shell/model/drop-location.ts | 7 + packages/utils/src/asset.ts | 5 +- packages/utils/src/build-components.ts | 2 +- packages/utils/src/create-content.ts | 5 +- packages/utils/src/create-icon.tsx | 5 +- packages/utils/src/index.ts | 1 - packages/utils/src/is-public-event-name.ts | 10 - packages/utils/src/misc.ts | 14 +- packages/utils/src/node-helper.ts | 9 +- packages/workspace/src/base-context.ts | 7 + 38 files changed, 589 insertions(+), 531 deletions(-) create mode 100644 packages/plugin-outline-pane/src/controllers/ric-shim.d.ts delete mode 100644 packages/plugin-outline-pane/src/helper/tree-title-extra.ts delete mode 100644 packages/plugin-outline-pane/src/views/root-tree-node.tsx create mode 100644 packages/shell/src/model/active-tracker.ts delete mode 100644 packages/utils/src/is-public-event-name.ts diff --git a/docs/docs/api/model/document-model.md b/docs/docs/api/model/document-model.md index 68efe0575..0716588ce 100644 --- a/docs/docs/api/model/document-model.md +++ b/docs/docs/api/model/document-model.md @@ -42,6 +42,12 @@ sidebar_position: 0 参见 [模态节点管理](./modal-nodes-manager) +### dropLocation +文档的 dropLocation +相关类型:[IPublicModelDropLocation](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/drop-location.ts) + +**@since v1.1.0** + ## 方法签名 ### getNodeById @@ -90,6 +96,24 @@ removeNode(idOrNode: string | Node) function checkNesting(dropTarget: Node, dragObject: DragNodeObject | DragNodeDataObject): boolean {} ``` +### isDetectingNode +检查拖拽放置的目标节点是否可以放置该拖拽对象 + +```typescript +/** + * 判断是否当前节点处于被探测状态 + * check is node being detected + * @param node + * @since v1.1.0 + */ +isDetectingNode(node: IPublicModelNode): boolean; +``` +相关类型:[IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts) + + +**@since v1.1.0** + + ## 事件 ### onAddNode @@ -144,7 +168,53 @@ onChangeNodeProp(fn: (info: IPublicTypePropChangeOptions) => void) ### onImportSchema 当前 document 导入新的 schema 事件 -版本 >= 1.0.15 ```typescript -onImportSchema(fn: (schema: any) => void) -``` \ No newline at end of file +/** + * import schema event + * @param fn + * @since v1.0.15 + */ +onImportSchema(fn: (schema: IPublicTypeRootSchema) => void): IPublicTypeDisposable; +``` +相关类型: +- [IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) +- [IPublicTypeRootSchema](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/root-schema.ts) + +**@since v1.0.15** + +### onFocusNodeChanged +设置聚焦节点变化的回调 + +```typescript +/** + * 设置聚焦节点变化的回调 + * triggered focused node is set mannually from plugin + * @param fn + * @since v1.1.0 + */ +onFocusNodeChanged( + fn: (doc: IPublicModelDocumentModel, focusNode: IPublicModelNode) => void, +): IPublicTypeDisposable; +``` +相关类型: +- [IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts) +- [IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts) + +**@since v1.1.0** + +### onDropLocationChanged +设置 DropLocation 变化的回调 + +```typescript +/** + * 设置 DropLocation 变化的回调 + * triggered when drop location changed + * @param fn + * @since v1.1.0 + */ +onDropLocationChanged(fn: (doc: IPublicModelDocumentModel) => 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/packages/designer/src/designer/active-tracker.ts b/packages/designer/src/designer/active-tracker.ts index bf37efe80..d62ad9fe5 100644 --- a/packages/designer/src/designer/active-tracker.ts +++ b/packages/designer/src/designer/active-tracker.ts @@ -7,8 +7,9 @@ import { import { isNode } from '@alilc/lowcode-utils'; export interface IActiveTracker extends IPublicModelActiveTracker { - + track(originalTarget: IPublicTypeActiveTarget | INode): void; } + export class ActiveTracker implements IActiveTracker { private emitter: IEventBus = createModuleEventBus('ActiveTracker'); diff --git a/packages/designer/src/designer/location.ts b/packages/designer/src/designer/location.ts index c219a259d..a690b28d5 100644 --- a/packages/designer/src/designer/location.ts +++ b/packages/designer/src/designer/location.ts @@ -103,15 +103,11 @@ export interface IDropLocation extends IPublicModelDropLocation { readonly target: INode; - readonly detail: IPublicTypeLocationDetail; - readonly event: ILocateEvent; readonly source: string; get document(): IPublicModelDocumentModel; - - clone(event: ILocateEvent): IDropLocation; } export class DropLocation implements IDropLocation { diff --git a/packages/designer/src/document/document-model.ts b/packages/designer/src/document/document-model.ts index f38c2f8bc..5e0717e03 100644 --- a/packages/designer/src/document/document-model.ts +++ b/packages/designer/src/document/document-model.ts @@ -14,7 +14,6 @@ import { IPublicModelNode, IPublicApiProject, IPublicModelDropLocation, - IPublicEnumEventNames, IPublicEnumTransformStage, } from '@alilc/lowcode-types'; import { Project } from '../project'; @@ -326,7 +325,7 @@ export class DocumentModel implements IDocumentModel { this._dropLocation = loc; // pub event this.designer.editor.eventBus.emit( - IPublicEnumEventNames.DOCUMENT_DROPLOCATION_CHANGED, + 'document.dropLocation.changed', { document: this, location: loc }, ); } diff --git a/packages/editor-skeleton/src/skeleton.ts b/packages/editor-skeleton/src/skeleton.ts index a6a5f6ae6..649c45271 100644 --- a/packages/editor-skeleton/src/skeleton.ts +++ b/packages/editor-skeleton/src/skeleton.ts @@ -26,7 +26,6 @@ import { PluginClassSet, IPublicTypeWidgetBaseConfig, IPublicTypeWidgetConfigArea, - IPublicEnumEventNames, } from '@alilc/lowcode-types'; const logger = new Logger({ level: 'warn', bizName: 'skeleton' }); @@ -174,7 +173,7 @@ export class Skeleton { */ setupEvents() { // adjust pinned status when panel shown - this.editor.eventBus.on(IPublicEnumEventNames.SKELETON_PANEL_SHOW, (panelName, panel) => { + this.editor.eventBus.on(SkeletonEvents.PANEL_SHOW, (panelName, panel) => { const panelNameKey = `${panelName}-pinned-status-isFloat`; const isInFloatAreaPreferenceExists = engineConfig.getPreference()?.contains(panelNameKey, 'skeleton'); if (isInFloatAreaPreferenceExists) { diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index 1af53e9ba..d8d81aefc 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -171,7 +171,7 @@ export async function init( engineConfig.setEngineOptions(engineOptions as any); // 注册一批内置插件 - await plugins.register(OutlinePlugin); + await plugins.register(OutlinePlugin, {}, { autoInit: true }); await plugins.register(componentMetaParser(designer)); await plugins.register(setterRegistry, {}, { autoInit: true }); await plugins.register(defaultPanelRegistry(editor, designer)); diff --git a/packages/plugin-outline-pane/src/controllers/pane-controller.ts b/packages/plugin-outline-pane/src/controllers/pane-controller.ts index c472d4c9a..a368c8288 100644 --- a/packages/plugin-outline-pane/src/controllers/pane-controller.ts +++ b/packages/plugin-outline-pane/src/controllers/pane-controller.ts @@ -7,7 +7,7 @@ import { isLocationChildrenDetail, } from '@alilc/lowcode-utils'; import { - DragObject, + IPublicModelDragObject, IPublicModelScrollable, ISensor, IPublicTypeLocationChildrenDetail, @@ -50,7 +50,6 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab setup(); } - /** -------------------- ISensor begin -------------------- */ private indentTrack = new IndentTrack(); @@ -107,12 +106,12 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab const document = project.getCurrentDocument(); const pos = getPosFromEvent(e, this._shell); const irect = this.getInsertionRect(); - const originLoc = document.dropLocation; + const originLoc = document?.dropLocation; - const componentMeta = e.dragObject.nodes ? e.dragObject.nodes[0].componentMeta : null; - if (e.dragObject.type === 'node' && componentMeta && componentMeta.isModal) { + const componentMeta = e.dragObject?.nodes ? e.dragObject.nodes[0].componentMeta : null; + if (e.dragObject?.type === 'node' && componentMeta && componentMeta.isModal && document?.focusNode) { return canvas.createLocation({ - target: document.focusNode, + target: document?.focusNode, detail: { type: IPublicTypeLocationDetailType.Children, index: 0, @@ -123,7 +122,9 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab }); } - if (originLoc && ((pos && pos === 'unchanged') || (irect && globalY >= irect.top && globalY <= irect.bottom))) { + if (originLoc + && ((pos && pos === 'unchanged') || (irect && globalY >= irect.top && globalY <= irect.bottom)) + && dragObject) { const loc = originLoc.clone(e); const indented = this.indentTrack.getIndentParent(originLoc, loc); if (indented) { @@ -138,7 +139,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab detail: { type: IPublicTypeLocationDetailType.Children, index, - valid: document.checkNesting(parent, e.dragObject as any), + valid: document?.checkNesting(parent, e.dragObject as any), }, }); } @@ -177,7 +178,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab } } if (p !== node) { - node = p || document.focusNode; + node = p || document?.focusNode; treeNode = tree.getTreeNode(node); focusSlots = false; } @@ -258,7 +259,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab cancelIdleCallback(this.tryScrollAgain); this.tryScrollAgain = null; } - if (this.sensing || !this.bounds || !this.scroller || !this.scrollTarget) { + if (!this.bounds || !this.scroller || !this.scrollTarget) { // is a active sensor return; } @@ -305,7 +306,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab focus = { type: 'slots' }; } else { index = 0; - valid = document.checkNesting(target, event.dragObject as any); + valid = !!document?.checkNesting(target, event.dragObject as any); } canvas.createLocation({ target, @@ -320,23 +321,28 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab }); }); - private getNear(treeNode: TreeNode, e: IPublicModelLocateEvent, index?: number, rect?: DOMRect) { + private getNear(treeNode: TreeNode, e: IPublicModelLocateEvent, originalIndex?: number, originalRect?: DOMRect) { const { canvas, project } = this.pluginContext; const document = project.getCurrentDocument(); const { globalY, dragObject } = e; + if (!dragObject) { + return null; + } // TODO: check dragObject is anyData const { node, expanded } = treeNode; + let rect = originalRect; if (!rect) { rect = this.getTreeNodeRect(treeNode); if (!rect) { return null; } } + let index = originalIndex; if (index == null) { index = node.index; } - if (node.isSlot) { + if (node.isSlotNode) { // 是个插槽根节点 if (!treeNode.isContainer() && !treeNode.hasSlots()) { return canvas.createLocation({ @@ -385,7 +391,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab detail: { type: IPublicTypeLocationDetailType.Children, index, - valid: document.checkNesting(node.parent!, dragObject as any), + valid: document?.checkNesting(node.parent!, dragObject as any), near: { node, pos: 'before' }, focus: checkRecursion(focusNode, dragObject) ? { type: 'node', node: focusNode } : undefined, }, @@ -412,7 +418,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab detail: { type: IPublicTypeLocationDetailType.Children, index: index + 1, - valid: document.checkNesting(node.parent!, dragObject as any), + valid: document?.checkNesting(node.parent!, dragObject as any), near: { node, pos: 'after' }, focus: checkRecursion(focusNode, dragObject) ? { type: 'node', node: focusNode } : undefined, }, @@ -423,6 +429,9 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab const { canvas, project } = this.pluginContext; const document = project.getCurrentDocument(); const { dragObject, globalY } = e; + if (!dragObject) { + return null; + } if (!checkRecursion(treeNode.node, dragObject)) { return null; @@ -454,7 +463,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab detail.valid = false; } else { detail.index = 0; - detail.valid = document.checkNesting(container, dragObject); + detail.valid = document?.checkNesting(container, dragObject); } } @@ -526,7 +535,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab } else { detail.index = l; } - detail.valid = document.checkNesting(container, dragObject); + detail.valid = document?.checkNesting(container, dragObject); } return canvas.createLocation(locationData); @@ -572,9 +581,26 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab return; } this._shell = shell; + const { canvas, project } = this.pluginContext; if (shell) { - this._scrollTarget = this.pluginContext.canvas.createScrollTarget(shell); + this._scrollTarget = canvas.createScrollTarget(shell); this._sensorAvailable = true; + + // check if there is current selection and scroll to it + const selection = project.currentDocument?.selection; + const topNodes = selection?.getTopNodes(true); + const tree = this.treeMaster?.currentTree; + if (topNodes && topNodes[0] && tree) { + const treeNode = tree.getTreeNodeById(topNodes[0].id); + if (treeNode) { + // at this moment, it is possible that pane is not ready yet, so + // put ui related operations to the next loop + setTimeout(() => { + tree.setNodeSelected(treeNode.id); + this.scrollToNode(treeNode, null, 4); + }, 0); + } + } } else { this._scrollTarget = undefined; this._sensorAvailable = false; @@ -610,7 +636,7 @@ export class PaneController implements ISensor, ITreeBoard, IPublicModelScrollab } } -function checkRecursion(parent: IPublicModelNode | undefined | null, dragObject: DragObject): boolean { +function checkRecursion(parent: IPublicModelNode | undefined | null, dragObject: IPublicModelDragObject): boolean { if (!parent) { return false; } @@ -633,14 +659,14 @@ function getPosFromEvent( if (target.matches('.insertion')) { return 'unchanged'; } - target = target.closest('[data-id]'); - if (!target || !stop.contains(target)) { + const closest = target.closest('[data-id]'); + if (!closest || !stop.contains(closest)) { return null; } - const nodeId = (target as HTMLDivElement).dataset.id!; + const nodeId = (closest as HTMLDivElement).dataset.id!; return { - focusSlots: target.matches('.tree-node-slots'), + focusSlots: closest.matches('.tree-node-slots'), nodeId, }; } diff --git a/packages/plugin-outline-pane/src/controllers/ric-shim.d.ts b/packages/plugin-outline-pane/src/controllers/ric-shim.d.ts new file mode 100644 index 000000000..74f3fbd94 --- /dev/null +++ b/packages/plugin-outline-pane/src/controllers/ric-shim.d.ts @@ -0,0 +1 @@ +declare module 'ric-shim'; \ No newline at end of file diff --git a/packages/plugin-outline-pane/src/controllers/tree-master.ts b/packages/plugin-outline-pane/src/controllers/tree-master.ts index c660f0457..38b723a98 100644 --- a/packages/plugin-outline-pane/src/controllers/tree-master.ts +++ b/packages/plugin-outline-pane/src/controllers/tree-master.ts @@ -1,5 +1,5 @@ import { isLocationChildrenDetail } from '@alilc/lowcode-utils'; -import { IPublicModelPluginContext, IPublicTypeActiveTarget, IPublicModelNode, IPublicEnumEventNames } from '@alilc/lowcode-types'; +import { IPublicModelPluginContext, IPublicTypeActiveTarget, IPublicModelNode } from '@alilc/lowcode-types'; import TreeNode from './tree-node'; import { Tree } from './tree'; @@ -54,8 +54,8 @@ export class TreeMaster { time: (endTime - startTime).toFixed(2), }); }); - event.on(IPublicEnumEventNames.DESIGNER_DOCUMENT_REMOVE, (doc) => { - const { id } = doc as any; + project.onRemoveDocument((data: {id: string}) => { + const { id } = data; this.treeMap.delete(id); }); } diff --git a/packages/plugin-outline-pane/src/controllers/tree-node.ts b/packages/plugin-outline-pane/src/controllers/tree-node.ts index daac5bc2c..b6063e724 100644 --- a/packages/plugin-outline-pane/src/controllers/tree-node.ts +++ b/packages/plugin-outline-pane/src/controllers/tree-node.ts @@ -24,6 +24,10 @@ export interface FilterResult { export default class TreeNode { readonly pluginContext: IPublicModelPluginContext; onFilterResultChanged: () => void; + onExpandedChanged: (expanded: boolean) => void; + onHiddenChanged: (hidden: boolean) => void; + onLockedChanged: (locked: boolean) => void; + onTitleLabelChanged: (treeNode: TreeNode) => void; get id(): string { return this.node.id; @@ -87,7 +91,7 @@ export default class TreeNode { setExpanded(value: boolean) { this._expanded = value; - this.pluginContext.pluginEvent.emit('tree-node.expandedChanged', { expanded: value, nodeId: this.id }); + this.onExpandedChanged && this.onExpandedChanged(value); } get detecting() { @@ -108,7 +112,7 @@ export default class TreeNode { return; } this.node.setVisible(!flag); - this.pluginContext.pluginEvent.emit('tree-node.hiddenChanged', { hidden: flag, nodeId: this.id }); + this.onHiddenChanged && this.onHiddenChanged(flag); } get locked(): boolean { @@ -117,7 +121,7 @@ export default class TreeNode { setLocked(flag: boolean) { this.node.lock(flag); - this.pluginContext.pluginEvent.emit('tree-node.lockedChanged', { locked: flag, nodeId: this.id }); + this.onLockedChanged && this.onLockedChanged(flag); } get selected(): boolean { @@ -162,7 +166,7 @@ export default class TreeNode { } else { this.node.getExtraProp('title', true)?.setValue(label); } - this.pluginContext.pluginEvent.emit('tree-node.titleLabelChanged', { titleLabel: label, nodeId: this.id }); + this.onTitleLabelChanged && this.onTitleLabelChanged(this); } get icon() { diff --git a/packages/plugin-outline-pane/src/controllers/tree.ts b/packages/plugin-outline-pane/src/controllers/tree.ts index b99e04f64..a4106088b 100644 --- a/packages/plugin-outline-pane/src/controllers/tree.ts +++ b/packages/plugin-outline-pane/src/controllers/tree.ts @@ -20,6 +20,15 @@ export class Tree { this.id = this.pluginContext.project.currentDocument?.id; } + setNodeSelected(nodeId: string): void { + // 目标节点选中,其他节点展开 + const treeNode = this.treeNodesMap.get(nodeId); + if (!treeNode) { + return; + } + this.expandAllAncestors(treeNode); + } + getTreeNode(node: IPublicModelNode): TreeNode { if (this.treeNodesMap.has(node.id)) { const tnode = this.treeNodesMap.get(node.id)!; @@ -36,7 +45,29 @@ export class Tree { return this.treeNodesMap.get(id); } - expandAllDecendants(treeNode: TreeNode | undefined) { + expandAllAncestors(treeNode: TreeNode | undefined | null) { + if (!treeNode) { + return; + } + if (treeNode.isRoot()) { + return; + } + const ancestors = []; + let currentNode: TreeNode | null | undefined = treeNode; + while (!treeNode.isRoot()) { + currentNode = currentNode?.parent; + if (currentNode) { + ancestors.unshift(currentNode); + } else { + break; + } + } + ancestors.forEach((ancestor) => { + ancestor.setExpanded(true); + }); + } + + expandAllDecendants(treeNode: TreeNode | undefined | null) { if (!treeNode) { return; } @@ -49,7 +80,7 @@ export class Tree { } } - collapseAllDecendants(treeNode: TreeNode | undefined) { + collapseAllDecendants(treeNode: TreeNode | undefined | null): void { if (!treeNode) { return; } diff --git a/packages/plugin-outline-pane/src/helper/tree-title-extra.ts b/packages/plugin-outline-pane/src/helper/tree-title-extra.ts deleted file mode 100644 index b1bcbf6ed..000000000 --- a/packages/plugin-outline-pane/src/helper/tree-title-extra.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { globalContext } from '@alilc/lowcode-editor-core'; -import { ReactElement } from 'react'; - -const TREE_TITLE_EXTRA_KEY = 'TREE_TITLE_EXTRA_KEY'; - -export const registerTreeTitleExtra = (extra: ReactElement) => { - if (extra && !globalContext.has(TREE_TITLE_EXTRA_KEY)) { - globalContext.register(extra, TREE_TITLE_EXTRA_KEY); - } -}; - -export const getTreeTitleExtra = () => { - try { - return globalContext.get(TREE_TITLE_EXTRA_KEY); - } catch (e) { - // console.error('getTreeTitleExtra Error', e); - } - - return null; -}; diff --git a/packages/plugin-outline-pane/src/index.tsx b/packages/plugin-outline-pane/src/index.tsx index 0a6ef527c..bf3c95b1a 100644 --- a/packages/plugin-outline-pane/src/index.tsx +++ b/packages/plugin-outline-pane/src/index.tsx @@ -1,11 +1,13 @@ import { Pane } from './views/pane'; import { IconOutline } from './icons/outline'; -import { IPublicModelPluginContext, IPublicEnumEventNames } from '@alilc/lowcode-types'; +import { IPublicModelPluginContext, IPublicModelDocumentModel } from '@alilc/lowcode-types'; import { enUS, zhCN } from './locale'; import { MasterPaneName, BackupPaneName } from './helper/consts'; +import { getTreeMaster } from './controllers/tree-master'; +import { PaneController } from './controllers/pane-controller'; export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { - const { skeleton, config, common, event, canvas } = ctx; + const { skeleton, config, common, event, canvas, project } = ctx; const { intl, intlNode, getLocale } = common.utils.createIntl({ 'en-US': enUS, 'zh-CN': zhCN, @@ -24,7 +26,9 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { masterPane: false, backupPane: false, }; - + const treeMaster = getTreeMaster(ctx); + let masterPaneController: PaneController | null = null; + let backupPaneController: PaneController | null = null; return { async init() { skeleton.add({ @@ -39,10 +43,13 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { description: intlNode('Outline Tree'), }, content: (props: any) => { + masterPaneController = new PaneController(MasterPaneName, ctx, treeMaster); return ( <Pane config={config} pluginContext={ctx} + treeMaster={treeMaster} + controller={masterPaneController} {...props} /> ); @@ -65,12 +72,17 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { props: { hiddenWhenInit: true, }, - content: (props: any) => ( - <Pane - pluginContext={ctx} - {...props} - /> - ), + content: (props: any) => { + backupPaneController = new PaneController(BackupPaneName, ctx, treeMaster); + return ( + <Pane + pluginContext={ctx} + treeMaster={treeMaster} + controller={backupPaneController} + {...props} + /> + ); + }, }); // 处理 master pane 和 backup pane 切换 @@ -91,8 +103,7 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { canvas.dragon?.onDragend(() => { switchPanes(); }); - - event.on(IPublicEnumEventNames.SKELETON_PANEL_SHOW, (key: string) => { + skeleton.onShowPanel((key: string) => { if (key === MasterPaneName) { showingPanes.masterPane = true; } @@ -100,7 +111,7 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { showingPanes.backupPane = true; } }); - event.on(IPublicEnumEventNames.SKELETON_PANEL_HIDE, (key: string) => { + skeleton.onHidePanel((key: string) => { if (key === MasterPaneName) { showingPanes.masterPane = false; switchPanes(); @@ -109,6 +120,25 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { showingPanes.backupPane = false; } }); + project.onChangeDocument((document: IPublicModelDocumentModel) => { + if (!document) { + return; + } + + const { selection } = document; + + selection?.onSelectionChange(() => { + const selectedNodes = selection?.getNodes(); + if (!selectedNodes || selectedNodes.length === 0) { + return; + } + const tree = treeMaster.currentTree; + selectedNodes.forEach((node) => { + const treeNode = tree?.getTreeNodeById(node.id); + tree?.expandAllAncestors(treeNode); + }); + }); + }); }, }; }; diff --git a/packages/plugin-outline-pane/src/views/pane.tsx b/packages/plugin-outline-pane/src/views/pane.tsx index 6d037eb5b..1ed4c74ba 100644 --- a/packages/plugin-outline-pane/src/views/pane.tsx +++ b/packages/plugin-outline-pane/src/views/pane.tsx @@ -4,35 +4,29 @@ import TreeView from './tree'; import './style.less'; import { IPublicModelPluginContext } from '@alilc/lowcode-types'; import Filter from './filter'; -import { registerTreeTitleExtra } from '../helper/tree-title-extra'; -import { getTreeMaster, TreeMaster } from '../controllers/tree-master'; +import { TreeMaster } from '../controllers/tree-master'; export class Pane extends Component<{ config: any; pluginContext: IPublicModelPluginContext; + treeMaster: TreeMaster; + controller: PaneController; }> { private controller; private treeMaster: TreeMaster; constructor(props: any) { super(props); - this.treeMaster = getTreeMaster(this.props.pluginContext); - this.controller = new PaneController( - this.props.config.name || this.props.config.pluginKey, - this.props.pluginContext, - this.treeMaster, - ); + const { controller, treeMaster } = props; + this.treeMaster = treeMaster; + this.controller = controller; } componentWillUnmount() { this.controller.purge(); } - componentDidMount() { - registerTreeTitleExtra(this.props?.config?.contentProps?.treeTitleExtra); - } - render() { const tree = this.treeMaster.currentTree; diff --git a/packages/plugin-outline-pane/src/views/root-tree-node.tsx b/packages/plugin-outline-pane/src/views/root-tree-node.tsx deleted file mode 100644 index f82783835..000000000 --- a/packages/plugin-outline-pane/src/views/root-tree-node.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import { Component } from 'react'; -import classNames from 'classnames'; -import TreeNode from '../controllers/tree-node'; -import TreeTitle from './tree-title'; -import TreeBranches from './tree-branches'; -import { IconEyeClose } from '../icons/eye-close'; -import { IPublicModelPluginContext, IPublicModelModalNodesManager, IPublicEnumEventNames } from '@alilc/lowcode-types'; - -class ModalTreeNodeView extends Component<{ - treeNode: TreeNode; - pluginContext: IPublicModelPluginContext; -}> { - private modalNodesManager: IPublicModelModalNodesManager | undefined | null; - readonly pluginContext: IPublicModelPluginContext; - - constructor(props: any) { - super(props); - - // 模态管理对象 - this.pluginContext = props.pluginContext; - const { project } = this.pluginContext; - this.modalNodesManager = project.currentDocument?.modalNodesManager; - } - - hideAllNodes() { - this.modalNodesManager?.hideModalNodes(); - } - - render() { - const { treeNode } = this.props; - // 当指定了新的根节点时,要从原始的根节点去获取模态节点 - const { project } = this.pluginContext; - const rootNode = project.currentDocument?.root; - const rootTreeNode = treeNode.tree.getTreeNode(rootNode!); - const modalNodes = rootTreeNode.children?.filter((item) => { - return item.node.componentMeta?.isModal; - }); - if (!modalNodes || modalNodes.length === 0) { - return null; - } - - const hasVisibleModalNode = !!this.modalNodesManager?.getVisibleModalNode(); - return ( - <div className="tree-node-modal"> - <div className="tree-node-modal-title"> - <span>模态视图层</span> - <div - className="tree-node-modal-title-visible-icon" - onClick={this.hideAllNodes.bind(this)} - > - {hasVisibleModalNode ? <IconEyeClose /> : null} - </div> - </div> - <div className="tree-pane-modal-content"> - <TreeBranches treeNode={rootTreeNode} isModal pluginContext={this.pluginContext} /> - </div> - </div> - ); - } -} - -export default class RootTreeNodeView extends Component<{ - treeNode: TreeNode; - pluginContext: IPublicModelPluginContext; -}> { - state = { - expanded: false, - selected: false, - hidden: false, - locked: false, - detecting: false, - isRoot: false, - highlight: false, - dropping: false, - conditionFlow: false, - }; - - eventOffCallbacks: Array<() => void> = []; - - componentDidMount() { - const { treeNode, pluginContext } = this.props; - const { pluginEvent, event } = pluginContext; - const { id } = treeNode; - - this.state = { - expanded: false, - selected: false, - hidden: false, - locked: false, - detecting: false, - isRoot: treeNode.isRoot(), - // 是否投放响应 - dropping: treeNode.dropDetail?.index != null, - conditionFlow: treeNode.node.conditionGroup != null, - highlight: treeNode.isFocusingNode(), - }; - - const doc = pluginContext.project.currentDocument; - - this.eventOffCallbacks.push(pluginEvent.on('tree-node.hiddenChanged', (payload: any) => { - const { hidden, nodeId } = payload; - if (nodeId === id) { - this.setState({ hidden }); - } - })); - - this.eventOffCallbacks.push(pluginEvent.on('tree-node.expandedChanged', (payload: any) => { - const { expanded, nodeId } = payload; - if (nodeId === id) { - this.setState({ expanded }); - } - })); - - this.eventOffCallbacks.push(pluginEvent.on('tree-node.lockedChanged', (payload: any) => { - const { locked, nodeId } = payload; - if (nodeId === id) { - this.setState({ locked }); - } - })); - - this.eventOffCallbacks.push( - event.on( - IPublicEnumEventNames.DOCUMENT_DROPLOCATION_CHANGED, - (payload: any) => { - const { document } = payload; - if (document.id === doc?.id) { - this.setState({ - dropping: treeNode.dropDetail?.index != null, - }); - } - }, - ), - ); - - const offSelectionChange = doc?.selection?.onSelectionChange(() => { - this.setState({ selected: treeNode.selected }); - }); - this.eventOffCallbacks.push(offSelectionChange!); - const offDetectingChange = doc?.detecting?.onDetectingChange(() => { - this.setState({ detecting: treeNode.detecting }); - }); - this.eventOffCallbacks.push(offDetectingChange!); - } - - componentWillUnmount(): void { - this.eventOffCallbacks?.forEach((offFun: () => void) => { - offFun(); - }); - } - - render() { - const { treeNode } = this.props; - const className = classNames('tree-node', { - // 是否展开 - expanded: this.state.expanded, - // 是否选中的 - selected: this.state.selected, - // 是否隐藏的 - hidden: this.state.hidden, - // 是否锁定的 - locked: this.state.locked, - // 是否悬停中 - detecting: this.state.detecting, - // 是否投放响应 - dropping: this.state.dropping, - 'is-root': this.state.isRoot, - 'condition-flow': this.state.conditionFlow, - highlight: this.state.highlight, - }); - - return ( - <div className={className} data-id={treeNode.id}> - <TreeTitle treeNode={treeNode} pluginContext={this.props.pluginContext} /> - <ModalTreeNodeView treeNode={treeNode} pluginContext={this.props.pluginContext} /> - <TreeBranches treeNode={treeNode} pluginContext={this.props.pluginContext} /> - </div> - ); - } -} diff --git a/packages/plugin-outline-pane/src/views/tree-branches.tsx b/packages/plugin-outline-pane/src/views/tree-branches.tsx index 5dfdafb72..9af2dbd09 100644 --- a/packages/plugin-outline-pane/src/views/tree-branches.tsx +++ b/packages/plugin-outline-pane/src/views/tree-branches.tsx @@ -2,38 +2,33 @@ import { Component } from 'react'; import classNames from 'classnames'; import TreeNode from '../controllers/tree-node'; import TreeNodeView from './tree-node'; -import { IPublicModelPluginContext, IPublicModelExclusiveGroup, IPublicEnumEventNames, IPublicTypeLocationDetailType } from '@alilc/lowcode-types'; +import { IPublicModelPluginContext, IPublicModelExclusiveGroup } from '@alilc/lowcode-types'; export default class TreeBranches extends Component<{ treeNode: TreeNode; isModal?: boolean; pluginContext: IPublicModelPluginContext; + expanded: boolean; }> { state = { - expanded: false, filterWorking: false, matchChild: false, }; private offExpandedChanged: (() => void) | null; - componentDidMount() { - const { treeNode, pluginContext } = this.props; - const { expanded } = treeNode; - const { pluginEvent } = pluginContext; + constructor(props: any) { + super(props); + const { treeNode } = this.props; const { filterWorking, matchChild } = treeNode.filterReult; - this.setState({ expanded, filterWorking, matchChild }); + this.setState({ filterWorking, matchChild }); + } + + componentDidMount() { + const { treeNode } = this.props; treeNode.onFilterResultChanged = () => { const { filterWorking: newFilterWorking, matchChild: newMatchChild } = treeNode.filterReult; this.setState({ filterWorking: newFilterWorking, matchChild: newMatchChild }); }; - - this.offExpandedChanged = pluginEvent.on('tree-node.expandedChanged', (payload: any) => { - const { expanded: value, nodeId } = payload; - const { id } = this.props.treeNode; - if (nodeId === id) { - this.setState({ expanded: value }); - } - }); } componentWillUnmount(): void { @@ -43,8 +38,8 @@ export default class TreeBranches extends Component<{ } render() { - const { treeNode, isModal } = this.props; - const { filterWorking, matchChild, expanded } = this.state; + const { treeNode, isModal, expanded } = this.props; + const { filterWorking, matchChild } = this.state; // 条件过滤生效时,如果命中了子节点,需要将该节点展开 const expandInFilterResult = filterWorking && matchChild; @@ -57,7 +52,11 @@ export default class TreeBranches extends Component<{ { !isModal && <TreeNodeSlots treeNode={treeNode} pluginContext={this.props.pluginContext} /> } - <TreeNodeChildren treeNode={treeNode} isModal={isModal || false} pluginContext={this.props.pluginContext} /> + <TreeNodeChildren + treeNode={treeNode} + isModal={isModal || false} + pluginContext={this.props.pluginContext} + /> </div> ); } @@ -77,7 +76,7 @@ class TreeNodeChildren extends Component<{ offLocationChanged: () => void; componentDidMount() { const { treeNode, pluginContext } = this.props; - const { event } = pluginContext; + const { project } = pluginContext; const { filterWorking, matchSelf, keywords } = treeNode.filterReult; const { dropDetail } = treeNode; this.setState({ @@ -98,11 +97,10 @@ class TreeNodeChildren extends Component<{ keywords: newKeywords, }); }; - this.offLocationChanged = event.on( - IPublicEnumEventNames.DOCUMENT_DROPLOCATION_CHANGED, - (payload: any) => { - this.setState({ dropDetail: treeNode.dropDetail }); - }, + this.offLocationChanged = project.currentDocument?.onDropLocationChanged( + () => { + this.setState({ dropDetail: treeNode.dropDetail }); + }, ); } componentWillUnmount(): void { diff --git a/packages/plugin-outline-pane/src/views/tree-node.tsx b/packages/plugin-outline-pane/src/views/tree-node.tsx index 72b68aef3..091530706 100644 --- a/packages/plugin-outline-pane/src/views/tree-node.tsx +++ b/packages/plugin-outline-pane/src/views/tree-node.tsx @@ -3,12 +3,67 @@ import classNames from 'classnames'; import TreeNode from '../controllers/tree-node'; import TreeTitle from './tree-title'; import TreeBranches from './tree-branches'; -import { IPublicModelPluginContext, IPublicEnumEventNames } from '@alilc/lowcode-types'; +import { IconEyeClose } from '../icons/eye-close'; +import { IPublicModelPluginContext, IPublicModelModalNodesManager, IPublicModelDocumentModel } from '@alilc/lowcode-types'; + +class ModalTreeNodeView extends Component<{ + treeNode: TreeNode; + pluginContext: IPublicModelPluginContext; +}> { + private modalNodesManager: IPublicModelModalNodesManager | undefined | null; + readonly pluginContext: IPublicModelPluginContext; + + constructor(props: any) { + super(props); + + // 模态管理对象 + this.pluginContext = props.pluginContext; + const { project } = this.pluginContext; + this.modalNodesManager = project.currentDocument?.modalNodesManager; + } + + hideAllNodes() { + this.modalNodesManager?.hideModalNodes(); + } + + render() { + const { treeNode } = this.props; + // 当指定了新的根节点时,要从原始的根节点去获取模态节点 + const { project } = this.pluginContext; + const rootNode = project.currentDocument?.root; + const rootTreeNode = treeNode.tree.getTreeNode(rootNode!); + const expanded = rootTreeNode.expanded; + + const hasVisibleModalNode = !!this.modalNodesManager?.getVisibleModalNode(); + return ( + <div className="tree-node-modal"> + <div className="tree-node-modal-title"> + <span>模态视图层</span> + <div + className="tree-node-modal-title-visible-icon" + onClick={this.hideAllNodes.bind(this)} + > + {hasVisibleModalNode ? <IconEyeClose /> : null} + </div> + </div> + <div className="tree-pane-modal-content"> + <TreeBranches + treeNode={rootTreeNode} + expanded={expanded} + isModal + pluginContext={this.pluginContext} + /> + </div> + </div> + ); + } +} export default class TreeNodeView extends Component<{ treeNode: TreeNode; isModal?: boolean; pluginContext: IPublicModelPluginContext; + isRootNode: boolean; }> { state = { expanded: false, @@ -20,64 +75,51 @@ export default class TreeNodeView extends Component<{ highlight: false, dropping: false, conditionFlow: false, + expandable: false, }; eventOffCallbacks: Array<() => void> = []; + constructor(props: any) { + super(props); - componentDidMount() { - const { treeNode, pluginContext } = this.props; - const { pluginEvent, event } = pluginContext; - const { id } = treeNode; - + const { treeNode, isRootNode } = this.props; this.state = { - expanded: false, - selected: false, - hidden: false, - locked: false, - detecting: false, + expanded: isRootNode ? true : treeNode.expanded, + selected: treeNode.selected, + hidden: treeNode.hidden, + locked: treeNode.locked, + detecting: treeNode.detecting, isRoot: treeNode.isRoot(), // 是否投放响应 dropping: treeNode.dropDetail?.index != null, conditionFlow: treeNode.node.conditionGroup != null, highlight: treeNode.isFocusingNode(), + expandable: treeNode.expandable, + }; + } + + componentDidMount() { + const { treeNode, pluginContext } = this.props; + const { event, project } = pluginContext; + + const doc = project.currentDocument; + + treeNode.onExpandedChanged = ((expanded: boolean) => { + this.setState({ expanded }); + }); + treeNode.onHiddenChanged = (hidden: boolean) => { + this.setState({ hidden }); + }; + treeNode.onLockedChanged = (locked: boolean) => { + this.setState({ locked }); }; - const doc = pluginContext.project.currentDocument; - - - this.eventOffCallbacks.push(pluginEvent.on('tree-node.hiddenChanged', (payload: any) => { - const { hidden, nodeId } = payload; - if (nodeId === id) { - this.setState({ hidden }); - } - })); - - this.eventOffCallbacks.push(pluginEvent.on('tree-node.expandedChanged', (payload: any) => { - const { expanded, nodeId } = payload; - if (nodeId === id) { - this.setState({ expanded }); - } - })); - - this.eventOffCallbacks.push(pluginEvent.on('tree-node.lockedChanged', (payload: any) => { - const { locked, nodeId } = payload; - if (nodeId === id) { - this.setState({ locked }); - } - })); - this.eventOffCallbacks.push( - event.on( - IPublicEnumEventNames.DOCUMENT_DROPLOCATION_CHANGED, - (payload: any) => { - const { document } = payload; - if (document.id === doc?.id) { - this.setState({ - dropping: treeNode.dropDetail?.index != null, - }); - } - }, - ), + doc?.onDropLocationChanged((document: IPublicModelDocumentModel) => { + this.setState({ + dropping: treeNode.dropDetail?.index != null, + }); + }), ); const offSelectionChange = doc?.selection?.onSelectionChange(() => { @@ -95,8 +137,25 @@ export default class TreeNodeView extends Component<{ }); } + shouldShowModalTreeNode(): boolean { + const { treeNode, isRootNode, pluginContext } = this.props; + if (!isRootNode) { + // 只在 当前树 的根节点展示模态节点 + return false; + } + + // 当指定了新的根节点时,要从原始的根节点去获取模态节点 + const { project } = pluginContext; + const rootNode = project.currentDocument?.root; + const rootTreeNode = treeNode.tree.getTreeNode(rootNode!); + const modalNodes = rootTreeNode.children?.filter((item) => { + return item.node.componentMeta?.isModal; + }); + return !!(modalNodes && modalNodes.length > 0); + } + render() { - const { treeNode, isModal } = this.props; + const { treeNode, isModal, isRootNode } = this.props; const className = classNames('tree-node', { // 是否展开 expanded: this.state.expanded, @@ -114,24 +173,39 @@ export default class TreeNodeView extends Component<{ 'condition-flow': this.state.conditionFlow, highlight: this.state.highlight, }); + let shouldShowModalTreeNode: boolean = this.shouldShowModalTreeNode(); + // filter 处理 const { filterWorking, matchChild, matchSelf } = treeNode.filterReult; - - // 条件过滤生效时,如果未命中本节点或子节点,则不展示该节点 - if (filterWorking && !matchChild && !matchSelf) { + if (!isRootNode && filterWorking && !matchChild && !matchSelf) { + // 条件过滤生效时,如果未命中本节点或子节点,则不展示该节点 + // 根节点始终展示 return null; } - return ( - <div className={className} data-id={treeNode.id}> + <div + className={className} + data-id={treeNode.id} + > <TreeTitle treeNode={treeNode} isModal={isModal} + expanded={this.state.expanded} + hidden={this.state.hidden} + locked={this.state.locked} + expandable={this.state.expandable} pluginContext={this.props.pluginContext} /> + {shouldShowModalTreeNode && + <ModalTreeNodeView + treeNode={treeNode} + pluginContext={this.props.pluginContext} + /> + } <TreeBranches treeNode={treeNode} isModal={false} + expanded={this.state.expanded} pluginContext={this.props.pluginContext} /> </div> diff --git a/packages/plugin-outline-pane/src/views/tree-title.tsx b/packages/plugin-outline-pane/src/views/tree-title.tsx index 93f822988..92fac1bba 100644 --- a/packages/plugin-outline-pane/src/views/tree-title.tsx +++ b/packages/plugin-outline-pane/src/views/tree-title.tsx @@ -21,6 +21,10 @@ function emitOutlineEvent(event: IPublicApiEvent, type: string, treeNode: TreeNo export default class TreeTitle extends Component<{ treeNode: TreeNode; isModal?: boolean; + expanded: boolean; + hidden: boolean; + locked: boolean; + expandable: boolean; pluginContext: IPublicModelPluginContext; }> { state: { @@ -73,30 +77,18 @@ export default class TreeTitle extends Component<{ // 光标定位最后一个 // input.selectionStart = input.selectionEnd; }; - offTitleLabelChanged: (() => void) | undefined; componentDidMount() { - const { treeNode, pluginContext } = this.props; - const { id } = treeNode; - const { pluginEvent } = pluginContext; + const { treeNode } = this.props; this.setState({ editing: false, title: treeNode.titleLabel, }); - this.offTitleLabelChanged = pluginEvent.on('tree-node.titleLabelChanged', (payload: any) => { - const { nodeId } = payload; - if (nodeId === id) { - this.setState({ - title: treeNode.titleLabel, - }); - } - }); - } - - componentWillUnmount(): void { - if (this.offTitleLabelChanged) { - this.offTitleLabelChanged(); - } + treeNode.onTitleLabelChanged = () => { + this.setState({ + title: treeNode.titleLabel, + }); + }; } render() { @@ -158,7 +150,7 @@ export default class TreeTitle extends Component<{ <IconRadio className="tree-node-modal-radio" /> </div> )} - {isCNode && <ExpandBtn treeNode={treeNode} pluginContext={this.props.pluginContext} />} + {isCNode && <ExpandBtn expandable={this.props.expandable} expanded={this.props.expanded} treeNode={treeNode} pluginContext={this.props.pluginContext} />} <div className="tree-node-icon">{createIcon(treeNode.icon)}</div> <div className="tree-node-title-label"> {editing ? ( @@ -200,8 +192,8 @@ export default class TreeTitle extends Component<{ </Fragment> )} </div> - {shouldShowHideBtn && <HideBtn treeNode={treeNode} pluginContext={this.props.pluginContext} />} - {shouldShowLockBtn && <LockBtn treeNode={treeNode} pluginContext={this.props.pluginContext} />} + {shouldShowHideBtn && <HideBtn hidden={this.props.hidden} treeNode={treeNode} pluginContext={this.props.pluginContext} />} + {shouldShowLockBtn && <LockBtn locked={this.props.locked} treeNode={treeNode} pluginContext={this.props.pluginContext} />} {shouldEditBtn && <RenameBtn treeNode={treeNode} pluginContext={this.props.pluginContext} onClick={this.enableEdit} /> } </div> @@ -233,39 +225,10 @@ class RenameBtn extends Component<{ class LockBtn extends Component<{ treeNode: TreeNode; pluginContext: IPublicModelPluginContext; -}, { locked: boolean; }> { - state = { - locked: false, - }; - offLockedChanged: () => void; - - componentDidMount(): void { - const { treeNode, pluginContext } = this.props; - const { id } = treeNode; - const { pluginEvent } = pluginContext; - - this.setState({ - locked: treeNode.locked, - }); - - this.offLockedChanged = pluginEvent.on('tree-node.lockedChanged', (payload: any) => { - const { locked, nodeId } = payload; - if (nodeId === id) { - this.setState({ locked }); - } - }); - } - - componentWillUnmount() { - if (this.offLockedChanged) { - this.offLockedChanged(); - } - } - render() { - const { treeNode } = this.props; + const { treeNode, locked } = this.props; const { intl, common } = this.props.pluginContext; const Tip = common.editorCabin.Tip; return ( @@ -273,11 +236,11 @@ class LockBtn extends Component<{ className="tree-node-lock-btn" onClick={(e) => { e.stopPropagation(); - treeNode.setLocked(!this.state.locked); + treeNode.setLocked(!locked); }} > - {this.state.locked ? <IconUnlock /> : <IconLock /> } - <Tip>{this.state.locked ? intl('Unlock') : intl('Lock')}</Tip> + {locked ? <IconUnlock /> : <IconLock /> } + <Tip>{locked ? intl('Unlock') : intl('Lock')}</Tip> </div> ); } @@ -285,35 +248,13 @@ class LockBtn extends Component<{ class HideBtn extends Component<{ treeNode: TreeNode; + hidden: boolean; pluginContext: IPublicModelPluginContext; }, { hidden: boolean; }> { - state = { - hidden: false, - }; - offHiddenChanged: () => void; - componentDidMount(): void { - const { treeNode, pluginContext } = this.props; - const { pluginEvent } = pluginContext; - const { id } = treeNode; - this.setState({ - hidden: treeNode.hidden, - }); - this.offHiddenChanged = pluginEvent.on('tree-node.hiddenChanged', (payload: any) => { - const { hidden, nodeId } = payload; - if (nodeId === id) { - this.setState({ hidden }); - } - }); - } - componentWillUnmount(): void { - if (this.offHiddenChanged) { - this.offHiddenChanged(); - } - } render() { - const { treeNode } = this.props; + const { treeNode, hidden } = this.props; const { intl, common } = this.props.pluginContext; const Tip = common.editorCabin.Tip; return ( @@ -321,12 +262,12 @@ class HideBtn extends Component<{ className="tree-node-hide-btn" onClick={(e) => { e.stopPropagation(); - emitOutlineEvent(this.props.pluginContext.event, this.state.hidden ? 'show' : 'hide', treeNode); - treeNode.setHidden(!this.state.hidden); + emitOutlineEvent(this.props.pluginContext.event, hidden ? 'show' : 'hide', treeNode); + treeNode.setHidden(!hidden); }} > - {this.state.hidden ? <IconEye /> : <IconEyeClose />} - <Tip>{this.state.hidden ? intl('Show') : intl('Hide')}</Tip> + {hidden ? <IconEye /> : <IconEyeClose />} + <Tip>{hidden ? intl('Show') : intl('Hide')}</Tip> </div> ); } @@ -336,52 +277,24 @@ class HideBtn extends Component<{ class ExpandBtn extends Component<{ treeNode: TreeNode; pluginContext: IPublicModelPluginContext; -}, { expanded: boolean; expandable: boolean; }> { - state = { - expanded: false, - expandable: false, - }; - - offExpandedChanged: () => void; - - componentDidMount(): void { - const { treeNode, pluginContext } = this.props; - const { id } = treeNode; - const { pluginEvent } = pluginContext; - this.setState({ - expanded: treeNode.expanded, - expandable: treeNode.expandable, - }); - this.offExpandedChanged = pluginEvent.on('tree-node.expandedChanged', (payload: any) => { - const { expanded, nodeId } = payload; - if (nodeId === id) { - this.setState({ expanded }); - } - }); - } - componentWillUnmount(): void { - if (this.offExpandedChanged) { - this.offExpandedChanged(); - } - } render() { - const { treeNode } = this.props; - if (!this.state.expandable) { + const { treeNode, expanded, expandable } = this.props; + if (!expandable) { return <i className="tree-node-expand-placeholder" />; } return ( <div className="tree-node-expand-btn" onClick={(e) => { - if (this.state.expanded) { + if (expanded) { e.stopPropagation(); } - emitOutlineEvent(this.props.pluginContext.event, this.state.expanded ? 'collapse' : 'expand', treeNode); - treeNode.setExpanded(!this.state.expanded); + emitOutlineEvent(this.props.pluginContext.event, expanded ? 'collapse' : 'expand', treeNode); + treeNode.setExpanded(!expanded); }} > <IconArrowRight size="small" /> diff --git a/packages/plugin-outline-pane/src/views/tree.tsx b/packages/plugin-outline-pane/src/views/tree.tsx index bc4704b43..4eb1dbfee 100644 --- a/packages/plugin-outline-pane/src/views/tree.tsx +++ b/packages/plugin-outline-pane/src/views/tree.tsx @@ -1,8 +1,8 @@ import { Component, MouseEvent as ReactMouseEvent } from 'react'; import { isFormEvent, canClickNode, isShaken } from '@alilc/lowcode-utils'; import { Tree } from '../controllers/tree'; -import RootTreeNodeView from './root-tree-node'; -import { IPublicEnumDragObjectType, IPublicModelPluginContext, IPublicModelNode, IPublicEnumEventNames } from '@alilc/lowcode-types'; +import TreeNodeView from './tree-node'; +import { IPublicEnumDragObjectType, IPublicModelPluginContext, IPublicModelNode } from '@alilc/lowcode-types'; function getTreeNodeIdByEvent(e: ReactMouseEvent, stop: Element): null | string { let target: Element | null = e.target as Element; @@ -81,6 +81,9 @@ export default class TreeView extends Component<{ private onDoubleClick = (e: ReactMouseEvent) => { e.preventDefault(); const treeNode = this.getTreeNodeFromEvent(e); + if (treeNode?.id === this.state.root?.id) { + return; + } if (!treeNode?.expanded) { this.props.tree.expandAllDecendants(treeNode); } else { @@ -173,17 +176,13 @@ export default class TreeView extends Component<{ componentDidMount() { const { tree, pluginContext } = this.props; const { root } = tree; - const { event, project } = pluginContext; + const { project } = pluginContext; this.setState({ root }); const doc = project.currentDocument; - // root 变化 - event.on(IPublicEnumEventNames.DOCUMENT_FOCUS_NODE_CHANGED, (payload: any) => { - const { document } = payload; - if (document.id === doc?.id) { - this.setState({ - root: tree.root, - }); - } + doc?.onFocusNodeChanged(() => { + this.setState({ + root: tree.root, + }); }); } @@ -201,7 +200,12 @@ export default class TreeView extends Component<{ onDoubleClick={this.onDoubleClick} onMouseLeave={this.onMouseLeave} > - <RootTreeNodeView key={this.state.root?.id} treeNode={this.state.root} pluginContext={this.props.pluginContext} /> + <TreeNodeView + key={this.state.root?.id} + treeNode={this.state.root} + pluginContext={this.props.pluginContext} + isRootNode + /> </div> ); } diff --git a/packages/shell/src/api/canvas.ts b/packages/shell/src/api/canvas.ts index eb3b7e4cf..6c7040311 100644 --- a/packages/shell/src/api/canvas.ts +++ b/packages/shell/src/api/canvas.ts @@ -13,9 +13,11 @@ import { import { ScrollTarget as InnerScrollTarget, } from '@alilc/lowcode-designer'; -import { editorSymbol, designerSymbol } from '../symbols'; +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; @@ -40,7 +42,10 @@ export class Canvas implements IPublicApiCanvas { * 创建插入位置,考虑放到 dragon 中 */ createLocation(locationData: IPublicTypeLocationData): IPublicModelDropLocation { - return this[designerSymbol].createLocation(locationData); + return this[designerSymbol].createLocation({ + ...locationData, + target: (locationData.target as any)[nodeSymbol], + }); } get dragon(): IPublicModelDragon | null { @@ -48,8 +53,10 @@ export class Canvas implements IPublicApiCanvas { } get activeTracker(): IPublicModelActiveTracker | null { - return this[designerSymbol].activeTracker; + const activeTracker = new ActiveTracker(this[designerSymbol].activeTracker); + return activeTracker; } + /** * @deprecated */ diff --git a/packages/shell/src/api/event.ts b/packages/shell/src/api/event.ts index 9b7f765ab..1f82d5f87 100644 --- a/packages/shell/src/api/event.ts +++ b/packages/shell/src/api/event.ts @@ -1,5 +1,5 @@ import { Editor as InnerEditor, EventBus } from '@alilc/lowcode-editor-core'; -import { getLogger, isPublicEventName, isPluginEventName } from '@alilc/lowcode-utils'; +import { getLogger, isPluginEventName } from '@alilc/lowcode-utils'; import { IPublicApiEvent, IPublicTypeDisposable } from '@alilc/lowcode-types'; const logger = getLogger({ level: 'warn', bizName: 'shell-event' }); @@ -34,10 +34,10 @@ export class Event implements IPublicApiEvent { * @param listener 事件回调 */ on(event: string, listener: (...args: any[]) => void): IPublicTypeDisposable { - if (isPluginEventName(event) || isPublicEventName(event)) { + if (isPluginEventName(event)) { return this[eventBusSymbol].on(event, listener); } else { - logger.warn(`fail to monitor on event ${event}, which is neither a engine public event nor a plugin event`); + logger.warn(`fail to monitor on event ${event}, event should have a prefix like 'somePrefix:eventName'`); return () => {}; } } diff --git a/packages/shell/src/api/project.ts b/packages/shell/src/api/project.ts index 31c3277d4..9cae7d72e 100644 --- a/packages/shell/src/api/project.ts +++ b/packages/shell/src/api/project.ts @@ -11,7 +11,6 @@ import { IPublicApiSimulatorHost, IPublicModelDocumentModel, IPublicTypePropsTransducer, - IPublicEnumEventNames, IPublicEnumTransformStage, IPublicTypeDisposable, } from '@alilc/lowcode-types'; @@ -166,7 +165,7 @@ export class Project implements IPublicApiProject { */ onRemoveDocument(fn: (data: { id: string}) => void): IPublicTypeDisposable { return this[editorSymbol].eventBus.on( - IPublicEnumEventNames.DESIGNER_DOCUMENT_REMOVE, + 'designer.document.remove', (data: { id: string }) => fn(data), ); } diff --git a/packages/shell/src/model/active-tracker.ts b/packages/shell/src/model/active-tracker.ts new file mode 100644 index 000000000..0c9c42efe --- /dev/null +++ b/packages/shell/src/model/active-tracker.ts @@ -0,0 +1,36 @@ +import { IPublicModelActiveTracker, IPublicModelNode, IPublicTypeActiveTarget } from '@alilc/lowcode-types'; +import { IActiveTracker } from '@alilc/lowcode-designer'; +import { Node } from './node'; +import { nodeSymbol } from '../symbols'; + +const activeTrackerSymbol = Symbol('activeTracker'); + + +export class ActiveTracker implements IPublicModelActiveTracker { + private readonly [activeTrackerSymbol]: IActiveTracker; + + + constructor(innerTracker: IActiveTracker) { + this[activeTrackerSymbol] = innerTracker; + } + + onChange(fn: (target: IPublicTypeActiveTarget) => void): () => void { + if (!fn) { + return () => {}; + } + return this[activeTrackerSymbol].onChange((t: IPublicTypeActiveTarget) => { + const { node: innerNode, detail, instance } = t; + const publicNode = Node.create(innerNode); + const publicActiveTarget = { + node: publicNode!, + detail, + instance, + }; + fn(publicActiveTarget); + }); + } + + track(node: IPublicModelNode) { + this[activeTrackerSymbol].track((node as any)[nodeSymbol]); + } +} \ 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 c48d4c6ac..1d31d8132 100644 --- a/packages/shell/src/model/document-model.ts +++ b/packages/shell/src/model/document-model.ts @@ -22,8 +22,8 @@ import { IPublicModelModalNodesManager, IPublicTypePropChangeOptions, IPublicModelDropLocation, - IPublicEnumEventNames, IPublicApiCanvas, + IPublicTypeDisposable, } from '@alilc/lowcode-types'; import { Node } from './node'; import { Selection } from './selection'; @@ -108,7 +108,7 @@ export class DocumentModel implements IPublicModelDocumentModel { set focusNode(node: IPublicModelNode | null) { this._focusNode = node; this[editorSymbol].eventBus.emit( - IPublicEnumEventNames.DOCUMENT_FOCUS_NODE_CHANGED, + 'shell.document.focusNodeChanged', { document: this, focusNode: node }, ); } @@ -157,7 +157,7 @@ export class DocumentModel implements IPublicModelDocumentModel { */ importSchema(schema: IPublicTypeRootSchema): void { this[documentSymbol].import(schema); - this[editorSymbol].eventBus.emit(IPublicEnumEventNames.DOCUMENT_IMPORT_SCHEMA, schema); + this[editorSymbol].eventBus.emit('shell.document.importSchema', schema); } /** @@ -241,7 +241,7 @@ export class DocumentModel implements IPublicModelDocumentModel { /** * 当前 document 新增节点事件 */ - onAddNode(fn: (node: IPublicModelNode) => void): () => void { + onAddNode(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable { return this[documentSymbol].onNodeCreate((node: InnerNode) => { fn(Node.create(node)!); }); @@ -250,7 +250,7 @@ export class DocumentModel implements IPublicModelDocumentModel { /** * 当前 document 新增节点事件,此时节点已经挂载到 document 上 */ - onMountNode(fn: (payload: { node: IPublicModelNode }) => void): () => void { + onMountNode(fn: (payload: { node: IPublicModelNode }) => void): IPublicTypeDisposable { this[editorSymbol].eventBus.on('node.add', fn as any); return () => { this[editorSymbol].eventBus.off('node.add', fn as any); @@ -260,7 +260,7 @@ export class DocumentModel implements IPublicModelDocumentModel { /** * 当前 document 删除节点事件 */ - onRemoveNode(fn: (node: IPublicModelNode) => void): () => void { + onRemoveNode(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable { return this[documentSymbol].onNodeDestroy((node: InnerNode) => { fn(Node.create(node)!); }); @@ -269,7 +269,7 @@ export class DocumentModel implements IPublicModelDocumentModel { /** * 当前 document 的 hover 变更事件 */ - onChangeDetecting(fn: (node: IPublicModelNode) => void): () => void { + onChangeDetecting(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable { return this[documentSymbol].designer.detecting.onDetectingChange((node: InnerNode) => { fn(Node.create(node)!); }); @@ -278,7 +278,7 @@ export class DocumentModel implements IPublicModelDocumentModel { /** * 当前 document 的选中变更事件 */ - onChangeSelection(fn: (ids: string[]) => void): () => void { + onChangeSelection(fn: (ids: string[]) => void): IPublicTypeDisposable { return this[documentSymbol].selection.onSelectionChange((ids: string[]) => { fn(ids); }); @@ -338,11 +338,39 @@ export class DocumentModel implements IPublicModelDocumentModel { * import schema event * @param fn */ - onImportSchema(fn: (schema: IPublicTypeRootSchema) => void): void { - this[editorSymbol].on(IPublicEnumEventNames.DOCUMENT_IMPORT_SCHEMA, fn as any); + onImportSchema(fn: (schema: IPublicTypeRootSchema) => void): IPublicTypeDisposable { + return this[editorSymbol].eventBus.on('shell.document.importSchema', fn as any); } isDetectingNode(node: IPublicModelNode): boolean { return this.detecting.current === node; } + + onFocusNodeChanged( + fn: (doc: IPublicModelDocumentModel, focusNode: IPublicModelNode) => void, + ): IPublicTypeDisposable { + if (!fn) { + return () => {}; + } + return this[editorSymbol].eventBus.on( + 'shell.document.focusNodeChanged', + (payload) => { + const { document, focusNode } = payload; + fn(document, focusNode); + }, + ); + } + + onDropLocationChanged(fn: (doc: IPublicModelDocumentModel) => void): IPublicTypeDisposable { + if (!fn) { + return () => {}; + } + return this[editorSymbol].eventBus.on( + 'document.dropLocation.changed', + (payload) => { + const { document } = payload; + fn(document); + }, + ); + } } diff --git a/packages/shell/src/model/dragon.ts b/packages/shell/src/model/dragon.ts index d9c79239d..38fa6323e 100644 --- a/packages/shell/src/model/dragon.ts +++ b/packages/shell/src/model/dragon.ts @@ -1,7 +1,7 @@ import { ILocateEvent as InnerLocateEvent, } from '@alilc/lowcode-designer'; -import { dragonSymbol } from '../symbols'; +import { dragonSymbol, nodeSymbol } from '../symbols'; import LocateEvent from './locate-event'; import { DragObject } from './drag-object'; import { globalContext } from '@alilc/lowcode-editor-core'; @@ -98,7 +98,10 @@ export class Dragon implements IPublicModelDragon { * @param boostEvent 拖拽初始时事件 */ boost(dragObject: DragObject, boostEvent: MouseEvent | DragEvent, fromRglNode?: Node | IPublicModelNode): void { - return this[dragonSymbol].boost(dragObject, boostEvent, fromRglNode); + return this[dragonSymbol].boost({ + ...dragObject, + nodes: dragObject.nodes.map((node: any) => node[nodeSymbol]), + }, boostEvent, fromRglNode); } /** diff --git a/packages/types/src/shell/enum/event-names.ts b/packages/types/src/shell/enum/event-names.ts index ad8569e9a..1bb8682d4 100644 --- a/packages/types/src/shell/enum/event-names.ts +++ b/packages/types/src/shell/enum/event-names.ts @@ -6,10 +6,4 @@ */ // eslint-disable-next-line no-shadow export enum IPublicEnumEventNames { - DOCUMENT_IMPORT_SCHEMA = 'shell.document.importSchema', - DOCUMENT_FOCUS_NODE_CHANGED = 'shell.document.focusNodeChanged', - SKELETON_PANEL_SHOW = 'skeleton.panel.show', - SKELETON_PANEL_HIDE = 'skeleton.panel.hide', - DESIGNER_DOCUMENT_REMOVE = 'designer.document.remove', - DOCUMENT_DROPLOCATION_CHANGED = 'document.dropLocation.changed', } \ No newline at end of file diff --git a/packages/types/src/shell/model/active-tracker.ts b/packages/types/src/shell/model/active-tracker.ts index 5229d16f1..b0ceec11f 100644 --- a/packages/types/src/shell/model/active-tracker.ts +++ b/packages/types/src/shell/model/active-tracker.ts @@ -1,5 +1,8 @@ import { IPublicTypeActiveTarget } from '../type'; +import { IPublicModelNode } from './node'; export interface IPublicModelActiveTracker { onChange(fn: (target: IPublicTypeActiveTarget) => void): () => void; + + track(node: IPublicModelNode): void; } diff --git a/packages/types/src/shell/model/document-model.ts b/packages/types/src/shell/model/document-model.ts index 08bdf2789..2ba3f8b67 100644 --- a/packages/types/src/shell/model/document-model.ts +++ b/packages/types/src/shell/model/document-model.ts @@ -1,4 +1,4 @@ -import { IPublicTypeRootSchema, IPublicTypeDragNodeDataObject, IPublicTypeDragNodeObject, IPublicTypePropChangeOptions } from '../type'; +import { IPublicTypeRootSchema, IPublicTypeDragNodeDataObject, IPublicTypeDragNodeObject, IPublicTypePropChangeOptions, IPublicTypeDisposable } from '../type'; import { IPublicEnumTransformStage } from '../enum'; import { IPublicApiProject } from '../api'; import { IPublicModelDropLocation, IPublicModelDetecting, IPublicModelNode, IPublicModelSelection, IPublicModelHistory, IPublicModelModalNodesManager } from './'; @@ -114,27 +114,27 @@ export interface IPublicModelDocumentModel { /** * 当前 document 新增节点事件 */ - onAddNode(fn: (node: IPublicModelNode) => void): () => void; + onAddNode(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable; /** * 当前 document 新增节点事件,此时节点已经挂载到 document 上 */ - onMountNode(fn: (payload: { node: IPublicModelNode }) => void): () => void; + onMountNode(fn: (payload: { node: IPublicModelNode }) => void): IPublicTypeDisposable; /** * 当前 document 删除节点事件 */ - onRemoveNode(fn: (node: IPublicModelNode) => void): () => void; + onRemoveNode(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable; /** * 当前 document 的 hover 变更事件 */ - onChangeDetecting(fn: (node: IPublicModelNode) => void): () => void; + onChangeDetecting(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable; /** * 当前 document 的选中变更事件 */ - onChangeSelection(fn: (ids: string[]) => void): () => void; + onChangeSelection(fn: (ids: string[]) => void): IPublicTypeDisposable; /** * 当前 document 的节点显隐状态变更事件 @@ -152,16 +152,48 @@ export interface IPublicModelDocumentModel { /** * import schema event * @param fn + * @since v1.0.15 */ - onImportSchema(fn: (schema: IPublicTypeRootSchema) => void): void; + onImportSchema(fn: (schema: IPublicTypeRootSchema) => void): IPublicTypeDisposable; + /** + * 判断是否当前节点处于被探测状态 + * check is node being detected + * @param node + * @since v1.1.0 + */ isDetectingNode(node: IPublicModelNode): boolean; /** - * TODO: 待补充说明 + * 获取当前的 DropLocation 信息 + * get current drop location + * @since v1.1.0 */ get dropLocation(): IPublicModelDropLocation; - + /** + * 设置当前的 DropLocation 信息 + * set current drop location + * @since v1.1.0 + */ set dropLocation(loc: IPublicModelDropLocation | null); + + + /** + * 设置聚焦节点变化的回调 + * triggered focused node is set mannually from plugin + * @param fn + * @since v1.1.0 + */ + onFocusNodeChanged( + fn: (doc: IPublicModelDocumentModel, focusNode: IPublicModelNode) => void, + ): IPublicTypeDisposable; + + /** + * 设置 DropLocation 变化的回调 + * triggered when drop location changed + * @param fn + * @since v1.1.0 + */ + onDropLocationChanged(fn: (doc: IPublicModelDocumentModel) => void): IPublicTypeDisposable; } diff --git a/packages/types/src/shell/model/drop-location.ts b/packages/types/src/shell/model/drop-location.ts index 746850b1f..d1e57ccbf 100644 --- a/packages/types/src/shell/model/drop-location.ts +++ b/packages/types/src/shell/model/drop-location.ts @@ -1,3 +1,10 @@ +import { IPublicTypeLocationDetail } from '../type'; +import { IPublicModelLocateEvent } from './'; + export interface IPublicModelDropLocation { get target(): any; + + readonly detail: IPublicTypeLocationDetail; + + clone(event: IPublicModelLocateEvent): IPublicModelDropLocation; } diff --git a/packages/utils/src/asset.ts b/packages/utils/src/asset.ts index 85908c827..4e5a360b8 100644 --- a/packages/utils/src/asset.ts +++ b/packages/utils/src/asset.ts @@ -16,7 +16,10 @@ export function isAssetBundle(obj: any): obj is AssetBundle { return obj && obj.type === AssetType.Bundle; } -export function assetBundle(assets?: Asset | AssetList | null, level?: AssetLevel): AssetBundle | null { +export function assetBundle( + assets?: Asset | AssetList | null, + level?: AssetLevel, + ): AssetBundle | null { if (!assets) { return null; } diff --git a/packages/utils/src/build-components.ts b/packages/utils/src/build-components.ts index b58658972..9ed278996 100644 --- a/packages/utils/src/build-components.ts +++ b/packages/utils/src/build-components.ts @@ -1,10 +1,10 @@ import { ComponentType, forwardRef, createElement, FunctionComponent } from 'react'; import { IPublicTypeNpmInfo, IPublicTypeComponentSchema } from '@alilc/lowcode-types'; -import { Component } from '@alilc/lowcode-designer'; import { isESModule } from './is-es-module'; import { isReactComponent, acceptsRef, wrapReactClass } from './is-react'; import { isObject } from './is-object'; +type Component = ComponentType<any> | object; interface LibraryMap { [key: string]: string; } diff --git a/packages/utils/src/create-content.ts b/packages/utils/src/create-content.ts index 211c26f16..09a368d2b 100644 --- a/packages/utils/src/create-content.ts +++ b/packages/utils/src/create-content.ts @@ -1,7 +1,10 @@ import { ReactNode, ComponentType, isValidElement, cloneElement, createElement } from 'react'; import { isReactComponent } from './is-react'; -export function createContent(content: ReactNode | ComponentType<any>, props?: Record<string, unknown>): ReactNode { +export function createContent( + content: ReactNode | ComponentType<any>, + props?: Record<string, unknown>, + ): ReactNode { if (isValidElement(content)) { return props ? cloneElement(content, props) : content; } diff --git a/packages/utils/src/create-icon.tsx b/packages/utils/src/create-icon.tsx index ef22d4b1e..0f2500529 100644 --- a/packages/utils/src/create-icon.tsx +++ b/packages/utils/src/create-icon.tsx @@ -6,7 +6,10 @@ import { isESModule } from './is-es-module'; const URL_RE = /^(https?:)\/\//i; -export function createIcon(icon?: IPublicTypeIconType | null, props?: Record<string, unknown>): ReactNode { +export function createIcon( + icon?: IPublicTypeIconType | null, + props?: Record<string, unknown>, + ): ReactNode { if (!icon) { return null; } diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 950647639..e3f70ff80 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -26,7 +26,6 @@ export * from './node-helper'; export * from './clone-enumerable-property'; export * from './logger'; export * from './is-shaken'; -export * from './is-public-event-name'; export * from './is-plugin-event-name'; export * as css from './css-helper'; export { transactionManager } from './transaction-manager'; diff --git a/packages/utils/src/is-public-event-name.ts b/packages/utils/src/is-public-event-name.ts deleted file mode 100644 index 7d225c663..000000000 --- a/packages/utils/src/is-public-event-name.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IPublicEnumEventNames } from '@alilc/lowcode-types'; - -export function isPublicEventName(eventName: string): boolean { - for (const publicEvent in IPublicEnumEventNames) { - if (eventName === (IPublicEnumEventNames as any)[publicEvent]) { - return true; - } - } - return false; -} \ No newline at end of file diff --git a/packages/utils/src/misc.ts b/packages/utils/src/misc.ts index 8a8211250..4510e8643 100644 --- a/packages/utils/src/misc.ts +++ b/packages/utils/src/misc.ts @@ -1,8 +1,8 @@ import { isI18NObject } from './is-object'; import { get } from 'lodash'; -import { ComponentMeta } from '@alilc/lowcode-designer'; -import { TransformStage } from '@alilc/lowcode-types'; +import { IPublicEnumTransformStage, IPublicModelComponentMeta } from '@alilc/lowcode-types'; + interface Variable { type: 'variable'; variable: string; @@ -65,7 +65,7 @@ export function arrShallowEquals(arr1: any[], arr2: any[]): boolean { * 判断当前 meta 是否从 vc prototype 转换而来 * @param meta */ - export function isFromVC(meta: ComponentMeta) { + export function isFromVC(meta: IPublicModelComponentMeta) { return !!meta?.getMetadata().configure?.advanced; } @@ -86,12 +86,12 @@ const stageList = [ * @param stage * @returns */ -export function compatStage(stage: TransformStage | number): TransformStage { +export function compatStage(stage: IPublicEnumTransformStage | number): IPublicEnumTransformStage { if (typeof stage === 'number') { - console.warn('stage 直接指定为数字的使用方式已经过时,将在下一版本移除,请直接使用 TransformStage.Render|Serilize|Save|Clone|Init|Upgrade'); - return stageList[stage - 1] as TransformStage; + console.warn('stage 直接指定为数字的使用方式已经过时,将在下一版本移除,请直接使用 IPublicEnumTransformStage.Render|Serilize|Save|Clone|Init|Upgrade'); + return stageList[stage - 1] as IPublicEnumTransformStage; } - return stage as TransformStage; + return stage as IPublicEnumTransformStage; } export function invariant(check: any, message: string, thing?: any) { diff --git a/packages/utils/src/node-helper.ts b/packages/utils/src/node-helper.ts index 5f0547274..9d6573cdc 100644 --- a/packages/utils/src/node-helper.ts +++ b/packages/utils/src/node-helper.ts @@ -1,7 +1,10 @@ // 仅使用类型 -import { Node } from '@alilc/lowcode-designer'; +import { IPublicModelNode } from '@alilc/lowcode-types'; -export const getClosestNode = (node: Node, until: (node: Node) => boolean): Node | undefined => { +export const getClosestNode = ( + node: IPublicModelNode, + until: (n: IPublicModelNode) => boolean, + ): IPublicModelNode | undefined => { if (!node) { return undefined; } @@ -19,7 +22,7 @@ export const getClosestNode = (node: Node, until: (node: Node) => boolean): Node * @param {unknown} e 点击事件 * @returns {boolean} 是否可点击,true表示可点击 */ -export const canClickNode = (node: Node, e: unknown): boolean => { +export const canClickNode = (node: IPublicModelNode, e: unknown): boolean => { const onClickHook = node.componentMeta?.getMetadata().configure?.advanced?.callbacks?.onClickHook; const canClick = typeof onClickHook === 'function' ? onClickHook(e as MouseEvent, node) : true; return canClick; diff --git a/packages/workspace/src/base-context.ts b/packages/workspace/src/base-context.ts index af606ddf0..66c2966bd 100644 --- a/packages/workspace/src/base-context.ts +++ b/packages/workspace/src/base-context.ts @@ -28,11 +28,13 @@ import { Common, Logger, Workspace, + Canvas, } from '@alilc/lowcode-shell'; import { IPublicTypePluginMeta, } from '@alilc/lowcode-types'; import { getLogger } from '@alilc/lowcode-utils'; +import { OutlinePlugin } from '@alilc/lowcode-plugin-outline-pane'; import { setterRegistry } from '../../engine/src/inner-plugins/setter-registry'; import { componentMetaParser } from '../../engine/src/inner-plugins/component-meta-parser'; import defaultPanelRegistry from '../../engine/src/inner-plugins/default-panel-registry'; @@ -58,6 +60,7 @@ export class BasicContext { innerSkeleton: any; innerHotkey: InnerHotkey; innerPlugins: LowCodePluginManager; + canvas: Canvas; constructor(innerWorkspace: any, viewName: string, public editorWindow?: EditorWindow) { const editor = new Editor(viewName, true); @@ -84,6 +87,7 @@ export class BasicContext { const event = new Event(commonEvent, { prefix: 'common' }); const logger = getLogger({ level: 'warn', bizName: 'common' }); const skeleton = new Skeleton(innerSkeleton, 'any', true); + const canvas = new Canvas(editor); editor.set('setters', setters); editor.set('project', project); editor.set('material', material); @@ -103,6 +107,7 @@ export class BasicContext { this.innerHotkey = innerHotkey; this.editor = editor; this.designer = designer; + this.canvas = canvas; const common = new Common(editor, innerSkeleton); let plugins: any; @@ -120,6 +125,7 @@ export class BasicContext { context.common = common; context.plugins = plugins; context.logger = new Logger({ level: 'warn', bizName: `plugin:${pluginName}` }); + context.canvas = canvas; }, }; @@ -132,6 +138,7 @@ export class BasicContext { // 注册一批内置插件 this.registerInnerPlugins = async function registerPlugins() { + await plugins.register(OutlinePlugin, {}, { autoInit: true }); await plugins.register(componentMetaParser(designer)); await plugins.register(setterRegistry, {}, { autoInit: true }); await plugins.register(defaultPanelRegistry(editor, designer)); From aa1813d09b2815504c53444e56ba26cdf483be4b Mon Sep 17 00:00:00 2001 From: JackLian <jack.lianjie@gmail.com> Date: Mon, 26 Dec 2022 14:56:26 +0800 Subject: [PATCH 07/14] chore: publish docs 1.0.11 --- docs/docs/api/project.md | 2 +- docs/package.json | 2 +- docs/scripts/sync-oss.js | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/docs/api/project.md b/docs/docs/api/project.md index 0a22cd920..f0fa106de 100644 --- a/docs/docs/api/project.md +++ b/docs/docs/api/project.md @@ -18,7 +18,7 @@ sidebar_position: 3 - [历史操作 History)](./model/history) - [画布节点选中 Selection)](./model/selection) - [画布节点悬停 Detecting)](./model/detecting) -- [模态节点管理器 ModalNodesManager](./model/modal_nodes_manager) +- [模态节点管理器 ModalNodesManager](./model/modal-nodes-manager) 整个模型系统,以 project API 为入口,所有模型实例均需要通过 project 来获得,比如 project.currentDocument 来获取当前的文档模型,project.currentDocument.nodesMap 来获取当前文档模型里所有的节点列表。 diff --git a/docs/package.json b/docs/package.json index b5a6669fe..62747d46d 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-engine-docs", - "version": "1.0.10", + "version": "1.0.11", "description": "低代码引擎版本化文档", "license": "MIT", "files": [ diff --git a/docs/scripts/sync-oss.js b/docs/scripts/sync-oss.js index 8ab9402e8..9518a8ea7 100644 --- a/docs/scripts/sync-oss.js +++ b/docs/scripts/sync-oss.js @@ -4,7 +4,6 @@ const package = require('../package.json'); const { version, name } = package; const options = { method: 'PUT', - // 暂时使用 日常环境的 uipaas-node,上线后可切换成线上环境 https://uipaas-node.alibaba-inc.com hostname: 'uipaas-node.alibaba-inc.com', path: '/staticAssets/cdn/packages', headers: { From 37dffae1b8925a94888a8abba7e8bae9baf11a03 Mon Sep 17 00:00:00 2001 From: JackLian <jack.lianjie@gmail.com> Date: Mon, 26 Dec 2022 17:03:10 +0800 Subject: [PATCH 08/14] fix: expandable state not changed when node added/removed --- docs/docs/api/project.md | 2 +- .../src/controllers/tree-node.ts | 10 ++++++++- .../src/controllers/tree.ts | 22 ++++++++++++++++++- .../src/views/tree-node.tsx | 7 ++++-- scripts/sync-oss.js | 1 - 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/docs/docs/api/project.md b/docs/docs/api/project.md index f0fa106de..15fb3cb7e 100644 --- a/docs/docs/api/project.md +++ b/docs/docs/api/project.md @@ -5,7 +5,7 @@ sidebar_position: 3 ## 模块简介 引擎编排模块中包含多种模型,包括: -- [文档模型 DocumentModel)](./model/document-model) +- [文档模型 DocumentModel](./model/document-model) - [节点模型 Node](./model/node) - [节点孩子模型 NodeChildren](./model/node-children) - [属性模型 Prop](./model/prop) diff --git a/packages/plugin-outline-pane/src/controllers/tree-node.ts b/packages/plugin-outline-pane/src/controllers/tree-node.ts index b6063e724..32c21ede2 100644 --- a/packages/plugin-outline-pane/src/controllers/tree-node.ts +++ b/packages/plugin-outline-pane/src/controllers/tree-node.ts @@ -28,6 +28,7 @@ export default class TreeNode { onHiddenChanged: (hidden: boolean) => void; onLockedChanged: (locked: boolean) => void; onTitleLabelChanged: (treeNode: TreeNode) => void; + onExpandableChanged: (expandable: boolean) => void; get id(): string { return this.node.id; @@ -41,6 +42,13 @@ export default class TreeNode { return this.hasChildren() || this.hasSlots() || this.dropDetail?.index != null; } + /** + * 触发 onExpandableChanged 回调 + */ + notifyExpandableChanged(): void { + this.onExpandableChanged && this.onExpandableChanged(this.expandable); + } + /** * 插入"线"位置信息 */ @@ -96,7 +104,7 @@ export default class TreeNode { get detecting() { const doc = this.pluginContext.project.currentDocument; - return doc?.isDetectingNode(this.node); + return !!(doc?.isDetectingNode(this.node)); } get hidden(): boolean { diff --git a/packages/plugin-outline-pane/src/controllers/tree.ts b/packages/plugin-outline-pane/src/controllers/tree.ts index a4106088b..2256d45d9 100644 --- a/packages/plugin-outline-pane/src/controllers/tree.ts +++ b/packages/plugin-outline-pane/src/controllers/tree.ts @@ -17,7 +17,27 @@ export class Tree { constructor(pluginContext: IPublicModelPluginContext) { this.pluginContext = pluginContext; - this.id = this.pluginContext.project.currentDocument?.id; + const doc = this.pluginContext.project.currentDocument; + this.id = doc?.id; + + doc?.onMountNode((payload: {node: IPublicModelNode }) => { + const { node } = payload; + const parentNode = node.parent; + if (!parentNode) { + return; + } + const parentTreeNode = this.getTreeNodeById(parentNode.id); + parentTreeNode?.notifyExpandableChanged(); + }); + + doc?.onRemoveNode((node: IPublicModelNode) => { + const parentNode = node.parent; + if (!parentNode) { + return; + } + const parentTreeNode = this.getTreeNodeById(parentNode.id); + parentTreeNode?.notifyExpandableChanged(); + }); } setNodeSelected(nodeId: string): void { diff --git a/packages/plugin-outline-pane/src/views/tree-node.tsx b/packages/plugin-outline-pane/src/views/tree-node.tsx index 091530706..a6dc03f08 100644 --- a/packages/plugin-outline-pane/src/views/tree-node.tsx +++ b/packages/plugin-outline-pane/src/views/tree-node.tsx @@ -32,7 +32,7 @@ class ModalTreeNodeView extends Component<{ const { project } = this.pluginContext; const rootNode = project.currentDocument?.root; const rootTreeNode = treeNode.tree.getTreeNode(rootNode!); - const expanded = rootTreeNode.expanded; + const { expanded } = rootTreeNode; const hasVisibleModalNode = !!this.modalNodesManager?.getVisibleModalNode(); return ( @@ -100,7 +100,7 @@ export default class TreeNodeView extends Component<{ componentDidMount() { const { treeNode, pluginContext } = this.props; - const { event, project } = pluginContext; + const { project } = pluginContext; const doc = project.currentDocument; @@ -113,6 +113,9 @@ export default class TreeNodeView extends Component<{ treeNode.onLockedChanged = (locked: boolean) => { this.setState({ locked }); }; + treeNode.onExpandableChanged = (expandable: boolean) => { + this.setState({ expandable }); + }; this.eventOffCallbacks.push( doc?.onDropLocationChanged((document: IPublicModelDocumentModel) => { diff --git a/scripts/sync-oss.js b/scripts/sync-oss.js index 4b67bdca9..ba4a77be3 100644 --- a/scripts/sync-oss.js +++ b/scripts/sync-oss.js @@ -4,7 +4,6 @@ const package = require('../packages/engine/package.json'); const { version, name } = package; const options = { method: 'PUT', - // 暂时使用 日常环境的 uipaas-node,上线后可切换成线上环境 https://uipaas-node.alibaba-inc.com hostname: 'uipaas-node.alibaba-inc.com', path: '/staticAssets/cdn/packages', headers: { From a1828b1bbc90a98d847c109d88ba9690020b1334 Mon Sep 17 00:00:00 2001 From: liujuping <liujup@foxmail.com> Date: Mon, 26 Dec 2022 18:27:49 +0800 Subject: [PATCH 09/14] chore: update packages/workspace dependencies --- packages/workspace/package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/workspace/package.json b/packages/workspace/package.json index def218830..aac4afeff 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-workspace", - "version": "1.0.15", + "version": "1.0.18", "description": "Shell Layer for AliLowCodeEngine", "main": "lib/index.js", "private": true, @@ -16,11 +16,11 @@ }, "license": "MIT", "dependencies": { - "@alilc/lowcode-designer": "1.0.15", - "@alilc/lowcode-editor-core": "1.0.15", - "@alilc/lowcode-editor-skeleton": "1.0.15", - "@alilc/lowcode-types": "1.0.15", - "@alilc/lowcode-utils": "1.0.15", + "@alilc/lowcode-designer": "1.0.18", + "@alilc/lowcode-editor-core": "1.0.18", + "@alilc/lowcode-editor-skeleton": "1.0.18", + "@alilc/lowcode-types": "1.0.18", + "@alilc/lowcode-utils": "1.0.18", "classnames": "^2.2.6", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.5", From 1342481942ef791940eca9fca8a563b9c3b46f90 Mon Sep 17 00:00:00 2001 From: liujuping <liujup@foxmail.com> Date: Mon, 26 Dec 2022 21:37:15 +0800 Subject: [PATCH 10/14] chore: fix package/workspace build error --- lerna.json | 2 +- packages/engine/build.plugin.js | 1 - packages/engine/package.json | 1 + packages/engine/src/engine-core.ts | 18 +++++++++++------- packages/renderer-core/tests/setup.ts | 13 ------------- packages/shell/package.json | 1 + packages/workspace/package.json | 1 - packages/workspace/src/base-context.ts | 19 ++++--------------- packages/workspace/src/index.ts | 7 ++++++- packages/workspace/src/shell-model-factory.ts | 18 ------------------ scripts/build.sh | 1 + scripts/sync.sh | 3 ++- 12 files changed, 27 insertions(+), 58 deletions(-) delete mode 100644 packages/workspace/src/shell-model-factory.ts diff --git a/lerna.json b/lerna.json index af393cf27..dc8950db4 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "4.0.0", - "version": "1.0.18", + "version": "1.1.0", "npmClient": "yarn", "useWorkspaces": true, "packages": [ diff --git a/packages/engine/build.plugin.js b/packages/engine/build.plugin.js index cb0368d88..2881eb3ef 100644 --- a/packages/engine/build.plugin.js +++ b/packages/engine/build.plugin.js @@ -34,7 +34,6 @@ module.exports = ({ context, onGetWebpackConfig }) => { .use('babel-loader') .tap((options) => { const { plugins = [] } = options; - console.log('plugins', plugins); return { ...options, plugins: [ diff --git a/packages/engine/package.json b/packages/engine/package.json index 768fc93a8..c72c4a49b 100644 --- a/packages/engine/package.json +++ b/packages/engine/package.json @@ -27,6 +27,7 @@ "@alilc/lowcode-plugin-outline-pane": "1.0.18", "@alilc/lowcode-shell": "1.0.18", "@alilc/lowcode-utils": "1.0.18", + "@alilc/lowcode-workspace": "1.0.18", "react": "^16.8.1", "react-dom": "^16.8.1" }, diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index d8d81aefc..42019ad26 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -61,7 +61,16 @@ export * from './modules/lowcode-types'; registerDefaults(); -const innerWorkspace = new InnerWorkspace(); +async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: Plugins) { + // 注册一批内置插件 + await plugins.register(OutlinePlugin, {}, { autoInit: true }); + await plugins.register(componentMetaParser(designer)); + await plugins.register(setterRegistry, {}, { autoInit: true }); + await plugins.register(defaultPanelRegistry(editor, designer)); + await plugins.register(builtinHotkey); +} + +const innerWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory); const workspace = new Workspace(innerWorkspace); const editor = new Editor(); globalContext.register(editor, Editor); @@ -170,12 +179,7 @@ export async function init( } engineConfig.setEngineOptions(engineOptions as any); - // 注册一批内置插件 - await plugins.register(OutlinePlugin, {}, { autoInit: true }); - await plugins.register(componentMetaParser(designer)); - await plugins.register(setterRegistry, {}, { autoInit: true }); - await plugins.register(defaultPanelRegistry(editor, designer)); - await plugins.register(builtinHotkey); + await registryInnerPlugin(designer, editor, plugins); await plugins.init(pluginPreference as any); diff --git a/packages/renderer-core/tests/setup.ts b/packages/renderer-core/tests/setup.ts index 5c72dc71a..0d51f6bb5 100644 --- a/packages/renderer-core/tests/setup.ts +++ b/packages/renderer-core/tests/setup.ts @@ -1,16 +1,3 @@ -jest.mock('zen-logger', () => { - class Logger { - log() {} - error() {} - warn() {} - debug() {} - } - return { - __esModule: true, - default: Logger, - }; -}); - jest.mock('lodash', () => { const original = jest.requireActual('lodash'); diff --git a/packages/shell/package.json b/packages/shell/package.json index f0067b30b..7e2bce3fb 100644 --- a/packages/shell/package.json +++ b/packages/shell/package.json @@ -20,6 +20,7 @@ "@alilc/lowcode-editor-skeleton": "1.0.18", "@alilc/lowcode-types": "1.0.18", "@alilc/lowcode-utils": "1.0.18", + "@alilc/lowcode-workspace": "1.0.18", "classnames": "^2.2.6", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.5", diff --git a/packages/workspace/package.json b/packages/workspace/package.json index aac4afeff..74888c70b 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -3,7 +3,6 @@ "version": "1.0.18", "description": "Shell Layer for AliLowCodeEngine", "main": "lib/index.js", - "private": true, "module": "es/index.js", "files": [ "lib", diff --git a/packages/workspace/src/base-context.ts b/packages/workspace/src/base-context.ts index 66c2966bd..86b66fc31 100644 --- a/packages/workspace/src/base-context.ts +++ b/packages/workspace/src/base-context.ts @@ -16,7 +16,6 @@ import { import { Skeleton as InnerSkeleton, } from '@alilc/lowcode-editor-skeleton'; - import { Hotkey, Plugins, @@ -34,14 +33,8 @@ import { IPublicTypePluginMeta, } from '@alilc/lowcode-types'; import { getLogger } from '@alilc/lowcode-utils'; -import { OutlinePlugin } from '@alilc/lowcode-plugin-outline-pane'; -import { setterRegistry } from '../../engine/src/inner-plugins/setter-registry'; -import { componentMetaParser } from '../../engine/src/inner-plugins/component-meta-parser'; -import defaultPanelRegistry from '../../engine/src/inner-plugins/default-panel-registry'; -import { builtinHotkey } from '../../engine/src/inner-plugins/builtin-hotkey'; +import { Workspace as InnerWorkspace } from './index'; import { EditorWindow } from './editor-window/context'; -import { shellModelFactory } from './shell-model-factory'; - export class BasicContext { skeleton: Skeleton; plugins: Plugins; @@ -62,7 +55,7 @@ export class BasicContext { innerPlugins: LowCodePluginManager; canvas: Canvas; - constructor(innerWorkspace: any, viewName: string, public editorWindow?: EditorWindow) { + constructor(innerWorkspace: InnerWorkspace, viewName: string, public editorWindow?: EditorWindow) { const editor = new Editor(viewName, true); const innerSkeleton = new InnerSkeleton(editor, viewName); @@ -71,7 +64,7 @@ export class BasicContext { const designer: Designer = new Designer({ editor, viewName, - shellModelFactory, + shellModelFactory: innerWorkspace.shellModelFactory, }); editor.set('designer' as any, designer); @@ -138,11 +131,7 @@ export class BasicContext { // 注册一批内置插件 this.registerInnerPlugins = async function registerPlugins() { - await plugins.register(OutlinePlugin, {}, { autoInit: true }); - await plugins.register(componentMetaParser(designer)); - await plugins.register(setterRegistry, {}, { autoInit: true }); - await plugins.register(defaultPanelRegistry(editor, designer)); - await plugins.register(builtinHotkey); + await innerWorkspace.registryInnerPlugin(designer, editor, plugins); }; } } \ No newline at end of file diff --git a/packages/workspace/src/index.ts b/packages/workspace/src/index.ts index 7faad05f6..863f8d0b7 100644 --- a/packages/workspace/src/index.ts +++ b/packages/workspace/src/index.ts @@ -1,7 +1,9 @@ +import { Designer } from '@alilc/lowcode-designer'; import { Editor } from '@alilc/lowcode-editor-core'; import { Skeleton as InnerSkeleton, } from '@alilc/lowcode-editor-skeleton'; +import { Plugins } from '@alilc/lowcode-shell'; import { IPublicResourceOptions } from '@alilc/lowcode-types'; import { EditorWindow } from './editor-window/context'; import { Resource } from './resource'; @@ -14,7 +16,10 @@ export class Workspace { readonly editor = new Editor(); readonly skeleton = new InnerSkeleton(this.editor); - constructor() { + constructor( + readonly registryInnerPlugin: (designer: Designer, editor: Editor, plugins: Plugins) => Promise<void>, + readonly shellModelFactory: any, + ) { if (this.defaultResource) { this.window = new EditorWindow(this.defaultResource, this); } diff --git a/packages/workspace/src/shell-model-factory.ts b/packages/workspace/src/shell-model-factory.ts deleted file mode 100644 index 05c7b19cb..000000000 --- a/packages/workspace/src/shell-model-factory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { - Node as InnerNode, - SettingField as InnerSettingField, -} from '@alilc/lowcode-designer'; -import { IShellModelFactory, IPublicModelNode, IPublicModelSettingPropEntry } from '@alilc/lowcode-types'; -import { - Node, - SettingPropEntry, -} from '@alilc/lowcode-shell'; -class ShellModelFactory implements IShellModelFactory { - createNode(node: InnerNode | null | undefined): IPublicModelNode | null { - return Node.create(node); - } - createSettingPropEntry(prop: InnerSettingField): IPublicModelSettingPropEntry { - return SettingPropEntry.create(prop); - } -} -export const shellModelFactory = new ShellModelFactory(); \ No newline at end of file diff --git a/scripts/build.sh b/scripts/build.sh index bb203fa05..92b0a99c2 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -14,6 +14,7 @@ lerna run build \ --scope @alilc/lowcode-react-renderer \ --scope @alilc/lowcode-react-simulator-renderer \ --scope @alilc/lowcode-renderer-core \ + --scope @alilc/lowcode-workspace \ --scope @alilc/lowcode-engine \ --stream diff --git a/scripts/sync.sh b/scripts/sync.sh index 39a1f6e26..98895c723 100755 --- a/scripts/sync.sh +++ b/scripts/sync.sh @@ -14,4 +14,5 @@ tnpm sync @alilc/lowcode-rax-renderer tnpm sync @alilc/lowcode-rax-simulator-renderer tnpm sync @alilc/lowcode-react-renderer tnpm sync @alilc/lowcode-react-simulator-renderer -tnpm sync @alilc/lowcode-engine \ No newline at end of file +tnpm sync @alilc/lowcode-engine +tnpm sync @alilc/lowcode-workspace \ No newline at end of file From 1f8d91f85f2e98d39b1a7a571310af1112998f75 Mon Sep 17 00:00:00 2001 From: liujuping <liujup@foxmail.com> Date: Tue, 27 Dec 2022 18:24:10 +0800 Subject: [PATCH 11/14] feat: workspace mode supports webview type views --- docs/docs/api/model/window.md | 4 +- docs/docs/api/workspace.md | 2 + .../editor-skeleton/src/layouts/left-area.tsx | 3 ++ .../src/layouts/right-area.tsx | 3 ++ .../editor-skeleton/src/layouts/top-area.tsx | 3 ++ .../src/layouts/workbench.less | 1 + .../editor-skeleton/src/layouts/workbench.tsx | 18 ++++--- packages/engine/src/engine-core.ts | 2 +- .../inner-plugins/default-panel-registry.tsx | 2 +- packages/types/src/shell/model/window.ts | 4 +- .../types/src/shell/type/resource-options.ts | 10 +++- packages/workspace/src/base-context.ts | 1 + packages/workspace/src/editor-view/context.ts | 25 +++++++--- packages/workspace/src/editor-view/view.tsx | 6 ++- .../workspace/src/editor-window/context.ts | 4 ++ .../workspace/src/inner-plugins/webview.tsx | 49 +++++++++++++++++++ packages/workspace/src/resource.ts | 8 +-- 17 files changed, 119 insertions(+), 26 deletions(-) create mode 100644 packages/workspace/src/inner-plugins/webview.tsx diff --git a/docs/docs/api/model/window.md b/docs/docs/api/model/window.md index 8c2fa1802..177d3438a 100644 --- a/docs/docs/api/model/window.md +++ b/docs/docs/api/model/window.md @@ -16,8 +16,10 @@ sidebar_position: 12 ### importSchema(schema: IPublicTypeNodeSchema) 当前窗口导入 schema +相关类型:[IPublicTypeNodeSchema](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/node-schema.ts) + ### changeViewType(viewName: string) 修改当前窗口视图类型 -### async save +### async save() 调用当前窗口视图保存钩子 diff --git a/docs/docs/api/workspace.md b/docs/docs/api/workspace.md index 99d1fb6df..e710bceec 100644 --- a/docs/docs/api/workspace.md +++ b/docs/docs/api/workspace.md @@ -32,3 +32,5 @@ sidebar_position: 12 /** 注册资源 */ registerResourceType(resourceName: string, resourceType: 'editor', options: IPublicResourceOptions): void; ``` + +相关类型:[IPublicResourceOptions](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/resource-options.ts) diff --git a/packages/editor-skeleton/src/layouts/left-area.tsx b/packages/editor-skeleton/src/layouts/left-area.tsx index 1164c9799..cbeba5a5c 100644 --- a/packages/editor-skeleton/src/layouts/left-area.tsx +++ b/packages/editor-skeleton/src/layouts/left-area.tsx @@ -7,6 +7,9 @@ import { Area } from '../area'; export default class LeftArea extends Component<{ area: Area }> { render() { const { area } = this.props; + if (area.isEmpty()) { + return null; + } return ( <div className={classNames('lc-left-area', { 'lc-area-visible': area.visible, diff --git a/packages/editor-skeleton/src/layouts/right-area.tsx b/packages/editor-skeleton/src/layouts/right-area.tsx index 035be7e3a..4393eef55 100644 --- a/packages/editor-skeleton/src/layouts/right-area.tsx +++ b/packages/editor-skeleton/src/layouts/right-area.tsx @@ -8,6 +8,9 @@ import { Panel } from '../widget/panel'; export default class RightArea extends Component<{ area: Area<any, Panel> }> { render() { const { area } = this.props; + if (area.isEmpty()) { + return null; + } return ( <div className={classNames('lc-right-area engine-tabpane', { 'lc-area-visible': area.visible, diff --git a/packages/editor-skeleton/src/layouts/top-area.tsx b/packages/editor-skeleton/src/layouts/top-area.tsx index 91f026248..56949cbb7 100644 --- a/packages/editor-skeleton/src/layouts/top-area.tsx +++ b/packages/editor-skeleton/src/layouts/top-area.tsx @@ -7,6 +7,9 @@ import { Area } from '../area'; export default class TopArea extends Component<{ area: Area; itemClassName?: string }> { render() { const { area, itemClassName } = this.props; + if (area.isEmpty()) { + return null; + } return ( <div className={classNames('lc-top-area engine-actionpane', { 'lc-area-visible': area.visible, diff --git a/packages/editor-skeleton/src/layouts/workbench.less b/packages/editor-skeleton/src/layouts/workbench.less index 68098e941..af555c65e 100644 --- a/packages/editor-skeleton/src/layouts/workbench.less +++ b/packages/editor-skeleton/src/layouts/workbench.less @@ -154,6 +154,7 @@ body { &.active { z-index: 999; + background: #edeff3; } } } diff --git a/packages/editor-skeleton/src/layouts/workbench.tsx b/packages/editor-skeleton/src/layouts/workbench.tsx index 94781fb78..e50bec4ec 100644 --- a/packages/editor-skeleton/src/layouts/workbench.tsx +++ b/packages/editor-skeleton/src/layouts/workbench.tsx @@ -15,19 +15,25 @@ import { SkeletonContext } from '../context'; import { EditorConfig, PluginClassSet } from '@alilc/lowcode-types'; @observer -export class Workbench extends Component<{ skeleton: Skeleton; config?: EditorConfig; components?: PluginClassSet; className?: string; topAreaItemClassName?: string }> { +export class Workbench extends Component<{ + skeleton: Skeleton; + config?: EditorConfig; + components?: PluginClassSet; + className?: string; + topAreaItemClassName?: string; +}> { constructor(props: any) { super(props); const { config, components, skeleton } = this.props; skeleton.buildFromConfig(config, components); } - // componentDidCatch(error: any) { - // globalContext.get(Editor).emit('editor.skeleton.workbench.error', error); - // } - render() { - const { skeleton, className, topAreaItemClassName } = this.props; + const { + skeleton, + className, + topAreaItemClassName, + } = this.props; return ( <div className={classNames('lc-workbench', className)}> <SkeletonContext.Provider value={this.props.skeleton}> diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index 42019ad26..99286c3c3 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -66,7 +66,7 @@ async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: await plugins.register(OutlinePlugin, {}, { autoInit: true }); await plugins.register(componentMetaParser(designer)); await plugins.register(setterRegistry, {}, { autoInit: true }); - await plugins.register(defaultPanelRegistry(editor, designer)); + await plugins.register(defaultPanelRegistry(editor)); await plugins.register(builtinHotkey); } diff --git a/packages/engine/src/inner-plugins/default-panel-registry.tsx b/packages/engine/src/inner-plugins/default-panel-registry.tsx index 9ef1db7cb..d7549d764 100644 --- a/packages/engine/src/inner-plugins/default-panel-registry.tsx +++ b/packages/engine/src/inner-plugins/default-panel-registry.tsx @@ -3,7 +3,7 @@ import { SettingsPrimaryPane } from '@alilc/lowcode-editor-skeleton'; import DesignerPlugin from '@alilc/lowcode-plugin-designer'; // 注册默认的面板 -export const defaultPanelRegistry = (editor: any, designer: any) => { +export const defaultPanelRegistry = (editor: any) => { const fun = (ctx: IPublicModelPluginContext) => { return { init() { diff --git a/packages/types/src/shell/model/window.ts b/packages/types/src/shell/model/window.ts index 4ffed5d07..f0faedbcd 100644 --- a/packages/types/src/shell/model/window.ts +++ b/packages/types/src/shell/model/window.ts @@ -8,7 +8,5 @@ export interface IPublicModelWindow { changeViewType(viewName: string): void; /** 调用当前窗口视图保存钩子 */ - save(): Promise<{ - [viewName: string]: IPublicTypeNodeSchema | any; - }>; + save(): Promise<any>; } \ 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 index 63e8e4add..94547bb57 100644 --- a/packages/types/src/shell/type/resource-options.ts +++ b/packages/types/src/shell/type/resource-options.ts @@ -8,6 +8,8 @@ export interface IPublicViewFunctions { export interface IPublicEditorView { /** 资源名字 */ viewName: string; + /** 资源类型 */ + viewType?: 'editor' | 'webview'; (ctx: any): IPublicViewFunctions; } @@ -25,8 +27,12 @@ export interface IPublicResourceOptions { editorViews: IPublicEditorView[]; /** save 钩子 */ - save?: () => Promise<void>; + save?: (schema: { + [viewName: string]: any; + }) => Promise<void>; /** import 钩子 */ - import?: () => Promise<void>; + import?: (schema: any) => Promise<{ + [viewName: string]: any; + }>; } \ No newline at end of file diff --git a/packages/workspace/src/base-context.ts b/packages/workspace/src/base-context.ts index 86b66fc31..047e5179e 100644 --- a/packages/workspace/src/base-context.ts +++ b/packages/workspace/src/base-context.ts @@ -35,6 +35,7 @@ import { import { getLogger } from '@alilc/lowcode-utils'; import { Workspace as InnerWorkspace } from './index'; import { EditorWindow } from './editor-window/context'; + export class BasicContext { skeleton: Skeleton; plugins: Plugins; diff --git a/packages/workspace/src/editor-view/context.ts b/packages/workspace/src/editor-view/context.ts index 63f1e2965..913228674 100644 --- a/packages/workspace/src/editor-view/context.ts +++ b/packages/workspace/src/editor-view/context.ts @@ -1,16 +1,22 @@ import { makeObservable, obx } from '@alilc/lowcode-editor-core'; -import { EditorViewOptions, EditorWindow, ViewFunctions } from '@alilc/lowcode-workspace'; +import { IPublicEditorView, IPublicViewFunctions } from '@alilc/lowcode-types'; import { flow } from 'mobx'; +import { Workspace as InnerWorkspace } from '../'; import { BasicContext } from '../base-context'; +import { EditorWindow } from '../editor-window/context'; +import { getWebviewPlugin } from '../inner-plugins/webview'; export class Context extends BasicContext { - name = 'editor-view'; + viewName = 'editor-view'; - instance: ViewFunctions; + instance: IPublicViewFunctions; - constructor(public workspace: any, public editorWindow: EditorWindow, public editorView: EditorViewOptions) { + viewType: 'editor' | 'webview'; + + constructor(public workspace: InnerWorkspace, public editorWindow: EditorWindow, public editorView: IPublicEditorView) { super(workspace, editorView.viewName, editorWindow); - this.name = editorView.viewName; + this.viewType = editorView.viewType || 'editor'; + this.viewName = editorView.viewName; this.instance = editorView(this.innerPlugins._getLowCodePluginContext({ pluginName: 'any', })); @@ -31,8 +37,13 @@ export class Context extends BasicContext { @obx isInit: boolean = false; init = flow(function* (this: any) { - yield this.registerInnerPlugins(); - yield this.instance?.init(); + if (this.viewType === 'webview') { + const url = yield this.instance?.url?.(); + yield this.plugins.register(getWebviewPlugin(url, this.viewName)); + } else { + yield this.registerInnerPlugins(); + } + yield this.instance?.init?.(); yield this.innerPlugins.init(); this.isInit = true; }); diff --git a/packages/workspace/src/editor-view/view.tsx b/packages/workspace/src/editor-view/view.tsx index 7611a0010..ca3a08fb2 100644 --- a/packages/workspace/src/editor-view/view.tsx +++ b/packages/workspace/src/editor-view/view.tsx @@ -3,11 +3,15 @@ import { Workbench, } from '@alilc/lowcode-editor-skeleton'; import { Component } from 'react'; +import { Context } from './context'; export * from '../base-context'; @observer -export class EditorView extends Component<any, any> { +export class EditorView extends Component<{ + editorView: Context; + active: boolean; +}, any> { render() { const { active } = this.props; const editorView = this.props.editorView; diff --git a/packages/workspace/src/editor-window/context.ts b/packages/workspace/src/editor-window/context.ts index 31bbc88f3..ee680b140 100644 --- a/packages/workspace/src/editor-window/context.ts +++ b/packages/workspace/src/editor-window/context.ts @@ -12,6 +12,10 @@ export class EditorWindow { async importSchema(schema: any) { const newSchema = await this.resource.import(schema); + if (!newSchema) { + return; + } + Object.keys(newSchema).forEach(key => { const view = this.editorViews.get(key); view?.project.importSchema(newSchema[key]); diff --git a/packages/workspace/src/inner-plugins/webview.tsx b/packages/workspace/src/inner-plugins/webview.tsx new file mode 100644 index 000000000..be9c15f8b --- /dev/null +++ b/packages/workspace/src/inner-plugins/webview.tsx @@ -0,0 +1,49 @@ +import { IPublicModelPluginContext } from '@alilc/lowcode-types'; + +function DesignerView(props: { + url: string; + viewName: string; +}) { + return ( + <div className="lc-designer lowcode-plugin-designer"> + <div className="lc-project"> + <div className="lc-simulator-shell"> + <iframe + name={`webview-view-${props.viewName}`} + className="lc-simulator-content-frame" + style={{ + height: '100%', + width: '100%', + }} + src={props.url} + /> + </div> + </div> + </div> + ); +} + +export function getWebviewPlugin(url: string, viewName: string) { + function webviewPlugin(ctx: IPublicModelPluginContext) { + const { skeleton } = ctx; + return { + init() { + skeleton.add({ + area: 'mainArea', + name: 'designer', + type: 'Widget', + content: DesignerView, + contentProps: { + ctx, + url, + viewName, + }, + }); + }, + }; + } + + webviewPlugin.pluginName = '___webview_plugin___'; + + return webviewPlugin; +} diff --git a/packages/workspace/src/resource.ts b/packages/workspace/src/resource.ts index e04c02d09..a1147713e 100644 --- a/packages/workspace/src/resource.ts +++ b/packages/workspace/src/resource.ts @@ -1,7 +1,7 @@ -import { EditorViewOptions, ResourceOptions } from '.'; +import { IPublicEditorView, IPublicResourceOptions } from '@alilc/lowcode-types'; export class Resource { - constructor(options: ResourceOptions) { + constructor(options: IPublicResourceOptions) { if (options.editorViews) { options.editorViews.forEach((d: any) => { this.editorViewMap.set(d.viewName, d); @@ -11,9 +11,9 @@ export class Resource { this.options = options; } - options: ResourceOptions; + options: IPublicResourceOptions; - editorViewMap: Map<string, EditorViewOptions> = new Map<string, EditorViewOptions>(); + editorViewMap: Map<string, IPublicEditorView> = new Map<string, IPublicEditorView>(); init(ctx: any) { this.options.init(ctx); From 0a2427354b447a6c479ee950548b49b868cce1ab Mon Sep 17 00:00:00 2001 From: liujuping <liujup@foxmail.com> Date: Thu, 29 Dec 2022 18:30:07 +0800 Subject: [PATCH 12/14] feat: plugin add get\getAll\has\delete api --- docs/docs/api/model/plugin-instance.md | 54 +++++++++++++++++++ docs/docs/api/plugins.md | 41 ++++++++++++++ packages/designer/src/plugin/plugin-types.ts | 1 + packages/designer/src/plugin/plugin.ts | 2 +- packages/shell/src/api/plugins.ts | 23 ++++++++ packages/shell/src/model/plugin-instance.ts | 31 +++++++++++ packages/shell/src/symbols.ts | 3 +- packages/types/src/shell/api/plugins.ts | 14 ++++- packages/types/src/shell/model/index.ts | 1 + .../types/src/shell/model/plugin-instance.ts | 11 ++++ 10 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 docs/docs/api/model/plugin-instance.md create mode 100644 packages/shell/src/model/plugin-instance.ts create mode 100644 packages/types/src/shell/model/plugin-instance.ts diff --git a/docs/docs/api/model/plugin-instance.md b/docs/docs/api/model/plugin-instance.md new file mode 100644 index 000000000..9ca872a4f --- /dev/null +++ b/docs/docs/api/model/plugin-instance.md @@ -0,0 +1,54 @@ +--- +title: plugin-instance +sidebar_position: 12 +--- + +> **@types** [IPublicModelPluginInstance](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/plugin-instance.ts)<br/> +> **@since** v1.1.0 + + +## 基本介绍 + +插件实例 + +## 属性 + +### pluginName + +插件名字 + +```typescript +get name(): string; +``` + +### dep + +插件依赖 + +```typescript +get dep(): string[]; +``` + +### disabled + +插件是否禁用 + +```typescript +get disabled(): boolean + +set disabled(disabled: boolean): void; + +``` + +### meta + +插件 meta 信息 + +```typescript +get meta(): IPublicTypePluginMeta + +``` + +- [IPublicTypePluginMeta](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/plugin-meta.ts) + + diff --git a/docs/docs/api/plugins.md b/docs/docs/api/plugins.md index 06b558369..1b20bdabe 100644 --- a/docs/docs/api/plugins.md +++ b/docs/docs/api/plugins.md @@ -159,10 +159,51 @@ BuiltinPluginRegistry.meta = { await plugins.register(BuiltinPluginRegistry, { key1: 'abc', key5: 'willNotPassToPlugin' }); ``` +### get + +获取指定插件 + +```typescript +function get(pluginName: string): IPublicModelPluginInstance; + +``` + +关联模型 [IPublicModelPluginInstance](./model/plugin-instance) + +### getAll + +获取所有的插件实例 + +```typescript +function getAll(): IPublicModelPluginInstance[]; + +``` + +关联模型 [IPublicModelPluginInstance](./model/plugin-instance) + +### has + +判断是否有指定插件 + +```typescript +function has(pluginName: string): boolean; + +``` + +### delete + +删除指定插件 + +```typescript +function delete(pluginName: string): void; + +``` + ## 相关类型定义 - [IPublicModelPluginContext](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/plugin-context.ts) - [IPublicTypePluginConfig](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/plugin-config.ts) +- [IPublicModelPluginInstance](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/plugin-instance.ts) ## 插件元数据工程转化示例 your-plugin/package.json diff --git a/packages/designer/src/plugin/plugin-types.ts b/packages/designer/src/plugin/plugin-types.ts index 7ecfc7c35..8c246a5d2 100644 --- a/packages/designer/src/plugin/plugin-types.ts +++ b/packages/designer/src/plugin/plugin-types.ts @@ -31,6 +31,7 @@ export interface ILowCodePluginRuntimeCore { destroy(): void; toProxy(): any; setDisabled(flag: boolean): void; + meta: IPublicTypePluginMeta; } interface ILowCodePluginRuntimeExportsAccessor { diff --git a/packages/designer/src/plugin/plugin.ts b/packages/designer/src/plugin/plugin.ts index 5bbaaa745..dd57325fc 100644 --- a/packages/designer/src/plugin/plugin.ts +++ b/packages/designer/src/plugin/plugin.ts @@ -20,7 +20,7 @@ export class LowCodePluginRuntime implements ILowCodePluginRuntime { private pluginName: string; - private meta: IPublicTypePluginMeta; + meta: IPublicTypePluginMeta; /** * 标识插件状态,是否被 disabled diff --git a/packages/shell/src/api/plugins.ts b/packages/shell/src/api/plugins.ts index 8a0a506e5..681628515 100644 --- a/packages/shell/src/api/plugins.ts +++ b/packages/shell/src/api/plugins.ts @@ -4,10 +4,12 @@ import { import { globalContext } from '@alilc/lowcode-editor-core'; import { IPublicApiPlugins, + IPublicModelPluginInstance, IPublicTypePlugin, IPublicTypePluginRegisterOptions, IPublicTypePreferenceValueType, } from '@alilc/lowcode-types'; +import { PluginInstance } from '../model/plugin-instance'; import { pluginsSymbol } from '../symbols'; const innerPluginsSymbol = Symbol('plugin'); @@ -45,6 +47,27 @@ export class Plugins implements IPublicApiPlugins { return this[pluginsSymbol].getPluginPreference(pluginName); } + get(pluginName: string): IPublicModelPluginInstance | null { + const instance = this[pluginsSymbol].get(pluginName); + if (instance) { + return new PluginInstance(instance); + } + + return null; + } + + getAll() { + return this[pluginsSymbol].getAll()?.map(d => new PluginInstance(d)); + } + + has(pluginName: string) { + return this[pluginsSymbol].has(pluginName); + } + + delete(pluginName: string) { + this[pluginsSymbol].delete(pluginName); + } + toProxy() { return new Proxy(this, { get(target, prop, receiver) { diff --git a/packages/shell/src/model/plugin-instance.ts b/packages/shell/src/model/plugin-instance.ts new file mode 100644 index 000000000..47a619482 --- /dev/null +++ b/packages/shell/src/model/plugin-instance.ts @@ -0,0 +1,31 @@ +import { ILowCodePluginRuntime } from '@alilc/lowcode-designer'; +import { IPublicModelPluginInstance } from '@alilc/lowcode-types'; +import { pluginInstanceSymbol } from '../symbols'; + +export class PluginInstance implements IPublicModelPluginInstance { + private readonly [pluginInstanceSymbol]: ILowCodePluginRuntime; + + constructor(pluginInstance: ILowCodePluginRuntime) { + this[pluginInstanceSymbol] = pluginInstance; + } + + get pluginName(): string { + return this[pluginInstanceSymbol].name; + } + + get dep(): string[] { + return this[pluginInstanceSymbol].dep; + } + + get disabled(): boolean { + return this[pluginInstanceSymbol].disabled; + } + + set disabled(disabled: boolean) { + this[pluginInstanceSymbol].setDisabled(disabled); + } + + get meta() { + return this[pluginInstanceSymbol].meta; + } +} \ No newline at end of file diff --git a/packages/shell/src/symbols.ts b/packages/shell/src/symbols.ts index 44c241b28..6e0924893 100644 --- a/packages/shell/src/symbols.ts +++ b/packages/shell/src/symbols.ts @@ -28,4 +28,5 @@ export const designerCabinSymbol = Symbol('designerCabin'); export const hotkeySymbol = Symbol('hotkey'); export const pluginsSymbol = Symbol('plugins'); export const workspaceSymbol = Symbol('workspace'); -export const windowSymbol = Symbol('window'); \ No newline at end of file +export const windowSymbol = Symbol('window'); +export const pluginInstanceSymbol = Symbol('plugin-instance'); \ No newline at end of file diff --git a/packages/types/src/shell/api/plugins.ts b/packages/types/src/shell/api/plugins.ts index 2797ba071..32e13c3b6 100644 --- a/packages/types/src/shell/api/plugins.ts +++ b/packages/types/src/shell/api/plugins.ts @@ -1,4 +1,4 @@ -import { IPublicTypePlugin } from '../model'; +import { IPublicModelPluginInstance, IPublicTypePlugin } from '../model'; import { IPublicTypePreferenceValueType } from '../type'; import { IPublicTypePluginRegisterOptions } from '../type/plugin-register-options'; @@ -25,4 +25,16 @@ export interface IPublicApiPlugins { getPluginPreference( pluginName: string, ): Record<string, IPublicTypePreferenceValueType> | null | undefined; + + /** 获取指定插件 */ + get(pluginName: string): IPublicModelPluginInstance | null; + + /** 获取所有的插件实例 */ + getAll(): IPublicModelPluginInstance[]; + + /** 判断是否有指定插件 */ + has(pluginName: string): boolean; + + /** 删除指定插件 */ + delete(pluginName: string): void; } \ No newline at end of file diff --git a/packages/types/src/shell/model/index.ts b/packages/types/src/shell/model/index.ts index c9d8f3a98..e1daf63a0 100644 --- a/packages/types/src/shell/model/index.ts +++ b/packages/types/src/shell/model/index.ts @@ -26,3 +26,4 @@ export * from './setting-target'; export * from './engine-config'; export * from './editor'; export * from './preference'; +export * from './plugin-instance'; \ No newline at end of file diff --git a/packages/types/src/shell/model/plugin-instance.ts b/packages/types/src/shell/model/plugin-instance.ts new file mode 100644 index 000000000..7855d903b --- /dev/null +++ b/packages/types/src/shell/model/plugin-instance.ts @@ -0,0 +1,11 @@ +import { IPublicTypePluginMeta } from '../type/plugin-meta'; + +export interface IPublicModelPluginInstance { + pluginName: string; + + dep: string[]; + + disabled: boolean; + + meta: IPublicTypePluginMeta; +} \ No newline at end of file From dae09e3bcb21b420a88a5e5256320ad50ab1ac2d Mon Sep 17 00:00:00 2001 From: liujuping <liujup@foxmail.com> Date: Thu, 29 Dec 2022 17:53:23 +0800 Subject: [PATCH 13/14] feat: added workspace api to support registration of multiple resources --- docs/docs/api/model/window.md | 36 +++- docs/docs/api/workspace.md | 40 +++++ packages/designer/src/component-actions.ts | 155 ++++++++++++++++ packages/designer/src/component-meta.ts | 153 +--------------- packages/designer/src/designer/designer.ts | 3 + .../src/designer/setting/setting-field.ts | 8 +- .../designer/src/project/project-view.tsx | 2 +- packages/designer/src/project/project.ts | 8 +- .../tests/main/meta/component-meta.test.ts | 27 ++- .../settings/settings-primary-pane.tsx | 2 +- .../src/layouts/workbench.less | 10 ++ .../editor-skeleton/src/register-defaults.ts | 24 ++- packages/editor-skeleton/src/skeleton.ts | 15 ++ packages/engine/src/engine-core.ts | 5 +- packages/shell/src/api/material.ts | 21 +-- packages/shell/src/api/project.ts | 15 +- packages/shell/src/api/workspace.ts | 33 ++++ packages/shell/src/model/window.ts | 16 ++ packages/shell/src/symbols.ts | 1 - packages/types/src/shell/api/workspace.ts | 18 ++ packages/types/src/shell/model/window.ts | 9 + .../types/src/shell/type/resource-options.ts | 10 +- .../src/shell/type/widget-config-area.ts | 2 +- packages/workspace/src/base-context.ts | 8 +- packages/workspace/src/editor-view/context.ts | 2 +- packages/workspace/src/editor-view/view.tsx | 10 +- .../workspace/src/editor-window/context.ts | 13 +- packages/workspace/src/editor-window/view.tsx | 23 ++- packages/workspace/src/index.ts | 71 +------- packages/workspace/src/layouts/left-area.tsx | 3 + .../workspace/src/layouts/sub-top-area.tsx | 67 +++++++ packages/workspace/src/layouts/top-area.tsx | 2 +- packages/workspace/src/layouts/workbench.less | 15 +- packages/workspace/src/layouts/workbench.tsx | 27 ++- packages/workspace/src/resource.ts | 8 + packages/workspace/src/workspace.ts | 169 ++++++++++++++++++ 36 files changed, 720 insertions(+), 311 deletions(-) create mode 100644 packages/designer/src/component-actions.ts create mode 100644 packages/workspace/src/layouts/sub-top-area.tsx create mode 100644 packages/workspace/src/workspace.ts diff --git a/docs/docs/api/model/window.md b/docs/docs/api/model/window.md index 177d3438a..6a5413883 100644 --- a/docs/docs/api/model/window.md +++ b/docs/docs/api/model/window.md @@ -11,15 +11,41 @@ sidebar_position: 12 低代码设计器窗口模型 +## 变量 + +### id + +窗口唯一 id + +### title + +窗口标题 + +### resourceName + +窗口资源名字 + ## 方法签名 -### importSchema(schema: IPublicTypeNodeSchema) -当前窗口导入 schema +### importSchema +当前窗口导入 schema, 会调用当前窗口对应资源的 import 钩子 + +```typescript +function importSchema(schema: IPublicTypeNodeSchema): void +``` 相关类型:[IPublicTypeNodeSchema](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/node-schema.ts) -### changeViewType(viewName: string) +### changeViewType 修改当前窗口视图类型 -### async save() -调用当前窗口视图保存钩子 +```typescript +function changeViewType(viewName: string): void +``` + +### save +当前窗口的保存方法,会调用当前窗口对应资源的 save 钩子 + +```typescript +function save(): Promise(void) +``` diff --git a/docs/docs/api/workspace.md b/docs/docs/api/workspace.md index e710bceec..5a9ad2a44 100644 --- a/docs/docs/api/workspace.md +++ b/docs/docs/api/workspace.md @@ -21,6 +21,30 @@ sidebar_position: 12 当前设计器窗口模型 +```typescript +get window(): IPublicModelWindow +``` + +关联模型 [IPublicModelWindow](./model/window) + +### plugins + +应用级别的插件注册 + +```typescript +get plugins(): IPublicApiPlugins +``` + +关联模型 [IPublicApiPlugins](./plugins) + +### windows + +当前设计器的编辑窗口 + +```typescript +get window(): IPublicModelWindow[] +``` + 关联模型 [IPublicModelWindow](./model/window) ## 方法签名 @@ -34,3 +58,19 @@ registerResourceType(resourceName: string, resourceType: 'editor', options: IPub ``` 相关类型:[IPublicResourceOptions](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/resource-options.ts) + +### onChangeWindows + +窗口新增/删除的事件 + +```typescript +function onChangeWindows(fn: () => void): void; +``` + +### onChangeActiveWindow + +active 窗口变更事件 + +```typescript +function onChangeActiveWindow(fn: () => void): void; +``` diff --git a/packages/designer/src/component-actions.ts b/packages/designer/src/component-actions.ts new file mode 100644 index 000000000..793bbdc20 --- /dev/null +++ b/packages/designer/src/component-actions.ts @@ -0,0 +1,155 @@ +import { IPublicTypeComponentAction, IPublicTypeMetadataTransducer } from '@alilc/lowcode-types'; +import { engineConfig } from '@alilc/lowcode-editor-core'; +import { intlNode } from './locale'; +import { + IconLock, + IconUnlock, + IconRemove, + IconClone, + IconHidden, +} from './icons'; +import { Node } from './document'; +import { componentDefaults, legacyIssues } from './transducers'; + +export class ComponentActions { + actions: IPublicTypeComponentAction[] = [ + { + name: 'remove', + content: { + icon: IconRemove, + title: intlNode('remove'), + /* istanbul ignore next */ + action(node: Node) { + node.remove(); + }, + }, + important: true, + }, + { + name: 'hide', + content: { + icon: IconHidden, + title: intlNode('hide'), + /* istanbul ignore next */ + action(node: Node) { + node.setVisible(false); + }, + }, + /* istanbul ignore next */ + condition: (node: Node) => { + return node.componentMeta.isModal; + }, + important: true, + }, + { + name: 'copy', + content: { + icon: IconClone, + title: intlNode('copy'), + /* istanbul ignore next */ + action(node: Node) { + // node.remove(); + const { document: doc, parent, index } = node; + if (parent) { + const newNode = doc.insertNode(parent, node, index + 1, true); + newNode.select(); + const { isRGL, rglNode } = node.getRGL(); + if (isRGL) { + // 复制 layout 信息 + let layout = rglNode.getPropValue('layout') || []; + let curLayout = layout.filter((item) => item.i === node.getPropValue('fieldId')); + if (curLayout && curLayout[0]) { + layout.push({ + ...curLayout[0], + i: newNode.getPropValue('fieldId'), + }); + rglNode.setPropValue('layout', layout); + // 如果是磁贴块复制,则需要滚动到影响位置 + setTimeout(() => newNode.document.simulator?.scrollToNode(newNode), 10); + } + } + } + }, + }, + important: true, + }, + { + name: 'lock', + content: { + icon: IconLock, // 锁定 icon + title: intlNode('lock'), + /* istanbul ignore next */ + action(node: Node) { + node.lock(); + }, + }, + /* istanbul ignore next */ + condition: (node: Node) => { + return engineConfig.get('enableCanvasLock', false) && node.isContainer() && !node.isLocked; + }, + important: true, + }, + { + name: 'unlock', + content: { + icon: IconUnlock, // 解锁 icon + title: intlNode('unlock'), + /* istanbul ignore next */ + action(node: Node) { + node.lock(false); + }, + }, + /* istanbul ignore next */ + condition: (node: Node) => { + return engineConfig.get('enableCanvasLock', false) && node.isContainer() && node.isLocked; + }, + important: true, + }, + ]; + + constructor() { + this.registerMetadataTransducer(legacyIssues, 2, 'legacy-issues'); // should use a high level priority, eg: 2 + this.registerMetadataTransducer(componentDefaults, 100, 'component-defaults'); + } + + removeBuiltinComponentAction(name: string) { + const i = this.actions.findIndex((action) => action.name === name); + if (i > -1) { + this.actions.splice(i, 1); + } + } + addBuiltinComponentAction(action: IPublicTypeComponentAction) { + this.actions.push(action); + } + + modifyBuiltinComponentAction( + actionName: string, + handle: (action: IPublicTypeComponentAction) => void, + ) { + const builtinAction = this.actions.find((action) => action.name === actionName); + if (builtinAction) { + handle(builtinAction); + } + } + + private metadataTransducers: IPublicTypeMetadataTransducer[] = []; + + registerMetadataTransducer( + transducer: IPublicTypeMetadataTransducer, + level = 100, + id?: string, + ) { + transducer.level = level; + transducer.id = id; + const i = this.metadataTransducers.findIndex((item) => item.level != null && item.level > level); + if (i < 0) { + this.metadataTransducers.push(transducer); + } else { + this.metadataTransducers.splice(i, 0, transducer); + } + } + + getRegisteredMetadataTransducers(): IPublicTypeMetadataTransducer[] { + return this.metadataTransducers; + } +} \ No newline at end of file diff --git a/packages/designer/src/component-meta.ts b/packages/designer/src/component-meta.ts index 45f431b6d..70d8630b5 100644 --- a/packages/designer/src/component-meta.ts +++ b/packages/designer/src/component-meta.ts @@ -4,7 +4,6 @@ import { IPublicTypeNpmInfo, IPublicTypeNodeData, IPublicTypeNodeSchema, - IPublicTypeComponentAction, IPublicTypeTitleContent, IPublicTypeTransformedComponentMetadata, IPublicTypeNestingFilter, @@ -15,20 +14,13 @@ import { IPublicModelComponentMeta, } from '@alilc/lowcode-types'; import { deprecate, isRegExp, isTitleConfig } from '@alilc/lowcode-utils'; -import { computed, engineConfig, createModuleEventBus, IEventBus } from '@alilc/lowcode-editor-core'; -import { componentDefaults, legacyIssues } from './transducers'; +import { computed, createModuleEventBus, IEventBus } from '@alilc/lowcode-editor-core'; import { isNode, Node, INode } from './document'; import { Designer } from './designer'; -import { intlNode } from './locale'; import { - IconLock, - IconUnlock, IconContainer, IconPage, IconComponent, - IconRemove, - IconClone, - IconHidden, } from './icons'; export function ensureAList(list?: string | string[]): string[] | null { @@ -272,7 +264,7 @@ export class ComponentMeta implements IComponentMeta { } private transformMetadata(metadta: IPublicTypeComponentMetadata): IPublicTypeTransformedComponentMetadata { - const result = getRegisteredMetadataTransducers().reduce((prevMetadata, current) => { + const result = this.designer.componentActions.getRegisteredMetadataTransducers().reduce((prevMetadata, current) => { return current(prevMetadata); }, preprocessMetadata(metadta)); @@ -300,7 +292,7 @@ export class ComponentMeta implements IComponentMeta { const disabled = ensureAList(disableBehaviors) || (this.isRootComponent(false) ? ['copy', 'remove', 'lock', 'unlock'] : null); - actions = builtinComponentActions.concat( + actions = this.designer.componentActions.actions.concat( this.designer.getGlobalComponentActions() || [], actions || [], ); @@ -382,142 +374,3 @@ function preprocessMetadata(metadata: IPublicTypeComponentMetadata): IPublicType }; } - -const metadataTransducers: IPublicTypeMetadataTransducer[] = []; - -export function registerMetadataTransducer( - transducer: IPublicTypeMetadataTransducer, - level = 100, - id?: string, -) { - transducer.level = level; - transducer.id = id; - const i = metadataTransducers.findIndex((item) => item.level != null && item.level > level); - if (i < 0) { - metadataTransducers.push(transducer); - } else { - metadataTransducers.splice(i, 0, transducer); - } -} - -export function getRegisteredMetadataTransducers(): IPublicTypeMetadataTransducer[] { - return metadataTransducers; -} - -const builtinComponentActions: IPublicTypeComponentAction[] = [ - { - name: 'remove', - content: { - icon: IconRemove, - title: intlNode('remove'), - /* istanbul ignore next */ - action(node: Node) { - node.remove(); - }, - }, - important: true, - }, - { - name: 'hide', - content: { - icon: IconHidden, - title: intlNode('hide'), - /* istanbul ignore next */ - action(node: Node) { - node.setVisible(false); - }, - }, - /* istanbul ignore next */ - condition: (node: Node) => { - return node.componentMeta.isModal; - }, - important: true, - }, - { - name: 'copy', - content: { - icon: IconClone, - title: intlNode('copy'), - /* istanbul ignore next */ - action(node: Node) { - // node.remove(); - const { document: doc, parent, index } = node; - if (parent) { - const newNode = doc.insertNode(parent, node, index + 1, true); - newNode.select(); - const { isRGL, rglNode } = node.getRGL(); - if (isRGL) { - // 复制 layout 信息 - let layout = rglNode.getPropValue('layout') || []; - let curLayout = layout.filter((item) => item.i === node.getPropValue('fieldId')); - if (curLayout && curLayout[0]) { - layout.push({ - ...curLayout[0], - i: newNode.getPropValue('fieldId'), - }); - rglNode.setPropValue('layout', layout); - // 如果是磁贴块复制,则需要滚动到影响位置 - setTimeout(() => newNode.document.simulator?.scrollToNode(newNode), 10); - } - } - } - }, - }, - important: true, - }, - { - name: 'lock', - content: { - icon: IconLock, // 锁定 icon - title: intlNode('lock'), - /* istanbul ignore next */ - action(node: Node) { - node.lock(); - }, - }, - /* istanbul ignore next */ - condition: (node: Node) => { - return engineConfig.get('enableCanvasLock', false) && node.isContainer() && !node.isLocked; - }, - important: true, - }, - { - name: 'unlock', - content: { - icon: IconUnlock, // 解锁 icon - title: intlNode('unlock'), - /* istanbul ignore next */ - action(node: Node) { - node.lock(false); - }, - }, - /* istanbul ignore next */ - condition: (node: Node) => { - return engineConfig.get('enableCanvasLock', false) && node.isContainer() && node.isLocked; - }, - important: true, - }, -]; - -export function removeBuiltinComponentAction(name: string) { - const i = builtinComponentActions.findIndex((action) => action.name === name); - if (i > -1) { - builtinComponentActions.splice(i, 1); - } -} -export function addBuiltinComponentAction(action: IPublicTypeComponentAction) { - builtinComponentActions.push(action); -} - -export function modifyBuiltinComponentAction( - actionName: string, - handle: (action: IPublicTypeComponentAction) => void, -) { - const builtinAction = builtinComponentActions.find((action) => action.name === actionName); - if (builtinAction) { - handle(builtinAction); - } -} - -registerMetadataTransducer(legacyIssues, 2, 'legacy-issues'); // should use a high level priority, eg: 2 -registerMetadataTransducer(componentDefaults, 100, 'component-defaults'); diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts index d16d6c267..118d068cf 100644 --- a/packages/designer/src/designer/designer.ts +++ b/packages/designer/src/designer/designer.ts @@ -32,6 +32,7 @@ import { OffsetObserver, createOffsetObserver } from './offset-observer'; import { focusing } from './focusing'; import { SettingTopEntry } from './setting'; import { BemToolsManager } from '../builtin-simulator/bem-tools/manager'; +import { ComponentActions } from '../component-actions'; const logger = new Logger({ level: 'warn', bizName: 'designer' }); @@ -60,6 +61,8 @@ export interface DesignerProps { export class Designer implements IDesigner { readonly dragon = new Dragon(this); + readonly componentActions = new ComponentActions(); + readonly activeTracker = new ActiveTracker(); readonly detecting = new Detecting(); diff --git a/packages/designer/src/designer/setting/setting-field.ts b/packages/designer/src/designer/setting/setting-field.ts index a7d0ea9f9..ab5b8eadb 100644 --- a/packages/designer/src/designer/setting/setting-field.ts +++ b/packages/designer/src/designer/setting/setting-field.ts @@ -2,7 +2,7 @@ import { IPublicTypeTitleContent, IPublicTypeSetterType, IPublicTypeDynamicSette import { Transducer } from './utils'; import { SettingPropEntry } from './setting-prop-entry'; import { SettingEntry } from './setting-entry'; -import { computed, obx, makeObservable, action } from '@alilc/lowcode-editor-core'; +import { computed, obx, makeObservable, action, untracked } from '@alilc/lowcode-editor-core'; import { cloneDeep, isCustomView, isDynamicSetter } from '@alilc/lowcode-utils'; function getSettingFieldCollectorKey(parent: SettingEntry, config: IPublicTypeFieldConfig) { @@ -43,8 +43,10 @@ export class SettingField extends SettingPropEntry implements SettingEntry { return null; } if (isDynamicSetter(this._setter)) { - const shellThis = this.internalToShellPropEntry(); - return this._setter.call(shellThis, shellThis); + return untracked(() => { + const shellThis = this.internalToShellPropEntry(); + return this._setter.call(shellThis, shellThis); + }); } return this._setter; } diff --git a/packages/designer/src/project/project-view.tsx b/packages/designer/src/project/project-view.tsx index f6feaa0af..a16d4451a 100644 --- a/packages/designer/src/project/project-view.tsx +++ b/packages/designer/src/project/project-view.tsx @@ -4,7 +4,7 @@ import { Designer } from '../designer'; import { BuiltinSimulatorHostView } from '../builtin-simulator'; import './project.less'; -class BuiltinLoading extends Component { +export class BuiltinLoading extends Component { render() { return ( <div id="engine-loading-wrapper"> diff --git a/packages/designer/src/project/project.ts b/packages/designer/src/project/project.ts index 08b96d7c4..f140b30d4 100644 --- a/packages/designer/src/project/project.ts +++ b/packages/designer/src/project/project.ts @@ -30,6 +30,8 @@ export class Project implements IProject { private _simulator?: ISimulatorHost; + private isRendererReady: boolean = false; + /** * 模拟器 */ @@ -318,6 +320,7 @@ export class Project implements IProject { } setRendererReady(renderer: any) { + this.isRendererReady = true; this.emitter.emit('lowcode_engine_renderer_ready', renderer); } @@ -328,7 +331,10 @@ export class Project implements IProject { }; } - onRendererReady(fn: (args: any) => void): () => void { + onRendererReady(fn: () => void): () => void { + if (this.isRendererReady) { + fn(); + } this.emitter.on('lowcode_engine_renderer_ready', fn); return () => { this.emitter.removeListener('lowcode_engine_renderer_ready', fn); diff --git a/packages/designer/tests/main/meta/component-meta.test.ts b/packages/designer/tests/main/meta/component-meta.test.ts index a1a113d93..d943f85af 100644 --- a/packages/designer/tests/main/meta/component-meta.test.ts +++ b/packages/designer/tests/main/meta/component-meta.test.ts @@ -1,5 +1,4 @@ import '../../fixtures/window'; -import { Node } from '../../../src/document/node/node'; import { Designer } from '../../../src/designer/designer'; import divMeta from '../../fixtures/component-metadata/div'; import div2Meta from '../../fixtures/component-metadata/div2'; @@ -19,22 +18,18 @@ import page2Meta from '../../fixtures/component-metadata/page2'; import { ComponentMeta, isComponentMeta, - removeBuiltinComponentAction, - addBuiltinComponentAction, - modifyBuiltinComponentAction, ensureAList, buildFilter, - registerMetadataTransducer, - getRegisteredMetadataTransducers, } from '../../../src/component-meta'; -import { componentDefaults } from '../../../src/transducers'; -const mockCreateSettingEntry = jest.fn(); + jest.mock('../../../src/designer/designer', () => { return { Designer: jest.fn().mockImplementation(() => { + const { ComponentActions } = require('../../../src/component-actions'); return { getGlobalComponentActions: () => [], + componentActions: new ComponentActions(), }; }), }; @@ -126,12 +121,12 @@ describe('组件元数据处理', () => { expect(meta.availableActions[1].name).toBe('hide'); expect(meta.availableActions[2].name).toBe('copy'); - removeBuiltinComponentAction('remove'); + designer.componentActions.removeBuiltinComponentAction('remove'); expect(meta.availableActions).toHaveLength(4); expect(meta.availableActions[0].name).toBe('hide'); expect(meta.availableActions[1].name).toBe('copy'); - addBuiltinComponentAction({ + designer.componentActions.addBuiltinComponentAction({ name: 'new', content: { action() {}, @@ -227,17 +222,17 @@ describe('帮助函数', () => { }); it('registerMetadataTransducer', () => { - expect(getRegisteredMetadataTransducers()).toHaveLength(2); + expect(designer.componentActions.getRegisteredMetadataTransducers()).toHaveLength(2); // 插入到 legacy-issues 和 component-defaults 的中间 - registerMetadataTransducer((metadata) => metadata, 3, 'noop'); - expect(getRegisteredMetadataTransducers()).toHaveLength(3); + designer.componentActions.registerMetadataTransducer((metadata) => metadata, 3, 'noop'); + expect(designer.componentActions.getRegisteredMetadataTransducers()).toHaveLength(3); - registerMetadataTransducer((metadata) => metadata); - expect(getRegisteredMetadataTransducers()).toHaveLength(4); + designer.componentActions.registerMetadataTransducer((metadata) => metadata); + expect(designer.componentActions.getRegisteredMetadataTransducers()).toHaveLength(4); }); it('modifyBuiltinComponentAction', () => { - modifyBuiltinComponentAction('copy', (action) => { + designer.componentActions.modifyBuiltinComponentAction('copy', (action) => { expect(action.name).toBe('copy'); }); }); diff --git a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx index 50b18d50f..c12d966be 100644 --- a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx @@ -69,7 +69,7 @@ export class SettingsPrimaryPane extends Component<{ engineEditor: Editor; confi } const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = this.props.engineEditor; const designer = editor.get('designer'); const current = designer?.currentSelection?.getNodes()?.[0]; let node: Node | null = settings.first; diff --git a/packages/editor-skeleton/src/layouts/workbench.less b/packages/editor-skeleton/src/layouts/workbench.less index af555c65e..4e96badcf 100644 --- a/packages/editor-skeleton/src/layouts/workbench.less +++ b/packages/editor-skeleton/src/layouts/workbench.less @@ -138,6 +138,16 @@ body { display: flex; flex-direction: column; background-color: #edeff3; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + z-index: -1; + + &.active { + z-index: 999; + } .lc-workbench { diff --git a/packages/editor-skeleton/src/register-defaults.ts b/packages/editor-skeleton/src/register-defaults.ts index bb35b277d..573631f78 100644 --- a/packages/editor-skeleton/src/register-defaults.ts +++ b/packages/editor-skeleton/src/register-defaults.ts @@ -1,15 +1,23 @@ -import { registerMetadataTransducer } from '@alilc/lowcode-designer'; import parseJSFunc from './transducers/parse-func'; import parseProps from './transducers/parse-props'; import addonCombine from './transducers/addon-combine'; +import { IPublicModelPluginContext } from '@alilc/lowcode-types'; -export const registerDefaults = () => { - // parseFunc - registerMetadataTransducer(parseJSFunc, 1, 'parse-func'); +export const registerDefaults = (ctx: IPublicModelPluginContext) => { + const { material } = ctx; + return { + init() { + // parseFunc + material.registerMetadataTransducer(parseJSFunc, 1, 'parse-func'); - // parseProps - registerMetadataTransducer(parseProps, 5, 'parse-props'); + // parseProps + material.registerMetadataTransducer(parseProps, 5, 'parse-props'); - // addon/platform custom - registerMetadataTransducer(addonCombine, 10, 'combine-props'); + // addon/platform custom + material.registerMetadataTransducer(addonCombine, 10, 'combine-props'); + }, + }; }; + + +registerDefaults.pluginName = '___register_defaults___'; diff --git a/packages/editor-skeleton/src/skeleton.ts b/packages/editor-skeleton/src/skeleton.ts index 649c45271..90fba2e1b 100644 --- a/packages/editor-skeleton/src/skeleton.ts +++ b/packages/editor-skeleton/src/skeleton.ts @@ -50,6 +50,8 @@ export class Skeleton { readonly topArea: Area<DockConfig | DividerConfig | PanelDockConfig | DialogDockConfig>; + readonly subTopArea: Area<DockConfig | DividerConfig | PanelDockConfig | DialogDockConfig>; + readonly toolbar: Area<DockConfig | DividerConfig | PanelDockConfig | DialogDockConfig>; readonly leftFixedArea: Area<PanelConfig, Panel>; @@ -88,6 +90,17 @@ export class Skeleton { }, false, ); + this.subTopArea = new Area( + this, + 'subTopArea', + (config) => { + if (isWidget(config)) { + return config; + } + return this.createWidget(config); + }, + false, + ); this.toolbar = new Area( this, 'toolbar', @@ -389,6 +402,8 @@ export class Skeleton { case 'topArea': case 'top': return this.topArea.add(parsedConfig as PanelDockConfig); + case 'subTopArea': + return this.subTopArea.add(parsedConfig as PanelDockConfig); case 'toolbar': return this.toolbar.add(parsedConfig as PanelDockConfig); case 'mainArea': diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index 99286c3c3..a580380a1 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -59,8 +59,6 @@ export * from './modules/skeleton-types'; export * from './modules/designer-types'; export * from './modules/lowcode-types'; -registerDefaults(); - async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: Plugins) { // 注册一批内置插件 await plugins.register(OutlinePlugin, {}, { autoInit: true }); @@ -68,6 +66,7 @@ async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: await plugins.register(setterRegistry, {}, { autoInit: true }); await plugins.register(defaultPanelRegistry(editor)); await plugins.register(builtinHotkey); + await plugins.register(registerDefaults); } const innerWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory); @@ -82,6 +81,7 @@ editor.set('skeleton' as any, innerSkeleton); const designer = new Designer({ editor, shellModelFactory }); editor.set('designer' as any, designer); + const { project: innerProject } = designer; const innerHotkey = new InnerHotkey(); @@ -195,6 +195,7 @@ export async function init( engineContainer, ); innerWorkspace.setActive(true); + await innerWorkspace.plugins.init(pluginPreference); return; } diff --git a/packages/shell/src/api/material.ts b/packages/shell/src/api/material.ts index e824290a5..08deefdcc 100644 --- a/packages/shell/src/api/material.ts +++ b/packages/shell/src/api/material.ts @@ -1,11 +1,6 @@ import { Editor, globalContext } from '@alilc/lowcode-editor-core'; import { Designer, - registerMetadataTransducer, - getRegisteredMetadataTransducers, - addBuiltinComponentAction, - removeBuiltinComponentAction, - modifyBuiltinComponentAction, isComponentMeta, } from '@alilc/lowcode-designer'; import { IPublicTypeAssetsJson } from '@alilc/lowcode-utils'; @@ -85,20 +80,20 @@ export class Material implements IPublicApiMaterial { * @param level * @param id */ - registerMetadataTransducer( + registerMetadataTransducer = ( transducer: IPublicTypeMetadataTransducer, level?: number, id?: string | undefined, - ) { - registerMetadataTransducer(transducer, level, id); - } + ) => { + this[designerSymbol].componentActions.registerMetadataTransducer(transducer, level, id); + }; /** * 获取所有物料元数据管道函数 * @returns */ getRegisteredMetadataTransducers() { - return getRegisteredMetadataTransducers(); + return this[designerSymbol].componentActions.getRegisteredMetadataTransducers(); } /** @@ -147,7 +142,7 @@ export class Material implements IPublicApiMaterial { * @param action */ addBuiltinComponentAction(action: IPublicTypeComponentAction) { - addBuiltinComponentAction(action); + this[designerSymbol].componentActions.addBuiltinComponentAction(action); } /** @@ -155,7 +150,7 @@ export class Material implements IPublicApiMaterial { * @param name */ removeBuiltinComponentAction(name: string) { - removeBuiltinComponentAction(name); + this[designerSymbol].componentActions.removeBuiltinComponentAction(name); } /** @@ -164,7 +159,7 @@ export class Material implements IPublicApiMaterial { * @param handle */ modifyBuiltinComponentAction(actionName: string, handle: (action: IPublicTypeComponentAction) => void) { - modifyBuiltinComponentAction(actionName, handle); + this[designerSymbol].componentActions.modifyBuiltinComponentAction(actionName, handle); } /** diff --git a/packages/shell/src/api/project.ts b/packages/shell/src/api/project.ts index 9cae7d72e..e97a18334 100644 --- a/packages/shell/src/api/project.ts +++ b/packages/shell/src/api/project.ts @@ -18,14 +18,12 @@ import { import { DocumentModel } from '../model/document-model'; import { SimulatorHost } from './simulator-host'; -import { editorSymbol, projectSymbol, simulatorHostSymbol, simulatorRendererSymbol, documentSymbol } from '../symbols'; +import { editorSymbol, projectSymbol, simulatorHostSymbol, documentSymbol } from '../symbols'; const innerProjectSymbol = Symbol('innerProject'); export class Project implements IPublicApiProject { - private readonly [editorSymbol]: IPublicModelEditor; private readonly [innerProjectSymbol]: InnerProject; private [simulatorHostSymbol]: BuiltinSimulatorHost; - private [simulatorRendererSymbol]: any; get [projectSymbol](): InnerProject { if (this.workspaceMode) { return this[innerProjectSymbol]; @@ -38,9 +36,12 @@ export class Project implements IPublicApiProject { return this[innerProjectSymbol]; } + get [editorSymbol](): IPublicModelEditor { + return this[projectSymbol]?.designer.editor; + } + constructor(project: InnerProject, public workspaceMode: boolean = false) { this[innerProjectSymbol] = project; - this[editorSymbol] = project?.designer.editor; } static create(project: InnerProject) { @@ -201,13 +202,9 @@ export class Project implements IPublicApiProject { * 当前 project 的渲染器 ready 事件 */ onSimulatorRendererReady(fn: () => void): IPublicTypeDisposable { - const offFn = this[projectSymbol].onRendererReady((renderer: any) => { - this[simulatorRendererSymbol] = renderer; + const offFn = this[projectSymbol].onRendererReady(() => { fn(); }); - if (this[simulatorRendererSymbol]) { - fn(); - } return offFn; } diff --git a/packages/shell/src/api/workspace.ts b/packages/shell/src/api/workspace.ts index 28c143030..3440871c8 100644 --- a/packages/shell/src/api/workspace.ts +++ b/packages/shell/src/api/workspace.ts @@ -1,5 +1,6 @@ import { IPublicApiWorkspace } 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'; @@ -21,4 +22,36 @@ export class Workspace implements IPublicApiWorkspace { registerResourceType(resourceName: string, resourceType: 'editor', options: any): void { this[workspaceSymbol].registerResourceType(resourceName, resourceType, options); } + + openEditorWindow(resourceName: string, title: string, viewType?: string) { + this[workspaceSymbol].openEditorWindow(resourceName, title, viewType); + } + + openEditorWindowById(id: string) { + this[workspaceSymbol].openEditorWindowById(id); + } + + removeEditorWindow(resourceName: string, title: string) { + this[workspaceSymbol].removeEditorWindow(resourceName, title); + } + + removeEditorWindowById(id: string) { + this[workspaceSymbol].removeEditorWindowById(id); + } + + get plugins() { + return new Plugins(this[workspaceSymbol].plugins, true); + } + + get windows() { + return this[workspaceSymbol].windows.map(d => new Window(d)); + } + + onChangeWindows(fn: () => void) { + return this[workspaceSymbol].onChangeWindows(fn); + } + + onChangeActiveWindow(fn: () => void) { + return this[workspaceSymbol].onChangeActiveWindow(fn); + } } diff --git a/packages/shell/src/model/window.ts b/packages/shell/src/model/window.ts index b471fc864..fee783f53 100644 --- a/packages/shell/src/model/window.ts +++ b/packages/shell/src/model/window.ts @@ -5,6 +5,22 @@ import { EditorWindow } from '@alilc/lowcode-workspace'; export class Window implements IPublicModelWindow { private readonly [windowSymbol]: EditorWindow; + get id() { + return this[windowSymbol].id; + } + + get title() { + return this[windowSymbol].title; + } + + get icon() { + return this[windowSymbol].icon; + } + + get resourceName() { + return this[windowSymbol].resourceName; + } + constructor(editorWindow: EditorWindow) { this[windowSymbol] = editorWindow; } diff --git a/packages/shell/src/symbols.ts b/packages/shell/src/symbols.ts index 6e0924893..b87e1f24b 100644 --- a/packages/shell/src/symbols.ts +++ b/packages/shell/src/symbols.ts @@ -21,7 +21,6 @@ export const dragonSymbol = Symbol('dragon'); export const componentMetaSymbol = Symbol('componentMeta'); export const dropLocationSymbol = Symbol('dropLocation'); export const simulatorHostSymbol = Symbol('simulatorHost'); -export const simulatorRendererSymbol = Symbol('simulatorRenderer'); export const dragObjectSymbol = Symbol('dragObject'); export const locateEventSymbol = Symbol('locateEvent'); export const designerCabinSymbol = Symbol('designerCabin'); diff --git a/packages/types/src/shell/api/workspace.ts b/packages/types/src/shell/api/workspace.ts index 52db05825..b9cd29afa 100644 --- a/packages/types/src/shell/api/workspace.ts +++ b/packages/types/src/shell/api/workspace.ts @@ -1,5 +1,6 @@ import { IPublicModelWindow } from '../model'; import { IPublicResourceOptions } from '../type'; +import { IPublicApiPlugins } from '@alilc/lowcode-types'; export interface IPublicApiWorkspace { /** 是否启用 workspace 模式 */ @@ -10,4 +11,21 @@ export interface IPublicApiWorkspace { /** 注册资源 */ 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[]; + + /** 窗口新增/删除的事件 */ + onChangeWindows: (fn: () => void) => void; + + /** active 窗口变更事件 */ + onChangeActiveWindow: (fn: () => void) => void; } \ No newline at end of file diff --git a/packages/types/src/shell/model/window.ts b/packages/types/src/shell/model/window.ts index f0faedbcd..1502f2a3c 100644 --- a/packages/types/src/shell/model/window.ts +++ b/packages/types/src/shell/model/window.ts @@ -9,4 +9,13 @@ export interface IPublicModelWindow { /** 调用当前窗口视图保存钩子 */ save(): Promise<any>; + + /** 窗口 id */ + id: string; + + /** 窗口标题 */ + title?: string; + + /** 窗口资源名字 */ + resourceName?: string; } \ 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 index 94547bb57..e82db194c 100644 --- a/packages/types/src/shell/type/resource-options.ts +++ b/packages/types/src/shell/type/resource-options.ts @@ -1,7 +1,7 @@ export interface IPublicViewFunctions { - /** 视图初始化 */ + /** 视图初始化钩子 */ init?: () => Promise<void>; - /** 资源保存时调用视图的钩子 */ + /** 资源保存时,会调用视图的钩子 */ save?: () => Promise<void>; } @@ -20,6 +20,9 @@ export interface IPublicResourceOptions { /** 资源描述 */ description?: string; + /** 资源 icon 标识 */ + icon?: React.ReactElement; + /** 默认视图类型 */ defaultViewType: string; @@ -35,4 +38,7 @@ export interface IPublicResourceOptions { import?: (schema: any) => Promise<{ [viewName: string]: any; }>; + + /** 默认标题 */ + defaultTitle?: string; } \ No newline at end of file diff --git a/packages/types/src/shell/type/widget-config-area.ts b/packages/types/src/shell/type/widget-config-area.ts index 7731ab3b0..41e71baa2 100644 --- a/packages/types/src/shell/type/widget-config-area.ts +++ b/packages/types/src/shell/type/widget-config-area.ts @@ -2,7 +2,7 @@ * 所有可能的停靠位置 */ export type IPublicTypeWidgetConfigArea = 'leftArea' | 'left' | 'rightArea' | - 'right' | 'topArea' | 'top' | + 'right' | 'topArea' | 'subTopArea' | 'top' | 'toolbar' | 'mainArea' | 'main' | 'center' | 'centerArea' | 'bottomArea' | 'bottom' | 'leftFixedArea' | diff --git a/packages/workspace/src/base-context.ts b/packages/workspace/src/base-context.ts index 047e5179e..8ab26e455 100644 --- a/packages/workspace/src/base-context.ts +++ b/packages/workspace/src/base-context.ts @@ -33,7 +33,7 @@ import { IPublicTypePluginMeta, } from '@alilc/lowcode-types'; import { getLogger } from '@alilc/lowcode-utils'; -import { Workspace as InnerWorkspace } from './index'; +import { Workspace as InnerWorkspace } from './workspace'; import { EditorWindow } from './editor-window/context'; export class BasicContext { @@ -51,7 +51,7 @@ export class BasicContext { designer: Designer; registerInnerPlugins: () => Promise<void>; innerSetters: InnerSetters; - innerSkeleton: any; + innerSkeleton: InnerSkeleton; innerHotkey: InnerHotkey; innerPlugins: LowCodePluginManager; canvas: Canvas; @@ -65,7 +65,7 @@ export class BasicContext { const designer: Designer = new Designer({ editor, viewName, - shellModelFactory: innerWorkspace.shellModelFactory, + shellModelFactory: innerWorkspace?.shellModelFactory, }); editor.set('designer' as any, designer); @@ -132,7 +132,7 @@ export class BasicContext { // 注册一批内置插件 this.registerInnerPlugins = async function registerPlugins() { - await innerWorkspace.registryInnerPlugin(designer, editor, plugins); + await innerWorkspace?.registryInnerPlugin(designer, editor, plugins); }; } } \ No newline at end of file diff --git a/packages/workspace/src/editor-view/context.ts b/packages/workspace/src/editor-view/context.ts index 913228674..a845d36c1 100644 --- a/packages/workspace/src/editor-view/context.ts +++ b/packages/workspace/src/editor-view/context.ts @@ -1,7 +1,7 @@ import { makeObservable, obx } from '@alilc/lowcode-editor-core'; import { IPublicEditorView, IPublicViewFunctions } from '@alilc/lowcode-types'; import { flow } from 'mobx'; -import { Workspace as InnerWorkspace } from '../'; +import { Workspace as InnerWorkspace } from '../workspace'; import { BasicContext } from '../base-context'; import { EditorWindow } from '../editor-window/context'; import { getWebviewPlugin } from '../inner-plugins/webview'; diff --git a/packages/workspace/src/editor-view/view.tsx b/packages/workspace/src/editor-view/view.tsx index ca3a08fb2..77f0dffeb 100644 --- a/packages/workspace/src/editor-view/view.tsx +++ b/packages/workspace/src/editor-view/view.tsx @@ -1,14 +1,15 @@ -import { observer } from '@alilc/lowcode-editor-core'; +import { BuiltinLoading } from '@alilc/lowcode-designer'; +import { engineConfig, observer } from '@alilc/lowcode-editor-core'; import { Workbench, } from '@alilc/lowcode-editor-skeleton'; -import { Component } from 'react'; +import { PureComponent } from 'react'; import { Context } from './context'; export * from '../base-context'; @observer -export class EditorView extends Component<{ +export class EditorView extends PureComponent<{ editorView: Context; active: boolean; }, any> { @@ -17,7 +18,8 @@ export class EditorView extends Component<{ const editorView = this.props.editorView; const skeleton = editorView.innerSkeleton; if (!editorView.isInit) { - return null; + const Loading = engineConfig.get('loadingComponent', BuiltinLoading); + return <Loading />; } return ( diff --git a/packages/workspace/src/editor-window/context.ts b/packages/workspace/src/editor-window/context.ts index ee680b140..2c1eee719 100644 --- a/packages/workspace/src/editor-window/context.ts +++ b/packages/workspace/src/editor-window/context.ts @@ -1,12 +1,21 @@ +import { uniqueId } from '@alilc/lowcode-utils'; import { makeObservable, obx } from '@alilc/lowcode-editor-core'; import { Context } from '../editor-view/context'; -import { Workspace } from '..'; +import { Workspace } from '../workspace'; import { Resource } from '../resource'; export class EditorWindow { - constructor(readonly resource: Resource, readonly workspace: Workspace) { + id: string = uniqueId('window'); + icon: React.ReactElement | undefined; + + constructor(readonly resource: Resource, readonly workspace: Workspace, public title: string | undefined = '') { makeObservable(this); this.init(); + this.icon = resource.icon; + } + + get resourceName(): string { + return this.resource.options.name; } async importSchema(schema: any) { diff --git a/packages/workspace/src/editor-window/view.tsx b/packages/workspace/src/editor-window/view.tsx index b080c3344..eb049aeed 100644 --- a/packages/workspace/src/editor-window/view.tsx +++ b/packages/workspace/src/editor-window/view.tsx @@ -1,28 +1,35 @@ -import { Component } from 'react'; +import { PureComponent } from 'react'; import { EditorView } from '../editor-view/view'; -import { observer } from '@alilc/lowcode-editor-core'; +import { engineConfig, observer } from '@alilc/lowcode-editor-core'; import { EditorWindow } from './context'; +import { BuiltinLoading } from '@alilc/lowcode-designer'; @observer -export class EditorWindowView extends Component<{ +export class EditorWindowView extends PureComponent<{ editorWindow: EditorWindow; + active: boolean; }, any> { render() { - const { resource, editorView, editorViews } = this.props.editorWindow; + const { active } = this.props; + const { editorView, editorViews } = this.props.editorWindow; if (!editorView) { - return null; + const Loading = engineConfig.get('loadingComponent', BuiltinLoading); + return ( + <div className={`workspace-engine-main ${active ? 'active' : ''}`}> + <Loading /> + </div> + ); } + return ( - <div className="workspace-engine-main"> + <div className={`workspace-engine-main ${active ? 'active' : ''}`}> { Array.from(editorViews.values()).map((editorView: any) => { return ( <EditorView - resource={resource} key={editorView.name} active={editorView.active} editorView={editorView} - defaultViewType /> ); }) diff --git a/packages/workspace/src/index.ts b/packages/workspace/src/index.ts index 863f8d0b7..00b054eba 100644 --- a/packages/workspace/src/index.ts +++ b/packages/workspace/src/index.ts @@ -1,70 +1 @@ -import { Designer } from '@alilc/lowcode-designer'; -import { Editor } from '@alilc/lowcode-editor-core'; -import { - Skeleton as InnerSkeleton, -} from '@alilc/lowcode-editor-skeleton'; -import { Plugins } from '@alilc/lowcode-shell'; -import { IPublicResourceOptions } from '@alilc/lowcode-types'; -import { EditorWindow } from './editor-window/context'; -import { Resource } from './resource'; - -export { Resource } from './resource'; -export * from './editor-window/context'; -export * from './layouts/workbench'; - -export class Workspace { - readonly editor = new Editor(); - readonly skeleton = new InnerSkeleton(this.editor); - - constructor( - readonly registryInnerPlugin: (designer: Designer, editor: Editor, plugins: Plugins) => Promise<void>, - readonly shellModelFactory: any, - ) { - if (this.defaultResource) { - this.window = new EditorWindow(this.defaultResource, this); - } - } - - private _isActive = false; - - get isActive() { - return this._isActive; - } - - setActive(value: boolean) { - this._isActive = value; - } - - editorWindows: []; - - window: EditorWindow; - - private resources: Map<string, Resource> = new Map(); - - registerResourceType(resourceName: string, resourceType: 'editor' | 'webview', options: IPublicResourceOptions): void { - if (resourceType === 'editor') { - const resource = new Resource(options); - this.resources.set(resourceName, resource); - - if (!this.window) { - this.window = new EditorWindow(this.defaultResource, this); - } - } - } - - get defaultResource() { - if (this.resources.size === 1) { - return this.resources.values().next().value; - } - - return null; - } - - removeResourceType(resourceName: string) { - if (this.resources.has(resourceName)) { - this.resources.delete(resourceName); - } - } - - openEditorWindow() {} -} +export { Workspace } from './workspace'; \ No newline at end of file diff --git a/packages/workspace/src/layouts/left-area.tsx b/packages/workspace/src/layouts/left-area.tsx index 6427499df..3057386ed 100644 --- a/packages/workspace/src/layouts/left-area.tsx +++ b/packages/workspace/src/layouts/left-area.tsx @@ -7,6 +7,9 @@ import { Area } from '@alilc/lowcode-editor-skeleton'; export default class LeftArea extends Component<{ area: Area }> { render() { const { area } = this.props; + if (area.isEmpty()) { + return null; + } return ( <div className={classNames('lc-left-area', { 'lc-area-visible': area.visible, diff --git a/packages/workspace/src/layouts/sub-top-area.tsx b/packages/workspace/src/layouts/sub-top-area.tsx new file mode 100644 index 000000000..e6bf61871 --- /dev/null +++ b/packages/workspace/src/layouts/sub-top-area.tsx @@ -0,0 +1,67 @@ +import { Component, Fragment } from 'react'; +import classNames from 'classnames'; +import { observer } from '@alilc/lowcode-editor-core'; +import { Area } from '@alilc/lowcode-editor-skeleton'; + +@observer +export default class SubTopArea extends Component<{ area: Area; itemClassName?: string }> { + render() { + const { area, itemClassName } = this.props; + + if (area.isEmpty()) { + return null; + } + + return ( + <div className={classNames('lc-sub-top-area engine-actionpane', { + 'lc-area-visible': area.visible, + })} + > + <Contents area={area} itemClassName={itemClassName} /> + </div> + ); + } +} + +@observer +class Contents extends Component<{ area: Area; itemClassName?: string }> { + render() { + const { area, itemClassName } = this.props; + const left: any[] = []; + const center: any[] = []; + const right: any[] = []; + area.container.items.slice().sort((a, b) => { + const index1 = a.config?.index || 0; + const index2 = b.config?.index || 0; + return index1 === index2 ? 0 : (index1 > index2 ? 1 : -1); + }).forEach(item => { + const content = ( + <div className={itemClassName || ''} key={`top-area-${item.name}`}> + {item.content} + </div> + ); + if (item.align === 'center') { + center.push(content); + } else if (item.align === 'left') { + left.push(content); + } else { + right.push(content); + } + }); + let children = []; + if (left && left.length) { + children.push(<div className="lc-sub-top-area-left">{left}</div>); + } + if (center && center.length) { + children.push(<div className="lc-sub-top-area-center">{center}</div>); + } + if (right && right.length) { + children.push(<div className="lc-sub-top-area-right">{right}</div>); + } + return ( + <Fragment> + {children} + </Fragment> + ); + } +} diff --git a/packages/workspace/src/layouts/top-area.tsx b/packages/workspace/src/layouts/top-area.tsx index c6301470d..457e928d2 100644 --- a/packages/workspace/src/layouts/top-area.tsx +++ b/packages/workspace/src/layouts/top-area.tsx @@ -8,7 +8,7 @@ export default class TopArea extends Component<{ area: Area; itemClassName?: str render() { const { area, itemClassName } = this.props; - if (!area?.container?.items?.length) { + if (area.isEmpty()) { return null; } diff --git a/packages/workspace/src/layouts/workbench.less b/packages/workspace/src/layouts/workbench.less index 0217b8496..0639a1fa1 100644 --- a/packages/workspace/src/layouts/workbench.less +++ b/packages/workspace/src/layouts/workbench.less @@ -138,7 +138,7 @@ body { display: flex; flex-direction: column; background-color: #edeff3; - .lc-top-area { + .lc-top-area, .lc-sub-top-area { height: var(--top-area-height); background-color: var(--color-pane-background); width: 100%; @@ -150,18 +150,18 @@ body { display: flex; } - .lc-top-area-left { + .lc-top-area-left, .lc-sub-top-area-left { display: flex; align-items: center; } - .lc-top-area-center { + .lc-top-area-center, .lc-sub-top-area-center { flex: 1; display: flex; justify-content: center; margin: 0 8px; } - .lc-top-area-right { + .lc-top-area-right, .lc-sub-top-area-right { display: flex; align-items: center; > * { @@ -335,6 +335,7 @@ body { display: flex; flex-direction: column; z-index: 10; + position: relative; .lc-toolbar { display: flex; height: var(--toolbar-height); @@ -359,6 +360,12 @@ body { } } } + + .lc-workspace-workbench-window { + position: relative; + height: 100%; + } + .lc-right-area { height: 100%; width: var(--right-area-width); diff --git a/packages/workspace/src/layouts/workbench.tsx b/packages/workspace/src/layouts/workbench.tsx index c7b2762e4..66ddb7de0 100644 --- a/packages/workspace/src/layouts/workbench.tsx +++ b/packages/workspace/src/layouts/workbench.tsx @@ -11,10 +11,17 @@ import BottomArea from './bottom-area'; import './workbench.less'; import { SkeletonContext } from '../skeleton-context'; import { EditorConfig, PluginClassSet } from '@alilc/lowcode-types'; -import { Workspace } from '..'; +import { Workspace } from '../workspace'; +import SubTopArea from './sub-top-area'; @observer -export class Workbench extends Component<{ workspace: Workspace; config?: EditorConfig; components?: PluginClassSet; className?: string; topAreaItemClassName?: string }> { +export class Workbench extends Component<{ + workspace: Workspace; + config?: EditorConfig; + components?: PluginClassSet; + className?: string; + topAreaItemClassName?: string; +}> { constructor(props: any) { super(props); const { config, components, workspace } = this.props; @@ -34,8 +41,20 @@ export class Workbench extends Component<{ workspace: Workspace; config?: Editor <LeftFloatPane area={skeleton.leftFloatArea} /> <LeftFixedPane area={skeleton.leftFixedArea} /> <div className="lc-workspace-workbench-center"> - {/* <Toolbar area={skeleton.toolbar} /> */} - <EditorWindowView editorWindow={workspace.window} /> + <> + <SubTopArea area={skeleton.subTopArea} itemClassName={topAreaItemClassName} /> + <div className="lc-workspace-workbench-window"> + { + workspace.windows.map(d => ( + <EditorWindowView + active={d.id === workspace.window.id} + editorWindow={d} + key={d.id} + /> + )) + } + </div> + </> <MainArea area={skeleton.mainArea} /> <BottomArea area={skeleton.bottomArea} /> </div> diff --git a/packages/workspace/src/resource.ts b/packages/workspace/src/resource.ts index a1147713e..f881caf0f 100644 --- a/packages/workspace/src/resource.ts +++ b/packages/workspace/src/resource.ts @@ -19,6 +19,10 @@ export class Resource { this.options.init(ctx); } + get icon() { + return this.options.icon; + } + async import(schema: any) { return await this.options.import?.(schema); } @@ -38,4 +42,8 @@ export class Resource { 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 new file mode 100644 index 000000000..8b0fe29c4 --- /dev/null +++ b/packages/workspace/src/workspace.ts @@ -0,0 +1,169 @@ +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 { BasicContext } from './base-context'; +import { EditorWindow } from './editor-window/context'; +import { Resource } from './resource'; + +export { Resource } from './resource'; +export * from './editor-window/context'; +export * from './layouts/workbench'; + +enum event { + ChangeWindow = 'change_window', + + ChangeActiveWindow = 'change_active_window', +} + +export class Workspace implements IPublicApiWorkspace { + private context: BasicContext; + + private emitter: IEventBus = createModuleEventBus('workspace'); + + get skeleton() { + return this.context.innerSkeleton; + } + + get plugins() { + return this.context.innerPlugins; + } + + constructor( + readonly registryInnerPlugin: (designer: Designer, editor: Editor, plugins: Plugins) => Promise<void>, + readonly shellModelFactory: any, + ) { + this.init(); + makeObservable(this); + } + + init() { + this.initWindow(); + this.context = new BasicContext(this, ''); + } + + initWindow() { + if (!this.defaultResource) { + return; + } + const title = this.defaultResource.title; + this.window = new EditorWindow(this.defaultResource, 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; + } + + setActive(value: boolean) { + this._isActive = value; + } + + windows: EditorWindow[] = []; + + editorWindowMap: Map<string, EditorWindow> = new Map<string, EditorWindow>(); + + @obx.ref window: EditorWindow; + + private resources: Map<string, Resource> = new Map(); + + async registerResourceType(resourceName: string, resourceType: 'editor' | 'webview', options: IPublicResourceOptions): Promise<void> { + if (resourceType === 'editor') { + const resource = new Resource(options); + this.resources.set(resourceName, resource); + + if (!this.window && this.defaultResource) { + this.initWindow(); + } + } + } + + get defaultResource(): Resource | null { + if (this.resources.size > 1) { + return this.resources.values().next().value; + } + + return null; + } + + removeResourceType(resourceName: string) { + if (this.resources.has(resourceName)) { + this.resources.delete(resourceName); + } + } + + removeEditorWindowById(id: string) { + const index = this.windows.findIndex(d => (d.id === id)); + this.remove(index); + } + + private remove(index: number) { + const window = this.windows[index]; + this.windows = this.windows.splice(index - 1, 1); + if (this.window === window) { + this.window = this.windows[index] || this.windows[index + 1] || this.windows[index - 1]; + this.emitChangeActiveWindow(); + } + this.emitChangeWindow(); + } + + removeEditorWindow(resourceName: string, title: string) { + const index = this.windows.findIndex(d => (d.resourceName === resourceName && d.title)); + this.remove(index); + } + + openEditorWindowById(id: string) { + const window = this.editorWindowMap.get(id); + if (window) { + this.window = window; + this.emitChangeActiveWindow(); + } + } + + openEditorWindow(resourceName: string, title: string, viewType?: string) { + const resource = this.resources.get(resourceName); + if (!resource) { + console.error(`${resourceName} is not available`); + return; + } + const filterWindows = this.windows.filter(d => (d.resourceName === resourceName && d.title == title)); + if (filterWindows && filterWindows.length) { + this.window = filterWindows[0]; + this.emitChangeActiveWindow(); + return; + } + this.window = new EditorWindow(resource, this, title); + this.windows.push(this.window); + this.editorWindowMap.set(this.window.id, this.window); + this.emitChangeWindow(); + this.emitChangeActiveWindow(); + } + + onChangeWindows(fn: () => void) { + this.emitter.on(event.ChangeWindow, fn); + return () => { + this.emitter.removeListener(event.ChangeWindow, fn); + }; + } + + emitChangeWindow() { + this.emitter.emit(event.ChangeWindow); + } + + emitChangeActiveWindow() { + this.emitter.emit(event.ChangeActiveWindow); + } + + onChangeActiveWindow(fn: () => void) { + this.emitter.on(event.ChangeActiveWindow, fn); + return () => { + this.emitter.removeListener(event.ChangeActiveWindow, fn); + }; + } +} From 5a1e1bc052598e32c39b80082d7a5cc0fa323965 Mon Sep 17 00:00:00 2001 From: liujuping <liujup@foxmail.com> Date: Fri, 30 Dec 2022 11:00:31 +0800 Subject: [PATCH 14/14] chore: publish docs 1.0.12 --- docs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/package.json b/docs/package.json index 62747d46d..309eaf23b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-engine-docs", - "version": "1.0.11", + "version": "1.0.12", "description": "低代码引擎版本化文档", "license": "MIT", "files": [