mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-12 11:20:11 +00:00
Compare commits
24 Commits
main
...
v1.2.4-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b2e3a7c24 | ||
|
|
cdf2a3bb66 | ||
|
|
4406dc4103 | ||
|
|
11e2a1f1d6 | ||
|
|
19bd17f317 | ||
|
|
d9c517ffcb | ||
|
|
c716164536 | ||
|
|
7fbb5db1b8 | ||
|
|
4827286802 | ||
|
|
30fbbac91c | ||
|
|
3160b3c2a3 | ||
|
|
64aa80ad0f | ||
|
|
303f280be0 | ||
|
|
3eb44a1fde | ||
|
|
570c2dedfe | ||
|
|
dc13e5e3a5 | ||
|
|
7772547c33 | ||
|
|
956e6fef5b | ||
|
|
3eb3b9b97b | ||
|
|
ee6c5af6ca | ||
|
|
6d8e308d15 | ||
|
|
df286acdfb | ||
|
|
b0f768e1e6 | ||
|
|
5034994a24 |
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@ -2,7 +2,7 @@
|
||||
|
||||
# These owners will be the default owners for everything in
|
||||
# the repo. Unless a later match takes precedence
|
||||
* @liujuping @1ncounter
|
||||
* @liujuping @JackLian
|
||||
|
||||
/modules/material-parser @akirakai
|
||||
/modules/code-generator @qingniaotonghua
|
||||
/modules/code-generator @leoyuan
|
||||
|
||||
34
.github/workflows/pre build.yml
vendored
34
.github/workflows/pre build.yml
vendored
@ -1,34 +0,0 @@
|
||||
name: Pre Build
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'packages/**'
|
||||
- '!packages/**.md'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'packages/**'
|
||||
- '!packages/**.md'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install dependencies and setup
|
||||
run: npm install && npm run setup
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Check build status
|
||||
run: |
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Build succeeded!"
|
||||
else
|
||||
echo "Build failed!"
|
||||
exit 1
|
||||
fi
|
||||
2
.github/workflows/publish docs.yml
vendored
2
.github/workflows/publish docs.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: echo "version=$(node -p "require('./docs/package.json').version")" >> $GITHUB_OUTPUT
|
||||
run: echo "::set-output name=version::$(node -p "require('./docs/package.json').version")"
|
||||
|
||||
comment-pr:
|
||||
needs: publish-docs
|
||||
|
||||
2
.github/workflows/publish engine beta.yml
vendored
2
.github/workflows/publish engine beta.yml
vendored
@ -27,4 +27,4 @@ jobs:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
||||
run: echo "::set-output name=version::$(node -p "require('./package.json').version")"
|
||||
|
||||
10
.github/workflows/publish engine.yml
vendored
10
.github/workflows/publish engine.yml
vendored
@ -2,17 +2,13 @@ name: Publish Engine
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
publishCommand:
|
||||
description: 'publish command'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
publish-engine:
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
contains(github.ref, 'refs/heads/release/') &&
|
||||
(github.actor == '1ncounter' || github.actor == 'liujuping')
|
||||
(github.actor == 'JackLian' || github.actor == 'liujuping')
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Node.js
|
||||
@ -25,9 +21,9 @@ jobs:
|
||||
npm run build
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
- run: npm run ${{ github.event.inputs.publishCommand }}
|
||||
- run: npm run pub
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
||||
run: echo "::set-output name=version::$(node -p "require('./package.json').version")"
|
||||
|
||||
34
.github/workflows/test packages.yml
vendored
34
.github/workflows/test packages.yml
vendored
@ -105,36 +105,4 @@ jobs:
|
||||
run: npm i && npm run setup:skip-build
|
||||
|
||||
- name: test
|
||||
run: cd packages/utils && npm test
|
||||
|
||||
test-editor-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/editor-core && npm test
|
||||
|
||||
test-plugin-command:
|
||||
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/plugin-command && npm test
|
||||
run: cd packages/utils && npm test
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -108,5 +108,3 @@ typings/
|
||||
# codealike
|
||||
codealike.json
|
||||
.node
|
||||
|
||||
.must.config.js
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: canvas - 画布 API
|
||||
sidebar_position: 10
|
||||
sidebar_position: 12
|
||||
---
|
||||
|
||||
> **@types** [IPublicApiCanvas](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/canvas.ts)<br/>
|
||||
|
||||
@ -1,101 +0,0 @@
|
||||
---
|
||||
title: command - 指令 API
|
||||
sidebar_position: 10
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 模块概览
|
||||
|
||||
该模块使得与命令系统的交互成为可能,提供了一种全面的方式来处理、执行和管理应用程序中的命令。
|
||||
|
||||
|
||||
|
||||
## 接口
|
||||
|
||||
### IPublicApiCommand
|
||||
|
||||
与命令交互的接口。它提供了注册、注销、执行和管理命令的方法。
|
||||
|
||||
|
||||
|
||||
## 方法
|
||||
|
||||
### registerCommand
|
||||
|
||||
注册一个新命令及其处理函数。
|
||||
|
||||
```
|
||||
typescriptCopy code
|
||||
/**
|
||||
* 注册一个新的命令及其处理程序。
|
||||
* @param command {IPublicTypeCommand} - 要注册的命令。
|
||||
*/
|
||||
registerCommand(command: IPublicTypeCommand): void;
|
||||
```
|
||||
|
||||
### unregisterCommand
|
||||
|
||||
注销一个已存在的命令。
|
||||
|
||||
```
|
||||
typescriptCopy code
|
||||
/**
|
||||
* 注销一个已存在的命令。
|
||||
* @param name {string} - 要注销的命令的名称。
|
||||
*/
|
||||
unregisterCommand(name: string): void;
|
||||
```
|
||||
|
||||
### executeCommand
|
||||
|
||||
根据名称和提供的参数执行命令,确保参数符合命令的定义。
|
||||
|
||||
```
|
||||
typescriptCopy code
|
||||
/**
|
||||
* 根据名称和提供的参数执行命令。
|
||||
* @param name {string} - 要执行的命令的名称。
|
||||
* @param args {IPublicTypeCommandHandlerArgs} - 命令的参数。
|
||||
*/
|
||||
executeCommand(name: string, args?: IPublicTypeCommandHandlerArgs): void;
|
||||
```
|
||||
|
||||
### batchExecuteCommand
|
||||
|
||||
批量执行命令,在所有命令执行后进行重绘,历史记录中只记录一次。
|
||||
|
||||
```
|
||||
typescriptCopy code
|
||||
/**
|
||||
* 批量执行命令,随后进行重绘,历史记录中只记录一次。
|
||||
* @param commands {Array} - 命令对象的数组,包含名称和可选参数。
|
||||
*/
|
||||
batchExecuteCommand(commands: { name: string; args?: IPublicTypeCommandHandlerArgs }[]): void;
|
||||
```
|
||||
|
||||
### listCommands
|
||||
|
||||
列出所有已注册的命令。
|
||||
|
||||
```
|
||||
typescriptCopy code
|
||||
/**
|
||||
* 列出所有已注册的命令。
|
||||
* @returns {IPublicTypeListCommand[]} - 已注册命令的数组。
|
||||
*/
|
||||
listCommands(): IPublicTypeListCommand[];
|
||||
```
|
||||
|
||||
### onCommandError
|
||||
|
||||
为命令执行过程中的错误注册错误处理回调函数。
|
||||
|
||||
```
|
||||
typescriptCopy code
|
||||
/**
|
||||
* 为命令执行过程中的错误注册一个回调函数。
|
||||
* @param callback {(name: string, error: Error) => void} - 错误处理的回调函数。
|
||||
*/
|
||||
onCommandError(callback: (name: string, error: Error) => void): void;
|
||||
```
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: common - 通用 API
|
||||
sidebar_position: 10
|
||||
sidebar_position: 11
|
||||
---
|
||||
|
||||
> **@types** [IPublicApiCommon](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/common.ts)<br/>
|
||||
@ -82,7 +82,7 @@ executeTransaction(fn: () => void, type: IPublicEnumTransitionType): void;
|
||||
```
|
||||
**@since v1.0.16**
|
||||
|
||||
**示例**
|
||||
##### 示例
|
||||
```typescript
|
||||
import { common } from '@alilc/lowcode-engine';
|
||||
import { IPublicEnumTransitionType } from '@alilc/lowcode-types';
|
||||
@ -132,8 +132,7 @@ createIntl(instance: string | object): {
|
||||
|
||||
**@since v1.0.17**
|
||||
|
||||
**示例**
|
||||
|
||||
##### 示例
|
||||
```typescript
|
||||
import { common } from '@alilc/lowcode-engine';
|
||||
import enUS from './en-US.json';
|
||||
@ -146,22 +145,6 @@ const { intl, getLocale, setLocale } = common.utils.createIntl({
|
||||
|
||||
```
|
||||
|
||||
#### intl
|
||||
|
||||
i18n 转换方法
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* i18n 转换方法
|
||||
*/
|
||||
intl(data: IPublicTypeI18nData | string, params?: object): string;
|
||||
```
|
||||
|
||||
**示例**
|
||||
```
|
||||
const title = common.utils.intl(node.title)
|
||||
```
|
||||
|
||||
### skeletonCabin
|
||||
#### Workbench
|
||||
编辑器框架 View
|
||||
|
||||
@ -1,210 +0,0 @@
|
||||
---
|
||||
title: commonUI - UI 组件库
|
||||
sidebar_position: 10
|
||||
---
|
||||
|
||||
## 简介
|
||||
CommonUI API 是一个专为低代码引擎设计的组件 UI 库,使用它开发的插件,可以保证在不同项目和主题切换中能够保持一致性和兼容性。
|
||||
|
||||
## 组件列表
|
||||
|
||||
### Tip
|
||||
|
||||
提示组件
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|-----------|--------------|---------------------------------------|--------|
|
||||
| className | className | string (optional) | |
|
||||
| children | tip 的内容 | IPublicTypeI18nData \| ReactNode | |
|
||||
| direction | tip 的方向 | 'top' \| 'bottom' \| 'left' \| 'right' | |
|
||||
|
||||
|
||||
### HelpTip
|
||||
|
||||
带 help icon 的提示组件
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|-----------|--------|-----------------------------------|--------|
|
||||
| help | 描述 | IPublicTypeHelpTipConfig | |
|
||||
| direction | 方向 | IPublicTypeTipConfig['direction'] | 'top' |
|
||||
| size | 方向 | IconProps['size'] | 'small'|
|
||||
|
||||
### Title
|
||||
|
||||
标题组件
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|-----------|------------|-----------------------------|--------|
|
||||
| title | 标题内容 | IPublicTypeTitleContent | |
|
||||
| className | className | string (optional) | |
|
||||
| onClick | 点击事件 | () => void (optional) | |
|
||||
|
||||
### ContextMenu
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|--------|----------------------------------------------------|------------------------------------|--------|
|
||||
| menus | 定义上下文菜单的动作数组 | IPublicTypeContextMenuAction[] | |
|
||||
| children | 组件的子元素 | React.ReactElement[] | |
|
||||
|
||||
**IPublicTypeContextMenuAction Interface**
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|------------|--------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|----------------------------------------|
|
||||
| name | 动作的唯一标识符<br/>Unique identifier for the action | string | |
|
||||
| title | 显示的标题,可以是字符串或国际化数据<br/>Display title, can be a string or internationalized data | string \| IPublicTypeI18nData (optional) | |
|
||||
| type | 菜单项类型<br/>Menu item type | IPublicEnumContextMenuType (optional) | IPublicEnumContextMenuType.MENU_ITEM |
|
||||
| action | 点击时执行的动作,可选<br/>Action to execute on click, optional | (nodes: IPublicModelNode[]) => void (optional) | |
|
||||
| items | 子菜单项或生成子节点的函数,可选,仅支持两级<br/>Sub-menu items or function to generate child node, optional | Omit<IPublicTypeContextMenuAction, 'items'>[] \| ((nodes: IPublicModelNode[]) => Omit<IPublicTypeContextMenuAction, 'items'>[]) (optional) | |
|
||||
| condition | 显示条件函数<br/>Function to determine display condition | (nodes: IPublicModelNode[]) => boolean (optional) | |
|
||||
| disabled | 禁用条件函数,可选<br/>Function to determine disabled condition, optional | (nodes: IPublicModelNode[]) => boolean (optional) | |
|
||||
|
||||
**ContextMenu 示例**
|
||||
|
||||
```typescript
|
||||
const App = () => {
|
||||
const menuItems: IPublicTypeContextMenuAction[] = [
|
||||
{
|
||||
name: 'a',
|
||||
title: '选项 1',
|
||||
action: () => console.log('选项 1 被点击'),
|
||||
},
|
||||
{
|
||||
name: 'b',
|
||||
title: '选项 2',
|
||||
action: () => console.log('选项 2 被点击'),
|
||||
},
|
||||
];
|
||||
|
||||
const ContextMenu = ctx.commonUI.ContextMenu;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ContextMenu menus={menuItems}>
|
||||
<div>右键点击这里</div>
|
||||
</ContextMenu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
**ContextMenu.create 示例**
|
||||
|
||||
```typescript
|
||||
const App = () => {
|
||||
const menuItems: IPublicTypeContextMenuAction[] = [
|
||||
{
|
||||
name: 'a',
|
||||
title: '选项 1',
|
||||
action: () => console.log('选项 1 被点击'),
|
||||
},
|
||||
{
|
||||
name: 'b',
|
||||
title: '选项 2',
|
||||
action: () => console.log('选项 2 被点击'),
|
||||
},
|
||||
];
|
||||
|
||||
const ContextMenu = ctx.commonUI.ContextMenu;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div onClick={(e) => {
|
||||
ContextMenu.create(menuItems, e);
|
||||
}}>点击这里</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
### Balloon
|
||||
|
||||
详细文档: [Balloon Documentation](https://fusion.design/pc/component/balloon)
|
||||
|
||||
### Breadcrumb
|
||||
详细文档: [Breadcrumb Documentation](https://fusion.design/pc/component/breadcrumb)
|
||||
|
||||
### Button
|
||||
详细文档: [Button Documentation](https://fusion.design/pc/component/button)
|
||||
|
||||
### Card
|
||||
详细文档:[Card Documentation](https://fusion.design/pc/component/card)
|
||||
|
||||
### Checkbox
|
||||
详细文档:[Checkbox Documentation](https://fusion.design/pc/component/checkbox)
|
||||
|
||||
### DatePicker
|
||||
详细文档:[DatePicker Documentation](https://fusion.design/pc/component/datepicker)
|
||||
|
||||
### Dialog
|
||||
详细文档:[Dialog Documentation](https://fusion.design/pc/component/dialog)
|
||||
|
||||
### Dropdown
|
||||
详细文档:[Dropdown Documentation](https://fusion.design/pc/component/dropdown)
|
||||
|
||||
### Form
|
||||
详细文档:[Form Documentation](https://fusion.design/pc/component/form)
|
||||
|
||||
### Icon
|
||||
详细文档:[Icon Documentation](https://fusion.design/pc/component/icon)
|
||||
|
||||
引擎默认主题支持的 icon 列表:https://fusion.design/64063/component/icon?themeid=20133
|
||||
|
||||
|
||||
### Input
|
||||
详细文档:[Input Documentation](https://fusion.design/pc/component/input)
|
||||
|
||||
### Loading
|
||||
详细文档:[Loading Documentation](https://fusion.design/pc/component/loading)
|
||||
|
||||
### Message
|
||||
详细文档:[Message Documentation](https://fusion.design/pc/component/message)
|
||||
|
||||
### Overlay
|
||||
详细文档:[Overlay Documentation](https://fusion.design/pc/component/overlay)
|
||||
|
||||
### Pagination
|
||||
详细文档:[Pagination Documentation](https://fusion.design/pc/component/pagination)
|
||||
|
||||
### Radio
|
||||
详细文档:[Radio Documentation](https://fusion.design/pc/component/radio)
|
||||
|
||||
### Search
|
||||
详细文档:[Search Documentation](https://fusion.design/pc/component/search)
|
||||
|
||||
### Select
|
||||
详细文档:[Select Documentation](https://fusion.design/pc/component/select)
|
||||
|
||||
### SplitButton
|
||||
详细文档:[SplitButton Documentation](https://fusion.design/pc/component/splitbutton)
|
||||
|
||||
### Step
|
||||
详细文档:[Step Documentation](https://fusion.design/pc/component/step)
|
||||
|
||||
### Switch
|
||||
详细文档:[Switch Documentation](https://fusion.design/pc/component/switch)
|
||||
|
||||
### Tab
|
||||
详细文档:[Tab Documentation](https://fusion.design/pc/component/tab)
|
||||
|
||||
### Table
|
||||
详细文档:[Table Documentation](https://fusion.design/pc/component/table)
|
||||
|
||||
### Tree
|
||||
详细文档:[Tree Documentation](https://fusion.design/pc/component/tree)
|
||||
|
||||
### TreeSelect
|
||||
详细文档:[TreeSelect Documentation](https://fusion.design/pc/component/treeselect)
|
||||
|
||||
### Upload
|
||||
详细文档:[Upload Documentation](https://fusion.design/pc/component/upload)
|
||||
|
||||
### Divider
|
||||
详细文档:[Divider Documentation](https://fusion.design/pc/component/divider)
|
||||
|
||||
## 说明
|
||||
|
||||
如果需要其他组件,可以提 issue 给我们。
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: config - 配置 API
|
||||
sidebar_position: 5
|
||||
sidebar_position: 8
|
||||
---
|
||||
|
||||
> **@types** [IPublicModelEngineConfig](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/engine-config.ts)<br/>
|
||||
@ -24,7 +24,7 @@ sidebar_position: 5
|
||||
*/
|
||||
get(key: string, defaultValue?: any): any;
|
||||
```
|
||||
**示例**
|
||||
#### 示例
|
||||
```typescript
|
||||
import { config } from '@alilc/lowcode-engine';
|
||||
|
||||
@ -43,7 +43,7 @@ config.get('keyB', { a: 1 });
|
||||
*/
|
||||
set(key: string, value: any): void;
|
||||
```
|
||||
**示例**
|
||||
#### 示例
|
||||
```typescript
|
||||
import { config } from '@alilc/lowcode-engine';
|
||||
|
||||
@ -63,7 +63,7 @@ config.set('keyC', 1);
|
||||
has(key: string): boolean;
|
||||
```
|
||||
|
||||
**示例**
|
||||
#### 示例
|
||||
```typescript
|
||||
import { config } from '@alilc/lowcode-engine';
|
||||
|
||||
@ -81,7 +81,7 @@ config.has('keyD');
|
||||
*/
|
||||
setConfig(config: { [key: string]: any }): void;
|
||||
```
|
||||
**示例**
|
||||
#### 示例
|
||||
```typescript
|
||||
import { config } from '@alilc/lowcode-engine';
|
||||
|
||||
@ -134,7 +134,7 @@ config.getPreference().set(`${panelName}-pinned-status-isFloat`, false, 'skeleto
|
||||
*/
|
||||
onceGot(key: string): Promise<any>;
|
||||
```
|
||||
**示例**
|
||||
#### 示例
|
||||
```typescript
|
||||
import { config } from '@alilc/lowcode-engine';
|
||||
|
||||
@ -160,7 +160,7 @@ const value = await config.onceGot('keyA');
|
||||
*/
|
||||
onGot(key: string, fn: (data: any) => void): () => void;
|
||||
```
|
||||
**示例**
|
||||
#### 示例
|
||||
```typescript
|
||||
import { config } from '@alilc/lowcode-engine';
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: config options - 配置列表
|
||||
sidebar_position: 5
|
||||
sidebar_position: 13
|
||||
---
|
||||
|
||||
> **@types** [IPublicTypeEngineOptions](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/engine-options.ts)<br/>
|
||||
@ -185,12 +185,6 @@ config.set('enableCondition', false)
|
||||
|
||||
`@type {boolean}` `@default {false}`
|
||||
|
||||
#### enableContextMenu - 开启右键菜单
|
||||
|
||||
`@type {boolean}` `@default {false}`
|
||||
|
||||
是否开启右键菜单
|
||||
|
||||
#### disableDetecting
|
||||
|
||||
`@type {boolean}` `@default {false}`
|
||||
@ -222,12 +216,6 @@ config.set('enableCondition', false)
|
||||
|
||||
是否在只有一个 item 的时候隐藏设置 tabs
|
||||
|
||||
#### hideComponentAction
|
||||
|
||||
`@type {boolean}` `@default {false}`
|
||||
|
||||
隐藏设计器辅助层
|
||||
|
||||
#### thisRequiredInJSE
|
||||
|
||||
`@type {boolean}` `@default {true}`
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: event - 事件 API
|
||||
sidebar_position: 10
|
||||
sidebar_position: 7
|
||||
---
|
||||
|
||||
> **@types** [IPublicApiEvent](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/event.ts)<br/>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: hotkey - 快捷键 API
|
||||
sidebar_position: 10
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
> **@types** [IPublicApiHotkey](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/hotkey.ts)<br/>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: init - 初始化 API
|
||||
sidebar_position: 0
|
||||
sidebar_position: 10
|
||||
---
|
||||
|
||||
> **@since** v1.0.0
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: logger - 日志 API
|
||||
sidebar_position: 10
|
||||
sidebar_position: 9
|
||||
---
|
||||
|
||||
> **@types** [IPublicApiLogger](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/logger.ts)<br/>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: material - 物料 API
|
||||
sidebar_position: 10
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
> **@types** [IPublicApiMaterial](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/material.ts)<br/>
|
||||
@ -39,7 +39,7 @@ setAssets(assets: IPublicTypeAssetsJson): void;
|
||||
相关类型:[IPublicTypeAssetsJson](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/assets-json.ts)
|
||||
|
||||
|
||||
**示例**
|
||||
##### 示例
|
||||
直接在项目中引用 npm 包
|
||||
```javascript
|
||||
import { material } from '@alilc/lowcode-engine';
|
||||
@ -85,7 +85,7 @@ getAssets(): IPublicTypeAssetsJson;
|
||||
相关类型:[IPublicTypeAssetsJson](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/assets-json.ts)
|
||||
|
||||
|
||||
**示例**
|
||||
##### 示例
|
||||
```typescript
|
||||
import { material } from '@alilc/lowcode-engine';
|
||||
|
||||
@ -106,7 +106,7 @@ loadIncrementalAssets(incrementalAssets: IPublicTypeAssetsJson): Promise<void>;
|
||||
```
|
||||
相关类型:[IPublicTypeAssetsJson](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/assets-json.ts)
|
||||
|
||||
**示例**
|
||||
##### 示例
|
||||
```typescript
|
||||
import { material } from '@alilc/lowcode-engine';
|
||||
import assets1 from '@alilc/mc-assets-<siteId>/assets.json';
|
||||
@ -146,7 +146,7 @@ addBuiltinComponentAction(action: IPublicTypeComponentAction): void;
|
||||
相关类型:[IPublicTypeComponentAction](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/component-action.ts)
|
||||
|
||||
|
||||
**示例**
|
||||
##### 示例
|
||||
新增设计扩展位,并绑定事件
|
||||
```typescript
|
||||
import { material } from '@alilc/lowcode-engine';
|
||||
@ -186,7 +186,7 @@ removeBuiltinComponentAction(name: string): void;
|
||||
- lock:锁定,不可编辑
|
||||
- unlock:解锁,可编辑
|
||||
|
||||
**示例**
|
||||
##### 示例
|
||||
```typescript
|
||||
import { material } from '@alilc/lowcode-engine';
|
||||
|
||||
@ -222,7 +222,7 @@ modifyBuiltinComponentAction(
|
||||
|
||||
|
||||
|
||||
**示例**
|
||||
##### 示例
|
||||
给原始的 remove 扩展时间添加执行前后的日志
|
||||
```typescript
|
||||
import { material } from '@alilc/lowcode-engine';
|
||||
@ -237,90 +237,7 @@ material.modifyBuiltinComponentAction('remove', (action) => {
|
||||
});
|
||||
```
|
||||
|
||||
### 右键菜单项
|
||||
#### addContextMenuOption
|
||||
|
||||
添加右键菜单项
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 添加右键菜单项
|
||||
* @param action
|
||||
*/
|
||||
addContextMenuOption(action: IPublicTypeContextMenuAction): void;
|
||||
```
|
||||
|
||||
示例
|
||||
|
||||
```typescript
|
||||
import { IPublicEnumContextMenuType } from '@alilc/lowcode-types';
|
||||
|
||||
material.addContextMenuOption({
|
||||
name: 'parentItem',
|
||||
title: 'Parent Item',
|
||||
condition: (nodes) => true,
|
||||
items: [
|
||||
{
|
||||
name: 'childItem1',
|
||||
title: 'Child Item 1',
|
||||
action: (nodes) => console.log('Child Item 1 clicked', nodes),
|
||||
condition: (nodes) => true
|
||||
},
|
||||
// 分割线
|
||||
{
|
||||
type: IPublicEnumContextMenuType.SEPARATOR
|
||||
name: 'separator.1'
|
||||
}
|
||||
// 更多子菜单项...
|
||||
]
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
#### removeContextMenuOption
|
||||
|
||||
删除特定右键菜单项
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 删除特定右键菜单项
|
||||
* @param name
|
||||
*/
|
||||
removeContextMenuOption(name: string): void;
|
||||
```
|
||||
|
||||
#### adjustContextMenuLayout
|
||||
|
||||
调整右键菜单项布局,每次调用都会覆盖之前注册的调整函数,只有最后注册的函数会被应用。
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 调整右键菜单项布局
|
||||
* @param actions
|
||||
*/
|
||||
adjustContextMenuLayout(fn: (actions: IPublicTypeContextMenuItem[]) => IPublicTypeContextMenuItem[]): void;
|
||||
```
|
||||
|
||||
**示例**
|
||||
|
||||
通过 adjustContextMenuLayout 补充分割线
|
||||
|
||||
```typescript
|
||||
material.adjustContextMenuLayout((actions: IPublicTypeContextMenuAction) => {
|
||||
const names = ['a', 'b'];
|
||||
const newActions = [];
|
||||
actions.forEach(d => {
|
||||
newActions.push(d);
|
||||
if (names.include(d.name)) {
|
||||
newActions.push({ type: 'separator' })
|
||||
}
|
||||
});
|
||||
return newActions
|
||||
})
|
||||
```
|
||||
|
||||
### 物料元数据
|
||||
|
||||
#### getComponentMeta
|
||||
获取指定名称的物料元数据
|
||||
|
||||
@ -335,7 +252,7 @@ getComponentMeta(componentName: string): IPublicModelComponentMeta | null;
|
||||
```
|
||||
相关类型:[IPublicModelComponentMeta](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/component-meta.ts)
|
||||
|
||||
**示例**
|
||||
##### 示例
|
||||
```typescript
|
||||
import { material } from '@alilc/lowcode-engine';
|
||||
|
||||
@ -356,7 +273,7 @@ material.getComponentMeta('Input');
|
||||
```
|
||||
相关类型:[IPublicModelComponentMeta](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/component-meta.ts)
|
||||
|
||||
**示例**
|
||||
##### 示例
|
||||
```typescript
|
||||
import { material } from '@alilc/lowcode-engine';
|
||||
|
||||
@ -393,7 +310,7 @@ registerMetadataTransducer(
|
||||
): void;
|
||||
```
|
||||
|
||||
**示例**
|
||||
##### 示例
|
||||
给每一个组件的配置添加高级配置面板,其中有一个是否渲染配置项
|
||||
```typescript
|
||||
import { material } from '@alilc/lowcode-engine'
|
||||
@ -475,7 +392,7 @@ material.registerMetadataTransducer((transducer) => {
|
||||
getRegisteredMetadataTransducers(): IPublicTypeMetadataTransducer[];
|
||||
```
|
||||
|
||||
**示例**
|
||||
##### 示例
|
||||
```typescript
|
||||
import { material } from '@alilc/lowcode-engine'
|
||||
|
||||
@ -496,7 +413,7 @@ onChangeAssets(fn: () => void): IPublicTypeDisposable;
|
||||
|
||||
相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts)
|
||||
|
||||
**示例**
|
||||
##### 示例
|
||||
```typescript
|
||||
import { material } from '@alilc/lowcode-engine';
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"label": "模型定义 Models",
|
||||
"position": 100,
|
||||
"position": 14,
|
||||
"collapsed": false,
|
||||
"collapsible": true
|
||||
}
|
||||
|
||||
@ -15,12 +15,6 @@ sidebar_position: 13
|
||||
|
||||
`@type {string}`
|
||||
|
||||
### id
|
||||
|
||||
资源 id
|
||||
|
||||
`@type {string}`
|
||||
|
||||
### name
|
||||
|
||||
资源名字
|
||||
@ -50,9 +44,3 @@ sidebar_position: 13
|
||||
资源配置信息
|
||||
|
||||
`@type {Object}`
|
||||
|
||||
### config
|
||||
|
||||
资源配置信息
|
||||
|
||||
`@type {Object}`
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: plugins - 插件 API
|
||||
sidebar_position: 2
|
||||
sidebar_position: 4
|
||||
---
|
||||
> **@types** [IPublicApiPlugins](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/plugins.ts)<br/>
|
||||
> **@since** v1.0.0
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: project - 模型 API
|
||||
sidebar_position: 10
|
||||
sidebar_position: 3
|
||||
---
|
||||
## 模块简介
|
||||
|
||||
@ -201,7 +201,7 @@ addPropsTransducer(
|
||||
- [IPublicTypePropsTransducer](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/props-transducer.ts)
|
||||
- [IPublicEnumTransformStage](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/enum/transform-stage.ts)
|
||||
|
||||
**示例**
|
||||
#### 示例
|
||||
在保存的时候删除每一个组件的 props.hidden
|
||||
```typescript
|
||||
import { project } from '@alilc/lowcode-engine';
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: setters - 设置器 API
|
||||
sidebar_position: 10
|
||||
sidebar_position: 6
|
||||
---
|
||||
> **@types** [IPublicApiSetters](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/setters.ts)<br/>
|
||||
> **@since** v1.0.0
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: simulatorHost - 模拟器 API
|
||||
sidebar_position: 10
|
||||
sidebar_position: 3
|
||||
---
|
||||
> **@types** [IPublicApiSimulatorHost](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/simulator-host.ts)<br/>
|
||||
> **@since** v1.0.0
|
||||
@ -20,7 +20,7 @@ sidebar_position: 10
|
||||
*/
|
||||
set(key: string, value: any): void;
|
||||
```
|
||||
**示例**
|
||||
#### 示例
|
||||
设置若干用于画布渲染的变量,比如画布大小、locale 等。
|
||||
|
||||
以设置画布大小为例:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: skeleton - 面板 API
|
||||
sidebar_position: 10
|
||||
sidebar_position: 1
|
||||
---
|
||||
> **@types** [IPublicApiSkeleton](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/api/skeleton.ts)<br/>
|
||||
> **@since** v1.0.0
|
||||
@ -297,24 +297,7 @@ showArea(areaName: string): void;
|
||||
hideArea(areaName: string): void;
|
||||
```
|
||||
|
||||
### getAreaItems
|
||||
|
||||
获取某个区域下的所有面板实例
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 获取某个区域下的所有面板实例
|
||||
* @param areaName IPublicTypeWidgetConfigArea
|
||||
*/
|
||||
getAreaItems(areaName: IPublicTypeWidgetConfigArea): IPublicModelSkeletonItem[] | undefined;
|
||||
```
|
||||
|
||||
相关类型:[IPublicModelSkeletonItem](https://github.com/alibaba/lowcode-engine/blob/main/packages/shell/src/model/skeleton-item.ts)
|
||||
|
||||
|
||||
|
||||
### registerConfigTransducer
|
||||
|
||||
注册一个面板的配置转换器(transducer)。
|
||||
|
||||
```typescript
|
||||
@ -387,7 +370,7 @@ export default controlPanelWidthPlugin;
|
||||
* @param listener
|
||||
* @returns
|
||||
*/
|
||||
onShowPanel(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => void): IPublicTypeDisposable;
|
||||
onShowPanel(listener: (...args: any[]) => void): IPublicTypeDisposable;
|
||||
```
|
||||
|
||||
相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts)
|
||||
@ -403,38 +386,11 @@ onShowPanel(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => v
|
||||
* @param listener
|
||||
* @returns
|
||||
*/
|
||||
onHidePanel(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => void): IPublicTypeDisposable;
|
||||
onHidePanel(listener: (...args: any[]) => void): IPublicTypeDisposable;
|
||||
```
|
||||
|
||||
相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts)
|
||||
|
||||
### onDisableWidget
|
||||
|
||||
监听 Widget 实例 Disable 事件
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 监听 Widget 实例 Disable 事件
|
||||
* @param listener
|
||||
*/
|
||||
onDisableWidget(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => void): IPublicTypeDisposable;
|
||||
```
|
||||
|
||||
相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts)
|
||||
|
||||
### onEnableWidget
|
||||
|
||||
监听 Widget 实例 Enable 事件
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 监听 Widget 实例 Enable 事件
|
||||
* @param listener
|
||||
*/
|
||||
onEnableWidget(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => void): IPublicTypeDisposable;
|
||||
```
|
||||
|
||||
相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts)
|
||||
|
||||
### onShowWidget
|
||||
|
||||
@ -447,7 +403,7 @@ onEnableWidget(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) =
|
||||
* @param listener
|
||||
* @returns
|
||||
*/
|
||||
onShowWidget(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => void): IPublicTypeDisposable;
|
||||
onShowWidget(listener: (...args: any[]) => void): IPublicTypeDisposable;
|
||||
```
|
||||
|
||||
相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts)
|
||||
@ -463,7 +419,7 @@ onShowWidget(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) =>
|
||||
* @param listener
|
||||
* @returns
|
||||
*/
|
||||
onHideWidget(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => void): IPublicTypeDisposable;
|
||||
onHideWidget(listener: (...args: any[]) => void): IPublicTypeDisposable;
|
||||
```
|
||||
|
||||
相关类型:[IPublicTypeDisposable](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/type/disposable.ts)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: workspace - 应用级 API
|
||||
sidebar_position: 10
|
||||
sidebar_position: 12
|
||||
---
|
||||
|
||||
> **[@experimental](./#experimental)**<br/>
|
||||
|
||||
@ -2,8 +2,6 @@
|
||||
title: 3. 如何通过按钮展示/隐藏弹窗
|
||||
sidebar_position: 1
|
||||
---
|
||||
> 说明:这个方式依赖低代码弹窗组件是否对外保留了相关的 API,不同的物料支持的方式不一样,这里只针对综合场景的弹窗物料。
|
||||
|
||||
## 1.拖拽一个按钮
|
||||
|
||||

|
||||
|
||||
@ -12,6 +12,8 @@ sidebar_position: 3
|
||||
| @alilc/lowcode-engine | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/engine |
|
||||
| @alilc/lowcode-plugin-designer | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/plugin-designer |
|
||||
| @alilc/lowcode-plugin-outline-pane | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/plugin-outline-pane |
|
||||
| @alilc/lowcode-rax-renderer | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/rax-renderer |
|
||||
| @alilc/lowcode-rax-simulator-renderer | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/rax-simulator-renderer |
|
||||
| @alilc/lowcode-react-renderer | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/react-renderer |
|
||||
| @alilc/lowcode-react-simulator-renderer | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/react-simulator-renderer |
|
||||
| @alilc/lowcode-renderer-core | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/renderer-core |
|
||||
|
||||
@ -15,13 +15,15 @@ sidebar_position: 2
|
||||
5. ignitor
|
||||
6. plugin-designer
|
||||
7. plugin-outline-pane
|
||||
8. react-renderer
|
||||
9. react-simulator-renderer
|
||||
10. renderer-core
|
||||
11. types
|
||||
12. utils
|
||||
13. material-parser
|
||||
14. code-generator
|
||||
8. rax-renderer
|
||||
9. rax-simulator-renderer
|
||||
10. react-renderer
|
||||
11. react-simulator-renderer
|
||||
12. renderer-core
|
||||
13. types
|
||||
14. utils
|
||||
15. material-parser
|
||||
16. code-generator
|
||||
|
||||
## 2. 引擎官方扩展包
|
||||
包含了常用的设置器(setter)、跟 setter 绑定的插件等
|
||||
|
||||
@ -11,6 +11,7 @@ sidebar_position: 4
|
||||
## npm 包与仓库信息
|
||||
|
||||
- React 框架渲染 npm 包:@alilc/lowcode-react-renderer
|
||||
- Rax 框架渲染 npm 包:@alilc/lowcode-rax-renderer
|
||||
- 仓库:[https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) 下的
|
||||
- packages/renderer-core
|
||||
- packages/react-renderer
|
||||
|
||||
@ -53,11 +53,6 @@ sidebar_position: 9
|
||||
- `--color-text-reverse`: 反色情况下,文字颜色
|
||||
- `--color-text-disabled`: 禁用态文字颜色
|
||||
|
||||
#### 菜单颜色
|
||||
- `--color-context-menu-text`: 菜单项颜色
|
||||
- `--color-context-menu-text-hover`: 菜单项 hover 颜色
|
||||
- `--color-context-menu-text-disabled`: 菜单项 disabled 颜色
|
||||
|
||||
#### 字段和边框颜色
|
||||
|
||||
- `--color-field-label`: field 标签颜色
|
||||
@ -133,7 +128,6 @@ sidebar_position: 9
|
||||
- `--pane-title-height`: 面板标题高度
|
||||
- `--pane-title-font-size`: 面板标题字体大小
|
||||
- `--pane-title-padding`: 面板标题边距
|
||||
- `--context-menu-item-height`: 右键菜单项高度
|
||||
|
||||
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@ ReactDOM.render((
|
||||
), document.getElementById('root'));
|
||||
```
|
||||
|
||||
- rax-renderer:npm 包替换为 @alilc/lowcode-rax-renderer
|
||||
####
|
||||
### 项目使用示例
|
||||
> [设计器 demo](https://lowcode-engine.cn/demo/demo-general/index.html)
|
||||
|
||||
@ -47,7 +47,15 @@ npm install && npm run setup
|
||||
[
|
||||
"https?://uipaas-assets.com/prod/npm/@alilc/lowcode-engine/(.*)/dist/css/react-simulator-renderer.css",
|
||||
"http://localhost:5555/css/ReactSimulatorRenderer.css"
|
||||
]
|
||||
],
|
||||
[
|
||||
"https?://uipaas-assets.com/prod/npm/@alilc/lowcode-engine/(.*)/dist/js/rax-simulator-renderer.js",
|
||||
"http://localhost:5555/js/RaxSimulatorRenderer.js"
|
||||
],
|
||||
[
|
||||
"https?://uipaas-assets.com/prod/npm/@alilc/lowcode-engine/(.*)/dist/css/rax-simulator-renderer.css",
|
||||
"http://localhost:5555/css/RaxSimulatorRenderer.css"
|
||||
],
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@ -499,6 +499,7 @@ try {
|
||||
- 说明:组件即将从 DOM 中移除
|
||||
- componentDidCatch(error, info)
|
||||
- 说明:组件捕获到异常
|
||||
- Rax:目前没有使用生命周期,使用 hooks 替代生命周期;
|
||||
|
||||
该对象由一系列 key-value 组成,key 为生命周期方法名,value 为 JSFunction 的描述,详见下方示例:
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-engine-docs",
|
||||
"version": "1.2.31",
|
||||
"version": "1.2.15",
|
||||
"description": "低代码引擎版本化文档",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"lerna": "4.0.0",
|
||||
"version": "1.3.2",
|
||||
"version": "1.2.4-beta.3",
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"packages": [
|
||||
|
||||
@ -94,26 +94,16 @@ await CodeGenerator.init();
|
||||
4. 出码
|
||||
|
||||
```js
|
||||
const project = await CodeGenerator.generateCode({
|
||||
const result = await CodeGenerator.generateCode({
|
||||
solution: 'icejs', // 出码方案 (目前内置有 icejs 和 rax )
|
||||
schema, // 编排搭建出来的 schema
|
||||
});
|
||||
|
||||
console.log(project); // 出码结果(默认是递归结构描述的,可以传 flattenResult: true 以生成扁平结构的结果)
|
||||
console.log(result); // 出码结果(默认是递归结构描述的,可以传 flattenResult: true 以生成扁平结构的结果)
|
||||
```
|
||||
|
||||
注:一般来说在浏览器中出码适合做即时预览功能。
|
||||
|
||||
5. 下载 zip 包
|
||||
|
||||
```js
|
||||
// 写入到 zip 包
|
||||
await CodeGenerator.publishers.zip().publish({
|
||||
project, // 上一步生成的 project
|
||||
projectSlug: 'your-project-slug', // 项目标识 -- 对应下载 your-project-slug.zip 文件
|
||||
});
|
||||
```
|
||||
|
||||
### 5)自定义出码
|
||||
|
||||
前端框架灵活多变,默认内置的出码方案很难满足所有人的需求,好在此代码生成器支持非常灵活的插件机制 -- 欢迎参考 ./src/plugins/xxx 来编写您自己的出码插件,然后参考 ./src/solutions/xxx 将各种插件组合成一套适合您的业务场景的出码方案。
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-code-generator",
|
||||
"version": "1.1.7",
|
||||
"version": "1.1.6",
|
||||
"description": "出码引擎 for LowCode Engine",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
@ -80,7 +80,6 @@
|
||||
"change-case": "^3.1.0",
|
||||
"commander": "^6.1.0",
|
||||
"debug": "^4.3.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"fp-ts": "^2.11.9",
|
||||
"fs-extra": "9.x",
|
||||
"glob": "^7.2.0",
|
||||
@ -110,7 +109,6 @@
|
||||
"devDependencies": {
|
||||
"@iceworks/spec": "^1.4.2",
|
||||
"@types/babel__traverse": "^7.11.0",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/lodash": "^4.14.162",
|
||||
"@types/node": "^14.14.20",
|
||||
|
||||
@ -2,9 +2,9 @@ import { ResultDir } from '@alilc/lowcode-types';
|
||||
import { PublisherFactory, IPublisher, IPublisherFactoryParams, PublisherError } from '../../types';
|
||||
import { getErrorMessage } from '../../utils/errors';
|
||||
import { isNodeProcess, writeZipToDisk, generateProjectZip } from './utils';
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
export type ZipBuffer = Buffer | Blob;
|
||||
// export type ZipBuffer = Buffer | Blob;
|
||||
export type ZipBuffer = Buffer;
|
||||
|
||||
declare type ZipPublisherResponse = string | ZipBuffer;
|
||||
|
||||
@ -44,16 +44,10 @@ export const createZipPublisher: PublisherFactory<ZipFactoryParams, ZipPublisher
|
||||
try {
|
||||
const zipContent = await generateProjectZip(projectToPublish);
|
||||
|
||||
if (isNodeProcess()) {
|
||||
// If not output path is provided on the node side, zip is not written to disk
|
||||
const projectOutputPath = options.outputPath || outputPath;
|
||||
if (projectOutputPath) {
|
||||
await writeZipToDisk(projectOutputPath, zipContent, zipName);
|
||||
}
|
||||
} else {
|
||||
// the browser end does not require a path
|
||||
// auto download zip files
|
||||
saveAs(zipContent as Blob, `${zipName}.zip`);
|
||||
// If not output path is provided, zip is not written to disk
|
||||
const projectOutputPath = options.outputPath || outputPath;
|
||||
if (projectOutputPath && isNodeProcess()) {
|
||||
await writeZipToDisk(projectOutputPath, zipContent, zipName);
|
||||
}
|
||||
|
||||
return { success: true, payload: zipContent };
|
||||
|
||||
@ -40,7 +40,8 @@ export const writeZipToDisk = (
|
||||
export const generateProjectZip = async (project: ResultDir): Promise<ZipBuffer> => {
|
||||
let zip = new JSZip();
|
||||
zip = writeFolderToZip(project, zip, true);
|
||||
const zipType = isNodeProcess() ? 'nodebuffer' : 'blob';
|
||||
// const zipType = isNodeProcess() ? 'nodebuffer' : 'blob';
|
||||
const zipType = 'nodebuffer'; // 目前先只支持 node 调用
|
||||
return zip.generateAsync({ type: zipType });
|
||||
};
|
||||
|
||||
|
||||
@ -1,14 +1,6 @@
|
||||
import CodeGen from '../../../../src';
|
||||
import fileSaver from 'file-saver';
|
||||
import * as utils from '../../../../src/publisher/zip/utils';
|
||||
|
||||
jest.mock('file-saver');
|
||||
|
||||
describe('public/publisher/zip/zip', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should works', async () => {
|
||||
const zip = CodeGen.publishers.zip({
|
||||
outputPath: 'demo-output',
|
||||
@ -27,15 +19,15 @@ describe('public/publisher/zip/zip', () => {
|
||||
],
|
||||
};
|
||||
|
||||
expect(zip.getOutputPath()).toMatchInlineSnapshot('"demo-output"');
|
||||
expect(zip.getOutputPath()).toMatchInlineSnapshot(`"demo-output"`);
|
||||
|
||||
expect(zip.getProject()).toMatchInlineSnapshot('undefined');
|
||||
expect(zip.getProject()).toMatchInlineSnapshot(`undefined`);
|
||||
zip.setProject(demoProject);
|
||||
expect(zip.getProject()).toBeTruthy();
|
||||
|
||||
expect(zip.getOutputPath()).toMatchInlineSnapshot('"demo-output"');
|
||||
expect(zip.getOutputPath()).toMatchInlineSnapshot(`"demo-output"`);
|
||||
expect(zip.setOutputPath('output')).toBe(undefined);
|
||||
expect(zip.getOutputPath()).toMatchInlineSnapshot('"output"');
|
||||
expect(zip.getOutputPath()).toMatchInlineSnapshot(`"output"`);
|
||||
|
||||
const publishRes = await zip.publish({
|
||||
project: demoProject,
|
||||
@ -49,39 +41,4 @@ describe('public/publisher/zip/zip', () => {
|
||||
const zip = CodeGen.publishers.zip({});
|
||||
expect(zip.publish()).rejects.toBeTruthy();
|
||||
});
|
||||
|
||||
it('should publish the project as a zip file in the browser', async () => {
|
||||
const zipContent = 'zip content';
|
||||
const zipName = 'example-project';
|
||||
jest.spyOn(utils, 'isNodeProcess').mockReturnValue(false);
|
||||
// new Zip 里面也有平台判断,所以这里 mock
|
||||
jest.spyOn(utils, 'generateProjectZip').mockResolvedValue(zipContent as any);
|
||||
const spy = jest.spyOn(fileSaver, 'saveAs');
|
||||
|
||||
const zip = CodeGen.publishers.zip({
|
||||
projectSlug: zipName,
|
||||
});
|
||||
|
||||
const demoProject = {
|
||||
name: 'demo',
|
||||
dirs: [],
|
||||
files: [
|
||||
{
|
||||
name: 'package',
|
||||
ext: 'json',
|
||||
content: '{ "name": "demo", "version": "1.0.0" }',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
zip.setProject(demoProject);
|
||||
const publishRes = await zip.publish({
|
||||
project: demoProject,
|
||||
});
|
||||
|
||||
expect(publishRes.success).toBeTruthy();
|
||||
expect(spy).toBeCalledWith(zipContent, `${zipName}.zip`);
|
||||
spy.mockReset();
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
@ -21,8 +21,6 @@
|
||||
"lint:modules": "f2elint scan -q -i ./modules/*/src",
|
||||
"lint:modules:fix": "f2elint fix -i ./modules/*/src",
|
||||
"pub": "npm run watchdog:build && lerna publish patch --yes --force-publish --exact --no-changelog",
|
||||
"pub:minor": "npm run watchdog:build && lerna publish minor --yes --force-publish --exact --no-changelog",
|
||||
"pub:major": "npm run watchdog:build && lerna publish major --yes --force-publish --exact --no-changelog",
|
||||
"pub:premajor": "npm run watchdog:build && lerna publish premajor --force-publish --exact --dist-tag beta --preid beta --no-changelog",
|
||||
"pub:preminor": "npm run watchdog:build && lerna publish preminor --force-publish --exact --dist-tag beta --preid beta --no-changelog",
|
||||
"pub:prepatch": "npm run watchdog:build && lerna publish prepatch --force-publish --exact --dist-tag beta --preid beta --no-changelog",
|
||||
@ -54,7 +52,7 @@
|
||||
"yarn": "^1.22.17",
|
||||
"rimraf": "^3.0.2",
|
||||
"@types/react-router": "5.1.18",
|
||||
"@alilc/build-plugin-lce": "^0.0.5",
|
||||
"@alilc/build-plugin-lce": "^0.0.3",
|
||||
"babel-jest": "^26.5.2",
|
||||
"@alilc/lowcode-test-mate": "^1.0.1"
|
||||
},
|
||||
|
||||
@ -21,7 +21,6 @@ const jestConfig = {
|
||||
// testMatch: ['**/builtin-hotkey.test.ts'],
|
||||
// testMatch: ['**/selection.test.ts'],
|
||||
// testMatch: ['**/plugin/sequencify.test.ts'],
|
||||
// testMatch: ['**/builtin-simulator/utils/parse-metadata.test.ts'],
|
||||
transformIgnorePatterns: [
|
||||
`/node_modules/(?!${esModules})/`,
|
||||
],
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-designer",
|
||||
"version": "1.3.2",
|
||||
"version": "1.2.4-beta.3",
|
||||
"description": "Designer for Ali LowCode Engine",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
@ -15,9 +15,9 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alilc/lowcode-editor-core": "1.3.2",
|
||||
"@alilc/lowcode-types": "1.3.2",
|
||||
"@alilc/lowcode-utils": "1.3.2",
|
||||
"@alilc/lowcode-editor-core": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-types": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-utils": "1.2.4-beta.3",
|
||||
"classnames": "^2.2.6",
|
||||
"react": "^16",
|
||||
"react-dom": "^16.7.0",
|
||||
@ -52,7 +52,7 @@
|
||||
"type": "http",
|
||||
"url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/designer"
|
||||
},
|
||||
"gitHead": "2669f179e6f899d395ce1942d0fe04f9c5ed48a6",
|
||||
"gitHead": "5034994a2421e18d3ee6e14f6ce772ac133865d3",
|
||||
"bugs": "https://github.com/alibaba/lowcode-engine/issues",
|
||||
"homepage": "https://github.com/alibaba/lowcode-engine/#readme"
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
ComponentType,
|
||||
} from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer, computed, Tip, engineConfig } 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';
|
||||
@ -47,18 +47,14 @@ export class BorderSelectingInstance extends Component<{
|
||||
});
|
||||
|
||||
const { hideSelectTools } = observed.node.componentMeta.advanced;
|
||||
const hideComponentAction = engineConfig.get('hideComponentAction');
|
||||
|
||||
if (hideSelectTools) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
style={style}
|
||||
>
|
||||
{(!dragging && !hideComponentAction) ? <Toolbar observed={observed} /> : null}
|
||||
<div className={className} style={style}>
|
||||
{!dragging && <Toolbar observed={observed} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -39,7 +39,6 @@ import {
|
||||
isDragAnyObject,
|
||||
isDragNodeObject,
|
||||
isLocationData,
|
||||
Logger,
|
||||
} from '@alilc/lowcode-utils';
|
||||
import {
|
||||
isShaken,
|
||||
@ -73,8 +72,6 @@ import { IScroller } from '../designer/scroller';
|
||||
import { isElementNode, isDOMNodeVisible } from '../utils/misc';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
const logger = new Logger({ level: 'warn', bizName: 'designer' });
|
||||
|
||||
export type LibraryItem = IPublicTypePackage & {
|
||||
package: string;
|
||||
library: string;
|
||||
@ -125,6 +122,21 @@ const defaultSimulatorUrl = (() => {
|
||||
return urls;
|
||||
})();
|
||||
|
||||
const defaultRaxSimulatorUrl = (() => {
|
||||
const publicPath = getPublicPath();
|
||||
let urls;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_, prefix = '', dev] = /^(.+?)(\/js)?\/?$/.exec(publicPath) || [];
|
||||
if (dev) {
|
||||
urls = [`${prefix}/css/rax-simulator-renderer.css`, `${prefix}/js/rax-simulator-renderer.js`];
|
||||
} else if (process.env.NODE_ENV === 'production') {
|
||||
urls = [`${prefix}/rax-simulator-renderer.css`, `${prefix}/rax-simulator-renderer.js`];
|
||||
} else {
|
||||
urls = [`${prefix}/rax-simulator-renderer.css`, `${prefix}/rax-simulator-renderer.js`];
|
||||
}
|
||||
return urls;
|
||||
})();
|
||||
|
||||
const defaultEnvironment = [
|
||||
// https://g.alicdn.com/mylib/??react/16.11.0/umd/react.production.min.js,react-dom/16.8.6/umd/react-dom.production.min.js,prop-types/15.7.2/prop-types.min.js
|
||||
assetItem(
|
||||
@ -139,6 +151,17 @@ const defaultEnvironment = [
|
||||
),
|
||||
];
|
||||
|
||||
const defaultRaxEnvironment = [
|
||||
assetItem(
|
||||
AssetType.JSText,
|
||||
'window.Rax=parent.Rax;window.React=parent.React;window.ReactDOM=parent.ReactDOM;window.VisualEngineUtils=parent.VisualEngineUtils;window.VisualEngine=parent.VisualEngine',
|
||||
),
|
||||
assetItem(
|
||||
AssetType.JSText,
|
||||
'window.PropTypes=parent.PropTypes;React.PropTypes=parent.PropTypes; window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;',
|
||||
),
|
||||
];
|
||||
|
||||
export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProps> {
|
||||
readonly isSimulator = true;
|
||||
|
||||
@ -444,15 +467,11 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
|
||||
const libraryAsset: AssetList = this.buildLibrary();
|
||||
|
||||
if (this.renderEnv === 'rax') {
|
||||
logger.error('After LowcodeEngine v1.3.0, Rax is no longer supported.');
|
||||
}
|
||||
|
||||
const vendors = [
|
||||
// required & use once
|
||||
assetBundle(
|
||||
this.get('environment') ||
|
||||
defaultEnvironment,
|
||||
(this.renderEnv === 'rax' ? defaultRaxEnvironment : defaultEnvironment),
|
||||
AssetLevel.Environment,
|
||||
),
|
||||
// required & use once
|
||||
@ -465,7 +484,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
// required & use once
|
||||
assetBundle(
|
||||
this.get('simulatorUrl') ||
|
||||
defaultSimulatorUrl,
|
||||
(this.renderEnv === 'rax' ? defaultRaxSimulatorUrl : defaultSimulatorUrl),
|
||||
AssetLevel.Runtime,
|
||||
),
|
||||
];
|
||||
@ -832,22 +851,16 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
doc.addEventListener('contextmenu', (e: MouseEvent) => {
|
||||
const targetElement = e.target as HTMLElement;
|
||||
const nodeInst = this.getNodeInstanceFromElement(targetElement);
|
||||
const editor = this.designer?.editor;
|
||||
if (!nodeInst) {
|
||||
editor?.eventBus.emit('designer.builtinSimulator.contextmenu', {
|
||||
originalEvent: e,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const node = nodeInst.node || this.project.currentDocument?.focusNode;
|
||||
if (!node) {
|
||||
editor?.eventBus.emit('designer.builtinSimulator.contextmenu', {
|
||||
originalEvent: e,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// dirty code should refector
|
||||
const editor = this.designer?.editor;
|
||||
const npm = node?.componentMeta?.npm;
|
||||
const selected =
|
||||
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
|
||||
|
||||
@ -16,16 +16,8 @@ export const primitiveTypes = [
|
||||
'any',
|
||||
];
|
||||
|
||||
interface LowcodeCheckType {
|
||||
// isRequired, props, propName, componentName, location, propFullName, secret
|
||||
(props: any, propName: string, componentName: string, ...rest: any[]): Error | null;
|
||||
// (...reset: any[]): Error | null;
|
||||
isRequired?: LowcodeCheckType;
|
||||
type?: string | object;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
function makeRequired(propType: any, lowcodeType: string | object): LowcodeCheckType {
|
||||
function makeRequired(propType: any, lowcodeType: string | object) {
|
||||
function lowcodeCheckTypeIsRequired(...rest: any[]) {
|
||||
return propType.isRequired(...rest);
|
||||
}
|
||||
@ -42,7 +34,7 @@ function makeRequired(propType: any, lowcodeType: string | object): LowcodeCheck
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
function define(propType: any = PropTypes.any, lowcodeType: string | object = {}): LowcodeCheckType {
|
||||
function define(propType: any = PropTypes.any, lowcodeType: string | object = {}) {
|
||||
if (!propType._inner && propType.name !== 'lowcodeCheckType') {
|
||||
propType.lowcodeType = lowcodeType;
|
||||
}
|
||||
|
||||
@ -48,17 +48,13 @@ export function buildFilter(rule?: string | string[] | RegExp | IPublicTypeNesti
|
||||
return rule;
|
||||
}
|
||||
if (isRegExp(rule)) {
|
||||
return (testNode: Node | IPublicTypeNodeSchema) => {
|
||||
return rule.test(testNode.componentName);
|
||||
};
|
||||
return (testNode: Node | IPublicTypeNodeSchema) => rule.test(testNode.componentName);
|
||||
}
|
||||
const list = ensureAList(rule);
|
||||
if (!list) {
|
||||
return null;
|
||||
}
|
||||
return (testNode: Node | IPublicTypeNodeSchema) => {
|
||||
return list.includes(testNode.componentName);
|
||||
};
|
||||
return (testNode: Node | IPublicTypeNodeSchema) => list.includes(testNode.componentName);
|
||||
}
|
||||
|
||||
export interface IComponentMeta extends IPublicModelComponentMeta<INode> {
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
.engine-context-menu {
|
||||
&.next-menu.next-ver .next-menu-item {
|
||||
padding-right: 30px;
|
||||
|
||||
.next-menu-item-inner {
|
||||
height: var(--context-menu-item-height, 30px);
|
||||
line-height: var(--context-menu-item-height, 30px);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,233 +0,0 @@
|
||||
import { IPublicTypeContextMenuAction, IPublicEnumContextMenuType, IPublicTypeContextMenuItem, IPublicApiMaterial, IPublicModelPluginContext } from '@alilc/lowcode-types';
|
||||
import { IDesigner, INode } from './designer';
|
||||
import { createContextMenu, parseContextMenuAsReactNode, parseContextMenuProperties, uniqueId } from '@alilc/lowcode-utils';
|
||||
import { Menu } from '@alifd/next';
|
||||
import { engineConfig } from '@alilc/lowcode-editor-core';
|
||||
import './context-menu-actions.scss';
|
||||
|
||||
export interface IContextMenuActions {
|
||||
actions: IPublicTypeContextMenuAction[];
|
||||
|
||||
adjustMenuLayoutFn: (actions: IPublicTypeContextMenuItem[]) => IPublicTypeContextMenuItem[];
|
||||
|
||||
addMenuAction: IPublicApiMaterial['addContextMenuOption'];
|
||||
|
||||
removeMenuAction: IPublicApiMaterial['removeContextMenuOption'];
|
||||
|
||||
adjustMenuLayout: IPublicApiMaterial['adjustContextMenuLayout'];
|
||||
}
|
||||
|
||||
let adjustMenuLayoutFn: Function = (actions: IPublicTypeContextMenuAction[]) => actions;
|
||||
|
||||
export class GlobalContextMenuActions {
|
||||
enableContextMenu: boolean;
|
||||
|
||||
dispose: Function[];
|
||||
|
||||
contextMenuActionsMap: Map<string, ContextMenuActions> = new Map();
|
||||
|
||||
constructor() {
|
||||
this.dispose = [];
|
||||
|
||||
engineConfig.onGot('enableContextMenu', (enable) => {
|
||||
if (this.enableContextMenu === enable) {
|
||||
return;
|
||||
}
|
||||
this.enableContextMenu = enable;
|
||||
this.dispose.forEach(d => d());
|
||||
if (enable) {
|
||||
this.initEvent();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleContextMenu = (
|
||||
event: MouseEvent,
|
||||
) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const actions: IPublicTypeContextMenuAction[] = [];
|
||||
let contextMenu: ContextMenuActions = this.contextMenuActionsMap.values().next().value;
|
||||
this.contextMenuActionsMap.forEach((contextMenu) => {
|
||||
actions.push(...contextMenu.actions);
|
||||
});
|
||||
|
||||
let destroyFn: Function | undefined;
|
||||
|
||||
const destroy = () => {
|
||||
destroyFn?.();
|
||||
};
|
||||
const pluginContext: IPublicModelPluginContext = contextMenu.designer.editor.get('pluginContext') as IPublicModelPluginContext;
|
||||
|
||||
const menus: IPublicTypeContextMenuItem[] = parseContextMenuProperties(actions, {
|
||||
nodes: [],
|
||||
destroy,
|
||||
event,
|
||||
pluginContext,
|
||||
});
|
||||
|
||||
if (!menus.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const layoutMenu = adjustMenuLayoutFn(menus);
|
||||
|
||||
const menuNode = parseContextMenuAsReactNode(layoutMenu, {
|
||||
destroy,
|
||||
nodes: [],
|
||||
pluginContext,
|
||||
});
|
||||
|
||||
const target = event.target;
|
||||
|
||||
const { top, left } = target?.getBoundingClientRect();
|
||||
|
||||
const menuInstance = Menu.create({
|
||||
target: event.target,
|
||||
offset: [event.clientX - left, event.clientY - top],
|
||||
children: menuNode,
|
||||
className: 'engine-context-menu',
|
||||
});
|
||||
|
||||
destroyFn = (menuInstance as any).destroy;
|
||||
};
|
||||
|
||||
initEvent() {
|
||||
this.dispose.push(
|
||||
(() => {
|
||||
const handleContextMenu = (e: MouseEvent) => {
|
||||
this.handleContextMenu(e);
|
||||
};
|
||||
|
||||
document.addEventListener('contextmenu', handleContextMenu);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('contextmenu', handleContextMenu);
|
||||
};
|
||||
})(),
|
||||
);
|
||||
}
|
||||
|
||||
registerContextMenuActions(contextMenu: ContextMenuActions) {
|
||||
this.contextMenuActionsMap.set(contextMenu.id, contextMenu);
|
||||
}
|
||||
}
|
||||
|
||||
const globalContextMenuActions = new GlobalContextMenuActions();
|
||||
|
||||
export class ContextMenuActions implements IContextMenuActions {
|
||||
actions: IPublicTypeContextMenuAction[] = [];
|
||||
|
||||
designer: IDesigner;
|
||||
|
||||
dispose: Function[];
|
||||
|
||||
enableContextMenu: boolean;
|
||||
|
||||
id: string = uniqueId('contextMenu');;
|
||||
|
||||
constructor(designer: IDesigner) {
|
||||
this.designer = designer;
|
||||
this.dispose = [];
|
||||
|
||||
engineConfig.onGot('enableContextMenu', (enable) => {
|
||||
if (this.enableContextMenu === enable) {
|
||||
return;
|
||||
}
|
||||
this.enableContextMenu = enable;
|
||||
this.dispose.forEach(d => d());
|
||||
if (enable) {
|
||||
this.initEvent();
|
||||
}
|
||||
});
|
||||
|
||||
globalContextMenuActions.registerContextMenuActions(this);
|
||||
}
|
||||
|
||||
handleContextMenu = (
|
||||
nodes: INode[],
|
||||
event: MouseEvent,
|
||||
) => {
|
||||
const designer = this.designer;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const actions = designer.contextMenuActions.actions;
|
||||
|
||||
const { bounds } = designer.project.simulator?.viewport || { bounds: { left: 0, top: 0 } };
|
||||
const { left: simulatorLeft, top: simulatorTop } = bounds;
|
||||
|
||||
let destroyFn: Function | undefined;
|
||||
|
||||
const destroy = () => {
|
||||
destroyFn?.();
|
||||
};
|
||||
|
||||
const pluginContext: IPublicModelPluginContext = this.designer.editor.get('pluginContext') as IPublicModelPluginContext;
|
||||
|
||||
const menus: IPublicTypeContextMenuItem[] = parseContextMenuProperties(actions, {
|
||||
nodes: nodes.map(d => designer.shellModelFactory.createNode(d)!),
|
||||
destroy,
|
||||
event,
|
||||
pluginContext,
|
||||
});
|
||||
|
||||
if (!menus.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const layoutMenu = adjustMenuLayoutFn(menus);
|
||||
|
||||
const menuNode = parseContextMenuAsReactNode(layoutMenu, {
|
||||
destroy,
|
||||
nodes: nodes.map(d => designer.shellModelFactory.createNode(d)!),
|
||||
pluginContext,
|
||||
});
|
||||
|
||||
destroyFn = createContextMenu(menuNode, {
|
||||
event,
|
||||
offset: [simulatorLeft, simulatorTop],
|
||||
});
|
||||
};
|
||||
|
||||
initEvent() {
|
||||
const designer = this.designer;
|
||||
this.dispose.push(
|
||||
designer.editor.eventBus.on('designer.builtinSimulator.contextmenu', ({
|
||||
node,
|
||||
originalEvent,
|
||||
}: {
|
||||
node: INode;
|
||||
originalEvent: MouseEvent;
|
||||
}) => {
|
||||
originalEvent.stopPropagation();
|
||||
originalEvent.preventDefault();
|
||||
// 如果右键的节点不在 当前选中的节点中,选中该节点
|
||||
if (!designer.currentSelection.has(node.id)) {
|
||||
designer.currentSelection.select(node.id);
|
||||
}
|
||||
const nodes = designer.currentSelection.getNodes();
|
||||
this.handleContextMenu(nodes, originalEvent);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
addMenuAction(action: IPublicTypeContextMenuAction) {
|
||||
this.actions.push({
|
||||
type: IPublicEnumContextMenuType.MENU_ITEM,
|
||||
...action,
|
||||
});
|
||||
}
|
||||
|
||||
removeMenuAction(name: string) {
|
||||
const i = this.actions.findIndex((action) => action.name === name);
|
||||
if (i > -1) {
|
||||
this.actions.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
adjustMenuLayout(fn: (actions: IPublicTypeContextMenuItem[]) => IPublicTypeContextMenuItem[]) {
|
||||
adjustMenuLayoutFn = fn;
|
||||
}
|
||||
}
|
||||
@ -36,10 +36,6 @@ class Clipboard implements IClipboard {
|
||||
|
||||
private waitFn?: (data: any, e: ClipboardEvent) => void;
|
||||
|
||||
constructor() {
|
||||
this.injectCopyPaster(document);
|
||||
}
|
||||
|
||||
isCopyPasteEvent(e: Event) {
|
||||
this.isCopyPaster(e.target);
|
||||
}
|
||||
@ -73,13 +69,7 @@ class Clipboard implements IClipboard {
|
||||
}
|
||||
const copyPaster = document.createElement<'textarea'>('textarea');
|
||||
copyPaster.style.cssText = 'position: absolute;left: -9999px;top:-100px';
|
||||
if (document.body) {
|
||||
document.body.appendChild(copyPaster);
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.body.appendChild(copyPaster);
|
||||
});
|
||||
}
|
||||
document.body.appendChild(copyPaster);
|
||||
const dispose = this.initCopyPaster(copyPaster);
|
||||
return () => {
|
||||
dispose();
|
||||
|
||||
@ -4,6 +4,7 @@ import BuiltinDragGhostComponent from './drag-ghost';
|
||||
import { Designer, DesignerProps } from './designer';
|
||||
import { ProjectView } from '../project';
|
||||
import './designer.less';
|
||||
import { clipboard } from './clipboard';
|
||||
|
||||
type IProps = DesignerProps & {
|
||||
designer?: Designer;
|
||||
@ -43,6 +44,7 @@ export class DesignerView extends Component<IProps> {
|
||||
if (onMount) {
|
||||
onMount(this.designer);
|
||||
}
|
||||
clipboard.injectCopyPaster(document);
|
||||
this.designer.postEvent('mount', this.designer);
|
||||
}
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ import {
|
||||
} from '@alilc/lowcode-types';
|
||||
import { mergeAssets, IPublicTypeAssetsJson, isNodeSchema, isDragNodeObject, isDragNodeDataObject, isLocationChildrenDetail, Logger } from '@alilc/lowcode-utils';
|
||||
import { IProject, Project } from '../project';
|
||||
import { Node, DocumentModel, insertChildren, INode, ISelection } from '../document';
|
||||
import { Node, DocumentModel, insertChildren, INode } from '../document';
|
||||
import { ComponentMeta, IComponentMeta } from '../component-meta';
|
||||
import { INodeSelector, Component } from '../simulator';
|
||||
import { Scroller } from './scroller';
|
||||
@ -32,7 +32,6 @@ import { OffsetObserver, createOffsetObserver } from './offset-observer';
|
||||
import { ISettingTopEntry, SettingTopEntry } from './setting';
|
||||
import { BemToolsManager } from '../builtin-simulator/bem-tools/manager';
|
||||
import { ComponentActions } from '../component-actions';
|
||||
import { ContextMenuActions, IContextMenuActions } from '../context-menu-actions';
|
||||
|
||||
const logger = new Logger({ level: 'warn', bizName: 'designer' });
|
||||
|
||||
@ -73,16 +72,12 @@ export interface IDesigner {
|
||||
|
||||
get componentActions(): ComponentActions;
|
||||
|
||||
get contextMenuActions(): ContextMenuActions;
|
||||
|
||||
get editor(): IPublicModelEditor;
|
||||
|
||||
get detecting(): Detecting;
|
||||
|
||||
get simulatorComponent(): ComponentType<any> | undefined;
|
||||
|
||||
get currentSelection(): ISelection;
|
||||
|
||||
createScroller(scrollable: IPublicTypeScrollable): IPublicModelScroller;
|
||||
|
||||
refreshComponentMetasMap(): void;
|
||||
@ -127,8 +122,6 @@ export class Designer implements IDesigner {
|
||||
|
||||
readonly componentActions = new ComponentActions();
|
||||
|
||||
readonly contextMenuActions: IContextMenuActions;
|
||||
|
||||
readonly activeTracker = new ActiveTracker();
|
||||
|
||||
readonly detecting = new Detecting();
|
||||
@ -205,8 +198,6 @@ export class Designer implements IDesigner {
|
||||
this.postEvent('dragstart', e);
|
||||
});
|
||||
|
||||
this.contextMenuActions = new ContextMenuActions(this);
|
||||
|
||||
this.dragon.onDrag((e) => {
|
||||
if (this.props?.onDrag) {
|
||||
this.props.onDrag(e);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { IPublicTypeCustomView, IPublicModelEditor, IPublicModelSettingTopEntry, IPublicApiSetters } from '@alilc/lowcode-types';
|
||||
import { IPublicTypeCustomView, IPublicModelEditor, IPublicModelSettingTopEntry } from '@alilc/lowcode-types';
|
||||
import { isCustomView } from '@alilc/lowcode-utils';
|
||||
import { computed, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
|
||||
import { ISettingEntry } from './setting-entry-type';
|
||||
@ -6,6 +6,7 @@ import { ISettingField, SettingField } from './setting-field';
|
||||
import { INode } from '../../document';
|
||||
import type { IComponentMeta } from '../../component-meta';
|
||||
import { IDesigner } from '../designer';
|
||||
import { Setters } from '@alilc/lowcode-shell';
|
||||
|
||||
function generateSessionId(nodes: INode[]) {
|
||||
return nodes
|
||||
@ -18,18 +19,18 @@ export interface ISettingTopEntry extends ISettingEntry, IPublicModelSettingTopE
|
||||
INode,
|
||||
ISettingField
|
||||
> {
|
||||
purge(): void;
|
||||
|
||||
items: Array<ISettingField | IPublicTypeCustomView>;
|
||||
|
||||
readonly top: ISettingTopEntry;
|
||||
|
||||
readonly parent: ISettingTopEntry;
|
||||
|
||||
readonly path: never[];
|
||||
|
||||
items: Array<ISettingField | IPublicTypeCustomView>;
|
||||
|
||||
componentMeta: IComponentMeta | null;
|
||||
|
||||
purge(): void;
|
||||
|
||||
getExtraPropValue(propName: string): void;
|
||||
|
||||
setExtraPropValue(propName: string, value: any): void;
|
||||
@ -91,7 +92,7 @@ export class SettingTopEntry implements ISettingTopEntry {
|
||||
|
||||
readonly designer: IDesigner | undefined;
|
||||
|
||||
readonly setters: IPublicApiSetters;
|
||||
readonly setters: Setters;
|
||||
|
||||
disposeFunctions: any[] = [];
|
||||
|
||||
@ -102,7 +103,7 @@ export class SettingTopEntry implements ISettingTopEntry {
|
||||
this.id = generateSessionId(nodes);
|
||||
this.first = nodes[0];
|
||||
this.designer = this.first.document?.designer;
|
||||
this.setters = editor.get('setters') as IPublicApiSetters;
|
||||
this.setters = editor.get('setters') as Setters;
|
||||
|
||||
// setups
|
||||
this.setupComponentMeta();
|
||||
|
||||
@ -70,7 +70,7 @@ export class Transducer {
|
||||
}
|
||||
if (isDynamicSetter(setter) && isDynamic) {
|
||||
try {
|
||||
setter = setter.call(context.internalToShellField(), context.internalToShellField());
|
||||
setter = setter.call(context, context);
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
|
||||
|
||||
@ -462,9 +462,6 @@ export class NodeChildren implements INodeChildren {
|
||||
const node: INode = this.owner.document?.createNode(child);
|
||||
this.children.push(node);
|
||||
node.internalSetParent(this.owner);
|
||||
/* istanbul ignore next */
|
||||
const editor = node.document?.designer.editor;
|
||||
editor?.eventBus.emit('node.add', { node });
|
||||
});
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@ -392,7 +392,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
|
||||
this.isInited = true;
|
||||
this.emitter = createModuleEventBus('Node');
|
||||
const { editor } = this.document.designer;
|
||||
const editor = this.document.designer.editor;
|
||||
this.onVisibleChange((visible: boolean) => {
|
||||
editor?.eventBus.emit(EDITOR_EVENT.NODE_VISIBLE_CHANGE, this, visible);
|
||||
});
|
||||
@ -1219,18 +1219,11 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
/**
|
||||
* 获取磁贴相关信息
|
||||
*/
|
||||
getRGL(): {
|
||||
isContainerNode: boolean;
|
||||
isEmptyNode: boolean;
|
||||
isRGLContainerNode: boolean;
|
||||
isRGLNode: boolean;
|
||||
isRGL: boolean;
|
||||
rglNode: Node | null;
|
||||
} {
|
||||
getRGL() {
|
||||
const isContainerNode = this.isContainer();
|
||||
const isEmptyNode = this.isEmpty();
|
||||
const isRGLContainerNode = this.isRGLContainer;
|
||||
const isRGLNode = (this.getParent()?.isRGLContainer) as boolean;
|
||||
const isRGLNode = this.getParent()?.isRGLContainer;
|
||||
const isRGL = isRGLContainerNode || (isRGLNode && (!isContainerNode || !isEmptyNode));
|
||||
let rglNode = isRGLContainerNode ? this : isRGL ? this?.getParent() : null;
|
||||
return { isContainerNode, isEmptyNode, isRGLContainerNode, isRGLNode, isRGL, rglNode };
|
||||
|
||||
@ -353,6 +353,7 @@ export class Prop implements IProp, IPropParent {
|
||||
@action
|
||||
setValue(val: IPublicTypeCompositeValue) {
|
||||
if (val === this._value) return;
|
||||
const editor = this.owner.document?.designer.editor;
|
||||
const oldValue = this._value;
|
||||
this._value = val;
|
||||
this._code = null;
|
||||
@ -385,31 +386,22 @@ export class Prop implements IProp, IPropParent {
|
||||
this.setupItems();
|
||||
|
||||
if (oldValue !== this._value) {
|
||||
this.emitChange({ oldValue });
|
||||
const propsInfo = {
|
||||
key: this.key,
|
||||
prop: this,
|
||||
oldValue,
|
||||
newValue: this._value,
|
||||
};
|
||||
|
||||
editor?.eventBus.emit(GlobalEvent.Node.Prop.InnerChange, {
|
||||
node: this.owner as any,
|
||||
...propsInfo,
|
||||
});
|
||||
|
||||
this.owner?.emitPropChange?.(propsInfo);
|
||||
}
|
||||
}
|
||||
|
||||
emitChange = ({
|
||||
oldValue,
|
||||
}: {
|
||||
oldValue: IPublicTypeCompositeValue | UNSET;
|
||||
}) => {
|
||||
const editor = this.owner.document?.designer.editor;
|
||||
const propsInfo = {
|
||||
key: this.key,
|
||||
prop: this,
|
||||
oldValue,
|
||||
newValue: this.type === 'unset' ? undefined : this._value,
|
||||
};
|
||||
|
||||
editor?.eventBus.emit(GlobalEvent.Node.Prop.InnerChange, {
|
||||
node: this.owner as any,
|
||||
...propsInfo,
|
||||
});
|
||||
|
||||
this.owner?.emitPropChange?.(propsInfo);
|
||||
};
|
||||
|
||||
getValue(): IPublicTypeCompositeValue {
|
||||
return this.export(IPublicEnumTransformStage.Serilize);
|
||||
}
|
||||
@ -470,12 +462,7 @@ export class Prop implements IProp, IPropParent {
|
||||
*/
|
||||
@action
|
||||
unset() {
|
||||
if (this._type !== 'unset') {
|
||||
this._type = 'unset';
|
||||
this.emitChange({
|
||||
oldValue: this._value,
|
||||
});
|
||||
}
|
||||
this._type = 'unset';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -570,7 +557,6 @@ export class Prop implements IProp, IPropParent {
|
||||
@action
|
||||
remove() {
|
||||
this.parent.delete(this);
|
||||
this.unset();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -6,4 +6,3 @@ export * from './project';
|
||||
export * from './builtin-simulator';
|
||||
export * from './plugin';
|
||||
export * from './types';
|
||||
export * from './context-menu-actions';
|
||||
|
||||
@ -19,7 +19,6 @@ import {
|
||||
IPublicApiWorkspace,
|
||||
IPublicEnumPluginRegisterLevel,
|
||||
IPublicModelWindow,
|
||||
IPublicApiCommonUI,
|
||||
} from '@alilc/lowcode-types';
|
||||
import {
|
||||
IPluginContextOptions,
|
||||
@ -46,8 +45,6 @@ export default class PluginContext implements
|
||||
workspace: IPublicApiWorkspace;
|
||||
registerLevel: IPublicEnumPluginRegisterLevel;
|
||||
editorWindow: IPublicModelWindow;
|
||||
commonUI: IPublicApiCommonUI;
|
||||
isPluginRegisteredInWorkspace: false;
|
||||
|
||||
constructor(
|
||||
options: IPluginContextOptions,
|
||||
|
||||
@ -18,8 +18,6 @@ import {
|
||||
IPublicTypePluginRegisterOptions,
|
||||
IPublicModelWindow,
|
||||
IPublicEnumPluginRegisterLevel,
|
||||
IPublicApiCommonUI,
|
||||
IPublicApiCommand,
|
||||
} from '@alilc/lowcode-types';
|
||||
import PluginContext from './plugin-context';
|
||||
|
||||
@ -63,8 +61,6 @@ export interface ILowCodePluginContextPrivate {
|
||||
set editorWindow(window: IPublicModelWindow);
|
||||
set registerLevel(level: IPublicEnumPluginRegisterLevel);
|
||||
set isPluginRegisteredInWorkspace(flag: boolean);
|
||||
set commonUI(commonUI: IPublicApiCommonUI);
|
||||
set command(command: IPublicApiCommand);
|
||||
}
|
||||
export interface ILowCodePluginContextApiAssembler {
|
||||
assembleApis(
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import '../../fixtures/window';
|
||||
import PropTypes from 'prop-types';
|
||||
import { LowcodeTypes, parseMetadata, parseProps } from '../../../src/builtin-simulator/utils/parse-metadata';
|
||||
import { default as ReactPropTypesSecret } from 'prop-types/lib/ReactPropTypesSecret';
|
||||
import { parseMetadata } from '../../../src/builtin-simulator/utils/parse-metadata';
|
||||
|
||||
describe('parseMetadata', () => {
|
||||
it('parseMetadata', async () => {
|
||||
@ -13,165 +11,3 @@ describe('parseMetadata', () => {
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('LowcodeTypes basic type validators', () => {
|
||||
it('should validate string types', () => {
|
||||
const stringValidator = LowcodeTypes.string;
|
||||
// 对 stringValidator 进行测试
|
||||
const props = { testProp: 'This is a string' };
|
||||
const propName = 'testProp';
|
||||
const componentName = 'TestComponent';
|
||||
|
||||
const result = stringValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeNull(); // No error for valid string
|
||||
});
|
||||
|
||||
it('should fail with a non-string type', () => {
|
||||
const stringValidator = LowcodeTypes.string;
|
||||
const props = { testProp: 42 };
|
||||
const propName = 'testProp';
|
||||
const componentName = 'TestComponent';
|
||||
|
||||
const result = stringValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeInstanceOf(Error); // Error for non-string type
|
||||
expect(result.message).toContain('Invalid prop `testProp` of type `number` supplied to `TestComponent`, expected `string`.');
|
||||
});
|
||||
|
||||
it('should pass with a valid number', () => {
|
||||
const numberValidator = LowcodeTypes.number;
|
||||
const props = { testProp: 42 };
|
||||
const propName = 'testProp';
|
||||
const componentName = 'TestComponent';
|
||||
|
||||
const result = numberValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeNull(); // No error for valid number
|
||||
});
|
||||
|
||||
it('should fail with a non-number type', () => {
|
||||
const numberValidator = LowcodeTypes.number;
|
||||
const props = { testProp: 'Not a number' };
|
||||
const propName = 'testProp';
|
||||
const componentName = 'TestComponent';
|
||||
|
||||
const result = numberValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeInstanceOf(Error); // Error for non-number type
|
||||
expect(result.message).toContain('Invalid prop `testProp` of type `string` supplied to `TestComponent`, expected `number`.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Custom type constructors', () => {
|
||||
it('should create a custom type validator using define', () => {
|
||||
const customType = LowcodeTypes.define(PropTypes.string, 'customType');
|
||||
const props = { testProp: 'This is a string' };
|
||||
const propName = 'testProp';
|
||||
const componentName = 'TestComponent';
|
||||
|
||||
// 测试有效值
|
||||
const validResult = customType(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(validResult).toBeNull(); // No error for valid string
|
||||
|
||||
// 测试无效值
|
||||
const invalidProps = { testProp: 42 };
|
||||
const invalidResult = customType(invalidProps, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(invalidResult).toBeInstanceOf(Error); // Error for non-string type
|
||||
|
||||
// 验证 lowcodeType 属性
|
||||
expect(customType.lowcodeType).toEqual('customType');
|
||||
|
||||
// 验证 isRequired 属性
|
||||
const requiredResult = customType.isRequired(invalidProps, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(requiredResult).toBeInstanceOf(Error); // Error for non-string type
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('Advanced type constructors', () => {
|
||||
describe('oneOf Type Validator', () => {
|
||||
const oneOfValidator = LowcodeTypes.oneOf(['red', 'green', 'blue']);
|
||||
const propName = 'color';
|
||||
const componentName = 'ColorPicker';
|
||||
|
||||
it('should pass with a valid value', () => {
|
||||
const props = { color: 'red' };
|
||||
const result = oneOfValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeNull(); // No error for valid value
|
||||
});
|
||||
|
||||
it('should fail with an invalid value', () => {
|
||||
const props = { color: 'yellow' };
|
||||
const result = oneOfValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeInstanceOf(Error); // Error for invalid value
|
||||
expect(result.message).toContain(`Invalid prop \`${propName}\` of value \`yellow\` supplied to \`${componentName}\`, expected one of ["red","green","blue"].`);
|
||||
});
|
||||
|
||||
it('should fail with a non-existing value', () => {
|
||||
const props = { color: 'others' };
|
||||
const result = oneOfValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeInstanceOf(Error); // Error for non-existing value
|
||||
expect(result.message).toContain(`Invalid prop \`${propName}\` of value \`others\` supplied to \`${componentName}\`, expected one of ["red","green","blue"].`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('parseProps function', () => {
|
||||
it('should correctly parse propTypes and defaultProps', () => {
|
||||
const component = {
|
||||
propTypes: {
|
||||
name: LowcodeTypes.string,
|
||||
age: LowcodeTypes.number,
|
||||
},
|
||||
defaultProps: {
|
||||
name: 'John Doe',
|
||||
age: 30,
|
||||
},
|
||||
};
|
||||
const parsedProps = parseProps(component);
|
||||
|
||||
// 测试结果长度
|
||||
expect(parsedProps.length).toBe(2);
|
||||
|
||||
// 测试 name 属性
|
||||
const nameProp: any = parsedProps.find(prop => prop.name === 'name');
|
||||
expect(nameProp).toBeDefined();
|
||||
expect(nameProp.propType).toEqual('string');
|
||||
expect(nameProp.defaultValue).toEqual('John Doe');
|
||||
|
||||
// 测试 age 属性
|
||||
const ageProp: any = parsedProps.find(prop => prop.name === 'age');
|
||||
expect(ageProp).toBeDefined();
|
||||
expect(ageProp.propType).toEqual('number');
|
||||
expect(ageProp.defaultValue).toEqual(30);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseProps function', () => {
|
||||
it('should correctly parse propTypes and defaultProps', () => {
|
||||
const component = {
|
||||
propTypes: {
|
||||
name: LowcodeTypes.string,
|
||||
age: LowcodeTypes.number,
|
||||
},
|
||||
defaultProps: {
|
||||
name: 'John Doe',
|
||||
age: 30,
|
||||
},
|
||||
};
|
||||
const parsedProps = parseProps(component);
|
||||
|
||||
// 测试结果长度
|
||||
expect(parsedProps.length).toBe(2);
|
||||
|
||||
// 测试 name 属性
|
||||
const nameProp: any = parsedProps.find(prop => prop.name === 'name');
|
||||
expect(nameProp).toBeDefined();
|
||||
expect(nameProp.propType).toEqual('string');
|
||||
expect(nameProp.defaultValue).toEqual('John Doe');
|
||||
|
||||
// 测试 age 属性
|
||||
const ageProp: any = parsedProps.find(prop => prop.name === 'age');
|
||||
expect(ageProp).toBeDefined();
|
||||
expect(ageProp.propType).toEqual('number');
|
||||
expect(ageProp.defaultValue).toEqual(30);
|
||||
});
|
||||
});
|
||||
|
||||
@ -3,7 +3,7 @@ import { Editor, engineConfig } from '@alilc/lowcode-editor-core';
|
||||
import { Designer } from '../../../../src/designer/designer';
|
||||
import { DocumentModel } from '../../../../src/document/document-model';
|
||||
import { Prop, isProp, isValidArrayIndex } from '../../../../src/document/node/props/prop';
|
||||
import { GlobalEvent, IPublicEnumTransformStage } from '@alilc/lowcode-types';
|
||||
import { IPublicEnumTransformStage } from '@alilc/lowcode-types';
|
||||
import { shellModelFactory } from '../../../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
const slotNodeImportMockFn = jest.fn();
|
||||
@ -24,24 +24,14 @@ const mockOwner = {
|
||||
remove: slotNodeRemoveMockFn,
|
||||
};
|
||||
},
|
||||
designer: {
|
||||
editor: {
|
||||
eventBus: {
|
||||
emit: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
designer: {},
|
||||
},
|
||||
isInited: true,
|
||||
emitPropChange: jest.fn(),
|
||||
delete() {},
|
||||
};
|
||||
|
||||
const mockPropsInst = {
|
||||
owner: mockOwner,
|
||||
delete() {},
|
||||
};
|
||||
|
||||
mockPropsInst.props = mockPropsInst;
|
||||
|
||||
describe('Prop 类测试', () => {
|
||||
@ -574,124 +564,3 @@ describe('其他导出函数', () => {
|
||||
expect(isValidArrayIndex('2', 1)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setValue with event', () => {
|
||||
let propInstance;
|
||||
let mockEmitChange;
|
||||
let mockEventBusEmit;
|
||||
let mockEmitPropChange;
|
||||
|
||||
beforeEach(() => {
|
||||
// Initialize the instance of your class
|
||||
propInstance = new Prop(mockPropsInst, true, 'stringProp');;
|
||||
|
||||
// Mock necessary methods and properties
|
||||
mockEmitChange = jest.spyOn(propInstance, 'emitChange');
|
||||
propInstance.owner = {
|
||||
document: {
|
||||
designer: {
|
||||
editor: {
|
||||
eventBus: {
|
||||
emit: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
emitPropChange: jest.fn(),
|
||||
delete() {},
|
||||
};
|
||||
mockEventBusEmit = jest.spyOn(propInstance.owner.document.designer.editor.eventBus, 'emit');
|
||||
mockEmitPropChange = jest.spyOn(propInstance.owner, 'emitPropChange');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should correctly handle string values and emit changes', () => {
|
||||
const oldValue = propInstance._value;
|
||||
const newValue = 'new string value';
|
||||
|
||||
propInstance.setValue(newValue);
|
||||
|
||||
const expectedPartialPropsInfo = expect.objectContaining({
|
||||
key: propInstance.key,
|
||||
newValue, // You can specifically test only certain keys
|
||||
oldValue,
|
||||
});
|
||||
|
||||
expect(propInstance.getValue()).toBe(newValue);
|
||||
expect(propInstance.type).toBe('literal');
|
||||
expect(mockEmitChange).toHaveBeenCalledWith({ oldValue });
|
||||
expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo);
|
||||
expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo);
|
||||
});
|
||||
|
||||
it('should handle object values and set type to map', () => {
|
||||
const oldValue = propInstance._value;
|
||||
const newValue = 234;
|
||||
|
||||
const expectedPartialPropsInfo = expect.objectContaining({
|
||||
key: propInstance.key,
|
||||
newValue, // You can specifically test only certain keys
|
||||
oldValue,
|
||||
});
|
||||
|
||||
propInstance.setValue(newValue);
|
||||
|
||||
expect(propInstance.getValue()).toEqual(newValue);
|
||||
expect(propInstance.type).toBe('literal');
|
||||
expect(mockEmitChange).toHaveBeenCalledWith({ oldValue });
|
||||
expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo);
|
||||
expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo);
|
||||
});
|
||||
|
||||
it('should has event when unset call', () => {
|
||||
const oldValue = propInstance._value;
|
||||
|
||||
propInstance.unset();
|
||||
|
||||
const expectedPartialPropsInfo = expect.objectContaining({
|
||||
key: propInstance.key,
|
||||
newValue: undefined, // You can specifically test only certain keys
|
||||
oldValue,
|
||||
});
|
||||
|
||||
expect(propInstance.getValue()).toEqual(undefined);
|
||||
expect(propInstance.type).toBe('unset');
|
||||
expect(mockEmitChange).toHaveBeenCalledWith({
|
||||
oldValue,
|
||||
newValue: undefined,
|
||||
});
|
||||
expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo);
|
||||
expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo);
|
||||
|
||||
propInstance.unset();
|
||||
expect(mockEmitChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// remove
|
||||
it('should has event when remove call', () => {
|
||||
const oldValue = propInstance._value;
|
||||
|
||||
propInstance.remove();
|
||||
|
||||
const expectedPartialPropsInfo = expect.objectContaining({
|
||||
key: propInstance.key,
|
||||
newValue: undefined, // You can specifically test only certain keys
|
||||
oldValue,
|
||||
});
|
||||
|
||||
expect(propInstance.getValue()).toEqual(undefined);
|
||||
// expect(propInstance.type).toBe('unset');
|
||||
expect(mockEmitChange).toHaveBeenCalledWith({
|
||||
oldValue,
|
||||
newValue: undefined,
|
||||
});
|
||||
expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo);
|
||||
expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo);
|
||||
|
||||
propInstance.remove();
|
||||
expect(mockEmitChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@alilc/build-plugin-lce",
|
||||
"@alilc/lowcode-test-mate/plugin/index.ts"
|
||||
],
|
||||
"babelPlugins": [
|
||||
["@babel/plugin-proposal-private-property-in-object", { "loose": true }]
|
||||
]
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const { join } = require('path');
|
||||
const esModules = [].join('|');
|
||||
const pkgNames = fs.readdirSync(join('..')).filter(pkgName => !pkgName.startsWith('.'));
|
||||
|
||||
const jestConfig = {
|
||||
transformIgnorePatterns: [
|
||||
`/node_modules/(?!${esModules})/`,
|
||||
],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
||||
collectCoverage: false,
|
||||
collectCoverageFrom: [
|
||||
'src/**/*.ts',
|
||||
'!src/**/*.d.ts',
|
||||
'!src/icons/**',
|
||||
'!src/locale/**',
|
||||
'!**/node_modules/**',
|
||||
'!**/vendor/**',
|
||||
],
|
||||
};
|
||||
|
||||
// 只对本仓库内的 pkg 做 mapping
|
||||
jestConfig.moduleNameMapper = {};
|
||||
jestConfig.moduleNameMapper[`^@alilc/lowcode\\-(${pkgNames.join('|')})$`] = '<rootDir>/../$1/src';
|
||||
|
||||
module.exports = jestConfig;
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-editor-core",
|
||||
"version": "1.3.2",
|
||||
"version": "1.2.4-beta.3",
|
||||
"description": "Core Api for Ali lowCode engine",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
@ -10,14 +10,12 @@
|
||||
"es"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "build-scripts build",
|
||||
"test": "build-scripts test --config build.test.json",
|
||||
"test:cov": "build-scripts test --config build.test.json --jest-coverage"
|
||||
"build": "build-scripts build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alifd/next": "^1.19.16",
|
||||
"@alilc/lowcode-types": "1.3.2",
|
||||
"@alilc/lowcode-utils": "1.3.2",
|
||||
"@alilc/lowcode-types": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-utils": "1.2.4-beta.3",
|
||||
"classnames": "^2.2.6",
|
||||
"debug": "^4.1.1",
|
||||
"intl-messageformat": "^9.3.1",
|
||||
@ -49,7 +47,7 @@
|
||||
"type": "http",
|
||||
"url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/editor-core"
|
||||
},
|
||||
"gitHead": "2669f179e6f899d395ce1942d0fe04f9c5ed48a6",
|
||||
"gitHead": "5034994a2421e18d3ee6e14f6ce772ac133865d3",
|
||||
"bugs": "https://github.com/alibaba/lowcode-engine/issues",
|
||||
"homepage": "https://github.com/alibaba/lowcode-engine/#readme"
|
||||
}
|
||||
|
||||
@ -1,91 +0,0 @@
|
||||
import { IPublicApiCommand, IPublicEnumTransitionType, IPublicModelPluginContext, IPublicTypeCommand, IPublicTypeCommandHandlerArgs, IPublicTypeListCommand } from '@alilc/lowcode-types';
|
||||
import { checkPropTypes } from '@alilc/lowcode-utils';
|
||||
export interface ICommand extends Omit<IPublicApiCommand, 'registerCommand' | 'batchExecuteCommand'> {
|
||||
registerCommand(command: IPublicTypeCommand, options?: {
|
||||
commandScope?: string;
|
||||
}): void;
|
||||
|
||||
batchExecuteCommand(commands: { name: string; args: IPublicTypeCommandHandlerArgs }[], pluginContext?: IPublicModelPluginContext): void;
|
||||
}
|
||||
|
||||
export interface ICommandOptions {
|
||||
commandScope?: string;
|
||||
}
|
||||
|
||||
export class Command implements ICommand {
|
||||
private commands: Map<string, IPublicTypeCommand> = new Map();
|
||||
private commandErrors: Function[] = [];
|
||||
|
||||
registerCommand(command: IPublicTypeCommand, options?: ICommandOptions): void {
|
||||
if (!options?.commandScope) {
|
||||
throw new Error('plugin meta.commandScope is required.');
|
||||
}
|
||||
const name = `${options.commandScope}:${command.name}`;
|
||||
if (this.commands.has(name)) {
|
||||
throw new Error(`Command '${command.name}' is already registered.`);
|
||||
}
|
||||
this.commands.set(name, {
|
||||
...command,
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
unregisterCommand(name: string): void {
|
||||
if (!this.commands.has(name)) {
|
||||
throw new Error(`Command '${name}' is not registered.`);
|
||||
}
|
||||
this.commands.delete(name);
|
||||
}
|
||||
|
||||
executeCommand(name: string, args: IPublicTypeCommandHandlerArgs): void {
|
||||
const command = this.commands.get(name);
|
||||
if (!command) {
|
||||
throw new Error(`Command '${name}' is not registered.`);
|
||||
}
|
||||
command.parameters?.forEach(d => {
|
||||
if (!checkPropTypes(args[d.name], d.name, d.propType, 'command')) {
|
||||
throw new Error(`Command '${name}' arguments ${d.name} is invalid.`);
|
||||
}
|
||||
});
|
||||
try {
|
||||
command.handler(args);
|
||||
} catch (error) {
|
||||
if (this.commandErrors && this.commandErrors.length) {
|
||||
this.commandErrors.forEach(callback => callback(name, error));
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
batchExecuteCommand(commands: { name: string; args: IPublicTypeCommandHandlerArgs }[], pluginContext: IPublicModelPluginContext): void {
|
||||
if (!commands || !commands.length) {
|
||||
return;
|
||||
}
|
||||
pluginContext.common.utils.executeTransaction(() => {
|
||||
commands.forEach(command => this.executeCommand(command.name, command.args));
|
||||
}, IPublicEnumTransitionType.REPAINT);
|
||||
}
|
||||
|
||||
listCommands(): IPublicTypeListCommand[] {
|
||||
return Array.from(this.commands.values()).map(d => {
|
||||
const result: IPublicTypeListCommand = {
|
||||
name: d.name,
|
||||
};
|
||||
|
||||
if (d.description) {
|
||||
result.description = d.description;
|
||||
}
|
||||
|
||||
if (d.parameters) {
|
||||
result.parameters = d.parameters;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
onCommandError(callback: (name: string, error: Error) => void): void {
|
||||
this.commandErrors.push(callback);
|
||||
}
|
||||
}
|
||||
@ -44,7 +44,7 @@ const VALID_ENGINE_OPTIONS = {
|
||||
},
|
||||
renderEnv: {
|
||||
type: 'string',
|
||||
enum: ['react', 'any string value'],
|
||||
enum: ['react', 'rax', 'any string value'],
|
||||
default: 'react',
|
||||
description: '渲染器类型',
|
||||
},
|
||||
@ -159,16 +159,6 @@ const VALID_ENGINE_OPTIONS = {
|
||||
type: 'function',
|
||||
description: '应用级设计模式下,窗口为空时展示的占位组件',
|
||||
},
|
||||
enableContextMenu: {
|
||||
type: 'boolean',
|
||||
description: '是否开启右键菜单',
|
||||
default: false,
|
||||
},
|
||||
hideComponentAction: {
|
||||
type: 'boolean',
|
||||
description: '是否隐藏设计器辅助层',
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
|
||||
const getStrictModeValue = (engineOptions: IPublicTypeEngineOptions, defaultValue: boolean): boolean => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { IPublicApiSetters, IPublicModelSettingField, 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 & {
|
||||
@ -28,7 +28,7 @@ export function registerSetter(
|
||||
if (!setter.initialValue) {
|
||||
const initial = getInitialFromSetter(setter.component);
|
||||
if (initial) {
|
||||
setter.initialValue = (field: IPublicModelSettingField) => {
|
||||
setter.initialValue = (field: any) => {
|
||||
return initial.call(field, field.getValue());
|
||||
};
|
||||
}
|
||||
@ -81,7 +81,7 @@ export class Setters implements ISetters {
|
||||
if (!setter.initialValue) {
|
||||
const initial = getInitialFromSetter(setter.component);
|
||||
if (initial) {
|
||||
setter.initialValue = (field: IPublicModelSettingField) => {
|
||||
setter.initialValue = (field: any) => {
|
||||
return initial.call(field, field.getValue());
|
||||
};
|
||||
}
|
||||
|
||||
@ -6,4 +6,3 @@ export * from './hotkey';
|
||||
export * from './widgets';
|
||||
export * from './config';
|
||||
export * from './event-bus';
|
||||
export * from './command';
|
||||
|
||||
@ -3,7 +3,6 @@ import { IntlMessageFormat } from 'intl-messageformat';
|
||||
import { globalLocale } from './global-locale';
|
||||
import { isI18nData } from '@alilc/lowcode-utils';
|
||||
import { observer } from '../utils';
|
||||
import { IPublicTypeI18nData } from '@alilc/lowcode-types';
|
||||
|
||||
function generateTryLocales(locale: string) {
|
||||
const tries = [locale, locale.replace('-', '_')];
|
||||
@ -27,9 +26,18 @@ function injectVars(msg: string, params: any, locale: string): string {
|
||||
}
|
||||
const formater = new IntlMessageFormat(msg, locale);
|
||||
return formater.format(params as any) as string;
|
||||
/*
|
||||
|
||||
return template.replace(/({\w+})/g, (_, $1) => {
|
||||
const key = (/\d+/.exec($1) || [])[0] as any;
|
||||
if (key && params[key] != null) {
|
||||
return params[key];
|
||||
}
|
||||
return $1;
|
||||
}); */
|
||||
}
|
||||
|
||||
export function intl(data: IPublicTypeI18nData | string, params?: object): ReactNode {
|
||||
export function intl(data: any, params?: object): ReactNode {
|
||||
if (!isI18nData(data)) {
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
import { IPublicTypeHelpTipConfig, IPublicTypeTipConfig } from '@alilc/lowcode-types';
|
||||
import { Tip } from './tip';
|
||||
import { Icon } from '@alifd/next';
|
||||
import { IconProps } from '@alifd/next/types/icon';
|
||||
|
||||
export function HelpTip({
|
||||
help,
|
||||
direction = 'top',
|
||||
size = 'small',
|
||||
}: {
|
||||
help: IPublicTypeHelpTipConfig;
|
||||
direction?: IPublicTypeTipConfig['direction'];
|
||||
size?: IconProps['size'];
|
||||
}) {
|
||||
if (typeof help === 'string') {
|
||||
return (
|
||||
<div>
|
||||
<Icon type="help" size={size} className="lc-help-tip" />
|
||||
<Tip direction={direction}>{help}</Tip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof help === 'object' && help.url) {
|
||||
return (
|
||||
<div>
|
||||
<a href={help.url} target="_blank" rel="noopener noreferrer">
|
||||
<Icon type="help" size={size} className="lc-help-tip" />
|
||||
</a>
|
||||
<Tip direction={direction}>{help.content}</Tip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Icon type="help" size="small" className="lc-help-tip" />
|
||||
<Tip direction={direction}>{help.content}</Tip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -2,4 +2,3 @@ import './style.less';
|
||||
|
||||
export * from './tip';
|
||||
export * from './tip-container';
|
||||
export * from './help-tips';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component, isValidElement, ReactNode } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { createIcon, isI18nData, isTitleConfig } from '@alilc/lowcode-utils';
|
||||
import { IPublicTypeI18nData, IPublicTypeTitleConfig, IPublicTypeTitleProps } from '@alilc/lowcode-types';
|
||||
import { IPublicTypeTitleContent, IPublicTypeI18nData, IPublicTypeTitleConfig } from '@alilc/lowcode-types';
|
||||
import { intl } from '../../intl';
|
||||
import { Tip } from '../tip';
|
||||
import './title.less';
|
||||
@ -36,7 +36,13 @@ import './title.less';
|
||||
return fragments;
|
||||
}
|
||||
|
||||
export class Title extends Component<IPublicTypeTitleProps> {
|
||||
export class Title extends Component<{
|
||||
title: IPublicTypeTitleContent;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
match?: boolean;
|
||||
keywords?: string;
|
||||
}> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
|
||||
@ -1,326 +0,0 @@
|
||||
import { Command } from '../src/command';
|
||||
|
||||
describe('Command', () => {
|
||||
let commandInstance;
|
||||
let mockHandler;
|
||||
|
||||
beforeEach(() => {
|
||||
commandInstance = new Command();
|
||||
mockHandler = jest.fn();
|
||||
});
|
||||
|
||||
describe('registerCommand', () => {
|
||||
it('should register a command successfully', () => {
|
||||
const command = {
|
||||
name: 'testCommand',
|
||||
handler: mockHandler,
|
||||
};
|
||||
commandInstance.registerCommand(command, { commandScope: 'testScope' });
|
||||
|
||||
const registeredCommand = commandInstance.listCommands().find(c => c.name === 'testScope:testCommand');
|
||||
expect(registeredCommand).toBeDefined();
|
||||
expect(registeredCommand.name).toBe('testScope:testCommand');
|
||||
});
|
||||
|
||||
it('should throw an error if commandScope is not provided', () => {
|
||||
const command = {
|
||||
name: 'testCommand',
|
||||
handler: mockHandler,
|
||||
};
|
||||
|
||||
expect(() => {
|
||||
commandInstance.registerCommand(command);
|
||||
}).toThrow('plugin meta.commandScope is required.');
|
||||
});
|
||||
|
||||
it('should throw an error if command is already registered', () => {
|
||||
const command = {
|
||||
name: 'testCommand',
|
||||
handler: mockHandler,
|
||||
};
|
||||
commandInstance.registerCommand(command, { commandScope: 'testScope' });
|
||||
|
||||
expect(() => {
|
||||
commandInstance.registerCommand(command, { commandScope: 'testScope' });
|
||||
}).toThrow(`Command 'testCommand' is already registered.`);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
});
|
||||
|
||||
describe('unregisterCommand', () => {
|
||||
let commandInstance;
|
||||
let mockHandler;
|
||||
|
||||
beforeEach(() => {
|
||||
commandInstance = new Command();
|
||||
mockHandler = jest.fn();
|
||||
// 先注册一个命令以便之后注销
|
||||
const command = {
|
||||
name: 'testCommand',
|
||||
handler: mockHandler,
|
||||
};
|
||||
commandInstance.registerCommand(command, { commandScope: 'testScope' });
|
||||
});
|
||||
|
||||
it('should unregister a command successfully', () => {
|
||||
const commandName = 'testScope:testCommand';
|
||||
expect(commandInstance.listCommands().find(c => c.name === commandName)).toBeDefined();
|
||||
|
||||
commandInstance.unregisterCommand(commandName);
|
||||
|
||||
expect(commandInstance.listCommands().find(c => c.name === commandName)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should throw an error if the command is not registered', () => {
|
||||
const nonExistingCommandName = 'testScope:nonExistingCommand';
|
||||
expect(() => {
|
||||
commandInstance.unregisterCommand(nonExistingCommandName);
|
||||
}).toThrow(`Command '${nonExistingCommandName}' is not registered.`);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeCommand', () => {
|
||||
let commandInstance;
|
||||
let mockHandler;
|
||||
|
||||
beforeEach(() => {
|
||||
commandInstance = new Command();
|
||||
mockHandler = jest.fn();
|
||||
// 注册一个带参数校验的命令
|
||||
const command = {
|
||||
name: 'testCommand',
|
||||
handler: mockHandler,
|
||||
parameters: [
|
||||
{ name: 'param1', propType: 'string' },
|
||||
{ name: 'param2', propType: 'number' }
|
||||
],
|
||||
};
|
||||
commandInstance.registerCommand(command, { commandScope: 'testScope' });
|
||||
});
|
||||
|
||||
it('should execute a command successfully', () => {
|
||||
const commandName = 'testScope:testCommand';
|
||||
const args = { param1: 'test', param2: 42 };
|
||||
|
||||
commandInstance.executeCommand(commandName, args);
|
||||
|
||||
expect(mockHandler).toHaveBeenCalledWith(args);
|
||||
});
|
||||
|
||||
it('should throw an error if the command is not registered', () => {
|
||||
const nonExistingCommandName = 'testScope:nonExistingCommand';
|
||||
expect(() => {
|
||||
commandInstance.executeCommand(nonExistingCommandName, {});
|
||||
}).toThrow(`Command '${nonExistingCommandName}' is not registered.`);
|
||||
});
|
||||
|
||||
it('should throw an error if arguments are invalid', () => {
|
||||
const commandName = 'testScope:testCommand';
|
||||
const invalidArgs = { param1: 'test', param2: 'not-a-number' }; // param2 should be a number
|
||||
|
||||
expect(() => {
|
||||
commandInstance.executeCommand(commandName, invalidArgs);
|
||||
}).toThrow(`Command '${commandName}' arguments param2 is invalid.`);
|
||||
});
|
||||
|
||||
it('should handle errors thrown by the command handler', () => {
|
||||
const commandName = 'testScope:testCommand';
|
||||
const args = { param1: 'test', param2: 42 };
|
||||
const errorMessage = 'Command handler error';
|
||||
mockHandler.mockImplementation(() => {
|
||||
throw new Error(errorMessage);
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
commandInstance.executeCommand(commandName, args);
|
||||
}).toThrow(errorMessage);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
});
|
||||
|
||||
describe('batchExecuteCommand', () => {
|
||||
let commandInstance;
|
||||
let mockHandler;
|
||||
let mockExecuteTransaction;
|
||||
let mockPluginContext;
|
||||
|
||||
beforeEach(() => {
|
||||
commandInstance = new Command();
|
||||
mockHandler = jest.fn();
|
||||
mockExecuteTransaction = jest.fn(callback => callback());
|
||||
mockPluginContext = {
|
||||
common: {
|
||||
utils: {
|
||||
executeTransaction: mockExecuteTransaction
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 注册几个命令
|
||||
const command1 = {
|
||||
name: 'testCommand1',
|
||||
handler: mockHandler,
|
||||
};
|
||||
const command2 = {
|
||||
name: 'testCommand2',
|
||||
handler: mockHandler,
|
||||
};
|
||||
commandInstance.registerCommand(command1, { commandScope: 'testScope' });
|
||||
commandInstance.registerCommand(command2, { commandScope: 'testScope' });
|
||||
});
|
||||
|
||||
it('should execute a batch of commands', () => {
|
||||
const commands = [
|
||||
{ name: 'testScope:testCommand1', args: { param: 'value1' } },
|
||||
{ name: 'testScope:testCommand2', args: { param: 'value2' } },
|
||||
];
|
||||
|
||||
commandInstance.batchExecuteCommand(commands, mockPluginContext);
|
||||
|
||||
expect(mockExecuteTransaction).toHaveBeenCalledTimes(1);
|
||||
expect(mockHandler).toHaveBeenCalledWith({ param: 'value1' });
|
||||
expect(mockHandler).toHaveBeenCalledWith({ param: 'value2' });
|
||||
});
|
||||
|
||||
it('should not execute anything if commands array is empty', () => {
|
||||
commandInstance.batchExecuteCommand([], mockPluginContext);
|
||||
|
||||
expect(mockExecuteTransaction).not.toHaveBeenCalled();
|
||||
expect(mockHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle errors thrown during command execution', () => {
|
||||
const errorMessage = 'Command handler error';
|
||||
mockHandler.mockImplementation(() => {
|
||||
throw new Error(errorMessage);
|
||||
});
|
||||
|
||||
const commands = [
|
||||
{ name: 'testScope:testCommand1', args: { param: 'value1' } },
|
||||
{ name: 'testScope:testCommand2', args: { param: 'value2' } },
|
||||
];
|
||||
|
||||
expect(() => {
|
||||
commandInstance.batchExecuteCommand(commands, mockPluginContext);
|
||||
}).toThrow(errorMessage);
|
||||
|
||||
expect(mockExecuteTransaction).toHaveBeenCalledTimes(1); // Still called once
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
});
|
||||
|
||||
describe('listCommands', () => {
|
||||
let commandInstance;
|
||||
let mockHandler;
|
||||
|
||||
beforeEach(() => {
|
||||
commandInstance = new Command();
|
||||
mockHandler = jest.fn();
|
||||
});
|
||||
|
||||
it('should list all registered commands', () => {
|
||||
// 注册几个命令
|
||||
const command1 = {
|
||||
name: 'testCommand1',
|
||||
handler: mockHandler,
|
||||
description: 'Test Command 1',
|
||||
parameters: [{ name: 'param1', propType: 'string' }]
|
||||
};
|
||||
const command2 = {
|
||||
name: 'testCommand2',
|
||||
handler: mockHandler,
|
||||
description: 'Test Command 2',
|
||||
parameters: [{ name: 'param2', propType: 'number' }]
|
||||
};
|
||||
commandInstance.registerCommand(command1, { commandScope: 'testScope' });
|
||||
commandInstance.registerCommand(command2, { commandScope: 'testScope' });
|
||||
|
||||
const listedCommands = commandInstance.listCommands();
|
||||
|
||||
expect(listedCommands.length).toBe(2);
|
||||
expect(listedCommands).toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: 'testScope:testCommand1',
|
||||
description: 'Test Command 1',
|
||||
parameters: [{ name: 'param1', propType: 'string' }]
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: 'testScope:testCommand2',
|
||||
description: 'Test Command 2',
|
||||
parameters: [{ name: 'param2', propType: 'number' }]
|
||||
})
|
||||
]));
|
||||
});
|
||||
|
||||
it('should return an empty array if no commands are registered', () => {
|
||||
const listedCommands = commandInstance.listCommands();
|
||||
expect(listedCommands).toEqual([]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onCommandError', () => {
|
||||
let commandInstance;
|
||||
let mockHandler;
|
||||
let mockErrorHandler1;
|
||||
let mockErrorHandler2;
|
||||
|
||||
beforeEach(() => {
|
||||
commandInstance = new Command();
|
||||
mockHandler = jest.fn();
|
||||
mockErrorHandler1 = jest.fn();
|
||||
mockErrorHandler2 = jest.fn();
|
||||
|
||||
// 注册一个命令,该命令会抛出错误
|
||||
const command = {
|
||||
name: 'testCommand',
|
||||
handler: () => {
|
||||
throw new Error('Command execution failed');
|
||||
},
|
||||
};
|
||||
commandInstance.registerCommand(command, { commandScope: 'testScope' });
|
||||
});
|
||||
|
||||
it('should call all registered error handlers when a command throws an error', () => {
|
||||
const commandName = 'testScope:testCommand';
|
||||
commandInstance.onCommandError(mockErrorHandler1);
|
||||
commandInstance.onCommandError(mockErrorHandler2);
|
||||
|
||||
expect(() => {
|
||||
commandInstance.executeCommand(commandName, {});
|
||||
}).not.toThrow();
|
||||
|
||||
// 确保所有错误处理函数都被调用,并且传递了正确的参数
|
||||
expect(mockErrorHandler1).toHaveBeenCalledWith(commandName, expect.any(Error));
|
||||
expect(mockErrorHandler2).toHaveBeenCalledWith(commandName, expect.any(Error));
|
||||
});
|
||||
|
||||
it('should throw the error if no error handlers are registered', () => {
|
||||
const commandName = 'testScope:testCommand';
|
||||
|
||||
expect(() => {
|
||||
commandInstance.executeCommand(commandName, {});
|
||||
}).toThrow('Command execution failed');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
});
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-editor-skeleton",
|
||||
"version": "1.3.2",
|
||||
"version": "1.2.4-beta.3",
|
||||
"description": "alibaba lowcode editor skeleton",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
@ -19,10 +19,10 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@alifd/next": "^1.20.12",
|
||||
"@alilc/lowcode-designer": "1.3.2",
|
||||
"@alilc/lowcode-editor-core": "1.3.2",
|
||||
"@alilc/lowcode-types": "1.3.2",
|
||||
"@alilc/lowcode-utils": "1.3.2",
|
||||
"@alilc/lowcode-designer": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-editor-core": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-types": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-utils": "1.2.4-beta.3",
|
||||
"classnames": "^2.2.6",
|
||||
"react": "^16.8.1",
|
||||
"react-dom": "^16.8.1"
|
||||
@ -42,7 +42,7 @@
|
||||
"type": "http",
|
||||
"url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/editor-skeleton"
|
||||
},
|
||||
"gitHead": "2669f179e6f899d395ce1942d0fe04f9c5ed48a6",
|
||||
"gitHead": "5034994a2421e18d3ee6e14f6ce772ac133865d3",
|
||||
"bugs": "https://github.com/alibaba/lowcode-engine/issues",
|
||||
"homepage": "https://github.com/alibaba/lowcode-engine/#readme"
|
||||
}
|
||||
|
||||
@ -225,7 +225,7 @@ class SettingFieldView extends Component<SettingFieldViewProps, SettingFieldView
|
||||
|
||||
const value = this.value;
|
||||
|
||||
let onChangeAPI = extraProps?.onChange;
|
||||
let _onChange = extraProps?.onChange;
|
||||
let stageName = this.stageName;
|
||||
|
||||
return createField(
|
||||
@ -261,7 +261,7 @@ class SettingFieldView extends Component<SettingFieldViewProps, SettingFieldView
|
||||
value,
|
||||
});
|
||||
field.setValue(value, true);
|
||||
if (onChangeAPI) onChangeAPI(value, field.internalToShellField());
|
||||
if (_onChange) _onChange(value, field);
|
||||
},
|
||||
onInitial: () => {
|
||||
if (initialValue == null) {
|
||||
|
||||
@ -65,10 +65,8 @@ export class SettingsPrimaryPane extends Component<ISettingsPrimaryPaneProps, {
|
||||
{createIcon(settings.componentMeta?.icon, {
|
||||
className: 'lc-settings-navigator-icon',
|
||||
})}
|
||||
<div style={{ marginLeft: '5px' }}>
|
||||
<Title title={settings.componentMeta!.title} />
|
||||
<span> x {settings.nodes.length}</span>
|
||||
</div>
|
||||
<Title title={settings.componentMeta!.title} />
|
||||
<span> x {settings.nodes.length}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Component, ReactElement } from 'react';
|
||||
import { Icon } from '@alifd/next';
|
||||
import classNames from 'classnames';
|
||||
import { Title, observer, HelpTip } 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';
|
||||
@ -25,6 +26,25 @@ export function DockView({ title, icon, description, size, className, onClick }:
|
||||
);
|
||||
}
|
||||
|
||||
function HelpTip({ tip }: any) {
|
||||
if (tip && tip.url) {
|
||||
return (
|
||||
<div>
|
||||
<a href={tip.url} target="_blank" rel="noopener noreferrer">
|
||||
<Icon type="help" size="small" className="lc-help-tip" />
|
||||
</a>
|
||||
<Tip>{tip.content}</Tip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Icon type="help" size="small" className="lc-help-tip" />
|
||||
<Tip>{tip.content}</Tip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@observer
|
||||
export class PanelDockView extends Component<DockProps & { dock: PanelDock }> {
|
||||
private lastActived = false;
|
||||
@ -308,7 +328,7 @@ class PanelTitle extends Component<{ panel: Panel; className?: string }> {
|
||||
data-name={panel.name}
|
||||
>
|
||||
<Title title={panel.title || panel.name} />
|
||||
{panel.help ? <HelpTip help={panel.help} /> : null}
|
||||
{panel.help ? <HelpTip tip={panel.help} /> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ import {
|
||||
IPublicTypeTransformedComponentMetadata,
|
||||
IPublicTypeOneOfType,
|
||||
ConfigureSupportEvent,
|
||||
IPublicModelSettingField,
|
||||
} from '@alilc/lowcode-types';
|
||||
|
||||
function propConfigToFieldConfig(propConfig: IPublicTypePropConfig): IPublicTypeFieldConfig {
|
||||
@ -103,7 +102,7 @@ function propTypeToSetter(propType: IPublicTypePropType): IPublicTypeSetterType
|
||||
},
|
||||
},
|
||||
isRequired,
|
||||
initialValue: (field: IPublicModelSettingField) => {
|
||||
initialValue: (field: any) => {
|
||||
const data: any = {};
|
||||
items.forEach((item: any) => {
|
||||
let initial = item.defaultValue;
|
||||
|
||||
@ -126,7 +126,7 @@ https://cdn.jsdelivr.net/npm/@alilc/lowcode-react-simulator-renderer@1.0.18/dist
|
||||
```
|
||||
|
||||
#### 方式 5:使用自有 cdn
|
||||
将源码中 packages/engine/dist 和 packages/react-simulator-renderer/dist 下的文件传至你的 cdn 提供商
|
||||
将源码中 packages/engine/dist 和 packages/(react|rax)-simulator-renderer/dist 下的文件传至你的 cdn 提供商
|
||||
|
||||
## 🔗 相关链接
|
||||
|
||||
|
||||
@ -126,7 +126,7 @@ https://cdn.jsdelivr.net/npm/@alilc/lowcode-react-simulator-renderer@1.0.18/dist
|
||||
```
|
||||
|
||||
#### Method 5: Use your own cdn
|
||||
Pass the files under packages/engine/dist and packages/react-simulator-renderer/dist in the source code to your cdn provider
|
||||
Pass the files under packages/engine/dist and packages/(react|rax)-simulator-renderer/dist in the source code to your cdn provider
|
||||
|
||||
## 🔗 Related Links
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-engine",
|
||||
"version": "1.3.2",
|
||||
"version": "1.2.4-beta.3",
|
||||
"description": "An enterprise-class low-code technology stack with scale-out design / 一套面向扩展设计的企业级低代码技术体系",
|
||||
"main": "lib/engine-core.js",
|
||||
"module": "es/engine-core.js",
|
||||
@ -19,16 +19,15 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alifd/next": "^1.19.12",
|
||||
"@alilc/lowcode-designer": "1.3.2",
|
||||
"@alilc/lowcode-editor-core": "1.3.2",
|
||||
"@alilc/lowcode-editor-skeleton": "1.3.2",
|
||||
"@alilc/lowcode-designer": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-editor-core": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-editor-skeleton": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-engine-ext": "^1.0.0",
|
||||
"@alilc/lowcode-plugin-command": "1.3.2",
|
||||
"@alilc/lowcode-plugin-designer": "1.3.2",
|
||||
"@alilc/lowcode-plugin-outline-pane": "1.3.2",
|
||||
"@alilc/lowcode-shell": "1.3.2",
|
||||
"@alilc/lowcode-utils": "1.3.2",
|
||||
"@alilc/lowcode-workspace": "1.3.2",
|
||||
"@alilc/lowcode-plugin-designer": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-plugin-outline-pane": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-shell": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-utils": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-workspace": "1.2.4-beta.3",
|
||||
"react": "^16.8.1",
|
||||
"react-dom": "^16.8.1"
|
||||
},
|
||||
@ -54,7 +53,7 @@
|
||||
"type": "http",
|
||||
"url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/engine"
|
||||
},
|
||||
"gitHead": "2669f179e6f899d395ce1942d0fe04f9c5ed48a6",
|
||||
"gitHead": "5034994a2421e18d3ee6e14f6ce772ac133865d3",
|
||||
"bugs": "https://github.com/alibaba/lowcode-engine/issues",
|
||||
"homepage": "https://github.com/alibaba/lowcode-engine/#readme"
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ import {
|
||||
Setters as InnerSetters,
|
||||
Hotkey as InnerHotkey,
|
||||
IEditor,
|
||||
Command as InnerCommand,
|
||||
} from '@alilc/lowcode-editor-core';
|
||||
import {
|
||||
IPublicTypeEngineOptions,
|
||||
@ -20,7 +19,6 @@ import {
|
||||
IPublicApiPlugins,
|
||||
IPublicApiWorkspace,
|
||||
IPublicEnumPluginRegisterLevel,
|
||||
IPublicModelPluginContext,
|
||||
} from '@alilc/lowcode-types';
|
||||
import {
|
||||
Designer,
|
||||
@ -53,8 +51,6 @@ import {
|
||||
Canvas,
|
||||
Workspace,
|
||||
Config,
|
||||
CommonUI,
|
||||
Command,
|
||||
} from '@alilc/lowcode-shell';
|
||||
import { isPlainObject } from '@alilc/lowcode-utils';
|
||||
import './modules/live-editing';
|
||||
@ -65,8 +61,6 @@ import { setterRegistry } from './inner-plugins/setter-registry';
|
||||
import { defaultPanelRegistry } from './inner-plugins/default-panel-registry';
|
||||
import { shellModelFactory } from './modules/shell-model-factory';
|
||||
import { builtinHotkey } from './inner-plugins/builtin-hotkey';
|
||||
import { defaultContextMenu } from './inner-plugins/default-context-menu';
|
||||
import { CommandPlugin } from '@alilc/lowcode-plugin-command';
|
||||
import { OutlinePlugin } from '@alilc/lowcode-plugin-outline-pane';
|
||||
|
||||
export * from './modules/skeleton-types';
|
||||
@ -83,8 +77,6 @@ async function registryInnerPlugin(designer: IDesigner, editor: IEditor, plugins
|
||||
await plugins.register(defaultPanelRegistryPlugin);
|
||||
await plugins.register(builtinHotkey);
|
||||
await plugins.register(registerDefaults, {}, { autoInit: true });
|
||||
await plugins.register(defaultContextMenu);
|
||||
await plugins.register(CommandPlugin, {});
|
||||
|
||||
return () => {
|
||||
plugins.delete(OutlinePlugin.pluginName);
|
||||
@ -93,8 +85,6 @@ async function registryInnerPlugin(designer: IDesigner, editor: IEditor, plugins
|
||||
plugins.delete(defaultPanelRegistryPlugin.pluginName);
|
||||
plugins.delete(builtinHotkey.pluginName);
|
||||
plugins.delete(registerDefaults.pluginName);
|
||||
plugins.delete(defaultContextMenu.pluginName);
|
||||
plugins.delete(CommandPlugin.pluginName);
|
||||
};
|
||||
}
|
||||
|
||||
@ -105,8 +95,6 @@ globalContext.register(editor, Editor);
|
||||
globalContext.register(editor, 'editor');
|
||||
globalContext.register(innerWorkspace, 'workspace');
|
||||
|
||||
const engineContext: Partial<ILowCodePluginContextPrivate> = {};
|
||||
|
||||
const innerSkeleton = new InnerSkeleton(editor);
|
||||
editor.set('skeleton' as any, innerSkeleton);
|
||||
|
||||
@ -121,11 +109,8 @@ const project = new Project(innerProject);
|
||||
const skeleton = new Skeleton(innerSkeleton, 'any', false);
|
||||
const innerSetters = new InnerSetters();
|
||||
const setters = new Setters(innerSetters);
|
||||
const innerCommand = new InnerCommand();
|
||||
const command = new Command(innerCommand, engineContext as IPublicModelPluginContext);
|
||||
|
||||
const material = new Material(editor);
|
||||
const commonUI = new CommonUI(editor);
|
||||
editor.set('project', project);
|
||||
editor.set('setters' as any, setters);
|
||||
editor.set('material', material);
|
||||
@ -146,7 +131,6 @@ const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = {
|
||||
context.setters = setters;
|
||||
context.material = material;
|
||||
const eventPrefix = meta?.eventPrefix || 'common';
|
||||
const commandScope = meta?.commandScope;
|
||||
context.event = new Event(commonEvent, { prefix: eventPrefix });
|
||||
context.config = config;
|
||||
context.common = common;
|
||||
@ -154,13 +138,8 @@ const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = {
|
||||
context.plugins = plugins;
|
||||
context.logger = new Logger({ level: 'warn', bizName: `plugin:${pluginName}` });
|
||||
context.workspace = workspace;
|
||||
context.commonUI = commonUI;
|
||||
context.command = new Command(innerCommand, context as IPublicModelPluginContext, {
|
||||
commandScope,
|
||||
});
|
||||
context.registerLevel = IPublicEnumPluginRegisterLevel.Default;
|
||||
context.isPluginRegisteredInWorkspace = false;
|
||||
editor.set('pluginContext', context);
|
||||
},
|
||||
};
|
||||
|
||||
@ -169,20 +148,6 @@ plugins = new Plugins(innerPlugins).toProxy();
|
||||
editor.set('innerPlugins' as any, innerPlugins);
|
||||
editor.set('plugins' as any, plugins);
|
||||
|
||||
engineContext.skeleton = skeleton;
|
||||
engineContext.plugins = plugins;
|
||||
engineContext.project = project;
|
||||
engineContext.setters = setters;
|
||||
engineContext.material = material;
|
||||
engineContext.event = event;
|
||||
engineContext.logger = logger;
|
||||
engineContext.hotkey = hotkey;
|
||||
engineContext.common = common;
|
||||
engineContext.workspace = workspace;
|
||||
engineContext.canvas = canvas;
|
||||
engineContext.commonUI = commonUI;
|
||||
engineContext.command = command;
|
||||
|
||||
export {
|
||||
skeleton,
|
||||
plugins,
|
||||
@ -196,8 +161,6 @@ export {
|
||||
common,
|
||||
workspace,
|
||||
canvas,
|
||||
commonUI,
|
||||
command,
|
||||
};
|
||||
// declare this is open-source version
|
||||
export const isOpenSource = true;
|
||||
|
||||
@ -1,223 +0,0 @@
|
||||
import {
|
||||
IPublicEnumContextMenuType,
|
||||
IPublicEnumDragObjectType,
|
||||
IPublicEnumTransformStage,
|
||||
IPublicModelNode,
|
||||
IPublicModelPluginContext,
|
||||
IPublicTypeDragNodeDataObject,
|
||||
IPublicTypeNodeSchema,
|
||||
} from '@alilc/lowcode-types';
|
||||
import { isProjectSchema } from '@alilc/lowcode-utils';
|
||||
import { Message } from '@alifd/next';
|
||||
import { intl } from '../locale';
|
||||
|
||||
function getNodesSchema(nodes: IPublicModelNode[]) {
|
||||
const componentsTree = nodes.map((node) => node?.exportSchema(IPublicEnumTransformStage.Clone));
|
||||
const data = { type: 'nodeSchema', componentsMap: {}, componentsTree };
|
||||
return data;
|
||||
}
|
||||
|
||||
async function getClipboardText(): Promise<IPublicTypeNodeSchema[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 使用 Clipboard API 读取剪贴板内容
|
||||
navigator.clipboard.readText().then(
|
||||
(text) => {
|
||||
try {
|
||||
const data = JSON.parse(text);
|
||||
if (isProjectSchema(data)) {
|
||||
resolve(data.componentsTree);
|
||||
} else {
|
||||
Message.error(intl('NotValidNodeData'));
|
||||
reject(
|
||||
new Error(intl('NotValidNodeData')),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
Message.error(intl('NotValidNodeData'));
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
reject(err);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export const defaultContextMenu = (ctx: IPublicModelPluginContext) => {
|
||||
const { material, canvas, common } = ctx;
|
||||
const { clipboard } = canvas;
|
||||
const { intl: utilsIntl } = common.utils;
|
||||
|
||||
return {
|
||||
init() {
|
||||
material.addContextMenuOption({
|
||||
name: 'selectComponent',
|
||||
title: intl('SelectComponents'),
|
||||
condition: (nodes = []) => {
|
||||
return nodes.length === 1;
|
||||
},
|
||||
items: [
|
||||
{
|
||||
name: 'nodeTree',
|
||||
type: IPublicEnumContextMenuType.NODE_TREE,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
material.addContextMenuOption({
|
||||
name: 'copyAndPaste',
|
||||
title: intl('CopyAndPaste'),
|
||||
disabled: (nodes = []) => {
|
||||
return nodes?.filter((node) => !node?.canPerformAction('copy')).length > 0;
|
||||
},
|
||||
condition: (nodes) => {
|
||||
return nodes?.length === 1;
|
||||
},
|
||||
action(nodes) {
|
||||
const node = nodes?.[0];
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
const { document: doc, parent, index } = node;
|
||||
const data = getNodesSchema(nodes);
|
||||
clipboard.setData(data);
|
||||
|
||||
if (parent) {
|
||||
const newNode = doc?.insertNode(parent, node, (index ?? 0) + 1, true);
|
||||
newNode?.select();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
material.addContextMenuOption({
|
||||
name: 'copy',
|
||||
title: intl('Copy'),
|
||||
disabled: (nodes = []) => {
|
||||
return nodes?.filter((node) => !node?.canPerformAction('copy')).length > 0;
|
||||
},
|
||||
condition(nodes = []) {
|
||||
return nodes?.length > 0;
|
||||
},
|
||||
action(nodes) {
|
||||
if (!nodes || nodes.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = getNodesSchema(nodes);
|
||||
clipboard.setData(data);
|
||||
},
|
||||
});
|
||||
|
||||
material.addContextMenuOption({
|
||||
name: 'pasteToBottom',
|
||||
title: intl('PasteToTheBottom'),
|
||||
condition: (nodes) => {
|
||||
return nodes?.length === 1;
|
||||
},
|
||||
async action(nodes) {
|
||||
if (!nodes || nodes.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const node = nodes[0];
|
||||
const { document: doc, parent, index } = node;
|
||||
|
||||
try {
|
||||
const nodeSchema = await getClipboardText();
|
||||
if (nodeSchema.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (parent) {
|
||||
let canAddNodes = nodeSchema.filter((nodeSchema: IPublicTypeNodeSchema) => {
|
||||
const dragNodeObject: IPublicTypeDragNodeDataObject = {
|
||||
type: IPublicEnumDragObjectType.NodeData,
|
||||
data: nodeSchema,
|
||||
};
|
||||
return doc?.checkNesting(parent, dragNodeObject);
|
||||
});
|
||||
if (canAddNodes.length === 0) {
|
||||
Message.error(`${nodeSchema.map(d => utilsIntl(d.title || d.componentName)).join(',')}等组件无法放置到${utilsIntl(parent.title || parent.componentName as any)}内`);
|
||||
return;
|
||||
}
|
||||
const nodes: IPublicModelNode[] = [];
|
||||
canAddNodes.forEach((schema, schemaIndex) => {
|
||||
const node = doc?.insertNode(parent, schema, (index ?? 0) + 1 + schemaIndex, true);
|
||||
node && nodes.push(node);
|
||||
});
|
||||
doc?.selection.selectAll(nodes.map((node) => node?.id));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
material.addContextMenuOption({
|
||||
name: 'pasteToInner',
|
||||
title: intl('PasteToTheInside'),
|
||||
condition: (nodes) => {
|
||||
return nodes?.length === 1;
|
||||
},
|
||||
disabled: (nodes = []) => {
|
||||
// 获取粘贴数据
|
||||
const node = nodes?.[0];
|
||||
return !node.isContainerNode;
|
||||
},
|
||||
async action(nodes) {
|
||||
const node = nodes?.[0];
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
const { document: doc } = node;
|
||||
|
||||
try {
|
||||
const nodeSchema = await getClipboardText();
|
||||
const index = node.children?.size || 0;
|
||||
if (nodeSchema.length === 0) {
|
||||
return;
|
||||
}
|
||||
let canAddNodes = nodeSchema.filter((nodeSchema: IPublicTypeNodeSchema) => {
|
||||
const dragNodeObject: IPublicTypeDragNodeDataObject = {
|
||||
type: IPublicEnumDragObjectType.NodeData,
|
||||
data: nodeSchema,
|
||||
};
|
||||
return doc?.checkNesting(node, dragNodeObject);
|
||||
});
|
||||
if (canAddNodes.length === 0) {
|
||||
Message.error(`${nodeSchema.map(d => utilsIntl(d.title || d.componentName)).join(',')}等组件无法放置到${utilsIntl(node.title || node.componentName as any)}内`);
|
||||
return;
|
||||
}
|
||||
|
||||
const nodes: IPublicModelNode[] = [];
|
||||
nodeSchema.forEach((schema, schemaIndex) => {
|
||||
const newNode = doc?.insertNode(node, schema, (index ?? 0) + 1 + schemaIndex, true);
|
||||
newNode && nodes.push(newNode);
|
||||
});
|
||||
doc?.selection.selectAll(nodes.map((node) => node?.id));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
material.addContextMenuOption({
|
||||
name: 'delete',
|
||||
title: intl('Delete'),
|
||||
disabled(nodes = []) {
|
||||
return nodes?.filter((node) => !node?.canPerformAction('remove')).length > 0;
|
||||
},
|
||||
condition(nodes = []) {
|
||||
return nodes.length > 0;
|
||||
},
|
||||
action(nodes) {
|
||||
nodes?.forEach((node) => {
|
||||
node.remove();
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
defaultContextMenu.pluginName = '___default_context_menu___';
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"NotValidNodeData": "Not valid node data",
|
||||
"SelectComponents": "Select components",
|
||||
"CopyAndPaste": "Copy and Paste",
|
||||
"Copy": "Copy",
|
||||
"PasteToTheBottom": "Paste to the bottom",
|
||||
"PasteToTheInside": "Paste to the inside",
|
||||
"Delete": "Delete"
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
import { createIntl } from '@alilc/lowcode-editor-core';
|
||||
import enUS from './en-US.json';
|
||||
import zhCN from './zh-CN.json';
|
||||
|
||||
const { intl, getLocale } = createIntl?.({
|
||||
'en-US': enUS,
|
||||
'zh-CN': zhCN,
|
||||
}) || {
|
||||
intl: (id) => {
|
||||
return zhCN[id];
|
||||
},
|
||||
};
|
||||
|
||||
export { intl, enUS, zhCN, getLocale };
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"NotValidNodeData": "不是有效的节点数据",
|
||||
"SelectComponents": "选择组件",
|
||||
"CopyAndPaste": "复制",
|
||||
"Copy": "拷贝",
|
||||
"PasteToTheBottom": "粘贴至下方",
|
||||
"PasteToTheInside": "粘贴至内部",
|
||||
"Delete": "删除"
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
{
|
||||
"entry": {
|
||||
"AliLowCodeEngine": "../engine/src/index.ts",
|
||||
"ReactSimulatorRenderer": "../react-simulator-renderer/src/index.ts"
|
||||
"ReactSimulatorRenderer": "../react-simulator-renderer/src/index.ts",
|
||||
"RaxSimulatorRenderer": "../rax-simulator-renderer/src/index.ts"
|
||||
},
|
||||
"vendor": false,
|
||||
"devServer": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-ignitor",
|
||||
"version": "1.3.2",
|
||||
"version": "1.2.4-beta.3",
|
||||
"description": "点火器,bootstrap lce project",
|
||||
"main": "lib/index.js",
|
||||
"private": true,
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
# `@alilc/plugin-command`
|
||||
|
||||
> TODO: description
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
const pluginCommand = require('@alilc/plugin-command');
|
||||
|
||||
// TODO: DEMONSTRATE API
|
||||
```
|
||||
@ -1,110 +0,0 @@
|
||||
import { checkPropTypes } from '@alilc/lowcode-utils/src/check-prop-types';
|
||||
import { nodeSchemaPropType } from '../src/node-command';
|
||||
|
||||
describe('nodeSchemaPropType', () => {
|
||||
const componentName = 'NodeComponent';
|
||||
const getPropType = (name: string) => nodeSchemaPropType.value.find(d => d.name === name)?.propType;
|
||||
|
||||
it('should validate the id as a string', () => {
|
||||
const validId = 'node1';
|
||||
const invalidId = 123; // Not a string
|
||||
expect(checkPropTypes(validId, 'id', getPropType('id'), componentName)).toBe(true);
|
||||
expect(checkPropTypes(invalidId, 'id', getPropType('id'), componentName)).toBe(false);
|
||||
// is not required
|
||||
expect(checkPropTypes(undefined, 'id', getPropType('id'), componentName)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate the componentName as a string', () => {
|
||||
const validComponentName = 'Button';
|
||||
const invalidComponentName = false; // Not a string
|
||||
expect(checkPropTypes(validComponentName, 'componentName', getPropType('componentName'), componentName)).toBe(true);
|
||||
expect(checkPropTypes(invalidComponentName, 'componentName', getPropType('componentName'), componentName)).toBe(false);
|
||||
// isRequired
|
||||
expect(checkPropTypes(undefined, 'componentName', getPropType('componentName'), componentName)).toBe(false);
|
||||
});
|
||||
|
||||
it('should validate the props as an object', () => {
|
||||
const validProps = { key: 'value' };
|
||||
const invalidProps = 'Not an object'; // Not an object
|
||||
expect(checkPropTypes(validProps, 'props', getPropType('props'), componentName)).toBe(true);
|
||||
expect(checkPropTypes(invalidProps, 'props', getPropType('props'), componentName)).toBe(false);
|
||||
});
|
||||
|
||||
it('should validate the props as a JSExpression', () => {
|
||||
const validProps = { type: 'JSExpression', value: 'props' };
|
||||
expect(checkPropTypes(validProps, 'props', getPropType('props'), componentName)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate the props as a JSFunction', () => {
|
||||
const validProps = { type: 'JSFunction', value: 'props' };
|
||||
expect(checkPropTypes(validProps, 'props', getPropType('props'), componentName)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate the props as a JSSlot', () => {
|
||||
const validProps = { type: 'JSSlot', value: 'props' };
|
||||
expect(checkPropTypes(validProps, 'props', getPropType('props'), componentName)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate the condition as a bool', () => {
|
||||
const validCondition = true;
|
||||
const invalidCondition = 'Not a bool'; // Not a boolean
|
||||
expect(checkPropTypes(validCondition, 'condition', getPropType('condition'), componentName)).toBe(true);
|
||||
expect(checkPropTypes(invalidCondition, 'condition', getPropType('condition'), componentName)).toBe(false);
|
||||
});
|
||||
|
||||
it('should validate the condition as a JSExpression', () => {
|
||||
const validCondition = { type: 'JSExpression', value: '1 + 1 === 2' };
|
||||
const invalidCondition = { type: 'JSExpression', value: 123 }; // Not a string
|
||||
expect(checkPropTypes(validCondition, 'condition', getPropType('condition'), componentName)).toBe(true);
|
||||
expect(checkPropTypes(invalidCondition, 'condition', getPropType('condition'), componentName)).toBe(false);
|
||||
});
|
||||
|
||||
it('should validate the loop as an array', () => {
|
||||
const validLoop = ['item1', 'item2'];
|
||||
const invalidLoop = 'Not an array'; // Not an array
|
||||
expect(checkPropTypes(validLoop, 'loop', getPropType('loop'), componentName)).toBe(true);
|
||||
expect(checkPropTypes(invalidLoop, 'loop', getPropType('loop'), componentName)).toBe(false);
|
||||
});
|
||||
|
||||
it('should validate the loop as a JSExpression', () => {
|
||||
const validLoop = { type: 'JSExpression', value: 'items' };
|
||||
const invalidLoop = { type: 'JSExpression', value: 123 }; // Not a string
|
||||
expect(checkPropTypes(validLoop, 'loop', getPropType('loop'), componentName)).toBe(true);
|
||||
expect(checkPropTypes(invalidLoop, 'loop', getPropType('loop'), componentName)).toBe(false);
|
||||
});
|
||||
|
||||
it('should validate the loopArgs as an array', () => {
|
||||
const validLoopArgs = ['item'];
|
||||
const invalidLoopArgs = 'Not an array'; // Not an array
|
||||
expect(checkPropTypes(validLoopArgs, 'loopArgs', getPropType('loopArgs'), componentName)).toBe(true);
|
||||
expect(checkPropTypes(invalidLoopArgs, 'loopArgs', getPropType('loopArgs'), componentName)).toBe(false);
|
||||
});
|
||||
|
||||
it('should validate the loopArgs as a JSExpression', () => {
|
||||
const validLoopArgs = { type: 'JSExpression', value: 'item' };
|
||||
const invalidLoopArgs = { type: 'JSExpression', value: 123 }; // Not a string
|
||||
const validLoopArgs2 = [{ type: 'JSExpression', value: 'item' }, { type: 'JSExpression', value: 'index' }];
|
||||
expect(checkPropTypes(validLoopArgs, 'loopArgs', getPropType('loopArgs'), componentName)).toBe(true);
|
||||
expect(checkPropTypes(invalidLoopArgs, 'loopArgs', getPropType('loopArgs'), componentName)).toBe(false);
|
||||
expect(checkPropTypes(validLoopArgs2, 'loopArgs', getPropType('loopArgs'), componentName)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate the children as an array', () => {
|
||||
const validChildren = [{
|
||||
id: 'child1',
|
||||
componentName: 'Button',
|
||||
}, {
|
||||
id: 'child2',
|
||||
componentName: 'Button',
|
||||
}];
|
||||
const invalidChildren = 'Not an array'; // Not an array
|
||||
const invalidChildren2 = [{}]; // Not an valid array
|
||||
expect(checkPropTypes(invalidChildren, 'children', getPropType('children'), componentName)).toBe(false);
|
||||
expect(checkPropTypes(validChildren, 'children', getPropType('children'), componentName)).toBe(true);
|
||||
expect(checkPropTypes(invalidChildren2, 'children', getPropType('children'), componentName)).toBe(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@alilc/build-plugin-lce",
|
||||
"build-plugin-fusion",
|
||||
["build-plugin-moment-locales", {
|
||||
"locales": ["zh-cn"]
|
||||
}]
|
||||
]
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
[
|
||||
"@alilc/build-plugin-lce",
|
||||
{
|
||||
"filename": "editor-preset-vision",
|
||||
"library": "LowcodeEditor",
|
||||
"libraryTarget": "umd",
|
||||
"externals": {
|
||||
"react": "var window.React",
|
||||
"react-dom": "var window.ReactDOM",
|
||||
"prop-types": "var window.PropTypes",
|
||||
"rax": "var window.Rax"
|
||||
}
|
||||
}
|
||||
],
|
||||
"@alilc/lowcode-test-mate/plugin/index.ts"
|
||||
]
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const { join } = require('path');
|
||||
const esModules = [].join('|');
|
||||
const pkgNames = fs.readdirSync(join('..')).filter(pkgName => !pkgName.startsWith('.'));
|
||||
|
||||
const jestConfig = {
|
||||
transformIgnorePatterns: [
|
||||
`/node_modules/(?!${esModules})/`,
|
||||
],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: [
|
||||
'src/**/*.ts',
|
||||
'src/**/*.tsx',
|
||||
],
|
||||
};
|
||||
|
||||
// 只对本仓库内的 pkg 做 mapping
|
||||
jestConfig.moduleNameMapper = {};
|
||||
jestConfig.moduleNameMapper[`^@alilc/lowcode\\-(${pkgNames.join('|')})$`] = '<rootDir>/../$1/src';
|
||||
|
||||
module.exports = jestConfig;
|
||||
@ -1,39 +0,0 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-plugin-command",
|
||||
"version": "1.3.2",
|
||||
"description": "> TODO: description",
|
||||
"author": "liujuping <liujup@foxmail.com>",
|
||||
"homepage": "https://github.com/alibaba/lowcode-engine#readme",
|
||||
"license": "ISC",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"directories": {
|
||||
"lib": "lib",
|
||||
"test": "__tests__"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"es"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/alibaba/lowcode-engine.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "build-scripts test --config build.test.json --jest-passWithNoTests",
|
||||
"build": "build-scripts build"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/alibaba/lowcode-engine/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alilc/lowcode-types": "1.3.2",
|
||||
"@alilc/lowcode-utils": "1.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alib/build-scripts": "^0.1.18"
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
import { IPublicModelPluginContext, IPublicTypePlugin } from '@alilc/lowcode-types';
|
||||
|
||||
export const historyCommand: IPublicTypePlugin = (ctx: IPublicModelPluginContext) => {
|
||||
const { command, project } = ctx;
|
||||
return {
|
||||
init() {
|
||||
command.registerCommand({
|
||||
name: 'undo',
|
||||
description: 'Undo the last operation.',
|
||||
handler: () => {
|
||||
const state = project.currentDocument?.history.getState() || 0;
|
||||
const enable = !!(state & 1);
|
||||
if (!enable) {
|
||||
throw new Error('Can not undo.');
|
||||
}
|
||||
project.currentDocument?.history.back();
|
||||
},
|
||||
});
|
||||
|
||||
command.registerCommand({
|
||||
name: 'redo',
|
||||
description: 'Redo the last operation.',
|
||||
handler: () => {
|
||||
const state = project.currentDocument?.history.getState() || 0;
|
||||
const enable = !!(state & 2);
|
||||
if (!enable) {
|
||||
throw new Error('Can not redo.');
|
||||
}
|
||||
project.currentDocument?.history.forward();
|
||||
},
|
||||
});
|
||||
},
|
||||
destroy() {
|
||||
command.unregisterCommand('history:undo');
|
||||
command.unregisterCommand('history:redo');
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
historyCommand.pluginName = '___history_command___';
|
||||
historyCommand.meta = {
|
||||
commandScope: 'history',
|
||||
};
|
||||
@ -1,25 +0,0 @@
|
||||
import { IPublicModelPluginContext, IPublicTypePlugin } from '@alilc/lowcode-types';
|
||||
import { nodeCommand } from './node-command';
|
||||
import { historyCommand } from './history-command';
|
||||
|
||||
export const CommandPlugin: IPublicTypePlugin = (ctx: IPublicModelPluginContext) => {
|
||||
const { plugins } = ctx;
|
||||
|
||||
return {
|
||||
async init() {
|
||||
await plugins.register(nodeCommand, {}, { autoInit: true });
|
||||
await plugins.register(historyCommand, {}, { autoInit: true });
|
||||
},
|
||||
destroy() {
|
||||
plugins.delete(nodeCommand.pluginName);
|
||||
plugins.delete(historyCommand.pluginName);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
CommandPlugin.pluginName = '___default_command___';
|
||||
CommandPlugin.meta = {
|
||||
commandScope: 'common',
|
||||
};
|
||||
|
||||
export default CommandPlugin;
|
||||
@ -1,497 +0,0 @@
|
||||
import { IPublicModelPluginContext, IPublicTypeNodeSchema, IPublicTypePlugin, IPublicTypePropType } from '@alilc/lowcode-types';
|
||||
import { isNodeSchema } from '@alilc/lowcode-utils';
|
||||
|
||||
const sampleNodeSchema: IPublicTypePropType = {
|
||||
type: 'shape',
|
||||
value: [
|
||||
{
|
||||
name: 'id',
|
||||
propType: 'string',
|
||||
},
|
||||
{
|
||||
name: 'componentName',
|
||||
propType: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'props',
|
||||
propType: 'object',
|
||||
},
|
||||
{
|
||||
name: 'condition',
|
||||
propType: 'any',
|
||||
},
|
||||
{
|
||||
name: 'loop',
|
||||
propType: 'any',
|
||||
},
|
||||
{
|
||||
name: 'loopArgs',
|
||||
propType: 'any',
|
||||
},
|
||||
{
|
||||
name: 'children',
|
||||
propType: 'any',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const nodeSchemaPropType: IPublicTypePropType = {
|
||||
type: 'shape',
|
||||
value: [
|
||||
sampleNodeSchema.value[0],
|
||||
sampleNodeSchema.value[1],
|
||||
{
|
||||
name: 'props',
|
||||
propType: {
|
||||
type: 'objectOf',
|
||||
value: {
|
||||
type: 'oneOfType',
|
||||
// 不会强制校验,更多作为提示
|
||||
value: [
|
||||
'any',
|
||||
{
|
||||
type: 'shape',
|
||||
value: [
|
||||
{
|
||||
name: 'type',
|
||||
propType: {
|
||||
type: 'oneOf',
|
||||
value: ['JSExpression'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
propType: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'shape',
|
||||
value: [
|
||||
{
|
||||
name: 'type',
|
||||
propType: {
|
||||
type: 'oneOf',
|
||||
value: ['JSFunction'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
propType: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'shape',
|
||||
value: [
|
||||
{
|
||||
name: 'type',
|
||||
propType: {
|
||||
type: 'oneOf',
|
||||
value: ['JSSlot'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
propType: {
|
||||
type: 'oneOfType',
|
||||
value: [
|
||||
sampleNodeSchema,
|
||||
{
|
||||
type: 'arrayOf',
|
||||
value: sampleNodeSchema,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'condition',
|
||||
propType: {
|
||||
type: 'oneOfType',
|
||||
value: [
|
||||
'bool',
|
||||
{
|
||||
type: 'shape',
|
||||
value: [
|
||||
{
|
||||
name: 'type',
|
||||
propType: {
|
||||
type: 'oneOf',
|
||||
value: ['JSExpression'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
propType: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'loop',
|
||||
propType: {
|
||||
type: 'oneOfType',
|
||||
value: [
|
||||
'array',
|
||||
{
|
||||
type: 'shape',
|
||||
value: [
|
||||
{
|
||||
name: 'type',
|
||||
propType: {
|
||||
type: 'oneOf',
|
||||
value: ['JSExpression'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
propType: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'loopArgs',
|
||||
propType: {
|
||||
type: 'oneOfType',
|
||||
value: [
|
||||
{
|
||||
type: 'arrayOf',
|
||||
value: {
|
||||
type: 'oneOfType',
|
||||
value: [
|
||||
'any',
|
||||
{
|
||||
type: 'shape',
|
||||
value: [
|
||||
{
|
||||
name: 'type',
|
||||
propType: {
|
||||
type: 'oneOf',
|
||||
value: ['JSExpression'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
propType: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'shape',
|
||||
value: [
|
||||
{
|
||||
name: 'type',
|
||||
propType: {
|
||||
type: 'oneOf',
|
||||
value: ['JSExpression'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
propType: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'children',
|
||||
propType: {
|
||||
type: 'arrayOf',
|
||||
value: sampleNodeSchema,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const nodeCommand: IPublicTypePlugin = (ctx: IPublicModelPluginContext) => {
|
||||
const { command, project } = ctx;
|
||||
return {
|
||||
init() {
|
||||
command.registerCommand({
|
||||
name: 'add',
|
||||
description: 'Add a node to the canvas.',
|
||||
handler: (param: {
|
||||
parentNodeId: string;
|
||||
nodeSchema: IPublicTypeNodeSchema;
|
||||
index: number;
|
||||
}) => {
|
||||
const {
|
||||
parentNodeId,
|
||||
nodeSchema,
|
||||
index,
|
||||
} = param;
|
||||
const { project } = ctx;
|
||||
const parentNode = project.currentDocument?.getNodeById(parentNodeId);
|
||||
if (!parentNode) {
|
||||
throw new Error(`Can not find node '${parentNodeId}'.`);
|
||||
}
|
||||
|
||||
if (!parentNode.isContainerNode) {
|
||||
throw new Error(`Node '${parentNodeId}' is not a container node.`);
|
||||
}
|
||||
|
||||
if (!isNodeSchema(nodeSchema)) {
|
||||
throw new Error('Invalid node.');
|
||||
}
|
||||
|
||||
if (index < 0 || index > (parentNode.children?.size || 0)) {
|
||||
throw new Error(`Invalid index '${index}'.`);
|
||||
}
|
||||
|
||||
project.currentDocument?.insertNode(parentNode, nodeSchema, index);
|
||||
},
|
||||
parameters: [
|
||||
{
|
||||
name: 'parentNodeId',
|
||||
propType: 'string',
|
||||
description: 'The id of the parent node.',
|
||||
},
|
||||
{
|
||||
name: 'nodeSchema',
|
||||
propType: nodeSchemaPropType,
|
||||
description: 'The node to be added.',
|
||||
},
|
||||
{
|
||||
name: 'index',
|
||||
propType: 'number',
|
||||
description: 'The index of the node to be added.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
command.registerCommand({
|
||||
name: 'move',
|
||||
description: 'Move a node to another node.',
|
||||
handler(param: {
|
||||
nodeId: string;
|
||||
targetNodeId: string;
|
||||
index: number;
|
||||
}) {
|
||||
const {
|
||||
nodeId,
|
||||
targetNodeId,
|
||||
index = 0,
|
||||
} = param;
|
||||
|
||||
if (!nodeId) {
|
||||
throw new Error('Invalid node id.');
|
||||
}
|
||||
|
||||
if (!targetNodeId) {
|
||||
throw new Error('Invalid target node id.');
|
||||
}
|
||||
|
||||
const node = project.currentDocument?.getNodeById(nodeId);
|
||||
const targetNode = project.currentDocument?.getNodeById(targetNodeId);
|
||||
if (!node) {
|
||||
throw new Error(`Can not find node '${nodeId}'.`);
|
||||
}
|
||||
|
||||
if (!targetNode) {
|
||||
throw new Error(`Can not find node '${targetNodeId}'.`);
|
||||
}
|
||||
|
||||
if (!targetNode.isContainerNode) {
|
||||
throw new Error(`Node '${targetNodeId}' is not a container node.`);
|
||||
}
|
||||
|
||||
if (index < 0 || index > (targetNode.children?.size || 0)) {
|
||||
throw new Error(`Invalid index '${index}'.`);
|
||||
}
|
||||
|
||||
project.currentDocument?.removeNode(node);
|
||||
project.currentDocument?.insertNode(targetNode, node, index);
|
||||
},
|
||||
parameters: [
|
||||
{
|
||||
name: 'nodeId',
|
||||
propType: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
},
|
||||
description: 'The id of the node to be moved.',
|
||||
},
|
||||
{
|
||||
name: 'targetNodeId',
|
||||
propType: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
},
|
||||
description: 'The id of the target node.',
|
||||
},
|
||||
{
|
||||
name: 'index',
|
||||
propType: 'number',
|
||||
description: 'The index of the node to be moved.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
command.registerCommand({
|
||||
name: 'remove',
|
||||
description: 'Remove a node from the canvas.',
|
||||
handler(param: {
|
||||
nodeId: string;
|
||||
}) {
|
||||
const {
|
||||
nodeId,
|
||||
} = param;
|
||||
|
||||
const node = project.currentDocument?.getNodeById(nodeId);
|
||||
if (!node) {
|
||||
throw new Error(`Can not find node '${nodeId}'.`);
|
||||
}
|
||||
|
||||
project.currentDocument?.removeNode(node);
|
||||
},
|
||||
parameters: [
|
||||
{
|
||||
name: 'nodeId',
|
||||
propType: 'string',
|
||||
description: 'The id of the node to be removed.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
command.registerCommand({
|
||||
name: 'update',
|
||||
description: 'Update a node.',
|
||||
handler(param: {
|
||||
nodeId: string;
|
||||
nodeSchema: IPublicTypeNodeSchema;
|
||||
}) {
|
||||
const {
|
||||
nodeId,
|
||||
nodeSchema,
|
||||
} = param;
|
||||
|
||||
const node = project.currentDocument?.getNodeById(nodeId);
|
||||
if (!node) {
|
||||
throw new Error(`Can not find node '${nodeId}'.`);
|
||||
}
|
||||
|
||||
if (!isNodeSchema(nodeSchema)) {
|
||||
throw new Error('Invalid node.');
|
||||
}
|
||||
|
||||
node.importSchema(nodeSchema);
|
||||
},
|
||||
parameters: [
|
||||
{
|
||||
name: 'nodeId',
|
||||
propType: 'string',
|
||||
description: 'The id of the node to be updated.',
|
||||
},
|
||||
{
|
||||
name: 'nodeSchema',
|
||||
propType: nodeSchemaPropType,
|
||||
description: 'The node to be updated.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
command.registerCommand({
|
||||
name: 'updateProps',
|
||||
description: 'Update the properties of a node.',
|
||||
handler(param: {
|
||||
nodeId: string;
|
||||
props: Record<string, any>;
|
||||
}) {
|
||||
const {
|
||||
nodeId,
|
||||
props,
|
||||
} = param;
|
||||
|
||||
const node = project.currentDocument?.getNodeById(nodeId);
|
||||
if (!node) {
|
||||
throw new Error(`Can not find node '${nodeId}'.`);
|
||||
}
|
||||
|
||||
Object.keys(props).forEach(key => {
|
||||
node.setPropValue(key, props[key]);
|
||||
});
|
||||
},
|
||||
parameters: [
|
||||
{
|
||||
name: 'nodeId',
|
||||
propType: 'string',
|
||||
description: 'The id of the node to be updated.',
|
||||
},
|
||||
{
|
||||
name: 'props',
|
||||
propType: 'object',
|
||||
description: 'The properties to be updated.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
command.registerCommand({
|
||||
name: 'removeProps',
|
||||
description: 'Remove the properties of a node.',
|
||||
handler(param: {
|
||||
nodeId: string;
|
||||
propNames: string[];
|
||||
}) {
|
||||
const {
|
||||
nodeId,
|
||||
propNames,
|
||||
} = param;
|
||||
|
||||
const node = project.currentDocument?.getNodeById(nodeId);
|
||||
if (!node) {
|
||||
throw new Error(`Can not find node '${nodeId}'.`);
|
||||
}
|
||||
|
||||
propNames.forEach(key => {
|
||||
node.props?.getProp(key)?.remove();
|
||||
});
|
||||
},
|
||||
parameters: [
|
||||
{
|
||||
name: 'nodeId',
|
||||
propType: 'string',
|
||||
description: 'The id of the node to be updated.',
|
||||
},
|
||||
{
|
||||
name: 'propNames',
|
||||
propType: 'array',
|
||||
description: 'The properties to be removed.',
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
destroy() {
|
||||
command.unregisterCommand('node:add');
|
||||
command.unregisterCommand('node:move');
|
||||
command.unregisterCommand('node:remove');
|
||||
command.unregisterCommand('node:update');
|
||||
command.unregisterCommand('node:updateProps');
|
||||
command.unregisterCommand('node:removeProps');
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
nodeCommand.pluginName = '___node_command___';
|
||||
nodeCommand.meta = {
|
||||
commandScope: 'node',
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-plugin-designer",
|
||||
"version": "1.3.2",
|
||||
"version": "1.2.4-beta.3",
|
||||
"description": "alibaba lowcode editor designer plugin",
|
||||
"files": [
|
||||
"es",
|
||||
@ -18,9 +18,9 @@
|
||||
],
|
||||
"author": "xiayang.xy",
|
||||
"dependencies": {
|
||||
"@alilc/lowcode-designer": "1.3.2",
|
||||
"@alilc/lowcode-editor-core": "1.3.2",
|
||||
"@alilc/lowcode-utils": "1.3.2",
|
||||
"@alilc/lowcode-designer": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-editor-core": "1.2.4-beta.3",
|
||||
"@alilc/lowcode-utils": "1.2.4-beta.3",
|
||||
"react": "^16.8.1",
|
||||
"react-dom": "^16.8.1"
|
||||
},
|
||||
@ -37,7 +37,7 @@
|
||||
"type": "http",
|
||||
"url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/plugin-designer"
|
||||
},
|
||||
"gitHead": "2669f179e6f899d395ce1942d0fe04f9c5ed48a6",
|
||||
"gitHead": "5034994a2421e18d3ee6e14f6ce772ac133865d3",
|
||||
"bugs": "https://github.com/alibaba/lowcode-engine/issues",
|
||||
"homepage": "https://github.com/alibaba/lowcode-engine/#readme"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user