Merge branch 'develop' into release/code-gen-1.1.3-beta

This commit is contained in:
LeoYuan 袁力皓 2023-06-05 18:53:58 +08:00
commit fd87ddcdd1
56 changed files with 580 additions and 173 deletions

View File

@ -62,11 +62,11 @@ delete(node: IPublicModelNode): boolean;
```typescript
/**
* 删除指定节点
* delete the node
* 插入一个节点
* insert the node
* @param node
*/
delete(node: IPublicModelNode): boolean;
insert(node: IPublicModelNode): boolean;
```
相关类型:[IPublicModelNode](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/node.ts)

View File

@ -45,6 +45,8 @@ sidebar_position: 12
关联模型 [IPublicModelEditorView](./editor-view)
**@since v1.1.7**
### editorViews
窗口所有视图
@ -53,6 +55,7 @@ sidebar_position: 12
关联模型 [IPublicModelEditorView](./editor-view)
**@since v1.1.7**
## 方法
@ -90,3 +93,15 @@ onChangeViewType(fn: (viewName: string) => void): IPublicTypeDisposable;
```
相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts)
### onSave
窗口视图保存事件
```
onSave(fn: () => void): IPublicTypeDisposable;
```
相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts)
**@since v1.1.7**

View File

@ -0,0 +1,155 @@
---
title: 图编排扩展
sidebar_position: 9
---
## 项目运行
### 前置准备
1. 参考 https://lowcode-engine.cn/site/docs/guide/quickStart/start
2. 参考至Demo下载 https://lowcode-engine.cn/site/docs/guide/quickStart/start#%E4%B8%8B%E8%BD%BD-demo
### 选择demo-graph-x6
在根目录下执行:
```bash
cd demo-graph-x6
```
### 安装依赖
 lowcode-demo/demo-graph-x6目录下执行
```bash
npm install
```
### 启动Demo
 lowcode-demo/demo-graph-x6 目录下执行
```bash
npm run start
```
之后就可以通过 http://localhost:5556/ 来访问我们的 DEMO 了。
## 认识Demo
这里的Demo即通过图编排引擎加入了几个简单的物料而来已经是可以面向真是用户的产品界面。
![image.png](https://img.alicdn.com/imgextra/i1/O1CN016TbCI31hM2sJy8qkR_!!6000000004262-2-tps-5120-2726.png)
### 区域组成
#### 顶部:操作区​
- 右侧:保存到本地、重置页面、自定义按钮
#### 顶部:工具区
- 左侧:删除、撤销、重做、放大、缩小
#### 左侧:面板与操作区​
- 物料面板:可以查找节点,并在此拖动节点到编辑器画布中
#### 中部:可视化页面编辑画布区域​
- 点击节点/边在右侧面板中能够显示出对应组件的属性配置选项
- 拖拽修改节点的排列顺序
#### 右侧:组件级别配置​
- 选中的组件:从页面开始一直到当前选中的节点/边位置,点击对应的名称可以切换到对应的节点上
- 选中组件的配置:属性:节点的基础属性值设置
> 每个区域的组成都可以被替换和自定义来生成开发者需要的业务产品。
## 目录介绍
![image.png](https://img.alicdn.com/imgextra/i3/O1CN01Luc8gr1tLq5QTbpb9_!!6000000005886-0-tps-832-1522.jpg)
- public与其他demo保持一致均是lowcode engine所必要依赖
- src
- plugins:自定义插件完成了x6的切面回调处理功能
- servicesmock数据真实场景中可能为异步获取数据
## 开发插件
```typescript
function pluginX6DesignerExtension(ctx: IPublicModelPluginContext) {
return {
init() {
// 获取 x6 designer 内置插件的导出 api
const x6Designer = ctx.plugins['plugin-x6-designer'] as IDesigner;
x6Designer.onNodeRender((model, node) => {
// @ts-ignore
// 自定义 node 渲染逻辑
const { name, title } = model.propsData;
node.attr('text/textWrap/text', title || name);
});
x6Designer.onEdgeRender((model, edge) => {
// @ts-ignore
const { source, target, sourcePortId, targetPortId } = model.propsData;
console.log(sourcePortId, targetPortId);
requestAnimationFrame(() => {
edge.setSource({ cell: source, port: sourcePortId });
edge.setTarget({ cell: target, port: targetPortId });
});
// https://x6.antv.vision/zh/docs/tutorial/intermediate/edge-labels x6 标签模块
// appendLabel 会触发 onEdgeLabelRender
edge.appendLabel({
markup: Markup.getForeignObjectMarkup(),
attrs: {
fo: {
width: 120,
height: 30,
x: -60,
y: -15,
},
},
});
});
x6Designer.onEdgeLabelRender((args) => {
const { selectors } = args
const content = selectors.foContent as HTMLDivElement
if (content) {
ReactDOM.render(<div>自定义 react 标签</div>, content)
}
})
}
}
}
pluginX6DesignerExtension.pluginName = 'plugin-x6-designer-extension';
export default pluginX6DesignerExtension;
```
x6Designer为图实例暴露出来的一些接口可基于此进行一些图的必要插件的封装整个插件的封装完全follow低代码引擎的插件详情可参考 https://lowcode-engine.cn/site/docs/guide/expand/editor/pluginWidget
## 开发物料
```bash
npm init @alilc/element your-material-demo
```
![image.png](https://img.alicdn.com/imgextra/i3/O1CN01DCCqO82ADuhS8ztCt_!!6000000008170-2-tps-546-208.png)
仓库初始化完成
![image.png](https://img.alicdn.com/imgextra/i2/O1CN01qK2rUe1JNpdqbdhoW_!!6000000001017-0-tps-5120-2830.jpg)
接下来即可编写物料内容了
图物料与低代码的dom场景存在画布的差异因此暂不支持物料单独调试须通过项目demo进行物料调试
### 资产描述
```bash
npm run lowcode:build
```
如果物料是个React组件则在执行上述命令时会自动生成对应的meta.ts<b>但图物料很多时候并非一个React组件因此须手动生产meta.ts</b>
可参考: https://github.com/alibaba/lowcode-materials/blob/main/packages/graph-x6-materials/lowcode/send-email/meta.ts
同时会自动生成物料描述文件
### 物料调试
#### 物料侧
物料想要支持被项目动态inject调试须在build.lowcode.js中加入
```javascript
[
'@alilc/build-plugin-alt',
{
type: 'component',
inject: true,
library
},
]
```
![image.png](https://img.alicdn.com/imgextra/i4/O1CN01HyXfL12992sDkOmOg_!!6000000008024-0-tps-5120-2824.jpg)
本地启动
```bash
npm run lowcode:dev
```
#### 项目侧
通过@alilc/lce-graph-core加载物料的天然支持了debug因此无须特殊处理。
若项目中自行加载,则参考 https://lowcode-engine.cn/site/docs/guide/expand/editor/cli
项目访问地址后拼接query "?debug"即可进入物料调试
![image.png](https://img.alicdn.com/imgextra/i2/O1CN01ke58hT1aRoYJzkutk_!!6000000003327-2-tps-5120-2790.png)

View File

@ -18,8 +18,7 @@ import { Icon, Message } from '@alifd/next';
const addHelloAction = (ctx: IPublicModelPluginContext) => {
return {
async init() {
const { addBuiltinComponentAction } = ctx.material;
addBuiltinComponentAction({
ctx.material.addBuiltinComponentAction({
name: 'hello',
content: {
icon: <Icon type="atm" />,
@ -54,8 +53,7 @@ import { IPublicModelPluginContext } from '@alilc/lowcode-types';
const removeCopyAction = (ctx: IPublicModelPluginContext) => {
return {
async init() {
const { removeBuiltinComponentAction } = ctx.material;
removeBuiltinComponentAction('copy');
ctx.material.removeBuiltinComponentAction('copy');
}
}
};

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-engine-docs",
"version": "1.0.28",
"version": "1.0.30",
"description": "低代码引擎版本化文档",
"license": "MIT",
"files": [

View File

@ -1,6 +1,6 @@
{
"lerna": "4.0.0",
"version": "1.1.6",
"version": "1.1.7",
"npmClient": "yarn",
"useWorkspaces": true,
"packages": [

View File

@ -106,6 +106,11 @@ function processChildren(schema: IPublicTypeNodeSchema): void {
}
}
function getInternalDep(internalDeps: Record<string, IInternalDependency>, depName: string) {
const dep = internalDeps[depName];
return (dep && dep.type !== InternalDependencyType.PAGE) ? dep : null;
}
export class SchemaParser implements ISchemaParser {
validate(schema: IPublicTypeProjectSchema): boolean {
if (SUPPORT_SCHEMA_VERSION_LIST.indexOf(schema.version) < 0) {
@ -221,12 +226,11 @@ export class SchemaParser implements ISchemaParser {
}
});
// 分析容器内部组件依赖
containers.forEach((container) => {
const depNames = this.getComponentNames(container);
// eslint-disable-next-line no-param-reassign
container.deps = uniqueArray<string>(depNames, (i: string) => i)
.map((depName) => internalDeps[depName] || compDeps[depName])
.map((depName) => getInternalDep(internalDeps, depName) || compDeps[depName])
.filter(Boolean);
// container.deps = Object.keys(compDeps).map((depName) => compDeps[depName]);
});

View File

@ -20,7 +20,7 @@ const factory: PostProcessorFactory<ProcessorConfig> = (config?: ProcessorConfig
const codePrettier: PostProcessor = (content: string, fileType: string) => {
let parser: prettier.BuiltInParserName | any;
if (fileType === 'js' || fileType === 'jsx') {
if (fileType === 'js' || fileType === 'jsx' || fileType === 'ts' || fileType === 'tsx') {
parser = 'babel';
} else if (fileType === 'json') {
parser = 'json-stringify';

View File

@ -167,7 +167,7 @@ export function parseExpressionGetKeywords(expr: string | null | undefined): str
],
});
const addIdentifierIfNeeded = (x: Record<string, unknown> | number | null | undefined) => {
const addIdentifierIfNeeded = (x: Node | null | undefined) => {
if (typeof x === 'object' && isIdentifier(x) && JS_KEYWORDS.includes(x.name)) {
keywordVars.add(x.name);
}
@ -189,7 +189,7 @@ export function parseExpressionGetKeywords(expr: string | null | undefined): str
addIdentifierIfNeeded(item);
});
} else {
addIdentifierIfNeeded(fieldValue as Record<string, unknown> | null);
addIdentifierIfNeeded(fieldValue as any);
}
}
});
@ -217,7 +217,7 @@ export function parseExpressionGetGlobalVariables(
const ast = parser.parse(`!(${expr});`);
const addUndeclaredIdentifierIfNeeded = (
x: Record<string, unknown> | number | null | undefined,
x: Node | null | undefined,
path: NodePath<Node>,
) => {
if (typeof x === 'object' && isIdentifier(x) && !path.scope.hasBinding(x.name)) {
@ -241,7 +241,7 @@ export function parseExpressionGetGlobalVariables(
addUndeclaredIdentifierIfNeeded(item, path);
});
} else {
addUndeclaredIdentifierIfNeeded(fieldValue as Record<string, unknown> | null, path);
addUndeclaredIdentifierIfNeeded(fieldValue as any, path);
}
}
});

View File

@ -19,6 +19,7 @@ import { executeFunctionStack } from './aopHelper';
import { encodeJsxStringNode } from './encodeJsxAttrString';
import { unwrapJsExprQuoteInJsx } from './jsxHelpers';
import { transformThis2Context } from '../core/jsx/handlers/transformThis2Context';
import { isValidIdentifier } from './validate';
function mergeNodeGeneratorConfig(
cfg1: NodeGeneratorConfig,
@ -126,11 +127,13 @@ function generateAttrs(
if (props) {
if (!Array.isArray(props)) {
Object.keys(props).forEach((propName: string) => {
pieces = pieces.concat(generateAttr(propName, props[propName] as IPublicTypeCompositeValue, scope, config));
if (isValidIdentifier(propName)) {
pieces = pieces.concat(generateAttr(propName, props[propName] as IPublicTypeCompositeValue, scope, config));
}
});
} else {
props.forEach((prop) => {
if (prop.name && !prop.spread) {
if (prop.name && isValidIdentifier(prop.name) && !prop.spread) {
pieces = pieces.concat(generateAttr(prop.name, prop.value, scope, config));
}

View File

@ -281,3 +281,75 @@ Object {
},
}
`;
exports[`condition at root invalid attr name should not be generated 1`] = `
Object {
"chunks": Array [
Object {
"content": "
const __$$context = this._context || this;
const { state } = __$$context;
return <Page><Text a={1}>Hello world!</Text></Page>;
",
"fileType": "jsx",
"linkAfter": Array [
"ReactComponentClassRenderStart",
"ReactComponentClassRenderPre",
],
"name": "ReactComponentClassRenderJSX",
"type": "string",
},
Object {
"content": "
function __$$eval(expr) {
try {
return expr();
} catch (error) {
}
}
function __$$evalArray(expr) {
const res = __$$eval(expr);
return Array.isArray(res) ? res : [];
}
function __$$createChildContext(oldContext, ext) {
const childContext = {
...oldContext,
...ext,
};
childContext.__proto__ = oldContext;
return childContext;
}
",
"fileType": "jsx",
"linkAfter": Array [
"CommonFileExport",
],
"name": "CommonCustomContent",
"type": "string",
},
],
"contextData": Object {},
"depNames": Array [],
"ir": Object {
"children": Array [
Object {
"children": "Hello world!",
"componentName": "Text",
"props": Object {
"a": 1,
"a.b": 2,
},
},
],
"componentName": "Page",
"condition": null,
"containerType": "Page",
"fileName": "test",
"moduleName": "test",
},
}
`;

View File

@ -83,4 +83,22 @@ describe('condition at root', () => {
});
expect(result).toMatchSnapshot();
});
test('invalid attr name should not be generated', async () => {
const containerIr: IContainerInfo = {
containerType: 'Page',
moduleName: 'test',
componentName: 'Page',
fileName: 'test',
condition: null,
children: [{ componentName: 'Text', children: 'Hello world!', props: { 'a': 1, 'a.b': 2 } }],
};
const result = await jsx()({
ir: containerIr,
contextData: {},
chunks: [],
depNames: [],
});
expect(result).toMatchSnapshot();
})
});

View File

@ -19,6 +19,7 @@ const jestConfig = {
// testMatch: ['**/setting-field.test.ts'],
// testMatch: ['**/node.test.ts'],
// testMatch: ['**/builtin-hotkey.test.ts'],
// testMatch: ['**/selection.test.ts'],
transformIgnorePatterns: [
`/node_modules/(?!${esModules})/`,
],

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-designer",
"version": "1.1.6",
"version": "1.1.7",
"description": "Designer for Ali LowCode Engine",
"main": "lib/index.js",
"module": "es/index.js",
@ -15,9 +15,10 @@
},
"license": "MIT",
"dependencies": {
"@alilc/lowcode-editor-core": "1.1.6",
"@alilc/lowcode-types": "1.1.6",
"@alilc/lowcode-utils": "1.1.6",
"@alilc/build-plugin-lce": "^0.0.4-beta.2",
"@alilc/lowcode-editor-core": "1.1.7",
"@alilc/lowcode-types": "1.1.7",
"@alilc/lowcode-utils": "1.1.7",
"classnames": "^2.2.6",
"react": "^16",
"react-dom": "^16.7.0",

View File

@ -1,29 +1,7 @@
import { Component } from '../simulator';
import { IPublicTypeComponentInstance, IPublicTypeNodeInstance, Asset, IPublicTypeComponentSchema, IPublicTypeProjectSchema, IPublicTypeLowCodeComponent } from '@alilc/lowcode-types';
import { IPublicTypeComponentInstance, IPublicTypeSimulatorRenderer } from '@alilc/lowcode-types';
export interface BuiltinSimulatorRenderer {
readonly isSimulatorRenderer: true;
autoRepaintNode?: boolean;
components: Record<string, Component>;
rerender: () => void;
createComponent(schema: IPublicTypeProjectSchema<IPublicTypeComponentSchema>): Component | null;
getComponent(componentName: string): Component;
getClosestNodeInstance(
from: IPublicTypeComponentInstance,
nodeId?: string,
): IPublicTypeNodeInstance<IPublicTypeComponentInstance> | null;
findDOMNodes(instance: IPublicTypeComponentInstance): Array<Element | Text> | null;
getClientRects(element: Element | Text): DOMRect[];
setNativeSelection(enableFlag: boolean): void;
setDraggingState(state: boolean): void;
setCopyState(state: boolean): void;
loadAsyncLibrary(asyncMap: { [index: string]: any }): void;
clearState(): void;
stopAutoRepaintNode(): void;
enableAutoRepaintNode(): void;
run(): void;
load(asset: Asset): Promise<any>;
}
export type BuiltinSimulatorRenderer = IPublicTypeSimulatorRenderer<Component, IPublicTypeComponentInstance>;
export function isSimulatorRenderer(obj: any): obj is BuiltinSimulatorRenderer {
return obj && obj.isSimulatorRenderer;

View File

@ -839,13 +839,18 @@ export class DocumentModel implements IDocumentModel {
}
// 合并外界传入的自定义渲染的组件
if (Array.isArray(extraComps)) {
extraComps.forEach(c => {
if (c && !exsitingMap[c]) {
const m = this.getComponentMeta(c);
if (m && m.npm?.package) {
extraComps.forEach((componentName) => {
if (componentName && !exsitingMap[componentName]) {
const meta = this.getComponentMeta(componentName);
if (meta?.npm?.package) {
componentsMap.push({
...m?.npm,
componentName: c,
...meta?.npm,
componentName,
});
} else {
componentsMap.push({
devMode: 'lowCode',
componentName,
});
}
}

View File

@ -159,6 +159,9 @@ export interface IBaseNode<Schema extends IPublicTypeNodeSchema = IPublicTypeNod
setProps(props?: IPublicTypePropsMap | IPublicTypePropsList | Props | null): void;
mergeProps(props: IPublicTypePropsMap): void;
/** 是否可以选中 */
canSelect(): boolean;
}
/**
@ -644,6 +647,12 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
return !!this.getExtraProp('isLocked')?.getValue();
}
canSelect(): boolean {
const onSelectHook = this.componentMeta?.advanced?.callbacks?.onSelectHook;
const canSelect = typeof onSelectHook === 'function' ? onSelectHook(this.internalToShellNode()!) : true;
return canSelect;
}
/**
*
*/

View File

@ -148,15 +148,13 @@ export class Prop implements IProp, IPropParent {
@obx.shallow private _items: IProp[] | null = null;
@obx.shallow private _maps: Map<string | number, IProp> | null = null;
/**
* _maps Prop
* Prop
* Prop#_value { a: 1 } setValue({ a: 2 }) Prop
* mobx reaction observer
* reaction Prop(a) _value Prop(a) _value
*/
private _prevMaps: Map<string | number, IProp> | null = null;
@obx.shallow private _maps: Map<string | number, IProp> | null = null;
/**
* items maps
@ -171,8 +169,8 @@ export class Prop implements IProp, IPropParent {
data.forEach((item: any, idx: number) => {
items = items || [];
let prop;
if (this._prevMaps?.has(idx.toString())) {
prop = this._prevMaps.get(idx.toString())!;
if (this._maps?.has(idx.toString())) {
prop = this._maps.get(idx.toString())!;
prop.setValue(item);
} else {
prop = new Prop(this, item, idx);
@ -187,8 +185,8 @@ export class Prop implements IProp, IPropParent {
const keys = Object.keys(data);
for (const key of keys) {
let prop: IProp;
if (this._prevMaps?.has(key)) {
prop = this._prevMaps.get(key)!;
if (this._maps?.has(key)) {
prop = this._maps.get(key)!;
prop.setValue(data[key]);
} else {
prop = new Prop(this, data[key], key);
@ -419,8 +417,6 @@ export class Prop implements IProp, IPropParent {
items.forEach((prop) => prop.purge());
}
this._items = null;
this._prevMaps = this._maps;
this._maps = null;
if (this._type !== 'slot' && this._slotNode) {
this._slotNode.remove();
this._slotNode = undefined;

View File

@ -32,6 +32,12 @@ export class Selection implements ISelection {
return;
}
const node = this.doc.getNode(id);
if (!node?.canSelect()) {
return;
}
this._selected = [id];
this.emitter.emit('selectionchange', this._selected);
}
@ -40,7 +46,18 @@ export class Selection implements ISelection {
*
*/
selectAll(ids: string[]) {
this._selected = ids;
const selectIds: string[] = [];
ids.forEach(d => {
const node = this.doc.getNode(d);
if (node?.canSelect()) {
selectIds.push(d);
}
});
this._selected = selectIds;
this.emitter.emit('selectionchange', this._selected);
}

View File

@ -379,7 +379,6 @@ describe('Prop 类测试', () => {
prop.dispose();
expect(prop._items).toBeNull();
expect(prop._maps).toBeNull();
});
});

View File

@ -122,7 +122,7 @@ describe('选择区测试', () => {
selectionChangeHandler.mockClear();
});
it('dispose 方法', () => {
it('selectAll 包含不存在的 id', () => {
const project = new Project(designer, {
componentsTree: [
formSchema,
@ -135,14 +135,7 @@ describe('选择区测试', () => {
selection.selectAll(['form', 'node_k1ow3cbj', 'form2']);
const selectionChangeHandler = jest.fn();
selection.onSelectionChange(selectionChangeHandler);
selection.dispose();
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form', 'node_k1ow3cbj']);
expect(selection.selected).toEqual(['form', 'node_k1ow3cbj']);
selectionChangeHandler.mockClear();
});
it('dispose 方法 - 选中的节点没有被删除的', () => {

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-editor-core",
"version": "1.1.6",
"version": "1.1.7",
"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.6",
"@alilc/lowcode-utils": "1.1.6",
"@alilc/lowcode-types": "1.1.7",
"@alilc/lowcode-utils": "1.1.7",
"classnames": "^2.2.6",
"debug": "^4.1.1",
"intl-messageformat": "^9.3.1",

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-editor-skeleton",
"version": "1.1.6",
"version": "1.1.7",
"description": "alibaba lowcode editor skeleton",
"main": "lib/index.js",
"module": "es/index.js",
@ -19,10 +19,10 @@
],
"dependencies": {
"@alifd/next": "^1.20.12",
"@alilc/lowcode-designer": "1.1.6",
"@alilc/lowcode-editor-core": "1.1.6",
"@alilc/lowcode-types": "1.1.6",
"@alilc/lowcode-utils": "1.1.6",
"@alilc/lowcode-designer": "1.1.7",
"@alilc/lowcode-editor-core": "1.1.7",
"@alilc/lowcode-types": "1.1.7",
"@alilc/lowcode-utils": "1.1.7",
"classnames": "^2.2.6",
"react": "^16.8.1",
"react-dom": "^16.8.1"

View File

@ -1,6 +1,6 @@
import { Component, MouseEvent, Fragment, ReactNode } from 'react';
import { shallowIntl, observer, obx, engineConfig, runInAction } from '@alilc/lowcode-editor-core';
import { createContent, isJSSlot, isSetterConfig } from '@alilc/lowcode-utils';
import { createContent, isJSSlot, isSetterConfig, shouldUseVariableSetter } from '@alilc/lowcode-utils';
import { Skeleton, Stage } from '@alilc/lowcode-editor-skeleton';
import { IPublicApiSetters, IPublicTypeCustomView, IPublicTypeDynamicProps } from '@alilc/lowcode-types';
import { ISettingEntry, IComponentMeta, ISettingField, isSettingField, ISettingTopEntry } from '@alilc/lowcode-designer';
@ -155,23 +155,29 @@ class SettingFieldView extends Component<SettingFieldViewProps, SettingFieldView
const supportVariable = this.field.extraProps?.supportVariable;
// supportVariableGlobally 只对标准组件生效vc 需要单独配置
const supportVariableGlobally = engineConfig.get('supportVariableGlobally', false) && isStandardComponent(componentMeta);
if (supportVariable || supportVariableGlobally) {
if (setterType === 'MixedSetter') {
// VariableSetter 不单独使用
if (Array.isArray(setterProps.setters) && !setterProps.setters.includes('VariableSetter')) {
setterProps.setters.push('VariableSetter');
}
} else {
setterType = 'MixedSetter';
setterProps = {
setters: [
setter,
'VariableSetter',
],
};
}
const isUseVariableSetter = shouldUseVariableSetter(supportVariable, supportVariableGlobally);
if (isUseVariableSetter === false) {
return {
setterProps,
initialValue,
setterType,
};
}
if (setterType === 'MixedSetter') {
// VariableSetter 不单独使用
if (Array.isArray(setterProps.setters) && !setterProps.setters.includes('VariableSetter')) {
setterProps.setters.push('VariableSetter');
}
} else {
setterType = 'MixedSetter';
setterProps = {
setters: [
setter,
'VariableSetter',
],
};
}
return {
setterProps,
initialValue,

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-engine",
"version": "1.1.6",
"version": "1.1.7",
"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.6",
"@alilc/lowcode-editor-core": "1.1.6",
"@alilc/lowcode-editor-skeleton": "1.1.6",
"@alilc/lowcode-designer": "1.1.7",
"@alilc/lowcode-editor-core": "1.1.7",
"@alilc/lowcode-editor-skeleton": "1.1.7",
"@alilc/lowcode-engine-ext": "^1.0.0",
"@alilc/lowcode-plugin-designer": "1.1.6",
"@alilc/lowcode-plugin-outline-pane": "1.1.6",
"@alilc/lowcode-shell": "1.1.6",
"@alilc/lowcode-utils": "1.1.6",
"@alilc/lowcode-workspace": "1.1.6",
"@alilc/lowcode-plugin-designer": "1.1.7",
"@alilc/lowcode-plugin-outline-pane": "1.1.7",
"@alilc/lowcode-shell": "1.1.7",
"@alilc/lowcode-utils": "1.1.7",
"@alilc/lowcode-workspace": "1.1.7",
"react": "^16.8.1",
"react-dom": "^16.8.1"
},

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-ignitor",
"version": "1.1.6",
"version": "1.1.7",
"description": "点火器bootstrap lce project",
"main": "lib/index.js",
"private": true,

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-plugin-designer",
"version": "1.1.6",
"version": "1.1.7",
"description": "alibaba lowcode editor designer plugin",
"files": [
"es",
@ -18,9 +18,9 @@
],
"author": "xiayang.xy",
"dependencies": {
"@alilc/lowcode-designer": "1.1.6",
"@alilc/lowcode-editor-core": "1.1.6",
"@alilc/lowcode-utils": "1.1.6",
"@alilc/lowcode-designer": "1.1.7",
"@alilc/lowcode-editor-core": "1.1.7",
"@alilc/lowcode-utils": "1.1.7",
"react": "^16.8.1",
"react-dom": "^16.8.1"
},

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-plugin-outline-pane",
"version": "1.1.6",
"version": "1.1.7",
"description": "Outline pane for Ali lowCode engine",
"files": [
"es",
@ -13,8 +13,8 @@
},
"dependencies": {
"@alifd/next": "^1.19.16",
"@alilc/lowcode-types": "1.1.6",
"@alilc/lowcode-utils": "1.1.6",
"@alilc/lowcode-types": "1.1.7",
"@alilc/lowcode-utils": "1.1.7",
"classnames": "^2.2.6",
"react": "^16",
"react-dom": "^16.7.0",

View File

@ -244,7 +244,9 @@ export default class TreeNode {
if (this.node.conditionGroup) {
return;
}
this.node.visible = !flag;
if (this.node.visible !== !flag) {
this.node.visible = !flag;
}
this.event.emit(EVENT_NAMES.hiddenChanged, flag);
}

View File

@ -40,6 +40,11 @@ export class Tree {
treeNode?.notifyConditionChanged();
}
});
doc?.onChangeNodeVisible((node: IPublicModelNode, visible: boolean) => {
const treeNode = this.getTreeNodeById(node.id);
treeNode?.setHidden(!visible);
});
}
setNodeSelected(nodeId: string): void {

View File

@ -4,7 +4,7 @@ import { IPublicModelPluginContext, IPublicModelDocumentModel } from '@alilc/low
import { MasterPaneName, BackupPaneName } from './helper/consts';
import { TreeMaster } from './controllers/tree-master';
import { PaneController } from './controllers/pane-controller';
import { useState } from 'react';
import { useState, useEffect } from 'react';
export function OutlinePaneContext(props: {
treeMaster?: TreeMaster;
@ -19,9 +19,11 @@ export function OutlinePaneContext(props: {
}) {
const treeMaster = props.treeMaster || new TreeMaster(props.pluginContext, props.options);
const [masterPaneController, setMasterPaneController] = useState(new PaneController(props.paneName || MasterPaneName, treeMaster));
treeMaster.onPluginContextChange(() => {
setMasterPaneController(new PaneController(props.paneName || MasterPaneName, treeMaster));
});
useEffect(() => {
return treeMaster.onPluginContextChange(() => {
setMasterPaneController(new PaneController(props.paneName || MasterPaneName, treeMaster));
});
}, []);
return (
<Pane

View File

@ -28,6 +28,7 @@ export default class TreeTitle extends PureComponent<{
editing: boolean;
title: string;
condition?: boolean;
visible?: boolean;
} = {
editing: false,
title: '',
@ -93,6 +94,11 @@ export default class TreeTitle extends PureComponent<{
condition: treeNode.condition,
});
});
treeNode.onHiddenChanged((hidden: boolean) => {
this.setState({
visible: !hidden,
});
});
}
render() {
@ -132,7 +138,7 @@ export default class TreeTitle extends PureComponent<{
data-id={treeNode.id}
onClick={() => {
if (isModal) {
if (node.visible) {
if (this.state.visible) {
node.document?.modalNodesManager?.setInvisible(node);
} else {
node.document?.modalNodesManager?.setVisible(node);
@ -144,7 +150,7 @@ export default class TreeTitle extends PureComponent<{
}
}}
>
{isModal && node.visible && (
{isModal && this.state.visible && (
<div onClick={() => {
node.document?.modalNodesManager?.setInvisible(node);
}}
@ -152,7 +158,7 @@ export default class TreeTitle extends PureComponent<{
<IconRadioActive className="tree-node-modal-radio-active" />
</div>
)}
{isModal && !node.visible && (
{isModal && !this.state.visible && (
<div onClick={() => {
node.document?.modalNodesManager?.setVisible(node);
}}

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-rax-renderer",
"version": "1.1.6",
"version": "1.1.7",
"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.6",
"@alilc/lowcode-utils": "1.1.6",
"@alilc/lowcode-renderer-core": "1.1.7",
"@alilc/lowcode-utils": "1.1.7",
"rax-find-dom-node": "^1.0.1"
},
"devDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-rax-simulator-renderer",
"version": "1.1.6",
"version": "1.1.7",
"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.6",
"@alilc/lowcode-rax-renderer": "1.1.6",
"@alilc/lowcode-types": "1.1.6",
"@alilc/lowcode-utils": "1.1.6",
"@alilc/lowcode-designer": "1.1.7",
"@alilc/lowcode-rax-renderer": "1.1.7",
"@alilc/lowcode-types": "1.1.7",
"@alilc/lowcode-utils": "1.1.7",
"classnames": "^2.2.6",
"driver-universal": "^3.1.3",
"history": "^5.0.0",

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-react-renderer",
"version": "1.1.6",
"version": "1.1.7",
"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.6"
"@alilc/lowcode-renderer-core": "1.1.7"
},
"devDependencies": {
"@alib/build-scripts": "^0.1.18",

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-react-simulator-renderer",
"version": "1.1.6",
"version": "1.1.7",
"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.6",
"@alilc/lowcode-react-renderer": "1.1.6",
"@alilc/lowcode-types": "1.1.6",
"@alilc/lowcode-utils": "1.1.6",
"@alilc/lowcode-designer": "1.1.7",
"@alilc/lowcode-react-renderer": "1.1.7",
"@alilc/lowcode-types": "1.1.7",
"@alilc/lowcode-utils": "1.1.7",
"classnames": "^2.2.6",
"mobx": "^6.3.0",
"mobx-react": "^7.2.0",

View File

@ -12,6 +12,7 @@ const jestConfig = {
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
// testMatch: ['**/*/base.test.tsx'],
// testMatch: ['**/utils/common.test.ts'],
// testMatch: ['**/*/leaf.test.tsx'],
transformIgnorePatterns: [
`/node_modules/(?!${esModules})/`,
],

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-renderer-core",
"version": "1.1.6",
"version": "1.1.7",
"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.6",
"@alilc/lowcode-utils": "1.1.6",
"@alilc/lowcode-types": "1.1.7",
"@alilc/lowcode-utils": "1.1.7",
"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.6",
"@alilc/lowcode-designer": "1.1.7",
"@babel/plugin-transform-typescript": "^7.16.8",
"@testing-library/react": "^11.2.2",
"@types/classnames": "^2.2.11",

View File

@ -521,16 +521,11 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
}
get hasChildren(): boolean {
let { children } = this.props;
if (this.state.childrenInState) {
children = this.state.nodeChildren;
if (!this.state.childrenInState) {
return 'children' in this.props;
}
if (Array.isArray(children)) {
return Boolean(children && children.length);
}
return Boolean(children);
return true;
}
get children(): any {
@ -576,7 +571,11 @@ export function leafWrapper(Comp: types.IBaseRenderComponent, {
delete compProps.__inner__;
return engine.createElement(Comp, compProps, this.hasChildren ? this.children : null);
if (this.hasChildren) {
return engine.createElement(Comp, compProps, this.children);
}
return engine.createElement(Comp, compProps);
}
}

View File

@ -504,6 +504,41 @@ describe('onChildrenChange', () => {
DivNode.emitChildrenChange();
makeSnapshot(component);
});
it('children is 0', () => {
DivNode.schema.children = 0
DivNode.emitChildrenChange();
const componentInstance = component.root;
expect(componentInstance.findByType(components.Div).props.children).toEqual(0);
});
it('children is false', () => {
DivNode.schema.children = false
DivNode.emitChildrenChange();
const componentInstance = component.root;
expect(componentInstance.findByType(components.Div).props.children).toEqual(false);
});
it('children is []', () => {
DivNode.schema.children = []
DivNode.emitChildrenChange();
const componentInstance = component.root;
expect(componentInstance.findByType(components.Div).props.children).toEqual([]);
});
it('children is null', () => {
DivNode.schema.children = null
DivNode.emitChildrenChange();
const componentInstance = component.root;
expect(componentInstance.findByType(components.Div).props.children).toEqual(null);
});
it('children is undefined', () => {
DivNode.schema.children = undefined;
DivNode.emitChildrenChange();
const componentInstance = component.root;
expect(componentInstance.findByType(components.Div).props.children).toEqual(undefined);
});
});
describe('not render leaf', () => {

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-shell",
"version": "1.1.6",
"version": "1.1.7",
"description": "Shell Layer for AliLowCodeEngine",
"main": "lib/index.js",
"module": "es/index.js",
@ -13,12 +13,12 @@
},
"license": "MIT",
"dependencies": {
"@alilc/lowcode-designer": "1.1.6",
"@alilc/lowcode-editor-core": "1.1.6",
"@alilc/lowcode-editor-skeleton": "1.1.6",
"@alilc/lowcode-types": "1.1.6",
"@alilc/lowcode-utils": "1.1.6",
"@alilc/lowcode-workspace": "1.1.6",
"@alilc/lowcode-designer": "1.1.7",
"@alilc/lowcode-editor-core": "1.1.7",
"@alilc/lowcode-editor-skeleton": "1.1.7",
"@alilc/lowcode-types": "1.1.7",
"@alilc/lowcode-utils": "1.1.7",
"@alilc/lowcode-workspace": "1.1.7",
"classnames": "^2.2.6",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",

View File

@ -47,11 +47,11 @@ export class Setters implements IPublicApiSetters {
* settersMap
* @returns
*/
getSettersMap(): Map<string, IPublicTypeRegisteredSetter & {
getSettersMap = (): Map<string, IPublicTypeRegisteredSetter & {
type: string;
}> {
}> => {
return this[settersSymbol].getSettersMap();
}
};
/**
* setter

View File

@ -13,7 +13,13 @@ export class ActiveTracker implements IPublicModelActiveTracker {
}
get target() {
const { node: innerNode, detail, instance } = this[activeTrackerSymbol]._target;
const _target = this[activeTrackerSymbol]._target;
if (!_target) {
return null;
}
const { node: innerNode, detail, instance } = _target;
const publicNode = ShellNode.create(innerNode);
return {
node: publicNode!,

View File

@ -43,6 +43,10 @@ export class Window implements IPublicModelWindow {
return await this[windowSymbol].save();
}
onSave(fn: () => void) {
return this[windowSymbol].onSave(fn);
}
get currentEditorView() {
if (this[windowSymbol].editorView) {
return new EditorView(this[windowSymbol].editorView).toProxy() as any;

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-types",
"version": "1.1.6",
"version": "1.1.7",
"description": "Types for Ali lowCode engine",
"files": [
"es",

View File

@ -6,7 +6,7 @@ export interface IPublicModelActiveTracker {
/**
* @since 1.1.7
*/
target: IPublicTypeActiveTarget;
target: IPublicTypeActiveTarget | null;
onChange(fn: (target: IPublicTypeActiveTarget) => void): () => void;

View File

@ -13,7 +13,7 @@ import {
IPublicApiWorkspace,
} from '../api';
import { IPublicEnumPluginRegisterLevel } from '../enum';
import { IPublicModelEngineConfig, IPublicModelEditorView } from './';
import { IPublicModelEngineConfig, IPublicModelWindow } from './';
export interface IPublicModelPluginContext {
@ -108,7 +108,7 @@ export interface IPublicModelPluginContext {
*/
get registerLevel(): IPublicEnumPluginRegisterLevel;
get editorWindow(): IPublicModelEditorView;
get editorWindow(): IPublicModelWindow;
}
/**

View File

@ -42,4 +42,10 @@ export interface IPublicModelWindow<
/** 窗口视图变更事件 */
onChangeViewType(fn: (viewName: string) => void): IPublicTypeDisposable;
/**
*
* @since 1.1.7
*/
onSave(fn: () => void): IPublicTypeDisposable;
}

View File

@ -90,3 +90,4 @@ export * from './editor-view-config';
export * from './hotkey-callback-config';
export * from './hotkey-callbacks';
export * from './scrollable';
export * from './simulator-renderer';

View File

@ -195,6 +195,9 @@ export interface IPublicTypeCallbacks {
onMoveHook?: (currentNode: IPublicModelNode) => boolean;
// thinkof 限制性拖拽
onHoverHook?: (currentNode: IPublicModelNode) => boolean;
/** 选中 hook如果返回值是 false可以控制组件不可被选中 */
onSelectHook?: (currentNode: IPublicModelNode) => boolean;
onChildMoveHook?: (childNode: IPublicModelNode, currentNode: IPublicModelNode) => boolean;
// events

View File

@ -0,0 +1,32 @@
import { Asset } from '../../assets';
import {
IPublicTypeNodeInstance,
IPublicTypeProjectSchema,
IPublicTypeComponentSchema,
} from './';
export interface IPublicTypeSimulatorRenderer<Component, ComponentInstance> {
readonly isSimulatorRenderer: true;
autoRepaintNode?: boolean;
components: Record<string, Component>;
rerender: () => void;
createComponent(
schema: IPublicTypeProjectSchema<IPublicTypeComponentSchema>,
): Component | null;
getComponent(componentName: string): Component;
getClosestNodeInstance(
from: ComponentInstance,
nodeId?: string,
): IPublicTypeNodeInstance<ComponentInstance> | null;
findDOMNodes(instance: ComponentInstance): Array<Element | Text> | null;
getClientRects(element: Element | Text): DOMRect[];
setNativeSelection(enableFlag: boolean): void;
setDraggingState(state: boolean): void;
setCopyState(state: boolean): void;
loadAsyncLibrary(asyncMap: { [index: string]: any }): void;
clearState(): void;
stopAutoRepaintNode(): void;
enableAutoRepaintNode(): void;
run(): void;
load(asset: Asset): Promise<any>;
}

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-utils",
"version": "1.1.6",
"version": "1.1.7",
"description": "Utils for Ali lowCode engine",
"files": [
"lib",
@ -14,7 +14,7 @@
},
"dependencies": {
"@alifd/next": "^1.19.16",
"@alilc/lowcode-types": "1.1.6",
"@alilc/lowcode-types": "1.1.7",
"lodash": "^4.17.21",
"mobx": "^6.3.0",
"react": "^16"

View File

@ -81,6 +81,7 @@ const stageList = [
'init',
'upgrade',
];
/**
*
* @param stage
@ -108,4 +109,18 @@ export function deprecate(fail: any, message: string, alterative?: string) {
export function isRegExp(obj: any): obj is RegExp {
return obj && obj.test && obj.exec && obj.compile;
}
/**
* The prop supportVariable SHOULD take precedence over default global supportVariable.
* @param propSupportVariable prop supportVariable
* @param globalSupportVariable global supportVariable
* @returns
*/
export function shouldUseVariableSetter(
propSupportVariable: boolean | undefined,
globalSupportVariable: boolean,
) {
if (propSupportVariable === false) return false;
return propSupportVariable || globalSupportVariable;
}

View File

@ -0,0 +1,9 @@
import { shouldUseVariableSetter } from '../../src/misc';
it('shouldUseVariableSetter', () => {
expect(shouldUseVariableSetter(false, true)).toBeFalsy();
expect(shouldUseVariableSetter(true, true)).toBeTruthy();
expect(shouldUseVariableSetter(true, false)).toBeTruthy();
expect(shouldUseVariableSetter(undefined, false)).toBeFalsy();
expect(shouldUseVariableSetter(undefined, true)).toBeTruthy();
});

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-workspace",
"version": "1.1.6",
"version": "1.1.7",
"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.6",
"@alilc/lowcode-editor-core": "1.1.6",
"@alilc/lowcode-editor-skeleton": "1.1.6",
"@alilc/lowcode-types": "1.1.6",
"@alilc/lowcode-utils": "1.1.6",
"@alilc/lowcode-designer": "1.1.7",
"@alilc/lowcode-editor-core": "1.1.7",
"@alilc/lowcode-editor-skeleton": "1.1.7",
"@alilc/lowcode-types": "1.1.7",
"@alilc/lowcode-utils": "1.1.7",
"classnames": "^2.2.6",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",

View File

@ -77,7 +77,18 @@ export class EditorWindow implements IEditorWindow {
const saveResult = await this.editorViews.get(name)?.save();
value[name] = saveResult;
}
return await this.resource.save(value);
const result = await this.resource.save(value);
this.emitter.emit('handle.save');
return result;
}
onSave(fn: () => void) {
this.emitter.on('handle.save', fn);
return () => {
this.emitter.off('handle.save', fn);
};
}
async init() {