From 33fd6bf6426f3300a1e95c80b3021fed26656872 Mon Sep 17 00:00:00 2001 From: liujuping Date: Thu, 13 Apr 2023 11:33:39 +0800 Subject: [PATCH 01/17] feat: added features in workspace mode --- .../src/builtin-simulator/create-simulator.ts | 2 + .../src/builtin-simulator/host-view.tsx | 5 +- packages/designer/src/plugin/plugin-types.ts | 2 + .../tests/builtin-simulator/host.test.ts | 14 +++++- .../src/components/settings/settings-pane.tsx | 29 ++++++----- packages/engine/src/engine-core.ts | 27 +++++++--- packages/renderer-core/src/hoc/leaf.tsx | 9 ++-- packages/shell/src/api/common.tsx | 7 +-- packages/shell/src/api/project.ts | 19 ++++--- packages/shell/src/api/setters.ts | 7 +++ packages/shell/src/api/workspace.ts | 4 +- packages/shell/src/index.ts | 2 + packages/shell/src/model/document-model.ts | 2 +- packages/types/src/shell/api/workspace.ts | 2 +- .../workspace/src/context/base-context.ts | 4 ++ packages/workspace/src/layouts/workbench.less | 3 ++ packages/workspace/src/resource.ts | 2 +- packages/workspace/src/window.ts | 16 +++++- packages/workspace/src/workspace.ts | 49 ++++++++++++++++--- 19 files changed, 155 insertions(+), 50 deletions(-) diff --git a/packages/designer/src/builtin-simulator/create-simulator.ts b/packages/designer/src/builtin-simulator/create-simulator.ts index e46582180..15ab3507b 100644 --- a/packages/designer/src/builtin-simulator/create-simulator.ts +++ b/packages/designer/src/builtin-simulator/create-simulator.ts @@ -20,7 +20,9 @@ export function createSimulator( ): Promise { const win: any = iframe.contentWindow; const doc = iframe.contentDocument!; + const innerPlugins = host.designer.editor.get('innerPlugins'); + win.AliLowCodeEngine = innerPlugins._getLowCodePluginContext({}); win.LCSimulatorHost = host; win._ = window._; diff --git a/packages/designer/src/builtin-simulator/host-view.tsx b/packages/designer/src/builtin-simulator/host-view.tsx index a7943ea9f..21e007930 100644 --- a/packages/designer/src/builtin-simulator/host-view.tsx +++ b/packages/designer/src/builtin-simulator/host-view.tsx @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { observer, globalContext } from '@alilc/lowcode-editor-core'; +import { observer } from '@alilc/lowcode-editor-core'; import { BuiltinSimulatorHost, BuiltinSimulatorProps } from './host'; import { BemTools } from './bem-tools'; import { Project } from '../project'; @@ -76,8 +76,7 @@ class Content extends Component<{ host: BuiltinSimulatorHost }> { private dispose?: () => void; componentDidMount() { - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); + const editor = this.props.host.designer.editor; const onEnableEvents = (type: boolean) => { this.setState({ disabledEvents: type, diff --git a/packages/designer/src/plugin/plugin-types.ts b/packages/designer/src/plugin/plugin-types.ts index f76e20827..23aac2849 100644 --- a/packages/designer/src/plugin/plugin-types.ts +++ b/packages/designer/src/plugin/plugin-types.ts @@ -16,6 +16,7 @@ import { IPublicApiWorkspace, IPublicTypePluginMeta, IPublicTypePluginRegisterOptions, + IPublicModelWindow, } from '@alilc/lowcode-types'; import PluginContext from './plugin-context'; @@ -56,6 +57,7 @@ export interface ILowCodePluginContextPrivate { set pluginEvent(event: IPublicApiEvent); set canvas(canvas: IPublicApiCanvas); set workspace(workspace: IPublicApiWorkspace); + set editorWindow(window: IPublicModelWindow); } export interface ILowCodePluginContextApiAssembler { assembleApis( diff --git a/packages/designer/tests/builtin-simulator/host.test.ts b/packages/designer/tests/builtin-simulator/host.test.ts index 61ae61742..57e74be57 100644 --- a/packages/designer/tests/builtin-simulator/host.test.ts +++ b/packages/designer/tests/builtin-simulator/host.test.ts @@ -1,3 +1,4 @@ +import { IPublicTypePluginMeta } from './../../../../lib/packages/types/src/shell/type/plugin-meta.d'; import '../fixtures/window'; import { Editor, @@ -22,6 +23,7 @@ import { BuiltinSimulatorHost } from '../../src/builtin-simulator/host'; import { fireEvent } from '@testing-library/react'; import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory'; import { Setters, Workspace } from '@alilc/lowcode-shell'; +import { ILowCodePluginContextApiAssembler, ILowCodePluginContextPrivate, LowCodePluginManager } from '@alilc/lowcode-designer'; describe('Host 测试', () => { let editor: Editor; @@ -32,10 +34,20 @@ describe('Host 测试', () => { beforeAll(() => { editor = new Editor(); - const innerWorkspace = new InnerWorkspace(); + const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + assembleApis: (context: ILowCodePluginContextPrivate, pluginName: string, meta: IPublicTypePluginMeta) => { + context.project = project; + const eventPrefix = meta?.eventPrefix || 'common'; + context.workspace = workspace; + }, + }; + const innerPlugins = new LowCodePluginManager(pluginContextApiAssembler); + const innerWorkspace = new InnerWorkspace(() => {}, {}); const workspace = new Workspace(innerWorkspace); editor.set('innerHotkey', new InnerHotkey()) editor.set('setters', new Setters(new InnerSetters())); + editor.set('innerPlugins' as any, innerPlugins); !globalContext.has(Editor) && globalContext.register(editor, Editor); !globalContext.has('workspace') && globalContext.register(innerWorkspace, 'workspace'); }); diff --git a/packages/editor-skeleton/src/components/settings/settings-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-pane.tsx index 46b188acc..31ea78cce 100644 --- a/packages/editor-skeleton/src/components/settings/settings-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-pane.tsx @@ -1,5 +1,5 @@ import { Component, MouseEvent, Fragment } from 'react'; -import { shallowIntl, observer, obx, engineConfig, runInAction, globalContext } from '@alilc/lowcode-editor-core'; +import { shallowIntl, observer, obx, engineConfig, runInAction } from '@alilc/lowcode-editor-core'; import { createContent, isJSSlot, isSetterConfig } from '@alilc/lowcode-utils'; import { Skeleton, Stage } from '@alilc/lowcode-editor-skeleton'; import { IPublicTypeCustomView } from '@alilc/lowcode-types'; @@ -40,7 +40,7 @@ class SettingFieldView extends Component { @@ -291,9 +291,8 @@ class SettingGroupView extends Component { const { field } = this.props; const { extraProps } = field; const { display } = extraProps; - const workspace = globalContext.get('workspace'); - const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor'); - const { stages } = editor.get('skeleton') as Skeleton; + const editor = this.props.field.designer?.editor; + const { stages } = editor?.get('skeleton') as Skeleton; // const items = field.items; let stageName; @@ -343,15 +342,15 @@ class SettingGroupView extends Component { } } -export function createSettingFieldView(item: ISettingField | IPublicTypeCustomView, field: ISettingEntry, index?: number) { - if (isSettingField(item)) { - if (item.isGroup) { - return ; +export function createSettingFieldView(field: ISettingField | IPublicTypeCustomView, fieldEntry: ISettingEntry, index?: number) { + if (isSettingField(field)) { + if (field.isGroup) { + return ; } else { - return ; + return ; } } else { - return createContent(item, { key: index, field }); + return createContent(field, { key: index, field: fieldEntry }); } } diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index b038df818..7148bb38a 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -14,6 +14,7 @@ import { IPublicTypeEngineOptions, IPublicModelDocumentModel, IPublicTypePluginMeta, + IPublicTypeDisposable, } from '@alilc/lowcode-types'; import { Designer, @@ -60,14 +61,26 @@ export * from './modules/skeleton-types'; export * from './modules/designer-types'; export * from './modules/lowcode-types'; -async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: Plugins) { +async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: Plugins): Promise { // 注册一批内置插件 + const componentMetaParserPlugin = componentMetaParser(designer); + const defaultPanelRegistryPlugin = defaultPanelRegistry(editor); await plugins.register(OutlinePlugin, {}, { autoInit: true }); - await plugins.register(componentMetaParser(designer)); + await plugins.register(componentMetaParserPlugin); await plugins.register(setterRegistry, {}); - await plugins.register(defaultPanelRegistry(editor)); + await plugins.register(defaultPanelRegistryPlugin); await plugins.register(builtinHotkey); await plugins.register(registerDefaults, {}, { autoInit: true }); + + return () => { + plugins.delete(OutlinePlugin.pluginName); + plugins.delete(componentMetaParserPlugin.pluginName); + plugins.delete(setterRegistry.pluginName); + plugins.delete(defaultPanelRegistryPlugin.pluginName); + plugins.delete(defaultPanelRegistryPlugin.pluginName); + plugins.delete(builtinHotkey.pluginName); + plugins.delete(registerDefaults.pluginName); + }; } const innerWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory); @@ -158,7 +171,7 @@ let engineContainer: HTMLElement; export const version = VERSION_PLACEHOLDER; engineConfig.set('ENGINE_VERSION', version); -registryInnerPlugin(designer, editor, plugins); +const pluginPromise = registryInnerPlugin(designer, editor, plugins); export async function init( container?: HTMLElement, @@ -183,10 +196,10 @@ export async function init( } engineConfig.setEngineOptions(engineOptions as any); - await plugins.init(pluginPreference as any); - const { Workbench } = common.skeletonCabin; if (options && options.enableWorkspaceMode) { + const disposeFun = await pluginPromise; + disposeFun && disposeFun(); render( createElement(WorkSpaceWorkbench, { workspace: innerWorkspace, @@ -202,6 +215,8 @@ export async function init( return; } + await plugins.init(pluginPreference as any); + render( createElement(Workbench, { skeleton: innerSkeleton, diff --git a/packages/renderer-core/src/hoc/leaf.tsx b/packages/renderer-core/src/hoc/leaf.tsx index 5df988048..98b267475 100644 --- a/packages/renderer-core/src/hoc/leaf.tsx +++ b/packages/renderer-core/src/hoc/leaf.tsx @@ -102,6 +102,9 @@ function initRerenderEvent({ return; } cache.event.get(schema.id)?.dispose.forEach((disposeFn: any) => disposeFn && disposeFn()); + const debounceRerender = debounce(() => { + container.rerender(); + }, 20); cache.event.set(schema.id, { clear: false, leaf, @@ -111,21 +114,21 @@ function initRerenderEvent({ return; } __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onPropsChange make rerender`); - container.rerender(); + debounceRerender(); }), leaf?.onChildrenChange?.(() => { if (!container.autoRepaintNode) { return; } __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onChildrenChange make rerender`); - container.rerender(); + debounceRerender(); }) as Function, leaf?.onVisibleChange?.(() => { if (!container.autoRepaintNode) { return; } __debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onVisibleChange make rerender`); - container.rerender(); + debounceRerender(); }), ], }); diff --git a/packages/shell/src/api/common.tsx b/packages/shell/src/api/common.tsx index 6a44dc82e..d9009ff8e 100644 --- a/packages/shell/src/api/common.tsx +++ b/packages/shell/src/api/common.tsx @@ -1,4 +1,4 @@ -import { editorSymbol, skeletonSymbol, designerCabinSymbol, designerSymbol } from '../symbols'; +import { editorSymbol, skeletonSymbol, designerCabinSymbol, designerSymbol, settingFieldSymbol } from '../symbols'; import { isFormEvent as innerIsFormEvent, compatibleLegaoSchema as innerCompatibleLegaoSchema, @@ -25,6 +25,7 @@ import { IPublicTypeLocationDetailType as InnerLocationDetailType, IPublicApiCommonEditorCabin, IPublicModelDragon, + IPublicModelSettingField, } from '@alilc/lowcode-types'; import { SettingField as InnerSettingField, @@ -168,8 +169,8 @@ class SkeletonCabin implements IPublicApiCommonSkeletonCabin { /** * @deprecated */ - createSettingFieldView(item: any, field: any) { - return innerCreateSettingFieldView(item, field); + createSettingFieldView(field: IPublicModelSettingField, fieldEntry: any) { + return innerCreateSettingFieldView((field as any)[settingFieldSymbol] || field, fieldEntry); } /** diff --git a/packages/shell/src/api/project.ts b/packages/shell/src/api/project.ts index ed45701b3..e33a038b9 100644 --- a/packages/shell/src/api/project.ts +++ b/packages/shell/src/api/project.ts @@ -18,6 +18,9 @@ import { import { DocumentModel as ShellDocumentModel } from '../model'; import { SimulatorHost } from './simulator-host'; import { editorSymbol, projectSymbol, simulatorHostSymbol, documentSymbol } from '../symbols'; +import { getLogger } from '@alilc/lowcode-utils'; + +const logger = getLogger({ level: 'warn', bizName: 'shell-project' }); const innerProjectSymbol = Symbol('innerProject'); export class Project implements IPublicApiProject { @@ -29,6 +32,10 @@ export class Project implements IPublicApiProject { } const workspace = globalContext.get('workspace'); if (workspace.isActive) { + if (!workspace.window.innerProject) { + logger.error('project api 调用时机出现问题,请检查'); + return this[innerProjectSymbol]; + } return workspace.window.innerProject; } @@ -43,8 +50,8 @@ export class Project implements IPublicApiProject { this[innerProjectSymbol] = project; } - static create(project: InnerProject) { - return new Project(project); + static create(project: InnerProject, workspaceMode: boolean = false) { + return new Project(project, workspaceMode); } /** @@ -225,15 +232,15 @@ export class Project implements IPublicApiProject { */ setConfig(key: T, value: IPublicTypeAppConfig[T]): void; setConfig(value: IPublicTypeAppConfig): void; - setConfig(...params: any[]): void{ - if(params.length === 2) { + setConfig(...params: any[]): void { + if (params.length === 2) { const oldConfig = this[projectSymbol].get('config'); this[projectSymbol].set('config', { ...oldConfig, [params[0]]: params[1], - }) + }); } else { - this[projectSymbol].set('config', params[0]) + this[projectSymbol].set('config', params[0]); } } } diff --git a/packages/shell/src/api/setters.ts b/packages/shell/src/api/setters.ts index 553f32c4e..72d29c8a8 100644 --- a/packages/shell/src/api/setters.ts +++ b/packages/shell/src/api/setters.ts @@ -1,10 +1,13 @@ import { IPublicTypeCustomView, IPublicApiSetters, IPublicTypeRegisteredSetter } from '@alilc/lowcode-types'; import { Setters as InnerSetters, globalContext } from '@alilc/lowcode-editor-core'; import { ReactNode } from 'react'; +import { getLogger } from '@alilc/lowcode-utils'; const innerSettersSymbol = Symbol('setters'); const settersSymbol = Symbol('setters'); +const logger = getLogger({ level: 'warn', bizName: 'shell-setters' }); + export class Setters implements IPublicApiSetters { readonly [innerSettersSymbol]: InnerSetters; @@ -15,6 +18,10 @@ export class Setters implements IPublicApiSetters { const workspace = globalContext.get('workspace'); if (workspace.isActive) { + if (!workspace.window.innerSetters) { + logger.error('setter api 调用时机出现问题,请检查'); + return this[innerSettersSymbol]; + } return workspace.window.innerSetters; } diff --git a/packages/shell/src/api/workspace.ts b/packages/shell/src/api/workspace.ts index 21015431f..aa56d0d37 100644 --- a/packages/shell/src/api/workspace.ts +++ b/packages/shell/src/api/workspace.ts @@ -35,8 +35,8 @@ export class Workspace implements IPublicApiWorkspace { this[workspaceSymbol].registerResourceType(resourceTypeModel); } - openEditorWindow(resourceName: string, title: string, extra: object, viewName?: string) { - this[workspaceSymbol].openEditorWindow(resourceName, title, extra, viewName); + openEditorWindow(resourceName: string, title: string, extra: object, viewName?: string, sleep?: boolean): void { + this[workspaceSymbol].openEditorWindow(resourceName, title, extra, viewName, sleep); } openEditorWindowById(id: string) { diff --git a/packages/shell/src/index.ts b/packages/shell/src/index.ts index 7017ca289..b3fca90b0 100644 --- a/packages/shell/src/index.ts +++ b/packages/shell/src/index.ts @@ -10,6 +10,7 @@ import { SettingTopEntry, Clipboard, SettingField, + Window, } from './model'; import { Project, @@ -50,6 +51,7 @@ export { Selection, Setters, Hotkey, + Window, Skeleton, SettingField as SettingPropEntry, SettingTopEntry, diff --git a/packages/shell/src/model/document-model.ts b/packages/shell/src/model/document-model.ts index 68252e3a5..06f3cebcd 100644 --- a/packages/shell/src/model/document-model.ts +++ b/packages/shell/src/model/document-model.ts @@ -90,7 +90,7 @@ export class DocumentModel implements IPublicModelDocumentModel { * @returns */ get project(): IPublicApiProject { - return ShellProject.create(this[documentSymbol].project); + return ShellProject.create(this[documentSymbol].project, true); } /** diff --git a/packages/types/src/shell/api/workspace.ts b/packages/types/src/shell/api/workspace.ts index 8c19846f7..4422dde4e 100644 --- a/packages/types/src/shell/api/workspace.ts +++ b/packages/types/src/shell/api/workspace.ts @@ -30,7 +30,7 @@ export interface IPublicApiWorkspace< registerResourceType(resourceTypeModel: IPublicTypeResourceType): void; /** 打开视图窗口 */ - openEditorWindow(resourceName: string, title: string, extra: Object, viewName?: string): void; + openEditorWindow(resourceName: string, title: string, extra: Object, viewName?: string, sleep?: boolean): void; /** 通过视图 id 打开窗口 */ openEditorWindowById(id: string): void; diff --git a/packages/workspace/src/context/base-context.ts b/packages/workspace/src/context/base-context.ts index e74ae2ffb..57a38712b 100644 --- a/packages/workspace/src/context/base-context.ts +++ b/packages/workspace/src/context/base-context.ts @@ -30,6 +30,7 @@ import { Common, Logger, Workspace, + Window, Canvas, } from '@alilc/lowcode-shell'; import { @@ -164,6 +165,9 @@ export class BasicContext implements IBasicContext { context.plugins = plugins; context.logger = new Logger({ level: 'warn', bizName: `plugin:${pluginName}` }); context.canvas = canvas; + if (editorWindow) { + context.editorWindow = new Window(editorWindow); + } }, }; diff --git a/packages/workspace/src/layouts/workbench.less b/packages/workspace/src/layouts/workbench.less index 95574871a..c8c89d6f0 100644 --- a/packages/workspace/src/layouts/workbench.less +++ b/packages/workspace/src/layouts/workbench.less @@ -368,6 +368,9 @@ body { right: 0; bottom: 0; left: 0; + flex-direction: column; + display: flex; + align-content: stretch; } .engine-actionitem { diff --git a/packages/workspace/src/resource.ts b/packages/workspace/src/resource.ts index 10cb7f0a1..222cb1162 100644 --- a/packages/workspace/src/resource.ts +++ b/packages/workspace/src/resource.ts @@ -39,7 +39,7 @@ export class Resource implements IResource { } get viewName() { - return this.resourceData.viewName || (this.resourceData as any).viewType; + return this.resourceData.viewName || (this.resourceData as any).viewType || this.defaultViewType; } get description() { diff --git a/packages/workspace/src/window.ts b/packages/workspace/src/window.ts index 96707dcb8..1b9bec410 100644 --- a/packages/workspace/src/window.ts +++ b/packages/workspace/src/window.ts @@ -10,6 +10,7 @@ interface IWindowCOnfig { title: string | undefined; options?: Object; viewType?: string | undefined; + sleep?: boolean; } export interface IEditorWindow extends Omit, 'changeViewType'> { @@ -18,6 +19,12 @@ export interface IEditorWindow extends Omit, 'chan editorViews: Map; changeViewType: (name: string, ignoreEmit?: boolean) => void; + + initReady: boolean; + + sleep?: boolean; + + init(): void; } export class EditorWindow implements IEditorWindow { @@ -36,11 +43,16 @@ export class EditorWindow implements IEditorWindow { @obx initReady = false; + sleep: boolean | undefined; + constructor(readonly resource: IResource, readonly workspace: IWorkspace, private config: IWindowCOnfig) { makeObservable(this); - this.init(); this.title = config.title; this.icon = resource.icon; + this.sleep = config.sleep; + if (!config.sleep) { + this.init(); + } } async importSchema(schema: any) { @@ -73,6 +85,8 @@ export class EditorWindow implements IEditorWindow { this.url = await this.resource.url(); this.setDefaultViewType(); this.initReady = true; + this.workspace.checkWindowQueue(); + this.sleep = false; } initViewTypes = async () => { diff --git a/packages/workspace/src/workspace.ts b/packages/workspace/src/workspace.ts index d959c55eb..30c8fcdad 100644 --- a/packages/workspace/src/workspace.ts +++ b/packages/workspace/src/workspace.ts @@ -30,6 +30,8 @@ export interface IWorkspace extends Omit Promise, readonly shellModelFactory: any, @@ -77,6 +86,17 @@ export class Workspace implements IWorkspace { makeObservable(this); } + checkWindowQueue() { + if (!this.windowQueue || !this.windowQueue.length) { + return; + } + + const windowInfo = this.windowQueue.shift(); + if (windowInfo) { + this.openEditorWindow(windowInfo.name, windowInfo.title, windowInfo.options, windowInfo.viewType); + } + } + init() { this.initWindow(); this.context = new BasicContext(this, ''); @@ -86,13 +106,13 @@ export class Workspace implements IWorkspace { if (!this.defaultResourceType) { return; } - const title = this.defaultResourceType.name; + const resourceName = this.defaultResourceType.name; const resource = new Resource({ - resourceName: title, + resourceName, options: {}, }, this.defaultResourceType, this); this.window = new EditorWindow(resource, this, { - title, + title: resource.title, }); this.editorWindowMap.set(this.window.id, this.window); this.windows.push(this.window); @@ -167,7 +187,13 @@ export class Workspace implements IWorkspace { } } - openEditorWindow(name: string, title: string, options: Object, viewType?: string) { + openEditorWindow(name: string, title: string, options: Object, viewType?: string, sleep?: boolean) { + if (!this.window?.initReady && !sleep) { + this.windowQueue.push({ + name, title, options, viewType, + }); + return; + } const resourceType = this.resourceTypeMap.get(name); if (!resourceType) { console.error(`${name} resourceType is not available`); @@ -176,6 +202,11 @@ export class Workspace implements IWorkspace { const filterWindows = this.windows.filter(d => (d.resource?.name === name && d.resource.title == title)); if (filterWindows && filterWindows.length) { this.window = filterWindows[0]; + if (!sleep && this.window.sleep) { + this.window.init(); + } else { + this.checkWindowQueue(); + } this.emitChangeActiveWindow(); return; } @@ -184,13 +215,17 @@ export class Workspace implements IWorkspace { title, options, }, resourceType, this); - this.window = new EditorWindow(resource, this, { + const window = new EditorWindow(resource, this, { title, options, viewType, + sleep, }); - this.windows = [...this.windows, this.window]; - this.editorWindowMap.set(this.window.id, this.window); + this.windows = [...this.windows, window]; + this.editorWindowMap.set(window.id, window); + if (!sleep) { + this.window = window; + } this.emitChangeWindow(); this.emitChangeActiveWindow(); } From f1ff1a07065160a61f103b249c108709976c9589 Mon Sep 17 00:00:00 2001 From: liujuping Date: Thu, 13 Apr 2023 14:58:08 +0800 Subject: [PATCH 02/17] fix: fix that the outline tree does not respond to modal addition/deletion related operations --- .../src/views/tree-node.tsx | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/plugin-outline-pane/src/views/tree-node.tsx b/packages/plugin-outline-pane/src/views/tree-node.tsx index 712dc20fa..331397a0b 100644 --- a/packages/plugin-outline-pane/src/views/tree-node.tsx +++ b/packages/plugin-outline-pane/src/views/tree-node.tsx @@ -9,6 +9,8 @@ import { IPublicModelPluginContext, IPublicModelModalNodesManager, IPublicTypeDi class ModalTreeNodeView extends PureComponent<{ treeNode: TreeNode; pluginContext: IPublicModelPluginContext; +}, { + treeChildren: TreeNode[] | null; }> { private modalNodesManager: IPublicModelModalNodesManager | undefined | null; readonly pluginContext: IPublicModelPluginContext; @@ -20,18 +22,36 @@ class ModalTreeNodeView extends PureComponent<{ this.pluginContext = props.pluginContext; const { project } = this.pluginContext; this.modalNodesManager = project.currentDocument?.modalNodesManager; + this.state = { + treeChildren: this.rootTreeNode.children, + }; } hideAllNodes() { this.modalNodesManager?.hideModalNodes(); } - render() { + componentDidMount(): void { + const rootTreeNode = this.rootTreeNode; + rootTreeNode.onExpandableChanged(() => { + this.setState({ + treeChildren: rootTreeNode.children, + }); + }); + } + + get rootTreeNode() { const { treeNode } = this.props; // 当指定了新的根节点时,要从原始的根节点去获取模态节点 const { project } = this.pluginContext; const rootNode = project.currentDocument?.root; const rootTreeNode = treeNode.tree.getTreeNode(rootNode!); + + return rootTreeNode; + } + + render() { + const rootTreeNode = this.rootTreeNode; const { expanded } = rootTreeNode; const hasVisibleModalNode = !!this.modalNodesManager?.getVisibleModalNode(); @@ -49,7 +69,7 @@ class ModalTreeNodeView extends PureComponent<{
Date: Mon, 17 Apr 2023 11:31:19 +0800 Subject: [PATCH 03/17] fix(utils): isReactComponent not including react.memo --- .github/workflows/cov packages.yml | 4 +- .github/workflows/test packages.yml | 52 ++++++++++++++++++- packages/renderer-core/jest.config.js | 1 + .../renderer-core/tests/utils/common.test.ts | 2 +- packages/utils/jest.config.js | 17 ++++-- packages/utils/src/is-react.ts | 11 +++- .../build-components/buildComponents.test.ts | 10 +++- packages/utils/test/src/is-react.test.ts | 38 ++++++++++++++ 8 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 packages/utils/test/src/is-react.test.ts diff --git a/.github/workflows/cov packages.yml b/.github/workflows/cov packages.yml index 499750282..7f92e1009 100644 --- a/.github/workflows/cov packages.yml +++ b/.github/workflows/cov packages.yml @@ -73,7 +73,7 @@ jobs: package-manager: yarn annotations: none -cov-utils: + cov-utils: runs-on: ubuntu-latest # skip fork's PR, otherwise it fails while making a comment if: ${{ github.event.pull_request.head.repo.full_name == 'alibaba/lowcode-engine' }} @@ -91,6 +91,6 @@ cov-utils: - uses: ArtiomTr/jest-coverage-report-action@v2 with: working-directory: packages/utils - test-script: npm test + test-script: npm test -- --jest-ci --jest-json --jest-coverage --jest-testLocationInResults --jest-outputFile=report.json package-manager: yarn annotations: none \ No newline at end of file diff --git a/.github/workflows/test packages.yml b/.github/workflows/test packages.yml index 484ef849a..5d1ee8907 100644 --- a/.github/workflows/test packages.yml +++ b/.github/workflows/test packages.yml @@ -43,7 +43,7 @@ jobs: - name: test run: cd packages/designer && npm test - editor-skeleton: + test-editor-skeleton: runs-on: ubuntu-latest steps: - name: checkout @@ -57,4 +57,52 @@ jobs: run: npm i && npm run setup:skip-build - name: test - run: cd packages/editor-skeleton && npm test \ No newline at end of file + run: cd packages/editor-skeleton && npm test + + test-renderer-core: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + + - uses: actions/setup-node@v2 + with: + node-version: '14' + + - name: install + run: npm i && npm run setup:skip-build + + - name: test + run: cd packages/renderer-core && npm test + + test-react-simulator-renderer: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + + - uses: actions/setup-node@v2 + with: + node-version: '14' + + - name: install + run: npm i && npm run setup:skip-build + + - name: test + run: cd packages/react-simulator-renderer && npm test + + test-utils: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + + - uses: actions/setup-node@v2 + with: + node-version: '14' + + - name: install + run: npm i && npm run setup:skip-build + + - name: test + run: cd packages/utils && npm test \ No newline at end of file diff --git a/packages/renderer-core/jest.config.js b/packages/renderer-core/jest.config.js index 652a76e70..2489490fa 100644 --- a/packages/renderer-core/jest.config.js +++ b/packages/renderer-core/jest.config.js @@ -11,6 +11,7 @@ const jestConfig = { // }, // testMatch: ['(/tests?/.*(test))\\.[jt]s$'], // testMatch: ['**/*/base.test.tsx'], + // testMatch: ['**/utils/common.test.ts'], transformIgnorePatterns: [ `/node_modules/(?!${esModules})/`, ], diff --git a/packages/renderer-core/tests/utils/common.test.ts b/packages/renderer-core/tests/utils/common.test.ts index 6fac55024..995e55642 100644 --- a/packages/renderer-core/tests/utils/common.test.ts +++ b/packages/renderer-core/tests/utils/common.test.ts @@ -374,7 +374,7 @@ describe('test parseThisRequiredExpression', () => { }; const fn = logger.error = jest.fn(); parseThisRequiredExpression(mockExpression, { state: { text: 'text' } }); - expect(fn).toBeCalledWith('parseExpression.error', new ReferenceError('state is not defined'), {"type": "JSExpression", "value": "state.text"}, {"state": {"text": "text"}}); + expect(fn).toBeCalledWith(' parseExpression.error', new ReferenceError('state is not defined'), {"type": "JSExpression", "value": "state.text"}, {"state": {"text": "text"}}); }); it('[success] JSExpression handle without this use scopeValue', () => { diff --git a/packages/utils/jest.config.js b/packages/utils/jest.config.js index 0e05687d7..328ea622e 100644 --- a/packages/utils/jest.config.js +++ b/packages/utils/jest.config.js @@ -1,9 +1,20 @@ -module.exports = { +const fs = require('fs'); +const { join } = require('path'); +const pkgNames = fs.readdirSync(join('..')).filter(pkgName => !pkgName.startsWith('.')); + +const jestConfig = { moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], - collectCoverage: true, + collectCoverage: false, collectCoverageFrom: [ - 'src/**/*.{ts,tsx}', + 'src/**/*.ts', + '!src/**/*.d.ts', '!**/node_modules/**', '!**/vendor/**', ], }; + +// 只对本仓库内的 pkg 做 mapping +jestConfig.moduleNameMapper = {}; +jestConfig.moduleNameMapper[`^@alilc/lowcode\\-(${pkgNames.join('|')})$`] = '/../$1/src'; + +module.exports = jestConfig; \ No newline at end of file diff --git a/packages/utils/src/is-react.ts b/packages/utils/src/is-react.ts index 1f17f9afc..07568db98 100644 --- a/packages/utils/src/is-react.ts +++ b/packages/utils/src/is-react.ts @@ -3,6 +3,7 @@ import { cloneEnumerableProperty } from './clone-enumerable-property'; const hasSymbol = typeof Symbol === 'function' && Symbol.for; const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0; +const REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3; export function isReactClass(obj: any): obj is ComponentClass { return obj && obj.prototype && (obj.prototype.isReactComponent || obj.prototype instanceof Component); @@ -16,8 +17,16 @@ function isForwardRefType(obj: any): boolean { return obj?.$$typeof && obj?.$$typeof === REACT_FORWARD_REF_TYPE; } +function isMemoType(obj: any): boolean { + return obj?.$$typeof && obj.$$typeof === REACT_MEMO_TYPE; +} + export function isReactComponent(obj: any): obj is ComponentType { - return obj && (isReactClass(obj) || typeof obj === 'function' || isForwardRefType(obj)); + if (!obj) { + return false; + } + + return Boolean(isReactClass(obj) || typeof obj === 'function' || isForwardRefType(obj) || isMemoType(obj)); } export function wrapReactClass(view: FunctionComponent) { diff --git a/packages/utils/test/src/build-components/buildComponents.test.ts b/packages/utils/test/src/build-components/buildComponents.test.ts index 5662aa12c..e854890da 100644 --- a/packages/utils/test/src/build-components/buildComponents.test.ts +++ b/packages/utils/test/src/build-components/buildComponents.test.ts @@ -309,8 +309,14 @@ describe('build-component', () => { )) .toEqual({ Button: { - componentName: 'Component', - schema: {}, + componentsMap: [], + componentsTree: [ + { + componentName: 'Component', + schema: {}, + } + ], + version: "", }, }); }) diff --git a/packages/utils/test/src/is-react.test.ts b/packages/utils/test/src/is-react.test.ts new file mode 100644 index 000000000..74c88c933 --- /dev/null +++ b/packages/utils/test/src/is-react.test.ts @@ -0,0 +1,38 @@ +import React from "react"; +import { isReactComponent, wrapReactClass } from "../../src/is-react"; + +class reactDemo extends React.Component { + +} + +const reactMemo = React.memo(reactDemo); + +const reactForwardRef = React.forwardRef((props, ref): any => { + return ''; +}); + +describe('is-react-ut', () => { + it('isReactComponent', () => { + expect(isReactComponent(null)).toBeFalsy(); + expect(isReactComponent(() => {})).toBeTruthy(); + expect(isReactComponent({ + $$typeof: Symbol.for('react.memo') + })).toBeTruthy(); + expect(isReactComponent({ + $$typeof: Symbol.for('react.forward_ref') + })).toBeTruthy(); + expect(isReactComponent(reactDemo)).toBeTruthy(); + expect(isReactComponent(reactMemo)).toBeTruthy(); + expect(isReactComponent(reactForwardRef)).toBeTruthy(); + + }); + + it('wrapReactClass', () => { + const wrap = wrapReactClass(() => {}); + expect(isReactComponent(wrap)).toBeTruthy(); + + const fun = () => {}; + fun.displayName = 'mock'; + expect(wrapReactClass(fun).displayName).toBe('mock'); + }) +}) \ No newline at end of file From 016d54bddeb53268bcb36977733fa5fafb656e4d Mon Sep 17 00:00:00 2001 From: liujuping Date: Mon, 17 Apr 2023 16:35:57 +0800 Subject: [PATCH 04/17] feat(common): add some api to be compatible with old platforms --- packages/shell/src/api/common.tsx | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/shell/src/api/common.tsx b/packages/shell/src/api/common.tsx index d9009ff8e..9f38cf109 100644 --- a/packages/shell/src/api/common.tsx +++ b/packages/shell/src/api/common.tsx @@ -58,6 +58,9 @@ import { untracked as innerUntracked, computed as innerComputed, observer as innerObserver, + action as innerAction, + runInAction as innerRunInAction, + engineConfig as innerEngineConfig, } from '@alilc/lowcode-editor-core'; import { Dragon as ShellDragon } from '../model'; import { ReactNode } from 'react'; @@ -302,6 +305,27 @@ class EditorCabin implements IPublicApiCommonEditorCabin { return innerObx; } + /** + * @deprecated + */ + get action() { + return innerAction; + } + + /** + * @deprecated + */ + get engineConfig() { + return innerEngineConfig; + } + + /** + * @deprecated + */ + get runInAction() { + return innerRunInAction; + } + /** * @deprecated */ From 5c49044a7738713fa5f883f1fa1afa11a898d9bf Mon Sep 17 00:00:00 2001 From: liujuping Date: Mon, 17 Apr 2023 17:37:33 +0800 Subject: [PATCH 05/17] feat(renderer-core): added log when executing setState --- packages/renderer-core/src/renderer/page.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/renderer-core/src/renderer/page.tsx b/packages/renderer-core/src/renderer/page.tsx index 9ba49c723..16d55e01b 100644 --- a/packages/renderer-core/src/renderer/page.tsx +++ b/packages/renderer-core/src/renderer/page.tsx @@ -1,6 +1,9 @@ +import { getLogger } from '@alilc/lowcode-utils'; import baseRendererFactory from './base'; import { IBaseRendererProps, IBaseRenderComponent } from '../types'; +const logger = getLogger({ level: 'warn', bizName: 'renderer-core:page' }); + export default function pageRendererFactory(): IBaseRenderComponent { const BaseRenderer = baseRendererFactory(); return class PageRenderer extends BaseRenderer { @@ -29,6 +32,11 @@ export default function pageRendererFactory(): IBaseRenderComponent { super.componentDidUpdate?.(prevProps, _prevState, snapshot); } + setState(state: any, callback?: () => void) { + logger.info('page set state', state); + super.setState(state, callback); + } + render() { const { __schema, __components } = this.props; if (this.__checkSchema(__schema)) { From 2c38c5d9a03dce36f80941e8672d9b1b69562de0 Mon Sep 17 00:00:00 2001 From: liujuping Date: Mon, 17 Apr 2023 17:41:12 +0800 Subject: [PATCH 06/17] feat: remove editor api --- packages/engine/src/engine-core.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index 7148bb38a..a94f192d4 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -152,8 +152,6 @@ export { logger, hotkey, common, - // 兼容原 editor 的事件功能 - event as editor, workspace, canvas, }; From aef10fca0dc73b99ebea76d51ea5dd3d719604ee Mon Sep 17 00:00:00 2001 From: liujuping Date: Tue, 18 Apr 2023 11:50:23 +0800 Subject: [PATCH 07/17] feat(material): material apis add refreshComponentMetasMap function --- docs/docs/api/material.md | 10 ++++++++++ packages/shell/src/api/material.ts | 11 +++++++++-- packages/types/src/shell/api/material.ts | 6 ++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/docs/api/material.md b/docs/docs/api/material.md index b52ad8cb2..5a5502fda 100644 --- a/docs/docs/api/material.md +++ b/docs/docs/api/material.md @@ -245,6 +245,7 @@ material.getComponentMeta('Input'); ``` #### getComponentMetasMap + 获取所有已注册的物料元数据 ```typescript @@ -264,6 +265,15 @@ import { material } from '@alilc/lowcode-engine'; material.getComponentMetasMap(); ``` +#### refreshComponentMetasMap + +刷新 componentMetasMap,可触发模拟器里的 components 重新构建 + +**@since v1.1.7** + +```typescript + refreshComponentMetasMap(): void; +``` ### 物料元数据管道函数 #### registerMetadataTransducer diff --git a/packages/shell/src/api/material.ts b/packages/shell/src/api/material.ts index e3ebd20ec..ea9d6e01b 100644 --- a/packages/shell/src/api/material.ts +++ b/packages/shell/src/api/material.ts @@ -143,9 +143,16 @@ export class Material implements IPublicApiMaterial { * 在设计器辅助层增加一个扩展 action * @param action */ - addBuiltinComponentAction(action: IPublicTypeComponentAction) { + addBuiltinComponentAction = (action: IPublicTypeComponentAction) => { this[designerSymbol].componentActions.addBuiltinComponentAction(action); - } + }; + + /** + * 刷新 componentMetasMap,可触发模拟器里的 components 重新构建 + */ + refreshComponentMetasMap = () => { + this[designerSymbol].refreshComponentMetasMap(); + }; /** * 移除设计器辅助层的指定 action diff --git a/packages/types/src/shell/api/material.ts b/packages/types/src/shell/api/material.ts index 1e9f54996..d64455edd 100644 --- a/packages/types/src/shell/api/material.ts +++ b/packages/types/src/shell/api/material.ts @@ -122,4 +122,10 @@ export interface IPublicApiMaterial { * @param fn */ onChangeAssets(fn: () => void): IPublicTypeDisposable; + + /** + * 刷新 componentMetasMap,可触发模拟器里的 components 重新构建 + * @since v1.1.7 + */ + refreshComponentMetasMap(): void; } From 82cdafe07ec57ada9174139ae6f56b5f88c923e1 Mon Sep 17 00:00:00 2001 From: liujuping Date: Wed, 19 Apr 2023 11:55:42 +0800 Subject: [PATCH 08/17] docs(config): add demo for config.getPreference api --- docs/docs/api/config.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/docs/api/config.md b/docs/docs/api/config.md index ea4c7dbfc..9294b9d28 100644 --- a/docs/docs/api/config.md +++ b/docs/docs/api/config.md @@ -105,6 +105,17 @@ getPreference(): IPublicModelPreference; **@since v1.1.0** +示例 + +```javascript +import { config } from '@alilc/lowcode-engine'; + +const panelName = 'outline-master-pane'; + +// 设置大纲树面板钉住,在大纲树下次重新打开时生效 +config.getPreference().set(`${panelName}-pinned-status-isFloat`, false, 'skeleton') +``` + ## 事件 ### onceGot From dff06e70ac56466c9c256e54ed95c520dbe41391 Mon Sep 17 00:00:00 2001 From: liujuping Date: Wed, 19 Apr 2023 15:39:31 +0800 Subject: [PATCH 09/17] fix(prop): fix prop.export can not correctly export undefined[] --- packages/designer/src/document/node/props/prop.ts | 6 +----- packages/designer/tests/document/node/props/prop.test.ts | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/designer/src/document/node/props/prop.ts b/packages/designer/src/document/node/props/prop.ts index b1fd1aec4..5dc1ea5df 100644 --- a/packages/designer/src/document/node/props/prop.ts +++ b/packages/designer/src/document/node/props/prop.ts @@ -336,13 +336,9 @@ export class Prop implements IProp, IPropParent { if (!this._items) { return this._value; } - const values = this.items!.map((prop) => { + return this.items!.map((prop) => { return prop?.export(stage); }); - if (values.every((val) => val === undefined)) { - return undefined; - } - return values; } } diff --git a/packages/designer/tests/document/node/props/prop.test.ts b/packages/designer/tests/document/node/props/prop.test.ts index 932733b1a..177bc5247 100644 --- a/packages/designer/tests/document/node/props/prop.test.ts +++ b/packages/designer/tests/document/node/props/prop.test.ts @@ -435,7 +435,7 @@ describe('Prop 类测试', () => { it('should return undefined when all items are undefined', () => { prop = new Prop(mockPropsInst, [undefined, undefined], '___loopArgs___'); - expect(prop.getValue()).toBeUndefined(); + expect(prop.getValue()).toEqual([undefined, undefined]); }); it('迭代器 / map / forEach', () => { From 9b50bc700e18e02d590d7a77591809cdbad8a160 Mon Sep 17 00:00:00 2001 From: liujuping Date: Wed, 19 Apr 2023 15:27:34 +0800 Subject: [PATCH 10/17] feat(workspace): add enableAutoOpenFirstWindow config and onWindowRendererReady function --- packages/editor-core/src/config.ts | 5 +++ packages/engine/src/engine-core.ts | 13 ++++-- packages/shell/src/api/project.ts | 2 +- packages/shell/src/api/skeleton.ts | 7 +++ packages/shell/src/api/workspace.ts | 7 +++ packages/shell/src/model/window.ts | 4 ++ packages/types/src/shell/api/workspace.ts | 8 +++- .../types/src/shell/type/engine-options.ts | 9 ++++ .../workspace/src/context/view-context.ts | 16 +++++-- packages/workspace/src/layouts/workbench.tsx | 2 +- packages/workspace/src/window.ts | 7 +++ packages/workspace/src/workspace.ts | 44 +++++++++++++------ 12 files changed, 101 insertions(+), 23 deletions(-) diff --git a/packages/editor-core/src/config.ts b/packages/editor-core/src/config.ts index ef889e727..4f0f708e7 100644 --- a/packages/editor-core/src/config.ts +++ b/packages/editor-core/src/config.ts @@ -145,6 +145,11 @@ const VALID_ENGINE_OPTIONS = { type: 'function', description: '配置指定节点为根组件', }, + enableAutoOpenFirstWindow: { + type: 'boolean', + description: '应用级设计模式下,自动打开第一个窗口', + default: true, + }, }; const getStrictModeValue = (engineOptions: IPublicTypeEngineOptions, defaultValue: boolean): boolean => { diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index a94f192d4..f0c283ec1 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -9,12 +9,15 @@ import { engineConfig, Setters as InnerSetters, Hotkey as InnerHotkey, + IEditor, } from '@alilc/lowcode-editor-core'; import { IPublicTypeEngineOptions, IPublicModelDocumentModel, IPublicTypePluginMeta, IPublicTypeDisposable, + IPublicApiPlugins, + IPublicApiWorkspace, } from '@alilc/lowcode-types'; import { Designer, @@ -22,6 +25,7 @@ import { ILowCodePluginContextPrivate, ILowCodePluginContextApiAssembler, PluginPreference, + IDesigner, } from '@alilc/lowcode-designer'; import { Skeleton as InnerSkeleton, @@ -30,6 +34,7 @@ import { import { Workspace as InnerWorkspace, Workbench as WorkSpaceWorkbench, + IWorkspace, } from '@alilc/lowcode-workspace'; import { @@ -61,7 +66,7 @@ export * from './modules/skeleton-types'; export * from './modules/designer-types'; export * from './modules/lowcode-types'; -async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: Plugins): Promise { +async function registryInnerPlugin(designer: IDesigner, editor: IEditor, plugins: IPublicApiPlugins): Promise { // 注册一批内置插件 const componentMetaParserPlugin = componentMetaParser(designer); const defaultPanelRegistryPlugin = defaultPanelRegistry(editor); @@ -83,8 +88,8 @@ async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: }; } -const innerWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory); -const workspace = new Workspace(innerWorkspace); +const innerWorkspace: IWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory); +const workspace: IPublicApiWorkspace = new Workspace(innerWorkspace); const editor = new Editor(); globalContext.register(editor, Editor); globalContext.register(editor, 'editor'); @@ -207,7 +212,9 @@ export async function init( }), engineContainer, ); + innerWorkspace.enableAutoOpenFirstWindow = engineConfig.get('enableAutoOpenFirstWindow', true); innerWorkspace.setActive(true); + innerWorkspace.initWindow(); innerHotkey.activate(false); await innerWorkspace.plugins.init(pluginPreference); return; diff --git a/packages/shell/src/api/project.ts b/packages/shell/src/api/project.ts index e33a038b9..f005d0af0 100644 --- a/packages/shell/src/api/project.ts +++ b/packages/shell/src/api/project.ts @@ -32,7 +32,7 @@ export class Project implements IPublicApiProject { } const workspace = globalContext.get('workspace'); if (workspace.isActive) { - if (!workspace.window.innerProject) { + if (!workspace.window?.innerProject) { logger.error('project api 调用时机出现问题,请检查'); return this[innerProjectSymbol]; } diff --git a/packages/shell/src/api/skeleton.ts b/packages/shell/src/api/skeleton.ts index 928c55a0d..cb5c8f8aa 100644 --- a/packages/shell/src/api/skeleton.ts +++ b/packages/shell/src/api/skeleton.ts @@ -5,9 +5,12 @@ import { } from '@alilc/lowcode-editor-skeleton'; import { skeletonSymbol } from '../symbols'; import { IPublicApiSkeleton, IPublicTypeDisposable, IPublicTypeSkeletonConfig, IPublicTypeWidgetConfigArea } from '@alilc/lowcode-types'; +import { getLogger } from '@alilc/lowcode-utils'; const innerSkeletonSymbol = Symbol('skeleton'); +const logger = getLogger({ level: 'warn', bizName: 'shell-skeleton' }); + export class Skeleton implements IPublicApiSkeleton { private readonly [innerSkeletonSymbol]: ISkeleton; private readonly pluginName: string; @@ -18,6 +21,10 @@ export class Skeleton implements IPublicApiSkeleton { } const workspace = globalContext.get('workspace'); if (workspace.isActive) { + if (!workspace.window.innerSkeleton) { + logger.error('skeleton api 调用时机出现问题,请检查'); + return this[innerSkeletonSymbol]; + } return workspace.window.innerSkeleton; } diff --git a/packages/shell/src/api/workspace.ts b/packages/shell/src/api/workspace.ts index aa56d0d37..9676b8522 100644 --- a/packages/shell/src/api/workspace.ts +++ b/packages/shell/src/api/workspace.ts @@ -28,9 +28,16 @@ export class Workspace implements IPublicApiWorkspace { } get window() { + if (!this[workspaceSymbol].window) { + return null; + } return new ShellWindow(this[workspaceSymbol].window); } + onWindowRendererReady(fn: () => void): IPublicTypeDisposable { + return this[workspaceSymbol].onWindowRendererReady(fn); + } + registerResourceType(resourceTypeModel: IPublicTypeResourceType): void { this[workspaceSymbol].registerResourceType(resourceTypeModel); } diff --git a/packages/shell/src/model/window.ts b/packages/shell/src/model/window.ts index b1263d541..23bc5c06d 100644 --- a/packages/shell/src/model/window.ts +++ b/packages/shell/src/model/window.ts @@ -41,4 +41,8 @@ export class Window implements IPublicModelWindow { async save() { return await this[windowSymbol].save(); } + + get plugins() { + return this[windowSymbol].plugins; + } } diff --git a/packages/types/src/shell/api/workspace.ts b/packages/types/src/shell/api/workspace.ts index 4422dde4e..8904a8231 100644 --- a/packages/types/src/shell/api/workspace.ts +++ b/packages/types/src/shell/api/workspace.ts @@ -10,7 +10,7 @@ export interface IPublicApiWorkspace< isActive: boolean; /** 当前设计器窗口 */ - window: ModelWindow; + window: ModelWindow | null; plugins: Plugins; @@ -46,4 +46,10 @@ export interface IPublicApiWorkspace< /** active 窗口变更事件 */ onChangeActiveWindow(fn: () => void): IPublicTypeDisposable; + + /** + * window 下的所有视图 renderer ready 事件 + * @since v1.1.7 + */ + onWindowRendererReady(fn: () => void): IPublicTypeDisposable; } \ No newline at end of file diff --git a/packages/types/src/shell/type/engine-options.ts b/packages/types/src/shell/type/engine-options.ts index 195db8912..f17716653 100644 --- a/packages/types/src/shell/type/engine-options.ts +++ b/packages/types/src/shell/type/engine-options.ts @@ -2,6 +2,7 @@ import { RequestHandlersMap } from '@alilc/lowcode-datasource-types'; import { ComponentType } from 'react'; export interface IPublicTypeEngineOptions { + /** * 是否开启 condition 的能力,默认在设计器中不管 condition 是啥都正常展示 * when this is true, node that configured as conditional not renderring @@ -136,8 +137,10 @@ export interface IPublicTypeEngineOptions { * 与 react-renderer 的 appHelper 一致,https://lowcode-engine.cn/site/docs/guide/expand/runtime/renderer#apphelper */ appHelper?: { + /** 全局公共函数 */ utils?: Record; + /** 全局常量 */ constants?: Record; }; @@ -168,6 +171,12 @@ export interface IPublicTypeEngineOptions { * 开启应用级设计模式 */ enableWorkspaceMode?: boolean; + + /** + * @default true + * 应用级设计模式下,自动打开第一个窗口 + */ + enableAutoOpenFirstWindow?: boolean; } /** diff --git a/packages/workspace/src/context/view-context.ts b/packages/workspace/src/context/view-context.ts index 38a9e570f..393989850 100644 --- a/packages/workspace/src/context/view-context.ts +++ b/packages/workspace/src/context/view-context.ts @@ -17,10 +17,6 @@ export class Context extends BasicContext { @obx isInit: boolean = false; - @computed get active() { - return this._activate; - } - init = flow(function* (this: Context) { if (this.viewType === 'webview') { const url = yield this.instance?.url?.(); @@ -43,6 +39,18 @@ export class Context extends BasicContext { makeObservable(this); } + @computed get active() { + return this._activate; + } + + onSimulatorRendererReady = (): Promise => { + return new Promise((resolve) => { + this.project.onSimulatorRendererReady(() => { + resolve(); + }); + }); + }; + setActivate = (_activate: boolean) => { this._activate = _activate; this.innerHotkey.activate(this._activate); diff --git a/packages/workspace/src/layouts/workbench.tsx b/packages/workspace/src/layouts/workbench.tsx index fe5ef846f..0c69f9717 100644 --- a/packages/workspace/src/layouts/workbench.tsx +++ b/packages/workspace/src/layouts/workbench.tsx @@ -47,7 +47,7 @@ export class Workbench extends Component<{ { workspace.windows.map(d => ( diff --git a/packages/workspace/src/window.ts b/packages/workspace/src/window.ts index 1b9bec410..37a1622c0 100644 --- a/packages/workspace/src/window.ts +++ b/packages/workspace/src/window.ts @@ -82,6 +82,9 @@ export class EditorWindow implements IEditorWindow { async init() { await this.initViewTypes(); await this.execViewTypesInit(); + Promise.all(Array.from(this.editorViews.values()).map((d) => d.onSimulatorRendererReady)).then(() => { + this.workspace.emitWindowRendererReady(); + }); this.url = await this.resource.url(); this.setDefaultViewType(); this.initReady = true; @@ -182,6 +185,10 @@ export class EditorWindow implements IEditorWindow { return this.editorView?.designer; } + get plugins() { + return this.editorView?.plugins; + } + get innerPlugins() { return this.editorView?.innerPlugins; } diff --git a/packages/workspace/src/workspace.ts b/packages/workspace/src/workspace.ts index 30c8fcdad..ea19ea0c0 100644 --- a/packages/workspace/src/workspace.ts +++ b/packages/workspace/src/workspace.ts @@ -1,6 +1,6 @@ import { IDesigner, ILowCodePluginManager, LowCodePluginManager } from '@alilc/lowcode-designer'; -import { createModuleEventBus, Editor, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core'; -import { IPublicApiPlugins, IPublicApiWorkspace, IPublicResourceList, IPublicTypeResourceType, IShellModelFactory } from '@alilc/lowcode-types'; +import { createModuleEventBus, Editor, IEditor, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core'; +import { IPublicApiPlugins, IPublicApiWorkspace, IPublicResourceList, IPublicTypeDisposable, IPublicTypeResourceType, IShellModelFactory } from '@alilc/lowcode-types'; import { BasicContext } from './context/base-context'; import { EditorWindow } from './window'; import type { IEditorWindow } from './window'; @@ -11,6 +11,8 @@ enum EVENT { CHANGE_WINDOW = 'change_window', CHANGE_ACTIVE_WINDOW = 'change_active_window', + + WINDOW_RENDER_READY = 'window_render_ready', } const CHANGE_EVENT = 'resource.list.change'; @@ -19,10 +21,12 @@ export interface IWorkspace extends Omit, 'resourceList' | 'plugins'> { - readonly registryInnerPlugin: (designer: IDesigner, editor: Editor, plugins: IPublicApiPlugins) => Promise; + readonly registryInnerPlugin: (designer: IDesigner, editor: Editor, plugins: IPublicApiPlugins) => Promise; readonly shellModelFactory: IShellModelFactory; + enableAutoOpenFirstWindow: boolean; + window: IEditorWindow; plugins: ILowCodePluginManager; @@ -32,11 +36,19 @@ export interface IWorkspace extends Omit Promise, + readonly registryInnerPlugin: (designer: IDesigner, editor: IEditor, plugins: IPublicApiPlugins) => Promise, readonly shellModelFactory: any, ) { - this.init(); + this.context = new BasicContext(this, ''); makeObservable(this); } @@ -97,13 +109,8 @@ export class Workspace implements IWorkspace { } } - init() { - this.initWindow(); - this.context = new BasicContext(this, ''); - } - initWindow() { - if (!this.defaultResourceType) { + if (!this.defaultResourceType || this.enableAutoOpenFirstWindow === false) { return; } const resourceName = this.defaultResourceType.name; @@ -128,7 +135,7 @@ export class Workspace implements IWorkspace { const resourceType = new ResourceType(resourceTypeModel); this.resourceTypeMap.set(resourceTypeModel.resourceName, resourceType); - if (!this.window && this.defaultResourceType) { + if (!this.window && this.defaultResourceType && this._isActive) { this.initWindow(); } } @@ -149,6 +156,17 @@ export class Workspace implements IWorkspace { }; } + onWindowRendererReady(fn: () => void): IPublicTypeDisposable { + this.emitter.on(EVENT.WINDOW_RENDER_READY, fn); + return () => { + this.emitter.off(EVENT.WINDOW_RENDER_READY, fn); + }; + } + + emitWindowRendererReady() { + this.emitter.emit(EVENT.WINDOW_RENDER_READY); + } + getResourceType(resourceName: string): IResourceType { return this.resourceTypeMap.get(resourceName)!; } @@ -188,7 +206,7 @@ export class Workspace implements IWorkspace { } openEditorWindow(name: string, title: string, options: Object, viewType?: string, sleep?: boolean) { - if (!this.window?.initReady && !sleep) { + if (this.window && !this.window?.initReady && !sleep) { this.windowQueue.push({ name, title, options, viewType, }); From 832e2a0aa3e2bfe87e5c1977682c4837c5167a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LeoYuan=20=E8=A2=81=E5=8A=9B=E7=9A=93?= Date: Wed, 19 Apr 2023 16:12:22 +0800 Subject: [PATCH 11/17] chore: add awesome badge for exploring LCE ecosystem --- packages/engine/README-zh_CN.md | 4 ++-- packages/engine/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/engine/README-zh_CN.md b/packages/engine/README-zh_CN.md index b5256af9c..c99e98cf6 100644 --- a/packages/engine/README-zh_CN.md +++ b/packages/engine/README-zh_CN.md @@ -14,8 +14,8 @@ [![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url] -[![codecov][codecov-image-url]][codecov-url] - +[![codecov][codecov-image-url]][codecov-url] [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/lowcode-workspace/awesome-lowcode-engine) + [![](https://img.shields.io/badge/LowCodeEngine-%E6%9F%A5%E7%9C%8B%E8%B4%A1%E7%8C%AE%E6%8E%92%E8%A1%8C%E6%A6%9C-orange)](https://opensource.alibaba.com/contribution_leaderboard/details?projectValue=lowcode-engine) [npm-image]: https://img.shields.io/npm/v/@alilc/lowcode-engine.svg?style=flat-square diff --git a/packages/engine/README.md b/packages/engine/README.md index 2d1254e4a..52bce8616 100644 --- a/packages/engine/README.md +++ b/packages/engine/README.md @@ -14,7 +14,7 @@ An enterprise-class low-code technology stack with scale-out design [![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url] -[![codecov][codecov-image-url]][codecov-url] +[![codecov][codecov-image-url]][codecov-url] [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/lowcode-workspace/awesome-lowcode-engine) [![](https://img.shields.io/badge/LowCodeEngine-Check%20Your%20Contribution-orange)](https://opensource.alibaba.com/contribution_leaderboard/details?projectValue=lowcode-engine) From e964492b89dec702a1c54467e3c45e2b850a4dd5 Mon Sep 17 00:00:00 2001 From: "{authemail@qq.com}" Date: Thu, 20 Apr 2023 10:02:10 +0800 Subject: [PATCH 12/17] =?UTF-8?q?docs:=20=E4=BF=AE=E5=A4=8D=E8=A1=A8?= =?UTF-8?q?=E6=A0=BC=E6=8E=92=E7=89=88=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/docs/demoUsage/panels/code.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/docs/demoUsage/panels/code.md b/docs/docs/demoUsage/panels/code.md index 929c9bcb0..645fcc7af 100644 --- a/docs/docs/demoUsage/panels/code.md +++ b/docs/docs/demoUsage/panels/code.md @@ -45,16 +45,13 @@ window.Next.Message.success('成功') - 读取:每次打开面板时,都会尝试读取 schema 中的 originCode 字段,如果没有,则从 schema 上的字段还原代码; - 写入:在关闭代码编辑面板(主动点击叉或者点击非代码编辑区块的被动关闭都算)时,将自动写入到 schema 中;您也可以在编辑过程中点击“保存”按钮手动保存; -| 源码面板中 | schema 中 | + +| 源码面板中 | Schema 中 | | --- | --- | -| 本地数据初始值设置: -![image.png](https://img.alicdn.com/imgextra/i4/O1CN01V6iaTY1gVNHi7gQfK_!!6000000004147-2-tps-370-146.png) | ![image.png](https://img.alicdn.com/imgextra/i2/O1CN010rhIPa268BEfGmzO6_!!6000000007616-2-tps-2098-826.png) | -| 生命周期方法: -![image.png](https://img.alicdn.com/imgextra/i4/O1CN010Y1TxV1QOvrVLRUjD_!!6000000001967-2-tps-478-260.png) | ![image.png](https://img.alicdn.com/imgextra/i3/O1CN01pbJzVQ1VSfAL7Lh8G_!!6000000002652-2-tps-2010-836.png) | -| 自定义函数: -![image.png](https://img.alicdn.com/imgextra/i4/O1CN01S2gjFk1CU3fm61eiD_!!6000000000083-2-tps-660-642.png) | ![image.png](https://img.alicdn.com/imgextra/i2/O1CN01X35YxU1GUkjj1YWVj_!!6000000000626-2-tps-1862-822.png) | -| 编译前全量代码: -![image.png](https://img.alicdn.com/imgextra/i2/O1CN01sbiK9N1kc1Uxp1OHY_!!6000000004703-2-tps-762-1122.png) | ![image.png](https://img.alicdn.com/imgextra/i3/O1CN01adKSg61QXAzRjQ4bm_!!6000000001985-2-tps-1906-796.png) | +| 本地数据初始值设置:![image.png](https://img.alicdn.com/imgextra/i4/O1CN01V6iaTY1gVNHi7gQfK_!!6000000004147-2-tps-370-146.png) | ![image.png](https://img.alicdn.com/imgextra/i2/O1CN010rhIPa268BEfGmzO6_!!6000000007616-2-tps-2098-826.png) | +| 生命周期方法:![image.png](https://img.alicdn.com/imgextra/i4/O1CN010Y1TxV1QOvrVLRUjD_!!6000000001967-2-tps-478-260.png) | ![image.png](https://img.alicdn.com/imgextra/i3/O1CN01pbJzVQ1VSfAL7Lh8G_!!6000000002652-2-tps-2010-836.png) | +| 自定义函数:![image.png](https://img.alicdn.com/imgextra/i4/O1CN01S2gjFk1CU3fm61eiD_!!6000000000083-2-tps-660-642.png) | ![image.png](https://img.alicdn.com/imgextra/i2/O1CN01X35YxU1GUkjj1YWVj_!!6000000000626-2-tps-1862-822.png) | +| 编译前全量代码:![image.png](https://img.alicdn.com/imgextra/i2/O1CN01sbiK9N1kc1Uxp1OHY_!!6000000004703-2-tps-762-1122.png) | ![image.png](https://img.alicdn.com/imgextra/i3/O1CN01adKSg61QXAzRjQ4bm_!!6000000001985-2-tps-1906-796.png) | - 异常处理:如果代码解析失败,它将无法被正常保存到 schema 中,此时编辑器会弹层提示: From f2e014cdbea998725559dd0f0c65df3c2520af72 Mon Sep 17 00:00:00 2001 From: JackLian Date: Thu, 20 Apr 2023 10:38:53 +0800 Subject: [PATCH 13/17] chore(docs): publish docs 1.0.27 --- docs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/package.json b/docs/package.json index 4c8cfc161..8c6e8a86c 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-engine-docs", - "version": "1.0.26", + "version": "1.0.27", "description": "低代码引擎版本化文档", "license": "MIT", "files": [ From 4a18f71ebc1d74a9c719f1260e9c324f0c7dfdbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LeoYuan=20=E8=A2=81=E5=8A=9B=E7=9A=93?= Date: Wed, 19 Apr 2023 16:48:16 +0800 Subject: [PATCH 14/17] feat: add workflow for checking base branch --- .github/workflows/check base branch.yml | 33 +++++++++++++++++++ .github/workflows/help wanted.yml | 2 +- .../workflows/insufficient information.yml | 2 +- .github/workflows/test modules.yml | 2 +- .github/workflows/test packages.yml | 2 +- 5 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/check base branch.yml diff --git a/.github/workflows/check base branch.yml b/.github/workflows/check base branch.yml new file mode 100644 index 000000000..cef996c75 --- /dev/null +++ b/.github/workflows/check base branch.yml @@ -0,0 +1,33 @@ +name: Check Base Branch + +on: + pull_request: + types: [opened] + +jobs: + code-review: + name: Check + runs-on: ubuntu-latest + + steps: + # 判断用户是否有写仓库权限 + - name: 'Check User Permission' + uses: 'lannonbr/repo-permission-check-action@2.0.0' + with: + permission: 'write' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: 'Check base branch name is develop or not' + if: github.event.pull_request.base.ref != 'develop' # check the target branch if it's master + uses: actions-cool/issues-helper@v2 + with: + actions: 'create-comment' + token: ${{ secrets.GITHUB_TOKEN }} + issue-number: ${{ github.event.pull_request.number }} + body: | + 感谢你的 PR,根据引擎的 [研发协作流程](https://lowcode-engine.cn/site/docs/participate/flow),请将目标合入分支设置为 **develop**。 + + Thanks in advance, according to the [Contribution Guideline](https://lowcode-engine.cn/site/docs/participate/flow), please set the base branch to **develop**. + + @${{ github.event.pull_request.user.login }} \ No newline at end of file diff --git a/.github/workflows/help wanted.yml b/.github/workflows/help wanted.yml index 94927ad28..619d08b93 100644 --- a/.github/workflows/help wanted.yml +++ b/.github/workflows/help wanted.yml @@ -1,4 +1,4 @@ -name: Issue Reply +name: Help Wanted on: issues: diff --git a/.github/workflows/insufficient information.yml b/.github/workflows/insufficient information.yml index 15885043a..c49e133f1 100644 --- a/.github/workflows/insufficient information.yml +++ b/.github/workflows/insufficient information.yml @@ -1,4 +1,4 @@ -name: Issue Reply +name: Insufficient Info on: issues: diff --git a/.github/workflows/test modules.yml b/.github/workflows/test modules.yml index 9410626e8..b2464cc40 100644 --- a/.github/workflows/test modules.yml +++ b/.github/workflows/test modules.yml @@ -1,4 +1,4 @@ -name: lint & test +name: Lint & Test (Mods) on: push: diff --git a/.github/workflows/test packages.yml b/.github/workflows/test packages.yml index 5d1ee8907..4ee9b4156 100644 --- a/.github/workflows/test packages.yml +++ b/.github/workflows/test packages.yml @@ -1,4 +1,4 @@ -name: lint & test +name: Lint & Test (Pkgs) on: push: From aab8a3a10ec1612164de76aec81f196d5e395dc5 Mon Sep 17 00:00:00 2001 From: liujuping Date: Fri, 21 Apr 2023 16:29:25 +0800 Subject: [PATCH 15/17] feat: update setter types --- packages/editor-core/src/di/setter.ts | 11 +++++---- .../src/components/settings/settings-pane.tsx | 13 ++++++----- packages/shell/src/api/setters.ts | 23 +++++++++++-------- packages/types/src/shell/api/setters.ts | 8 +++++++ 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/packages/editor-core/src/di/setter.ts b/packages/editor-core/src/di/setter.ts index 4139c5939..437d9a89e 100644 --- a/packages/editor-core/src/di/setter.ts +++ b/packages/editor-core/src/di/setter.ts @@ -1,8 +1,7 @@ import { ReactNode } from 'react'; -import { IPublicTypeCustomView, IPublicTypeRegisteredSetter } from '@alilc/lowcode-types'; +import { IPublicApiSetters, IPublicTypeCustomView, IPublicTypeRegisteredSetter } from '@alilc/lowcode-types'; import { createContent, isCustomView } from '@alilc/lowcode-utils'; - const settersMap = new Map(); @@ -44,13 +43,17 @@ function getInitialFromSetter(setter: any) { ) || null; // eslint-disable-line } -export class Setters { - constructor(readonly viewName: string = 'global') {} +export interface ISetters extends IPublicApiSetters { +} + +export class Setters implements ISetters { settersMap = new Map(); + constructor(readonly viewName: string = 'global') {} + getSetter = (type: string): IPublicTypeRegisteredSetter | null => { return this.settersMap.get(type) || null; }; diff --git a/packages/editor-skeleton/src/components/settings/settings-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-pane.tsx index 31ea78cce..9cc8d9cae 100644 --- a/packages/editor-skeleton/src/components/settings/settings-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-pane.tsx @@ -1,14 +1,13 @@ -import { Component, MouseEvent, Fragment } from 'react'; +import { Component, MouseEvent, Fragment, ReactNode } from 'react'; import { shallowIntl, observer, obx, engineConfig, runInAction } from '@alilc/lowcode-editor-core'; import { createContent, isJSSlot, isSetterConfig } from '@alilc/lowcode-utils'; import { Skeleton, Stage } from '@alilc/lowcode-editor-skeleton'; -import { IPublicTypeCustomView } from '@alilc/lowcode-types'; +import { IPublicApiSetters, IPublicTypeCustomView, IPublicTypeDynamicProps } from '@alilc/lowcode-types'; import { ISettingEntry, IComponentMeta, ISettingField, isSettingField, ISettingTopEntry } from '@alilc/lowcode-designer'; import { createField } from '../field'; import PopupService, { PopupPipe } from '../popup'; import { SkeletonContext } from '../../context'; import { intl } from '../../locale'; -import { Setters } from '@alilc/lowcode-shell'; function isStandardComponent(componentMeta: IComponentMeta | null) { if (!componentMeta) return false; @@ -40,7 +39,7 @@ class SettingFieldView extends Component | IPublicTypeDynamicProps = {}; let setterType: any; let initialValue: any = null; @@ -236,7 +237,7 @@ class SettingFieldView extends Component { + if (!workspace.window.innerSetters) { + logger.error('setter api 调用时机出现问题,请检查'); + return this[innerSettersSymbol]; + } + return workspace.window.innerSetters; + }); } return this[innerSettersSymbol]; } - constructor(innerSetters: InnerSetters, readonly workspaceMode = false) { + constructor(innerSetters: ISetters, readonly workspaceMode = false) { this[innerSettersSymbol] = innerSetters; } @@ -64,6 +66,9 @@ export class Setters implements IPublicApiSetters { return this[settersSymbol].registerSetter(typeOrMaps, setter); }; + /** + * @deprecated + */ createSetterContent = (setter: any, props: Record): ReactNode => { return this[settersSymbol].createSetterContent(setter, props); }; diff --git a/packages/types/src/shell/api/setters.ts b/packages/types/src/shell/api/setters.ts index 9ff2e8eda..011a9dcac 100644 --- a/packages/types/src/shell/api/setters.ts +++ b/packages/types/src/shell/api/setters.ts @@ -1,6 +1,9 @@ +import { ReactNode } from 'react'; + import { IPublicTypeRegisteredSetter, IPublicTypeCustomView } from '../type'; export interface IPublicApiSetters { + /** * 获取指定 setter * get setter by type @@ -29,4 +32,9 @@ export interface IPublicApiSetters { typeOrMaps: string | { [key: string]: IPublicTypeCustomView | IPublicTypeRegisteredSetter }, setter?: IPublicTypeCustomView | IPublicTypeRegisteredSetter | undefined ): void; + + /** + * @deprecated + */ + createSetterContent (setter: any, props: Record): ReactNode; } From c50a0823db7630f20ad29d66d3f63a836285cd27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E6=9C=A8?= <31089228+StringKe@users.noreply.github.com> Date: Sat, 22 Apr 2023 04:42:16 +0000 Subject: [PATCH 16/17] =?UTF-8?q?feat(utils):=20cursor=20=E4=B8=8D?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=20less?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/utils/src/cursor.css | 19 +++++++++++++++++++ packages/utils/src/cursor.less | 15 --------------- packages/utils/src/cursor.ts | 2 +- 3 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 packages/utils/src/cursor.css delete mode 100644 packages/utils/src/cursor.less diff --git a/packages/utils/src/cursor.css b/packages/utils/src/cursor.css new file mode 100644 index 000000000..e13da656e --- /dev/null +++ b/packages/utils/src/cursor.css @@ -0,0 +1,19 @@ +html.lc-cursor-dragging, +html.lc-cursor-dragging * { + cursor: move !important; +} + +html.lc-cursor-x-resizing, +html.lc-cursor-x-resizing * { + cursor: col-resize; +} + +html.lc-cursor-y-resizing, +html.lc-cursor-y-resizing * { + cursor: row-resize; +} + +html.lc-cursor-copy, +html.lc-cursor-copy * { + cursor: copy !important; +} diff --git a/packages/utils/src/cursor.less b/packages/utils/src/cursor.less deleted file mode 100644 index 30c890862..000000000 --- a/packages/utils/src/cursor.less +++ /dev/null @@ -1,15 +0,0 @@ -html.lc-cursor-dragging, html.lc-cursor-dragging * { - cursor: move !important; -} - -html.lc-cursor-x-resizing, html.lc-cursor-x-resizing * { - cursor: col-resize; -} - -html.lc-cursor-y-resizing, html.lc-cursor-y-resizing * { - cursor: row-resize; -} - -html.lc-cursor-copy, html.lc-cursor-copy * { - cursor: copy !important; -} diff --git a/packages/utils/src/cursor.ts b/packages/utils/src/cursor.ts index fea4bce65..c12ec64b9 100644 --- a/packages/utils/src/cursor.ts +++ b/packages/utils/src/cursor.ts @@ -1,4 +1,4 @@ -import './cursor.less'; +import './cursor.css'; export class Cursor { private states = new Set(); From 358dde43a4a6097abc71fd47e708cca7af105acf Mon Sep 17 00:00:00 2001 From: liujuping Date: Sun, 23 Apr 2023 17:33:32 +0800 Subject: [PATCH 17/17] feat(shell): add editor-view model --- docs/docs/api/model/editor-view.md | 21 +++ docs/docs/api/model/window.md | 16 ++ packages/designer/src/plugin/plugin-types.ts | 2 + packages/engine/src/engine-core.ts | 2 + packages/plugin-outline-pane/package.json | 2 - .../src/controllers/pane-controller.ts | 13 +- .../src/controllers/tree-master.ts | 176 +++++++++++++----- .../src/controllers/tree-node.ts | 8 +- .../src/controllers/tree.ts | 14 +- packages/plugin-outline-pane/src/index.tsx | 30 ++- .../plugin-outline-pane/src/views/filter.tsx | 13 +- .../plugin-outline-pane/src/views/pane.tsx | 40 +++- .../src/views/tree-branches.tsx | 26 ++- .../src/views/tree-node.tsx | 25 ++- .../src/views/tree-title.tsx | 37 ++-- .../plugin-outline-pane/src/views/tree.tsx | 36 ++-- packages/shell/src/model/editor-view.ts | 27 +++ packages/shell/src/model/index.ts | 1 + packages/shell/src/model/window.ts | 9 +- packages/shell/src/symbols.ts | 2 + packages/types/src/shell/enum/index.ts | 3 +- .../src/shell/enum/plugin-register-level.ts | 6 + packages/types/src/shell/model/dragon.ts | 8 +- packages/types/src/shell/model/editor-view.ts | 3 + packages/types/src/shell/model/index.ts | 1 + .../types/src/shell/model/plugin-context.ts | 15 +- packages/types/src/shell/model/window.ts | 13 ++ .../workspace/src/context/base-context.ts | 4 +- .../workspace/src/context/view-context.ts | 12 +- packages/workspace/src/index.ts | 1 + packages/workspace/src/resource.ts | 4 +- packages/workspace/src/window.ts | 11 +- packages/workspace/src/workspace.ts | 4 +- 33 files changed, 397 insertions(+), 188 deletions(-) create mode 100644 docs/docs/api/model/editor-view.md create mode 100644 packages/shell/src/model/editor-view.ts create mode 100644 packages/types/src/shell/enum/plugin-register-level.ts create mode 100644 packages/types/src/shell/model/editor-view.ts diff --git a/docs/docs/api/model/editor-view.md b/docs/docs/api/model/editor-view.md new file mode 100644 index 000000000..a3cc83e1f --- /dev/null +++ b/docs/docs/api/model/editor-view.md @@ -0,0 +1,21 @@ +--- +title: EditorView +sidebar_position: 12 +--- + +> **[@experimental](./#experimental)**
+> **@types** [IPublicModelEditorView](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/editor-view.ts)
+> **@since** v1.1.7 + +窗口编辑视图 + +## 类型定义 + +``` +import { IPublicModelPluginContext } from "./plugin-context"; + +export interface IPublicModelEditorView extends IPublicModelPluginContext {}; + +``` + +相关类型定义: [IPublicModelPluginContext](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/plugin-context.ts) diff --git a/docs/docs/api/model/window.md b/docs/docs/api/model/window.md index f102c0cab..3cc8b5d5e 100644 --- a/docs/docs/api/model/window.md +++ b/docs/docs/api/model/window.md @@ -38,6 +38,22 @@ sidebar_position: 12 关联模型 [IPublicModelResource](./resource) +### currentEditorView +窗口当前视图 + +`@type {IPublicModelEditorView}` + +关联模型 [IPublicModelEditorView](./editor-view) + +### editorViews + +窗口所有视图 + +`@type {IPublicModelEditorView[]}` + +关联模型 [IPublicModelEditorView](./editor-view) + + ## 方法 ### importSchema diff --git a/packages/designer/src/plugin/plugin-types.ts b/packages/designer/src/plugin/plugin-types.ts index 23aac2849..ac08d7d0c 100644 --- a/packages/designer/src/plugin/plugin-types.ts +++ b/packages/designer/src/plugin/plugin-types.ts @@ -17,6 +17,7 @@ import { IPublicTypePluginMeta, IPublicTypePluginRegisterOptions, IPublicModelWindow, + IPublicEnumPluginRegisterLevel, } from '@alilc/lowcode-types'; import PluginContext from './plugin-context'; @@ -58,6 +59,7 @@ export interface ILowCodePluginContextPrivate { set canvas(canvas: IPublicApiCanvas); set workspace(workspace: IPublicApiWorkspace); set editorWindow(window: IPublicModelWindow); + set registerLevel(level: IPublicEnumPluginRegisterLevel); } export interface ILowCodePluginContextApiAssembler { assembleApis( diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts index f0c283ec1..84dfd67aa 100644 --- a/packages/engine/src/engine-core.ts +++ b/packages/engine/src/engine-core.ts @@ -18,6 +18,7 @@ import { IPublicTypeDisposable, IPublicApiPlugins, IPublicApiWorkspace, + IPublicEnumPluginRegisterLevel, } from '@alilc/lowcode-types'; import { Designer, @@ -138,6 +139,7 @@ const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = { context.plugins = plugins; context.logger = new Logger({ level: 'warn', bizName: `plugin:${pluginName}` }); context.workspace = workspace; + context.registerLevel = IPublicEnumPluginRegisterLevel.Default; }, }; diff --git a/packages/plugin-outline-pane/package.json b/packages/plugin-outline-pane/package.json index ec4738cd7..8a1472ce2 100644 --- a/packages/plugin-outline-pane/package.json +++ b/packages/plugin-outline-pane/package.json @@ -13,8 +13,6 @@ }, "dependencies": { "@alifd/next": "^1.19.16", - "@alilc/lowcode-designer": "1.1.6", - "@alilc/lowcode-editor-core": "1.1.6", "@alilc/lowcode-types": "1.1.6", "@alilc/lowcode-utils": "1.1.6", "classnames": "^2.2.6", diff --git a/packages/plugin-outline-pane/src/controllers/pane-controller.ts b/packages/plugin-outline-pane/src/controllers/pane-controller.ts index a02844ad1..e7c41892c 100644 --- a/packages/plugin-outline-pane/src/controllers/pane-controller.ts +++ b/packages/plugin-outline-pane/src/controllers/pane-controller.ts @@ -16,16 +16,15 @@ import { IPublicModelDropLocation, IPublicModelScroller, IPublicModelScrollTarget, - IPublicModelPluginContext, IPublicModelLocateEvent, } from '@alilc/lowcode-types'; import TreeNode from './tree-node'; import { IndentTrack } from '../helper/indent-track'; import DwellTimer from '../helper/dwell-timer'; -import { ITreeBoard, TreeMaster } from './tree-master'; +import { IOutlinePanelPluginContext, ITreeBoard, TreeMaster } from './tree-master'; export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicTypeScrollable { - private pluginContext: IPublicModelPluginContext; + private pluginContext: IOutlinePanelPluginContext; private treeMaster?: TreeMaster; @@ -100,8 +99,8 @@ export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicTy private _shell: HTMLDivElement | null = null; - constructor(at: string | symbol, pluginContext: IPublicModelPluginContext, treeMaster: TreeMaster) { - this.pluginContext = pluginContext; + constructor(at: string | symbol, treeMaster: TreeMaster) { + this.pluginContext = treeMaster.pluginContext; this.treeMaster = treeMaster; this.at = at; let inited = false; @@ -237,7 +236,7 @@ export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicTy let { node } = treeNode; if (isDragNodeObject(dragObject)) { const newNodes = operationalNodes; - let i = newNodes.length; + let i = newNodes?.length; let p: any = node; while (i-- > 0) { if (newNodes[i].contains(p)) { @@ -482,7 +481,7 @@ export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicTy const isSlotContainer = treeNode.hasSlots(); const isContainer = treeNode.isContainer(); - if (container.isSlot && !treeNode.expanded) { + if (container.isSlotNode && !treeNode.expanded) { // 未展开,直接定位到内部第一个节点 if (isSlotContainer) { detail.index = null; diff --git a/packages/plugin-outline-pane/src/controllers/tree-master.ts b/packages/plugin-outline-pane/src/controllers/tree-master.ts index 074ed3447..a6852803b 100644 --- a/packages/plugin-outline-pane/src/controllers/tree-master.ts +++ b/packages/plugin-outline-pane/src/controllers/tree-master.ts @@ -1,67 +1,139 @@ import { isLocationChildrenDetail } from '@alilc/lowcode-utils'; -import { IPublicModelPluginContext, IPublicTypeActiveTarget, IPublicModelNode } from '@alilc/lowcode-types'; +import { IPublicModelPluginContext, IPublicTypeActiveTarget, IPublicModelNode, IPublicTypeDisposable, IPublicEnumPluginRegisterLevel } from '@alilc/lowcode-types'; import TreeNode from './tree-node'; import { Tree } from './tree'; +import EventEmitter from 'events'; +import { enUS, zhCN } from '../locale'; +import { ReactNode } from 'react'; export interface ITreeBoard { readonly at: string | symbol; scrollToNode(treeNode: TreeNode, detail?: any): void; } +enum EVENT_NAMES { + pluginContextChanged = 'pluginContextChanged', +} + +export interface IOutlinePanelPluginContext extends IPublicModelPluginContext { + extraTitle?: string; + intlNode(id: string, params?: object): ReactNode; + intl(id: string, params?: object): string; + getLocale(): string; +} + export class TreeMaster { - readonly pluginContext: IPublicModelPluginContext; + pluginContext: IOutlinePanelPluginContext; private boards = new Set(); private treeMap = new Map(); - constructor(pluginContext: IPublicModelPluginContext) { - this.pluginContext = pluginContext; + private disposeEvents: (IPublicTypeDisposable | undefined)[] = []; + + event = new EventEmitter(); + + constructor(pluginContext: IPublicModelPluginContext, readonly options: { + extraTitle?: string; + }) { + this.setPluginContext(pluginContext); + const { workspace } = this.pluginContext; + this.initEvent(); + if (pluginContext.registerLevel === IPublicEnumPluginRegisterLevel.Workspace) { + workspace.onWindowRendererReady(() => { + this.setPluginContext(workspace.window?.currentEditorView); + let dispose: IPublicTypeDisposable | undefined; + const windowViewTypeChangeEvent = () => { + dispose = workspace.window?.onChangeViewType(() => { + this.setPluginContext(workspace.window?.currentEditorView); + }); + }; + + windowViewTypeChangeEvent(); + + workspace.onChangeActiveWindow(() => { + windowViewTypeChangeEvent(); + this.setPluginContext(workspace.window?.currentEditorView); + dispose && dispose(); + }); + }); + } + } + + private setPluginContext(pluginContext: IPublicModelPluginContext | undefined) { + if (!pluginContext) { + return; + } + const { intl, intlNode, getLocale } = pluginContext.common.utils.createIntl({ + 'en-US': enUS, + 'zh-CN': zhCN, + }); + let _pluginContext: IOutlinePanelPluginContext = Object.assign(pluginContext, { + intl, + intlNode, + getLocale, + }); + _pluginContext.extraTitle = this.options && this.options['extraTitle']; + this.pluginContext = _pluginContext; + this.disposeEvent(); + this.initEvent(); + this.emitPluginContextChange(); + } + + private disposeEvent() { + this.disposeEvents.forEach(d => { + d && d(); + }); + } + + private initEvent() { let startTime: any; const { event, project, canvas } = this.pluginContext; - canvas.dragon?.onDragstart(() => { - startTime = Date.now() / 1000; - // needs? - this.toVision(); - }); - canvas.activeTracker?.onChange((target: IPublicTypeActiveTarget) => { - const { node, detail } = target; - const tree = this.currentTree; - if (!tree/* || node.document !== tree.document */) { - return; - } - const treeNode = tree.getTreeNode(node); - if (detail && isLocationChildrenDetail(detail)) { - treeNode.expand(true); - } else { - treeNode.expandParents(); - } - this.boards.forEach((board) => { - board.scrollToNode(treeNode, detail); - }); - }); - canvas.dragon?.onDragend(() => { - const endTime: any = Date.now() / 1000; - const nodes = project.currentDocument?.selection?.getNodes(); - event.emit('outlinePane.dragend', { - selected: nodes - ?.map((n) => { - if (!n) { - return; - } - const npm = n?.componentMeta?.npm; - return ( - [npm?.package, npm?.componentName].filter((item) => !!item).join('-') || n?.componentMeta?.componentName - ); - }) - .join('&'), - time: (endTime - startTime).toFixed(2), - }); - }); - project.onRemoveDocument((data: {id: string}) => { - const { id } = data; - this.treeMap.delete(id); - }); + this.disposeEvents = [ + canvas.dragon?.onDragstart(() => { + startTime = Date.now() / 1000; + // needs? + this.toVision(); + }), + canvas.activeTracker?.onChange((target: IPublicTypeActiveTarget) => { + const { node, detail } = target; + const tree = this.currentTree; + if (!tree/* || node.document !== tree.document */) { + return; + } + const treeNode = tree.getTreeNode(node); + if (detail && isLocationChildrenDetail(detail)) { + treeNode.expand(true); + } else { + treeNode.expandParents(); + } + this.boards.forEach((board) => { + board.scrollToNode(treeNode, detail); + }); + }), + canvas.dragon?.onDragend(() => { + const endTime: any = Date.now() / 1000; + const nodes = project.currentDocument?.selection?.getNodes(); + event.emit('outlinePane.dragend', { + selected: nodes + ?.map((n) => { + if (!n) { + return; + } + const npm = n?.componentMeta?.npm; + return ( + [npm?.package, npm?.componentName].filter((item) => !!item).join('-') || n?.componentMeta?.componentName + ); + }) + .join('&'), + time: (endTime - startTime).toFixed(2), + }); + }), + project.onRemoveDocument((data: {id: string}) => { + const { id } = data; + this.treeMap.delete(id); + }), + ]; } private toVision() { @@ -86,6 +158,14 @@ export class TreeMaster { // todo others purge } + onPluginContextChange(fn: () => void) { + this.event.on(EVENT_NAMES.pluginContextChanged, fn); + } + + emitPluginContextChange() { + this.event.emit(EVENT_NAMES.pluginContextChanged); + } + get currentTree(): Tree | null { const doc = this.pluginContext.project.getCurrentDocument(); if (doc) { @@ -93,7 +173,7 @@ export class TreeMaster { if (this.treeMap.has(id)) { return this.treeMap.get(id)!; } - const tree = new Tree(this.pluginContext); + const tree = new Tree(this); this.treeMap.set(id, tree); return tree; } diff --git a/packages/plugin-outline-pane/src/controllers/tree-node.ts b/packages/plugin-outline-pane/src/controllers/tree-node.ts index 62f373e2a..2a6bc0aca 100644 --- a/packages/plugin-outline-pane/src/controllers/tree-node.ts +++ b/packages/plugin-outline-pane/src/controllers/tree-node.ts @@ -2,12 +2,12 @@ import { IPublicTypeTitleContent, IPublicTypeLocationChildrenDetail, IPublicModelNode, - IPublicModelPluginContext, IPublicTypeDisposable, } from '@alilc/lowcode-types'; import { isI18nData, isLocationChildrenDetail } from '@alilc/lowcode-utils'; import EventEmitter from 'events'; import { Tree } from './tree'; +import { IOutlinePanelPluginContext } from './tree-master'; /** * 大纲树过滤结果 @@ -38,7 +38,7 @@ enum EVENT_NAMES { } export default class TreeNode { - readonly pluginContext: IPublicModelPluginContext; + readonly pluginContext: IOutlinePanelPluginContext; event = new EventEmitter(); private _node: IPublicModelNode; @@ -160,9 +160,9 @@ export default class TreeNode { return this._node; } - constructor(tree: Tree, node: IPublicModelNode, pluginContext: IPublicModelPluginContext) { + constructor(tree: Tree, node: IPublicModelNode) { this.tree = tree; - this.pluginContext = pluginContext; + this.pluginContext = tree.pluginContext; this._node = node; } diff --git a/packages/plugin-outline-pane/src/controllers/tree.ts b/packages/plugin-outline-pane/src/controllers/tree.ts index c0098794c..94cab7879 100644 --- a/packages/plugin-outline-pane/src/controllers/tree.ts +++ b/packages/plugin-outline-pane/src/controllers/tree.ts @@ -1,12 +1,13 @@ import TreeNode from './tree-node'; -import { IPublicModelNode, IPublicModelPluginContext, IPublicTypePropChangeOptions } from '@alilc/lowcode-types'; +import { IPublicModelNode, IPublicTypePropChangeOptions } from '@alilc/lowcode-types'; +import { IOutlinePanelPluginContext, TreeMaster } from './tree-master'; export class Tree { private treeNodesMap = new Map(); readonly id: string | undefined; - readonly pluginContext: IPublicModelPluginContext; + readonly pluginContext: IOutlinePanelPluginContext; get root(): TreeNode | null { if (this.pluginContext.project.currentDocument?.focusNode) { @@ -15,8 +16,11 @@ export class Tree { return null; } - constructor(pluginContext: IPublicModelPluginContext) { - this.pluginContext = pluginContext; + readonly treeMaster: TreeMaster; + + constructor(treeMaster: TreeMaster) { + this.treeMaster = treeMaster; + this.pluginContext = treeMaster.pluginContext; const doc = this.pluginContext.project.currentDocument; this.id = doc?.id; @@ -51,7 +55,7 @@ export class Tree { return tnode; } - const treeNode = new TreeNode(this, node, this.pluginContext); + const treeNode = new TreeNode(this, node); this.treeNodesMap.set(node.id, treeNode); return treeNode; } diff --git a/packages/plugin-outline-pane/src/index.tsx b/packages/plugin-outline-pane/src/index.tsx index 701e46540..1ea53b2f0 100644 --- a/packages/plugin-outline-pane/src/index.tsx +++ b/packages/plugin-outline-pane/src/index.tsx @@ -1,21 +1,13 @@ import { Pane } from './views/pane'; import { IconOutline } from './icons/outline'; import { IPublicModelPluginContext, IPublicModelDocumentModel } from '@alilc/lowcode-types'; -import { enUS, zhCN } from './locale'; import { MasterPaneName, BackupPaneName } from './helper/consts'; import { TreeMaster } from './controllers/tree-master'; import { PaneController } from './controllers/pane-controller'; +import { useState } from 'react'; export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { - const { skeleton, config, common, event, canvas, project } = ctx; - const { intl, intlNode, getLocale } = common.utils.createIntl({ - 'en-US': enUS, - 'zh-CN': zhCN, - }); - ctx.intl = intl; - ctx.intlNode = intlNode; - ctx.getLocale = getLocale; - ctx.extraTitle = options && options['extraTitle']; + const { skeleton, config, canvas, project } = ctx; let isInFloatArea = true; const hasPreferenceForOutline = config.getPreference().contains('outline-pane-pinned-status-isFloat', 'skeleton'); @@ -26,8 +18,7 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { masterPane: false, backupPane: false, }; - const treeMaster = new TreeMaster(ctx); - let masterPaneController: PaneController | null = null; + const treeMaster = new TreeMaster(ctx, options); let backupPaneController: PaneController | null = null; return { async init() { @@ -40,16 +31,20 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { name: MasterPaneName, props: { icon: IconOutline, - description: intlNode('Outline Tree'), + description: treeMaster.pluginContext.intlNode('Outline Tree'), }, - content: (props: any) => { - masterPaneController = new PaneController(MasterPaneName, ctx, treeMaster); + content: function Context(props: any) { + const [masterPaneController, setMasterPaneController] = useState(new PaneController(MasterPaneName, treeMaster)); + treeMaster.onPluginContextChange(() => { + setMasterPaneController(new PaneController(MasterPaneName, treeMaster)); + }); + return ( ); @@ -73,10 +68,9 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => { hiddenWhenInit: true, }, content: (props: any) => { - backupPaneController = new PaneController(BackupPaneName, ctx, treeMaster); + backupPaneController = new PaneController(BackupPaneName, treeMaster); return ( + {/* @ts-ignore */} + {/* @ts-ignore */} )} > + {/* @ts-ignore */} - {this.props.pluginContext.intlNode('Check All')} + {this.props.tree.pluginContext.intlNode('Check All')} + {/* @ts-ignore */} + {/* @ts-ignore */} - {this.props.pluginContext.intlNode(op.label)} + {this.props.tree.pluginContext.intlNode(op.label)} ))} diff --git a/packages/plugin-outline-pane/src/views/pane.tsx b/packages/plugin-outline-pane/src/views/pane.tsx index adaada786..8805cfffa 100644 --- a/packages/plugin-outline-pane/src/views/pane.tsx +++ b/packages/plugin-outline-pane/src/views/pane.tsx @@ -1,47 +1,71 @@ import React, { PureComponent } from 'react'; +import { Loading } from '@alifd/next'; import { PaneController } from '../controllers/pane-controller'; import TreeView from './tree'; import './style.less'; -import { IPublicModelPluginContext } from '@alilc/lowcode-types'; import Filter from './filter'; import { TreeMaster } from '../controllers/tree-master'; +import { Tree } from '../controllers/tree'; +import { IPublicTypeDisposable } from '@alilc/lowcode-types'; export class Pane extends PureComponent<{ config: any; - pluginContext: IPublicModelPluginContext; treeMaster: TreeMaster; controller: PaneController; +}, { + tree: Tree | null; }> { private controller; - private treeMaster: TreeMaster; + + private dispose: IPublicTypeDisposable; constructor(props: any) { super(props); const { controller, treeMaster } = props; - this.treeMaster = treeMaster; this.controller = controller; + this.state = { + tree: treeMaster.currentTree, + }; } componentWillUnmount() { this.controller.purge(); + this.dispose && this.dispose(); + } + + componentDidMount() { + this.dispose = this.props.treeMaster.pluginContext.project.onSimulatorRendererReady(() => { + this.setState({ + tree: this.props.treeMaster.currentTree, + }); + }); } render() { - const tree = this.treeMaster.currentTree; + const tree = this.state.tree; if (!tree) { return (
-

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

+

+ {/* @ts-ignore */} + +

); } return (
- +
this.controller.mount(shell)} className="lc-outline-tree-container"> - +
); diff --git a/packages/plugin-outline-pane/src/views/tree-branches.tsx b/packages/plugin-outline-pane/src/views/tree-branches.tsx index 210fe0af8..2e281071b 100644 --- a/packages/plugin-outline-pane/src/views/tree-branches.tsx +++ b/packages/plugin-outline-pane/src/views/tree-branches.tsx @@ -2,12 +2,11 @@ import { PureComponent } from 'react'; import classNames from 'classnames'; import TreeNode from '../controllers/tree-node'; import TreeNodeView from './tree-node'; -import { IPublicModelPluginContext, IPublicModelExclusiveGroup, IPublicTypeDisposable, IPublicTypeLocationChildrenDetail } from '@alilc/lowcode-types'; +import { IPublicModelExclusiveGroup, IPublicTypeDisposable, IPublicTypeLocationChildrenDetail } from '@alilc/lowcode-types'; export default class TreeBranches extends PureComponent<{ treeNode: TreeNode; isModal?: boolean; - pluginContext: IPublicModelPluginContext; expanded: boolean; treeChildren: TreeNode[] | null; }> { @@ -51,12 +50,11 @@ export default class TreeBranches extends PureComponent<{ return (
{ - !isModal && + !isModal && }
@@ -73,7 +71,6 @@ interface ITreeNodeChildrenState { class TreeNodeChildren extends PureComponent<{ treeNode: TreeNode; isModal?: boolean; - pluginContext: IPublicModelPluginContext; treeChildren: TreeNode[] | null; }, ITreeNodeChildrenState> { state: ITreeNodeChildrenState = { @@ -84,8 +81,8 @@ class TreeNodeChildren extends PureComponent<{ }; offLocationChanged: IPublicTypeDisposable | undefined; componentDidMount() { - const { treeNode, pluginContext } = this.props; - const { project } = pluginContext; + const { treeNode } = this.props; + const { project } = treeNode.pluginContext; const { filterWorking, matchSelf, keywords } = treeNode.filterReult; const { dropDetail } = treeNode; this.setState({ @@ -122,13 +119,14 @@ class TreeNodeChildren extends PureComponent<{ let groupContents: any[] = []; let currentGrp: IPublicModelExclusiveGroup; const { filterWorking, matchSelf, keywords } = this.state; - const Title = this.props.pluginContext.common.editorCabin.Title; + const Title = this.props.treeNode.pluginContext.common.editorCabin.Title; const endGroup = () => { if (groupContents.length > 0) { children.push(
+ {/* @ts-ignore */} ); + groupContents.push(<TreeNodeView key={child.id} treeNode={child} isModal={isModal} />); } else { if (index === dropIndex) { children.push(insertion); } - children.push(<TreeNodeView key={child.id} treeNode={child} isModal={isModal} pluginContext={this.props.pluginContext} />); + children.push(<TreeNodeView key={child.id} treeNode={child} isModal={isModal} />); } }); endGroup(); @@ -191,14 +189,13 @@ class TreeNodeChildren extends PureComponent<{ class TreeNodeSlots extends PureComponent<{ treeNode: TreeNode; - pluginContext: IPublicModelPluginContext; }> { render() { const { treeNode } = this.props; if (!treeNode.hasSlots()) { return null; } - const Title = this.props.pluginContext.common.editorCabin.Title; + const Title = this.props.treeNode.pluginContext.common.editorCabin.Title; return ( <div className={classNames('tree-node-slots', { @@ -207,10 +204,11 @@ class TreeNodeSlots extends PureComponent<{ data-id={treeNode.id} > <div className="tree-node-slots-title"> - <Title title={{ type: 'i18n', intl: this.props.pluginContext.intlNode('Slots') }} /> + {/* @ts-ignore */} + <Title title={{ type: 'i18n', intl: this.props.treeNode.pluginContext.intlNode('Slots') }} /> </div> {treeNode.slots.map(tnode => ( - <TreeNodeView key={tnode.id} treeNode={tnode} pluginContext={this.props.pluginContext} /> + <TreeNodeView key={tnode.id} treeNode={tnode} /> ))} </div> ); diff --git a/packages/plugin-outline-pane/src/views/tree-node.tsx b/packages/plugin-outline-pane/src/views/tree-node.tsx index 331397a0b..882fe15b1 100644 --- a/packages/plugin-outline-pane/src/views/tree-node.tsx +++ b/packages/plugin-outline-pane/src/views/tree-node.tsx @@ -4,22 +4,24 @@ import TreeNode from '../controllers/tree-node'; import TreeTitle from './tree-title'; import TreeBranches from './tree-branches'; import { IconEyeClose } from '../icons/eye-close'; -import { IPublicModelPluginContext, IPublicModelModalNodesManager, IPublicTypeDisposable } from '@alilc/lowcode-types'; +import { IPublicModelModalNodesManager, IPublicTypeDisposable } from '@alilc/lowcode-types'; +import { IOutlinePanelPluginContext } from '../controllers/tree-master'; class ModalTreeNodeView extends PureComponent<{ treeNode: TreeNode; - pluginContext: IPublicModelPluginContext; }, { treeChildren: TreeNode[] | null; }> { private modalNodesManager: IPublicModelModalNodesManager | undefined | null; - readonly pluginContext: IPublicModelPluginContext; + readonly pluginContext: IOutlinePanelPluginContext; - constructor(props: any) { + constructor(props: { + treeNode: TreeNode; + }) { super(props); // 模态管理对象 - this.pluginContext = props.pluginContext; + this.pluginContext = props.treeNode.pluginContext; const { project } = this.pluginContext; this.modalNodesManager = project.currentDocument?.modalNodesManager; this.state = { @@ -72,7 +74,6 @@ class ModalTreeNodeView extends PureComponent<{ treeChildren={this.state.treeChildren} expanded={expanded} isModal - pluginContext={this.pluginContext} /> </div> </div> @@ -83,7 +84,6 @@ class ModalTreeNodeView extends PureComponent<{ export default class TreeNodeView extends PureComponent<{ treeNode: TreeNode; isModal?: boolean; - pluginContext: IPublicModelPluginContext; isRootNode?: boolean; }> { state: { @@ -134,8 +134,8 @@ export default class TreeNodeView extends PureComponent<{ } componentDidMount() { - const { treeNode, pluginContext } = this.props; - const { project } = pluginContext; + const { treeNode } = this.props; + const { project } = treeNode.pluginContext; const doc = project.currentDocument; @@ -178,14 +178,14 @@ export default class TreeNodeView extends PureComponent<{ } shouldShowModalTreeNode(): boolean { - const { treeNode, isRootNode, pluginContext } = this.props; + const { treeNode, isRootNode } = this.props; if (!isRootNode) { // 只在 当前树 的根节点展示模态节点 return false; } // 当指定了新的根节点时,要从原始的根节点去获取模态节点 - const { project } = pluginContext; + const { project } = treeNode.pluginContext; const rootNode = project.currentDocument?.root; const rootTreeNode = treeNode.tree.getTreeNode(rootNode!); const modalNodes = rootTreeNode.children?.filter((item) => { @@ -234,19 +234,16 @@ export default class TreeNodeView extends PureComponent<{ hidden={this.state.hidden} locked={this.state.locked} expandable={this.state.expandable} - pluginContext={this.props.pluginContext} /> {shouldShowModalTreeNode && <ModalTreeNodeView treeNode={treeNode} - pluginContext={this.props.pluginContext} /> } <TreeBranches treeNode={treeNode} isModal={false} expanded={this.state.expanded} - pluginContext={this.props.pluginContext} treeChildren={this.state.treeChildren} /> </div> diff --git a/packages/plugin-outline-pane/src/views/tree-title.tsx b/packages/plugin-outline-pane/src/views/tree-title.tsx index 6e1b14a46..96a3dfa7b 100644 --- a/packages/plugin-outline-pane/src/views/tree-title.tsx +++ b/packages/plugin-outline-pane/src/views/tree-title.tsx @@ -1,7 +1,7 @@ import { KeyboardEvent, FocusEvent, Fragment, PureComponent } from 'react'; import classNames from 'classnames'; import { createIcon } from '@alilc/lowcode-utils'; -import { IPublicModelPluginContext, IPublicApiEvent } from '@alilc/lowcode-types'; +import { IPublicApiEvent } from '@alilc/lowcode-types'; import TreeNode from '../controllers/tree-node'; import { IconLock, IconUnlock, IconArrowRight, IconEyeClose, IconEye, IconCond, IconLoop, IconRadioActive, IconRadio, IconSetting } from '../icons'; @@ -23,7 +23,6 @@ export default class TreeTitle extends PureComponent<{ hidden: boolean; locked: boolean; expandable: boolean; - pluginContext: IPublicModelPluginContext; }> { state: { editing: boolean; @@ -53,7 +52,7 @@ export default class TreeTitle extends PureComponent<{ const { treeNode } = this.props; const value = (e.target as HTMLInputElement).value || ''; treeNode.setTitleLabel(value); - emitOutlineEvent(this.props.pluginContext.event, 'rename', treeNode, { value }); + emitOutlineEvent(this.props.treeNode.pluginContext.event, 'rename', treeNode, { value }); this.cancelEdit(); }; @@ -90,7 +89,8 @@ export default class TreeTitle extends PureComponent<{ } render() { - const { treeNode, isModal, pluginContext } = this.props; + const { treeNode, isModal } = this.props; + const { pluginContext } = treeNode; const { editing } = this.state; const isCNode = !treeNode.isRoot(); const { node } = treeNode; @@ -153,7 +153,7 @@ export default class TreeTitle extends PureComponent<{ <IconRadio className="tree-node-modal-radio" /> </div> )} - {isCNode && <ExpandBtn expandable={this.props.expandable} expanded={this.props.expanded} treeNode={treeNode} pluginContext={this.props.pluginContext} />} + {isCNode && <ExpandBtn expandable={this.props.expandable} expanded={this.props.expanded} treeNode={treeNode} />} <div className="tree-node-icon">{createIcon(treeNode.icon)}</div> <div className="tree-node-title-label"> {editing ? ( @@ -166,6 +166,7 @@ export default class TreeTitle extends PureComponent<{ /> ) : ( <Fragment> + {/* @ts-ignore */} <Title title={this.state.title} match={filterWorking && matchSelf} @@ -175,6 +176,7 @@ export default class TreeTitle extends PureComponent<{ {node.slotFor && ( <a className="tree-node-tag slot"> {/* todo: click redirect to prop */} + {/* @ts-ignore */} <Tip>{intlNode('Slot for {prop}', { prop: node.slotFor.key })}</Tip> </a> )} @@ -182,6 +184,7 @@ export default class TreeTitle extends PureComponent<{ <a className="tree-node-tag loop"> {/* todo: click todo something */} <IconLoop /> + {/* @ts-ignore */} <Tip>{intlNode('Loop')}</Tip> </a> )} @@ -189,15 +192,16 @@ export default class TreeTitle extends PureComponent<{ <a className="tree-node-tag cond"> {/* todo: click todo something */} <IconCond /> + {/* @ts-ignore */} <Tip>{intlNode('Conditional')}</Tip> </a> )} </Fragment> )} </div> - {shouldShowHideBtn && <HideBtn hidden={this.props.hidden} treeNode={treeNode} pluginContext={this.props.pluginContext} />} - {shouldShowLockBtn && <LockBtn locked={this.props.locked} treeNode={treeNode} pluginContext={this.props.pluginContext} />} - {shouldEditBtn && <RenameBtn treeNode={treeNode} pluginContext={this.props.pluginContext} onClick={this.enableEdit} /> } + {shouldShowHideBtn && <HideBtn hidden={this.props.hidden} treeNode={treeNode} />} + {shouldShowLockBtn && <LockBtn locked={this.props.locked} treeNode={treeNode} />} + {shouldEditBtn && <RenameBtn treeNode={treeNode} onClick={this.enableEdit} /> } </div> ); @@ -206,11 +210,10 @@ export default class TreeTitle extends PureComponent<{ class RenameBtn extends PureComponent<{ treeNode: TreeNode; - pluginContext: IPublicModelPluginContext; onClick: (e: any) => void; }> { render() { - const { intl, common } = this.props.pluginContext; + const { intl, common } = this.props.treeNode.pluginContext; const Tip = common.editorCabin.Tip; return ( <div @@ -218,6 +221,7 @@ class RenameBtn extends PureComponent<{ onClick={this.props.onClick} > <IconSetting /> + {/* @ts-ignore */} <Tip>{intl('Rename')}</Tip> </div> ); @@ -226,12 +230,11 @@ class RenameBtn extends PureComponent<{ class LockBtn extends PureComponent<{ treeNode: TreeNode; - pluginContext: IPublicModelPluginContext; locked: boolean; }> { render() { const { treeNode, locked } = this.props; - const { intl, common } = this.props.pluginContext; + const { intl, common } = this.props.treeNode.pluginContext; const Tip = common.editorCabin.Tip; return ( <div @@ -242,6 +245,7 @@ class LockBtn extends PureComponent<{ }} > {locked ? <IconUnlock /> : <IconLock /> } + {/* @ts-ignore */} <Tip>{locked ? intl('Unlock') : intl('Lock')}</Tip> </div> ); @@ -251,24 +255,24 @@ class LockBtn extends PureComponent<{ class HideBtn extends PureComponent<{ treeNode: TreeNode; hidden: boolean; - pluginContext: IPublicModelPluginContext; }, { hidden: boolean; }> { render() { const { treeNode, hidden } = this.props; - const { intl, common } = this.props.pluginContext; + const { intl, common } = treeNode.pluginContext; const Tip = common.editorCabin.Tip; return ( <div className="tree-node-hide-btn" onClick={(e) => { e.stopPropagation(); - emitOutlineEvent(this.props.pluginContext.event, hidden ? 'show' : 'hide', treeNode); + emitOutlineEvent(treeNode.pluginContext.event, hidden ? 'show' : 'hide', treeNode); treeNode.setHidden(!hidden); }} > {hidden ? <IconEye /> : <IconEyeClose />} + {/* @ts-ignore */} <Tip>{hidden ? intl('Show') : intl('Hide')}</Tip> </div> ); @@ -277,7 +281,6 @@ class HideBtn extends PureComponent<{ class ExpandBtn extends PureComponent<{ treeNode: TreeNode; - pluginContext: IPublicModelPluginContext; expanded: boolean; expandable: boolean; }> { @@ -294,7 +297,7 @@ class ExpandBtn extends PureComponent<{ if (expanded) { e.stopPropagation(); } - emitOutlineEvent(this.props.pluginContext.event, expanded ? 'collapse' : 'expand', treeNode); + emitOutlineEvent(treeNode.pluginContext.event, expanded ? 'collapse' : 'expand', treeNode); treeNode.setExpanded(!expanded); }} > diff --git a/packages/plugin-outline-pane/src/views/tree.tsx b/packages/plugin-outline-pane/src/views/tree.tsx index 930c65cce..4bc502886 100644 --- a/packages/plugin-outline-pane/src/views/tree.tsx +++ b/packages/plugin-outline-pane/src/views/tree.tsx @@ -2,7 +2,7 @@ import { MouseEvent as ReactMouseEvent, PureComponent } from 'react'; import { isFormEvent, canClickNode, isShaken } from '@alilc/lowcode-utils'; import { Tree } from '../controllers/tree'; import TreeNodeView from './tree-node'; -import { IPublicEnumDragObjectType, IPublicModelPluginContext, IPublicModelNode } from '@alilc/lowcode-types'; +import { IPublicEnumDragObjectType, IPublicModelNode } from '@alilc/lowcode-types'; import TreeNode from '../controllers/tree-node'; function getTreeNodeIdByEvent(e: ReactMouseEvent, stop: Element): null | string { @@ -20,12 +20,21 @@ function getTreeNodeIdByEvent(e: ReactMouseEvent, stop: Element): null | string export default class TreeView extends PureComponent<{ tree: Tree; - pluginContext: IPublicModelPluginContext; }> { private shell: HTMLDivElement | null = null; + private ignoreUpSelected = false; + + private boostEvent?: MouseEvent; + + state: { + root: TreeNode | null; + } = { + root: null, + }; + private hover(e: ReactMouseEvent) { - const { project } = this.props.pluginContext; + const { project } = this.props.tree.pluginContext; const detecting = project.currentDocument?.detecting; if (detecting?.enable) { return; @@ -54,7 +63,7 @@ export default class TreeView extends PureComponent<{ return; } - const { project, event, canvas } = this.props.pluginContext; + const { project, event, canvas } = this.props.tree.pluginContext; const doc = project.currentDocument; const selection = doc?.selection; const focusNode = doc?.focusNode; @@ -109,10 +118,6 @@ export default class TreeView extends PureComponent<{ return tree.getTreeNodeById(id); } - private ignoreUpSelected = false; - - private boostEvent?: MouseEvent; - private onMouseDown = (e: ReactMouseEvent) => { if (isFormEvent(e.nativeEvent)) { return; @@ -127,7 +132,7 @@ export default class TreeView extends PureComponent<{ if (!canClickNode(node, e)) { return; } - const { project, canvas } = this.props.pluginContext; + const { project, canvas } = this.props.tree.pluginContext; const selection = project.currentDocument?.selection; const focusNode = project.currentDocument?.focusNode; @@ -166,22 +171,16 @@ export default class TreeView extends PureComponent<{ }; private onMouseLeave = () => { - const { pluginContext } = this.props; + const { pluginContext } = this.props.tree; const { project } = pluginContext; const doc = project.currentDocument; doc?.detecting.leave(); }; - state: { - root: TreeNode | null - } = { - root: null, - }; - componentDidMount() { - const { tree, pluginContext } = this.props; + const { tree } = this.props; const { root } = tree; - const { project } = pluginContext; + const { project } = tree.pluginContext; this.setState({ root }); const doc = project.currentDocument; doc?.onFocusNodeChanged(() => { @@ -208,7 +207,6 @@ export default class TreeView extends PureComponent<{ <TreeNodeView key={this.state.root?.id} treeNode={this.state.root} - pluginContext={this.props.pluginContext} isRootNode /> </div> diff --git a/packages/shell/src/model/editor-view.ts b/packages/shell/src/model/editor-view.ts new file mode 100644 index 000000000..31027a5de --- /dev/null +++ b/packages/shell/src/model/editor-view.ts @@ -0,0 +1,27 @@ +import { editorViewSymbol, pluginContextSymbol } from '../symbols'; +import { IPublicModelPluginContext } from '@alilc/lowcode-types'; +import { IViewContext } from '@alilc/lowcode-workspace'; + +export class EditorView { + [editorViewSymbol]: IViewContext; + + [pluginContextSymbol]: IPublicModelPluginContext; + + constructor(editorView: IViewContext) { + this[editorViewSymbol] = editorView; + this[pluginContextSymbol] = this[editorViewSymbol].innerPlugins._getLowCodePluginContext({ + pluginName: '', + }); + } + + toProxy() { + return new Proxy(this, { + get(target, prop, receiver) { + if ((target[pluginContextSymbol] as any)[prop as string]) { + return Reflect.get(target[pluginContextSymbol], prop, receiver); + } + return Reflect.get(target, prop, receiver); + }, + }); + } +} diff --git a/packages/shell/src/model/index.ts b/packages/shell/src/model/index.ts index cd481643e..8b668c947 100644 --- a/packages/shell/src/model/index.ts +++ b/packages/shell/src/model/index.ts @@ -19,3 +19,4 @@ export * from './active-tracker'; export * from './plugin-instance'; export * from './window'; export * from './clipboard'; +export * from './editor-view'; diff --git a/packages/shell/src/model/window.ts b/packages/shell/src/model/window.ts index 23bc5c06d..6af2534dd 100644 --- a/packages/shell/src/model/window.ts +++ b/packages/shell/src/model/window.ts @@ -2,6 +2,7 @@ import { windowSymbol } from '../symbols'; import { IPublicModelResource, IPublicModelWindow, IPublicTypeDisposable } from '@alilc/lowcode-types'; import { IEditorWindow } from '@alilc/lowcode-workspace'; import { Resource as ShellResource } from './resource'; +import { EditorView } from './editor-view'; export class Window implements IPublicModelWindow { private readonly [windowSymbol]: IEditorWindow; @@ -42,7 +43,11 @@ export class Window implements IPublicModelWindow { return await this[windowSymbol].save(); } - get plugins() { - return this[windowSymbol].plugins; + get currentEditorView() { + return new EditorView(this[windowSymbol].editorView).toProxy() as any; + } + + get editorViews() { + return Array.from(this[windowSymbol].editorViews.values()).map(d => new EditorView(d).toProxy() as any); } } diff --git a/packages/shell/src/symbols.ts b/packages/shell/src/symbols.ts index 4ba6ff236..48d6a9656 100644 --- a/packages/shell/src/symbols.ts +++ b/packages/shell/src/symbols.ts @@ -34,3 +34,5 @@ export const resourceSymbol = Symbol('resource'); export const clipboardSymbol = Symbol('clipboard'); export const configSymbol = Symbol('configSymbol'); export const conditionGroupSymbol = Symbol('conditionGroup'); +export const editorViewSymbol = Symbol('editorView'); +export const pluginContextSymbol = Symbol('pluginContext'); \ No newline at end of file diff --git a/packages/types/src/shell/enum/index.ts b/packages/types/src/shell/enum/index.ts index 506055825..f3d558011 100644 --- a/packages/types/src/shell/enum/index.ts +++ b/packages/types/src/shell/enum/index.ts @@ -2,4 +2,5 @@ export * from './event-names'; export * from './transition-type'; export * from './transform-stage'; export * from './drag-object-type'; -export * from './prop-value-changed-type'; \ No newline at end of file +export * from './prop-value-changed-type'; +export * from './plugin-register-level'; \ No newline at end of file diff --git a/packages/types/src/shell/enum/plugin-register-level.ts b/packages/types/src/shell/enum/plugin-register-level.ts new file mode 100644 index 000000000..a0d9b746b --- /dev/null +++ b/packages/types/src/shell/enum/plugin-register-level.ts @@ -0,0 +1,6 @@ +export enum IPublicEnumPluginRegisterLevel { + Default = 'default', + Workspace = 'workspace', + Resource = 'resource', + EditorView = 'editorView', +} \ No newline at end of file diff --git a/packages/types/src/shell/model/dragon.ts b/packages/types/src/shell/model/dragon.ts index 662eb6a00..917149faf 100644 --- a/packages/types/src/shell/model/dragon.ts +++ b/packages/types/src/shell/model/dragon.ts @@ -1,5 +1,5 @@ /* eslint-disable max-len */ -import { IPublicTypeDragNodeDataObject, IPublicTypeDragObject } from '../type'; +import { IPublicTypeDisposable, IPublicTypeDragNodeDataObject, IPublicTypeDragObject } from '../type'; import { IPublicModelDragObject, IPublicModelLocateEvent, IPublicModelNode } from './'; export interface IPublicModelDragon< @@ -19,7 +19,7 @@ export interface IPublicModelDragon< * @param func * @returns */ - onDragstart(func: (e: LocateEvent) => any): () => void; + onDragstart(func: (e: LocateEvent) => any): IPublicTypeDisposable; /** * 绑定 drag 事件 @@ -27,7 +27,7 @@ export interface IPublicModelDragon< * @param func * @returns */ - onDrag(func: (e: LocateEvent) => any): () => void; + onDrag(func: (e: LocateEvent) => any): IPublicTypeDisposable; /** * 绑定 dragend 事件 @@ -35,7 +35,7 @@ export interface IPublicModelDragon< * @param func * @returns */ - onDragend(func: (o: { dragObject: IPublicModelDragObject; copy?: boolean }) => any): () => void; + onDragend(func: (o: { dragObject: IPublicModelDragObject; copy?: boolean }) => any): IPublicTypeDisposable; /** * 设置拖拽监听的区域 shell,以及自定义拖拽转换函数 boost diff --git a/packages/types/src/shell/model/editor-view.ts b/packages/types/src/shell/model/editor-view.ts new file mode 100644 index 000000000..793417845 --- /dev/null +++ b/packages/types/src/shell/model/editor-view.ts @@ -0,0 +1,3 @@ +import { IPublicModelPluginContext } from './plugin-context'; + +export interface IPublicModelEditorView extends IPublicModelPluginContext {} \ No newline at end of file diff --git a/packages/types/src/shell/model/index.ts b/packages/types/src/shell/model/index.ts index b62fb76b9..e310128ca 100644 --- a/packages/types/src/shell/model/index.ts +++ b/packages/types/src/shell/model/index.ts @@ -30,3 +30,4 @@ export * from './sensor'; export * from './resource'; export * from './clipboard'; export * from './setting-field'; +export * from './editor-view'; diff --git a/packages/types/src/shell/model/plugin-context.ts b/packages/types/src/shell/model/plugin-context.ts index 5d97b5472..e670e54ea 100644 --- a/packages/types/src/shell/model/plugin-context.ts +++ b/packages/types/src/shell/model/plugin-context.ts @@ -12,18 +12,11 @@ import { IPublicApiPlugins, IPublicApiWorkspace, } from '../api'; +import { IPublicEnumPluginRegisterLevel } from '../enum'; import { IPublicModelEngineConfig } from './'; export interface IPublicModelPluginContext { - /** - * 对于插件开发者来说,可以在 context 挂载自定义的内容,作为插件内全局上下文使用 - * - * for plugin developers, costom properties can be add to plugin context - * from inside plugin for convenience. - */ - [key: string]: any; - /** * 可通过该对象读取插件初始化配置 * by using this, init options can be accessed from inside plugin @@ -108,6 +101,12 @@ export interface IPublicModelPluginContext { * @tutorial https://lowcode-engine.cn/site/docs/api/workspace */ get workspace(): IPublicApiWorkspace; + + /** + * 插件注册层级 + * @since v1.1.7 + */ + get registerLevel(): IPublicEnumPluginRegisterLevel; } /** diff --git a/packages/types/src/shell/model/window.ts b/packages/types/src/shell/model/window.ts index bb27ce317..5f77b0cbf 100644 --- a/packages/types/src/shell/model/window.ts +++ b/packages/types/src/shell/model/window.ts @@ -1,6 +1,7 @@ import { ReactElement } from 'react'; import { IPublicTypeDisposable, IPublicTypeNodeSchema } from '../type'; import { IPublicModelResource } from './resource'; +import { IPublicModelEditorView } from './editor-view'; export interface IPublicModelWindow< Resource = IPublicModelResource @@ -18,6 +19,18 @@ export interface IPublicModelWindow< /** 窗口资源类型 */ resource?: Resource; + /** + * 窗口当前视图 + * @since v1.1.7 + */ + currentEditorView: IPublicModelEditorView; + + /** + * 窗口全部视图实例 + * @since v1.1.7 + */ + editorViews: IPublicModelEditorView[]; + /** 当前窗口导入 schema */ importSchema(schema: IPublicTypeNodeSchema): void; diff --git a/packages/workspace/src/context/base-context.ts b/packages/workspace/src/context/base-context.ts index 57a38712b..b12a78183 100644 --- a/packages/workspace/src/context/base-context.ts +++ b/packages/workspace/src/context/base-context.ts @@ -44,6 +44,7 @@ import { IPublicApiProject, IPublicApiSetters, IPublicApiSkeleton, + IPublicEnumPluginRegisterLevel, IPublicModelPluginContext, IPublicTypePluginMeta, } from '@alilc/lowcode-types'; @@ -100,7 +101,7 @@ export class BasicContext implements IBasicContext { preference: IPluginPreferenceMananger; workspace: IWorkspace; - constructor(innerWorkspace: IWorkspace, viewName: string, public editorWindow?: IEditorWindow) { + constructor(innerWorkspace: IWorkspace, viewName: string, registerLevel: IPublicEnumPluginRegisterLevel, public editorWindow?: IEditorWindow) { const editor = new Editor(viewName, true); const innerSkeleton = new InnerSkeleton(editor, viewName); @@ -168,6 +169,7 @@ export class BasicContext implements IBasicContext { if (editorWindow) { context.editorWindow = new Window(editorWindow); } + context.registerLevel = registerLevel; }, }; diff --git a/packages/workspace/src/context/view-context.ts b/packages/workspace/src/context/view-context.ts index 393989850..7dfc66393 100644 --- a/packages/workspace/src/context/view-context.ts +++ b/packages/workspace/src/context/view-context.ts @@ -1,12 +1,16 @@ import { computed, makeObservable, obx } from '@alilc/lowcode-editor-core'; -import { IPublicEditorViewConfig, IPublicTypeEditorView } from '@alilc/lowcode-types'; +import { IPublicEditorViewConfig, IPublicEnumPluginRegisterLevel, IPublicTypeEditorView } from '@alilc/lowcode-types'; import { flow } from 'mobx'; import { IWorkspace } from '../workspace'; -import { BasicContext } from './base-context'; +import { BasicContext, IBasicContext } from './base-context'; import { IEditorWindow } from '../window'; import { getWebviewPlugin } from '../inner-plugins/webview'; -export class Context extends BasicContext { +export interface IViewContext extends IBasicContext { + +} + +export class Context extends BasicContext implements IViewContext { viewName = 'editor-view'; instance: IPublicEditorViewConfig; @@ -30,7 +34,7 @@ export class Context extends BasicContext { }); constructor(public workspace: IWorkspace, public editorWindow: IEditorWindow, public editorView: IPublicTypeEditorView, options: Object | undefined) { - super(workspace, editorView.viewName, editorWindow); + super(workspace, editorView.viewName, IPublicEnumPluginRegisterLevel.EditorView, editorWindow); this.viewType = editorView.viewType || 'editor'; this.viewName = editorView.viewName; this.instance = editorView(this.innerPlugins._getLowCodePluginContext({ diff --git a/packages/workspace/src/index.ts b/packages/workspace/src/index.ts index 1f3b77a7a..6c437fad0 100644 --- a/packages/workspace/src/index.ts +++ b/packages/workspace/src/index.ts @@ -4,3 +4,4 @@ export * from './window'; export * from './layouts/workbench'; export { Resource } from './resource'; export type { IResource } from './resource'; +export type { IViewContext } from './context/view-context'; diff --git a/packages/workspace/src/resource.ts b/packages/workspace/src/resource.ts index 222cb1162..e51993c81 100644 --- a/packages/workspace/src/resource.ts +++ b/packages/workspace/src/resource.ts @@ -1,5 +1,5 @@ import { ISkeleton } from '@alilc/lowcode-editor-skeleton'; -import { IPublicTypeEditorView, IPublicResourceData, IPublicResourceTypeConfig, IBaseModelResource } from '@alilc/lowcode-types'; +import { IPublicTypeEditorView, IPublicResourceData, IPublicResourceTypeConfig, IBaseModelResource, IPublicEnumPluginRegisterLevel } from '@alilc/lowcode-types'; import { Logger } from '@alilc/lowcode-utils'; import { BasicContext, IBasicContext } from './context/base-context'; import { ResourceType, IResourceType } from './resource-type'; @@ -75,7 +75,7 @@ export class Resource implements IResource { } constructor(readonly resourceData: IPublicResourceData, readonly resourceType: IResourceType, readonly workspace: IWorkspace) { - this.context = new BasicContext(workspace, `resource-${resourceData.resourceName || resourceType.name}`); + this.context = new BasicContext(workspace, `resource-${resourceData.resourceName || resourceType.name}`, IPublicEnumPluginRegisterLevel.Resource); this.resourceTypeInstance = resourceType.resourceTypeModel(this.context.innerPlugins._getLowCodePluginContext({ pluginName: '', }), this.options); diff --git a/packages/workspace/src/window.ts b/packages/workspace/src/window.ts index 37a1622c0..92b707851 100644 --- a/packages/workspace/src/window.ts +++ b/packages/workspace/src/window.ts @@ -1,6 +1,6 @@ import { uniqueId } from '@alilc/lowcode-utils'; import { createModuleEventBus, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core'; -import { Context } from './context/view-context'; +import { Context, IViewContext } from './context/view-context'; import { IWorkspace } from './workspace'; import { IResource } from './resource'; import { IPublicTypeDisposable } from '../../types/es/shell/type/disposable'; @@ -13,10 +13,12 @@ interface IWindowCOnfig { sleep?: boolean; } -export interface IEditorWindow extends Omit<IPublicModelWindow<IResource>, 'changeViewType'> { +export interface IEditorWindow extends Omit<IPublicModelWindow<IResource>, 'changeViewType' | 'currentEditorView' | 'editorViews'> { readonly resource: IResource; - editorViews: Map<string, Context>; + editorViews: Map<string, IViewContext>; + + editorView: IViewContext; changeViewType: (name: string, ignoreEmit?: boolean) => void; @@ -71,6 +73,9 @@ export class EditorWindow implements IEditorWindow { async save() { const value: any = {}; const editorViews = this.resource.editorViews; + if (!editorViews) { + return; + } for (let i = 0; i < editorViews.length; i++) { const name = editorViews[i].viewName; const saveResult = await this.editorViews.get(name)?.save(); diff --git a/packages/workspace/src/workspace.ts b/packages/workspace/src/workspace.ts index ea19ea0c0..37341cdc3 100644 --- a/packages/workspace/src/workspace.ts +++ b/packages/workspace/src/workspace.ts @@ -1,6 +1,6 @@ import { IDesigner, ILowCodePluginManager, LowCodePluginManager } from '@alilc/lowcode-designer'; import { createModuleEventBus, Editor, IEditor, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core'; -import { IPublicApiPlugins, IPublicApiWorkspace, IPublicResourceList, IPublicTypeDisposable, IPublicTypeResourceType, IShellModelFactory } from '@alilc/lowcode-types'; +import { IPublicApiPlugins, IPublicApiWorkspace, IPublicEnumPluginRegisterLevel, IPublicResourceList, IPublicTypeDisposable, IPublicTypeResourceType, IShellModelFactory } from '@alilc/lowcode-types'; import { BasicContext } from './context/base-context'; import { EditorWindow } from './window'; import type { IEditorWindow } from './window'; @@ -94,7 +94,7 @@ export class Workspace implements IWorkspace { readonly registryInnerPlugin: (designer: IDesigner, editor: IEditor, plugins: IPublicApiPlugins) => Promise<IPublicTypeDisposable>, readonly shellModelFactory: any, ) { - this.context = new BasicContext(this, ''); + this.context = new BasicContext(this, '', IPublicEnumPluginRegisterLevel.Workspace); makeObservable(this); }