, 'node'> {
- /**
- * 获取选中的节点实例
- * @returns
- */
- getNodes(): INode[];
-
- /**
- * 获取顶层选区节点,场景:拖拽时,建立蒙层,只蒙在最上层
- */
- getTopNodes(includeRoot?: boolean): INode[];
}
export class Selection implements ISelection {
@@ -115,7 +105,7 @@ export class Selection implements ISelection {
/**
* 选区是否包含节点
*/
- containsNode(node: Node, excludeRoot = false) {
+ containsNode(node: INode, excludeRoot = false) {
for (const id of this._selected) {
const parent = this.doc.getNode(id);
if (excludeRoot && parent?.contains(this.doc.focusNode)) {
@@ -131,8 +121,8 @@ export class Selection implements ISelection {
/**
* 获取选中的节点
*/
- getNodes(): Node[] {
- const nodes = [];
+ getNodes(): INode[] {
+ const nodes: INode[] = [];
for (const id of this._selected) {
const node = this.doc.getNode(id);
if (node) {
diff --git a/packages/designer/src/plugin/plugin-context.ts b/packages/designer/src/plugin/plugin-context.ts
index c1a7ddee8..f43134c75 100644
--- a/packages/designer/src/plugin/plugin-context.ts
+++ b/packages/designer/src/plugin/plugin-context.ts
@@ -16,6 +16,7 @@ import {
IPublicApiPlugins,
IPublicTypePluginDeclaration,
IPublicApiCanvas,
+ IPublicApiWorkspace,
} from '@alilc/lowcode-types';
import {
IPluginContextOptions,
@@ -24,8 +25,8 @@ import {
} from './plugin-types';
import { isValidPreferenceKey } from './plugin-utils';
-
-export default class PluginContext implements IPublicModelPluginContext, ILowCodePluginContextPrivate {
+export default class PluginContext implements
+ IPublicModelPluginContext, ILowCodePluginContextPrivate {
hotkey: IPublicApiHotkey;
project: IPublicApiProject;
skeleton: IPublicApiSkeleton;
@@ -39,6 +40,7 @@ export default class PluginContext implements IPublicModelPluginContext, ILowCod
preference: IPluginPreferenceMananger;
pluginEvent: IPublicApiEvent;
canvas: IPublicApiCanvas;
+ workspace: IPublicApiWorkspace;
constructor(
options: IPluginContextOptions,
diff --git a/packages/designer/src/project/project.ts b/packages/designer/src/project/project.ts
index 42ba0b4fc..6516d67aa 100644
--- a/packages/designer/src/project/project.ts
+++ b/packages/designer/src/project/project.ts
@@ -12,13 +12,31 @@ import {
import { isLowCodeComponentType, isProCodeComponentType } from '@alilc/lowcode-utils';
import { ISimulatorHost } from '../simulator';
-export interface IProject extends Omit< IPublicApiProject, 'simulatorHost' | 'importSchema' | 'exportSchema' | 'openDocument' | 'getDocumentById' | 'getCurrentDocument' | 'addPropsTransducer' | 'onRemoveDocument' | 'onChangeDocument' | 'onSimulatorHostReady' | 'onSimulatorRendererReady' | 'setI18n' > {
+export interface IProject extends Omit< IPublicApiProject,
+ 'simulatorHost' |
+ 'importSchema' |
+ 'exportSchema' |
+ 'openDocument' |
+ 'getDocumentById' |
+ 'getCurrentDocument' |
+ 'addPropsTransducer' |
+ 'onRemoveDocument' |
+ 'onChangeDocument' |
+ 'onSimulatorHostReady' |
+ 'onSimulatorRendererReady' |
+ 'setI18n' |
+ 'currentDocument' |
+ 'selection' |
+ 'documents' |
+ 'createDocument' |
+ 'getDocumentByFileName'
+> {
get designer(): IDesigner;
get simulator(): ISimulatorHost | null;
- get currentDocument(): IDocumentModel | null;
+ get currentDocument(): IDocumentModel | null | undefined;
get documents(): IDocumentModel[];
@@ -60,6 +78,8 @@ export interface IProject extends Omit< IPublicApiProject, 'simulatorHost' | 'im
// eslint-disable-next-line @typescript-eslint/no-unused-vars
value: any,
): void;
+
+ checkExclusive(activeDoc: DocumentModel): void;
}
export class Project implements IProject {
@@ -85,7 +105,7 @@ export class Project implements IProject {
return this._simulator || null;
}
- @computed get currentDocument(): IDocumentModel | null {
+ @computed get currentDocument(): IDocumentModel | null | undefined {
return this.documents.find((doc) => doc.active);
}
diff --git a/packages/designer/src/simulator.ts b/packages/designer/src/simulator.ts
index 5a2fb8efc..32b9233c9 100644
--- a/packages/designer/src/simulator.ts
+++ b/packages/designer/src/simulator.ts
@@ -2,7 +2,7 @@ import { ComponentType } from 'react';
import { IPublicTypeComponentMetadata, IPublicTypeNodeSchema, IPublicTypeScrollable, IPublicTypeComponentInstance, IPublicModelSensor, IPublicTypeNodeInstance } from '@alilc/lowcode-types';
import { Point, ScrollTarget, ILocateEvent } from './designer';
import { BuiltinSimulatorRenderer } from './builtin-simulator/renderer';
-import { Node, INode } from './document';
+import { INode } from './document';
export type AutoFit = '100%';
// eslint-disable-next-line no-redeclare
@@ -132,7 +132,7 @@ export interface ISimulatorHost extends IPublicModelSensor {
/**
* 滚动视口到节点
*/
- scrollToNode(node: Node, detail?: any): void;
+ scrollToNode(node: INode, detail?: any): void;
/**
* 描述组件
@@ -147,7 +147,7 @@ export interface ISimulatorHost
extends IPublicModelSensor {
/**
* 根据节点获取节点的组件实例
*/
- getComponentInstances(node: Node): IPublicTypeComponentInstance[] | null;
+ getComponentInstances(node: INode): IPublicTypeComponentInstance[] | null;
/**
* 根据 schema 创建组件类
@@ -157,11 +157,11 @@ export interface ISimulatorHost
extends IPublicModelSensor {
/**
* 根据节点获取节点的组件运行上下文
*/
- getComponentContext(node: Node): object | null;
+ getComponentContext(node: INode): object | null;
getClosestNodeInstance(from: IPublicTypeComponentInstance, specId?: string): IPublicTypeNodeInstance | null;
- computeRect(node: Node): DOMRect | null;
+ computeRect(node: INode): DOMRect | null;
computeComponentInstanceRect(instance: IPublicTypeComponentInstance, selector?: string): DOMRect | null;
@@ -189,6 +189,6 @@ export function isSimulatorHost(obj: any): obj is ISimulatorHost {
export type Component = ComponentType | object;
export interface INodeSelector {
- node: Node;
+ node: INode;
instance?: IPublicTypeComponentInstance;
}
diff --git a/packages/designer/tests/document/document-model/document-model.test.ts b/packages/designer/tests/document/document-model/document-model.test.ts
index 753c840a5..b47200cba 100644
--- a/packages/designer/tests/document/document-model/document-model.test.ts
+++ b/packages/designer/tests/document/document-model/document-model.test.ts
@@ -23,7 +23,7 @@ describe('document-model 测试', () => {
it('empty schema', () => {
const doc = new DocumentModel(project);
- expect(doc.rootNode.id).toBe('root');
+ expect(doc.rootNode?.id).toBe('root');
expect(doc.currentRoot).toBe(doc.rootNode);
expect(doc.root).toBe(doc.rootNode);
expect(doc.modalNode).toBeUndefined();
diff --git a/packages/designer/tests/document/node/node.add.test.ts b/packages/designer/tests/document/node/node.add.test.ts
index bede02196..87a4222cd 100644
--- a/packages/designer/tests/document/node/node.add.test.ts
+++ b/packages/designer/tests/document/node/node.add.test.ts
@@ -1,8 +1,8 @@
import set from 'lodash/set';
import cloneDeep from 'lodash/cloneDeep';
import '../../fixtures/window';
-import { Project } from '../../../src/project/project';
-import { Node } from '../../../src/document/node/node';
+import { Project, IProject } from '../../../src/project/project';
+import { Node, INode } from '../../../src/document/node/node';
import { Designer } from '../../../src/designer/designer';
import formSchema from '../../fixtures/schema/form';
import { getIdsFromSchema, getNodeFromSchemaById } from '../../utils';
@@ -37,7 +37,7 @@ beforeAll(() => {
describe('schema 生成节点模型测试', () => {
describe('block ❌ | component ❌ | slot ❌', () => {
- let project: Project;
+ let project: IProject;
beforeEach(() => {
project = new Project(designer, {
componentsTree: [
@@ -52,12 +52,12 @@ describe('schema 生成节点模型测试', () => {
it('基本的节点模型初始化,模型导出', () => {
expect(project).toBeTruthy();
const { currentDocument } = project;
- const { nodesMap } = currentDocument;
+ const nodesMap = currentDocument?.nodesMap;
const ids = getIdsFromSchema(formSchema);
const expectedNodeCnt = ids.length;
- expect(nodesMap.size).toBe(expectedNodeCnt);
+ expect(nodesMap?.size).toBe(expectedNodeCnt);
ids.forEach(id => {
- expect(nodesMap.get(id).componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName);
+ expect(nodesMap?.get(id)?.componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName);
});
const pageNode = currentDocument?.getNode('page');
@@ -76,18 +76,18 @@ describe('schema 生成节点模型测试', () => {
it('基本的节点模型初始化,节点深度', () => {
expect(project).toBeTruthy();
const { currentDocument } = project;
- const getNode = currentDocument.getNode.bind(currentDocument);
+ const getNode = currentDocument?.getNode.bind(currentDocument);
- const pageNode = getNode('page');
- const rootHeaderNode = getNode('node_k1ow3cba');
- const rootContentNode = getNode('node_k1ow3cbb');
- const rootFooterNode = getNode('node_k1ow3cbc');
- const formNode = getNode('form');
- const cardNode = getNode('node_k1ow3cbj');
- const cardContentNode = getNode('node_k1ow3cbk');
- const columnsLayoutNode = getNode('node_k1ow3cbw');
- const columnNode = getNode('node_k1ow3cbx');
- const textFieldNode = getNode('node_k1ow3cbz');
+ const pageNode = getNode?.('page');
+ const rootHeaderNode = getNode?.('node_k1ow3cba');
+ const rootContentNode = getNode?.('node_k1ow3cbb');
+ const rootFooterNode = getNode?.('node_k1ow3cbc');
+ const formNode = getNode?.('form');
+ const cardNode = getNode?.('node_k1ow3cbj');
+ const cardContentNode = getNode?.('node_k1ow3cbk');
+ const columnsLayoutNode = getNode?.('node_k1ow3cbw');
+ const columnNode = getNode?.('node_k1ow3cbx');
+ const textFieldNode = getNode?.('node_k1ow3cbz');
expect(pageNode?.zLevel).toBe(0);
expect(rootHeaderNode?.zLevel).toBe(1);
@@ -131,7 +131,7 @@ describe('schema 生成节点模型测试', () => {
const textFieldNode = getNode('node_k1ow3cbz');
expect(pageNode?.index).toBe(-1);
- expect(pageNode?.children.toString()).toBe('[object Array]');
+ expect(pageNode?.children?.toString()).toBe('[object Array]');
expect(pageNode?.children?.get(1)).toBe(rootContentNode);
expect(pageNode?.getChildren()?.get(1)).toBe(rootContentNode);
expect(pageNode?.getNode()).toBe(pageNode);
@@ -162,20 +162,20 @@ describe('schema 生成节点模型测试', () => {
it('基本的节点模型初始化,节点新建、删除等事件', () => {
expect(project).toBeTruthy();
const { currentDocument } = project;
- const getNode = currentDocument.getNode.bind(currentDocument);
- const createNode = currentDocument.createNode.bind(currentDocument);
+ const getNode = currentDocument?.getNode.bind(currentDocument);
+ const createNode = currentDocument?.createNode.bind(currentDocument);
- const pageNode = getNode('page');
+ const pageNode = getNode?.('page');
const nodeCreateHandler = jest.fn();
const offCreate = currentDocument?.onNodeCreate(nodeCreateHandler);
- const node = createNode({
+ const node = createNode?.({
componentName: 'TextInput',
props: {
propA: 'haha',
},
});
- currentDocument?.insertNode(pageNode, node);
+ pageNode && node && currentDocument?.insertNode(pageNode, node);
expect(nodeCreateHandler).toHaveBeenCalledTimes(1);
expect(nodeCreateHandler.mock.calls[0][0]).toBe(node);
@@ -184,7 +184,7 @@ describe('schema 生成节点模型测试', () => {
const nodeDestroyHandler = jest.fn();
const offDestroy = currentDocument?.onNodeDestroy(nodeDestroyHandler);
- node.remove();
+ node?.remove();
expect(nodeDestroyHandler).toHaveBeenCalledTimes(1);
expect(nodeDestroyHandler.mock.calls[0][0]).toBe(node);
expect(nodeDestroyHandler.mock.calls[0][0].componentName).toBe('TextInput');
@@ -290,9 +290,9 @@ describe('schema 生成节点模型测试', () => {
expect(project).toBeTruthy();
const ids = getIdsFromSchema(formSchema);
const { currentDocument } = project;
- const { nodesMap } = currentDocument;
- const formNode = nodesMap.get('form');
- currentDocument?.insertNode(formNode, {
+ const nodesMap = currentDocument?.nodesMap;
+ const formNode = nodesMap?.get('form');
+ formNode && currentDocument?.insertNode(formNode, {
componentName: 'TextInput',
id: 'nodeschema-id1',
props: {
@@ -300,11 +300,11 @@ describe('schema 生成节点模型测试', () => {
propB: 3,
},
}, 0);
- expect(nodesMap.size).toBe(ids.length + 1);
- expect(formNode.children.length).toBe(4);
- const insertedNode = formNode.children.get(0);
- expect(insertedNode.componentName).toBe('TextInput');
- expect(insertedNode.propsData).toEqual({
+ expect(nodesMap?.size).toBe(ids.length + 1);
+ expect(formNode?.children?.length).toBe(4);
+ const insertedNode = formNode?.children?.get(0);
+ expect(insertedNode?.componentName).toBe('TextInput');
+ expect(insertedNode?.propsData).toEqual({
propA: 'haha',
propB: 3,
});
@@ -316,9 +316,9 @@ describe('schema 生成节点模型测试', () => {
expect(project).toBeTruthy();
const ids = getIdsFromSchema(formSchema);
const { currentDocument } = project;
- const { nodesMap } = currentDocument;
- const formNode = nodesMap.get('form');
- currentDocument?.insertNode(formNode, {
+ const nodesMap = currentDocument?.nodesMap;
+ const formNode = nodesMap?.get('form');
+ formNode && currentDocument?.insertNode(formNode, {
componentName: 'TextInput',
id: 'nodeschema-id1',
props: {
@@ -326,11 +326,11 @@ describe('schema 生成节点模型测试', () => {
propB: 3,
},
}, 1);
- expect(nodesMap.size).toBe(ids.length + 1);
- expect(formNode.children.length).toBe(4);
- const insertedNode = formNode.children.get(1);
- expect(insertedNode.componentName).toBe('TextInput');
- expect(insertedNode.propsData).toEqual({
+ expect(nodesMap?.size).toBe(ids.length + 1);
+ expect(formNode?.children?.length).toBe(4);
+ const insertedNode = formNode?.children?.get(1);
+ expect(insertedNode?.componentName).toBe('TextInput');
+ expect(insertedNode?.propsData).toEqual({
propA: 'haha',
propB: 3,
});
@@ -342,8 +342,8 @@ describe('schema 生成节点模型测试', () => {
expect(project).toBeTruthy();
const ids = getIdsFromSchema(formSchema);
const { currentDocument } = project;
- const { nodesMap } = currentDocument;
- const formNode = nodesMap.get('form') as Node;
+ const nodesMap = currentDocument?.nodesMap;
+ const formNode = nodesMap?.get('form') as INode;
currentDocument?.insertNode(formNode, {
componentName: 'ParentNode',
props: {
@@ -367,8 +367,8 @@ describe('schema 生成节点模型测试', () => {
},
],
});
- expect(nodesMap.size).toBe(ids.length + 3);
- expect(formNode.children.length).toBe(4);
+ expect(nodesMap?.size).toBe(ids.length + 3);
+ expect(formNode.children?.length).toBe(4);
expect(formNode.children?.get(3)?.componentName).toBe('ParentNode');
expect(formNode.children?.get(3)?.children?.get(0)?.componentName).toBe('SubNode');
expect(formNode.children?.get(3)?.children?.get(1)?.componentName).toBe('SubNode2');
@@ -378,9 +378,9 @@ describe('schema 生成节点模型测试', () => {
expect(project).toBeTruthy();
const ids = getIdsFromSchema(formSchema);
const { currentDocument } = project;
- const { nodesMap } = currentDocument;
- const formNode = nodesMap.get('form');
- currentDocument?.insertNode(formNode, {
+ const nodesMap = currentDocument?.nodesMap;
+ const formNode = nodesMap?.get('form');
+ formNode && currentDocument?.insertNode(formNode, {
componentName: 'TextInput',
id: 'nodeschema-id1',
props: {
@@ -388,17 +388,17 @@ describe('schema 生成节点模型测试', () => {
propB: 3,
},
});
- expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
- expect(nodesMap.size).toBe(ids.length + 1);
+ expect(nodesMap?.get('nodeschema-id1')?.componentName).toBe('TextInput');
+ expect(nodesMap?.size).toBe(ids.length + 1);
});
it.skip('场景一:插入 NodeSchema,id 与现有 schema 里的 id 重复,但关闭了 id 检测器', () => {
expect(project).toBeTruthy();
const ids = getIdsFromSchema(formSchema);
const { currentDocument } = project;
- const { nodesMap } = currentDocument;
- const formNode = nodesMap.get('form');
- currentDocument?.insertNode(formNode, {
+ const nodesMap = currentDocument?.nodesMap;
+ const formNode = nodesMap?.get('form');
+ formNode && currentDocument?.insertNode(formNode, {
componentName: 'TextInput',
id: 'nodeschema-id1',
props: {
@@ -406,16 +406,16 @@ describe('schema 生成节点模型测试', () => {
propB: 3,
},
});
- expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
- expect(nodesMap.size).toBe(ids.length + 1);
+ expect(nodesMap?.get('nodeschema-id1')?.componentName).toBe('TextInput');
+ expect(nodesMap?.size).toBe(ids.length + 1);
});
it('场景二:插入 Node 实例', () => {
expect(project).toBeTruthy();
const ids = getIdsFromSchema(formSchema);
const { currentDocument } = project;
- const { nodesMap } = currentDocument;
- const formNode = nodesMap.get('form');
+ const nodesMap = currentDocument?.nodesMap;
+ const formNode = nodesMap?.get('form');
const inputNode = currentDocument?.createNode({
componentName: 'TextInput',
id: 'nodeschema-id2',
@@ -424,22 +424,22 @@ describe('schema 生成节点模型测试', () => {
propB: 3,
},
});
- currentDocument?.insertNode(formNode, inputNode);
- expect(formNode.children?.get(3)?.componentName).toBe('TextInput');
- expect(nodesMap.size).toBe(ids.length + 1);
+ formNode && currentDocument?.insertNode(formNode, inputNode);
+ expect(formNode?.children?.get(3)?.componentName).toBe('TextInput');
+ expect(nodesMap?.size).toBe(ids.length + 1);
});
it('场景三:插入 JSExpression', () => {
expect(project).toBeTruthy();
const ids = getIdsFromSchema(formSchema);
const { currentDocument } = project;
- const { nodesMap } = currentDocument;
- const formNode = nodesMap.get('form') as Node;
+ const nodesMap = currentDocument?.nodesMap;
+ const formNode = nodesMap?.get('form') as Node;
currentDocument?.insertNode(formNode, {
type: 'JSExpression',
value: 'just a expression',
});
- expect(nodesMap.size).toBe(ids.length + 1);
+ expect(nodesMap?.size).toBe(ids.length + 1);
expect(formNode.children?.get(3)?.componentName).toBe('Leaf');
// expect(formNode.children?.get(3)?.children).toEqual({
// type: 'JSExpression',
@@ -450,10 +450,10 @@ describe('schema 生成节点模型测试', () => {
expect(project).toBeTruthy();
const ids = getIdsFromSchema(formSchema);
const { currentDocument } = project;
- const { nodesMap } = currentDocument;
- const formNode = nodesMap.get('form') as Node;
+ const nodesMap = currentDocument?.nodesMap;
+ const formNode = nodesMap?.get('form') as Node;
currentDocument?.insertNode(formNode, 'just a string');
- expect(nodesMap.size).toBe(ids.length + 1);
+ expect(nodesMap?.size).toBe(ids.length + 1);
expect(formNode.children?.get(3)?.componentName).toBe('Leaf');
// expect(formNode.children?.get(3)?.children).toBe('just a string');
});
@@ -473,8 +473,8 @@ describe('schema 生成节点模型测试', () => {
expect(project).toBeTruthy();
const ids = getIdsFromSchema(formSchema);
const { currentDocument } = project;
- const { nodesMap } = currentDocument;
- const formNode = nodesMap.get('form') as Node;
+ const nodesMap = currentDocument?.nodesMap;
+ const formNode = nodesMap?.get('form') as Node;
const formNode2 = currentDocument?.getNode('form');
expect(formNode).toEqual(formNode2);
currentDocument?.insertNodes(formNode, [
@@ -493,17 +493,17 @@ describe('schema 生成节点模型测试', () => {
},
},
], 1);
- expect(nodesMap.size).toBe(ids.length + 2);
+ expect(nodesMap?.size).toBe(ids.length + 2);
expect(formNode.children?.length).toBe(5);
- const insertedNode1 = formNode.children.get(1);
- const insertedNode2 = formNode.children.get(2);
- expect(insertedNode1.componentName).toBe('TextInput');
- expect(insertedNode1.propsData).toEqual({
+ const insertedNode1 = formNode.children?.get(1);
+ const insertedNode2 = formNode.children?.get(2);
+ expect(insertedNode1?.componentName).toBe('TextInput');
+ expect(insertedNode1?.propsData).toEqual({
propA: 'haha2',
propB: 3,
});
- expect(insertedNode2.componentName).toBe('TextInput2');
- expect(insertedNode2.propsData).toEqual({
+ expect(insertedNode2?.componentName).toBe('TextInput2');
+ expect(insertedNode2?.propsData).toEqual({
propA: 'haha',
propB: 3,
});
@@ -513,8 +513,8 @@ describe('schema 生成节点模型测试', () => {
expect(project).toBeTruthy();
const ids = getIdsFromSchema(formSchema);
const { currentDocument } = project;
- const { nodesMap } = currentDocument;
- const formNode = nodesMap.get('form') as Node;
+ const nodesMap = currentDocument?.nodesMap;
+ const formNode = nodesMap?.get('form') as INode;
const formNode2 = currentDocument?.getNode('form');
expect(formNode).toEqual(formNode2);
const createdNode1 = currentDocument?.createNode({
@@ -532,17 +532,17 @@ describe('schema 生成节点模型测试', () => {
},
});
currentDocument?.insertNodes(formNode, [createdNode1, createdNode2], 1);
- expect(nodesMap.size).toBe(ids.length + 2);
+ expect(nodesMap?.size).toBe(ids.length + 2);
expect(formNode.children?.length).toBe(5);
- const insertedNode1 = formNode.children.get(1);
- const insertedNode2 = formNode.children.get(2);
- expect(insertedNode1.componentName).toBe('TextInput');
- expect(insertedNode1.propsData).toEqual({
+ const insertedNode1 = formNode.children?.get(1);
+ const insertedNode2 = formNode.children?.get(2);
+ expect(insertedNode1?.componentName).toBe('TextInput');
+ expect(insertedNode1?.propsData).toEqual({
propA: 'haha2',
propB: 3,
});
- expect(insertedNode2.componentName).toBe('TextInput2');
- expect(insertedNode2.propsData).toEqual({
+ expect(insertedNode2?.componentName).toBe('TextInput2');
+ expect(insertedNode2?.propsData).toEqual({
propA: 'haha',
propB: 3,
});
@@ -561,13 +561,13 @@ describe('schema 生成节点模型测试', () => {
project.open();
expect(project).toBeTruthy();
const { currentDocument } = project;
- const { nodesMap } = currentDocument;
+ const nodesMap = currentDocument?.nodesMap;
const ids = getIdsFromSchema(formSchema);
// 目前每个 slot 会新增(1 + children.length)个节点
const expectedNodeCnt = ids.length + 2;
- expect(nodesMap.size).toBe(expectedNodeCnt);
+ expect(nodesMap?.size).toBe(expectedNodeCnt);
// PageHeader
- expect(nodesMap.get('node_k1ow3cbd').slots).toHaveLength(1);
+ expect(nodesMap?.get('node_k1ow3cbd')?.slots).toHaveLength(1);
});
});
});
diff --git a/packages/designer/tests/document/node/props/prop.test.ts b/packages/designer/tests/document/node/props/prop.test.ts
index 606c7fc08..932733b1a 100644
--- a/packages/designer/tests/document/node/props/prop.test.ts
+++ b/packages/designer/tests/document/node/props/prop.test.ts
@@ -499,6 +499,59 @@ describe('Prop 类测试', () => {
expect(slotProp.purged).toBeTruthy();
slotProp.dispose();
});
+
+ describe('slotNode-value / setAsSlot', () => {
+ const editor = new Editor();
+ const designer = new Designer({ editor, shellModelFactory });
+ const doc = new DocumentModel(designer.project, {
+ componentName: 'Page',
+ children: [
+ {
+ id: 'div',
+ componentName: 'Div',
+ },
+ ],
+ });
+ const div = doc.getNode('div');
+
+ const slotProp = new Prop(div?.getProps(), {
+ type: 'JSSlot',
+ value: {
+ componentName: 'Slot',
+ id: 'node_oclei5rv2e2',
+ props: {
+ slotName: "content",
+ slotTitle: "主内容"
+ },
+ children: [
+ {
+ componentName: 'Button',
+ }
+ ]
+ },
+ });
+
+ expect(slotProp.slotNode?.componentName).toBe('Slot');
+
+ expect(slotProp.slotNode?.title).toBe('主内容');
+ expect(slotProp.slotNode?.getExtraProp('name')?.getValue()).toBe('content');
+ expect(slotProp.slotNode?.export()?.id).toBe('node_oclei5rv2e2');
+
+ slotProp.export();
+
+ // Save
+ expect(slotProp.export()?.value[0].componentName).toBe('Button');
+ expect(slotProp.export()?.title).toBe('主内容');
+ expect(slotProp.export()?.name).toBe('content');
+
+ // Render
+ expect(slotProp.export(IPublicEnumTransformStage.Render)?.value.children[0].componentName).toBe('Button');
+ expect(slotProp.export(IPublicEnumTransformStage.Render)?.value.componentName).toBe('Slot');
+
+ slotProp.purge();
+ expect(slotProp.purged).toBeTruthy();
+ slotProp.dispose();
+ });
});
describe('其他导出函数', () => {
diff --git a/packages/editor-core/package.json b/packages/editor-core/package.json
index a3e799761..87022ad09 100644
--- a/packages/editor-core/package.json
+++ b/packages/editor-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-editor-core",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "Core Api for Ali lowCode engine",
"license": "MIT",
"main": "lib/index.js",
@@ -14,8 +14,8 @@
},
"dependencies": {
"@alifd/next": "^1.19.16",
- "@alilc/lowcode-types": "1.1.2",
- "@alilc/lowcode-utils": "1.1.2",
+ "@alilc/lowcode-types": "1.1.3",
+ "@alilc/lowcode-utils": "1.1.3",
"classnames": "^2.2.6",
"debug": "^4.1.1",
"intl-messageformat": "^9.3.1",
diff --git a/packages/editor-core/src/config.ts b/packages/editor-core/src/config.ts
index 91ef33adb..ef889e727 100644
--- a/packages/editor-core/src/config.ts
+++ b/packages/editor-core/src/config.ts
@@ -124,9 +124,7 @@ const VALID_ENGINE_OPTIONS = {
type: 'array',
description: '自定义 simulatorUrl 的地址',
},
- /**
- * 与 react-renderer 的 appHelper 一致,https://lowcode-engine.cn/site/docs/guide/expand/runtime/renderer#apphelper
- */
+ // 与 react-renderer 的 appHelper 一致,https://lowcode-engine.cn/site/docs/guide/expand/runtime/renderer#apphelper
appHelper: {
type: 'object',
description: '定义 utils 和 constants 等对象',
@@ -149,7 +147,6 @@ const VALID_ENGINE_OPTIONS = {
},
};
-
const getStrictModeValue = (engineOptions: IPublicTypeEngineOptions, defaultValue: boolean): boolean => {
if (!engineOptions || !isPlainObject(engineOptions)) {
return defaultValue;
@@ -161,7 +158,8 @@ const getStrictModeValue = (engineOptions: IPublicTypeEngineOptions, defaultValu
return engineOptions.enableStrictPluginMode;
};
-export interface IEngineConfigPrivate {
+export interface IEngineConfig extends IPublicModelEngineConfig {
+
/**
* if engineOptions.strictPluginMode === true, only accept propertied predefined in EngineOptions.
*
@@ -176,8 +174,7 @@ export interface IEngineConfigPrivate {
delWait(key: string, fn: any): void;
}
-
-export class EngineConfig implements IPublicModelEngineConfig, IEngineConfigPrivate {
+export class EngineConfig implements IEngineConfig {
private config: { [key: string]: any } = {};
private waits = new Map<
@@ -291,13 +288,11 @@ export class EngineConfig implements IPublicModelEngineConfig, IEngineConfigPriv
const val = this.config?.[key];
if (val !== undefined) {
fn(val);
- return () => {};
- } else {
- this.setWait(key, fn);
- return () => {
- this.delWait(key, fn);
- };
}
+ this.setWait(key, fn);
+ return () => {
+ this.delWait(key, fn);
+ };
}
notifyGot(key: string): void {
@@ -350,4 +345,4 @@ export class EngineConfig implements IPublicModelEngineConfig, IEngineConfigPriv
}
}
-export const engineConfig = new EngineConfig();
\ No newline at end of file
+export const engineConfig = new EngineConfig();
diff --git a/packages/editor-core/src/editor.ts b/packages/editor-core/src/editor.ts
index 5fa6bb894..737a9fa26 100644
--- a/packages/editor-core/src/editor.ts
+++ b/packages/editor-core/src/editor.ts
@@ -123,6 +123,7 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor
await (new AssetLoader()).load(url);
function setAssetsComponent(component: any, extraNpmInfo: any = {}) {
const components = component.components;
+ assets.componentList = assets.componentList?.concat(component.componentList || []);
if (Array.isArray(components)) {
components.forEach(d => {
assets.components = assets.components.concat({
@@ -144,7 +145,6 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor
...component.components,
} || []);
}
- // assets.componentList = assets.componentList.concat(component.componentList || []);
}
function setArrayAssets(value: any[], preExportName: string = '', preSubName: string = '') {
value.forEach((d: any, i: number) => {
@@ -190,13 +190,11 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor
const x = this.context.get(keyOrType);
if (x !== undefined) {
fn(x);
- return () => { };
- } else {
- this.setWait(keyOrType, fn);
- return () => {
- this.delWait(keyOrType, fn);
- };
}
+ this.setWait(keyOrType, fn);
+ return () => {
+ this.delWait(keyOrType, fn);
+ };
}
register(data: any, key?: IPublicTypeEditorValueKey): void {
@@ -328,4 +326,4 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor
}
}
-export const commonEvent = new EventBus(new EventEmitter());
\ No newline at end of file
+export const commonEvent = new EventBus(new EventEmitter());
diff --git a/packages/editor-skeleton/package.json b/packages/editor-skeleton/package.json
index 3477ba1d5..d799bb68b 100644
--- a/packages/editor-skeleton/package.json
+++ b/packages/editor-skeleton/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-editor-skeleton",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "alibaba lowcode editor skeleton",
"main": "lib/index.js",
"module": "es/index.js",
@@ -18,10 +18,10 @@
],
"dependencies": {
"@alifd/next": "^1.20.12",
- "@alilc/lowcode-designer": "1.1.2",
- "@alilc/lowcode-editor-core": "1.1.2",
- "@alilc/lowcode-types": "1.1.2",
- "@alilc/lowcode-utils": "1.1.2",
+ "@alilc/lowcode-designer": "1.1.3",
+ "@alilc/lowcode-editor-core": "1.1.3",
+ "@alilc/lowcode-types": "1.1.3",
+ "@alilc/lowcode-utils": "1.1.3",
"classnames": "^2.2.6",
"react": "^16.8.1",
"react-dom": "^16.8.1"
diff --git a/packages/editor-skeleton/src/components/settings/settings-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-pane.tsx
index 8972c4ad7..0b470557b 100644
--- a/packages/editor-skeleton/src/components/settings/settings-pane.tsx
+++ b/packages/editor-skeleton/src/components/settings/settings-pane.tsx
@@ -3,7 +3,7 @@ import { shallowIntl, observer, obx, engineConfig, runInAction, globalContext }
import { createContent, isJSSlot, isSetterConfig, isSettingField } from '@alilc/lowcode-utils';
import { Skeleton } from '@alilc/lowcode-editor-skeleton';
import { IPublicTypeCustomView } from '@alilc/lowcode-types';
-import { SettingField, SettingTopEntry, SettingEntry, ComponentMeta } from '@alilc/lowcode-designer';
+import { SettingField, SettingTopEntry, ISettingEntry, ComponentMeta } from '@alilc/lowcode-designer';
import { createField } from '../field';
import PopupService, { PopupPipe } from '../popup';
import { SkeletonContext } from '../../context';
@@ -58,7 +58,7 @@ class SettingFieldView extends Component{field.items.map((item, index) => createSettingFieldView(item, field, index))},
@@ -324,7 +324,7 @@ class SettingGroupView extends Component {
}
}
-export function createSettingFieldView(item: SettingField | IPublicTypeCustomView, field: SettingEntry, index?: number) {
+export function createSettingFieldView(item: SettingField | IPublicTypeCustomView, field: ISettingEntry, index?: number) {
if (isSettingField(item)) {
if (item.isGroup) {
return ;
diff --git a/packages/engine/README-zh_CN.md b/packages/engine/README-zh_CN.md
index 276123391..b5256af9c 100644
--- a/packages/engine/README-zh_CN.md
+++ b/packages/engine/README-zh_CN.md
@@ -71,7 +71,7 @@ skeleton.add({
area: 'topArea',
type: 'Widget',
name: 'logo',
- content: YourFantaticLogo,
+ content: YourFantasticLogo,
contentProps: {
logo:
'https://img.alicdn.com/tfs/TB1_SocGkT2gK0jSZFkXXcIQFXa-66-66.png',
diff --git a/packages/engine/README.md b/packages/engine/README.md
index 4f0543c7a..2d1254e4a 100644
--- a/packages/engine/README.md
+++ b/packages/engine/README.md
@@ -71,7 +71,7 @@ skeleton.add({
area: 'topArea',
type: 'Widget',
name: 'logo',
- content: YourFantaticLogo,
+ content: YourFantasticLogo,
contentProps: {
logo:
'https://img.alicdn.com/tfs/TB1_SocGkT2gK0jSZFkXXcIQFXa-66-66.png',
diff --git a/packages/engine/package.json b/packages/engine/package.json
index 6edbe7ac0..768b80ffd 100644
--- a/packages/engine/package.json
+++ b/packages/engine/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-engine",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "An enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系",
"main": "lib/engine-core.js",
"module": "es/engine-core.js",
@@ -19,15 +19,15 @@
"license": "MIT",
"dependencies": {
"@alifd/next": "^1.19.12",
- "@alilc/lowcode-designer": "1.1.2",
- "@alilc/lowcode-editor-core": "1.1.2",
- "@alilc/lowcode-editor-skeleton": "1.1.2",
+ "@alilc/lowcode-designer": "1.1.3",
+ "@alilc/lowcode-editor-core": "1.1.3",
+ "@alilc/lowcode-editor-skeleton": "1.1.3",
"@alilc/lowcode-engine-ext": "^1.0.0",
- "@alilc/lowcode-plugin-designer": "1.1.2",
- "@alilc/lowcode-plugin-outline-pane": "1.1.2",
- "@alilc/lowcode-shell": "1.1.2",
- "@alilc/lowcode-utils": "1.1.2",
- "@alilc/lowcode-workspace": "1.1.2",
+ "@alilc/lowcode-plugin-designer": "1.1.3",
+ "@alilc/lowcode-plugin-outline-pane": "1.1.3",
+ "@alilc/lowcode-shell": "1.1.3",
+ "@alilc/lowcode-utils": "1.1.3",
+ "@alilc/lowcode-workspace": "1.1.3",
"react": "^16.8.1",
"react-dom": "^16.8.1"
},
diff --git a/packages/engine/src/engine-core.ts b/packages/engine/src/engine-core.ts
index abe378d64..b038df818 100644
--- a/packages/engine/src/engine-core.ts
+++ b/packages/engine/src/engine-core.ts
@@ -43,6 +43,7 @@ import {
Logger,
Canvas,
Workspace,
+ Config,
} from '@alilc/lowcode-shell';
import { isPlainObject } from '@alilc/lowcode-utils';
import './modules/live-editing';
@@ -96,7 +97,7 @@ editor.set('project', project);
editor.set('setters' as any, setters);
editor.set('material', material);
editor.set('innerHotkey', innerHotkey);
-const config = engineConfig;
+const config = new Config(engineConfig);
const event = new Event(commonEvent, { prefix: 'common' });
const logger = new Logger({ level: 'warn', bizName: 'common' });
const common = new Common(editor, innerSkeleton);
@@ -118,6 +119,7 @@ const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = {
context.canvas = canvas;
context.plugins = plugins;
context.logger = new Logger({ level: 'warn', bizName: `plugin:${pluginName}` });
+ context.workspace = workspace;
},
};
diff --git a/packages/engine/src/modules/shell-model-factory.ts b/packages/engine/src/modules/shell-model-factory.ts
index 05c7b19cb..bd3f795eb 100644
--- a/packages/engine/src/modules/shell-model-factory.ts
+++ b/packages/engine/src/modules/shell-model-factory.ts
@@ -1,5 +1,5 @@
import {
- Node as InnerNode,
+ INode,
SettingField as InnerSettingField,
} from '@alilc/lowcode-designer';
import { IShellModelFactory, IPublicModelNode, IPublicModelSettingPropEntry } from '@alilc/lowcode-types';
@@ -8,7 +8,7 @@ import {
SettingPropEntry,
} from '@alilc/lowcode-shell';
class ShellModelFactory implements IShellModelFactory {
- createNode(node: InnerNode | null | undefined): IPublicModelNode | null {
+ createNode(node: INode | null | undefined): IPublicModelNode | null {
return Node.create(node);
}
createSettingPropEntry(prop: InnerSettingField): IPublicModelSettingPropEntry {
diff --git a/packages/ignitor/package.json b/packages/ignitor/package.json
index 92f19ca67..71ea2b4c0 100644
--- a/packages/ignitor/package.json
+++ b/packages/ignitor/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-ignitor",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "点火器,bootstrap lce project",
"main": "lib/index.js",
"private": true,
diff --git a/packages/plugin-designer/package.json b/packages/plugin-designer/package.json
index fd0a246a6..ca426b6eb 100644
--- a/packages/plugin-designer/package.json
+++ b/packages/plugin-designer/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-plugin-designer",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "alibaba lowcode editor designer plugin",
"files": [
"es",
@@ -18,9 +18,9 @@
],
"author": "xiayang.xy",
"dependencies": {
- "@alilc/lowcode-designer": "1.1.2",
- "@alilc/lowcode-editor-core": "1.1.2",
- "@alilc/lowcode-utils": "1.1.2",
+ "@alilc/lowcode-designer": "1.1.3",
+ "@alilc/lowcode-editor-core": "1.1.3",
+ "@alilc/lowcode-utils": "1.1.3",
"react": "^16.8.1",
"react-dom": "^16.8.1"
},
diff --git a/packages/plugin-outline-pane/package.json b/packages/plugin-outline-pane/package.json
index 5a9147a8d..ff05813c4 100644
--- a/packages/plugin-outline-pane/package.json
+++ b/packages/plugin-outline-pane/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-plugin-outline-pane",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "Outline pane for Ali lowCode engine",
"files": [
"es",
@@ -13,10 +13,10 @@
},
"dependencies": {
"@alifd/next": "^1.19.16",
- "@alilc/lowcode-designer": "1.1.2",
- "@alilc/lowcode-editor-core": "1.1.2",
- "@alilc/lowcode-types": "1.1.2",
- "@alilc/lowcode-utils": "1.1.2",
+ "@alilc/lowcode-designer": "1.1.3",
+ "@alilc/lowcode-editor-core": "1.1.3",
+ "@alilc/lowcode-types": "1.1.3",
+ "@alilc/lowcode-utils": "1.1.3",
"classnames": "^2.2.6",
"react": "^16",
"react-dom": "^16.7.0",
diff --git a/packages/plugin-outline-pane/src/controllers/tree-node.ts b/packages/plugin-outline-pane/src/controllers/tree-node.ts
index 1babdbe61..1f15405e5 100644
--- a/packages/plugin-outline-pane/src/controllers/tree-node.ts
+++ b/packages/plugin-outline-pane/src/controllers/tree-node.ts
@@ -3,8 +3,10 @@ import {
IPublicTypeLocationChildrenDetail,
IPublicModelNode,
IPublicModelPluginContext,
+ IPublicTypeDisposable,
} from '@alilc/lowcode-types';
import { isI18nData, isLocationChildrenDetail } from '@alilc/lowcode-utils';
+import EventEmitter from 'events';
import { Tree } from './tree';
/**
@@ -21,14 +23,61 @@ export interface FilterResult {
keywords: string;
}
+enum EVENT_NAMES {
+ filterResultChanged = 'filterResultChanged',
+
+ expandedChanged = 'expandedChanged',
+
+ hiddenChanged = 'hiddenChanged',
+
+ lockedChanged = 'lockedChanged',
+
+ titleLabelChanged = 'titleLabelChanged',
+
+ expandableChanged = 'expandableChanged',
+}
+
export default class TreeNode {
readonly pluginContext: IPublicModelPluginContext;
- onFilterResultChanged: () => void;
- onExpandedChanged: (expanded: boolean) => void;
- onHiddenChanged: (hidden: boolean) => void;
- onLockedChanged: (locked: boolean) => void;
- onTitleLabelChanged: (treeNode: TreeNode) => void;
- onExpandableChanged: (expandable: boolean) => void;
+ event = new EventEmitter();
+
+ onFilterResultChanged(fn: () => void): IPublicTypeDisposable {
+ this.event.on(EVENT_NAMES.filterResultChanged, fn);
+ return () => {
+ this.event.off(EVENT_NAMES.filterResultChanged, fn);
+ }
+ };
+ onExpandedChanged(fn: (expanded: boolean) => void): IPublicTypeDisposable {
+ this.event.on(EVENT_NAMES.expandedChanged, fn);
+ return () => {
+ this.event.off(EVENT_NAMES.expandedChanged, fn);
+ }
+ };
+ onHiddenChanged(fn: (hidden: boolean) => void): IPublicTypeDisposable {
+ this.event.on(EVENT_NAMES.hiddenChanged, fn);
+ return () => {
+ this.event.off(EVENT_NAMES.hiddenChanged, fn);
+ }
+ };
+ onLockedChanged(fn: (locked: boolean) => void): IPublicTypeDisposable {
+ this.event.on(EVENT_NAMES.lockedChanged, fn);
+ return () => {
+ this.event.off(EVENT_NAMES.lockedChanged, fn);
+ }
+ };
+ onTitleLabelChanged(fn: (treeNode: TreeNode) => void): IPublicTypeDisposable {
+ this.event.on(EVENT_NAMES.titleLabelChanged, fn);
+
+ return () => {
+ this.event.off(EVENT_NAMES.titleLabelChanged, fn);
+ }
+ };
+ onExpandableChanged(fn: (expandable: boolean) => void): IPublicTypeDisposable {
+ this.event.on(EVENT_NAMES.expandableChanged, fn);
+ return () => {
+ this.event.off(EVENT_NAMES.expandableChanged, fn);
+ }
+ }
get id(): string {
return this.node.id;
@@ -46,7 +95,7 @@ export default class TreeNode {
* 触发 onExpandableChanged 回调
*/
notifyExpandableChanged(): void {
- this.onExpandableChanged && this.onExpandableChanged(this.expandable);
+ this.event.emit(EVENT_NAMES.expandableChanged, this.expandable);
}
/**
@@ -99,7 +148,7 @@ export default class TreeNode {
setExpanded(value: boolean) {
this._expanded = value;
- this.onExpandedChanged && this.onExpandedChanged(value);
+ this.event.emit(EVENT_NAMES.expandedChanged, value);
}
get detecting() {
@@ -120,7 +169,7 @@ export default class TreeNode {
return;
}
this.node.visible = !flag;
- this.onHiddenChanged && this.onHiddenChanged(flag);
+ this.event.emit(EVENT_NAMES.hiddenChanged, flag);
}
get locked(): boolean {
@@ -129,7 +178,7 @@ export default class TreeNode {
setLocked(flag: boolean) {
this.node.lock(flag);
- this.onLockedChanged && this.onLockedChanged(flag);
+ this.event.emit(EVENT_NAMES.lockedChanged, flag);
}
get selected(): boolean {
@@ -174,11 +223,11 @@ export default class TreeNode {
} else {
this.node.getExtraProp('title', true)?.setValue(label);
}
- this.onTitleLabelChanged && this.onTitleLabelChanged(this);
+ this.event.emit(EVENT_NAMES.titleLabelChanged, this);
}
get icon() {
- return this.node.componentMeta.icon;
+ return this.node.componentMeta?.icon;
}
get parent(): TreeNode | null {
@@ -282,8 +331,6 @@ export default class TreeNode {
setFilterReult(val: FilterResult) {
this._filterResult = val;
- if (this.onFilterResultChanged) {
- this.onFilterResultChanged();
- }
+ this.event.emit(EVENT_NAMES.filterResultChanged)
}
}
diff --git a/packages/plugin-outline-pane/src/views/tree-branches.tsx b/packages/plugin-outline-pane/src/views/tree-branches.tsx
index 9af2dbd09..d9b0f83a8 100644
--- a/packages/plugin-outline-pane/src/views/tree-branches.tsx
+++ b/packages/plugin-outline-pane/src/views/tree-branches.tsx
@@ -2,7 +2,7 @@ import { Component } from 'react';
import classNames from 'classnames';
import TreeNode from '../controllers/tree-node';
import TreeNodeView from './tree-node';
-import { IPublicModelPluginContext, IPublicModelExclusiveGroup } from '@alilc/lowcode-types';
+import { IPublicModelPluginContext, IPublicModelExclusiveGroup, IPublicTypeDisposable } from '@alilc/lowcode-types';
export default class TreeBranches extends Component<{
treeNode: TreeNode;
@@ -25,10 +25,10 @@ export default class TreeBranches extends Component<{
componentDidMount() {
const { treeNode } = this.props;
- treeNode.onFilterResultChanged = () => {
+ treeNode.onFilterResultChanged(() => {
const { filterWorking: newFilterWorking, matchChild: newMatchChild } = treeNode.filterReult;
this.setState({ filterWorking: newFilterWorking, matchChild: newMatchChild });
- };
+ });
}
componentWillUnmount(): void {
@@ -73,7 +73,7 @@ class TreeNodeChildren extends Component<{
keywords: null,
dropDetail: null,
};
- offLocationChanged: () => void;
+ offLocationChanged: IPublicTypeDisposable | undefined;
componentDidMount() {
const { treeNode, pluginContext } = this.props;
const { project } = pluginContext;
@@ -85,7 +85,7 @@ class TreeNodeChildren extends Component<{
keywords,
dropDetail,
});
- treeNode.onFilterResultChanged = () => {
+ treeNode.onFilterResultChanged(() => {
const {
filterWorking: newFilterWorking,
matchSelf: newMatchChild,
@@ -96,7 +96,7 @@ class TreeNodeChildren extends Component<{
matchSelf: newMatchChild,
keywords: newKeywords,
});
- };
+ });
this.offLocationChanged = project.currentDocument?.onDropLocationChanged(
() => {
this.setState({ dropDetail: treeNode.dropDetail });
@@ -118,7 +118,7 @@ class TreeNodeChildren extends Component<{
const endGroup = () => {
if (groupContents.length > 0) {
children.push(
-
+
void> = [];
+ eventOffCallbacks: Array = [];
constructor(props: any) {
super(props);
@@ -104,18 +104,18 @@ export default class TreeNodeView extends Component<{
const doc = project.currentDocument;
- treeNode.onExpandedChanged = ((expanded: boolean) => {
+ treeNode.onExpandedChanged(((expanded: boolean) => {
this.setState({ expanded });
- });
- treeNode.onHiddenChanged = (hidden: boolean) => {
+ }));
+ treeNode.onHiddenChanged((hidden: boolean) => {
this.setState({ hidden });
- };
- treeNode.onLockedChanged = (locked: boolean) => {
+ });
+ treeNode.onLockedChanged((locked: boolean) => {
this.setState({ locked });
- };
- treeNode.onExpandableChanged = (expandable: boolean) => {
+ });
+ treeNode.onExpandableChanged((expandable: boolean) => {
this.setState({ expandable });
- };
+ });
this.eventOffCallbacks.push(
doc?.onDropLocationChanged((document: IPublicModelDocumentModel) => {
@@ -135,8 +135,8 @@ export default class TreeNodeView extends Component<{
this.eventOffCallbacks.push(offDetectingChange!);
}
componentWillUnmount(): void {
- this.eventOffCallbacks?.forEach((offFun: () => void) => {
- offFun();
+ this.eventOffCallbacks?.forEach((offFun: IPublicTypeDisposable | undefined) => {
+ offFun && offFun();
});
}
diff --git a/packages/plugin-outline-pane/src/views/tree-title.tsx b/packages/plugin-outline-pane/src/views/tree-title.tsx
index 7a3718366..51d0fcf80 100644
--- a/packages/plugin-outline-pane/src/views/tree-title.tsx
+++ b/packages/plugin-outline-pane/src/views/tree-title.tsx
@@ -6,7 +6,6 @@ import { IPublicModelPluginContext, IPublicApiEvent } from '@alilc/lowcode-types
import TreeNode from '../controllers/tree-node';
import { IconLock, IconUnlock, IconArrowRight, IconEyeClose, IconEye, IconCond, IconLoop, IconRadioActive, IconRadio, IconSetting } from '../icons';
-
function emitOutlineEvent(event: IPublicApiEvent, type: string, treeNode: TreeNode, rest?: Record) {
const node = treeNode?.node;
const npm = node?.componentMeta?.npm;
@@ -35,6 +34,8 @@ export default class TreeTitle extends Component<{
title: '',
};
+ private lastInput?: HTMLInputElement;
+
private enableEdit = (e) => {
e.preventDefault();
this.setState({
@@ -66,8 +67,6 @@ export default class TreeTitle extends Component<{
}
};
- private lastInput?: HTMLInputElement;
-
private setCaret = (input: HTMLInputElement | null) => {
if (!input || this.lastInput === input) {
return;
@@ -84,11 +83,11 @@ export default class TreeTitle extends Component<{
editing: false,
title: treeNode.titleLabel,
});
- treeNode.onTitleLabelChanged = () => {
+ treeNode.onTitleLabelChanged(() => {
this.setState({
title: treeNode.titleLabel,
});
- };
+ });
}
render() {
@@ -96,6 +95,8 @@ export default class TreeTitle extends Component<{
const { editing } = this.state;
const isCNode = !treeNode.isRoot();
const { node } = treeNode;
+ const { componentMeta } = node;
+ const availableActions = componentMeta ? componentMeta.availableActions.map((availableAction) => availableAction.name) : [];
const isNodeParent = node.isParentalNode;
const isContainer = node.isContainerNode;
let style: any;
@@ -112,8 +113,11 @@ export default class TreeTitle extends Component<{
const { intlNode, common, config } = pluginContext;
const Tip = common.editorCabin.Tip;
const Title = common.editorCabin.Title;
- const shouldShowHideBtn = isCNode && isNodeParent && !isModal;
- const shouldShowLockBtn = config.get('enableCanvasLock', false) && isContainer && isCNode && isNodeParent;
+ const couldHide = availableActions.includes('hide');
+ const couldLock = availableActions.includes('lock');
+ const couldUnlock = availableActions.includes('unlock');
+ const shouldShowHideBtn = isCNode && isNodeParent && !isModal && couldHide;
+ const shouldShowLockBtn = config.get('enableCanvasLock', false) && isContainer && isCNode && isNodeParent && ((couldLock && !node.isLocked) || (couldUnlock && node.isLocked));
const shouldEditBtn = isCNode && isNodeParent;
return (
);
}
-}
\ No newline at end of file
+}
diff --git a/packages/rax-renderer/package.json b/packages/rax-renderer/package.json
index d6270e1f8..50209be4e 100644
--- a/packages/rax-renderer/package.json
+++ b/packages/rax-renderer/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-rax-renderer",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "Rax renderer for Ali lowCode engine",
"main": "lib/index.js",
"module": "es/index.js",
@@ -30,8 +30,8 @@
"build": "build-scripts build"
},
"dependencies": {
- "@alilc/lowcode-renderer-core": "1.1.2",
- "@alilc/lowcode-utils": "1.1.2",
+ "@alilc/lowcode-renderer-core": "1.1.3",
+ "@alilc/lowcode-utils": "1.1.3",
"rax-find-dom-node": "^1.0.1"
},
"devDependencies": {
diff --git a/packages/rax-simulator-renderer/package.json b/packages/rax-simulator-renderer/package.json
index 2ff493c2e..e5b08717d 100644
--- a/packages/rax-simulator-renderer/package.json
+++ b/packages/rax-simulator-renderer/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-rax-simulator-renderer",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "rax simulator renderer for alibaba lowcode designer",
"main": "lib/index.js",
"module": "es/index.js",
@@ -13,10 +13,10 @@
"build:umd": "build-scripts build --config build.umd.json"
},
"dependencies": {
- "@alilc/lowcode-designer": "1.1.2",
- "@alilc/lowcode-rax-renderer": "1.1.2",
- "@alilc/lowcode-types": "1.1.2",
- "@alilc/lowcode-utils": "1.1.2",
+ "@alilc/lowcode-designer": "1.1.3",
+ "@alilc/lowcode-rax-renderer": "1.1.3",
+ "@alilc/lowcode-types": "1.1.3",
+ "@alilc/lowcode-utils": "1.1.3",
"classnames": "^2.2.6",
"driver-universal": "^3.1.3",
"history": "^5.0.0",
diff --git a/packages/react-renderer/package.json b/packages/react-renderer/package.json
index 7ac933647..b897eea68 100644
--- a/packages/react-renderer/package.json
+++ b/packages/react-renderer/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-react-renderer",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "react renderer for ali lowcode engine",
"main": "lib/index.js",
"module": "es/index.js",
@@ -22,7 +22,7 @@
],
"dependencies": {
"@alifd/next": "^1.21.16",
- "@alilc/lowcode-renderer-core": "1.1.2"
+ "@alilc/lowcode-renderer-core": "1.1.3"
},
"devDependencies": {
"@alib/build-scripts": "^0.1.18",
diff --git a/packages/react-simulator-renderer/package.json b/packages/react-simulator-renderer/package.json
index 7c5868f3c..49c991f2a 100644
--- a/packages/react-simulator-renderer/package.json
+++ b/packages/react-simulator-renderer/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-react-simulator-renderer",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "react simulator renderer for alibaba lowcode designer",
"main": "lib/index.js",
"module": "es/index.js",
@@ -17,10 +17,10 @@
"test:cov": "build-scripts test --config build.test.json --jest-coverage"
},
"dependencies": {
- "@alilc/lowcode-designer": "1.1.2",
- "@alilc/lowcode-react-renderer": "1.1.2",
- "@alilc/lowcode-types": "1.1.2",
- "@alilc/lowcode-utils": "1.1.2",
+ "@alilc/lowcode-designer": "1.1.3",
+ "@alilc/lowcode-react-renderer": "1.1.3",
+ "@alilc/lowcode-types": "1.1.3",
+ "@alilc/lowcode-utils": "1.1.3",
"classnames": "^2.2.6",
"mobx": "^6.3.0",
"mobx-react": "^7.2.0",
diff --git a/packages/react-simulator-renderer/src/renderer-view.tsx b/packages/react-simulator-renderer/src/renderer-view.tsx
index 68e66fc02..e8c7ce52e 100644
--- a/packages/react-simulator-renderer/src/renderer-view.tsx
+++ b/packages/react-simulator-renderer/src/renderer-view.tsx
@@ -170,7 +170,9 @@ class Renderer extends Component<{
this.startTime = Date.now();
this.schemaChangedSymbol = false;
- if (!container.autoRender || isRendererDetached()) return null;
+ if (!container.autoRender || isRendererDetached()) {
+ return null;
+ }
const { intl } = createIntl(locale);
diff --git a/packages/react-simulator-renderer/src/renderer.ts b/packages/react-simulator-renderer/src/renderer.ts
index e76bfb585..766150028 100644
--- a/packages/react-simulator-renderer/src/renderer.ts
+++ b/packages/react-simulator-renderer/src/renderer.ts
@@ -39,10 +39,6 @@ export class DocumentInstance {
private disposeFunctions: Array<() => void> = [];
- constructor(readonly container: SimulatorRendererContainer, readonly document: DocumentModel) {
- makeObservable(this);
- }
-
@obx.ref private _components: any = {};
@computed get components(): object {
@@ -98,6 +94,10 @@ export class DocumentInstance {
return this.document.id;
}
+ constructor(readonly container: SimulatorRendererContainer, readonly document: DocumentModel) {
+ makeObservable(this);
+ }
+
private unmountInstance(id: string, instance: ReactInstance) {
const instances = this.instancesMap.get(id);
if (instances) {
@@ -170,8 +170,7 @@ export class DocumentInstance {
host.setInstance(this.document.id, id, instances);
}
- mountContext(docId: string, id: string, ctx: object) {
- // this.ctxMap.set(id, ctx);
+ mountContext() {
}
getNode(id: string): Node | null {
@@ -195,6 +194,60 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
return this._documentInstances;
}
+ @obx private _layout: any = null;
+
+ @computed get layout(): any {
+ // TODO: parse layout Component
+ return this._layout;
+ }
+
+ set layout(value: any) {
+ this._layout = value;
+ }
+
+ private _libraryMap: { [key: string]: string } = {};
+
+ private _components: any = {};
+
+ get components(): object {
+ // 根据 device 选择不同组件,进行响应式
+ // 更好的做法是,根据 device 选择加载不同的组件资源,甚至是 simulatorUrl
+ return this._components;
+ }
+ // context from: utils、constants、history、location、match
+ @obx.ref private _appContext: any = {};
+ @computed get context(): any {
+ return this._appContext;
+ }
+ @obx.ref private _designMode: string = 'design';
+ @computed get designMode(): any {
+ return this._designMode;
+ }
+ @obx.ref private _device: string = 'default';
+ @computed get device() {
+ return this._device;
+ }
+ @obx.ref private _locale: string | undefined = undefined;
+ @computed get locale() {
+ return this._locale;
+ }
+ @obx.ref private _componentsMap = {};
+ @computed get componentsMap(): any {
+ return this._componentsMap;
+ }
+
+ /**
+ * 是否为画布自动渲染
+ */
+ autoRender = true;
+
+ /**
+ * 画布是否自动监听事件来重绘节点
+ */
+ autoRepaintNode = true;
+
+ private _running = false;
+
constructor() {
makeObservable(this);
this.autoRender = host.autoRender;
@@ -306,19 +359,6 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
});
}
- @obx private _layout: any = null;
-
- @computed get layout(): any {
- // TODO: parse layout Component
- return this._layout;
- }
-
- set layout(value: any) {
- this._layout = value;
- }
-
- private _libraryMap: { [key: string]: string } = {};
-
private buildComponents() {
this._components = buildComponents(
this._libraryMap,
@@ -330,44 +370,6 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
...this._components,
};
}
- private _components: any = {};
-
- get components(): object {
- // 根据 device 选择不同组件,进行响应式
- // 更好的做法是,根据 device 选择加载不同的组件资源,甚至是 simulatorUrl
- return this._components;
- }
- // context from: utils、constants、history、location、match
- @obx.ref private _appContext: any = {};
- @computed get context(): any {
- return this._appContext;
- }
- @obx.ref private _designMode: string = 'design';
- @computed get designMode(): any {
- return this._designMode;
- }
- @obx.ref private _device: string = 'default';
- @computed get device() {
- return this._device;
- }
- @obx.ref private _locale: string | undefined = undefined;
- @computed get locale() {
- return this._locale;
- }
- @obx.ref private _componentsMap = {};
- @computed get componentsMap(): any {
- return this._componentsMap;
- }
-
- /**
- * 是否为画布自动渲染
- */
- autoRender = true;
-
- /**
- * 画布是否自动监听事件来重绘节点
- */
- autoRepaintNode = true;
/**
* 加载资源
@@ -457,6 +459,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
appHelper: renderer.context,
rendererName: 'LowCodeRenderer',
thisRequiredInJSE: host.thisRequiredInJSE,
+ faultComponent: host.faultComponent,
customCreateElement: (Comp: any, props: any, children: any) => {
const componentMeta = host.currentDocument?.getComponentMeta(Comp.displayName);
if (componentMeta?.isModal) {
@@ -479,8 +482,6 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
return LowCodeComp;
}
- private _running = false;
-
run() {
if (this._running) {
return;
diff --git a/packages/renderer-core/package.json b/packages/renderer-core/package.json
index deb30be11..aa1c7d204 100644
--- a/packages/renderer-core/package.json
+++ b/packages/renderer-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-renderer-core",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "renderer core",
"license": "MIT",
"main": "lib/index.js",
@@ -16,8 +16,8 @@
},
"dependencies": {
"@alilc/lowcode-datasource-engine": "^1.0.0",
- "@alilc/lowcode-types": "1.1.2",
- "@alilc/lowcode-utils": "1.1.2",
+ "@alilc/lowcode-types": "1.1.3",
+ "@alilc/lowcode-utils": "1.1.3",
"classnames": "^2.2.6",
"debug": "^4.1.1",
"fetch-jsonp": "^1.1.3",
@@ -32,7 +32,7 @@
"devDependencies": {
"@alib/build-scripts": "^0.1.18",
"@alifd/next": "^1.26.0",
- "@alilc/lowcode-designer": "1.1.2",
+ "@alilc/lowcode-designer": "1.1.3",
"@babel/plugin-transform-typescript": "^7.16.8",
"@testing-library/react": "^11.2.2",
"@types/classnames": "^2.2.11",
diff --git a/packages/renderer-core/src/renderer/addon.tsx b/packages/renderer-core/src/renderer/addon.tsx
index 62aeddbba..9cb114bee 100644
--- a/packages/renderer-core/src/renderer/addon.tsx
+++ b/packages/renderer-core/src/renderer/addon.tsx
@@ -45,7 +45,7 @@ export default function addonRendererFactory(): IBaseRenderComponent {
this.__initDataSource(props);
this.open = this.open || (() => { });
this.close = this.close || (() => { });
- this.__excuteLifeCycleMethod('constructor', [...arguments]);
+ this.__executeLifeCycleMethod('constructor', [...arguments]);
}
async componentWillUnmount() {
diff --git a/packages/renderer-core/src/renderer/base.tsx b/packages/renderer-core/src/renderer/base.tsx
index 054628c5f..a96554908 100644
--- a/packages/renderer-core/src/renderer/base.tsx
+++ b/packages/renderer-core/src/renderer/base.tsx
@@ -40,7 +40,7 @@ import isUseLoop from '../utils/is-use-loop';
* execute method in schema.lifeCycles with context
* @PRIVATE
*/
-export function excuteLifeCycleMethod(context: any, schema: IPublicTypeNodeSchema, method: string, args: any, thisRequiredInJSE: boolean | undefined): any {
+export function executeLifeCycleMethod(context: any, schema: IPublicTypeNodeSchema, method: string, args: any, thisRequiredInJSE: boolean | undefined): any {
if (!context || !isSchema(schema) || !method) {
return;
}
@@ -183,32 +183,32 @@ export default function baseRendererFactory(): IBaseRenderComponent {
__afterInit(_props: IBaseRendererProps) { }
static getDerivedStateFromProps(props: IBaseRendererProps, state: any) {
- return excuteLifeCycleMethod(this, props?.__schema, 'getDerivedStateFromProps', [props, state], props.thisRequiredInJSE);
+ return executeLifeCycleMethod(this, props?.__schema, 'getDerivedStateFromProps', [props, state], props.thisRequiredInJSE);
}
async getSnapshotBeforeUpdate(...args: any[]) {
- this.__excuteLifeCycleMethod('getSnapshotBeforeUpdate', args);
+ this.__executeLifeCycleMethod('getSnapshotBeforeUpdate', args);
this.__debug(`getSnapshotBeforeUpdate - ${this.props?.__schema?.fileName}`);
}
async componentDidMount(...args: any[]) {
this.reloadDataSource();
- this.__excuteLifeCycleMethod('componentDidMount', args);
+ this.__executeLifeCycleMethod('componentDidMount', args);
this.__debug(`componentDidMount - ${this.props?.__schema?.fileName}`);
}
async componentDidUpdate(...args: any[]) {
- this.__excuteLifeCycleMethod('componentDidUpdate', args);
+ this.__executeLifeCycleMethod('componentDidUpdate', args);
this.__debug(`componentDidUpdate - ${this.props.__schema.fileName}`);
}
async componentWillUnmount(...args: any[]) {
- this.__excuteLifeCycleMethod('componentWillUnmount', args);
+ this.__executeLifeCycleMethod('componentWillUnmount', args);
this.__debug(`componentWillUnmount - ${this.props?.__schema?.fileName}`);
}
async componentDidCatch(...args: any[]) {
- this.__excuteLifeCycleMethod('componentDidCatch', args);
+ this.__executeLifeCycleMethod('componentDidCatch', args);
console.warn(args);
}
@@ -248,8 +248,8 @@ export default function baseRendererFactory(): IBaseRenderComponent {
* execute method in schema.lifeCycles
* @PRIVATE
*/
- __excuteLifeCycleMethod = (method: string, args?: any) => {
- excuteLifeCycleMethod(this, this.props.__schema, method, args, this.props.thisRequiredInJSE);
+ __executeLifeCycleMethod = (method: string, args?: any) => {
+ executeLifeCycleMethod(this, this.props.__schema, method, args, this.props.thisRequiredInJSE);
};
/**
@@ -406,7 +406,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
__render = () => {
const schema = this.props.__schema;
- this.__excuteLifeCycleMethod('render');
+ this.__executeLifeCycleMethod('render');
this.__writeCss(this.props);
const { engine } = this.context;
@@ -774,6 +774,11 @@ export default function baseRendererFactory(): IBaseRenderComponent {
{
...schema,
loop: undefined,
+ props: {
+ ...schema.props,
+ // 循环下 key 不能为常量,这样会造成 key 值重复,渲染异常
+ key: isJSExpression(schema.props?.key) ? schema.props?.key : null,
+ },
},
loopSelf,
parentInfo,
diff --git a/packages/renderer-core/src/renderer/block.tsx b/packages/renderer-core/src/renderer/block.tsx
index 560b5924b..5132997f0 100644
--- a/packages/renderer-core/src/renderer/block.tsx
+++ b/packages/renderer-core/src/renderer/block.tsx
@@ -13,7 +13,7 @@ export default function blockRendererFactory(): IBaseRenderComponent {
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
this.__initDataSource(props);
- this.__excuteLifeCycleMethod('constructor', [...arguments]);
+ this.__executeLifeCycleMethod('constructor', [...arguments]);
}
render() {
diff --git a/packages/renderer-core/src/renderer/component.tsx b/packages/renderer-core/src/renderer/component.tsx
index 58d5c0093..4be33f5c1 100644
--- a/packages/renderer-core/src/renderer/component.tsx
+++ b/packages/renderer-core/src/renderer/component.tsx
@@ -15,7 +15,7 @@ export default function componentRendererFactory(): IBaseRenderComponent {
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
this.__initDataSource(props);
- this.__excuteLifeCycleMethod('constructor', arguments as any);
+ this.__executeLifeCycleMethod('constructor', arguments as any);
}
render() {
diff --git a/packages/renderer-core/src/renderer/page.tsx b/packages/renderer-core/src/renderer/page.tsx
index 9875f8d73..9ba49c723 100644
--- a/packages/renderer-core/src/renderer/page.tsx
+++ b/packages/renderer-core/src/renderer/page.tsx
@@ -15,7 +15,7 @@ export default function pageRendererFactory(): IBaseRenderComponent {
const schema = props.__schema || {};
this.state = this.__parseData(schema.state || {});
this.__initDataSource(props);
- this.__excuteLifeCycleMethod('constructor', [props, ...rest]);
+ this.__executeLifeCycleMethod('constructor', [props, ...rest]);
}
async componentDidUpdate(prevProps: IBaseRendererProps, _prevState: {}, snapshot: unknown) {
@@ -44,7 +44,6 @@ export default function pageRendererFactory(): IBaseRenderComponent {
});
this.__render();
-
const { Page } = __components;
if (Page) {
return this.__renderComp(Page, { pageContext: this });
diff --git a/packages/renderer-core/src/renderer/renderer.tsx b/packages/renderer-core/src/renderer/renderer.tsx
index c0f1c2afc..4968f269a 100644
--- a/packages/renderer-core/src/renderer/renderer.tsx
+++ b/packages/renderer-core/src/renderer/renderer.tsx
@@ -19,10 +19,9 @@ export default function rendererFactory(): IRenderComponent {
const debug = Debug('renderer:entry');
- class FaultComponent extends PureComponent
{
+ class FaultComponent extends PureComponent {
render() {
- // FIXME: errorlog
- console.error('render error', this.props);
+ logger.error(`%c组件渲染异常, 异常原因: ${this.props.error?.message || this.props.error || '未知'}`, 'color: #ff0000;');
return createElement(Div, {
style: {
width: '100%',
@@ -86,8 +85,9 @@ export default function rendererFactory(): IRenderComponent {
debug(`entry.componentWillUnmount - ${this.props?.schema?.componentName}`);
}
- async componentDidCatch(e: any) {
- console.warn(e);
+ componentDidCatch(error: Error) {
+ this.state.engineRenderError = true;
+ this.state.error = error;
}
shouldComponentUpdate(nextProps: IRendererProps) {
@@ -182,6 +182,13 @@ export default function rendererFactory(): IRenderComponent {
}
}
+ if (this.state && this.state.engineRenderError) {
+ return createElement(this.getFaultComponent(), {
+ ...this.props,
+ error: this.state.error,
+ });
+ }
+
if (Comp) {
return createElement(AppContext.Provider, { value: {
appHelper,
diff --git a/packages/renderer-core/src/types/index.ts b/packages/renderer-core/src/types/index.ts
index 8d619737c..067bf2153 100644
--- a/packages/renderer-core/src/types/index.ts
+++ b/packages/renderer-core/src/types/index.ts
@@ -281,7 +281,7 @@ export type IBaseRendererInstance = IGeneralComponent<
__beforeInit(props: IBaseRendererProps): void;
__init(props: IBaseRendererProps): void;
__afterInit(props: IBaseRendererProps): void;
- __excuteLifeCycleMethod(method: string, args?: any[]): void;
+ __executeLifeCycleMethod(method: string, args?: any[]): void;
__bindCustomMethods(props: IBaseRendererProps): void;
__generateCtx(ctx: Record): void;
__parseData(data: any, ctx?: any): any;
@@ -323,10 +323,10 @@ export interface IRenderComponent {
new(props: IRendererProps, context: any): IGeneralComponent & {
[x: string]: any;
__getRef: (ref: any) => void;
- componentDidMount(): Promise;
- componentDidUpdate(): Promise;
- componentWillUnmount(): Promise;
- componentDidCatch(e: any): Promise;
+ componentDidMount(): Promise | void;
+ componentDidUpdate(): Promise | void;
+ componentWillUnmount(): Promise | void;
+ componentDidCatch(e: any): Promise | void;
shouldComponentUpdate(nextProps: IRendererProps): boolean;
isValidComponent(SetComponent: any): any;
patchDidCatch(SetComponent: any): void;
diff --git a/packages/renderer-core/tests/renderer/base.test.tsx b/packages/renderer-core/tests/renderer/base.test.tsx
index 3d19e324b..63c5cfbb2 100644
--- a/packages/renderer-core/tests/renderer/base.test.tsx
+++ b/packages/renderer-core/tests/renderer/base.test.tsx
@@ -121,7 +121,7 @@ describe('Base Render methods', () => {
// it('should excute lifecycle.componentDidCatch when defined', () => {
// });
- // it('__excuteLifeCycleMethod should work', () => {
+ // it('__executeLifeCycleMethod should work', () => {
// });
// it('reloadDataSource should work', () => {
diff --git a/packages/shell/package.json b/packages/shell/package.json
index a475a05bf..7608df5f5 100644
--- a/packages/shell/package.json
+++ b/packages/shell/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-shell",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "Shell Layer for AliLowCodeEngine",
"main": "lib/index.js",
"module": "es/index.js",
@@ -15,12 +15,12 @@
},
"license": "MIT",
"dependencies": {
- "@alilc/lowcode-designer": "1.1.2",
- "@alilc/lowcode-editor-core": "1.1.2",
- "@alilc/lowcode-editor-skeleton": "1.1.2",
- "@alilc/lowcode-types": "1.1.2",
- "@alilc/lowcode-utils": "1.1.2",
- "@alilc/lowcode-workspace": "1.1.2",
+ "@alilc/lowcode-designer": "1.1.3",
+ "@alilc/lowcode-editor-core": "1.1.3",
+ "@alilc/lowcode-editor-skeleton": "1.1.3",
+ "@alilc/lowcode-types": "1.1.3",
+ "@alilc/lowcode-utils": "1.1.3",
+ "@alilc/lowcode-workspace": "1.1.3",
"classnames": "^2.2.6",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
diff --git a/packages/shell/src/index.ts b/packages/shell/src/index.ts
index 74d11288e..aed56fb95 100644
--- a/packages/shell/src/index.ts
+++ b/packages/shell/src/index.ts
@@ -10,6 +10,7 @@ import {
SettingPropEntry,
SettingTopEntry,
Clipboard,
+ Config,
} from './model';
import {
Project,
@@ -61,4 +62,5 @@ export {
Workspace,
Clipboard,
SimulatorHost,
-};
\ No newline at end of file
+ Config,
+};
diff --git a/packages/shell/src/model/condition-group.ts b/packages/shell/src/model/condition-group.ts
new file mode 100644
index 000000000..22b926615
--- /dev/null
+++ b/packages/shell/src/model/condition-group.ts
@@ -0,0 +1,42 @@
+import { IExclusiveGroup } from '@alilc/lowcode-designer';
+import { IPublicModelExclusiveGroup, IPublicModelNode } from '@alilc/lowcode-types';
+import { conditionGroupSymbol, nodeSymbol } from '../symbols';
+import { Node } from './node';
+
+export class ConditionGroup implements IPublicModelExclusiveGroup {
+ private [conditionGroupSymbol]: IExclusiveGroup | null;
+
+ constructor(conditionGroup: IExclusiveGroup | null) {
+ this[conditionGroupSymbol] = conditionGroup;
+ }
+
+ get id() {
+ return this[conditionGroupSymbol]?.id;
+ }
+
+ get title() {
+ return this[conditionGroupSymbol]?.title;
+ }
+
+ get firstNode() {
+ return Node.create(this[conditionGroupSymbol]?.firstNode);
+ }
+
+ setVisible(node: IPublicModelNode) {
+ this[conditionGroupSymbol]?.setVisible((node as any)[nodeSymbol] ? (node as any)[nodeSymbol] : node);
+ }
+
+ static create(conditionGroup: IExclusiveGroup | null) {
+ if (!conditionGroup) {
+ return null;
+ }
+ // @ts-ignore
+ if (conditionGroup[conditionGroupSymbol]) {
+ return (conditionGroup as any)[conditionGroupSymbol];
+ }
+ const shellConditionGroup = new ConditionGroup(conditionGroup);
+ // @ts-ignore
+ shellConditionGroup[conditionGroupSymbol] = shellConditionGroup;
+ return shellConditionGroup;
+ }
+}
\ No newline at end of file
diff --git a/packages/shell/src/model/config.ts b/packages/shell/src/model/config.ts
new file mode 100644
index 000000000..d84120878
--- /dev/null
+++ b/packages/shell/src/model/config.ts
@@ -0,0 +1,39 @@
+import { IPublicModelEngineConfig, IPublicModelPreference, IPublicTypeDisposable } from '@alilc/lowcode-types';
+import { configSymbol } from '../symbols';
+import { IEngineConfig } from '@alilc/lowcode-editor-core';
+
+export class Config implements IPublicModelEngineConfig {
+ private readonly [configSymbol]: IEngineConfig;
+
+ constructor(innerEngineConfig: IEngineConfig) {
+ this[configSymbol] = innerEngineConfig;
+ }
+
+ has(key: string): boolean {
+ return this[configSymbol].has(key);
+ }
+
+ get(key: string, defaultValue?: any): any {
+ return this[configSymbol].get(key, defaultValue);
+ }
+
+ set(key: string, value: any): void {
+ this[configSymbol].set(key, value);
+ }
+
+ setConfig(config: { [key: string]: any }): void {
+ this[configSymbol].setConfig(config);
+ }
+
+ onceGot(key: string): Promise {
+ return this[configSymbol].onceGot(key);
+ }
+
+ onGot(key: string, fn: (data: any) => void): IPublicTypeDisposable {
+ return this[configSymbol].onGot(key, fn);
+ }
+
+ getPreference(): IPublicModelPreference {
+ return this[configSymbol].getPreference();
+ }
+}
diff --git a/packages/shell/src/model/document-model.ts b/packages/shell/src/model/document-model.ts
index 85d44c2cc..b1015b8de 100644
--- a/packages/shell/src/model/document-model.ts
+++ b/packages/shell/src/model/document-model.ts
@@ -21,6 +21,7 @@ import {
IPublicApiCanvas,
IPublicTypeDisposable,
IPublicModelEditor,
+ IPublicTypeNodeSchema,
} from '@alilc/lowcode-types';
import { isDragNodeObject } from '@alilc/lowcode-utils';
import { Node as ShellNode } from './node';
@@ -195,7 +196,7 @@ export class DocumentModel implements IPublicModelDocumentModel {
* @param data
* @returns
*/
- createNode(data: any): IPublicModelNode | null {
+ createNode(data: IPublicTypeNodeSchema): IPublicModelNode | null {
return ShellNode.create(this[documentSymbol].createNode(data));
}
@@ -289,7 +290,7 @@ export class DocumentModel implements IPublicModelDocumentModel {
* @param fn
*/
onChangeNodeVisible(fn: (node: IPublicModelNode, visible: boolean) => void): IPublicTypeDisposable {
- return this[documentSymbol].onChangeNodeVisible((node: IPublicModelNode, visible: boolean) => {
+ return this[documentSymbol].onChangeNodeVisible((node: InnerNode, visible: boolean) => {
fn(ShellNode.create(node)!, visible);
});
}
diff --git a/packages/shell/src/model/index.ts b/packages/shell/src/model/index.ts
index f2805342e..8e18c36bb 100644
--- a/packages/shell/src/model/index.ts
+++ b/packages/shell/src/model/index.ts
@@ -18,4 +18,5 @@ export * from './resource';
export * from './active-tracker';
export * from './plugin-instance';
export * from './window';
-export * from './clipboard';
\ No newline at end of file
+export * from './clipboard';
+export * from './config';
diff --git a/packages/shell/src/model/node-children.ts b/packages/shell/src/model/node-children.ts
index 0192268bc..4d1cc0a22 100644
--- a/packages/shell/src/model/node-children.ts
+++ b/packages/shell/src/model/node-children.ts
@@ -129,6 +129,15 @@ export class NodeChildren implements IPublicModelNodeChildren {
});
}
+ /**
+ * 类似数组的 reverse
+ */
+ reverse(): IPublicModelNode[] {
+ return this[nodeChildrenSymbol].reverse().map(d => {
+ return ShellNode.create(d)!;
+ });
+ }
+
/**
* 类似数组的 map
* @param fn
diff --git a/packages/shell/src/model/node.ts b/packages/shell/src/model/node.ts
index ba2412e89..249e87f46 100644
--- a/packages/shell/src/model/node.ts
+++ b/packages/shell/src/model/node.ts
@@ -27,6 +27,7 @@ import { ComponentMeta as ShellComponentMeta } from './component-meta';
import { SettingTopEntry as ShellSettingTopEntry } from './setting-top-entry';
import { documentSymbol, nodeSymbol } from '../symbols';
import { ReactElement } from 'react';
+import { ConditionGroup } from './condition-group';
const shellNodeSymbol = Symbol('shellNodeSymbol');
@@ -289,7 +290,7 @@ export class Node implements IPublicModelNode {
/**
* 当前节点为插槽节点时,返回节点对应的属性实例
*/
- get slotFor(): IPublicModelProp | null {
+ get slotFor(): IPublicModelProp | null | undefined {
return ShellProp.create(this[nodeSymbol].slotFor);
}
@@ -349,7 +350,6 @@ export class Node implements IPublicModelNode {
/**
* 获取节点实例对应的 dom 节点
- * @deprecated
*/
getDOMNode() {
return (this[nodeSymbol] as any).getDOMNode();
@@ -641,7 +641,7 @@ export class Node implements IPublicModelNode {
* @since v1.1.0
*/
get conditionGroup(): IPublicModelExclusiveGroup | null {
- return this[nodeSymbol].conditionGroup;
+ return ConditionGroup.create(this[nodeSymbol].conditionGroup);
}
/**
diff --git a/packages/shell/src/model/resource.ts b/packages/shell/src/model/resource.ts
index 1f037c606..63435cdea 100644
--- a/packages/shell/src/model/resource.ts
+++ b/packages/shell/src/model/resource.ts
@@ -37,7 +37,7 @@ export class Resource implements IPublicModelResource {
return this[resourceSymbol].children.map((child) => new Resource(child));
}
- get viewType() {
- return this[resourceSymbol].viewType;
+ get viewName() {
+ return this[resourceSymbol].viewName;
}
}
\ No newline at end of file
diff --git a/packages/shell/src/model/selection.ts b/packages/shell/src/model/selection.ts
index ff2124bec..073083a65 100644
--- a/packages/shell/src/model/selection.ts
+++ b/packages/shell/src/model/selection.ts
@@ -1,14 +1,14 @@
import {
IDocumentModel as InnerDocumentModel,
INode as InnerNode,
- ISelection as InnerSelection,
+ ISelection,
} from '@alilc/lowcode-designer';
import { Node as ShellNode } from './node';
import { selectionSymbol } from '../symbols';
import { IPublicModelSelection, IPublicModelNode, IPublicTypeDisposable } from '@alilc/lowcode-types';
export class Selection implements IPublicModelSelection {
- private readonly [selectionSymbol]: InnerSelection;
+ private readonly [selectionSymbol]: ISelection;
constructor(document: InnerDocumentModel) {
this[selectionSymbol] = document.selection;
diff --git a/packages/shell/src/model/setting-prop-entry.ts b/packages/shell/src/model/setting-prop-entry.ts
index 88861cac4..94d39a522 100644
--- a/packages/shell/src/model/setting-prop-entry.ts
+++ b/packages/shell/src/model/setting-prop-entry.ts
@@ -1,4 +1,4 @@
-import { SettingField, SettingEntry } from '@alilc/lowcode-designer';
+import { SettingField, ISettingEntry } from '@alilc/lowcode-designer';
import {
IPublicTypeCompositeValue,
IPublicTypeFieldConfig,
@@ -233,7 +233,7 @@ export class SettingPropEntry implements IPublicModelSettingPropEntry {
* @returns
*/
getProps(): IPublicModelSettingTopEntry {
- return ShellSettingTopEntry.create(this[settingPropEntrySymbol].getProps() as SettingEntry);
+ return ShellSettingTopEntry.create(this[settingPropEntrySymbol].getProps() as ISettingEntry);
}
/**
diff --git a/packages/shell/src/model/setting-top-entry.ts b/packages/shell/src/model/setting-top-entry.ts
index ddcd16471..7947baffa 100644
--- a/packages/shell/src/model/setting-top-entry.ts
+++ b/packages/shell/src/model/setting-top-entry.ts
@@ -1,17 +1,17 @@
-import { SettingEntry } from '@alilc/lowcode-designer';
+import { ISettingEntry } from '@alilc/lowcode-designer';
import { settingTopEntrySymbol } from '../symbols';
import { Node as ShellNode } from './node';
import { SettingPropEntry as ShellSettingPropEntry } from './setting-prop-entry';
import { IPublicModelSettingTopEntry, IPublicModelNode, IPublicModelSettingPropEntry } from '@alilc/lowcode-types';
export class SettingTopEntry implements IPublicModelSettingTopEntry {
- private readonly [settingTopEntrySymbol]: SettingEntry;
+ private readonly [settingTopEntrySymbol]: ISettingEntry;
- constructor(prop: SettingEntry) {
+ constructor(prop: ISettingEntry) {
this[settingTopEntrySymbol] = prop;
}
- static create(prop: SettingEntry): IPublicModelSettingTopEntry {
+ static create(prop: ISettingEntry): IPublicModelSettingTopEntry {
return new SettingTopEntry(prop);
}
diff --git a/packages/shell/src/symbols.ts b/packages/shell/src/symbols.ts
index dac981e96..83d831747 100644
--- a/packages/shell/src/symbols.ts
+++ b/packages/shell/src/symbols.ts
@@ -31,4 +31,6 @@ export const windowSymbol = Symbol('window');
export const pluginInstanceSymbol = Symbol('plugin-instance');
export const resourceTypeSymbol = Symbol('resourceType');
export const resourceSymbol = Symbol('resource');
-export const clipboardSymbol = Symbol('clipboard');
\ No newline at end of file
+export const clipboardSymbol = Symbol('clipboard');
+export const configSymbol = Symbol('configSymbol');
+export const conditionGroupSymbol = Symbol('conditionGroup');
diff --git a/packages/types/package.json b/packages/types/package.json
index d36e7fb21..351c0311d 100644
--- a/packages/types/package.json
+++ b/packages/types/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-types",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "Types for Ali lowCode engine",
"files": [
"es",
diff --git a/packages/types/src/shell/api/canvas.ts b/packages/types/src/shell/api/canvas.ts
index b7ad196ce..6cb3df9fc 100644
--- a/packages/types/src/shell/api/canvas.ts
+++ b/packages/types/src/shell/api/canvas.ts
@@ -2,12 +2,14 @@ import { IPublicModelDragon, IPublicModelDropLocation, IPublicModelScrollTarget,
import { IPublicTypeLocationData, IPublicTypeScrollable } from '../type';
/**
+ * canvas - 画布 API
* @since v1.1.0
*/
export interface IPublicApiCanvas {
/**
* 创一个滚动控制器 Scroller,赋予一个视图滚动的基本能力,
+ *
* a Scroller is a controller that gives a view (IPublicTypeScrollable) the ability scrolling
* to some cordination by api scrollTo.
*
@@ -20,6 +22,7 @@ export interface IPublicApiCanvas {
/**
* 创建一个 ScrollTarget,与 Scroller 一起发挥作用,详见 createScroller 中的描述
+ *
* this works with Scroller, refer to createScroller`s description
* @since v1.1.0
*/
@@ -27,6 +30,7 @@ export interface IPublicApiCanvas {
/**
* 创建一个文档插入位置对象,该对象用来描述一个即将插入的节点在文档中的位置
+ *
* create a drop location for document, drop location describes a location in document
* @since v1.1.0
*/
@@ -34,6 +38,7 @@ export interface IPublicApiCanvas {
/**
* 获取拖拽操作对象的实例
+ *
* get dragon instance, you can use this to obtain draging related abilities and lifecycle hooks
* @since v1.1.0
*/
@@ -41,6 +46,7 @@ export interface IPublicApiCanvas {
/**
* 获取活动追踪器实例
+ *
* get activeTracker instance, which is a singleton running in engine.
* it tracks document`s current focusing node/node[], and notify it`s subscribers that when
* focusing node/node[] changed.
@@ -50,6 +56,7 @@ export interface IPublicApiCanvas {
/**
* 是否处于 LiveEditing 状态
+ *
* check if canvas is in liveEditing state
* @since v1.1.0
*/
@@ -57,6 +64,7 @@ export interface IPublicApiCanvas {
/**
* 获取全局剪贴板实例
+ *
* get clipboard instance
*
* @since v1.1.0
diff --git a/packages/types/src/shell/api/material.ts b/packages/types/src/shell/api/material.ts
index 19e42e4a8..7771aa6ba 100644
--- a/packages/types/src/shell/api/material.ts
+++ b/packages/types/src/shell/api/material.ts
@@ -76,8 +76,25 @@ export interface IPublicApiMaterial {
/**
* 在设计器辅助层增加一个扩展 action
+ *
* add an action button in canvas context menu area
* @param action
+ * @example
+ * ```ts
+ * import { plugins } from '@alilc/lowcode-engine';
+ * import { IPublicModelPluginContext } from '@alilc/lowcode-types';
+ *
+ * const removeCopyAction = (ctx: IPublicModelPluginContext) => {
+ * return {
+ * async init() {
+ * const { removeBuiltinComponentAction } = ctx.material;
+ * removeBuiltinComponentAction('copy');
+ * }
+ * }
+ * };
+ * removeCopyAction.pluginName = 'removeCopyAction';
+ * await plugins.register(removeCopyAction);
+ * ```
*/
addBuiltinComponentAction(action: IPublicTypeComponentAction): void;
diff --git a/packages/types/src/shell/api/plugins.ts b/packages/types/src/shell/api/plugins.ts
index 518362b97..a93016290 100644
--- a/packages/types/src/shell/api/plugins.ts
+++ b/packages/types/src/shell/api/plugins.ts
@@ -13,6 +13,11 @@ export interface IPluginPreferenceMananger {
export type PluginOptionsType = string | number | boolean | object;
export interface IPublicApiPlugins {
+ /**
+ * 可以通过 plugin api 获取其他插件 export 导出的内容
+ */
+ [key: string]: any;
+
register(
pluginModel: IPublicTypePlugin,
options?: Record,
@@ -21,6 +26,7 @@ export interface IPublicApiPlugins {
/**
* 引擎初始化时可以提供全局配置给到各插件,通过这个方法可以获得本插件对应的配置
+ *
* use this to get preference config for this plugin when engine.init() called
*/
getPluginPreference(
@@ -29,24 +35,28 @@ export interface IPublicApiPlugins {
/**
* 获取指定插件
+ *
* get plugin instance by name
*/
get(pluginName: string): IPublicModelPluginInstance | null;
/**
* 获取所有的插件实例
+ *
* get all plugin instances
*/
getAll(): IPublicModelPluginInstance[];
/**
* 判断是否有指定插件
+ *
* check if plugin with certain name exists
*/
has(pluginName: string): boolean;
/**
* 删除指定插件
+ *
* delete plugin instance by name
*/
delete(pluginName: string): void;
diff --git a/packages/types/src/shell/api/workspace.ts b/packages/types/src/shell/api/workspace.ts
index 8f8b16e39..c1be4cb7e 100644
--- a/packages/types/src/shell/api/workspace.ts
+++ b/packages/types/src/shell/api/workspace.ts
@@ -1,7 +1,9 @@
import { IPublicModelWindow } from '../model';
import { IPublicApiPlugins, IPublicModelResource, IPublicResourceList, IPublicTypeDisposable, IPublicTypeResourceType } from '@alilc/lowcode-types';
-export interface IPublicApiWorkspace {
+export interface IPublicApiWorkspace<
+ Plugins = IPublicApiPlugins
+> {
/** 是否启用 workspace 模式 */
isActive: boolean;
@@ -9,7 +11,7 @@ export interface IPublicApiWorkspace {
/** 当前设计器窗口 */
window: IPublicModelWindow;
- plugins: IPublicApiPlugins;
+ plugins: Plugins;
/** 当前设计器的编辑窗口 */
windows: IPublicModelWindow[];
diff --git a/packages/types/src/shell/model/component-meta.ts b/packages/types/src/shell/model/component-meta.ts
index 369680fd6..f2b0032a7 100644
--- a/packages/types/src/shell/model/component-meta.ts
+++ b/packages/types/src/shell/model/component-meta.ts
@@ -2,7 +2,9 @@ import { IPublicTypeNodeSchema, IPublicTypeNodeData, IPublicTypeIconType, IPubli
import { ReactElement } from 'react';
import { IPublicModelNode } from './node';
-export interface IPublicModelComponentMeta {
+export interface IPublicModelComponentMeta<
+ Node = IPublicModelNode
+> {
/**
* 组件名
@@ -92,7 +94,7 @@ export interface IPublicModelComponentMeta {
* @param my 当前节点
* @param parent 父节点
*/
- checkNestingUp(my: IPublicModelNode | IPublicTypeNodeData, parent: any): boolean;
+ checkNestingUp(my: Node | IPublicTypeNodeData, parent: any): boolean;
/**
* 检测目标节点是否可被放置在父节点中
@@ -101,8 +103,8 @@ export interface IPublicModelComponentMeta {
* @param parent 父节点
*/
checkNestingDown(
- my: IPublicModelNode | IPublicTypeNodeData,
- target: IPublicTypeNodeSchema | IPublicModelNode | IPublicTypeNodeSchema[],
+ my: Node | IPublicTypeNodeData,
+ target: IPublicTypeNodeSchema | Node | IPublicTypeNodeSchema[],
): boolean;
/**
diff --git a/packages/types/src/shell/model/detecting.ts b/packages/types/src/shell/model/detecting.ts
index 9bc215f9a..ec6320ad2 100644
--- a/packages/types/src/shell/model/detecting.ts
+++ b/packages/types/src/shell/model/detecting.ts
@@ -1,7 +1,7 @@
import { IPublicModelNode } from './';
import { IPublicTypeDisposable } from '../type';
-export interface IPublicModelDetecting {
+export interface IPublicModelDetecting {
/**
* 是否启用
@@ -15,7 +15,7 @@ export interface IPublicModelDetecting {
* get current hovering node
* @since v1.0.16
*/
- get current(): IPublicModelNode | null;
+ get current(): Node | null;
/**
* hover 指定节点
@@ -42,5 +42,5 @@ export interface IPublicModelDetecting {
* set callback which will be called when hovering object changed.
* @since v1.1.0
*/
- onDetectingChange(fn: (node: IPublicModelNode | null) => void): IPublicTypeDisposable;
+ onDetectingChange(fn: (node: Node | null) => void): IPublicTypeDisposable;
}
diff --git a/packages/types/src/shell/model/document-model.ts b/packages/types/src/shell/model/document-model.ts
index 72d20ceb0..b11ac6f08 100644
--- a/packages/types/src/shell/model/document-model.ts
+++ b/packages/types/src/shell/model/document-model.ts
@@ -2,15 +2,22 @@ import { IPublicTypeRootSchema, IPublicTypeDragNodeDataObject, IPublicTypeDragNo
import { IPublicEnumTransformStage } from '../enum';
import { IPublicApiProject } from '../api';
import { IPublicModelDropLocation, IPublicModelDetecting, IPublicModelNode, IPublicModelSelection, IPublicModelHistory, IPublicModelModalNodesManager } from './';
-import { IPublicTypeOnChangeOptions } from '@alilc/lowcode-types';
+import { IPublicTypeNodeData, IPublicTypeNodeSchema, IPublicTypeOnChangeOptions } from '@alilc/lowcode-types';
-export interface IPublicModelDocumentModel {
+export interface IPublicModelDocumentModel<
+ Selection = IPublicModelSelection,
+ History = IPublicModelHistory,
+ Node = IPublicModelNode,
+ DropLocation = IPublicModelDropLocation,
+ ModalNodesManager = IPublicModelModalNodesManager,
+ Project = IPublicApiProject
+> {
/**
* 节点选中区模型实例
* instance of selection
*/
- selection: IPublicModelSelection;
+ selection: Selection;
/**
* 画布节点 hover 区模型实例
@@ -22,7 +29,7 @@ export interface IPublicModelDocumentModel {
* 操作历史模型实例
* instance of history
*/
- history: IPublicModelHistory;
+ history: History;
/**
* id
@@ -36,30 +43,30 @@ export interface IPublicModelDocumentModel {
* get project which this documentModel belongs to
* @returns
*/
- get project(): IPublicApiProject;
+ get project(): Project;
/**
* 获取文档的根节点
* root node of this documentModel
* @returns
*/
- get root(): IPublicModelNode | null;
+ get root(): Node | null;
- get focusNode(): IPublicModelNode | null;
+ get focusNode(): Node | null;
- set focusNode(node: IPublicModelNode | null);
+ set focusNode(node: Node | null);
/**
* 获取文档下所有节点
* @returns
*/
- get nodesMap(): Map;
+ get nodesMap(): Map;
/**
* 模态节点管理
* get instance of modalNodesManager
*/
- get modalNodesManager(): IPublicModelModalNodesManager | null;
+ get modalNodesManager(): ModalNodesManager | null;
/**
* 根据 nodeId 返回 Node 实例
@@ -67,7 +74,7 @@ export interface IPublicModelDocumentModel {
* @param nodeId
* @returns
*/
- getNodeById(nodeId: string): IPublicModelNode | null;
+ getNodeById(nodeId: string): Node | null;
/**
* 导入 schema
@@ -89,11 +96,11 @@ export interface IPublicModelDocumentModel {
* insert a node
*/
insertNode(
- parent: IPublicModelNode,
- thing: IPublicModelNode,
+ parent: Node,
+ thing: Node | IPublicTypeNodeData,
at?: number | null | undefined,
copy?: boolean | undefined
- ): IPublicModelNode | null;
+ ): Node | null;
/**
* 创建一个节点
@@ -101,14 +108,14 @@ export interface IPublicModelDocumentModel {
* @param data
* @returns
*/
- createNode(data: any): IPublicModelNode | null;
+ createNode(data: IPublicTypeNodeSchema): Node | null;
/**
* 移除指定节点/节点id
* remove a node by node instance or nodeId
* @param idOrNode
*/
- removeNode(idOrNode: string | IPublicModelNode): void;
+ removeNode(idOrNode: string | Node): void;
/**
* componentsMap of documentModel
@@ -126,7 +133,7 @@ export interface IPublicModelDocumentModel {
* @since v1.0.16
*/
checkNesting(
- dropTarget: IPublicModelNode,
+ dropTarget: Node,
dragObject: IPublicTypeDragNodeObject | IPublicTypeDragNodeDataObject
): boolean;
@@ -134,26 +141,26 @@ export interface IPublicModelDocumentModel {
* 当前 document 新增节点事件
* set callback for event on node is created for a document
*/
- onAddNode(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable;
+ onAddNode(fn: (node: Node) => void): IPublicTypeDisposable;
/**
* 当前 document 新增节点事件,此时节点已经挂载到 document 上
* set callback for event on node is mounted to canvas
*/
- onMountNode(fn: (payload: { node: IPublicModelNode }) => void): IPublicTypeDisposable;
+ onMountNode(fn: (payload: { node: Node }) => void): IPublicTypeDisposable;
/**
* 当前 document 删除节点事件
* set callback for event on node is removed
*/
- onRemoveNode(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable;
+ onRemoveNode(fn: (node: Node) => void): IPublicTypeDisposable;
/**
* 当前 document 的 hover 变更事件
*
* set callback for event on detecting changed
*/
- onChangeDetecting(fn: (node: IPublicModelNode) => void): IPublicTypeDisposable;
+ onChangeDetecting(fn: (node: Node) => void): IPublicTypeDisposable;
/**
* 当前 document 的选中变更事件
@@ -166,19 +173,19 @@ export interface IPublicModelDocumentModel {
* set callback for event on visibility changed for certain node
* @param fn
*/
- onChangeNodeVisible(fn: (node: IPublicModelNode, visible: boolean) => void): IPublicTypeDisposable;
+ onChangeNodeVisible(fn: (node: Node, visible: boolean) => void): IPublicTypeDisposable;
/**
* 当前 document 的节点 children 变更事件
* @param fn
*/
- onChangeNodeChildren(fn: (info: IPublicTypeOnChangeOptions) => void): IPublicTypeDisposable;
+ onChangeNodeChildren(fn: (info: IPublicTypeOnChangeOptions) => void): IPublicTypeDisposable;
/**
* 当前 document 节点属性修改事件
* @param fn
*/
- onChangeNodeProp(fn: (info: IPublicTypePropChangeOptions) => void): IPublicTypeDisposable;
+ onChangeNodeProp(fn: (info: IPublicTypePropChangeOptions) => void): IPublicTypeDisposable;
/**
* import schema event
@@ -193,21 +200,21 @@ export interface IPublicModelDocumentModel {
* @param node
* @since v1.1.0
*/
- isDetectingNode(node: IPublicModelNode): boolean;
+ isDetectingNode(node: Node): boolean;
/**
* 获取当前的 DropLocation 信息
* get current drop location
* @since v1.1.0
*/
- get dropLocation(): IPublicModelDropLocation | null;
+ get dropLocation(): DropLocation | null;
/**
* 设置当前的 DropLocation 信息
* set current drop location
* @since v1.1.0
*/
- set dropLocation(loc: IPublicModelDropLocation | null);
+ set dropLocation(loc: DropLocation | null);
/**
* 设置聚焦节点变化的回调
@@ -216,7 +223,7 @@ export interface IPublicModelDocumentModel {
* @since v1.1.0
*/
onFocusNodeChanged(
- fn: (doc: IPublicModelDocumentModel, focusNode: IPublicModelNode) => void,
+ fn: (doc: IPublicModelDocumentModel, focusNode: Node) => void,
): IPublicTypeDisposable;
/**
diff --git a/packages/types/src/shell/model/engine-config.ts b/packages/types/src/shell/model/engine-config.ts
index 2b17d7e72..c9473cd12 100644
--- a/packages/types/src/shell/model/engine-config.ts
+++ b/packages/types/src/shell/model/engine-config.ts
@@ -1,3 +1,4 @@
+import { IPublicTypeDisposable } from '../type';
import { IPublicModelPreference } from './';
export interface IPublicModelEngineConfig {
@@ -52,7 +53,7 @@ export interface IPublicModelEngineConfig {
* @param fn
* @returns
*/
- onGot(key: string, fn: (data: any) => void): () => void;
+ onGot(key: string, fn: (data: any) => void): IPublicTypeDisposable;
/**
* 获取全局 Preference, 用于管理全局浏览器侧用户 Preference,如 Panel 是否钉住
diff --git a/packages/types/src/shell/model/exclusive-group.ts b/packages/types/src/shell/model/exclusive-group.ts
index 1fbfe089a..b930a1344 100644
--- a/packages/types/src/shell/model/exclusive-group.ts
+++ b/packages/types/src/shell/model/exclusive-group.ts
@@ -1,8 +1,10 @@
-import { IPublicModelNode } from '..';
+import { IPublicModelNode, IPublicTypeTitleContent } from '..';
-export interface IPublicModelExclusiveGroup {
- readonly id: string;
- readonly title: string;
- get firstNode(): IPublicModelNode;
+export interface IPublicModelExclusiveGroup<
+ Node = IPublicModelNode,
+> {
+ readonly id: string | undefined;
+ readonly title: IPublicTypeTitleContent | undefined;
+ get firstNode(): Node | null;
setVisible(node: Node): void;
}
diff --git a/packages/types/src/shell/model/modal-nodes-manager.ts b/packages/types/src/shell/model/modal-nodes-manager.ts
index bfbba50ec..07656c070 100644
--- a/packages/types/src/shell/model/modal-nodes-manager.ts
+++ b/packages/types/src/shell/model/modal-nodes-manager.ts
@@ -1,6 +1,6 @@
import { IPublicModelNode } from './';
-export interface IPublicModelModalNodesManager {
+export interface IPublicModelModalNodesManager {
/**
* 设置模态节点,触发内部事件
@@ -12,13 +12,13 @@ export interface IPublicModelModalNodesManager {
* 获取模态节点(们)
* get modal nodes
*/
- getModalNodes(): IPublicModelNode[];
+ getModalNodes(): Node[];
/**
* 获取当前可见的模态节点
* get current visible modal node
*/
- getVisibleModalNode(): IPublicModelNode | null;
+ getVisibleModalNode(): Node | null;
/**
* 隐藏模态节点(们)
@@ -31,12 +31,12 @@ export interface IPublicModelModalNodesManager {
* set specfic model node as visible
* @param node Node
*/
- setVisible(node: IPublicModelNode): void;
+ setVisible(node: Node): void;
/**
* 设置指定节点为不可见态
* set specfic model node as invisible
* @param node Node
*/
- setInvisible(node: IPublicModelNode): void;
+ setInvisible(node: Node): void;
}
diff --git a/packages/types/src/shell/model/node-children.ts b/packages/types/src/shell/model/node-children.ts
index d18bf78a3..f2be13250 100644
--- a/packages/types/src/shell/model/node-children.ts
+++ b/packages/types/src/shell/model/node-children.ts
@@ -2,13 +2,15 @@ import { IPublicTypeNodeSchema, IPublicTypeNodeData } from '../type';
import { IPublicEnumTransformStage } from '../enum';
import { IPublicModelNode } from './';
-export interface IPublicModelNodeChildren {
+export interface IPublicModelNodeChildren<
+ Node = IPublicModelNode
+> {
/**
* 返回当前 children 实例所属的节点实例
* get owner node of this nodeChildren
*/
- get owner(): IPublicModelNode | null;
+ get owner(): Node | null;
/**
* children 内的节点实例数
@@ -25,6 +27,7 @@ export interface IPublicModelNodeChildren {
/**
* 是否为空
+ *
* @returns
*/
get isEmptyNode(): boolean;
@@ -42,104 +45,121 @@ export interface IPublicModelNodeChildren {
/**
* 删除指定节点
+ *
* delete the node
* @param node
*/
- delete(node: IPublicModelNode): boolean;
+ delete(node: Node): boolean;
/**
* 插入一个节点
+ *
* insert a node at specific position
* @param node 待插入节点
* @param at 插入下标
* @returns
*/
- insert(node: IPublicModelNode, at?: number | null): void;
+ insert(node: Node, at?: number | null): void;
/**
* 返回指定节点的下标
+ *
* get index of node in current children
* @param node
* @returns
*/
- indexOf(node: IPublicModelNode): number;
+ indexOf(node: Node): number;
/**
* 类似数组 splice 操作
+ *
* provide the same function with {Array.prototype.splice}
* @param start
* @param deleteCount
* @param node
*/
- splice(start: number, deleteCount: number, node?: IPublicModelNode): any;
+ splice(start: number, deleteCount: number, node?: Node): any;
/**
* 返回指定下标的节点
+ *
* get node with index
* @param index
* @returns
*/
- get(index: number): IPublicModelNode | null;
+ get(index: number): Node | null;
/**
* 是否包含指定节点
+ *
* check if node exists in current children
* @param node
* @returns
*/
- has(node: IPublicModelNode): boolean;
+ has(node: Node): boolean;
/**
* 类似数组的 forEach
+ *
* provide the same function with {Array.prototype.forEach}
* @param fn
*/
- forEach(fn: (node: IPublicModelNode, index: number) => void): void;
+ forEach(fn: (node: Node, index: number) => void): void;
+
+ /**
+ * 类似数组的 reverse
+ *
+ * provide the same function with {Array.prototype.reverse}
+ */
+ reverse(): Node[];
/**
* 类似数组的 map
+ *
* provide the same function with {Array.prototype.map}
* @param fn
*/
- map(fn: (node: IPublicModelNode, index: number) => T[]): any[] | null;
+ map(fn: (node: Node, index: number) => T): T[] | null;
/**
* 类似数组的 every
* provide the same function with {Array.prototype.every}
* @param fn
*/
- every(fn: (node: IPublicModelNode, index: number) => boolean): boolean;
+ every(fn: (node: Node, index: number) => boolean): boolean;
/**
* 类似数组的 some
* provide the same function with {Array.prototype.some}
* @param fn
*/
- some(fn: (node: IPublicModelNode, index: number) => boolean): boolean;
+ some(fn: (node: Node, index: number) => boolean): boolean;
/**
* 类似数组的 filter
* provide the same function with {Array.prototype.filter}
* @param fn
*/
- filter(fn: (node: IPublicModelNode, index: number) => boolean): any;
+ filter(fn: (node: Node, index: number) => boolean): any;
/**
* 类似数组的 find
* provide the same function with {Array.prototype.find}
* @param fn
*/
- find(fn: (node: IPublicModelNode, index: number) => boolean): IPublicModelNode | null;
+ find(fn: (node: Node, index: number) => boolean): Node | null | undefined;
/**
* 类似数组的 reduce
+ *
* provide the same function with {Array.prototype.reduce}
* @param fn
*/
- reduce(fn: (acc: any, cur: IPublicModelNode) => any, initialValue: any): void;
+ reduce(fn: (acc: any, cur: Node) => any, initialValue: any): void;
/**
* 导入 schema
+ *
* import schema
* @param data
*/
@@ -147,6 +167,7 @@ export interface IPublicModelNodeChildren {
/**
* 导出 schema
+ *
* export schema
* @param stage
*/
@@ -154,15 +175,16 @@ export interface IPublicModelNodeChildren {
/**
* 执行新增、删除、排序等操作
+ *
* excute remove/add/sort operations
* @param remover
* @param adder
* @param sorter
*/
mergeChildren(
- remover: (node: IPublicModelNode, idx: number) => boolean,
- adder: (children: IPublicModelNode[]) => IPublicTypeNodeData[] | null,
- sorter: (firstNode: IPublicModelNode, secondNode: IPublicModelNode) => number
+ remover: (node: Node, idx: number) => boolean,
+ adder: (children: Node[]) => IPublicTypeNodeData[] | null,
+ sorter: (firstNode: Node, secondNode: Node) => number
): any;
}
diff --git a/packages/types/src/shell/model/node.ts b/packages/types/src/shell/model/node.ts
index 70c92016b..ba924d6d5 100644
--- a/packages/types/src/shell/model/node.ts
+++ b/packages/types/src/shell/model/node.ts
@@ -3,7 +3,16 @@ import { IPublicTypeNodeSchema, IPublicTypeIconType, IPublicTypeI18nData, IPubli
import { IPublicEnumTransformStage } from '../enum';
import { IPublicModelNodeChildren, IPublicModelComponentMeta, IPublicModelProp, IPublicModelProps, IPublicModelSettingTopEntry, IPublicModelDocumentModel, IPublicModelExclusiveGroup } from './';
-export interface IPublicModelNode {
+export interface IBaseModelNode<
+ Document = IPublicModelDocumentModel,
+ Node = IPublicModelNode,
+ NodeChildren = IPublicModelNodeChildren,
+ ComponentMeta = IPublicModelComponentMeta,
+ SettingTopEntry = IPublicModelSettingTopEntry,
+ Props = IPublicModelProps,
+ Prop = IPublicModelProp,
+ ExclusiveGroup = IPublicModelExclusiveGroup
+> {
/**
* 节点 id
@@ -161,7 +170,7 @@ export interface IPublicModelNode {
* 下标
* index
*/
- get index(): number;
+ get index(): number | undefined;
/**
* 图标
@@ -185,55 +194,55 @@ export interface IPublicModelNode {
* 节点的物料元数据
* get component meta of this node
*/
- get componentMeta(): IPublicModelComponentMeta | null;
+ get componentMeta(): ComponentMeta | null;
/**
* 获取节点所属的文档模型对象
* get documentModel of this node
*/
- get document(): IPublicModelDocumentModel | null;
+ get document(): Document | null;
/**
* 获取当前节点的前一个兄弟节点
* get previous sibling of this node
*/
- get prevSibling(): IPublicModelNode | null;
+ get prevSibling(): Node | null | undefined;
/**
* 获取当前节点的后一个兄弟节点
* get next sibling of this node
*/
- get nextSibling(): IPublicModelNode | null;
+ get nextSibling(): Node | null | undefined;
/**
* 获取当前节点的父亲节点
* get parent of this node
*/
- get parent(): IPublicModelNode | null;
+ get parent(): Node | null;
/**
* 获取当前节点的孩子节点模型
* get children of this node
*/
- get children(): IPublicModelNodeChildren | null;
+ get children(): NodeChildren | null;
/**
* 节点上挂载的插槽节点们
* get slots of this node
*/
- get slots(): IPublicModelNode[];
+ get slots(): Node[];
/**
* 当前节点为插槽节点时,返回节点对应的属性实例
* return coresponding prop when this node is a slot node
*/
- get slotFor(): IPublicModelProp | null;
+ get slotFor(): Prop | null | undefined;
/**
* 返回节点的属性集
* get props
*/
- get props(): IPublicModelProps | null;
+ get props(): Props | null;
/**
* 返回节点的属性集
@@ -244,7 +253,7 @@ export interface IPublicModelNode {
/**
* get conditionGroup
*/
- get conditionGroup(): IPublicModelExclusiveGroup | null;
+ get conditionGroup(): ExclusiveGroup | null;
/**
* 获取符合搭建协议 - 节点 schema 结构
@@ -258,7 +267,7 @@ export interface IPublicModelNode {
* get setting entry of this node
* @since v1.1.0
*/
- get settingEntry(): IPublicModelSettingTopEntry;
+ get settingEntry(): SettingTopEntry;
/**
* 返回节点的尺寸、位置信息
@@ -289,7 +298,7 @@ export interface IPublicModelNode {
* get prop by path
* @param path 属性路径,支持 a / a.b / a.0 等格式
*/
- getProp(path: string, createIfNone: boolean): IPublicModelProp | null;
+ getProp(path: string, createIfNone: boolean): Prop | null;
/**
* 获取指定 path 的属性模型实例值
@@ -307,7 +316,7 @@ export interface IPublicModelNode {
* @param path 属性路径,支持 a / a.b / a.0 等格式
* @param createIfNone 当没有属性的时候,是否创建一个属性
*/
- getExtraProp(path: string, createIfNone?: boolean): IPublicModelProp | null;
+ getExtraProp(path: string, createIfNone?: boolean): Prop | null;
/**
* 获取指定 path 的属性模型实例,
@@ -359,8 +368,8 @@ export interface IPublicModelNode {
* @param useMutator
*/
insertBefore(
- node: IPublicModelNode,
- ref?: IPublicModelNode | undefined,
+ node: Node,
+ ref?: Node | undefined,
useMutator?: boolean,
): void;
@@ -372,8 +381,8 @@ export interface IPublicModelNode {
* @param useMutator
*/
insertAfter(
- node: IPublicModelNode,
- ref?: IPublicModelNode | undefined,
+ node: Node,
+ ref?: Node | undefined,
useMutator?: boolean,
): void;
@@ -384,7 +393,7 @@ export interface IPublicModelNode {
* @param data 用作替换的节点对象或者节点描述
* @returns
*/
- replaceChild(node: IPublicModelNode, data: any): IPublicModelNode | null;
+ replaceChild(node: Node, data: any): Node | null;
/**
* 将当前节点替换成指定节点描述
@@ -427,9 +436,9 @@ export interface IPublicModelNode {
* @since v1.1.0
*/
mergeChildren(
- remover: (node: IPublicModelNode, idx: number) => boolean,
- adder: (children: IPublicModelNode[]) => any,
- sorter: (firstNode: IPublicModelNode, secondNode: IPublicModelNode) => number
+ remover: (node: Node, idx: number) => boolean,
+ adder: (children: Node[]) => any,
+ sorter: (firstNode: Node, secondNode: Node) => number
): any;
/**
@@ -438,7 +447,7 @@ export interface IPublicModelNode {
* @param node
* @since v1.1.0
*/
- contains(node: IPublicModelNode): boolean;
+ contains(node: Node): boolean;
/**
* 是否可执行某 action
@@ -475,4 +484,11 @@ export interface IPublicModelNode {
* @since v1.1.0
*/
setConditionalVisible(): void;
+
+ /**
+ * 获取节点实例对应的 dom 节点
+ */
+ getDOMNode(): HTMLElement;
}
+
+export interface IPublicModelNode extends IBaseModelNode {}
\ No newline at end of file
diff --git a/packages/types/src/shell/model/plugin-context.ts b/packages/types/src/shell/model/plugin-context.ts
index ae7ef8bbb..5d97b5472 100644
--- a/packages/types/src/shell/model/plugin-context.ts
+++ b/packages/types/src/shell/model/plugin-context.ts
@@ -10,6 +10,7 @@ import {
IPublicApiCanvas,
IPluginPreferenceMananger,
IPublicApiPlugins,
+ IPublicApiWorkspace,
} from '../api';
import { IPublicModelEngineConfig } from './';
@@ -28,31 +29,88 @@ export interface IPublicModelPluginContext {
* by using this, init options can be accessed from inside plugin
*/
preference: IPluginPreferenceMananger;
+
+ /**
+ * skeleton API
+ * @tutorial https://lowcode-engine.cn/site/docs/api/skeleton
+ */
get skeleton(): IPublicApiSkeleton;
+
+ /**
+ * hotkey API
+ * @tutorial https://lowcode-engine.cn/site/docs/api/hotkey
+ */
get hotkey(): IPublicApiHotkey;
+
+ /**
+ * setter API
+ * @tutorial https://lowcode-engine.cn/site/docs/api/setters
+ */
get setters(): IPublicApiSetters;
+
+ /**
+ * config API
+ * @tutorial https://lowcode-engine.cn/site/docs/api/config
+ */
get config(): IPublicModelEngineConfig;
+
+ /**
+ * material API
+ * @tutorial https://lowcode-engine.cn/site/docs/api/material
+ */
get material(): IPublicApiMaterial;
/**
+ * event API
* this event works globally, can be used between plugins and engine.
+ * @tutorial https://lowcode-engine.cn/site/docs/api/event
*/
get event(): IPublicApiEvent;
+
+ /**
+ * project API
+ * @tutorial https://lowcode-engine.cn/site/docs/api/project
+ */
get project(): IPublicApiProject;
+
+ /**
+ * common API
+ * @tutorial https://lowcode-engine.cn/site/docs/api/common
+ */
get common(): IPublicApiCommon;
+
+ /**
+ * plugins API
+ * @tutorial https://lowcode-engine.cn/site/docs/api/plugins
+ */
get plugins(): IPublicApiPlugins;
+
+ /**
+ * logger API
+ * @tutorial https://lowcode-engine.cn/site/docs/api/logger
+ */
get logger(): IPublicApiLogger;
/**
* this event works within current plugin, on an emit locally.
+ * @tutorial https://lowcode-engine.cn/site/docs/api/event
*/
get pluginEvent(): IPublicApiEvent;
+
+ /**
+ * canvas API
+ * @tutorial https://lowcode-engine.cn/site/docs/api/canvas
+ */
get canvas(): IPublicApiCanvas;
+
+ /**
+ * workspace API
+ * @tutorial https://lowcode-engine.cn/site/docs/api/workspace
+ */
+ get workspace(): IPublicApiWorkspace;
}
/**
- *
- *
* @deprecated please use IPublicModelPluginContext instead
*/
export interface ILowCodePluginContext extends IPublicModelPluginContext {
diff --git a/packages/types/src/shell/model/prop.ts b/packages/types/src/shell/model/prop.ts
index 7ac906762..71442e64a 100644
--- a/packages/types/src/shell/model/prop.ts
+++ b/packages/types/src/shell/model/prop.ts
@@ -2,7 +2,9 @@ import { IPublicEnumTransformStage } from '../enum';
import { IPublicTypeCompositeValue } from '../type';
import { IPublicModelNode } from './';
-export interface IPublicModelProp {
+export interface IPublicModelProp<
+ Node = IPublicModelNode
+> {
/**
* id
@@ -25,14 +27,14 @@ export interface IPublicModelProp {
* 返回所属的节点实例
* get node instance, which this prop belongs to
*/
- get node(): IPublicModelNode | null;
+ get node(): Node | null;
/**
* 当本 prop 代表一个 Slot 时,返回对应的 slotNode
* return the slot node (only if the current prop represents a slot)
* @since v1.1.0
*/
- get slotNode(): IPublicModelNode | undefined | null;
+ get slotNode(): Node | undefined | null;
/**
* 是否是 Prop , 固定返回 true
diff --git a/packages/types/src/shell/model/props.ts b/packages/types/src/shell/model/props.ts
index 9c862a008..3c390a8cf 100644
--- a/packages/types/src/shell/model/props.ts
+++ b/packages/types/src/shell/model/props.ts
@@ -1,7 +1,9 @@
import { IPublicTypeCompositeValue } from '../type';
-import { IPublicModelNode, IPublicModelProp } from './';
+import { IPublicModelNode } from './';
-export interface IPublicModelProps {
+export interface IBaseModelProps<
+ Prop
+> {
/**
* id
@@ -24,7 +26,7 @@ export interface IPublicModelProps {
* get prop by path
* @param path 属性路径,支持 a / a.b / a.0 等格式
*/
- getProp(path: string): IPublicModelProp | null;
+ getProp(path: string): Prop | null;
/**
* 获取指定 path 的属性模型实例值
@@ -39,7 +41,7 @@ export interface IPublicModelProps {
* get extra prop by path
* @param path 属性路径,支持 a / a.b / a.0 等格式
*/
- getExtraProp(path: string): IPublicModelProp | null;
+ getExtraProp(path: string): Prop | null;
/**
* 获取指定 path 的属性模型实例值
@@ -83,3 +85,5 @@ export interface IPublicModelProps {
add(value: IPublicTypeCompositeValue, key?: string | number | undefined): any;
}
+
+export type IPublicModelProps = IBaseModelProps;
\ No newline at end of file
diff --git a/packages/types/src/shell/model/resource.ts b/packages/types/src/shell/model/resource.ts
index 5f26c8d7b..c81776659 100644
--- a/packages/types/src/shell/model/resource.ts
+++ b/packages/types/src/shell/model/resource.ts
@@ -5,11 +5,15 @@ export interface IPublicModelResource {
get icon(): ReactElement | undefined;
- get options(): Object;
+ get options(): Record;
get name(): string | undefined;
get type(): string | undefined;
get category(): string | undefined;
+
+ get children(): IPublicModelResource[];
+
+ get viewName(): string | undefined;
}
\ No newline at end of file
diff --git a/packages/types/src/shell/model/selection.ts b/packages/types/src/shell/model/selection.ts
index 3b2fdb2f5..317a49837 100644
--- a/packages/types/src/shell/model/selection.ts
+++ b/packages/types/src/shell/model/selection.ts
@@ -1,7 +1,9 @@
import { IPublicModelNode } from './';
import { IPublicTypeDisposable } from '../type';
-export interface IPublicModelSelection {
+export interface IPublicModelSelection<
+ Node = IPublicModelNode
+> {
/**
* 返回选中的节点 id
@@ -14,7 +16,7 @@ export interface IPublicModelSelection {
* return selected Node instance,return the first one if multiple nodes are selected
* @since v1.1.0
*/
- get node(): IPublicModelNode | null;
+ get node(): Node | null;
/**
* 选中指定节点(覆盖方式)
@@ -62,7 +64,7 @@ export interface IPublicModelSelection {
* 获取选中的节点实例
* get selected nodes
*/
- getNodes(): IPublicModelNode[];
+ getNodes(): Node[];
/**
* 获取选区的顶层节点
@@ -72,7 +74,7 @@ export interface IPublicModelSelection {
* getTopNodes() will return [A, B], subA will be removed
* @since v1.0.16
*/
- getTopNodes(includeRoot?: boolean): IPublicModelNode[];
+ getTopNodes(includeRoot?: boolean): Node[];
/**
* 注册 selection 变化事件回调
diff --git a/packages/types/src/shell/type/drag-node-object.ts b/packages/types/src/shell/type/drag-node-object.ts
index ec375cbc0..21f14b2bc 100644
--- a/packages/types/src/shell/type/drag-node-object.ts
+++ b/packages/types/src/shell/type/drag-node-object.ts
@@ -1,7 +1,7 @@
import { IPublicModelNode } from '..';
import { IPublicEnumDragObjectType } from '../enum';
-export interface IPublicTypeDragNodeObject {
+export interface IPublicTypeDragNodeObject {
type: IPublicEnumDragObjectType.Node;
- nodes: IPublicModelNode[];
+ nodes: Node[];
}
diff --git a/packages/types/src/shell/type/field-config.ts b/packages/types/src/shell/type/field-config.ts
index 32b40f157..bd09e7b90 100644
--- a/packages/types/src/shell/type/field-config.ts
+++ b/packages/types/src/shell/type/field-config.ts
@@ -13,7 +13,7 @@ export interface IPublicTypeFieldConfig extends IPublicTypeFieldExtraProps {
/**
* the name of this setting field, which used in quickEditor
*/
- name: string | number;
+ name?: string | number;
/**
* the field title
diff --git a/packages/types/src/shell/type/field-extra-props.ts b/packages/types/src/shell/type/field-extra-props.ts
index 948411c89..79466389e 100644
--- a/packages/types/src/shell/type/field-extra-props.ts
+++ b/packages/types/src/shell/type/field-extra-props.ts
@@ -1,59 +1,72 @@
-import { IPublicModelSettingTarget } from '../model';
+import { IPublicModelSettingPropEntry, IPublicModelSettingTarget } from '../model';
import { IPublicTypeLiveTextEditingConfig } from './';
/**
* extra props for field
*/
export interface IPublicTypeFieldExtraProps {
+
/**
* 是否必填参数
*/
isRequired?: boolean;
+
/**
* default value of target prop for setter use
*/
defaultValue?: any;
+
/**
* get value for field
*/
- getValue?: (target: IPublicModelSettingTarget, fieldValue: any) => any;
+ getValue?: (target: IPublicModelSettingPropEntry, fieldValue: any) => any;
+
/**
* set value for field
*/
- setValue?: (target: IPublicModelSettingTarget, value: any) => void;
+ setValue?: (target: IPublicModelSettingPropEntry, value: any) => void;
+
/**
* the field conditional show, is not set always true
* @default undefined
*/
- condition?: (target: IPublicModelSettingTarget) => boolean;
+ condition?: (target: IPublicModelSettingPropEntry) => boolean;
+
/**
* autorun when something change
*/
autorun?: (target: IPublicModelSettingTarget) => void;
+
/**
* is this field is a virtual field that not save to schema
*/
virtual?: (target: IPublicModelSettingTarget) => boolean;
+
/**
* default collapsed when display accordion
*/
defaultCollapsed?: boolean;
+
/**
* important field
*/
important?: boolean;
+
/**
* internal use
*/
forceInline?: number;
+
/**
* 是否支持变量配置
*/
supportVariable?: boolean;
+
/**
* compatiable vision display
*/
display?: 'accordion' | 'inline' | 'block' | 'plain' | 'popup' | 'entry';
+
// @todo 这个 omit 是否合理?
/**
* @todo 待补充文档
diff --git a/packages/types/src/shell/type/metadata.ts b/packages/types/src/shell/type/metadata.ts
index 918074c58..39022d48f 100644
--- a/packages/types/src/shell/type/metadata.ts
+++ b/packages/types/src/shell/type/metadata.ts
@@ -1,5 +1,5 @@
import { IPublicTypePropType, IPublicTypeComponentAction } from './';
-import { IPublicModelProp, IPublicModelSettingTarget } from '../model';
+import { IPublicModelNode, IPublicModelProp, IPublicModelSettingTarget } from '../model';
/**
* 嵌套控制函数
@@ -184,20 +184,20 @@ export interface ConfigureSupport {
*/
export interface IPublicTypeCallbacks {
// hooks
- onMouseDownHook?: (e: MouseEvent, currentNode: any) => any;
- onDblClickHook?: (e: MouseEvent, currentNode: any) => any;
- onClickHook?: (e: MouseEvent, currentNode: any) => any;
+ onMouseDownHook?: (e: MouseEvent, currentNode: IPublicModelNode) => any;
+ onDblClickHook?: (e: MouseEvent, currentNode: IPublicModelNode) => any;
+ onClickHook?: (e: MouseEvent, currentNode: IPublicModelNode) => any;
// onLocateHook?: (e: any, currentNode: any) => any;
// onAcceptHook?: (currentNode: any, locationData: any) => any;
- onMoveHook?: (currentNode: any) => boolean;
+ onMoveHook?: (currentNode: IPublicModelNode) => boolean;
// thinkof 限制性拖拽
- onHoverHook?: (currentNode: any) => boolean;
- onChildMoveHook?: (childNode: any, currentNode: any) => boolean;
+ onHoverHook?: (currentNode: IPublicModelNode) => boolean;
+ onChildMoveHook?: (childNode: IPublicModelNode, currentNode: IPublicModelNode) => boolean;
// events
- onNodeRemove?: (removedNode: any, currentNode: any) => void;
- onNodeAdd?: (addedNode: any, currentNode: any) => void;
- onSubtreeModified?: (currentNode: any, options: any) => void;
+ onNodeRemove?: (removedNode: IPublicModelNode | null, currentNode: IPublicModelNode | null) => void;
+ onNodeAdd?: (addedNode: IPublicModelNode | null, currentNode: IPublicModelNode | null) => void;
+ onSubtreeModified?: (currentNode: IPublicModelNode, options: any) => void;
onResize?: (
e: MouseEvent & {
trigger: string;
@@ -220,6 +220,6 @@ export interface IPublicTypeCallbacks {
deltaX?: number;
deltaY?: number;
},
- currentNode: any,
+ currentNode: IPublicModelNode,
) => void;
}
diff --git a/packages/types/src/shell/type/on-change-options.ts b/packages/types/src/shell/type/on-change-options.ts
index a1b0c314d..47b88d72f 100644
--- a/packages/types/src/shell/type/on-change-options.ts
+++ b/packages/types/src/shell/type/on-change-options.ts
@@ -1,6 +1,8 @@
import { IPublicModelNode } from '..';
-export interface IPublicTypeOnChangeOptions {
+export interface IPublicTypeOnChangeOptions<
+ Node = IPublicModelNode
+> {
type: string;
- node: IPublicModelNode;
+ node: Node;
}
diff --git a/packages/types/src/shell/type/prop-change-options.ts b/packages/types/src/shell/type/prop-change-options.ts
index 2a351a2d6..b515aec53 100644
--- a/packages/types/src/shell/type/prop-change-options.ts
+++ b/packages/types/src/shell/type/prop-change-options.ts
@@ -3,10 +3,12 @@ import {
IPublicModelProp,
} from '../model';
-export interface IPublicTypePropChangeOptions {
+export interface IPublicTypePropChangeOptions<
+ Node = IPublicModelNode
+> {
key?: string | number;
prop?: IPublicModelProp;
- node: IPublicModelNode;
+ node: Node;
newValue: any;
oldValue: any;
}
diff --git a/packages/types/src/shell/type/props-map.ts b/packages/types/src/shell/type/props-map.ts
index e43095757..1b93f4625 100644
--- a/packages/types/src/shell/type/props-map.ts
+++ b/packages/types/src/shell/type/props-map.ts
@@ -1,3 +1,3 @@
-import { IPublicTypeCompositeObject } from './';
+import { IPublicTypeCompositeObject, IPublicTypeNodeData } from './';
-export type IPublicTypePropsMap = IPublicTypeCompositeObject;
+export type IPublicTypePropsMap = IPublicTypeCompositeObject;
diff --git a/packages/types/src/shell/type/resource-list.ts b/packages/types/src/shell/type/resource-list.ts
index bd5e4a3b0..db5e33dc8 100644
--- a/packages/types/src/shell/type/resource-list.ts
+++ b/packages/types/src/shell/type/resource-list.ts
@@ -1,14 +1,21 @@
import { ReactElement } from 'react';
export interface IPublicResourceData {
+ /** 资源名字 */
resourceName: string;
+ /** 资源标题 */
title: string;
+ /** 分类 */
category?: string;
- viewType?: string;
+ /** 资源视图 */
+ viewName?: string;
+ /** 资源 icon */
icon?: ReactElement;
+ /** 资源其他配置 */
options: {
[key: string]: any;
};
+ /** 资源子元素 */
children?: IPublicResourceData[];
}
diff --git a/packages/types/src/shell/type/setter-config.ts b/packages/types/src/shell/type/setter-config.ts
index ddca72829..ac8a05aa3 100644
--- a/packages/types/src/shell/type/setter-config.ts
+++ b/packages/types/src/shell/type/setter-config.ts
@@ -6,49 +6,60 @@ import { IPublicTypeDynamicProps } from './dynamic-props';
* 设置器配置
*/
export interface IPublicTypeSetterConfig {
+
// if *string* passed must be a registered Setter Name
/**
* 配置设置器用哪一个 setter
*/
componentName: string | IPublicTypeCustomView;
+
/**
* 传递给 setter 的属性
*
* the props pass to Setter Component
*/
props?: Record | IPublicTypeDynamicProps;
+
/**
* @deprecated
*/
children?: any;
+
/**
* 是否必填?
*
* ArraySetter 里有个快捷预览,可以在不打开面板的情况下直接编辑
*/
isRequired?: boolean;
+
/**
* Setter 的初始值
*
* @todo initialValue 可能要和 defaultValue 二选一
*/
initialValue?: any | ((target: IPublicModelSettingTarget) => any);
+
+ defaultValue?: any;
+
// for MixedSetter
/**
* 给 MixedSetter 时切换 Setter 展示用的
*/
title?: IPublicTypeTitleContent;
+
// for MixedSetter check this is available
/**
* 给 MixedSetter 用于判断优先选中哪个
*/
condition?: (target: IPublicModelSettingTarget) => boolean;
+
/**
* 给 MixedSetter,切换值时声明类型
*
* @todo 物料协议推进
*/
valueType?: IPublicTypeCompositeValue[];
+
// 标识是否为动态 setter,默认为 true
isDynamic?: boolean;
}
diff --git a/packages/types/src/shell/type/value-type.ts b/packages/types/src/shell/type/value-type.ts
index c0c012544..16fb789a2 100644
--- a/packages/types/src/shell/type/value-type.ts
+++ b/packages/types/src/shell/type/value-type.ts
@@ -131,6 +131,6 @@ export interface IPublicTypeJSONObject {
}
export type IPublicTypeCompositeArray = IPublicTypeCompositeValue[];
-export interface IPublicTypeCompositeObject {
- [key: string]: IPublicTypeCompositeValue;
+export interface IPublicTypeCompositeObject {
+ [key: string]: IPublicTypeCompositeValue | T;
}
\ No newline at end of file
diff --git a/packages/utils/package.json b/packages/utils/package.json
index 7ed3787a7..4f391d23c 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-utils",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "Utils for Ali lowCode engine",
"files": [
"lib",
@@ -14,7 +14,7 @@
},
"dependencies": {
"@alifd/next": "^1.19.16",
- "@alilc/lowcode-types": "1.1.2",
+ "@alilc/lowcode-types": "1.1.3",
"lodash": "^4.17.21",
"mobx": "^6.3.0",
"react": "^16"
diff --git a/packages/utils/src/check-types/is-dom-text.ts b/packages/utils/src/check-types/is-dom-text.ts
index 47edec139..950954440 100644
--- a/packages/utils/src/check-types/is-dom-text.ts
+++ b/packages/utils/src/check-types/is-dom-text.ts
@@ -1,3 +1,3 @@
-export function isDOMText(data: any): boolean {
+export function isDOMText(data: any): data is string {
return typeof data === 'string';
}
diff --git a/packages/utils/src/check-types/is-drag-node-data-object.ts b/packages/utils/src/check-types/is-drag-node-data-object.ts
index 6f17abf23..4b08f67fa 100644
--- a/packages/utils/src/check-types/is-drag-node-data-object.ts
+++ b/packages/utils/src/check-types/is-drag-node-data-object.ts
@@ -1,5 +1,5 @@
-import { IPublicEnumDragObjectType } from '@alilc/lowcode-types';
+import { IPublicEnumDragObjectType, IPublicTypeDragNodeDataObject } from '@alilc/lowcode-types';
-export function isDragNodeDataObject(obj: any): boolean {
+export function isDragNodeDataObject(obj: any): obj is IPublicTypeDragNodeDataObject {
return obj && obj.type === IPublicEnumDragObjectType.NodeData;
}
\ No newline at end of file
diff --git a/packages/utils/src/check-types/is-drag-node-object.ts b/packages/utils/src/check-types/is-drag-node-object.ts
index 79b28fb58..1b6c131e9 100644
--- a/packages/utils/src/check-types/is-drag-node-object.ts
+++ b/packages/utils/src/check-types/is-drag-node-object.ts
@@ -1,5 +1,5 @@
-import { IPublicEnumDragObjectType } from '@alilc/lowcode-types';
+import { IPublicEnumDragObjectType, IPublicModelNode, IPublicTypeDragNodeObject } from '@alilc/lowcode-types';
-export function isDragNodeObject(obj: any): boolean {
+export function isDragNodeObject(obj: any): obj is IPublicTypeDragNodeObject {
return obj && obj.type === IPublicEnumDragObjectType.Node;
}
\ No newline at end of file
diff --git a/packages/utils/src/check-types/is-node-schema.ts b/packages/utils/src/check-types/is-node-schema.ts
index 0bfb4d035..bfc3ff3f2 100644
--- a/packages/utils/src/check-types/is-node-schema.ts
+++ b/packages/utils/src/check-types/is-node-schema.ts
@@ -1,5 +1,5 @@
import { IPublicTypeNodeSchema } from '@alilc/lowcode-types';
export function isNodeSchema(data: any): data is IPublicTypeNodeSchema {
- return data && data.componentName;
+ return data && data.componentName && !data.isNode;
}
diff --git a/packages/utils/src/check-types/is-node.ts b/packages/utils/src/check-types/is-node.ts
index 9fe27b29d..14c2e2f74 100644
--- a/packages/utils/src/check-types/is-node.ts
+++ b/packages/utils/src/check-types/is-node.ts
@@ -1,3 +1,5 @@
-export function isNode(node: any): boolean {
+import { IPublicModelNode } from '@alilc/lowcode-types';
+
+export function isNode(node: any): node is Node {
return node && node.isNode;
}
\ No newline at end of file
diff --git a/packages/utils/src/node-helper.ts b/packages/utils/src/node-helper.ts
index 1293cec2e..66a514364 100644
--- a/packages/utils/src/node-helper.ts
+++ b/packages/utils/src/node-helper.ts
@@ -23,7 +23,7 @@ export const getClosestNode = (
* @returns {boolean} 是否可点击,true表示可点击
*/
export const canClickNode = (node: IPublicModelNode, e: unknown): boolean => {
- const onClickHook = node.componentMeta?.advanced.callbacks?.onClickHook;
+ const onClickHook = node.componentMeta?.advanced?.callbacks?.onClickHook;
const canClick = typeof onClickHook === 'function' ? onClickHook(e as MouseEvent, node) : true;
return canClick;
};
diff --git a/packages/utils/src/schema.ts b/packages/utils/src/schema.ts
index 58dfb3045..2e7dec70f 100644
--- a/packages/utils/src/schema.ts
+++ b/packages/utils/src/schema.ts
@@ -79,7 +79,7 @@ export function compatibleLegaoSchema(props: any): any {
}
export function getNodeSchemaById(schema: IPublicTypeNodeSchema, nodeId: string): IPublicTypeNodeSchema | undefined {
- let found: NodeSIPublicTypeNodeSchemachema | undefined;
+ let found: IPublicTypeNodeSchema | undefined;
if (schema.id === nodeId) {
return schema;
}
@@ -100,7 +100,7 @@ export function getNodeSchemaById(schema: IPublicTypeNodeSchema, nodeId: string)
function getNodeSchemaFromPropsById(props: any, nodeId: string): IPublicTypeNodeSchema | undefined {
let found: IPublicTypeNodeSchema | undefined;
- for (const [key, value] of Object.entries(props)) {
+ for (const [_key, value] of Object.entries(props)) {
if (isJSSlot(value)) {
// value 是数组类型 { type: 'JSSlot', value: IPublicTypeNodeSchema[] }
if (Array.isArray(value.value)) {
@@ -123,7 +123,7 @@ function getNodeSchemaFromPropsById(props: any, nodeId: string): IPublicTypeNode
* TODO: not sure if this is used anywhere
* @deprecated
*/
-export function applyActivities(pivotSchema: IPublicTypeRootSchema, activities: any, options?: any): IPublicTypeRootSchema {
+export function applyActivities(pivotSchema: IPublicTypeRootSchema, activities: any): IPublicTypeRootSchema {
let schema = { ...pivotSchema };
if (!Array.isArray(activities)) {
activities = [activities];
diff --git a/packages/utils/test/src/build-components/buildComponents.test.ts b/packages/utils/test/src/build-components/buildComponents.test.ts
new file mode 100644
index 000000000..5662aa12c
--- /dev/null
+++ b/packages/utils/test/src/build-components/buildComponents.test.ts
@@ -0,0 +1,336 @@
+
+import { buildComponents } from "../../../src/build-components";
+
+function Button() {};
+
+function WrapButton() {};
+
+function ButtonGroup() {};
+
+function WrapButtonGroup() {};
+
+ButtonGroup.Button = Button;
+
+Button.displayName = "Button";
+ButtonGroup.displayName = "ButtonGroup";
+ButtonGroup.prototype.isReactComponent = true;
+Button.prototype.isReactComponent = true;
+
+jest.mock('../../../src/is-react', () => {
+ const original = jest.requireActual('../../../src/is-react');
+ return {
+ ...original,
+ wrapReactClass(view) {
+ return view;
+ }
+ }
+})
+
+describe('build-component', () => {
+ it('basic button', () => {
+ expect(
+ buildComponents(
+ {
+ '@alilc/button': {
+ Button,
+ }
+ },
+ {
+ Button: {
+ componentName: 'Button',
+ package: '@alilc/button',
+ destructuring: true,
+ exportName: 'Button',
+ subName: 'Button',
+ }
+ },
+ () => {},
+ ))
+ .toEqual({
+ Button,
+ });
+ });
+
+ it('component is a __esModule', () => {
+ expect(
+ buildComponents(
+ {
+ '@alilc/button': {
+ __esModule: true,
+ default: Button,
+ }
+ },
+ {
+ Button: {
+ componentName: 'Button',
+ package: '@alilc/button',
+ }
+ },
+ () => {},
+ ))
+ .toEqual({
+ Button,
+ });
+ })
+
+ it('basic warp button', () => {
+ expect(
+ buildComponents(
+ {
+ '@alilc/button': {
+ WrapButton,
+ }
+ },
+ {
+ WrapButton: {
+ componentName: 'WrapButton',
+ package: '@alilc/button',
+ destructuring: true,
+ exportName: 'WrapButton',
+ subName: 'WrapButton',
+ }
+ },
+ () => {},
+ ))
+ .toEqual({
+ WrapButton,
+ });
+ });
+
+ it('destructuring is false button', () => {
+ expect(
+ buildComponents(
+ {
+ '@alilc/button': Button
+ },
+ {
+ Button: {
+ componentName: 'Button',
+ package: '@alilc/button',
+ destructuring: false,
+ }
+ },
+ () => {},
+ ))
+ .toEqual({
+ Button,
+ });
+ });
+
+ it('Button and ButtonGroup', () => {
+ expect(
+ buildComponents(
+ {
+ '@alilc/button': {
+ Button,
+ ButtonGroup,
+ }
+ },
+ {
+ Button: {
+ componentName: 'Button',
+ package: '@alilc/button',
+ destructuring: true,
+ exportName: 'Button',
+ subName: 'Button',
+ },
+ ButtonGroup: {
+ componentName: 'ButtonGroup',
+ package: '@alilc/button',
+ destructuring: true,
+ exportName: 'ButtonGroup',
+ subName: 'ButtonGroup',
+ }
+ },
+ () => {},
+ ))
+ .toEqual({
+ Button,
+ ButtonGroup,
+ });
+ });
+
+ it('ButtonGroup and ButtonGroup.Button', () => {
+ expect(
+ buildComponents(
+ {
+ '@alilc/button': {
+ ButtonGroup,
+ }
+ },
+ {
+ Button: {
+ componentName: 'Button',
+ package: '@alilc/button',
+ destructuring: true,
+ exportName: 'ButtonGroup',
+ subName: 'ButtonGroup.Button',
+ },
+ ButtonGroup: {
+ componentName: 'ButtonGroup',
+ package: '@alilc/button',
+ destructuring: true,
+ exportName: 'ButtonGroup',
+ subName: 'ButtonGroup',
+ }
+ },
+ () => {},
+ ))
+ .toEqual({
+ Button,
+ ButtonGroup,
+ });
+ });
+
+ it('ButtonGroup.default and ButtonGroup.Button', () => {
+ expect(
+ buildComponents(
+ {
+ '@alilc/button': ButtonGroup,
+ },
+ {
+ Button: {
+ componentName: 'Button',
+ package: '@alilc/button',
+ destructuring: true,
+ exportName: 'Button',
+ subName: 'Button',
+ },
+ ButtonGroup: {
+ componentName: 'ButtonGroup',
+ package: '@alilc/button',
+ destructuring: true,
+ exportName: 'default',
+ subName: 'default',
+ }
+ },
+ () => {},
+ ))
+ .toEqual({
+ Button,
+ ButtonGroup,
+ });
+ });
+
+ it('no npm component', () => {
+ expect(
+ buildComponents(
+ {
+ '@alilc/button': Button,
+ },
+ {
+ Button: null,
+ },
+ () => {},
+ ))
+ .toEqual({});
+ });
+
+ it('no npm component and global button', () => {
+ window.Button = Button;
+ expect(
+ buildComponents(
+ {},
+ {
+ Button: null,
+ },
+ () => {},
+ ))
+ .toEqual({
+ Button,
+ });
+ window.Button = null;
+ });
+
+ it('componentsMap value is component funtion', () => {
+ expect(
+ buildComponents(
+ {},
+ {
+ Button,
+ },
+ () => {},
+ ))
+ .toEqual({
+ Button,
+ });
+ });
+
+
+ it('componentsMap value is component', () => {
+ expect(
+ buildComponents(
+ {},
+ {
+ Button: WrapButton,
+ },
+ () => {},
+ ))
+ .toEqual({
+ Button: WrapButton,
+ });
+ });
+
+ it('componentsMap value is mix component', () => {
+ expect(
+ buildComponents(
+ {},
+ {
+ Button: {
+ WrapButton,
+ Button,
+ ButtonGroup,
+ },
+ },
+ () => {},
+ ))
+ .toEqual({
+ Button: {
+ WrapButton,
+ Button,
+ ButtonGroup,
+ },
+ });
+ });
+
+ it('componentsMap value is Lowcode Component', () => {
+ expect(
+ buildComponents(
+ {},
+ {
+ Button: {
+ componentName: 'Component',
+ schema: {},
+ },
+ },
+ (component) => {
+ return component as any;
+ },
+ ))
+ .toEqual({
+ Button: {
+ componentName: 'Component',
+ schema: {},
+ },
+ });
+ })
+});
+
+describe('build div component', () => {
+ it('build div component', () => {
+ const components = buildComponents(
+ {
+ '@alilc/div': 'div'
+ },
+ {
+ div: {
+ componentName: 'div',
+ package: '@alilc/div'
+ }
+ },
+ () => {},
+ );
+
+ expect(components['div']).not.toBeNull();
+ })
+})
\ No newline at end of file
diff --git a/packages/utils/test/src/build-components/getProjectUtils.test.ts b/packages/utils/test/src/build-components/getProjectUtils.test.ts
new file mode 100644
index 000000000..216f3db42
--- /dev/null
+++ b/packages/utils/test/src/build-components/getProjectUtils.test.ts
@@ -0,0 +1,43 @@
+import { getProjectUtils } from "../../../src/build-components";
+
+const sampleUtil = () => 'I am a sample util';
+const sampleUtil2 = () => 'I am a sample util 2';
+
+describe('get project utils', () => {
+ it('get utils with destructuring true', () => {
+ expect(getProjectUtils(
+ {
+ '@alilc/utils': {
+ destructuring: true,
+ sampleUtil,
+ sampleUtil2,
+ }
+ },
+ [{
+ name: 'sampleUtils',
+ npm: {
+ package: '@alilc/utils'
+ }
+ }]
+ )).toEqual({
+ sampleUtil,
+ sampleUtil2,
+ })
+ });
+
+ it('get utils with name', () => {
+ expect(getProjectUtils(
+ {
+ '@alilc/utils': sampleUtil
+ },
+ [{
+ name: 'sampleUtil',
+ npm: {
+ package: '@alilc/utils'
+ }
+ }]
+ )).toEqual({
+ sampleUtil,
+ })
+ });
+})
\ No newline at end of file
diff --git a/packages/utils/test/src/build-components/getSubComponent.test.ts b/packages/utils/test/src/build-components/getSubComponent.test.ts
new file mode 100644
index 000000000..ca91bb230
--- /dev/null
+++ b/packages/utils/test/src/build-components/getSubComponent.test.ts
@@ -0,0 +1,85 @@
+import { getSubComponent } from '../../../src/build-components';
+
+function Button() {}
+
+function ButtonGroup() {}
+
+ButtonGroup.Button = Button;
+
+function OnlyButtonGroup() {}
+
+describe('getSubComponent library is object', () => {
+ it('get Button from Button', () => {
+ expect(getSubComponent({
+ Button,
+ }, ['Button'])).toBe(Button);
+ });
+
+ it('get ButtonGroup.Button from ButtonGroup', () => {
+ expect(getSubComponent({
+ ButtonGroup,
+ }, ['ButtonGroup', 'Button'])).toBe(Button);
+ });
+
+ it('get ButtonGroup from ButtonGroup', () => {
+ expect(getSubComponent({
+ ButtonGroup,
+ }, ['ButtonGroup'])).toBe(ButtonGroup);
+ });
+
+ it('get ButtonGroup.Button from OnlyButtonGroup', () => {
+ expect(getSubComponent({
+ ButtonGroup: OnlyButtonGroup,
+ }, ['ButtonGroup', 'Button'])).toBe(OnlyButtonGroup);
+ });
+});
+
+describe('getSubComponent library is null', () => {
+ it('getSubComponent library is null', () => {
+ expect(getSubComponent(null, ['ButtonGroup', 'Button'])).toBeNull();
+ });
+})
+
+describe('getSubComponent paths is []', () => {
+ it('getSubComponent paths is []', () => {
+ expect(getSubComponent(Button, [])).toBe(Button);
+ });
+});
+
+describe('getSubComponent make error', () => {
+ it('library is string', () => {
+ expect(getSubComponent(true, ['Button'])).toBe(null);
+ });
+
+ it('library is boolean', () => {
+ expect(getSubComponent('I am a string', ['Button'])).toBe(null);
+ });
+
+ it('library is number', () => {
+ expect(getSubComponent(123, ['Button'])).toBe(null);
+ });
+
+ it('library ButtonGroup is null', () => {
+ expect(getSubComponent({
+ ButtonGroup: null,
+ }, ['ButtonGroup', 'Button'])).toBe(null);
+ });
+
+ it('library ButtonGroup.Button is null', () => {
+ expect(getSubComponent({
+ ButtonGroup: null,
+ }, ['ButtonGroup', 'Button', 'SubButton'])).toBe(null);
+ });
+
+ it('path s is [[]]', () => {
+ expect(getSubComponent({
+ ButtonGroup: null,
+ }, [['ButtonGroup'] as any, 'Button'])).toBe(null);
+ });
+
+ it('ButtonGroup is undefined', () => {
+ expect(getSubComponent({
+ ButtonGroup: undefined,
+ }, ['ButtonGroup', 'Button'])).toBe(null);
+ });
+})
\ No newline at end of file
diff --git a/packages/workspace/package.json b/packages/workspace/package.json
index 4bd99eaf9..ef80c7d67 100644
--- a/packages/workspace/package.json
+++ b/packages/workspace/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-workspace",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "Shell Layer for AliLowCodeEngine",
"main": "lib/index.js",
"module": "es/index.js",
@@ -15,11 +15,11 @@
},
"license": "MIT",
"dependencies": {
- "@alilc/lowcode-designer": "1.1.2",
- "@alilc/lowcode-editor-core": "1.1.2",
- "@alilc/lowcode-editor-skeleton": "1.1.2",
- "@alilc/lowcode-types": "1.1.2",
- "@alilc/lowcode-utils": "1.1.2",
+ "@alilc/lowcode-designer": "1.1.3",
+ "@alilc/lowcode-editor-core": "1.1.3",
+ "@alilc/lowcode-editor-skeleton": "1.1.3",
+ "@alilc/lowcode-types": "1.1.3",
+ "@alilc/lowcode-utils": "1.1.3",
"classnames": "^2.2.6",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
diff --git a/packages/workspace/src/context/base-context.ts b/packages/workspace/src/context/base-context.ts
index 37ce7fafa..b67842fe8 100644
--- a/packages/workspace/src/context/base-context.ts
+++ b/packages/workspace/src/context/base-context.ts
@@ -30,6 +30,7 @@ import {
import {
IPluginPreferenceMananger,
IPublicApiEvent,
+ IPublicApiWorkspace,
IPublicModelPluginContext,
IPublicTypePluginMeta,
} from '@alilc/lowcode-types';
@@ -59,6 +60,7 @@ export class BasicContext implements IPublicModelPluginContext {
canvas: Canvas;
pluginEvent: IPublicApiEvent;
preference: IPluginPreferenceMananger;
+ workspace: IPublicApiWorkspace;
constructor(innerWorkspace: InnerWorkspace, viewName: string, public editorWindow?: EditorWindow) {
const editor = new Editor(viewName, true);
diff --git a/packages/workspace/src/resource.ts b/packages/workspace/src/resource.ts
index 119bcc488..ffb60ff6a 100644
--- a/packages/workspace/src/resource.ts
+++ b/packages/workspace/src/resource.ts
@@ -17,8 +17,8 @@ export class Resource implements IPublicModelResource {
return this.resourceType.name;
}
- get viewType() {
- return this.resourceData.viewType;
+ get viewName() {
+ return this.resourceData.viewName || (this.resourceData as any).viewType;
}
get description() {
diff --git a/packages/workspace/src/workspace.ts b/packages/workspace/src/workspace.ts
index 0b7181cd8..f7d49ed49 100644
--- a/packages/workspace/src/workspace.ts
+++ b/packages/workspace/src/workspace.ts
@@ -1,4 +1,4 @@
-import { Designer } from '@alilc/lowcode-designer';
+import { Designer, LowCodePluginManager } from '@alilc/lowcode-designer';
import { createModuleEventBus, Editor, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core';
import { Plugins } from '@alilc/lowcode-shell';
import { IPublicApiWorkspace, IPublicResourceList, IPublicTypeResourceType } from '@alilc/lowcode-types';
@@ -15,7 +15,11 @@ enum event {
const CHANGE_EVENT = 'resource.list.change';
-export class Workspace implements IPublicApiWorkspace {
+interface IWorkspace extends Omit, 'resourceList'> {}
+
+export class Workspace implements IWorkspace {
context: BasicContext;
private emitter: IEventBus = createModuleEventBus('workspace');
@@ -134,7 +138,7 @@ export class Workspace implements IPublicApiWorkspace {
this.emitChangeWindow();
}
- removeEditorWindow(resourceName: string, title: string) {
+ removeEditorWindow(resourceName: string) {
const index = this.windows.findIndex(d => (d.resource.name === resourceName && d.title));
this.remove(index);
}