diff --git a/docs/docs/api/model/window.md b/docs/docs/api/model/window.md
index 177d3438a..6a5413883 100644
--- a/docs/docs/api/model/window.md
+++ b/docs/docs/api/model/window.md
@@ -11,15 +11,41 @@ sidebar_position: 12
低代码设计器窗口模型
+## 变量
+
+### id
+
+窗口唯一 id
+
+### title
+
+窗口标题
+
+### resourceName
+
+窗口资源名字
+
## 方法签名
-### importSchema(schema: IPublicTypeNodeSchema)
-当前窗口导入 schema
+### importSchema
+当前窗口导入 schema, 会调用当前窗口对应资源的 import 钩子
+
+```typescript
+function importSchema(schema: IPublicTypeNodeSchema): void
+```
相关类型:[IPublicTypeNodeSchema](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/node-schema.ts)
-### changeViewType(viewName: string)
+### changeViewType
修改当前窗口视图类型
-### async save()
-调用当前窗口视图保存钩子
+```typescript
+function changeViewType(viewName: string): void
+```
+
+### save
+当前窗口的保存方法,会调用当前窗口对应资源的 save 钩子
+
+```typescript
+function save(): Promise(void)
+```
diff --git a/docs/docs/api/workspace.md b/docs/docs/api/workspace.md
index e710bceec..5a9ad2a44 100644
--- a/docs/docs/api/workspace.md
+++ b/docs/docs/api/workspace.md
@@ -21,6 +21,30 @@ sidebar_position: 12
当前设计器窗口模型
+```typescript
+get window(): IPublicModelWindow
+```
+
+关联模型 [IPublicModelWindow](./model/window)
+
+### plugins
+
+应用级别的插件注册
+
+```typescript
+get plugins(): IPublicApiPlugins
+```
+
+关联模型 [IPublicApiPlugins](./plugins)
+
+### windows
+
+当前设计器的编辑窗口
+
+```typescript
+get window(): IPublicModelWindow[]
+```
+
关联模型 [IPublicModelWindow](./model/window)
## 方法签名
@@ -34,3 +58,19 @@ registerResourceType(resourceName: string, resourceType: 'editor', options: IPub
```
相关类型:[IPublicResourceOptions](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/resource-options.ts)
+
+### onChangeWindows
+
+窗口新增/删除的事件
+
+```typescript
+function onChangeWindows(fn: () => void): void;
+```
+
+### onChangeActiveWindow
+
+active 窗口变更事件
+
+```typescript
+function onChangeActiveWindow(fn: () => void): void;
+```
diff --git a/packages/designer/src/component-actions.ts b/packages/designer/src/component-actions.ts
new file mode 100644
index 000000000..793bbdc20
--- /dev/null
+++ b/packages/designer/src/component-actions.ts
@@ -0,0 +1,155 @@
+import { IPublicTypeComponentAction, IPublicTypeMetadataTransducer } from '@alilc/lowcode-types';
+import { engineConfig } from '@alilc/lowcode-editor-core';
+import { intlNode } from './locale';
+import {
+ IconLock,
+ IconUnlock,
+ IconRemove,
+ IconClone,
+ IconHidden,
+} from './icons';
+import { Node } from './document';
+import { componentDefaults, legacyIssues } from './transducers';
+
+export class ComponentActions {
+ actions: IPublicTypeComponentAction[] = [
+ {
+ name: 'remove',
+ content: {
+ icon: IconRemove,
+ title: intlNode('remove'),
+ /* istanbul ignore next */
+ action(node: Node) {
+ node.remove();
+ },
+ },
+ important: true,
+ },
+ {
+ name: 'hide',
+ content: {
+ icon: IconHidden,
+ title: intlNode('hide'),
+ /* istanbul ignore next */
+ action(node: Node) {
+ node.setVisible(false);
+ },
+ },
+ /* istanbul ignore next */
+ condition: (node: Node) => {
+ return node.componentMeta.isModal;
+ },
+ important: true,
+ },
+ {
+ name: 'copy',
+ content: {
+ icon: IconClone,
+ title: intlNode('copy'),
+ /* istanbul ignore next */
+ action(node: Node) {
+ // node.remove();
+ const { document: doc, parent, index } = node;
+ if (parent) {
+ const newNode = doc.insertNode(parent, node, index + 1, true);
+ newNode.select();
+ const { isRGL, rglNode } = node.getRGL();
+ if (isRGL) {
+ // 复制 layout 信息
+ let layout = rglNode.getPropValue('layout') || [];
+ let curLayout = layout.filter((item) => item.i === node.getPropValue('fieldId'));
+ if (curLayout && curLayout[0]) {
+ layout.push({
+ ...curLayout[0],
+ i: newNode.getPropValue('fieldId'),
+ });
+ rglNode.setPropValue('layout', layout);
+ // 如果是磁贴块复制,则需要滚动到影响位置
+ setTimeout(() => newNode.document.simulator?.scrollToNode(newNode), 10);
+ }
+ }
+ }
+ },
+ },
+ important: true,
+ },
+ {
+ name: 'lock',
+ content: {
+ icon: IconLock, // 锁定 icon
+ title: intlNode('lock'),
+ /* istanbul ignore next */
+ action(node: Node) {
+ node.lock();
+ },
+ },
+ /* istanbul ignore next */
+ condition: (node: Node) => {
+ return engineConfig.get('enableCanvasLock', false) && node.isContainer() && !node.isLocked;
+ },
+ important: true,
+ },
+ {
+ name: 'unlock',
+ content: {
+ icon: IconUnlock, // 解锁 icon
+ title: intlNode('unlock'),
+ /* istanbul ignore next */
+ action(node: Node) {
+ node.lock(false);
+ },
+ },
+ /* istanbul ignore next */
+ condition: (node: Node) => {
+ return engineConfig.get('enableCanvasLock', false) && node.isContainer() && node.isLocked;
+ },
+ important: true,
+ },
+ ];
+
+ constructor() {
+ this.registerMetadataTransducer(legacyIssues, 2, 'legacy-issues'); // should use a high level priority, eg: 2
+ this.registerMetadataTransducer(componentDefaults, 100, 'component-defaults');
+ }
+
+ removeBuiltinComponentAction(name: string) {
+ const i = this.actions.findIndex((action) => action.name === name);
+ if (i > -1) {
+ this.actions.splice(i, 1);
+ }
+ }
+ addBuiltinComponentAction(action: IPublicTypeComponentAction) {
+ this.actions.push(action);
+ }
+
+ modifyBuiltinComponentAction(
+ actionName: string,
+ handle: (action: IPublicTypeComponentAction) => void,
+ ) {
+ const builtinAction = this.actions.find((action) => action.name === actionName);
+ if (builtinAction) {
+ handle(builtinAction);
+ }
+ }
+
+ private metadataTransducers: IPublicTypeMetadataTransducer[] = [];
+
+ registerMetadataTransducer(
+ transducer: IPublicTypeMetadataTransducer,
+ level = 100,
+ id?: string,
+ ) {
+ transducer.level = level;
+ transducer.id = id;
+ const i = this.metadataTransducers.findIndex((item) => item.level != null && item.level > level);
+ if (i < 0) {
+ this.metadataTransducers.push(transducer);
+ } else {
+ this.metadataTransducers.splice(i, 0, transducer);
+ }
+ }
+
+ getRegisteredMetadataTransducers(): IPublicTypeMetadataTransducer[] {
+ return this.metadataTransducers;
+ }
+}
\ No newline at end of file
diff --git a/packages/designer/src/component-meta.ts b/packages/designer/src/component-meta.ts
index 45f431b6d..70d8630b5 100644
--- a/packages/designer/src/component-meta.ts
+++ b/packages/designer/src/component-meta.ts
@@ -4,7 +4,6 @@ import {
IPublicTypeNpmInfo,
IPublicTypeNodeData,
IPublicTypeNodeSchema,
- IPublicTypeComponentAction,
IPublicTypeTitleContent,
IPublicTypeTransformedComponentMetadata,
IPublicTypeNestingFilter,
@@ -15,20 +14,13 @@ import {
IPublicModelComponentMeta,
} from '@alilc/lowcode-types';
import { deprecate, isRegExp, isTitleConfig } from '@alilc/lowcode-utils';
-import { computed, engineConfig, createModuleEventBus, IEventBus } from '@alilc/lowcode-editor-core';
-import { componentDefaults, legacyIssues } from './transducers';
+import { computed, createModuleEventBus, IEventBus } from '@alilc/lowcode-editor-core';
import { isNode, Node, INode } from './document';
import { Designer } from './designer';
-import { intlNode } from './locale';
import {
- IconLock,
- IconUnlock,
IconContainer,
IconPage,
IconComponent,
- IconRemove,
- IconClone,
- IconHidden,
} from './icons';
export function ensureAList(list?: string | string[]): string[] | null {
@@ -272,7 +264,7 @@ export class ComponentMeta implements IComponentMeta {
}
private transformMetadata(metadta: IPublicTypeComponentMetadata): IPublicTypeTransformedComponentMetadata {
- const result = getRegisteredMetadataTransducers().reduce((prevMetadata, current) => {
+ const result = this.designer.componentActions.getRegisteredMetadataTransducers().reduce((prevMetadata, current) => {
return current(prevMetadata);
}, preprocessMetadata(metadta));
@@ -300,7 +292,7 @@ export class ComponentMeta implements IComponentMeta {
const disabled =
ensureAList(disableBehaviors) ||
(this.isRootComponent(false) ? ['copy', 'remove', 'lock', 'unlock'] : null);
- actions = builtinComponentActions.concat(
+ actions = this.designer.componentActions.actions.concat(
this.designer.getGlobalComponentActions() || [],
actions || [],
);
@@ -382,142 +374,3 @@ function preprocessMetadata(metadata: IPublicTypeComponentMetadata): IPublicType
};
}
-
-const metadataTransducers: IPublicTypeMetadataTransducer[] = [];
-
-export function registerMetadataTransducer(
- transducer: IPublicTypeMetadataTransducer,
- level = 100,
- id?: string,
-) {
- transducer.level = level;
- transducer.id = id;
- const i = metadataTransducers.findIndex((item) => item.level != null && item.level > level);
- if (i < 0) {
- metadataTransducers.push(transducer);
- } else {
- metadataTransducers.splice(i, 0, transducer);
- }
-}
-
-export function getRegisteredMetadataTransducers(): IPublicTypeMetadataTransducer[] {
- return metadataTransducers;
-}
-
-const builtinComponentActions: IPublicTypeComponentAction[] = [
- {
- name: 'remove',
- content: {
- icon: IconRemove,
- title: intlNode('remove'),
- /* istanbul ignore next */
- action(node: Node) {
- node.remove();
- },
- },
- important: true,
- },
- {
- name: 'hide',
- content: {
- icon: IconHidden,
- title: intlNode('hide'),
- /* istanbul ignore next */
- action(node: Node) {
- node.setVisible(false);
- },
- },
- /* istanbul ignore next */
- condition: (node: Node) => {
- return node.componentMeta.isModal;
- },
- important: true,
- },
- {
- name: 'copy',
- content: {
- icon: IconClone,
- title: intlNode('copy'),
- /* istanbul ignore next */
- action(node: Node) {
- // node.remove();
- const { document: doc, parent, index } = node;
- if (parent) {
- const newNode = doc.insertNode(parent, node, index + 1, true);
- newNode.select();
- const { isRGL, rglNode } = node.getRGL();
- if (isRGL) {
- // 复制 layout 信息
- let layout = rglNode.getPropValue('layout') || [];
- let curLayout = layout.filter((item) => item.i === node.getPropValue('fieldId'));
- if (curLayout && curLayout[0]) {
- layout.push({
- ...curLayout[0],
- i: newNode.getPropValue('fieldId'),
- });
- rglNode.setPropValue('layout', layout);
- // 如果是磁贴块复制,则需要滚动到影响位置
- setTimeout(() => newNode.document.simulator?.scrollToNode(newNode), 10);
- }
- }
- }
- },
- },
- important: true,
- },
- {
- name: 'lock',
- content: {
- icon: IconLock, // 锁定 icon
- title: intlNode('lock'),
- /* istanbul ignore next */
- action(node: Node) {
- node.lock();
- },
- },
- /* istanbul ignore next */
- condition: (node: Node) => {
- return engineConfig.get('enableCanvasLock', false) && node.isContainer() && !node.isLocked;
- },
- important: true,
- },
- {
- name: 'unlock',
- content: {
- icon: IconUnlock, // 解锁 icon
- title: intlNode('unlock'),
- /* istanbul ignore next */
- action(node: Node) {
- node.lock(false);
- },
- },
- /* istanbul ignore next */
- condition: (node: Node) => {
- return engineConfig.get('enableCanvasLock', false) && node.isContainer() && node.isLocked;
- },
- important: true,
- },
-];
-
-export function removeBuiltinComponentAction(name: string) {
- const i = builtinComponentActions.findIndex((action) => action.name === name);
- if (i > -1) {
- builtinComponentActions.splice(i, 1);
- }
-}
-export function addBuiltinComponentAction(action: IPublicTypeComponentAction) {
- builtinComponentActions.push(action);
-}
-
-export function modifyBuiltinComponentAction(
- actionName: string,
- handle: (action: IPublicTypeComponentAction) => void,
-) {
- const builtinAction = builtinComponentActions.find((action) => action.name === actionName);
- if (builtinAction) {
- handle(builtinAction);
- }
-}
-
-registerMetadataTransducer(legacyIssues, 2, 'legacy-issues'); // should use a high level priority, eg: 2
-registerMetadataTransducer(componentDefaults, 100, 'component-defaults');
diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts
index d16d6c267..118d068cf 100644
--- a/packages/designer/src/designer/designer.ts
+++ b/packages/designer/src/designer/designer.ts
@@ -32,6 +32,7 @@ import { OffsetObserver, createOffsetObserver } from './offset-observer';
import { focusing } from './focusing';
import { SettingTopEntry } from './setting';
import { BemToolsManager } from '../builtin-simulator/bem-tools/manager';
+import { ComponentActions } from '../component-actions';
const logger = new Logger({ level: 'warn', bizName: 'designer' });
@@ -60,6 +61,8 @@ export interface DesignerProps {
export class Designer implements IDesigner {
readonly dragon = new Dragon(this);
+ readonly componentActions = new ComponentActions();
+
readonly activeTracker = new ActiveTracker();
readonly detecting = new Detecting();
diff --git a/packages/designer/src/designer/setting/setting-field.ts b/packages/designer/src/designer/setting/setting-field.ts
index a7d0ea9f9..ab5b8eadb 100644
--- a/packages/designer/src/designer/setting/setting-field.ts
+++ b/packages/designer/src/designer/setting/setting-field.ts
@@ -2,7 +2,7 @@ import { IPublicTypeTitleContent, IPublicTypeSetterType, IPublicTypeDynamicSette
import { Transducer } from './utils';
import { SettingPropEntry } from './setting-prop-entry';
import { SettingEntry } from './setting-entry';
-import { computed, obx, makeObservable, action } from '@alilc/lowcode-editor-core';
+import { computed, obx, makeObservable, action, untracked } from '@alilc/lowcode-editor-core';
import { cloneDeep, isCustomView, isDynamicSetter } from '@alilc/lowcode-utils';
function getSettingFieldCollectorKey(parent: SettingEntry, config: IPublicTypeFieldConfig) {
@@ -43,8 +43,10 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
return null;
}
if (isDynamicSetter(this._setter)) {
- const shellThis = this.internalToShellPropEntry();
- return this._setter.call(shellThis, shellThis);
+ return untracked(() => {
+ const shellThis = this.internalToShellPropEntry();
+ return this._setter.call(shellThis, shellThis);
+ });
}
return this._setter;
}
diff --git a/packages/designer/src/project/project-view.tsx b/packages/designer/src/project/project-view.tsx
index f6feaa0af..a16d4451a 100644
--- a/packages/designer/src/project/project-view.tsx
+++ b/packages/designer/src/project/project-view.tsx
@@ -4,7 +4,7 @@ import { Designer } from '../designer';
import { BuiltinSimulatorHostView } from '../builtin-simulator';
import './project.less';
-class BuiltinLoading extends Component {
+export class BuiltinLoading extends Component {
render() {
return (
diff --git a/packages/designer/src/project/project.ts b/packages/designer/src/project/project.ts
index 08b96d7c4..f140b30d4 100644
--- a/packages/designer/src/project/project.ts
+++ b/packages/designer/src/project/project.ts
@@ -30,6 +30,8 @@ export class Project implements IProject {
private _simulator?: ISimulatorHost;
+ private isRendererReady: boolean = false;
+
/**
* 模拟器
*/
@@ -318,6 +320,7 @@ export class Project implements IProject {
}
setRendererReady(renderer: any) {
+ this.isRendererReady = true;
this.emitter.emit('lowcode_engine_renderer_ready', renderer);
}
@@ -328,7 +331,10 @@ export class Project implements IProject {
};
}
- onRendererReady(fn: (args: any) => void): () => void {
+ onRendererReady(fn: () => void): () => void {
+ if (this.isRendererReady) {
+ fn();
+ }
this.emitter.on('lowcode_engine_renderer_ready', fn);
return () => {
this.emitter.removeListener('lowcode_engine_renderer_ready', fn);
diff --git a/packages/designer/tests/main/meta/component-meta.test.ts b/packages/designer/tests/main/meta/component-meta.test.ts
index a1a113d93..d943f85af 100644
--- a/packages/designer/tests/main/meta/component-meta.test.ts
+++ b/packages/designer/tests/main/meta/component-meta.test.ts
@@ -1,5 +1,4 @@
import '../../fixtures/window';
-import { Node } from '../../../src/document/node/node';
import { Designer } from '../../../src/designer/designer';
import divMeta from '../../fixtures/component-metadata/div';
import div2Meta from '../../fixtures/component-metadata/div2';
@@ -19,22 +18,18 @@ import page2Meta from '../../fixtures/component-metadata/page2';
import {
ComponentMeta,
isComponentMeta,
- removeBuiltinComponentAction,
- addBuiltinComponentAction,
- modifyBuiltinComponentAction,
ensureAList,
buildFilter,
- registerMetadataTransducer,
- getRegisteredMetadataTransducers,
} from '../../../src/component-meta';
-import { componentDefaults } from '../../../src/transducers';
-const mockCreateSettingEntry = jest.fn();
+
jest.mock('../../../src/designer/designer', () => {
return {
Designer: jest.fn().mockImplementation(() => {
+ const { ComponentActions } = require('../../../src/component-actions');
return {
getGlobalComponentActions: () => [],
+ componentActions: new ComponentActions(),
};
}),
};
@@ -126,12 +121,12 @@ describe('组件元数据处理', () => {
expect(meta.availableActions[1].name).toBe('hide');
expect(meta.availableActions[2].name).toBe('copy');
- removeBuiltinComponentAction('remove');
+ designer.componentActions.removeBuiltinComponentAction('remove');
expect(meta.availableActions).toHaveLength(4);
expect(meta.availableActions[0].name).toBe('hide');
expect(meta.availableActions[1].name).toBe('copy');
- addBuiltinComponentAction({
+ designer.componentActions.addBuiltinComponentAction({
name: 'new',
content: {
action() {},
@@ -227,17 +222,17 @@ describe('帮助函数', () => {
});
it('registerMetadataTransducer', () => {
- expect(getRegisteredMetadataTransducers()).toHaveLength(2);
+ expect(designer.componentActions.getRegisteredMetadataTransducers()).toHaveLength(2);
// 插入到 legacy-issues 和 component-defaults 的中间
- registerMetadataTransducer((metadata) => metadata, 3, 'noop');
- expect(getRegisteredMetadataTransducers()).toHaveLength(3);
+ designer.componentActions.registerMetadataTransducer((metadata) => metadata, 3, 'noop');
+ expect(designer.componentActions.getRegisteredMetadataTransducers()).toHaveLength(3);
- registerMetadataTransducer((metadata) => metadata);
- expect(getRegisteredMetadataTransducers()).toHaveLength(4);
+ designer.componentActions.registerMetadataTransducer((metadata) => metadata);
+ expect(designer.componentActions.getRegisteredMetadataTransducers()).toHaveLength(4);
});
it('modifyBuiltinComponentAction', () => {
- modifyBuiltinComponentAction('copy', (action) => {
+ designer.componentActions.modifyBuiltinComponentAction('copy', (action) => {
expect(action.name).toBe('copy');
});
});
diff --git a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx
index 50b18d50f..c12d966be 100644
--- a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx
+++ b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx
@@ -69,7 +69,7 @@ export class SettingsPrimaryPane extends Component<{ engineEditor: Editor; confi
}
const workspace = globalContext.get('workspace');
- const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
+ const editor = this.props.engineEditor;
const designer = editor.get('designer');
const current = designer?.currentSelection?.getNodes()?.[0];
let node: Node | null = settings.first;
diff --git a/packages/editor-skeleton/src/layouts/workbench.less b/packages/editor-skeleton/src/layouts/workbench.less
index af555c65e..4e96badcf 100644
--- a/packages/editor-skeleton/src/layouts/workbench.less
+++ b/packages/editor-skeleton/src/layouts/workbench.less
@@ -138,6 +138,16 @@ body {
display: flex;
flex-direction: column;
background-color: #edeff3;
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ z-index: -1;
+
+ &.active {
+ z-index: 999;
+ }
.lc-workbench {
diff --git a/packages/editor-skeleton/src/register-defaults.ts b/packages/editor-skeleton/src/register-defaults.ts
index bb35b277d..573631f78 100644
--- a/packages/editor-skeleton/src/register-defaults.ts
+++ b/packages/editor-skeleton/src/register-defaults.ts
@@ -1,15 +1,23 @@
-import { registerMetadataTransducer } from '@alilc/lowcode-designer';
import parseJSFunc from './transducers/parse-func';
import parseProps from './transducers/parse-props';
import addonCombine from './transducers/addon-combine';
+import { IPublicModelPluginContext } from '@alilc/lowcode-types';
-export const registerDefaults = () => {
- // parseFunc
- registerMetadataTransducer(parseJSFunc, 1, 'parse-func');
+export const registerDefaults = (ctx: IPublicModelPluginContext) => {
+ const { material } = ctx;
+ return {
+ init() {
+ // parseFunc
+ material.registerMetadataTransducer(parseJSFunc, 1, 'parse-func');
- // parseProps
- registerMetadataTransducer(parseProps, 5, 'parse-props');
+ // parseProps
+ material.registerMetadataTransducer(parseProps, 5, 'parse-props');
- // addon/platform custom
- registerMetadataTransducer(addonCombine, 10, 'combine-props');
+ // addon/platform custom
+ material.registerMetadataTransducer(addonCombine, 10, 'combine-props');
+ },
+ };
};
+
+
+registerDefaults.pluginName = '___register_defaults___';
diff --git a/packages/editor-skeleton/src/skeleton.ts b/packages/editor-skeleton/src/skeleton.ts
index 649c45271..90fba2e1b 100644
--- a/packages/editor-skeleton/src/skeleton.ts
+++ b/packages/editor-skeleton/src/skeleton.ts
@@ -50,6 +50,8 @@ export class Skeleton {
readonly topArea: Area
;
+ readonly subTopArea: Area;
+
readonly toolbar: Area;
readonly leftFixedArea: Area;
@@ -88,6 +90,17 @@ export class Skeleton {
},
false,
);
+ this.subTopArea = new Area(
+ this,
+ 'subTopArea',
+ (config) => {
+ if (isWidget(config)) {
+ return config;
+ }
+ return this.createWidget(config);
+ },
+ false,
+ );
this.toolbar = new Area(
this,
'toolbar',
@@ -389,6 +402,8 @@ export class Skeleton {
case 'topArea':
case 'top':
return this.topArea.add(parsedConfig as PanelDockConfig);
+ case 'subTopArea':
+ return this.subTopArea.add(parsedConfig as PanelDockConfig);
case 'toolbar':
return this.toolbar.add(parsedConfig as PanelDockConfig);
case 'mainArea':
diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts
index 99286c3c3..a580380a1 100644
--- a/packages/engine/src/engine-core.ts
+++ b/packages/engine/src/engine-core.ts
@@ -59,8 +59,6 @@ export * from './modules/skeleton-types';
export * from './modules/designer-types';
export * from './modules/lowcode-types';
-registerDefaults();
-
async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: Plugins) {
// 注册一批内置插件
await plugins.register(OutlinePlugin, {}, { autoInit: true });
@@ -68,6 +66,7 @@ async function registryInnerPlugin(designer: Designer, editor: Editor, plugins:
await plugins.register(setterRegistry, {}, { autoInit: true });
await plugins.register(defaultPanelRegistry(editor));
await plugins.register(builtinHotkey);
+ await plugins.register(registerDefaults);
}
const innerWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory);
@@ -82,6 +81,7 @@ editor.set('skeleton' as any, innerSkeleton);
const designer = new Designer({ editor, shellModelFactory });
editor.set('designer' as any, designer);
+
const { project: innerProject } = designer;
const innerHotkey = new InnerHotkey();
@@ -195,6 +195,7 @@ export async function init(
engineContainer,
);
innerWorkspace.setActive(true);
+ await innerWorkspace.plugins.init(pluginPreference);
return;
}
diff --git a/packages/shell/src/api/material.ts b/packages/shell/src/api/material.ts
index e824290a5..08deefdcc 100644
--- a/packages/shell/src/api/material.ts
+++ b/packages/shell/src/api/material.ts
@@ -1,11 +1,6 @@
import { Editor, globalContext } from '@alilc/lowcode-editor-core';
import {
Designer,
- registerMetadataTransducer,
- getRegisteredMetadataTransducers,
- addBuiltinComponentAction,
- removeBuiltinComponentAction,
- modifyBuiltinComponentAction,
isComponentMeta,
} from '@alilc/lowcode-designer';
import { IPublicTypeAssetsJson } from '@alilc/lowcode-utils';
@@ -85,20 +80,20 @@ export class Material implements IPublicApiMaterial {
* @param level
* @param id
*/
- registerMetadataTransducer(
+ registerMetadataTransducer = (
transducer: IPublicTypeMetadataTransducer,
level?: number,
id?: string | undefined,
- ) {
- registerMetadataTransducer(transducer, level, id);
- }
+ ) => {
+ this[designerSymbol].componentActions.registerMetadataTransducer(transducer, level, id);
+ };
/**
* 获取所有物料元数据管道函数
* @returns
*/
getRegisteredMetadataTransducers() {
- return getRegisteredMetadataTransducers();
+ return this[designerSymbol].componentActions.getRegisteredMetadataTransducers();
}
/**
@@ -147,7 +142,7 @@ export class Material implements IPublicApiMaterial {
* @param action
*/
addBuiltinComponentAction(action: IPublicTypeComponentAction) {
- addBuiltinComponentAction(action);
+ this[designerSymbol].componentActions.addBuiltinComponentAction(action);
}
/**
@@ -155,7 +150,7 @@ export class Material implements IPublicApiMaterial {
* @param name
*/
removeBuiltinComponentAction(name: string) {
- removeBuiltinComponentAction(name);
+ this[designerSymbol].componentActions.removeBuiltinComponentAction(name);
}
/**
@@ -164,7 +159,7 @@ export class Material implements IPublicApiMaterial {
* @param handle
*/
modifyBuiltinComponentAction(actionName: string, handle: (action: IPublicTypeComponentAction) => void) {
- modifyBuiltinComponentAction(actionName, handle);
+ this[designerSymbol].componentActions.modifyBuiltinComponentAction(actionName, handle);
}
/**
diff --git a/packages/shell/src/api/project.ts b/packages/shell/src/api/project.ts
index 9cae7d72e..e97a18334 100644
--- a/packages/shell/src/api/project.ts
+++ b/packages/shell/src/api/project.ts
@@ -18,14 +18,12 @@ import {
import { DocumentModel } from '../model/document-model';
import { SimulatorHost } from './simulator-host';
-import { editorSymbol, projectSymbol, simulatorHostSymbol, simulatorRendererSymbol, documentSymbol } from '../symbols';
+import { editorSymbol, projectSymbol, simulatorHostSymbol, documentSymbol } from '../symbols';
const innerProjectSymbol = Symbol('innerProject');
export class Project implements IPublicApiProject {
- private readonly [editorSymbol]: IPublicModelEditor;
private readonly [innerProjectSymbol]: InnerProject;
private [simulatorHostSymbol]: BuiltinSimulatorHost;
- private [simulatorRendererSymbol]: any;
get [projectSymbol](): InnerProject {
if (this.workspaceMode) {
return this[innerProjectSymbol];
@@ -38,9 +36,12 @@ export class Project implements IPublicApiProject {
return this[innerProjectSymbol];
}
+ get [editorSymbol](): IPublicModelEditor {
+ return this[projectSymbol]?.designer.editor;
+ }
+
constructor(project: InnerProject, public workspaceMode: boolean = false) {
this[innerProjectSymbol] = project;
- this[editorSymbol] = project?.designer.editor;
}
static create(project: InnerProject) {
@@ -201,13 +202,9 @@ export class Project implements IPublicApiProject {
* 当前 project 的渲染器 ready 事件
*/
onSimulatorRendererReady(fn: () => void): IPublicTypeDisposable {
- const offFn = this[projectSymbol].onRendererReady((renderer: any) => {
- this[simulatorRendererSymbol] = renderer;
+ const offFn = this[projectSymbol].onRendererReady(() => {
fn();
});
- if (this[simulatorRendererSymbol]) {
- fn();
- }
return offFn;
}
diff --git a/packages/shell/src/api/workspace.ts b/packages/shell/src/api/workspace.ts
index 28c143030..3440871c8 100644
--- a/packages/shell/src/api/workspace.ts
+++ b/packages/shell/src/api/workspace.ts
@@ -1,5 +1,6 @@
import { IPublicApiWorkspace } from '@alilc/lowcode-types';
import { Workspace as InnerWorkSpace } from '@alilc/lowcode-workspace';
+import { Plugins } from '@alilc/lowcode-shell';
import { Window } from '../model/window';
import { workspaceSymbol } from '../symbols';
@@ -21,4 +22,36 @@ export class Workspace implements IPublicApiWorkspace {
registerResourceType(resourceName: string, resourceType: 'editor', options: any): void {
this[workspaceSymbol].registerResourceType(resourceName, resourceType, options);
}
+
+ openEditorWindow(resourceName: string, title: string, viewType?: string) {
+ this[workspaceSymbol].openEditorWindow(resourceName, title, viewType);
+ }
+
+ openEditorWindowById(id: string) {
+ this[workspaceSymbol].openEditorWindowById(id);
+ }
+
+ removeEditorWindow(resourceName: string, title: string) {
+ this[workspaceSymbol].removeEditorWindow(resourceName, title);
+ }
+
+ removeEditorWindowById(id: string) {
+ this[workspaceSymbol].removeEditorWindowById(id);
+ }
+
+ get plugins() {
+ return new Plugins(this[workspaceSymbol].plugins, true);
+ }
+
+ get windows() {
+ return this[workspaceSymbol].windows.map(d => new Window(d));
+ }
+
+ onChangeWindows(fn: () => void) {
+ return this[workspaceSymbol].onChangeWindows(fn);
+ }
+
+ onChangeActiveWindow(fn: () => void) {
+ return this[workspaceSymbol].onChangeActiveWindow(fn);
+ }
}
diff --git a/packages/shell/src/model/window.ts b/packages/shell/src/model/window.ts
index b471fc864..fee783f53 100644
--- a/packages/shell/src/model/window.ts
+++ b/packages/shell/src/model/window.ts
@@ -5,6 +5,22 @@ import { EditorWindow } from '@alilc/lowcode-workspace';
export class Window implements IPublicModelWindow {
private readonly [windowSymbol]: EditorWindow;
+ get id() {
+ return this[windowSymbol].id;
+ }
+
+ get title() {
+ return this[windowSymbol].title;
+ }
+
+ get icon() {
+ return this[windowSymbol].icon;
+ }
+
+ get resourceName() {
+ return this[windowSymbol].resourceName;
+ }
+
constructor(editorWindow: EditorWindow) {
this[windowSymbol] = editorWindow;
}
diff --git a/packages/shell/src/symbols.ts b/packages/shell/src/symbols.ts
index 6e0924893..b87e1f24b 100644
--- a/packages/shell/src/symbols.ts
+++ b/packages/shell/src/symbols.ts
@@ -21,7 +21,6 @@ export const dragonSymbol = Symbol('dragon');
export const componentMetaSymbol = Symbol('componentMeta');
export const dropLocationSymbol = Symbol('dropLocation');
export const simulatorHostSymbol = Symbol('simulatorHost');
-export const simulatorRendererSymbol = Symbol('simulatorRenderer');
export const dragObjectSymbol = Symbol('dragObject');
export const locateEventSymbol = Symbol('locateEvent');
export const designerCabinSymbol = Symbol('designerCabin');
diff --git a/packages/types/src/shell/api/workspace.ts b/packages/types/src/shell/api/workspace.ts
index 52db05825..b9cd29afa 100644
--- a/packages/types/src/shell/api/workspace.ts
+++ b/packages/types/src/shell/api/workspace.ts
@@ -1,5 +1,6 @@
import { IPublicModelWindow } from '../model';
import { IPublicResourceOptions } from '../type';
+import { IPublicApiPlugins } from '@alilc/lowcode-types';
export interface IPublicApiWorkspace {
/** 是否启用 workspace 模式 */
@@ -10,4 +11,21 @@ export interface IPublicApiWorkspace {
/** 注册资源 */
registerResourceType(resourceName: string, resourceType: 'editor', options: IPublicResourceOptions): void;
+
+ /** 打开视图窗口 */
+ openEditorWindow(resourceName: string, title: string, viewType?: string): void;
+
+ /** 移除窗口 */
+ removeEditorWindow(resourceName: string, title: string): void;
+
+ plugins: IPublicApiPlugins;
+
+ /** 当前设计器的编辑窗口 */
+ windows: IPublicModelWindow[];
+
+ /** 窗口新增/删除的事件 */
+ onChangeWindows: (fn: () => void) => void;
+
+ /** active 窗口变更事件 */
+ onChangeActiveWindow: (fn: () => void) => void;
}
\ No newline at end of file
diff --git a/packages/types/src/shell/model/window.ts b/packages/types/src/shell/model/window.ts
index f0faedbcd..1502f2a3c 100644
--- a/packages/types/src/shell/model/window.ts
+++ b/packages/types/src/shell/model/window.ts
@@ -9,4 +9,13 @@ export interface IPublicModelWindow {
/** 调用当前窗口视图保存钩子 */
save(): Promise;
+
+ /** 窗口 id */
+ id: string;
+
+ /** 窗口标题 */
+ title?: string;
+
+ /** 窗口资源名字 */
+ resourceName?: string;
}
\ No newline at end of file
diff --git a/packages/types/src/shell/type/resource-options.ts b/packages/types/src/shell/type/resource-options.ts
index 94547bb57..e82db194c 100644
--- a/packages/types/src/shell/type/resource-options.ts
+++ b/packages/types/src/shell/type/resource-options.ts
@@ -1,7 +1,7 @@
export interface IPublicViewFunctions {
- /** 视图初始化 */
+ /** 视图初始化钩子 */
init?: () => Promise;
- /** 资源保存时调用视图的钩子 */
+ /** 资源保存时,会调用视图的钩子 */
save?: () => Promise;
}
@@ -20,6 +20,9 @@ export interface IPublicResourceOptions {
/** 资源描述 */
description?: string;
+ /** 资源 icon 标识 */
+ icon?: React.ReactElement;
+
/** 默认视图类型 */
defaultViewType: string;
@@ -35,4 +38,7 @@ export interface IPublicResourceOptions {
import?: (schema: any) => Promise<{
[viewName: string]: any;
}>;
+
+ /** 默认标题 */
+ defaultTitle?: string;
}
\ No newline at end of file
diff --git a/packages/types/src/shell/type/widget-config-area.ts b/packages/types/src/shell/type/widget-config-area.ts
index 7731ab3b0..41e71baa2 100644
--- a/packages/types/src/shell/type/widget-config-area.ts
+++ b/packages/types/src/shell/type/widget-config-area.ts
@@ -2,7 +2,7 @@
* 所有可能的停靠位置
*/
export type IPublicTypeWidgetConfigArea = 'leftArea' | 'left' | 'rightArea' |
- 'right' | 'topArea' | 'top' |
+ 'right' | 'topArea' | 'subTopArea' | 'top' |
'toolbar' | 'mainArea' | 'main' |
'center' | 'centerArea' | 'bottomArea' |
'bottom' | 'leftFixedArea' |
diff --git a/packages/workspace/src/base-context.ts b/packages/workspace/src/base-context.ts
index 047e5179e..8ab26e455 100644
--- a/packages/workspace/src/base-context.ts
+++ b/packages/workspace/src/base-context.ts
@@ -33,7 +33,7 @@ import {
IPublicTypePluginMeta,
} from '@alilc/lowcode-types';
import { getLogger } from '@alilc/lowcode-utils';
-import { Workspace as InnerWorkspace } from './index';
+import { Workspace as InnerWorkspace } from './workspace';
import { EditorWindow } from './editor-window/context';
export class BasicContext {
@@ -51,7 +51,7 @@ export class BasicContext {
designer: Designer;
registerInnerPlugins: () => Promise;
innerSetters: InnerSetters;
- innerSkeleton: any;
+ innerSkeleton: InnerSkeleton;
innerHotkey: InnerHotkey;
innerPlugins: LowCodePluginManager;
canvas: Canvas;
@@ -65,7 +65,7 @@ export class BasicContext {
const designer: Designer = new Designer({
editor,
viewName,
- shellModelFactory: innerWorkspace.shellModelFactory,
+ shellModelFactory: innerWorkspace?.shellModelFactory,
});
editor.set('designer' as any, designer);
@@ -132,7 +132,7 @@ export class BasicContext {
// 注册一批内置插件
this.registerInnerPlugins = async function registerPlugins() {
- await innerWorkspace.registryInnerPlugin(designer, editor, plugins);
+ await innerWorkspace?.registryInnerPlugin(designer, editor, plugins);
};
}
}
\ No newline at end of file
diff --git a/packages/workspace/src/editor-view/context.ts b/packages/workspace/src/editor-view/context.ts
index 913228674..a845d36c1 100644
--- a/packages/workspace/src/editor-view/context.ts
+++ b/packages/workspace/src/editor-view/context.ts
@@ -1,7 +1,7 @@
import { makeObservable, obx } from '@alilc/lowcode-editor-core';
import { IPublicEditorView, IPublicViewFunctions } from '@alilc/lowcode-types';
import { flow } from 'mobx';
-import { Workspace as InnerWorkspace } from '../';
+import { Workspace as InnerWorkspace } from '../workspace';
import { BasicContext } from '../base-context';
import { EditorWindow } from '../editor-window/context';
import { getWebviewPlugin } from '../inner-plugins/webview';
diff --git a/packages/workspace/src/editor-view/view.tsx b/packages/workspace/src/editor-view/view.tsx
index ca3a08fb2..77f0dffeb 100644
--- a/packages/workspace/src/editor-view/view.tsx
+++ b/packages/workspace/src/editor-view/view.tsx
@@ -1,14 +1,15 @@
-import { observer } from '@alilc/lowcode-editor-core';
+import { BuiltinLoading } from '@alilc/lowcode-designer';
+import { engineConfig, observer } from '@alilc/lowcode-editor-core';
import {
Workbench,
} from '@alilc/lowcode-editor-skeleton';
-import { Component } from 'react';
+import { PureComponent } from 'react';
import { Context } from './context';
export * from '../base-context';
@observer
-export class EditorView extends Component<{
+export class EditorView extends PureComponent<{
editorView: Context;
active: boolean;
}, any> {
@@ -17,7 +18,8 @@ export class EditorView extends Component<{
const editorView = this.props.editorView;
const skeleton = editorView.innerSkeleton;
if (!editorView.isInit) {
- return null;
+ const Loading = engineConfig.get('loadingComponent', BuiltinLoading);
+ return ;
}
return (
diff --git a/packages/workspace/src/editor-window/context.ts b/packages/workspace/src/editor-window/context.ts
index ee680b140..2c1eee719 100644
--- a/packages/workspace/src/editor-window/context.ts
+++ b/packages/workspace/src/editor-window/context.ts
@@ -1,12 +1,21 @@
+import { uniqueId } from '@alilc/lowcode-utils';
import { makeObservable, obx } from '@alilc/lowcode-editor-core';
import { Context } from '../editor-view/context';
-import { Workspace } from '..';
+import { Workspace } from '../workspace';
import { Resource } from '../resource';
export class EditorWindow {
- constructor(readonly resource: Resource, readonly workspace: Workspace) {
+ id: string = uniqueId('window');
+ icon: React.ReactElement | undefined;
+
+ constructor(readonly resource: Resource, readonly workspace: Workspace, public title: string | undefined = '') {
makeObservable(this);
this.init();
+ this.icon = resource.icon;
+ }
+
+ get resourceName(): string {
+ return this.resource.options.name;
}
async importSchema(schema: any) {
diff --git a/packages/workspace/src/editor-window/view.tsx b/packages/workspace/src/editor-window/view.tsx
index b080c3344..eb049aeed 100644
--- a/packages/workspace/src/editor-window/view.tsx
+++ b/packages/workspace/src/editor-window/view.tsx
@@ -1,28 +1,35 @@
-import { Component } from 'react';
+import { PureComponent } from 'react';
import { EditorView } from '../editor-view/view';
-import { observer } from '@alilc/lowcode-editor-core';
+import { engineConfig, observer } from '@alilc/lowcode-editor-core';
import { EditorWindow } from './context';
+import { BuiltinLoading } from '@alilc/lowcode-designer';
@observer
-export class EditorWindowView extends Component<{
+export class EditorWindowView extends PureComponent<{
editorWindow: EditorWindow;
+ active: boolean;
}, any> {
render() {
- const { resource, editorView, editorViews } = this.props.editorWindow;
+ const { active } = this.props;
+ const { editorView, editorViews } = this.props.editorWindow;
if (!editorView) {
- return null;
+ const Loading = engineConfig.get('loadingComponent', BuiltinLoading);
+ return (
+
+
+
+ );
}
+
return (
-
+
{
Array.from(editorViews.values()).map((editorView: any) => {
return (
);
})
diff --git a/packages/workspace/src/index.ts b/packages/workspace/src/index.ts
index 863f8d0b7..00b054eba 100644
--- a/packages/workspace/src/index.ts
+++ b/packages/workspace/src/index.ts
@@ -1,70 +1 @@
-import { Designer } from '@alilc/lowcode-designer';
-import { Editor } from '@alilc/lowcode-editor-core';
-import {
- Skeleton as InnerSkeleton,
-} from '@alilc/lowcode-editor-skeleton';
-import { Plugins } from '@alilc/lowcode-shell';
-import { IPublicResourceOptions } from '@alilc/lowcode-types';
-import { EditorWindow } from './editor-window/context';
-import { Resource } from './resource';
-
-export { Resource } from './resource';
-export * from './editor-window/context';
-export * from './layouts/workbench';
-
-export class Workspace {
- readonly editor = new Editor();
- readonly skeleton = new InnerSkeleton(this.editor);
-
- constructor(
- readonly registryInnerPlugin: (designer: Designer, editor: Editor, plugins: Plugins) => Promise
,
- readonly shellModelFactory: any,
- ) {
- if (this.defaultResource) {
- this.window = new EditorWindow(this.defaultResource, this);
- }
- }
-
- private _isActive = false;
-
- get isActive() {
- return this._isActive;
- }
-
- setActive(value: boolean) {
- this._isActive = value;
- }
-
- editorWindows: [];
-
- window: EditorWindow;
-
- private resources: Map = new Map();
-
- registerResourceType(resourceName: string, resourceType: 'editor' | 'webview', options: IPublicResourceOptions): void {
- if (resourceType === 'editor') {
- const resource = new Resource(options);
- this.resources.set(resourceName, resource);
-
- if (!this.window) {
- this.window = new EditorWindow(this.defaultResource, this);
- }
- }
- }
-
- get defaultResource() {
- if (this.resources.size === 1) {
- return this.resources.values().next().value;
- }
-
- return null;
- }
-
- removeResourceType(resourceName: string) {
- if (this.resources.has(resourceName)) {
- this.resources.delete(resourceName);
- }
- }
-
- openEditorWindow() {}
-}
+export { Workspace } from './workspace';
\ No newline at end of file
diff --git a/packages/workspace/src/layouts/left-area.tsx b/packages/workspace/src/layouts/left-area.tsx
index 6427499df..3057386ed 100644
--- a/packages/workspace/src/layouts/left-area.tsx
+++ b/packages/workspace/src/layouts/left-area.tsx
@@ -7,6 +7,9 @@ import { Area } from '@alilc/lowcode-editor-skeleton';
export default class LeftArea extends Component<{ area: Area }> {
render() {
const { area } = this.props;
+ if (area.isEmpty()) {
+ return null;
+ }
return (
{
+ render() {
+ const { area, itemClassName } = this.props;
+
+ if (area.isEmpty()) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+ }
+}
+
+@observer
+class Contents extends Component<{ area: Area; itemClassName?: string }> {
+ render() {
+ const { area, itemClassName } = this.props;
+ const left: any[] = [];
+ const center: any[] = [];
+ const right: any[] = [];
+ area.container.items.slice().sort((a, b) => {
+ const index1 = a.config?.index || 0;
+ const index2 = b.config?.index || 0;
+ return index1 === index2 ? 0 : (index1 > index2 ? 1 : -1);
+ }).forEach(item => {
+ const content = (
+
+ {item.content}
+
+ );
+ if (item.align === 'center') {
+ center.push(content);
+ } else if (item.align === 'left') {
+ left.push(content);
+ } else {
+ right.push(content);
+ }
+ });
+ let children = [];
+ if (left && left.length) {
+ children.push(
{left}
);
+ }
+ if (center && center.length) {
+ children.push(
{center}
);
+ }
+ if (right && right.length) {
+ children.push(
{right}
);
+ }
+ return (
+
+ {children}
+
+ );
+ }
+}
diff --git a/packages/workspace/src/layouts/top-area.tsx b/packages/workspace/src/layouts/top-area.tsx
index c6301470d..457e928d2 100644
--- a/packages/workspace/src/layouts/top-area.tsx
+++ b/packages/workspace/src/layouts/top-area.tsx
@@ -8,7 +8,7 @@ export default class TopArea extends Component<{ area: Area; itemClassName?: str
render() {
const { area, itemClassName } = this.props;
- if (!area?.container?.items?.length) {
+ if (area.isEmpty()) {
return null;
}
diff --git a/packages/workspace/src/layouts/workbench.less b/packages/workspace/src/layouts/workbench.less
index 0217b8496..0639a1fa1 100644
--- a/packages/workspace/src/layouts/workbench.less
+++ b/packages/workspace/src/layouts/workbench.less
@@ -138,7 +138,7 @@ body {
display: flex;
flex-direction: column;
background-color: #edeff3;
- .lc-top-area {
+ .lc-top-area, .lc-sub-top-area {
height: var(--top-area-height);
background-color: var(--color-pane-background);
width: 100%;
@@ -150,18 +150,18 @@ body {
display: flex;
}
- .lc-top-area-left {
+ .lc-top-area-left, .lc-sub-top-area-left {
display: flex;
align-items: center;
}
- .lc-top-area-center {
+ .lc-top-area-center, .lc-sub-top-area-center {
flex: 1;
display: flex;
justify-content: center;
margin: 0 8px;
}
- .lc-top-area-right {
+ .lc-top-area-right, .lc-sub-top-area-right {
display: flex;
align-items: center;
> * {
@@ -335,6 +335,7 @@ body {
display: flex;
flex-direction: column;
z-index: 10;
+ position: relative;
.lc-toolbar {
display: flex;
height: var(--toolbar-height);
@@ -359,6 +360,12 @@ body {
}
}
}
+
+ .lc-workspace-workbench-window {
+ position: relative;
+ height: 100%;
+ }
+
.lc-right-area {
height: 100%;
width: var(--right-area-width);
diff --git a/packages/workspace/src/layouts/workbench.tsx b/packages/workspace/src/layouts/workbench.tsx
index c7b2762e4..66ddb7de0 100644
--- a/packages/workspace/src/layouts/workbench.tsx
+++ b/packages/workspace/src/layouts/workbench.tsx
@@ -11,10 +11,17 @@ import BottomArea from './bottom-area';
import './workbench.less';
import { SkeletonContext } from '../skeleton-context';
import { EditorConfig, PluginClassSet } from '@alilc/lowcode-types';
-import { Workspace } from '..';
+import { Workspace } from '../workspace';
+import SubTopArea from './sub-top-area';
@observer
-export class Workbench extends Component<{ workspace: Workspace; config?: EditorConfig; components?: PluginClassSet; className?: string; topAreaItemClassName?: string }> {
+export class Workbench extends Component<{
+ workspace: Workspace;
+ config?: EditorConfig;
+ components?: PluginClassSet;
+ className?: string;
+ topAreaItemClassName?: string;
+}> {
constructor(props: any) {
super(props);
const { config, components, workspace } = this.props;
@@ -34,8 +41,20 @@ export class Workbench extends Component<{ workspace: Workspace; config?: Editor
- {/*
*/}
-
+ <>
+
+
+ {
+ workspace.windows.map(d => (
+
+ ))
+ }
+
+ >
diff --git a/packages/workspace/src/resource.ts b/packages/workspace/src/resource.ts
index a1147713e..f881caf0f 100644
--- a/packages/workspace/src/resource.ts
+++ b/packages/workspace/src/resource.ts
@@ -19,6 +19,10 @@ export class Resource {
this.options.init(ctx);
}
+ get icon() {
+ return this.options.icon;
+ }
+
async import(schema: any) {
return await this.options.import?.(schema);
}
@@ -38,4 +42,8 @@ export class Resource {
async save(value: any) {
return await this.options.save?.(value);
}
+
+ get title() {
+ return this.options.defaultTitle;
+ }
}
\ No newline at end of file
diff --git a/packages/workspace/src/workspace.ts b/packages/workspace/src/workspace.ts
new file mode 100644
index 000000000..8b0fe29c4
--- /dev/null
+++ b/packages/workspace/src/workspace.ts
@@ -0,0 +1,169 @@
+import { Designer } from '@alilc/lowcode-designer';
+import { createModuleEventBus, Editor, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core';
+import { Plugins } from '@alilc/lowcode-shell';
+import { IPublicApiWorkspace, IPublicResourceOptions } from '@alilc/lowcode-types';
+import { BasicContext } from './base-context';
+import { EditorWindow } from './editor-window/context';
+import { Resource } from './resource';
+
+export { Resource } from './resource';
+export * from './editor-window/context';
+export * from './layouts/workbench';
+
+enum event {
+ ChangeWindow = 'change_window',
+
+ ChangeActiveWindow = 'change_active_window',
+}
+
+export class Workspace implements IPublicApiWorkspace {
+ private context: BasicContext;
+
+ private emitter: IEventBus = createModuleEventBus('workspace');
+
+ get skeleton() {
+ return this.context.innerSkeleton;
+ }
+
+ get plugins() {
+ return this.context.innerPlugins;
+ }
+
+ constructor(
+ readonly registryInnerPlugin: (designer: Designer, editor: Editor, plugins: Plugins) => Promise
,
+ readonly shellModelFactory: any,
+ ) {
+ this.init();
+ makeObservable(this);
+ }
+
+ init() {
+ this.initWindow();
+ this.context = new BasicContext(this, '');
+ }
+
+ initWindow() {
+ if (!this.defaultResource) {
+ return;
+ }
+ const title = this.defaultResource.title;
+ this.window = new EditorWindow(this.defaultResource, this, title);
+ this.editorWindowMap.set(this.window.id, this.window);
+ this.windows.push(this.window);
+ this.emitChangeWindow();
+ this.emitChangeActiveWindow();
+ }
+
+
+ private _isActive = false;
+
+ get isActive() {
+ return this._isActive;
+ }
+
+ setActive(value: boolean) {
+ this._isActive = value;
+ }
+
+ windows: EditorWindow[] = [];
+
+ editorWindowMap: Map = new Map();
+
+ @obx.ref window: EditorWindow;
+
+ private resources: Map = new Map();
+
+ async registerResourceType(resourceName: string, resourceType: 'editor' | 'webview', options: IPublicResourceOptions): Promise {
+ if (resourceType === 'editor') {
+ const resource = new Resource(options);
+ this.resources.set(resourceName, resource);
+
+ if (!this.window && this.defaultResource) {
+ this.initWindow();
+ }
+ }
+ }
+
+ get defaultResource(): Resource | null {
+ if (this.resources.size > 1) {
+ return this.resources.values().next().value;
+ }
+
+ return null;
+ }
+
+ removeResourceType(resourceName: string) {
+ if (this.resources.has(resourceName)) {
+ this.resources.delete(resourceName);
+ }
+ }
+
+ removeEditorWindowById(id: string) {
+ const index = this.windows.findIndex(d => (d.id === id));
+ this.remove(index);
+ }
+
+ private remove(index: number) {
+ const window = this.windows[index];
+ this.windows = this.windows.splice(index - 1, 1);
+ if (this.window === window) {
+ this.window = this.windows[index] || this.windows[index + 1] || this.windows[index - 1];
+ this.emitChangeActiveWindow();
+ }
+ this.emitChangeWindow();
+ }
+
+ removeEditorWindow(resourceName: string, title: string) {
+ const index = this.windows.findIndex(d => (d.resourceName === resourceName && d.title));
+ this.remove(index);
+ }
+
+ openEditorWindowById(id: string) {
+ const window = this.editorWindowMap.get(id);
+ if (window) {
+ this.window = window;
+ this.emitChangeActiveWindow();
+ }
+ }
+
+ openEditorWindow(resourceName: string, title: string, viewType?: string) {
+ const resource = this.resources.get(resourceName);
+ if (!resource) {
+ console.error(`${resourceName} is not available`);
+ return;
+ }
+ const filterWindows = this.windows.filter(d => (d.resourceName === resourceName && d.title == title));
+ if (filterWindows && filterWindows.length) {
+ this.window = filterWindows[0];
+ this.emitChangeActiveWindow();
+ return;
+ }
+ this.window = new EditorWindow(resource, this, title);
+ this.windows.push(this.window);
+ this.editorWindowMap.set(this.window.id, this.window);
+ this.emitChangeWindow();
+ this.emitChangeActiveWindow();
+ }
+
+ onChangeWindows(fn: () => void) {
+ this.emitter.on(event.ChangeWindow, fn);
+ return () => {
+ this.emitter.removeListener(event.ChangeWindow, fn);
+ };
+ }
+
+ emitChangeWindow() {
+ this.emitter.emit(event.ChangeWindow);
+ }
+
+ emitChangeActiveWindow() {
+ this.emitter.emit(event.ChangeActiveWindow);
+ }
+
+ onChangeActiveWindow(fn: () => void) {
+ this.emitter.on(event.ChangeActiveWindow, fn);
+ return () => {
+ this.emitter.removeListener(event.ChangeActiveWindow, fn);
+ };
+ }
+}