Merge branch 'release/1.7.0'

This commit is contained in:
JackLian 2023-05-31 14:55:08 +08:00
commit 0e90ea81bb
171 changed files with 1769 additions and 620 deletions

33
.github/workflows/check base branch.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: Check Base Branch
on:
pull_request:
types: [opened]
jobs:
code-review:
name: Check
runs-on: ubuntu-latest
steps:
# 判断用户是否有写仓库权限
- name: 'Check User Permission'
uses: 'lannonbr/repo-permission-check-action@2.0.0'
with:
permission: 'write'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 'Check base branch name is develop or not'
if: github.event.pull_request.base.ref != 'develop' # check the target branch if it's master
uses: actions-cool/issues-helper@v2
with:
actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.pull_request.number }}
body: |
感谢你的 PR根据引擎的 [研发协作流程](https://lowcode-engine.cn/site/docs/participate/flow),请将目标合入分支设置为 **develop**。
Thanks in advance, according to the [Contribution Guideline](https://lowcode-engine.cn/site/docs/participate/flow), please set the base branch to **develop**.
@${{ github.event.pull_request.user.login }}

View File

@ -73,7 +73,7 @@ jobs:
package-manager: yarn
annotations: none
cov-utils:
cov-utils:
runs-on: ubuntu-latest
# skip fork's PR, otherwise it fails while making a comment
if: ${{ github.event.pull_request.head.repo.full_name == 'alibaba/lowcode-engine' }}
@ -91,6 +91,6 @@ cov-utils:
- uses: ArtiomTr/jest-coverage-report-action@v2
with:
working-directory: packages/utils
test-script: npm test
test-script: npm test -- --jest-ci --jest-json --jest-coverage --jest-testLocationInResults --jest-outputFile=report.json
package-manager: yarn
annotations: none

View File

@ -1,4 +1,4 @@
name: Issue Reply
name: Help Wanted
on:
issues:

View File

@ -1,4 +1,4 @@
name: Issue Reply
name: Insufficient Info
on:
issues:

View File

@ -1,4 +1,4 @@
name: lint & test
name: Lint & Test (Mods)
on:
push:

View File

@ -1,4 +1,4 @@
name: lint & test
name: Lint & Test (Pkgs)
on:
push:
@ -43,7 +43,7 @@ jobs:
- name: test
run: cd packages/designer && npm test
editor-skeleton:
test-editor-skeleton:
runs-on: ubuntu-latest
steps:
- name: checkout
@ -58,3 +58,51 @@ jobs:
- name: test
run: cd packages/editor-skeleton && npm test
test-renderer-core:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
- name: install
run: npm i && npm run setup:skip-build
- name: test
run: cd packages/renderer-core && npm test
test-react-simulator-renderer:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
- name: install
run: npm i && npm run setup:skip-build
- name: test
run: cd packages/react-simulator-renderer && npm test
test-utils:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
- name: install
run: npm i && npm run setup:skip-build
- name: test
run: cd packages/utils && npm test

View File

@ -95,6 +95,17 @@ common.utils.startTransaction(() => {
}, IPublicEnumTransitionType.repaint);
```
#### getConvertedExtraKey
props key 转化工具
```typescript
getConvertedExtraKey(key: string): string
```
**@since v1.0.17**
#### createIntl
i18n 相关工具
```typescript

View File

@ -105,6 +105,17 @@ getPreference(): IPublicModelPreference;
**@since v1.1.0**
示例
```javascript
import { config } from '@alilc/lowcode-engine';
const panelName = 'outline-master-pane';
// 设置大纲树面板钉住,在大纲树下次重新打开时生效
config.getPreference().set(`${panelName}-pinned-status-isFloat`, false, 'skeleton')
```
## 事件
### onceGot

View File

@ -245,6 +245,7 @@ material.getComponentMeta('Input');
```
#### getComponentMetasMap
获取所有已注册的物料元数据
```typescript
@ -264,6 +265,15 @@ import { material } from '@alilc/lowcode-engine';
material.getComponentMetasMap();
```
#### refreshComponentMetasMap
刷新 componentMetasMap可触发模拟器里的 components 重新构建
**@since v1.1.7**
```typescript
refreshComponentMetasMap(): void;
```
### 物料元数据管道函数
#### registerMetadataTransducer

View File

@ -0,0 +1,21 @@
---
title: EditorView
sidebar_position: 12
---
> **[@experimental](./#experimental)**<br/>
> **@types** [IPublicModelEditorView](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/editor-view.ts)<br/>
> **@since** v1.1.7
窗口编辑视图
## 类型定义
```
import { IPublicModelPluginContext } from "./plugin-context";
export interface IPublicModelEditorView extends IPublicModelPluginContext {};
```
相关类型定义: [IPublicModelPluginContext](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/plugin-context.ts)

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

@ -38,6 +38,25 @@ sidebar_position: 12
关联模型 [IPublicModelResource](./resource)
### currentEditorView
窗口当前视图
`@type {IPublicModelEditorView}`
关联模型 [IPublicModelEditorView](./editor-view)
**@since v1.1.7**
### editorViews
窗口所有视图
`@type {IPublicModelEditorView[]}`
关联模型 [IPublicModelEditorView](./editor-view)
**@since v1.1.7**
## 方法
### importSchema
@ -74,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

@ -145,7 +145,7 @@ export default class AltStringSetter extends React.PureComponent<AltStringSetter
```typescript
import AltStringSetter from './AltStringSetter';
import { setters } from '@alilc/lowcode-engine';
const { registerSetter } = registerSetter;
const { registerSetter } = setters;
registerSetter('AltStringSetter', AltStringSetter);
```
注册之后,我们就可以在物料中使用了,其中核心配置如下:

View File

@ -45,16 +45,13 @@ window.Next.Message.success('成功')
- 读取:每次打开面板时,都会尝试读取 schema 中的 originCode 字段,如果没有,则从 schema 上的字段还原代码;
- 写入:在关闭代码编辑面板(主动点击叉或者点击非代码编辑区块的被动关闭都算)时,将自动写入到 schema 中;您也可以在编辑过程中点击“保存”按钮手动保存;
| 源码面板中 | schema 中 |
| 源码面板中 | Schema 中 |
| --- | --- |
| 本地数据初始值设置:
![image.png](https://img.alicdn.com/imgextra/i4/O1CN01V6iaTY1gVNHi7gQfK_!!6000000004147-2-tps-370-146.png) | ![image.png](https://img.alicdn.com/imgextra/i2/O1CN010rhIPa268BEfGmzO6_!!6000000007616-2-tps-2098-826.png) |
| 生命周期方法:
![image.png](https://img.alicdn.com/imgextra/i4/O1CN010Y1TxV1QOvrVLRUjD_!!6000000001967-2-tps-478-260.png) | ![image.png](https://img.alicdn.com/imgextra/i3/O1CN01pbJzVQ1VSfAL7Lh8G_!!6000000002652-2-tps-2010-836.png) |
| 自定义函数:
![image.png](https://img.alicdn.com/imgextra/i4/O1CN01S2gjFk1CU3fm61eiD_!!6000000000083-2-tps-660-642.png) | ![image.png](https://img.alicdn.com/imgextra/i2/O1CN01X35YxU1GUkjj1YWVj_!!6000000000626-2-tps-1862-822.png) |
| 编译前全量代码:
![image.png](https://img.alicdn.com/imgextra/i2/O1CN01sbiK9N1kc1Uxp1OHY_!!6000000004703-2-tps-762-1122.png) | ![image.png](https://img.alicdn.com/imgextra/i3/O1CN01adKSg61QXAzRjQ4bm_!!6000000001985-2-tps-1906-796.png) |
| 本地数据初始值设置:![image.png](https://img.alicdn.com/imgextra/i4/O1CN01V6iaTY1gVNHi7gQfK_!!6000000004147-2-tps-370-146.png) | ![image.png](https://img.alicdn.com/imgextra/i2/O1CN010rhIPa268BEfGmzO6_!!6000000007616-2-tps-2098-826.png) |
| 生命周期方法:![image.png](https://img.alicdn.com/imgextra/i4/O1CN010Y1TxV1QOvrVLRUjD_!!6000000001967-2-tps-478-260.png) | ![image.png](https://img.alicdn.com/imgextra/i3/O1CN01pbJzVQ1VSfAL7Lh8G_!!6000000002652-2-tps-2010-836.png) |
| 自定义函数:![image.png](https://img.alicdn.com/imgextra/i4/O1CN01S2gjFk1CU3fm61eiD_!!6000000000083-2-tps-660-642.png) | ![image.png](https://img.alicdn.com/imgextra/i2/O1CN01X35YxU1GUkjj1YWVj_!!6000000000626-2-tps-1862-822.png) |
| 编译前全量代码:![image.png](https://img.alicdn.com/imgextra/i2/O1CN01sbiK9N1kc1Uxp1OHY_!!6000000004703-2-tps-762-1122.png) | ![image.png](https://img.alicdn.com/imgextra/i3/O1CN01adKSg61QXAzRjQ4bm_!!6000000001985-2-tps-1906-796.png) |
- 异常处理:如果代码解析失败,它将无法被正常保存到 schema 中,此时编辑器会弹层提示:

View File

@ -336,7 +336,7 @@ simulator-renderer 通过调用 host 的方法,将 schema、components 等参
1. **画布内拖拽:**此时 sensor 是 simulatorHost拖拽完成之后会根据拖拽的位置来完成节点的精确插入。
2. **从组件面板拖拽到画布**:此时的 sensor 还是 simulatorHost因为拖拽结束的目标还是画布。
3. **大纲树面板拖拽到画布中**:此时有两个 sensor一个是大纲树当我们拖拽到画布区域时画布区域内的 simulatorHost 开始接管。
4. **画布拖拽到画布中**:从画布中开始拖拽时,最新生效的是 simulatorHost当离开画布到大纲树时大纲树 sensor 开始接管生效。当拖拽到大纲树的某一个节点下时,大纲树会将大纲树中的信息转化为 schema然后渲染到画布中。
4. **画布拖拽到大纲树中**:从画布中开始拖拽时,最新生效的是 simulatorHost当离开画布到大纲树时大纲树 sensor 开始接管生效。当拖拽到大纲树的某一个节点下时,大纲树会将大纲树中的信息转化为 schema然后渲染到画布中。
### 其他
引擎的编排能力远远不止上述所描述的功能,这里只描述了其核心和关键的功能。在整个引擎的迭代和设计过程中还有很多细节来使我们的引擎更好用、更容易扩展。

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

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-engine-docs",
"version": "1.0.25",
"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

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-code-generator",
"version": "1.1.0",
"version": "1.1.2",
"description": "出码引擎 for LowCode Engine",
"license": "MIT",
"main": "lib/index.js",

View File

@ -9,7 +9,7 @@ import { createModuleBuilder } from './generator/ModuleBuilder';
import { createDiskPublisher } from './publisher/disk';
import { createZipPublisher } from './publisher/zip';
import createIceJsProjectBuilder, { plugins as icejsPlugins } from './solutions/icejs';
import createIce3JsProjectBuilder, { plugins as icejs3Plugins } from './solutions/icejs3';
import createIceJs3ProjectBuilder, { plugins as icejs3Plugins } from './solutions/icejs3';
import createRaxAppProjectBuilder, { plugins as raxPlugins } from './solutions/rax-app';
// 引入说明
@ -42,7 +42,7 @@ export default {
createModuleBuilder,
solutions: {
icejs: createIceJsProjectBuilder,
icejs3: createIce3JsProjectBuilder,
icejs3: createIceJs3ProjectBuilder,
rax: createRaxAppProjectBuilder,
},
solutionParts: {

View File

@ -32,7 +32,7 @@ import {
import { SUPPORT_SCHEMA_VERSION_LIST } from '../const';
import { getErrorMessage } from '../utils/errors';
import { handleSubNodes, isValidContainerType } from '../utils/schema';
import { handleSubNodes, isValidContainerType, ContainerType } from '../utils/schema';
import { uniqueArray } from '../utils/common';
import { componentAnalyzer } from '../analyzer/componentAnalyzer';
import { ensureValidClassName } from '../utils/validate';
@ -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) {
@ -161,7 +166,8 @@ export class SchemaParser implements ISchemaParser {
...subRoot,
componentName: getRootComponentName(subRoot.componentName, compDeps),
containerType: subRoot.componentName,
moduleName: ensureValidClassName(changeCase.pascalCase(subRoot.fileName)),
moduleName: ensureValidClassName(subRoot.componentName === ContainerType.Component ?
subRoot.fileName : changeCase.pascalCase(subRoot.fileName)),
};
return container;
});
@ -220,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

@ -21,6 +21,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
if (ir && ir.deps && ir.deps.length > 0) {
let lowcodeMaterialsStyleAdded = false;
let fusionUIStyleAdded = false;
let nextStyleAddedMap: Record<string, boolean> = {};
ir.deps.forEach((dep: any) => {
if (dep.package === '@alifd/next' && !nextStyleAddedMap[dep.exportName]) {
@ -41,6 +42,15 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport],
});
lowcodeMaterialsStyleAdded = true;
} else if (dep.package === '@alifd/fusion-ui' && !fusionUIStyleAdded) {
chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.InternalDepsImport,
content: 'import \'@alifd/fusion-ui/lib/style\';',
linkAfter: [COMMON_CHUNK_NAME.ExternalDepsImport],
});
fusionUIStyleAdded = true;
}
});
}

View File

@ -48,7 +48,7 @@ const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
type: ChunkType.STRING,
fileType: FileType.JSX,
name: CLASS_DEFINE_CHUNK_NAME.InsVar,
content: `static displayName = '${changeCase.pascalCase(ir.moduleName)}';`,
content: `static displayName = '${ir.moduleName}';`,
linkAfter: [
CLASS_DEFINE_CHUNK_NAME.Start,
],

View File

@ -58,7 +58,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
generateCompositeType(
{
type: 'JSFunction',
value: input.value || 'null',
value: input.value || 'function () {}',
},
Scope.createRootScope(),
),

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

@ -8,7 +8,8 @@ import './polyfills/buffer';
import { createProjectBuilder } from './generator/ProjectBuilder';
import { createModuleBuilder } from './generator/ModuleBuilder';
import { createZipPublisher } from './publisher/zip';
import createIceJsProjectBuilder, { plugins as reactPlugins } from './solutions/icejs';
import createIceJsProjectBuilder, { plugins as icejsPlugins } from './solutions/icejs';
import createIceJs3ProjectBuilder, { plugins as icejs3Plugins } from './solutions/icejs3';
import createRaxAppProjectBuilder, { plugins as raxPlugins } from './solutions/rax-app';
// 引入说明
@ -18,6 +19,7 @@ import { COMMON_CHUNK_NAME, CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '
// 引入通用插件组
import esmodule from './plugins/common/esmodule';
import requireUtils from './plugins/common/requireUtils';
import styleImport from './plugins/common/styleImport';
import css from './plugins/component/style/css';
import constants from './plugins/project/constants';
@ -32,6 +34,7 @@ import * as CONSTANTS from './const';
// 引入内置解决方案模块
import icejs from './plugins/project/framework/icejs';
import icejs3 from './plugins/project/framework/icejs3';
import rax from './plugins/project/framework/rax';
export default {
@ -39,10 +42,12 @@ export default {
createModuleBuilder,
solutions: {
icejs: createIceJsProjectBuilder,
icejs3: createIceJs3ProjectBuilder,
rax: createRaxAppProjectBuilder,
},
solutionParts: {
icejs,
icejs3,
rax,
},
publishers: {
@ -50,6 +55,7 @@ export default {
},
plugins: {
common: {
/**
* ES Module
* @deprecated please use esModule
@ -57,12 +63,7 @@ export default {
esmodule,
esModule: esmodule,
requireUtils,
},
react: {
...reactPlugins,
},
rax: {
...raxPlugins,
styleImport,
},
style: {
css,
@ -72,6 +73,22 @@ export default {
i18n,
utils,
},
icejs: {
...icejsPlugins,
},
icejs3: {
...icejs3Plugins,
},
rax: {
...raxPlugins,
},
/**
* @deprecated please use icejs
*/
react: {
...icejsPlugins,
},
},
postprocessor: {
prettier,

View File

@ -148,3 +148,9 @@ export function isValidContainerType(schema: IPublicTypeNodeSchema) {
'Block',
].includes(schema.componentName);
}
export const enum ContainerType {
Page = 'Page',
Component = 'Component',
Block = 'Block',
}

View File

@ -52,7 +52,7 @@
"yarn": "^1.22.17",
"rimraf": "^3.0.2",
"@types/react-router": "5.1.18",
"build-plugin-component": "^1.12.0",
"@alilc/build-plugin-lce": "^0.0.3",
"babel-jest": "^26.5.2",
"@alilc/lowcode-test-mate": "^1.0.1"
},

View File

@ -1,5 +1,5 @@
{
"plugins": [
"build-plugin-component"
"@alilc/build-plugin-lce"
]
}

View File

@ -1,6 +1,6 @@
{
"plugins": [
"build-plugin-component",
"@alilc/build-plugin-lce",
"@alilc/lowcode-test-mate/plugin/index.ts"
],
"babelPlugins": [

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",
@ -9,15 +9,15 @@
"es"
],
"scripts": {
"build": "build-scripts build --skip-demo",
"build": "build-scripts build",
"test": "build-scripts test --config build.test.json",
"test:cov": "build-scripts test --config build.test.json --jest-coverage"
},
"license": "MIT",
"dependencies": {
"@alilc/lowcode-editor-core": "1.1.6",
"@alilc/lowcode-types": "1.1.6",
"@alilc/lowcode-utils": "1.1.6",
"@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,10 +1,10 @@
import React, { Component, Fragment } from 'react';
import DragResizeEngine from './drag-resize-engine';
import { observer, computed, globalContext } from '@alilc/lowcode-editor-core';
import { observer, computed } from '@alilc/lowcode-editor-core';
import classNames from 'classnames';
import { SimulatorContext } from '../context';
import { BuiltinSimulatorHost } from '../host';
import { OffsetObserver, Designer } from '../../designer';
import { OffsetObserver, Designer, INode } from '../../designer';
import { Node } from '../../document';
import { normalizeTriggers } from '../../utils/misc';
@ -135,7 +135,7 @@ export class BoxResizingInstance extends Component<{
// this.hoveringCapture.setBoundary(this.outline);
this.willBind();
const resize = (e: MouseEvent, direction: string, node: any, moveX: number, moveY: number) => {
const resize = (e: MouseEvent, direction: string, node: INode, moveX: number, moveY: number) => {
const { advanced } = node.componentMeta;
if (
advanced.callbacks &&
@ -149,7 +149,7 @@ export class BoxResizingInstance extends Component<{
}
};
const resizeStart = (e: MouseEvent, direction: string, node: any) => {
const resizeStart = (e: MouseEvent, direction: string, node: INode) => {
const { advanced } = node.componentMeta;
if (
advanced.callbacks &&
@ -161,7 +161,7 @@ export class BoxResizingInstance extends Component<{
}
};
const resizeEnd = (e: MouseEvent, direction: string, node: any) => {
const resizeEnd = (e: MouseEvent, direction: string, node: INode) => {
const { advanced } = node.componentMeta;
if (
advanced.callbacks &&
@ -172,8 +172,7 @@ export class BoxResizingInstance extends Component<{
advanced.callbacks.onResizeEnd(e, cbNode);
}
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const editor = node.document?.designer.editor;
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||

View File

@ -9,7 +9,7 @@ import {
ComponentType,
} from 'react';
import classNames from 'classnames';
import { observer, computed, Tip, globalContext } from '@alilc/lowcode-editor-core';
import { observer, computed, Tip } from '@alilc/lowcode-editor-core';
import { createIcon, isReactComponent, isActionContentObject } from '@alilc/lowcode-utils';
import { IPublicTypeActionContentObject } from '@alilc/lowcode-types';
import { BuiltinSimulatorHost } from '../host';
@ -131,8 +131,7 @@ function createAction(content: ReactNode | ComponentType<any> | IPublicTypeActio
className="lc-borders-action"
onClick={() => {
action && action(node.internalToShellNode()!);
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const editor = node.document?.designer.editor;
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||

View File

@ -20,7 +20,9 @@ export function createSimulator(
): Promise<BuiltinSimulatorRenderer> {
const win: any = iframe.contentWindow;
const doc = iframe.contentDocument!;
const innerPlugins = host.designer.editor.get('innerPlugins');
win.AliLowCodeEngine = innerPlugins._getLowCodePluginContext({});
win.LCSimulatorHost = host;
win._ = window._;

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { observer, globalContext } from '@alilc/lowcode-editor-core';
import { observer } from '@alilc/lowcode-editor-core';
import { BuiltinSimulatorHost, BuiltinSimulatorProps } from './host';
import { BemTools } from './bem-tools';
import { Project } from '../project';
@ -76,8 +76,7 @@ class Content extends Component<{ host: BuiltinSimulatorHost }> {
private dispose?: () => void;
componentDidMount() {
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const editor = this.props.host.designer.editor;
const onEnableEvents = (type: boolean) => {
this.setState({
disabledEvents: type,

View File

@ -1,6 +1,6 @@
import { obx, globalContext } from '@alilc/lowcode-editor-core';
import { obx } from '@alilc/lowcode-editor-core';
import { IPublicTypePluginConfig, IPublicTypeLiveTextEditingConfig } from '@alilc/lowcode-types';
import { Node, Prop } from '../../document';
import { INode, Prop } from '../../document';
const EDITOR_KEY = 'data-setter-prop';
@ -17,7 +17,7 @@ function defaultSaveContent(content: string, prop: Prop) {
}
export interface EditingTarget {
node: Node;
node: INode;
rootElement: HTMLElement;
event: MouseEvent;
}
@ -47,13 +47,16 @@ export class LiveEditing {
@obx.ref private _editing: Prop | null = null;
private _dispose?: () => void;
private _save?: () => void;
apply(target: EditingTarget) {
const { node, event, rootElement } = target;
const targetElement = event.target as HTMLElement;
const { liveTextEditing } = node.componentMeta;
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const editor = node.document?.designer.editor;
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') || node?.componentMeta?.componentName || '';
@ -166,10 +169,6 @@ export class LiveEditing {
return this._editing;
}
private _dispose?: () => void;
private _save?: () => void;
saveAndDispose() {
if (this._save) {
this._save();

View File

@ -1,6 +1,6 @@
import { Overlay } from '@alifd/next';
import React, { MouseEvent } from 'react';
import { Title, globalContext } from '@alilc/lowcode-editor-core';
import { Title } from '@alilc/lowcode-editor-core';
import { canClickNode } from '@alilc/lowcode-utils';
import './index.less';
@ -66,8 +66,7 @@ export default class InstanceNodeSelector extends React.Component<IProps, IState
if (canClick && typeof node.select === 'function') {
node.select();
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const editor = node.document?.designer.editor;
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||

View File

@ -56,8 +56,8 @@ export class ComponentActions {
const { isRGL, rglNode } = node.getRGL();
if (isRGL) {
// 复制 layout 信息
let layout = rglNode.getPropValue('layout') || [];
let curLayout = layout.filter((item) => item.i === node.getPropValue('fieldId'));
const layout = rglNode.getPropValue('layout') || [];
const curLayout = layout.filter((item) => item.i === node.getPropValue('fieldId'));
if (curLayout && curLayout[0]) {
layout.push({
...curLayout[0],

View File

@ -60,6 +60,8 @@ export function buildFilter(rule?: string | string[] | RegExp | IPublicTypeNesti
export interface IComponentMeta extends IPublicModelComponentMeta<INode> {
prototype?: any;
liveTextEditing?: IPublicTypeLiveTextEditingConfig[];
get rootSelector(): string | undefined;
setMetadata(metadata: IPublicTypeComponentMetadata): void;

View File

@ -7,6 +7,8 @@ import {
import { isNode } from '@alilc/lowcode-utils';
export interface IActiveTracker extends Omit< IPublicModelActiveTracker, 'track' | 'onChange' > {
_target: ActiveTarget | INode;
track(originalTarget: ActiveTarget | INode): void;
onChange(fn: (target: ActiveTarget) => void): () => void;
@ -17,10 +19,10 @@ export interface ActiveTarget extends Omit< IPublicTypeActiveTarget, 'node' > {
}
export class ActiveTracker implements IActiveTracker {
private emitter: IEventBus = createModuleEventBus('ActiveTracker');
@obx.ref private _target?: ActiveTarget | INode;
private emitter: IEventBus = createModuleEventBus('ActiveTracker');
track(originalTarget: ActiveTarget | INode) {
let target = originalTarget;
if (isNode(originalTarget)) {

View File

@ -141,13 +141,17 @@ export interface IDocumentModel extends Omit<IPublicModelDocumentModel<
insertNodes(parent: INode, thing: INode[] | IPublicTypeNodeData[], at?: number | null, copy?: boolean): INode[];
open(): DocumentModel;
open(): IDocumentModel;
remove(): void;
suspense(): void;
close(): void;
unlinkNode(node: INode): void;
destroyNode(node: INode): void;
}
export class DocumentModel implements IDocumentModel {
@ -333,6 +337,7 @@ export class DocumentModel implements IDocumentModel {
this.import(schema as IPublicTypeRootSchema, true);
this.simulator?.rerender();
},
this,
);
this.setupListenActiveNodes();
@ -834,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

@ -1,6 +1,7 @@
import { reaction, untracked, globalContext, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { reaction, untracked, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { IPublicTypeNodeSchema, IPublicModelHistory, IPublicTypeDisposable } from '@alilc/lowcode-types';
import { Logger } from '@alilc/lowcode-utils';
import { IDocumentModel } from '../designer';
const logger = new Logger({ level: 'warn', bizName: 'history' });
@ -37,10 +38,12 @@ export class History<T = IPublicTypeNodeSchema> implements IHistory {
return this.session.data;
}
private timeGap: number = 1000;
constructor(
dataFn: () => T | null,
private redoer: (data: T) => void,
private timeGap: number = 1000,
private document?: IDocumentModel,
) {
this.session = new Session(0, null, this.timeGap);
this.records = [this.session];
@ -130,8 +133,7 @@ export class History<T = IPublicTypeNodeSchema> implements IHistory {
}
const cursor = this.session.cursor - 1;
this.go(cursor);
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const editor = this.document?.designer.editor;
if (!editor) {
return;
}
@ -144,8 +146,7 @@ export class History<T = IPublicTypeNodeSchema> implements IHistory {
}
const cursor = this.session.cursor + 1;
this.go(cursor);
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const editor = this.document?.designer.editor;
if (!editor) {
return;
}

View File

@ -1,4 +1,4 @@
import { obx, computed, globalContext, makeObservable, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { obx, computed, makeObservable, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { Node, INode } from './node';
import { IPublicTypeNodeData, IPublicModelNodeChildren, IPublicEnumTransformStage, IPublicTypeDisposable } from '@alilc/lowcode-types';
import { shallowEqual, compatStage, isNodeSchema } from '@alilc/lowcode-utils';
@ -16,12 +16,12 @@ export interface INodeChildren extends Omit<IPublicModelNodeChildren<INode>,
'isEmpty' |
'notEmpty'
> {
children: INode[];
get owner(): INode;
get length(): number;
children: INode[];
unlinkChild(node: INode): void;
/**
@ -239,11 +239,8 @@ export class NodeChildren implements INodeChildren {
}
const { document } = node;
/* istanbul ignore next */
if (globalContext.has('editor')) {
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
editor.eventBus.emit('node.remove', { node, index: i });
}
const editor = node.document?.designer.editor;
editor?.eventBus.emit('node.remove', { node, index: i });
document?.unlinkNode(node);
document?.selection.remove(node.id);
document?.destroyNode(node);
@ -281,14 +278,11 @@ export class NodeChildren implements INodeChildren {
const i = children.map(d => d.id).indexOf(node.id);
if (node.parent) {
if (globalContext.has('editor')) {
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
editor.eventBus.emit('node.remove.topLevel', {
node,
index: node.index,
});
}
const editor = node.document?.designer.editor;
editor?.eventBus.emit('node.remove.topLevel', {
node,
index: node.index,
});
}
if (i < 0) {
@ -317,11 +311,8 @@ export class NodeChildren implements INodeChildren {
});
this.emitter.emit('insert', node);
/* istanbul ignore next */
if (globalContext.has('editor')) {
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
editor.eventBus.emit('node.add', { node });
}
const editor = node.document?.designer.editor;
editor?.eventBus.emit('node.add', { node });
if (useMutator) {
this.reportModified(node, this.owner, { type: 'insert' });
}

View File

@ -16,6 +16,8 @@ export type UNSET = typeof UNSET;
export interface IProp extends Omit<IPublicModelProp<
INode
>, 'exportSchema' | 'node'>, IPropParent {
spread: boolean;
key: string | number | undefined;
readonly props: IProps;
@ -42,6 +44,8 @@ export interface IProp extends Omit<IPublicModelProp<
setupItems(): IProp[] | null;
isVirtual(): boolean;
get type(): ValueTypes;
get size(): number;
@ -336,13 +340,9 @@ export class Prop implements IProp, IPropParent {
if (!this._items) {
return this._value;
}
const values = this.items!.map((prop) => {
return this.items!.map((prop) => {
return prop?.export(stage);
});
if (values.every((val) => val === undefined)) {
return undefined;
}
return values;
}
}

View File

@ -17,6 +17,8 @@ import {
IPublicTypePluginDeclaration,
IPublicApiCanvas,
IPublicApiWorkspace,
IPublicEnumPluginRegisterLevel,
IPublicModelWindow,
} from '@alilc/lowcode-types';
import {
IPluginContextOptions,
@ -41,6 +43,8 @@ export default class PluginContext implements
pluginEvent: IPublicApiEvent;
canvas: IPublicApiCanvas;
workspace: IPublicApiWorkspace;
registerLevel: IPublicEnumPluginRegisterLevel;
editorWindow: IPublicModelWindow;
constructor(
options: IPluginContextOptions,

View File

@ -143,11 +143,10 @@ export class LowCodePluginManager implements ILowCodePluginManager {
}
async delete(pluginName: string): Promise<boolean> {
const idx = this.plugins.findIndex((plugin) => plugin.name === pluginName);
if (idx === -1) return false;
const plugin = this.plugins[idx];
const plugin = this.plugins.find(({ name }) => name === pluginName);
if (!plugin) return false;
await plugin.destroy();
const idx = this.plugins.indexOf(plugin);
this.plugins.splice(idx, 1);
return this.pluginsMap.delete(pluginName);
}

View File

@ -16,6 +16,8 @@ import {
IPublicApiWorkspace,
IPublicTypePluginMeta,
IPublicTypePluginRegisterOptions,
IPublicModelWindow,
IPublicEnumPluginRegisterLevel,
} from '@alilc/lowcode-types';
import PluginContext from './plugin-context';
@ -56,6 +58,8 @@ export interface ILowCodePluginContextPrivate {
set pluginEvent(event: IPublicApiEvent);
set canvas(canvas: IPublicApiCanvas);
set workspace(workspace: IPublicApiWorkspace);
set editorWindow(window: IPublicModelWindow);
set registerLevel(level: IPublicEnumPluginRegisterLevel);
}
export interface ILowCodePluginContextApiAssembler {
assembleApis(

View File

@ -1,7 +1,7 @@
import { obx, computed, makeObservable, action, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { IDesigner } from '../designer';
import { DocumentModel, isDocumentModel } from '../document';
import type { IDocumentModel } from "../document";
import type { IDocumentModel } from '../document';
import {
IPublicTypeComponentsMap,
IPublicEnumTransformStage,
@ -317,13 +317,13 @@ export class Project implements IProject {
doc = this.createDocument();
return doc.open();
}
if (typeof doc === 'string') {
const got = this.documents.find((item) => item.fileName === doc || item.id === doc);
if (typeof doc === 'string' || typeof doc === 'number') {
const got = this.documents.find((item) => item.fileName === String(doc) || String(item.id) === String(doc));
if (got) {
return got.open();
}
const data = this.data.componentsTree.find((data) => data.fileName === doc);
const data = this.data.componentsTree.find((data) => data.fileName === String(doc));
if (data) {
doc = this.createDocument(data);
return doc.open();

View File

@ -1,3 +1,4 @@
import { IPublicTypePluginMeta } from './../../../../lib/packages/types/src/shell/type/plugin-meta.d';
import '../fixtures/window';
import {
Editor,
@ -22,6 +23,7 @@ import { BuiltinSimulatorHost } from '../../src/builtin-simulator/host';
import { fireEvent } from '@testing-library/react';
import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory';
import { Setters, Workspace } from '@alilc/lowcode-shell';
import { ILowCodePluginContextApiAssembler, ILowCodePluginContextPrivate, LowCodePluginManager } from '@alilc/lowcode-designer';
describe('Host 测试', () => {
let editor: Editor;
@ -32,10 +34,20 @@ describe('Host 测试', () => {
beforeAll(() => {
editor = new Editor();
const innerWorkspace = new InnerWorkspace();
const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
assembleApis: (context: ILowCodePluginContextPrivate, pluginName: string, meta: IPublicTypePluginMeta) => {
context.project = project;
const eventPrefix = meta?.eventPrefix || 'common';
context.workspace = workspace;
},
};
const innerPlugins = new LowCodePluginManager(pluginContextApiAssembler);
const innerWorkspace = new InnerWorkspace(() => {}, {});
const workspace = new Workspace(innerWorkspace);
editor.set('innerHotkey', new InnerHotkey())
editor.set('setters', new Setters(new InnerSetters()));
editor.set('innerPlugins' as any, innerPlugins);
!globalContext.has(Editor) && globalContext.register(editor, Editor);
!globalContext.has('workspace') && globalContext.register(innerWorkspace, 'workspace');
});

View File

@ -435,7 +435,7 @@ describe('Prop 类测试', () => {
it('should return undefined when all items are undefined', () => {
prop = new Prop(mockPropsInst, [undefined, undefined], '___loopArgs___');
expect(prop.getValue()).toBeUndefined();
expect(prop.getValue()).toEqual([undefined, undefined]);
});
it('迭代器 / map / forEach', () => {

View File

@ -1,6 +1,6 @@
{
"plugins": [
"build-plugin-component",
"@alilc/build-plugin-lce",
"build-plugin-fusion",
"./build.plugin.js"
]

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",
@ -10,12 +10,12 @@
"es"
],
"scripts": {
"build": "build-scripts build --skip-demo"
"build": "build-scripts build"
},
"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

@ -145,6 +145,16 @@ const VALID_ENGINE_OPTIONS = {
type: 'function',
description: '配置指定节点为根组件',
},
enableAutoOpenFirstWindow: {
type: 'boolean',
description: '应用级设计模式下,自动打开第一个窗口',
default: true,
},
enableWorkspaceMode: {
type: 'boolean',
description: '是否开启应用级设计模式',
default: false,
},
};
const getStrictModeValue = (engineOptions: IPublicTypeEngineOptions, defaultValue: boolean): boolean => {

View File

@ -1,8 +1,7 @@
import { ReactNode } from 'react';
import { IPublicTypeCustomView, IPublicTypeRegisteredSetter } from '@alilc/lowcode-types';
import { IPublicApiSetters, IPublicTypeCustomView, IPublicTypeRegisteredSetter } from '@alilc/lowcode-types';
import { createContent, isCustomView } from '@alilc/lowcode-utils';
const settersMap = new Map<string, IPublicTypeRegisteredSetter & {
type: string;
}>();
@ -44,13 +43,17 @@ function getInitialFromSetter(setter: any) {
) || null; // eslint-disable-line
}
export class Setters {
constructor(readonly viewName: string = 'global') {}
export interface ISetters extends IPublicApiSetters {
}
export class Setters implements ISetters {
settersMap = new Map<string, IPublicTypeRegisteredSetter & {
type: string;
}>();
constructor(readonly viewName: string = 'global') {}
getSetter = (type: string): IPublicTypeRegisteredSetter | null => {
return this.settersMap.get(type) || null;
};

View File

@ -1,6 +1,6 @@
{
"plugins": [
"build-plugin-component",
"@alilc/build-plugin-lce",
"build-plugin-fusion",
["build-plugin-moment-locales", {
"locales": ["zh-cn"]

View File

@ -1,6 +1,6 @@
{
"plugins": [
"build-plugin-component",
"@alilc/build-plugin-lce",
"@alilc/lowcode-test-mate/plugin/index.ts"
],
"babelPlugins": [

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",
@ -11,7 +11,7 @@
],
"scripts": {
"test": "build-scripts test --config build.test.json",
"build": "build-scripts build --skip-demo"
"build": "build-scripts build"
},
"keywords": [
"lowcode",
@ -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

@ -4,6 +4,22 @@ import { uniqueId } from '@alilc/lowcode-utils';
import { IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import './style.less';
export interface PopupExtProps {
width?: number;
hasMask?: boolean;
trigger?: ReactNode;
canCloseByOutSideClick?: boolean
className?: string;
safeNode?: string[];
}
interface PopupProps extends PopupExtProps{
content?: ReactNode,
title?: ReactNode,
actionKey?: string
}
export const PopupContext = createContext<PopupPipe>({} as any);
export class PopupPipe {
@ -11,7 +27,7 @@ export class PopupPipe {
private currentId?: string;
create(props?: object): {
create(props?: PopupExtProps): {
send: (content: ReactNode, title: ReactNode) => void;
show: (target: Element) => void;
} {
@ -45,13 +61,13 @@ export class PopupPipe {
};
}
private popup(props: object, target?: Element) {
private popup(props: PopupProps, target?: Element) {
Promise.resolve().then(() => {
this.emitter.emit('popupchange', props, target);
});
}
onPopupChange(fn: (props: object, target?: Element) => void): () => void {
onPopupChange(fn: (props: PopupProps, target?: Element) => void): () => void {
this.emitter.on('popupchange', fn);
return () => {
this.emitter.removeListener('popupchange', fn);
@ -86,18 +102,23 @@ export default class PopupService extends Component<{
}
}
interface StateType extends PopupProps {
visible?: boolean,
offsetX?: number,
pos?: {top: number, height: number}
}
export class PopupContent extends PureComponent<{ safeId?: string; popupContainer?: string }> {
static contextType = PopupContext;
popupContainerId = uniqueId('popupContainer');
state: any = {
state: StateType = {
visible: false,
offsetX: -300,
};
private dispose = (this.context as PopupPipe).onPopupChange((props, target) => {
const state: any = {
const state: StateType = {
...props,
visible: true,
};
@ -132,7 +153,7 @@ export class PopupContent extends PureComponent<{ safeId?: string; popupContaine
};
render() {
const { content, visible, title, actionKey, pos, offsetX } = this.state;
const { content, visible, title, actionKey, pos, offsetX, width = 360, hasMask = false, canCloseByOutSideClick = true, safeNode = [] } = this.state;
if (!visible) {
return null;
}
@ -146,10 +167,10 @@ export class PopupContent extends PureComponent<{ safeId?: string; popupContaine
return (
<Drawer
width={360}
width={width}
visible={visible}
offset={[offsetX, 0]}
hasMask={false}
hasMask={hasMask}
onVisibleChange={(_visible, type) => {
if (avoidLaterHidden) {
return;
@ -160,11 +181,11 @@ export class PopupContent extends PureComponent<{ safeId?: string; popupContaine
}}
trigger={<div className="lc-popup-placeholder" style={pos} />}
triggerType="click"
canCloseByOutSideClick
canCloseByOutSideClick={canCloseByOutSideClick}
animation={false}
onClose={this.onClose}
id={this.props.safeId}
safeNode={id}
safeNode={[id, ...safeNode]}
closeable
container={this.props.popupContainer}
>

View File

@ -1,14 +1,13 @@
import { Component, MouseEvent, Fragment } from 'react';
import { shallowIntl, observer, obx, engineConfig, runInAction, globalContext } from '@alilc/lowcode-editor-core';
import { createContent, isJSSlot, isSetterConfig } from '@alilc/lowcode-utils';
import { Component, MouseEvent, Fragment, ReactNode } from 'react';
import { shallowIntl, observer, obx, engineConfig, runInAction } from '@alilc/lowcode-editor-core';
import { createContent, isJSSlot, isSetterConfig, shouldUseVariableSetter } from '@alilc/lowcode-utils';
import { Skeleton, Stage } from '@alilc/lowcode-editor-skeleton';
import { IPublicTypeCustomView } from '@alilc/lowcode-types';
import { IPublicApiSetters, IPublicTypeCustomView, IPublicTypeDynamicProps } from '@alilc/lowcode-types';
import { ISettingEntry, IComponentMeta, ISettingField, isSettingField, ISettingTopEntry } from '@alilc/lowcode-designer';
import { createField } from '../field';
import PopupService, { PopupPipe } from '../popup';
import { SkeletonContext } from '../../context';
import { intl } from '../../locale';
import { Setters } from '@alilc/lowcode-shell';
function isStandardComponent(componentMeta: IComponentMeta | null) {
if (!componentMeta) return false;
@ -40,7 +39,7 @@ class SettingFieldView extends Component<SettingFieldViewProps, SettingFieldView
stageName: string | undefined;
setters: Setters;
setters?: IPublicApiSetters;
constructor(props: SettingFieldViewProps) {
super(props);
@ -49,10 +48,10 @@ class SettingFieldView extends Component<SettingFieldViewProps, SettingFieldView
const { extraProps } = field;
const { display } = extraProps;
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const { stages } = editor.get('skeleton') as Skeleton;
this.setters = editor.get('setters');
const editor = field.designer?.editor;
const skeleton = editor?.get('skeleton') as Skeleton;
const { stages } = skeleton || {};
this.setters = editor?.get('setters');
let stageName;
if (display === 'entry') {
runInAction(() => {
@ -112,7 +111,9 @@ class SettingFieldView extends Component<SettingFieldViewProps, SettingFieldView
const { defaultValue } = extraProps;
const { setter } = this.field;
let setterProps: any = {};
let setterProps: {
setters?: (ReactNode | string)[];
} & Record<string, unknown> | IPublicTypeDynamicProps = {};
let setterType: any;
let initialValue: any = null;
@ -154,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,
@ -236,7 +243,7 @@ class SettingFieldView extends Component<SettingFieldViewProps, SettingFieldView
...extraProps,
},
!stageName &&
this.setters.createSetterContent(setterType, {
this.setters?.createSetterContent(setterType, {
...shallowIntl(setterProps),
forceInline: extraProps.forceInline,
key: field.id,
@ -291,9 +298,8 @@ class SettingGroupView extends Component<SettingGroupViewProps> {
const { field } = this.props;
const { extraProps } = field;
const { display } = extraProps;
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const { stages } = editor.get('skeleton') as Skeleton;
const editor = this.props.field.designer?.editor;
const { stages } = editor?.get('skeleton') as Skeleton;
// const items = field.items;
let stageName;
@ -343,15 +349,15 @@ class SettingGroupView extends Component<SettingGroupViewProps> {
}
}
export function createSettingFieldView(item: ISettingField | IPublicTypeCustomView, field: ISettingEntry, index?: number) {
if (isSettingField(item)) {
if (item.isGroup) {
return <SettingGroupView field={item} key={item.id} />;
export function createSettingFieldView(field: ISettingField | IPublicTypeCustomView, fieldEntry: ISettingEntry, index?: number) {
if (isSettingField(field)) {
if (field.isGroup) {
return <SettingGroupView field={field} key={field.id} />;
} else {
return <SettingFieldView field={item} key={item.id} />;
return <SettingFieldView field={field} key={field.id} />;
}
} else {
return createContent(item, { key: index, field });
return createContent(field, { key: index, field: fieldEntry });
}
}

View File

@ -53,8 +53,7 @@ export class SettingsPrimaryPane extends Component<ISettingsPrimaryPaneProps, {
}
renderBreadcrumb() {
const { settings } = this.main;
const { config } = this.props;
const { settings, editor } = this.main;
// const shouldIgnoreRoot = config.props?.ignoreRoot;
const { shouldIgnoreRoot } = this.state;
if (!settings) {
@ -73,8 +72,6 @@ export class SettingsPrimaryPane extends Component<ISettingsPrimaryPaneProps, {
);
}
const workspace = globalContext.get('workspace');
const editor = this.props.engineEditor;
const designer = editor.get('designer');
const current = designer?.currentSelection?.getNodes()?.[0];
let node: INode | null = settings.first;

View File

@ -1,7 +1,7 @@
import { Component, ReactElement } from 'react';
import { Icon } from '@alifd/next';
import classNames from 'classnames';
import { Title, observer, Tip, globalContext } from '@alilc/lowcode-editor-core';
import { Title, observer, Tip } from '@alilc/lowcode-editor-core';
import { DockProps } from '../../types';
import { PanelDock } from '../../widget/panel-dock';
import { composeTitle } from '../../widget/utils';
@ -116,14 +116,12 @@ export class DraggableLineView extends Component<{ panel: Panel }> {
}
// 抛出事件,对于有些需要 panel 插件随着 度变化进行再次渲染的由panel插件内部监听事件实现
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const editor = this.props.panel.skeleton.editor;
editor?.eventBus.emit('dockpane.drag', width);
}
onDragChange(type: 'start' | 'end') {
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const editor = this.props.panel.skeleton.editor;
editor?.eventBus.emit('dockpane.dragchange', type);
// builtinSimulator 屏蔽掉 鼠标事件
editor?.eventBus.emit('designer.builtinSimulator.disabledEvents', type === 'start');
@ -187,8 +185,7 @@ export class TitledPanelView extends Component<{ panel: Panel; area?: string }>
if (!panel.inited) {
return null;
}
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const editor = panel.skeleton.editor;
const panelName = area ? `${area}-${panel.name}` : panel.name;
editor?.eventBus.emit('skeleton.panel.toggle', {
name: panelName || '',
@ -250,8 +247,7 @@ export class PanelView extends Component<{
if (!panel.inited) {
return null;
}
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const editor = panel.skeleton.editor;
const panelName = area ? `${area}-${panel.name}` : panel.name;
editor?.eventBus.emit('skeleton.panel.toggle', {
name: panelName || '',

View File

@ -56,7 +56,8 @@ export interface ISkeleton extends Omit<IPublicApiSkeleton,
'onShowWidget' |
'onHideWidget' |
'remove' |
'hideArea'
'hideArea' |
'add'
> {
editor: IEditor;
@ -101,6 +102,8 @@ export interface ISkeleton extends Omit<IPublicApiSkeleton,
): WidgetContainer;
createPanel(config: PanelConfig): Panel;
add(config: IPublicTypeSkeletonConfig, extraConfig?: Record<string, any>): IWidget | Widget | Panel | Stage | Dock | PanelDock | undefined;
}
export class Skeleton {
@ -440,7 +443,7 @@ export class Skeleton {
return restConfig;
}
add(config: IPublicTypeSkeletonConfig, extraConfig?: Record<string, any>) {
add(config: IPublicTypeSkeletonConfig, extraConfig?: Record<string, any>): IWidget | Widget | Panel | Stage | Dock | PanelDock | undefined {
const parsedConfig = {
...this.parseConfig(config),
...extraConfig,

View File

@ -211,6 +211,10 @@ export class Panel implements IWidget {
this.setActive(false);
}
disable() {}
enable(): void {}
show() {
this.setActive(true);
}

View File

@ -14,7 +14,7 @@
[![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url]
[![codecov][codecov-image-url]][codecov-url]
[![codecov][codecov-image-url]][codecov-url] [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/lowcode-workspace/awesome-lowcode-engine)
[![](https://img.shields.io/badge/LowCodeEngine-%E6%9F%A5%E7%9C%8B%E8%B4%A1%E7%8C%AE%E6%8E%92%E8%A1%8C%E6%A6%9C-orange)](https://opensource.alibaba.com/contribution_leaderboard/details?projectValue=lowcode-engine)

View File

@ -14,7 +14,7 @@ An enterprise-class low-code technology stack with scale-out design
[![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url]
[![codecov][codecov-image-url]][codecov-url]
[![codecov][codecov-image-url]][codecov-url] [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/lowcode-workspace/awesome-lowcode-engine)
[![](https://img.shields.io/badge/LowCodeEngine-Check%20Your%20Contribution-orange)](https://opensource.alibaba.com/contribution_leaderboard/details?projectValue=lowcode-engine)

View File

@ -1,6 +1,6 @@
{
"plugins": [
"build-plugin-component",
"@alilc/build-plugin-lce",
[
"build-plugin-fusion",
{

View File

@ -1,7 +1,7 @@
{
"plugins": [
[
"build-plugin-component",
"@alilc/build-plugin-lce",
{
"filename": "editor-preset-vision",
"library": "LowcodeEditor",

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",
@ -12,22 +12,22 @@
"scripts": {
"start": "build-scripts start",
"version:update": "node ./scripts/version.js",
"build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --skip-demo",
"build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build",
"build:umd": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --config build.umd.json",
"test": "build-scripts test --config build.test.json --jest-passWithNoTests"
},
"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

@ -9,11 +9,16 @@ import {
engineConfig,
Setters as InnerSetters,
Hotkey as InnerHotkey,
IEditor,
} from '@alilc/lowcode-editor-core';
import {
IPublicTypeEngineOptions,
IPublicModelDocumentModel,
IPublicTypePluginMeta,
IPublicTypeDisposable,
IPublicApiPlugins,
IPublicApiWorkspace,
IPublicEnumPluginRegisterLevel,
} from '@alilc/lowcode-types';
import {
Designer,
@ -21,6 +26,7 @@ import {
ILowCodePluginContextPrivate,
ILowCodePluginContextApiAssembler,
PluginPreference,
IDesigner,
} from '@alilc/lowcode-designer';
import {
Skeleton as InnerSkeleton,
@ -29,6 +35,7 @@ import {
import {
Workspace as InnerWorkspace,
Workbench as WorkSpaceWorkbench,
IWorkspace,
} from '@alilc/lowcode-workspace';
import {
@ -60,18 +67,30 @@ export * from './modules/skeleton-types';
export * from './modules/designer-types';
export * from './modules/lowcode-types';
async function registryInnerPlugin(designer: Designer, editor: Editor, plugins: Plugins) {
async function registryInnerPlugin(designer: IDesigner, editor: IEditor, plugins: IPublicApiPlugins): Promise<IPublicTypeDisposable> {
// 注册一批内置插件
const componentMetaParserPlugin = componentMetaParser(designer);
const defaultPanelRegistryPlugin = defaultPanelRegistry(editor);
await plugins.register(OutlinePlugin, {}, { autoInit: true });
await plugins.register(componentMetaParser(designer));
await plugins.register(componentMetaParserPlugin);
await plugins.register(setterRegistry, {});
await plugins.register(defaultPanelRegistry(editor));
await plugins.register(defaultPanelRegistryPlugin);
await plugins.register(builtinHotkey);
await plugins.register(registerDefaults, {}, { autoInit: true });
return () => {
plugins.delete(OutlinePlugin.pluginName);
plugins.delete(componentMetaParserPlugin.pluginName);
plugins.delete(setterRegistry.pluginName);
plugins.delete(defaultPanelRegistryPlugin.pluginName);
plugins.delete(defaultPanelRegistryPlugin.pluginName);
plugins.delete(builtinHotkey.pluginName);
plugins.delete(registerDefaults.pluginName);
};
}
const innerWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory);
const workspace = new Workspace(innerWorkspace);
const innerWorkspace: IWorkspace = new InnerWorkspace(registryInnerPlugin, shellModelFactory);
const workspace: IPublicApiWorkspace = new Workspace(innerWorkspace);
const editor = new Editor();
globalContext.register(editor, Editor);
globalContext.register(editor, 'editor');
@ -120,6 +139,7 @@ const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = {
context.plugins = plugins;
context.logger = new Logger({ level: 'warn', bizName: `plugin:${pluginName}` });
context.workspace = workspace;
context.registerLevel = IPublicEnumPluginRegisterLevel.Default;
},
};
@ -139,8 +159,6 @@ export {
logger,
hotkey,
common,
// 兼容原 editor 的事件功能
event as editor,
workspace,
canvas,
};
@ -158,7 +176,7 @@ let engineContainer: HTMLElement;
export const version = VERSION_PLACEHOLDER;
engineConfig.set('ENGINE_VERSION', version);
registryInnerPlugin(designer, editor, plugins);
const pluginPromise = registryInnerPlugin(designer, editor, plugins);
export async function init(
container?: HTMLElement,
@ -183,10 +201,10 @@ export async function init(
}
engineConfig.setEngineOptions(engineOptions as any);
await plugins.init(pluginPreference as any);
const { Workbench } = common.skeletonCabin;
if (options && options.enableWorkspaceMode) {
const disposeFun = await pluginPromise;
disposeFun && disposeFun();
render(
createElement(WorkSpaceWorkbench, {
workspace: innerWorkspace,
@ -196,12 +214,16 @@ export async function init(
}),
engineContainer,
);
innerWorkspace.enableAutoOpenFirstWindow = engineConfig.get('enableAutoOpenFirstWindow', true);
innerWorkspace.setActive(true);
innerWorkspace.initWindow();
innerHotkey.activate(false);
await innerWorkspace.plugins.init(pluginPreference);
return;
}
await plugins.init(pluginPreference as any);
render(
createElement(Workbench, {
skeleton: innerSkeleton,

View File

@ -10,6 +10,7 @@ import {
Selection,
Prop,
SimulatorHost,
SkeletonItem,
} from '@alilc/lowcode-shell';
import { Node as InnerNode } from '@alilc/lowcode-designer';
@ -26,4 +27,5 @@ export default {
Selection,
Prop,
SimulatorHost,
SkeletonItem,
};

View File

@ -11,6 +11,7 @@ import {
designerCabinSymbol,
propSymbol,
simulatorHostSymbol,
skeletonItemSymbol,
} from '@alilc/lowcode-shell';
export default {
@ -26,4 +27,5 @@ export default {
designerCabinSymbol,
propSymbol,
simulatorHostSymbol,
skeletonItemSymbol,
};

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,5 +1,5 @@
{
"plugins": [
"build-plugin-component"
"@alilc/build-plugin-lce"
]
}

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",
@ -10,7 +10,7 @@
"module": "es/index.js",
"stylePath": "style.js",
"scripts": {
"build": "build-scripts build --skip-demo"
"build": "build-scripts build"
},
"keywords": [
"lowcode",
@ -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

@ -62,6 +62,11 @@ export default class DesignerPlugin extends PureComponent<PluginProps, DesignerP
if (!this._mounted) {
return;
}
engineConfig.onGot('locale', (locale) => {
this.setState({
locale,
});
});
const { components, packages, extraEnvironment, utils } = assets;
const state = {
componentMetadatas: components || [],

View File

@ -1,6 +1,6 @@
{
"plugins": [
"build-plugin-component",
"@alilc/build-plugin-lce",
"build-plugin-fusion",
["build-plugin-moment-locales", {
"locales": ["zh-cn"]

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",
@ -9,14 +9,12 @@
"main": "lib/index.js",
"module": "es/index.js",
"scripts": {
"build": "build-scripts build --skip-demo"
"build": "build-scripts build"
},
"dependencies": {
"@alifd/next": "^1.19.16",
"@alilc/lowcode-designer": "1.1.6",
"@alilc/lowcode-editor-core": "1.1.6",
"@alilc/lowcode-types": "1.1.6",
"@alilc/lowcode-utils": "1.1.6",
"@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

@ -16,16 +16,15 @@ import {
IPublicModelDropLocation,
IPublicModelScroller,
IPublicModelScrollTarget,
IPublicModelPluginContext,
IPublicModelLocateEvent,
} from '@alilc/lowcode-types';
import TreeNode from './tree-node';
import { IndentTrack } from '../helper/indent-track';
import DwellTimer from '../helper/dwell-timer';
import { ITreeBoard, TreeMaster } from './tree-master';
import { IOutlinePanelPluginContext, ITreeBoard, TreeMaster } from './tree-master';
export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicTypeScrollable {
private pluginContext: IPublicModelPluginContext;
private pluginContext: IOutlinePanelPluginContext;
private treeMaster?: TreeMaster;
@ -100,8 +99,8 @@ export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicTy
private _shell: HTMLDivElement | null = null;
constructor(at: string | symbol, pluginContext: IPublicModelPluginContext, treeMaster: TreeMaster) {
this.pluginContext = pluginContext;
constructor(at: string | symbol, treeMaster: TreeMaster) {
this.pluginContext = treeMaster.pluginContext;
this.treeMaster = treeMaster;
this.at = at;
let inited = false;
@ -237,7 +236,7 @@ export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicTy
let { node } = treeNode;
if (isDragNodeObject(dragObject)) {
const newNodes = operationalNodes;
let i = newNodes.length;
let i = newNodes?.length;
let p: any = node;
while (i-- > 0) {
if (newNodes[i].contains(p)) {
@ -482,7 +481,7 @@ export class PaneController implements IPublicModelSensor, ITreeBoard, IPublicTy
const isSlotContainer = treeNode.hasSlots();
const isContainer = treeNode.isContainer();
if (container.isSlot && !treeNode.expanded) {
if (container.isSlotNode && !treeNode.expanded) {
// 未展开,直接定位到内部第一个节点
if (isSlotContainer) {
detail.index = null;

View File

@ -1,30 +1,96 @@
import { isLocationChildrenDetail } from '@alilc/lowcode-utils';
import { IPublicModelPluginContext, IPublicTypeActiveTarget, IPublicModelNode } from '@alilc/lowcode-types';
import { IPublicModelPluginContext, IPublicTypeActiveTarget, IPublicModelNode, IPublicTypeDisposable, IPublicEnumPluginRegisterLevel } from '@alilc/lowcode-types';
import TreeNode from './tree-node';
import { Tree } from './tree';
import EventEmitter from 'events';
import { enUS, zhCN } from '../locale';
import { ReactNode } from 'react';
export interface ITreeBoard {
readonly at: string | symbol;
scrollToNode(treeNode: TreeNode, detail?: any): void;
}
enum EVENT_NAMES {
pluginContextChanged = 'pluginContextChanged',
}
export interface IOutlinePanelPluginContext extends IPublicModelPluginContext {
extraTitle?: string;
intlNode(id: string, params?: object): ReactNode;
intl(id: string, params?: object): string;
getLocale(): string;
}
export class TreeMaster {
readonly pluginContext: IPublicModelPluginContext;
pluginContext: IOutlinePanelPluginContext;
private boards = new Set<ITreeBoard>();
private treeMap = new Map<string, Tree>();
constructor(pluginContext: IPublicModelPluginContext) {
this.pluginContext = pluginContext;
private disposeEvents: (IPublicTypeDisposable | undefined)[] = [];
event = new EventEmitter();
constructor(pluginContext: IPublicModelPluginContext, readonly options: {
extraTitle?: string;
}) {
this.setPluginContext(pluginContext);
const { workspace } = this.pluginContext;
this.initEvent();
if (pluginContext.registerLevel === IPublicEnumPluginRegisterLevel.Workspace) {
this.setPluginContext(workspace.window?.currentEditorView);
workspace.onWindowRendererReady(() => {
this.setPluginContext(workspace.window?.currentEditorView);
let dispose: IPublicTypeDisposable | undefined;
const windowViewTypeChangeEvent = () => {
dispose = workspace.window?.onChangeViewType(() => {
this.setPluginContext(workspace.window?.currentEditorView);
});
};
windowViewTypeChangeEvent();
workspace.onChangeActiveWindow(() => {
windowViewTypeChangeEvent();
this.setPluginContext(workspace.window?.currentEditorView);
dispose && dispose();
});
});
}
}
private setPluginContext(pluginContext: IPublicModelPluginContext | undefined | null) {
if (!pluginContext) {
return;
}
const { intl, intlNode, getLocale } = pluginContext.common.utils.createIntl({
'en-US': enUS,
'zh-CN': zhCN,
});
let _pluginContext: IOutlinePanelPluginContext = Object.assign(pluginContext, {
intl,
intlNode,
getLocale,
});
_pluginContext.extraTitle = this.options && this.options['extraTitle'];
this.pluginContext = _pluginContext;
this.disposeEvent();
this.initEvent();
this.emitPluginContextChange();
}
private disposeEvent() {
this.disposeEvents.forEach(d => {
d && d();
});
}
private initEvent() {
let startTime: any;
const { event, project, canvas } = this.pluginContext;
canvas.dragon?.onDragstart(() => {
startTime = Date.now() / 1000;
// needs?
this.toVision();
});
canvas.activeTracker?.onChange((target: IPublicTypeActiveTarget) => {
const setExpandByActiveTracker = (target: IPublicTypeActiveTarget) => {
const { node, detail } = target;
const tree = this.currentTree;
if (!tree/* || node.document !== tree.document */) {
@ -39,29 +105,40 @@ export class TreeMaster {
this.boards.forEach((board) => {
board.scrollToNode(treeNode, detail);
});
});
canvas.dragon?.onDragend(() => {
const endTime: any = Date.now() / 1000;
const nodes = project.currentDocument?.selection?.getNodes();
event.emit('outlinePane.dragend', {
selected: nodes
?.map((n) => {
if (!n) {
return;
}
const npm = n?.componentMeta?.npm;
return (
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') || n?.componentMeta?.componentName
);
})
.join('&'),
time: (endTime - startTime).toFixed(2),
});
});
project.onRemoveDocument((data: {id: string}) => {
const { id } = data;
this.treeMap.delete(id);
});
};
this.disposeEvents = [
canvas.dragon?.onDragstart(() => {
startTime = Date.now() / 1000;
// needs?
this.toVision();
}),
canvas.activeTracker?.onChange(setExpandByActiveTracker),
canvas.dragon?.onDragend(() => {
const endTime: any = Date.now() / 1000;
const nodes = project.currentDocument?.selection?.getNodes();
event.emit('outlinePane.dragend', {
selected: nodes
?.map((n) => {
if (!n) {
return;
}
const npm = n?.componentMeta?.npm;
return (
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') || n?.componentMeta?.componentName
);
})
.join('&'),
time: (endTime - startTime).toFixed(2),
});
}),
project.onRemoveDocument((data: {id: string}) => {
const { id } = data;
this.treeMap.delete(id);
}),
];
if (canvas.activeTracker?.target) {
setExpandByActiveTracker(canvas.activeTracker?.target);
}
}
private toVision() {
@ -86,6 +163,14 @@ export class TreeMaster {
// todo others purge
}
onPluginContextChange(fn: () => void) {
this.event.on(EVENT_NAMES.pluginContextChanged, fn);
}
emitPluginContextChange() {
this.event.emit(EVENT_NAMES.pluginContextChanged);
}
get currentTree(): Tree | null {
const doc = this.pluginContext.project.getCurrentDocument();
if (doc) {
@ -93,7 +178,7 @@ export class TreeMaster {
if (this.treeMap.has(id)) {
return this.treeMap.get(id)!;
}
const tree = new Tree(this.pluginContext);
const tree = new Tree(this);
this.treeMap.set(id, tree);
return tree;
}

View File

@ -2,12 +2,12 @@ import {
IPublicTypeTitleContent,
IPublicTypeLocationChildrenDetail,
IPublicModelNode,
IPublicModelPluginContext,
IPublicTypeDisposable,
} from '@alilc/lowcode-types';
import { isI18nData, isLocationChildrenDetail } from '@alilc/lowcode-utils';
import EventEmitter from 'events';
import { Tree } from './tree';
import { IOutlinePanelPluginContext } from './tree-master';
/**
*
@ -35,10 +35,12 @@ enum EVENT_NAMES {
titleLabelChanged = 'titleLabelChanged',
expandableChanged = 'expandableChanged',
conditionChanged = 'conditionChanged',
}
export default class TreeNode {
readonly pluginContext: IPublicModelPluginContext;
readonly pluginContext: IOutlinePanelPluginContext;
event = new EventEmitter();
private _node: IPublicModelNode;
@ -152,6 +154,10 @@ export default class TreeNode {
return this.node.slots.map((node) => this.tree.getTreeNode(node));
}
get condition(): boolean {
return this.node.hasCondition() && !this.node.conditionGroup;
}
get children(): TreeNode[] | null {
return this.node.children?.map((node) => this.tree.getTreeNode(node)) || null;
}
@ -160,9 +166,9 @@ export default class TreeNode {
return this._node;
}
constructor(tree: Tree, node: IPublicModelNode, pluginContext: IPublicModelPluginContext) {
constructor(tree: Tree, node: IPublicModelNode) {
this.tree = tree;
this.pluginContext = pluginContext;
this.pluginContext = tree.pluginContext;
this._node = node;
}
@ -203,6 +209,15 @@ export default class TreeNode {
this.event.off(EVENT_NAMES.titleLabelChanged, fn);
};
}
onConditionChanged(fn: (treeNode: TreeNode) => void): IPublicTypeDisposable {
this.event.on(EVENT_NAMES.conditionChanged, fn);
return () => {
this.event.off(EVENT_NAMES.conditionChanged, fn);
};
}
onExpandableChanged(fn: (expandable: boolean) => void): IPublicTypeDisposable {
this.event.on(EVENT_NAMES.expandableChanged, fn);
return () => {
@ -221,11 +236,17 @@ export default class TreeNode {
this.event.emit(EVENT_NAMES.titleLabelChanged, this.title);
}
notifyConditionChanged(): void {
this.event.emit(EVENT_NAMES.conditionChanged, this.condition);
}
setHidden(flag: boolean) {
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

@ -1,12 +1,13 @@
import TreeNode from './tree-node';
import { IPublicModelNode, IPublicModelPluginContext, IPublicTypePropChangeOptions } from '@alilc/lowcode-types';
import { IPublicModelNode, IPublicTypePropChangeOptions } from '@alilc/lowcode-types';
import { IOutlinePanelPluginContext, TreeMaster } from './tree-master';
export class Tree {
private treeNodesMap = new Map<string, TreeNode>();
readonly id: string | undefined;
readonly pluginContext: IPublicModelPluginContext;
readonly pluginContext: IOutlinePanelPluginContext;
get root(): TreeNode | null {
if (this.pluginContext.project.currentDocument?.focusNode) {
@ -15,8 +16,11 @@ export class Tree {
return null;
}
constructor(pluginContext: IPublicModelPluginContext) {
this.pluginContext = pluginContext;
readonly treeMaster: TreeMaster;
constructor(treeMaster: TreeMaster) {
this.treeMaster = treeMaster;
this.pluginContext = treeMaster.pluginContext;
const doc = this.pluginContext.project.currentDocument;
this.id = doc?.id;
@ -31,8 +35,16 @@ export class Tree {
if (key === '___title___') {
const treeNode = this.getTreeNodeById(node.id);
treeNode?.notifyTitleLabelChanged();
} else if (key === '___condition___') {
const treeNode = this.getTreeNodeById(node.id);
treeNode?.notifyConditionChanged();
}
});
doc?.onChangeNodeVisible((node: IPublicModelNode, visible: boolean) => {
const treeNode = this.getTreeNodeById(node.id);
treeNode?.setHidden(!visible);
});
}
setNodeSelected(nodeId: string): void {
@ -51,7 +63,7 @@ export class Tree {
return tnode;
}
const treeNode = new TreeNode(this, node, this.pluginContext);
const treeNode = new TreeNode(this, node);
this.treeNodesMap.set(node.id, treeNode);
return treeNode;
}

View File

@ -1,21 +1,43 @@
import { Pane } from './views/pane';
import { IconOutline } from './icons/outline';
import { IPublicModelPluginContext, IPublicModelDocumentModel } from '@alilc/lowcode-types';
import { enUS, zhCN } from './locale';
import { MasterPaneName, BackupPaneName } from './helper/consts';
import { TreeMaster } from './controllers/tree-master';
import { PaneController } from './controllers/pane-controller';
import { useState, useEffect } from 'react';
export function OutlinePaneContext(props: {
treeMaster?: TreeMaster;
pluginContext: IPublicModelPluginContext;
options: any;
paneName: string;
hideFilter?: boolean;
}) {
const treeMaster = props.treeMaster || new TreeMaster(props.pluginContext, props.options);
const [masterPaneController, setMasterPaneController] = useState(new PaneController(props.paneName || MasterPaneName, treeMaster));
useEffect(() => {
return treeMaster.onPluginContextChange(() => {
setMasterPaneController(new PaneController(props.paneName || MasterPaneName, treeMaster));
});
}, []);
return (
<Pane
treeMaster={treeMaster}
controller={masterPaneController}
key={masterPaneController.id}
hideFilter={props.hideFilter}
{...props}
/>
);
}
export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => {
const { skeleton, config, common, event, canvas, project } = ctx;
const { intl, intlNode, getLocale } = common.utils.createIntl({
'en-US': enUS,
'zh-CN': zhCN,
});
ctx.intl = intl;
ctx.intlNode = intlNode;
ctx.getLocale = getLocale;
ctx.extraTitle = options && options['extraTitle'];
const { skeleton, config, canvas, project } = ctx;
let isInFloatArea = true;
const hasPreferenceForOutline = config.getPreference().contains('outline-pane-pinned-status-isFloat', 'skeleton');
@ -26,9 +48,7 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => {
masterPane: false,
backupPane: false,
};
const treeMaster = new TreeMaster(ctx);
let masterPaneController: PaneController | null = null;
let backupPaneController: PaneController | null = null;
const treeMaster = new TreeMaster(ctx, options);
return {
async init() {
skeleton.add({
@ -40,20 +60,9 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => {
name: MasterPaneName,
props: {
icon: IconOutline,
description: intlNode('Outline Tree'),
},
content: (props: any) => {
masterPaneController = new PaneController(MasterPaneName, ctx, treeMaster);
return (
<Pane
config={config}
pluginContext={ctx}
treeMaster={treeMaster}
controller={masterPaneController}
{...props}
/>
);
description: treeMaster.pluginContext.intlNode('Outline Tree'),
},
content: OutlinePaneContext,
},
panelProps: {
area: isInFloatArea ? 'leftFloatArea' : 'leftFixedArea',
@ -62,6 +71,8 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => {
},
contentProps: {
treeTitleExtra: config.get('treeTitleExtra'),
treeMaster,
paneName: MasterPaneName,
},
});
@ -72,16 +83,10 @@ export const OutlinePlugin = (ctx: IPublicModelPluginContext, options: any) => {
props: {
hiddenWhenInit: true,
},
content: (props: any) => {
backupPaneController = new PaneController(BackupPaneName, ctx, treeMaster);
return (
<Pane
pluginContext={ctx}
treeMaster={treeMaster}
controller={backupPaneController}
{...props}
/>
);
content: OutlinePaneContext,
contentProps: {
paneName: BackupPaneName,
treeMaster,
},
});

View File

@ -5,11 +5,9 @@ import { Search, Checkbox, Balloon, Divider } from '@alifd/next';
import TreeNode from '../controllers/tree-node';
import { Tree } from '../controllers/tree';
import { matchTreeNode, FILTER_OPTIONS } from './filter-tree';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
export default class Filter extends PureComponent<{
tree: Tree;
pluginContext: IPublicModelPluginContext;
}, {
keywords: string;
filterOps: string[];
@ -56,7 +54,7 @@ export default class Filter extends PureComponent<{
<Search
hasClear
shape="simple"
placeholder={this.props.pluginContext.intl('Filter Node')}
placeholder={this.props.tree.pluginContext.intl('Filter Node')}
className="lc-outline-filter-search-input"
value={keywords}
onChange={this.handleSearchChange}
@ -77,7 +75,7 @@ export default class Filter extends PureComponent<{
indeterminate={indeterminate}
onChange={this.handleCheckAll}
>
{this.props.pluginContext.intlNode('Check All')}
{this.props.tree.pluginContext.intlNode('Check All')}
</Checkbox>
<Divider />
<Checkbox.Group
@ -91,7 +89,7 @@ export default class Filter extends PureComponent<{
value={op.value}
key={op.value}
>
{this.props.pluginContext.intlNode(op.label)}
{this.props.tree.pluginContext.intlNode(op.label)}
</Checkbox>
))}
</Checkbox.Group>

View File

@ -1,47 +1,75 @@
import React, { PureComponent } from 'react';
import { Loading } from '@alifd/next';
import { PaneController } from '../controllers/pane-controller';
import TreeView from './tree';
import './style.less';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
import Filter from './filter';
import { TreeMaster } from '../controllers/tree-master';
import { Tree } from '../controllers/tree';
import { IPublicTypeDisposable } from '@alilc/lowcode-types';
export class Pane extends PureComponent<{
config: any;
pluginContext: IPublicModelPluginContext;
treeMaster: TreeMaster;
controller: PaneController;
hideFilter?: boolean;
}, {
tree: Tree | null;
}> {
private controller;
private treeMaster: TreeMaster;
private simulatorRendererReadyDispose: IPublicTypeDisposable;
private changeDocumentDispose: IPublicTypeDisposable;
private removeDocumentDispose: IPublicTypeDisposable;
constructor(props: any) {
super(props);
const { controller, treeMaster } = props;
this.treeMaster = treeMaster;
this.controller = controller;
this.state = {
tree: treeMaster.currentTree,
};
this.simulatorRendererReadyDispose = this.props.treeMaster.pluginContext?.project?.onSimulatorRendererReady(this.changeTree);
this.changeDocumentDispose = this.props.treeMaster.pluginContext?.project?.onChangeDocument(this.changeTree);
this.removeDocumentDispose = this.props.treeMaster.pluginContext?.project?.onRemoveDocument(this.changeTree);
}
changeTree = () => {
this.setState({
tree: this.props.treeMaster.currentTree,
});
};
componentWillUnmount() {
this.controller.purge();
this.simulatorRendererReadyDispose?.();
this.changeDocumentDispose?.();
this.removeDocumentDispose?.();
}
render() {
const tree = this.treeMaster.currentTree;
const tree = this.state.tree;
if (!tree) {
return (
<div className="lc-outline-pane">
<p className="lc-outline-notice">{this.props.pluginContext.intl('Initializing')}</p>
<p className="lc-outline-notice">
<Loading
style={{
display: 'block',
marginTop: '40px',
}}
tip={this.props.treeMaster.pluginContext.intl('Initializing')}
/>
</p>
</div>
);
}
return (
<div className="lc-outline-pane">
<Filter tree={tree} pluginContext={this.props.pluginContext} />
<div ref={(shell) => this.controller.mount(shell)} className="lc-outline-tree-container">
<TreeView key={tree.id} tree={tree} pluginContext={this.props.pluginContext} />
{ !this.props.hideFilter && <Filter tree={tree} /> }
<div ref={(shell) => this.controller.mount(shell)} className={`lc-outline-tree-container ${ this.props.hideFilter ? 'lc-hidden-outline-filter' : '' }`}>
<TreeView key={tree.id} tree={tree} />
</div>
</div>
);

View File

@ -14,10 +14,14 @@
overflow: auto;
}
> .lc-outline-tree-container.lc-hidden-outline-filter {
top: 0;
}
> .lc-outline-filter {
padding: 12px 16px;
display: flex;
align-items: center;
align-items: stretch;
justify-content: right;
.lc-outline-filter-search-input {
@ -27,7 +31,6 @@
.lc-outline-filter-icon {
background: #ebecf0;
border: 1px solid #c4c6cf;
height: 28px;
display: flex;
align-items: center;
border-radius: 0 2px 2px 0;

View File

@ -2,12 +2,11 @@ import { PureComponent } from 'react';
import classNames from 'classnames';
import TreeNode from '../controllers/tree-node';
import TreeNodeView from './tree-node';
import { IPublicModelPluginContext, IPublicModelExclusiveGroup, IPublicTypeDisposable, IPublicTypeLocationChildrenDetail } from '@alilc/lowcode-types';
import { IPublicModelExclusiveGroup, IPublicTypeDisposable, IPublicTypeLocationChildrenDetail } from '@alilc/lowcode-types';
export default class TreeBranches extends PureComponent<{
treeNode: TreeNode;
isModal?: boolean;
pluginContext: IPublicModelPluginContext;
expanded: boolean;
treeChildren: TreeNode[] | null;
}> {
@ -51,12 +50,11 @@ export default class TreeBranches extends PureComponent<{
return (
<div className="tree-node-branches">
{
!isModal && <TreeNodeSlots treeNode={treeNode} pluginContext={this.props.pluginContext} />
!isModal && <TreeNodeSlots treeNode={treeNode} />
}
<TreeNodeChildren
treeNode={treeNode}
isModal={isModal || false}
pluginContext={this.props.pluginContext}
treeChildren={this.props.treeChildren}
/>
</div>
@ -73,7 +71,6 @@ interface ITreeNodeChildrenState {
class TreeNodeChildren extends PureComponent<{
treeNode: TreeNode;
isModal?: boolean;
pluginContext: IPublicModelPluginContext;
treeChildren: TreeNode[] | null;
}, ITreeNodeChildrenState> {
state: ITreeNodeChildrenState = {
@ -84,8 +81,8 @@ class TreeNodeChildren extends PureComponent<{
};
offLocationChanged: IPublicTypeDisposable | undefined;
componentDidMount() {
const { treeNode, pluginContext } = this.props;
const { project } = pluginContext;
const { treeNode } = this.props;
const { project } = treeNode.pluginContext;
const { filterWorking, matchSelf, keywords } = treeNode.filterReult;
const { dropDetail } = treeNode;
this.setState({
@ -122,13 +119,14 @@ class TreeNodeChildren extends PureComponent<{
let groupContents: any[] = [];
let currentGrp: IPublicModelExclusiveGroup;
const { filterWorking, matchSelf, keywords } = this.state;
const Title = this.props.pluginContext.common.editorCabin.Title;
const Title = this.props.treeNode.pluginContext.common.editorCabin.Title;
const endGroup = () => {
if (groupContents.length > 0) {
children.push(
<div key={currentGrp.id} className="condition-group-container" data-id={currentGrp.firstNode?.id}>
<div className="condition-group-title">
{/* @ts-ignore */}
<Title
title={currentGrp.title}
match={filterWorking && matchSelf}
@ -171,12 +169,12 @@ class TreeNodeChildren extends PureComponent<{
children.push(insertion);
}
}
groupContents.push(<TreeNodeView key={child.id} treeNode={child} isModal={isModal} pluginContext={this.props.pluginContext} />);
groupContents.push(<TreeNodeView key={child.id} treeNode={child} isModal={isModal} />);
} else {
if (index === dropIndex) {
children.push(insertion);
}
children.push(<TreeNodeView key={child.id} treeNode={child} isModal={isModal} pluginContext={this.props.pluginContext} />);
children.push(<TreeNodeView key={child.id} treeNode={child} isModal={isModal} />);
}
});
endGroup();
@ -191,14 +189,13 @@ class TreeNodeChildren extends PureComponent<{
class TreeNodeSlots extends PureComponent<{
treeNode: TreeNode;
pluginContext: IPublicModelPluginContext;
}> {
render() {
const { treeNode } = this.props;
if (!treeNode.hasSlots()) {
return null;
}
const Title = this.props.pluginContext.common.editorCabin.Title;
const Title = this.props.treeNode.pluginContext.common.editorCabin.Title;
return (
<div
className={classNames('tree-node-slots', {
@ -207,10 +204,11 @@ class TreeNodeSlots extends PureComponent<{
data-id={treeNode.id}
>
<div className="tree-node-slots-title">
<Title title={{ type: 'i18n', intl: this.props.pluginContext.intlNode('Slots') }} />
{/* @ts-ignore */}
<Title title={{ type: 'i18n', intl: this.props.treeNode.pluginContext.intlNode('Slots') }} />
</div>
{treeNode.slots.map(tnode => (
<TreeNodeView key={tnode.id} treeNode={tnode} pluginContext={this.props.pluginContext} />
<TreeNodeView key={tnode.id} treeNode={tnode} />
))}
</div>
);

View File

@ -4,34 +4,56 @@ import TreeNode from '../controllers/tree-node';
import TreeTitle from './tree-title';
import TreeBranches from './tree-branches';
import { IconEyeClose } from '../icons/eye-close';
import { IPublicModelPluginContext, IPublicModelModalNodesManager, IPublicTypeDisposable } from '@alilc/lowcode-types';
import { IPublicModelModalNodesManager, IPublicTypeDisposable } from '@alilc/lowcode-types';
import { IOutlinePanelPluginContext } from '../controllers/tree-master';
class ModalTreeNodeView extends PureComponent<{
treeNode: TreeNode;
pluginContext: IPublicModelPluginContext;
}, {
treeChildren: TreeNode[] | null;
}> {
private modalNodesManager: IPublicModelModalNodesManager | undefined | null;
readonly pluginContext: IPublicModelPluginContext;
readonly pluginContext: IOutlinePanelPluginContext;
constructor(props: any) {
constructor(props: {
treeNode: TreeNode;
}) {
super(props);
// 模态管理对象
this.pluginContext = props.pluginContext;
this.pluginContext = props.treeNode.pluginContext;
const { project } = this.pluginContext;
this.modalNodesManager = project.currentDocument?.modalNodesManager;
this.state = {
treeChildren: this.rootTreeNode.children,
};
}
hideAllNodes() {
this.modalNodesManager?.hideModalNodes();
}
render() {
componentDidMount(): void {
const rootTreeNode = this.rootTreeNode;
rootTreeNode.onExpandableChanged(() => {
this.setState({
treeChildren: rootTreeNode.children,
});
});
}
get rootTreeNode() {
const { treeNode } = this.props;
// 当指定了新的根节点时,要从原始的根节点去获取模态节点
const { project } = this.pluginContext;
const rootNode = project.currentDocument?.root;
const rootTreeNode = treeNode.tree.getTreeNode(rootNode!);
return rootTreeNode;
}
render() {
const rootTreeNode = this.rootTreeNode;
const { expanded } = rootTreeNode;
const hasVisibleModalNode = !!this.modalNodesManager?.getVisibleModalNode();
@ -49,10 +71,9 @@ class ModalTreeNodeView extends PureComponent<{
<div className="tree-pane-modal-content">
<TreeBranches
treeNode={rootTreeNode}
treeChildren={rootTreeNode.children}
treeChildren={this.state.treeChildren}
expanded={expanded}
isModal
pluginContext={this.pluginContext}
/>
</div>
</div>
@ -63,7 +84,6 @@ class ModalTreeNodeView extends PureComponent<{
export default class TreeNodeView extends PureComponent<{
treeNode: TreeNode;
isModal?: boolean;
pluginContext: IPublicModelPluginContext;
isRootNode?: boolean;
}> {
state: {
@ -114,8 +134,8 @@ export default class TreeNodeView extends PureComponent<{
}
componentDidMount() {
const { treeNode, pluginContext } = this.props;
const { project } = pluginContext;
const { treeNode } = this.props;
const { project } = treeNode.pluginContext;
const doc = project.currentDocument;
@ -158,14 +178,14 @@ export default class TreeNodeView extends PureComponent<{
}
shouldShowModalTreeNode(): boolean {
const { treeNode, isRootNode, pluginContext } = this.props;
const { treeNode, isRootNode } = this.props;
if (!isRootNode) {
// 只在 当前树 的根节点展示模态节点
return false;
}
// 当指定了新的根节点时,要从原始的根节点去获取模态节点
const { project } = pluginContext;
const { project } = treeNode.pluginContext;
const rootNode = project.currentDocument?.root;
const rootTreeNode = treeNode.tree.getTreeNode(rootNode!);
const modalNodes = rootTreeNode.children?.filter((item) => {
@ -214,19 +234,16 @@ export default class TreeNodeView extends PureComponent<{
hidden={this.state.hidden}
locked={this.state.locked}
expandable={this.state.expandable}
pluginContext={this.props.pluginContext}
/>
{shouldShowModalTreeNode &&
<ModalTreeNodeView
treeNode={treeNode}
pluginContext={this.props.pluginContext}
/>
}
<TreeBranches
treeNode={treeNode}
isModal={false}
expanded={this.state.expanded}
pluginContext={this.props.pluginContext}
treeChildren={this.state.treeChildren}
/>
</div>

View File

@ -1,7 +1,7 @@
import { KeyboardEvent, FocusEvent, Fragment, PureComponent } from 'react';
import classNames from 'classnames';
import { createIcon } from '@alilc/lowcode-utils';
import { IPublicModelPluginContext, IPublicApiEvent } from '@alilc/lowcode-types';
import { IPublicApiEvent } from '@alilc/lowcode-types';
import TreeNode from '../controllers/tree-node';
import { IconLock, IconUnlock, IconArrowRight, IconEyeClose, IconEye, IconCond, IconLoop, IconRadioActive, IconRadio, IconSetting } from '../icons';
@ -23,11 +23,12 @@ export default class TreeTitle extends PureComponent<{
hidden: boolean;
locked: boolean;
expandable: boolean;
pluginContext: IPublicModelPluginContext;
}> {
state: {
editing: boolean;
title: string;
condition?: boolean;
visible?: boolean;
} = {
editing: false,
title: '',
@ -53,7 +54,7 @@ export default class TreeTitle extends PureComponent<{
const { treeNode } = this.props;
const value = (e.target as HTMLInputElement).value || '';
treeNode.setTitleLabel(value);
emitOutlineEvent(this.props.pluginContext.event, 'rename', treeNode, { value });
emitOutlineEvent(this.props.treeNode.pluginContext.event, 'rename', treeNode, { value });
this.cancelEdit();
};
@ -81,16 +82,28 @@ export default class TreeTitle extends PureComponent<{
this.setState({
editing: false,
title: treeNode.titleLabel,
condition: treeNode.condition,
});
treeNode.onTitleLabelChanged(() => {
this.setState({
title: treeNode.titleLabel,
});
});
treeNode.onConditionChanged(() => {
this.setState({
condition: treeNode.condition,
});
});
treeNode.onHiddenChanged((hidden: boolean) => {
this.setState({
visible: !hidden,
});
});
}
render() {
const { treeNode, isModal, pluginContext } = this.props;
const { treeNode, isModal } = this.props;
const { pluginContext } = treeNode;
const { editing } = this.state;
const isCNode = !treeNode.isRoot();
const { node } = treeNode;
@ -125,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);
@ -137,7 +150,7 @@ export default class TreeTitle extends PureComponent<{
}
}}
>
{isModal && node.visible && (
{isModal && this.state.visible && (
<div onClick={() => {
node.document?.modalNodesManager?.setInvisible(node);
}}
@ -145,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);
}}
@ -153,7 +166,7 @@ export default class TreeTitle extends PureComponent<{
<IconRadio className="tree-node-modal-radio" />
</div>
)}
{isCNode && <ExpandBtn expandable={this.props.expandable} expanded={this.props.expanded} treeNode={treeNode} pluginContext={this.props.pluginContext} />}
{isCNode && <ExpandBtn expandable={this.props.expandable} expanded={this.props.expanded} treeNode={treeNode} />}
<div className="tree-node-icon">{createIcon(treeNode.icon)}</div>
<div className="tree-node-title-label">
{editing ? (
@ -166,6 +179,7 @@ export default class TreeTitle extends PureComponent<{
/>
) : (
<Fragment>
{/* @ts-ignore */}
<Title
title={this.state.title}
match={filterWorking && matchSelf}
@ -175,6 +189,7 @@ export default class TreeTitle extends PureComponent<{
{node.slotFor && (
<a className="tree-node-tag slot">
{/* todo: click redirect to prop */}
{/* @ts-ignore */}
<Tip>{intlNode('Slot for {prop}', { prop: node.slotFor.key })}</Tip>
</a>
)}
@ -182,22 +197,24 @@ export default class TreeTitle extends PureComponent<{
<a className="tree-node-tag loop">
{/* todo: click todo something */}
<IconLoop />
{/* @ts-ignore */}
<Tip>{intlNode('Loop')}</Tip>
</a>
)}
{node.hasCondition() && !node.conditionGroup && (
{this.state.condition && (
<a className="tree-node-tag cond">
{/* todo: click todo something */}
<IconCond />
{/* @ts-ignore */}
<Tip>{intlNode('Conditional')}</Tip>
</a>
)}
</Fragment>
)}
</div>
{shouldShowHideBtn && <HideBtn hidden={this.props.hidden} treeNode={treeNode} pluginContext={this.props.pluginContext} />}
{shouldShowLockBtn && <LockBtn locked={this.props.locked} treeNode={treeNode} pluginContext={this.props.pluginContext} />}
{shouldEditBtn && <RenameBtn treeNode={treeNode} pluginContext={this.props.pluginContext} onClick={this.enableEdit} /> }
{shouldShowHideBtn && <HideBtn hidden={this.props.hidden} treeNode={treeNode} />}
{shouldShowLockBtn && <LockBtn locked={this.props.locked} treeNode={treeNode} />}
{shouldEditBtn && <RenameBtn treeNode={treeNode} onClick={this.enableEdit} /> }
</div>
);
@ -206,11 +223,10 @@ export default class TreeTitle extends PureComponent<{
class RenameBtn extends PureComponent<{
treeNode: TreeNode;
pluginContext: IPublicModelPluginContext;
onClick: (e: any) => void;
}> {
render() {
const { intl, common } = this.props.pluginContext;
const { intl, common } = this.props.treeNode.pluginContext;
const Tip = common.editorCabin.Tip;
return (
<div
@ -218,6 +234,7 @@ class RenameBtn extends PureComponent<{
onClick={this.props.onClick}
>
<IconSetting />
{/* @ts-ignore */}
<Tip>{intl('Rename')}</Tip>
</div>
);
@ -226,12 +243,11 @@ class RenameBtn extends PureComponent<{
class LockBtn extends PureComponent<{
treeNode: TreeNode;
pluginContext: IPublicModelPluginContext;
locked: boolean;
}> {
render() {
const { treeNode, locked } = this.props;
const { intl, common } = this.props.pluginContext;
const { intl, common } = this.props.treeNode.pluginContext;
const Tip = common.editorCabin.Tip;
return (
<div
@ -242,6 +258,7 @@ class LockBtn extends PureComponent<{
}}
>
{locked ? <IconUnlock /> : <IconLock /> }
{/* @ts-ignore */}
<Tip>{locked ? intl('Unlock') : intl('Lock')}</Tip>
</div>
);
@ -251,24 +268,24 @@ class LockBtn extends PureComponent<{
class HideBtn extends PureComponent<{
treeNode: TreeNode;
hidden: boolean;
pluginContext: IPublicModelPluginContext;
}, {
hidden: boolean;
}> {
render() {
const { treeNode, hidden } = this.props;
const { intl, common } = this.props.pluginContext;
const { intl, common } = treeNode.pluginContext;
const Tip = common.editorCabin.Tip;
return (
<div
className="tree-node-hide-btn"
onClick={(e) => {
e.stopPropagation();
emitOutlineEvent(this.props.pluginContext.event, hidden ? 'show' : 'hide', treeNode);
emitOutlineEvent(treeNode.pluginContext.event, hidden ? 'show' : 'hide', treeNode);
treeNode.setHidden(!hidden);
}}
>
{hidden ? <IconEye /> : <IconEyeClose />}
{/* @ts-ignore */}
<Tip>{hidden ? intl('Show') : intl('Hide')}</Tip>
</div>
);
@ -277,7 +294,6 @@ class HideBtn extends PureComponent<{
class ExpandBtn extends PureComponent<{
treeNode: TreeNode;
pluginContext: IPublicModelPluginContext;
expanded: boolean;
expandable: boolean;
}> {
@ -294,7 +310,7 @@ class ExpandBtn extends PureComponent<{
if (expanded) {
e.stopPropagation();
}
emitOutlineEvent(this.props.pluginContext.event, expanded ? 'collapse' : 'expand', treeNode);
emitOutlineEvent(treeNode.pluginContext.event, expanded ? 'collapse' : 'expand', treeNode);
treeNode.setExpanded(!expanded);
}}
>

View File

@ -2,7 +2,7 @@ import { MouseEvent as ReactMouseEvent, PureComponent } from 'react';
import { isFormEvent, canClickNode, isShaken } from '@alilc/lowcode-utils';
import { Tree } from '../controllers/tree';
import TreeNodeView from './tree-node';
import { IPublicEnumDragObjectType, IPublicModelPluginContext, IPublicModelNode } from '@alilc/lowcode-types';
import { IPublicEnumDragObjectType, IPublicModelNode } from '@alilc/lowcode-types';
import TreeNode from '../controllers/tree-node';
function getTreeNodeIdByEvent(e: ReactMouseEvent, stop: Element): null | string {
@ -20,12 +20,21 @@ function getTreeNodeIdByEvent(e: ReactMouseEvent, stop: Element): null | string
export default class TreeView extends PureComponent<{
tree: Tree;
pluginContext: IPublicModelPluginContext;
}> {
private shell: HTMLDivElement | null = null;
private ignoreUpSelected = false;
private boostEvent?: MouseEvent;
state: {
root: TreeNode | null;
} = {
root: null,
};
private hover(e: ReactMouseEvent) {
const { project } = this.props.pluginContext;
const { project } = this.props.tree.pluginContext;
const detecting = project.currentDocument?.detecting;
if (detecting?.enable) {
return;
@ -54,7 +63,7 @@ export default class TreeView extends PureComponent<{
return;
}
const { project, event, canvas } = this.props.pluginContext;
const { project, event, canvas } = this.props.tree.pluginContext;
const doc = project.currentDocument;
const selection = doc?.selection;
const focusNode = doc?.focusNode;
@ -109,10 +118,6 @@ export default class TreeView extends PureComponent<{
return tree.getTreeNodeById(id);
}
private ignoreUpSelected = false;
private boostEvent?: MouseEvent;
private onMouseDown = (e: ReactMouseEvent) => {
if (isFormEvent(e.nativeEvent)) {
return;
@ -127,7 +132,7 @@ export default class TreeView extends PureComponent<{
if (!canClickNode(node, e)) {
return;
}
const { project, canvas } = this.props.pluginContext;
const { project, canvas } = this.props.tree.pluginContext;
const selection = project.currentDocument?.selection;
const focusNode = project.currentDocument?.focusNode;
@ -166,22 +171,16 @@ export default class TreeView extends PureComponent<{
};
private onMouseLeave = () => {
const { pluginContext } = this.props;
const { pluginContext } = this.props.tree;
const { project } = pluginContext;
const doc = project.currentDocument;
doc?.detecting.leave();
};
state: {
root: TreeNode | null
} = {
root: null,
};
componentDidMount() {
const { tree, pluginContext } = this.props;
const { tree } = this.props;
const { root } = tree;
const { project } = pluginContext;
const { project } = tree.pluginContext;
this.setState({ root });
const doc = project.currentDocument;
doc?.onFocusNodeChanged(() => {
@ -208,7 +207,6 @@ export default class TreeView extends PureComponent<{
<TreeNodeView
key={this.state.root?.id}
treeNode={this.state.root}
pluginContext={this.props.pluginContext}
isRootNode
/>
</div>

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,3 +1,3 @@
{
"plugins": ["build-plugin-component", "./build.plugin.js"]
"plugins": ["@alilc/build-plugin-lce", "./build.plugin.js"]
}

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",
@ -9,14 +9,14 @@
"dist"
],
"scripts": {
"build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --skip-demo",
"build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build",
"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 @@
{
"plugins": [
"build-plugin-component",
"@alilc/build-plugin-lce",
"build-plugin-fusion",
["build-plugin-moment-locales", {
"locales": ["zh-cn"]

View File

@ -1,6 +1,6 @@
{
"plugins": [
"build-plugin-component",
"@alilc/build-plugin-lce",
"@alilc/lowcode-test-mate/plugin/index.ts"
]
}

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",
@ -12,7 +12,7 @@
"scripts": {
"test": "build-scripts test --config build.test.json",
"start": "build-scripts start",
"build": "build-scripts build --skip-demo",
"build": "build-scripts build",
"build:umd": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --config build.umd.json"
},
"keywords": [
@ -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,3 +1,3 @@
{
"plugins": ["build-plugin-component", "./build.plugin.js"]
"plugins": ["@alilc/build-plugin-lce", "./build.plugin.js"]
}

View File

@ -1,6 +1,6 @@
{
"plugins": [
"build-plugin-component",
"@alilc/build-plugin-lce",
"@alilc/lowcode-test-mate/plugin/index.ts"
]
}

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",
@ -12,15 +12,15 @@
],
"scripts": {
"test": "build-scripts test --config build.test.json",
"build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --skip-demo",
"build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build",
"build:umd": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --config build.umd.json",
"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

@ -1,7 +1,7 @@
{
"plugins": [
[
"build-plugin-component",
"@alilc/build-plugin-lce",
{
"babelPlugins": ["@babel/plugin-transform-typescript"]
}

View File

@ -1,6 +1,6 @@
{
"plugins": [
"build-plugin-component",
"@alilc/build-plugin-lce",
"@alilc/lowcode-test-mate/plugin/index.ts"
]
}

Some files were not shown because too many files have changed in this diff Show More