mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-03-05 01:37:17 +00:00
chore: merge from develop
This commit is contained in:
commit
7b90462109
@ -36,6 +36,12 @@ sidebar_position: 12
|
||||
|
||||
`@type {boolean}`
|
||||
|
||||
### clipboard
|
||||
全局剪贴板实例
|
||||
|
||||
`@type {IPublicModelClipboard}`
|
||||
|
||||
相关类型:[IPublicModelClipboard](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/clipboard.ts)
|
||||
|
||||
## 方法
|
||||
|
||||
|
||||
43
docs/docs/api/model/clipboard.md
Normal file
43
docs/docs/api/model/clipboard.md
Normal file
@ -0,0 +1,43 @@
|
||||
---
|
||||
title: Clipboard
|
||||
sidebar_position: 14
|
||||
---
|
||||
|
||||
> **@types** [IPublicModelClipboard](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/clipboard.ts)<br/>
|
||||
> **@since** v1.1.0
|
||||
|
||||
## 方法
|
||||
|
||||
### setData
|
||||
|
||||
给剪贴板赋值
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 给剪贴板赋值
|
||||
* set data to clipboard
|
||||
*
|
||||
* @param {*} data
|
||||
* @since v1.1.0
|
||||
*/
|
||||
setData(data: any): void;
|
||||
```
|
||||
|
||||
### waitPasteData
|
||||
|
||||
设置剪贴板数据设置的回调
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 设置剪贴板数据设置的回调
|
||||
* set callback for clipboard provide paste data
|
||||
*
|
||||
* @param {KeyboardEvent} keyboardEvent
|
||||
* @param {(data: any, clipboardEvent: ClipboardEvent) => void} cb
|
||||
* @since v1.1.0
|
||||
*/
|
||||
waitPasteData(
|
||||
keyboardEvent: KeyboardEvent,
|
||||
cb: (data: any, clipboardEvent: ClipboardEvent) => void,
|
||||
): void;
|
||||
```
|
||||
@ -160,7 +160,7 @@ sidebar_position: 1
|
||||
|
||||
`@type {IPublicModelComponentMeta | null}`
|
||||
|
||||
相关类型:[IPublicTypeIconType](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/component-meta.ts)
|
||||
相关类型:[IPublicModelComponentMeta](https://github.com/alibaba/lowcode-engine/blob/main/packages/types/src/shell/model/component-meta.ts)
|
||||
|
||||
|
||||
### document
|
||||
@ -644,4 +644,4 @@ isConditionalVisible(): boolean | undefined;
|
||||
setConditionalVisible(): void;
|
||||
```
|
||||
|
||||
**@since v1.1.0**
|
||||
**@since v1.1.0**
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Resource
|
||||
sidebar_position: 12
|
||||
sidebar_position: 13
|
||||
---
|
||||
|
||||
> **[@experimental](./#experimental)**<br/>
|
||||
|
||||
@ -23,7 +23,7 @@ sidebar_position: 1
|
||||
|
||||
对于低代码物料来说,A 平台创建的物料无法使用到 B 平台上,如果想在 B 平台实现同样的物料,需要按照 B 平台的标准搭建一份物料。
|
||||
|
||||
对于 ProCode 物料来说,需要在低代码平台进行消费,是需要进行转换的,包括搭建配置项的生成、物料搭建试图等,可能还需要特殊的描述文件进行描述。由于这一层没有统一,同一份 ProCode 物料每接入一个低代码,可能需要的描述文件格式不同,转换的代码不同,使用的工具也不同。
|
||||
对于 ProCode 物料来说,需要在低代码平台进行消费,是需要进行转换的,包括搭建配置项的生成、物料搭建视图等,可能还需要特殊的描述文件进行描述。由于这一层没有统一,同一份 ProCode 物料每接入一个低代码,可能需要的描述文件格式不同,转换的代码不同,使用的工具也不同。
|
||||
|
||||
### 生态隔离
|
||||
|
||||
|
||||
@ -84,7 +84,7 @@ component // 组件名称, 比如 biz-button
|
||||
```
|
||||
|
||||
|
||||
#### README.md
|
||||
##### README.md
|
||||
|
||||
- README.md 应该包含业务组件的源信息、使用说明以及 API,示例如下:
|
||||
|
||||
@ -126,7 +126,7 @@ npm install @alifd/ice-layout -S
|
||||
| type | type | String | `primray`、`normal` | normal |
|
||||
```
|
||||
|
||||
#### package.json
|
||||
##### package.json
|
||||
`package.json` 中包含了一些依赖信息和配置信息,示例如下:
|
||||
|
||||
```json
|
||||
@ -159,7 +159,7 @@ npm install @alifd/ice-layout -S
|
||||
}
|
||||
```
|
||||
|
||||
#### src/index.js
|
||||
##### src/index.js
|
||||
|
||||
包含组件的出口文件,示例如下:
|
||||
|
||||
@ -178,7 +178,7 @@ export default Button;
|
||||
import Button, { Group } form '@scope/button';
|
||||
```
|
||||
|
||||
#### src/index.scss
|
||||
##### src/index.scss
|
||||
|
||||
```css
|
||||
/* 不引入依赖组件的样式,比如组件 import { Button } from '@alifd/next'; */
|
||||
@ -193,7 +193,7 @@ import Button, { Group } form '@scope/button';
|
||||
}
|
||||
```
|
||||
|
||||
#### demo
|
||||
##### demo
|
||||
demo 目录存放的是组件的文档,无文档的业务组件无法带来任何价值,因此 demo 是必选项。demo 目录下的文件采取 markdown 的写法,可以是多个文件,示例(demo/basic.md)如下:
|
||||
|
||||
demo/basic.md
|
||||
@ -236,12 +236,12 @@ ReactDOM.render(<div className="test">
|
||||
|
||||
API 是组件的属性解释,给开发者作为组件属性配置的参考。为了保持 API 的一致性,我们制定这个 API 命名规范。对于业界通用的,约定俗成的命名,我们遵循社区的约定。对于业界有多种规则难以确定的,我们确定其中一种,大家共同遵守。
|
||||
|
||||
#### 通用规则
|
||||
##### 通用规则
|
||||
|
||||
- 所有的 API 采用小驼峰的书写规则,如 `onChange`、`direction`、`defaultVisible`。
|
||||
- 标签名采用大驼峰书写规则,如 `Menu`、`Slider`、`DatePicker`。
|
||||
|
||||
#### 通用命名
|
||||
##### 通用命名
|
||||
|
||||
| API 名称 | 类型 | 描述 | 常见变量 |
|
||||
| :------------- | :------------- | :----------------------------------------------------------- | :---------------------------------------------------- |
|
||||
@ -261,7 +261,7 @@ API 是组件的属性解释,给开发者作为组件属性配置的参考。
|
||||
| has+'属性' | boolean | 拥有某个属性 | 例如 `hasArrow`, `hasHeader`, `hasClose` 等等 |
|
||||
|
||||
|
||||
#### 多选枚举
|
||||
##### 多选枚举
|
||||
|
||||
当某个 API 的接口,允许用户指定多个枚举值的时候,我们把这个接口定义为多选枚举。一个很典型的例子是某个弹层组件的 `closable` 属性,我们会允许:键盘 esc 按键、点击 mask、点击 close 按钮、点击组件以外的任何区域进行关闭。
|
||||
|
||||
@ -280,11 +280,11 @@ true 表示触发规则都会关闭,false 表示触发规则不会关闭。
|
||||
- `<Dialog closable={false} />`,任何情况下都不关闭,只能通过受控设置 visible
|
||||
- `<Dialog closable closeMode={['close', 'esc']} />`,用户按 esc 或者点击关闭按钮会关闭
|
||||
|
||||
#### 事件
|
||||
##### 事件
|
||||
|
||||
- 标准事件或者自定义的符合 w3c 标准的事件,命名必须 on 开头, 即 `on` + 事件名,如 onExpand。
|
||||
|
||||
#### 表单规范
|
||||
##### 表单规范
|
||||
|
||||
- 支持[受控模式](https://reactjs.org/docs/forms.html#controlled-components)(value + onChange) (A)
|
||||
- value 控制组件数据展现
|
||||
@ -292,7 +292,7 @@ true 表示触发规则都会关闭,false 表示触发规则不会关闭。
|
||||
- `value={undefined}`的时候清空数据,field 的 reset 函数会给所有组件下发 undefined 数据 (AA))
|
||||
- 一次完整操作抛一次 onChange 事件 `建议` 比如有 Process 表示进展中的状态,建议增加 API `onProcess`;如果有 Start 表示启动状态,建议增加 API `onStart` (AA)
|
||||
|
||||
#### 属性的传递
|
||||
##### 属性的传递
|
||||
**1. 原子组件(Atomic Component)**
|
||||
> 最小粒子,不能再拆分的组件
|
||||
|
||||
@ -354,7 +354,7 @@ $ iceworks sync
|
||||
|
||||
文件命名采取 [bcp47](https://tools.ietf.org/html/bcp47) 规范
|
||||
|
||||
#### 目录规范
|
||||
##### 目录规范
|
||||
|
||||
在 `src` 目录新增 `locale` 目录用于管理不同语言的文案。
|
||||
|
||||
@ -367,7 +367,7 @@ $ iceworks sync
|
||||
|------ ja-JP.js
|
||||
```
|
||||
|
||||
#### 定义不同的语言
|
||||
##### 定义不同的语言
|
||||
|
||||
```javascript
|
||||
// zh-CN.js
|
||||
@ -390,7 +390,7 @@ export default {
|
||||
};
|
||||
```
|
||||
|
||||
#### 组件支持多语言建议方案
|
||||
##### 组件支持多语言建议方案
|
||||
|
||||
```jsx
|
||||
// index.jsx
|
||||
@ -417,7 +417,7 @@ export default class BizHello extends Component {
|
||||
}
|
||||
```
|
||||
|
||||
#### 组件支持全局替换国际化文案
|
||||
##### 组件支持全局替换国际化文案
|
||||
|
||||
配合 ConfigProvider 支持全局替换国际化文案。
|
||||
|
||||
@ -451,7 +451,7 @@ export default ConfigProvider.config(BizHello, {
|
||||
|
||||
业务组件中如果有自定义的需要跟随主题色的 UI,一定要引入变量的形式,增加组件的流通性。
|
||||
|
||||
#### src/index.scss
|
||||
##### src/index.scss
|
||||
|
||||
```css
|
||||
/* 如果需要引入主题变量引入此段 */
|
||||
@ -503,7 +503,7 @@ api 属性标准参考 [https://fusion.design/help.html#/dev-biz](https://fusio
|
||||
无障碍需要符合 [WCAG 2.1 A 级标准](https://www.w3.org/TR/WCAG21/),可参考 [W3C 无障碍最佳实践](https://www.w3.org/TR/wai-aria-practices-1.1/)、[Fusion 无障碍指引 2.3.1](https://alibaba-fusion.github.io/next/part1/basics.html) 章节等。
|
||||
|
||||
|
||||
#### 增加 a11y.md 无障碍 demo
|
||||
##### 增加 a11y.md 无障碍 demo
|
||||
|
||||
必须借助 API 才能完成无障碍工作的组件必须为开发者提供无障碍的使用文档,请[参考](https://fusion.design/pc/component/select?themeid=2#accessibility-container)组件 API 中 `ARIA and Keyboard` ,建议在 `demo` 目录新增 `a11y.md` 文件用于演示组件的无障碍使用。
|
||||
|
||||
@ -517,7 +517,7 @@ component
|
||||
详细指引查看无障碍开发指南 [https://alibaba-fusion.github.io/next/part1/basics.html](https://alibaba-fusion.github.io/next/part1/basics.html)。
|
||||
|
||||
|
||||
#### 通过键盘快速访问
|
||||
##### 通过键盘快速访问
|
||||
|
||||
一般键盘事件有 Up Arrow/Down Arrow/Enter/Esc/Tab
|
||||
|
||||
@ -531,7 +531,7 @@ component
|
||||
| Esc | 关闭列表 |
|
||||
|
||||
|
||||
#### 对读屏软件友好
|
||||
##### 对读屏软件友好
|
||||
|
||||
- 对于组件,我们为开发者内置 `role` 和特定 `aria-_属性`,开发者也可以对非组件 API 属性都可以透传至 DOM 元素,进行修改 `role` 和 `aria-_参数`,但是要注意对应关系,请[参考](https://alibaba-fusion.github.io/next/part1/WAI-ARIA.html)。
|
||||
- 对一些特殊的组件传递参数才能支持无障碍,设置 `id`,`autoFocus` 和传参数,如下:
|
||||
@ -925,18 +925,18 @@ props 数组下对象字段描述:
|
||||
|initialChildren | 组件拖入“设计器”时根据此配置自动生成 children 节点 schema |NodeData[]/Function NodeData[] | ((target: SettingTarget) => NodeData[]);|
|
||||
|getResizingHandlers| 用于配置设计器中组件 resize 操作工具的样式和内容 | Function| (currentNode: any) => Array<{ type: 'N' | 'W' | 'S' | 'E' | 'NW' | 'NE' | 'SE' | 'SW'; content?: ReactElement; propTarget?: string; appearOn?: 'mouse-enter' | 'mouse-hover' | 'selected' | 'always'; }> / ReactElement[];
|
||||
|callbacks| 配置 callbacks 可捕获引擎抛出的一些事件,例如 onNodeAdd、onResize 等 | Callback| -
|
||||
|callbacks.onNodeAdd| 在容器中拖入组件时触发的事件回调| Function| (e: MouseEvent, currentNode: any) => any
|
||||
|callbacks.onNodeRemove| 在容器中删除组件时触发的事件回调| Function| (e: MouseEvent, currentNode: any) => any
|
||||
|callbacks.onNodeAdd| 在容器中拖入组件时触发的事件回调 | Function| (e: MouseEvent, currentNode: any) => any
|
||||
|callbacks.onNodeRemove| 在容器中删除组件时触发的事件回调 | Function| (e: MouseEvent, currentNode: any) => any
|
||||
|callbacks.onResize| 调整容器尺寸时触发的事件回调,常常与 getResizingHandlers 搭配使用 | Function| 详见 Types 定义
|
||||
|callbacks.onResizeStart| 调整容器尺寸开始时触发的事件回调,常常与 getResizingHandlers 搭配使用 | Function| 详见 Types 定义
|
||||
|callbacks.onResizeEnd| 调整容器尺寸结束时触发的事件回调,常常与 getResizingHandlers 搭配使用 | Function| 详见 Types 定义
|
||||
|callbacks.onSubtreeModified| 容器节点结构树发生变化时触发的回调| Function| (currentNode: any, options: any) => void;
|
||||
|callbacks.onMouseDownHook| 鼠标按下操作回调| Function| (e: MouseEvent, currentNode: any) => any;
|
||||
|callbacks.onClickHook| 鼠标单击操作回调| Function| (e: MouseEvent, currentNode: any) => any;
|
||||
|callbacks.onDblClickHook| 鼠标双击操作回调| Function| (e: MouseEvent, currentNode: any) => any;
|
||||
|callbacks.onSubtreeModified| 容器节点结构树发生变化时触发的回调 | Function| (currentNode: any, options: any) => void;
|
||||
|callbacks.onMouseDownHook| 鼠标按下操作回调 | Function| (e: MouseEvent, currentNode: any) => any;
|
||||
|callbacks.onClickHook| 鼠标单击操作回调 | Function| (e: MouseEvent, currentNode: any) => any;
|
||||
|callbacks.onDblClickHook| 鼠标双击操作回调 | Function| (e: MouseEvent, currentNode: any) => any;
|
||||
|callbacks.onMoveHook| 节点被拖动回调 | Function| (currentNode: any) => boolean;
|
||||
|callbacks.onHoverHook| 节点被 hover 回调 | Function| (currentNode: any) => boolean;
|
||||
|callbacks.onChildMoveHook| 容器节点的子节点被拖动回调| Function| (childNode: any, currentNode: any) => boolean;
|
||||
|callbacks.onChildMoveHook| 容器节点的子节点被拖动回调 | Function| (childNode: any, currentNode: any) => boolean;
|
||||
|
||||
|
||||
描述举例:
|
||||
@ -1543,7 +1543,7 @@ block/
|
||||
```
|
||||
|
||||
|
||||
#### 入口文件
|
||||
##### 入口文件
|
||||
|
||||
(/src/index.jsx)
|
||||
|
||||
@ -1559,7 +1559,7 @@ const App = hot(router);
|
||||
ReactDOM.render(<App />, document.getElementById(pkg.config && pkg.config.targetRootID || 'root'));
|
||||
```
|
||||
|
||||
#### 应用参数配置文件
|
||||
##### 应用参数配置文件
|
||||
|
||||
(/src/config/app.js)
|
||||
|
||||
@ -1596,7 +1596,7 @@ export default {
|
||||
}
|
||||
```
|
||||
|
||||
#### 应用扩展配置规范:
|
||||
##### 应用扩展配置规范:
|
||||
|
||||
(/src/utils/index.js)
|
||||
|
||||
@ -1618,7 +1618,7 @@ export default {
|
||||
}
|
||||
```
|
||||
|
||||
#### 应用常量配置
|
||||
##### 应用常量配置
|
||||
|
||||
(/src/config/constants.js)
|
||||
|
||||
@ -1628,7 +1628,7 @@ export default {
|
||||
}
|
||||
```
|
||||
|
||||
#### 应用样式配置
|
||||
##### 应用样式配置
|
||||
|
||||
(/src/global.scss)
|
||||
|
||||
|
||||
@ -39,7 +39,6 @@ const config = {
|
||||
presets: [
|
||||
[
|
||||
'classic',
|
||||
/** @type {import('@docusaurus/preset-classic').Options} */
|
||||
({
|
||||
docs: {
|
||||
sidebarPath: require.resolve('./config/sidebars.js'),
|
||||
@ -55,7 +54,6 @@ const config = {
|
||||
],
|
||||
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
docs: {
|
||||
sidebar: {
|
||||
@ -76,7 +74,7 @@ const config = {
|
||||
metadata: [{ name: 'referrer', content: 'no-referrer' }],
|
||||
tableOfContents: {
|
||||
minHeadingLevel: 2,
|
||||
maxHeadingLevel: 5,
|
||||
maxHeadingLevel: 6,
|
||||
},
|
||||
}),
|
||||
|
||||
|
||||
@ -13,9 +13,9 @@ import {
|
||||
IPublicTypeMetadataTransducer,
|
||||
IPublicModelComponentMeta,
|
||||
} from '@alilc/lowcode-types';
|
||||
import { deprecate, isRegExp, isTitleConfig } from '@alilc/lowcode-utils';
|
||||
import { deprecate, isRegExp, isTitleConfig, isNode } from '@alilc/lowcode-utils';
|
||||
import { computed, createModuleEventBus, IEventBus } from '@alilc/lowcode-editor-core';
|
||||
import { isNode, Node, INode } from './document';
|
||||
import { Node, INode } from './document';
|
||||
import { Designer } from './designer';
|
||||
import {
|
||||
IconContainer,
|
||||
@ -161,6 +161,9 @@ export class ComponentMeta implements IComponentMeta {
|
||||
return this._acceptable!;
|
||||
}
|
||||
|
||||
// compatiable vision
|
||||
prototype?: any;
|
||||
|
||||
constructor(readonly designer: Designer, metadata: IPublicTypeComponentMetadata) {
|
||||
this.parseMetadata(metadata);
|
||||
}
|
||||
@ -347,8 +350,6 @@ export class ComponentMeta implements IComponentMeta {
|
||||
};
|
||||
}
|
||||
|
||||
// compatiable vision
|
||||
prototype?: any;
|
||||
}
|
||||
|
||||
export function isComponentMeta(obj: any): obj is ComponentMeta {
|
||||
@ -373,4 +374,3 @@ function preprocessMetadata(metadata: IPublicTypeComponentMetadata): IPublicType
|
||||
configure: {},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { IPublicModelClipboard } from '@alilc/lowcode-types';
|
||||
|
||||
function getDataFromPasteEvent(event: ClipboardEvent) {
|
||||
const { clipboardData } = event;
|
||||
if (!clipboardData) {
|
||||
@ -23,7 +25,13 @@ function getDataFromPasteEvent(event: ClipboardEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
class Clipboard {
|
||||
export interface IClipboard extends IPublicModelClipboard {
|
||||
|
||||
initCopyPaster(el: HTMLTextAreaElement): void;
|
||||
|
||||
injectCopyPaster(document: Document): void;
|
||||
}
|
||||
class Clipboard implements IClipboard {
|
||||
private copyPasters: HTMLTextAreaElement[] = [];
|
||||
|
||||
private waitFn?: (data: any, e: ClipboardEvent) => void;
|
||||
@ -56,7 +64,7 @@ class Clipboard {
|
||||
}
|
||||
|
||||
injectCopyPaster(document: Document) {
|
||||
if (this.copyPasters.find(x => x.ownerDocument === document)) {
|
||||
if (this.copyPasters.find((x) => x.ownerDocument === document)) {
|
||||
return;
|
||||
}
|
||||
const copyPaster = document.createElement<'textarea'>('textarea');
|
||||
@ -69,8 +77,8 @@ class Clipboard {
|
||||
};
|
||||
}
|
||||
|
||||
setData(data: any) {
|
||||
const copyPaster = this.copyPasters.find(x => x.ownerDocument);
|
||||
setData(data: any): void {
|
||||
const copyPaster = this.copyPasters.find((x) => x.ownerDocument);
|
||||
if (!copyPaster) {
|
||||
return;
|
||||
}
|
||||
@ -81,12 +89,12 @@ class Clipboard {
|
||||
copyPaster.blur();
|
||||
}
|
||||
|
||||
waitPasteData(e: KeyboardEvent, cb: (data: any, e: ClipboardEvent) => void) {
|
||||
const win = e.view;
|
||||
waitPasteData(keyboardEvent: KeyboardEvent, cb: (data: any, e: ClipboardEvent) => void) {
|
||||
const win = keyboardEvent.view;
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
const copyPaster = this.copyPasters.find(cp => cp.ownerDocument === win.document);
|
||||
const copyPaster = this.copyPasters.find((cp) => cp.ownerDocument === win.document);
|
||||
if (copyPaster) {
|
||||
copyPaster.select();
|
||||
this.waitFn = cb;
|
||||
|
||||
@ -30,7 +30,6 @@ import { ActiveTracker, IActiveTracker } from './active-tracker';
|
||||
import { Detecting } from './detecting';
|
||||
import { DropLocation } from './location';
|
||||
import { OffsetObserver, createOffsetObserver } from './offset-observer';
|
||||
import { focusing } from './focusing';
|
||||
import { SettingTopEntry } from './setting';
|
||||
import { BemToolsManager } from '../builtin-simulator/bem-tools/manager';
|
||||
import { ComponentActions } from '../component-actions';
|
||||
@ -241,9 +240,6 @@ export class Designer implements IDesigner {
|
||||
this.postEvent('init', this);
|
||||
this.setupSelection();
|
||||
setupHistory();
|
||||
|
||||
// TODO: 先简单实现,后期通过焦点赋值
|
||||
focusing.focusDesigner = this;
|
||||
}
|
||||
|
||||
setupSelection = () => {
|
||||
@ -341,6 +337,7 @@ export class Designer implements IDesigner {
|
||||
|
||||
/**
|
||||
* 获得合适的插入位置
|
||||
* @deprecated
|
||||
*/
|
||||
getSuitableInsertion(
|
||||
insertNode?: INode | IPublicTypeNodeSchema | IPublicTypeNodeSchema[],
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
import { Designer } from './designer';
|
||||
|
||||
// TODO: use focus-tracker replace
|
||||
class Focusing {
|
||||
focusDesigner?: Designer;
|
||||
}
|
||||
|
||||
export const focusing = new Focusing();
|
||||
@ -7,6 +7,5 @@ export * from './offset-observer';
|
||||
export * from './scroller';
|
||||
export * from './setting';
|
||||
export * from './active-tracker';
|
||||
export * from './focusing';
|
||||
export * from '../document';
|
||||
export * from './clipboard';
|
||||
|
||||
@ -17,11 +17,11 @@ import { IProject, Project } from '../project';
|
||||
import { ISimulatorHost } from '../simulator';
|
||||
import { ComponentMeta } from '../component-meta';
|
||||
import { IDropLocation, Designer, IHistory } from '../designer';
|
||||
import { Node, insertChildren, insertChild, isNode, RootNode, INode } from './node/node';
|
||||
import { Node, insertChildren, insertChild, RootNode, INode } from './node/node';
|
||||
import { Selection, ISelection } from './selection';
|
||||
import { History } from './history';
|
||||
import { IModalNodesManager, ModalNodesManager } from './node';
|
||||
import { uniqueId, isPlainObject, compatStage, isJSExpression, isDOMText, isNodeSchema, isDragNodeObject, isDragNodeDataObject } from '@alilc/lowcode-utils';
|
||||
import { uniqueId, isPlainObject, compatStage, isJSExpression, isDOMText, isNodeSchema, isDragNodeObject, isDragNodeDataObject, isNode } from '@alilc/lowcode-utils';
|
||||
import { EDITOR_EVENT } from '../types';
|
||||
|
||||
export type GetDataType<T, NodeType> = T extends undefined
|
||||
@ -32,7 +32,7 @@ export type GetDataType<T, NodeType> = T extends undefined
|
||||
: any
|
||||
: T;
|
||||
|
||||
export interface IDocumentModel extends Omit< IPublicModelDocumentModel, 'selection' > {
|
||||
export interface IDocumentModel extends Omit< IPublicModelDocumentModel, 'selection' | 'checkNesting' > {
|
||||
|
||||
readonly designer: Designer;
|
||||
|
||||
@ -59,6 +59,11 @@ export interface IDocumentModel extends Omit< IPublicModelDocumentModel, 'select
|
||||
|
||||
get rootNode(): INode | null;
|
||||
|
||||
checkNesting(
|
||||
dropTarget: INode,
|
||||
dragObject: IPublicTypeDragNodeObject | IPublicTypeNodeSchema | INode | IPublicTypeDragNodeDataObject,
|
||||
): boolean;
|
||||
|
||||
}
|
||||
|
||||
export class DocumentModel implements IDocumentModel {
|
||||
@ -569,7 +574,10 @@ export class DocumentModel implements IDocumentModel {
|
||||
this.rootNode = null;
|
||||
}
|
||||
|
||||
checkNesting(dropTarget: INode, dragObject: IPublicTypeDragNodeObject | IPublicTypeNodeSchema | Node | IPublicTypeDragNodeDataObject): boolean {
|
||||
checkNesting(
|
||||
dropTarget: INode,
|
||||
dragObject: IPublicTypeDragNodeObject | IPublicTypeNodeSchema | INode | IPublicTypeDragNodeDataObject,
|
||||
): boolean {
|
||||
let items: Array<Node | IPublicTypeNodeSchema>;
|
||||
if (isDragNodeDataObject(dragObject)) {
|
||||
items = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data];
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
IPublicModelExclusiveGroup,
|
||||
IPublicEnumTransformStage,
|
||||
} from '@alilc/lowcode-types';
|
||||
import { compatStage, isDOMText, isJSExpression } from '@alilc/lowcode-utils';
|
||||
import { compatStage, isDOMText, isJSExpression, isNode } from '@alilc/lowcode-utils';
|
||||
import { SettingTopEntry } from '@alilc/lowcode-designer';
|
||||
import { Props, getConvertedExtraKey, IProps } from './props/props';
|
||||
import { DocumentModel, IDocumentModel } from '../document-model';
|
||||
@ -109,6 +109,8 @@ export interface INode extends IPublicModelNode {
|
||||
getExtraProp(key: string, createIfNone?: boolean): IProp | null;
|
||||
|
||||
replaceChild(node: INode, data: any): INode;
|
||||
|
||||
getSuitablePlace(node: INode, ref: any): any;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -913,7 +915,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
/**
|
||||
* 判断是否包含特定节点
|
||||
*/
|
||||
contains(node: Node): boolean {
|
||||
contains(node: INode): boolean {
|
||||
return contains(this, node);
|
||||
}
|
||||
|
||||
@ -1147,16 +1149,16 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: replace non standard metas with standard ones.
|
||||
* @deprecated no one is using this, will be removed in a future release
|
||||
*/
|
||||
getSuitablePlace(node: Node, ref: any): any {
|
||||
getSuitablePlace(node: INode, ref: any): any {
|
||||
const focusNode = this.document?.focusNode;
|
||||
// 如果节点是模态框,插入到根节点下
|
||||
if (node?.componentMeta?.isModal) {
|
||||
return { container: focusNode, ref };
|
||||
}
|
||||
|
||||
if (!ref && this.contains(focusNode)) {
|
||||
if (!ref && focusNode && this.contains(focusNode)) {
|
||||
const rootCanDropIn = focusNode.componentMeta?.prototype?.options?.canDropIn;
|
||||
if (
|
||||
rootCanDropIn === undefined ||
|
||||
@ -1171,7 +1173,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
|
||||
if (this.isRoot() && this.children) {
|
||||
const dropElement = this.children.filter((c) => {
|
||||
if (!c.isContainer()) {
|
||||
if (!c.isContainerNode) {
|
||||
return false;
|
||||
}
|
||||
const canDropIn = c.componentMeta?.prototype?.options?.canDropIn;
|
||||
@ -1304,22 +1306,15 @@ export type PageNode = Node<IPublicTypePageSchema>;
|
||||
export type ComponentNode = Node<IPublicTypeComponentSchema>;
|
||||
export type RootNode = PageNode | ComponentNode;
|
||||
|
||||
/**
|
||||
* @deprecated use same function from '@alilc/lowcode-utils' instead
|
||||
*/
|
||||
export function isNode(node: any): node is Node {
|
||||
return node && node.isNode;
|
||||
export function isRootNode(node: INode): node is INode {
|
||||
return node && node.isRootNode;
|
||||
}
|
||||
|
||||
export function isRootNode(node: Node): node is RootNode {
|
||||
return node && node.isRoot();
|
||||
}
|
||||
|
||||
export function isLowCodeComponent(node: Node): boolean {
|
||||
export function isLowCodeComponent(node: INode): node is INode {
|
||||
return node.componentMeta?.getMetadata().devMode === 'lowCode';
|
||||
}
|
||||
|
||||
export function getZLevelTop(child: Node, zLevel: number): Node | null {
|
||||
export function getZLevelTop(child: INode, zLevel: number): INode | null {
|
||||
let l = child.zLevel;
|
||||
if (l < zLevel || zLevel < 0) {
|
||||
return null;
|
||||
@ -1340,12 +1335,12 @@ export function getZLevelTop(child: Node, zLevel: number): Node | null {
|
||||
* @param node2 测试的被包含节点
|
||||
* @returns 是否包含
|
||||
*/
|
||||
export function contains(node1: Node, node2: Node): boolean {
|
||||
export function contains(node1: INode, node2: INode): boolean {
|
||||
if (node1 === node2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!node1.isParental() || !node2.parent) {
|
||||
if (!node1.isParentalNode || !node2.parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1367,7 +1362,7 @@ export enum PositionNO {
|
||||
BeforeOrAfter = 2,
|
||||
TheSame = 0,
|
||||
}
|
||||
export function comparePosition(node1: Node, node2: Node): PositionNO {
|
||||
export function comparePosition(node1: INode, node2: INode): PositionNO {
|
||||
if (node1 === node2) {
|
||||
return PositionNO.TheSame;
|
||||
}
|
||||
@ -1396,11 +1391,11 @@ export function comparePosition(node1: Node, node2: Node): PositionNO {
|
||||
|
||||
export function insertChild(
|
||||
container: INode,
|
||||
thing: Node | IPublicTypeNodeData,
|
||||
thing: INode | IPublicTypeNodeData,
|
||||
at?: number | null,
|
||||
copy?: boolean,
|
||||
): Node {
|
||||
let node: Node;
|
||||
): INode {
|
||||
let node: INode;
|
||||
if (isNode(thing) && (copy || thing.isSlot())) {
|
||||
thing = thing.export(IPublicEnumTransformStage.Clone);
|
||||
}
|
||||
@ -1410,20 +1405,20 @@ export function insertChild(
|
||||
node = container.document.createNode(thing);
|
||||
}
|
||||
|
||||
container.children.internalInsert(node, at);
|
||||
container.children.insert(node, at);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
export function insertChildren(
|
||||
container: INode,
|
||||
nodes: Node[] | IPublicTypeNodeData[],
|
||||
nodes: INode[] | IPublicTypeNodeData[],
|
||||
at?: number | null,
|
||||
copy?: boolean,
|
||||
): Node[] {
|
||||
): INode[] {
|
||||
let index = at;
|
||||
let node: any;
|
||||
const results: Node[] = [];
|
||||
const results: INode[] = [];
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while ((node = nodes.pop())) {
|
||||
node = insertChild(container, node, index, copy);
|
||||
|
||||
@ -7,7 +7,6 @@ import { DocumentModel } from '../../../src/document/document-model';
|
||||
import {
|
||||
isRootNode,
|
||||
Node,
|
||||
isNode,
|
||||
comparePosition,
|
||||
contains,
|
||||
PositionNO,
|
||||
@ -23,6 +22,7 @@ import rootHeaderMetadata from '../../fixtures/component-metadata/root-header';
|
||||
import rootContentMetadata from '../../fixtures/component-metadata/root-content';
|
||||
import rootFooterMetadata from '../../fixtures/component-metadata/root-footer';
|
||||
import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory';
|
||||
import { isNode } from '@alilc/lowcode-utils';
|
||||
|
||||
describe('Node 方法测试', () => {
|
||||
let editor: Editor;
|
||||
|
||||
@ -1,8 +1,14 @@
|
||||
import { IPublicTypeTransformedComponentMetadata, IPublicTypeFieldConfig, IPublicModelSettingTarget } from '@alilc/lowcode-types';
|
||||
import {
|
||||
IPublicTypeTransformedComponentMetadata,
|
||||
IPublicTypeFieldConfig,
|
||||
IPublicModelSettingTarget,
|
||||
} from '@alilc/lowcode-types';
|
||||
import { IconSlot } from '../icons/slot';
|
||||
import { getConvertedExtraKey } from '@alilc/lowcode-designer';
|
||||
|
||||
export default function (metadata: IPublicTypeTransformedComponentMetadata): IPublicTypeTransformedComponentMetadata {
|
||||
export default function (
|
||||
metadata: IPublicTypeTransformedComponentMetadata,
|
||||
): IPublicTypeTransformedComponentMetadata {
|
||||
const { componentName, configure = {} } = metadata;
|
||||
|
||||
// 如果已经处理过,不再重新执行一遍
|
||||
@ -111,35 +117,33 @@ export default function (metadata: IPublicTypeTransformedComponentMetadata): IPu
|
||||
},
|
||||
];
|
||||
}
|
||||
/*
|
||||
propsGroup.push({
|
||||
name: '#generals',
|
||||
title: { type: 'i18n', 'zh-CN': '通用', 'en-US': 'General' },
|
||||
items: [
|
||||
{
|
||||
name: 'id',
|
||||
title: 'ID',
|
||||
setter: 'StringSetter',
|
||||
},
|
||||
{
|
||||
name: 'key',
|
||||
title: 'Key',
|
||||
// todo: use Mixin
|
||||
setter: 'StringSetter',
|
||||
},
|
||||
{
|
||||
name: 'ref',
|
||||
title: 'Ref',
|
||||
setter: 'StringSetter',
|
||||
},
|
||||
{
|
||||
name: '!more',
|
||||
title: '更多',
|
||||
setter: 'PropertiesSetter',
|
||||
},
|
||||
],
|
||||
});
|
||||
*/
|
||||
// propsGroup.push({
|
||||
// name: '#generals',
|
||||
// title: { type: 'i18n', 'zh-CN': '通用', 'en-US': 'General' },
|
||||
// items: [
|
||||
// {
|
||||
// name: 'id',
|
||||
// title: 'ID',
|
||||
// setter: 'StringSetter',
|
||||
// },
|
||||
// {
|
||||
// name: 'key',
|
||||
// title: 'Key',
|
||||
// // todo: use Mixin
|
||||
// setter: 'StringSetter',
|
||||
// },
|
||||
// {
|
||||
// name: 'ref',
|
||||
// title: 'Ref',
|
||||
// setter: 'StringSetter',
|
||||
// },
|
||||
// {
|
||||
// name: '!more',
|
||||
// title: '更多',
|
||||
// setter: 'PropertiesSetter',
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
const stylesGroup: IPublicTypeFieldConfig[] = [];
|
||||
const advancedGroup: IPublicTypeFieldConfig[] = [];
|
||||
if (propsGroup) {
|
||||
@ -216,18 +220,24 @@ export default function (metadata: IPublicTypeTransformedComponentMetadata): IPu
|
||||
|
||||
setValue(field: IPublicModelSettingTarget, eventData) {
|
||||
const { eventDataList, eventList } = eventData;
|
||||
Array.isArray(eventList) && eventList.map((item) => {
|
||||
field.parent.clearPropValue(item.name);
|
||||
return item;
|
||||
});
|
||||
Array.isArray(eventDataList) && eventDataList.map((item) => {
|
||||
field.parent.setPropValue(item.name, {
|
||||
type: 'JSFunction',
|
||||
// 需要传下入参
|
||||
value: `function(){return this.${item.relatedEventName}.apply(this,Array.prototype.slice.call(arguments).concat([${item.paramStr ? item.paramStr : ''}])) }`,
|
||||
Array.isArray(eventList) &&
|
||||
eventList.map((item) => {
|
||||
field.parent.clearPropValue(item.name);
|
||||
return item;
|
||||
});
|
||||
Array.isArray(eventDataList) &&
|
||||
eventDataList.map((item) => {
|
||||
field.parent.setPropValue(item.name, {
|
||||
type: 'JSFunction',
|
||||
// 需要传下入参
|
||||
value: `function(){return this.${
|
||||
item.relatedEventName
|
||||
}.apply(this,Array.prototype.slice.call(arguments).concat([${
|
||||
item.paramStr ? item.paramStr : ''
|
||||
}])) }`,
|
||||
});
|
||||
return item;
|
||||
});
|
||||
return item;
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -296,7 +306,7 @@ export default function (metadata: IPublicTypeTransformedComponentMetadata): IPu
|
||||
},
|
||||
{
|
||||
name: 'key',
|
||||
title: '循环 Key',
|
||||
title: { type: 'i18n', 'zh-CN': '循环 Key', 'en-US': 'Loop Key' },
|
||||
setter: [
|
||||
{
|
||||
componentName: 'StringSetter',
|
||||
@ -317,8 +327,16 @@ export default function (metadata: IPublicTypeTransformedComponentMetadata): IPu
|
||||
advancedGroup.push({
|
||||
name: 'key',
|
||||
title: {
|
||||
label: '渲染唯一标识(key)',
|
||||
tip: '搭配「条件渲染」或「循环渲染」时使用,和 react 组件中的 key 原理相同,点击查看帮助',
|
||||
label: {
|
||||
type: 'i18n',
|
||||
'zh-CN': '渲染唯一标识 (key)',
|
||||
'en-US': 'Render unique identifier (key)',
|
||||
},
|
||||
tip: {
|
||||
type: 'i18n',
|
||||
'zh-CN': '搭配「条件渲染」或「循环渲染」时使用,和 react 组件中的 key 原理相同,点击查看帮助',
|
||||
'en-US': 'Used with 「Conditional Rendering」or「Cycle Rendering」, the same principle as the key in the react component, click to view the help',
|
||||
},
|
||||
docUrl: 'https://www.yuque.com/lce/doc/qm75w3',
|
||||
},
|
||||
setter: [
|
||||
|
||||
@ -1,18 +1,102 @@
|
||||
/* eslint-disable max-len */
|
||||
import { isFormEvent } from '@alilc/lowcode-utils';
|
||||
import {
|
||||
focusing,
|
||||
insertChildren,
|
||||
clipboard,
|
||||
} from '@alilc/lowcode-designer';
|
||||
import { isFormEvent, isNodeSchema, isNode } from '@alilc/lowcode-utils';
|
||||
import {
|
||||
IPublicModelPluginContext,
|
||||
IPublicEnumTransformStage,
|
||||
IPublicModelNode,
|
||||
IPublicTypeNodeSchema,
|
||||
IPublicTypeNodeData,
|
||||
IPublicEnumDragObjectType,
|
||||
IPublicTypeDragNodeObject,
|
||||
} from '@alilc/lowcode-types';
|
||||
import symbols from '../modules/symbols';
|
||||
|
||||
const { nodeSymbol, documentSymbol } = symbols;
|
||||
function insertChild(
|
||||
container: IPublicModelNode,
|
||||
originalChild: IPublicModelNode | IPublicTypeNodeData,
|
||||
at?: number | null,
|
||||
): IPublicModelNode | null {
|
||||
let child = originalChild;
|
||||
if (isNode(child) && (child as IPublicModelNode).isSlotNode) {
|
||||
child = (child as IPublicModelNode).exportSchema(IPublicEnumTransformStage.Clone);
|
||||
}
|
||||
let node = null;
|
||||
if (isNode(child)) {
|
||||
node = (child as IPublicModelNode);
|
||||
container.children?.insert(node, at);
|
||||
} else {
|
||||
node = container.document?.createNode(child) || null;
|
||||
if (node) {
|
||||
container.children?.insert(node, at);
|
||||
}
|
||||
}
|
||||
|
||||
return (node as IPublicModelNode) || null;
|
||||
}
|
||||
|
||||
function insertChildren(
|
||||
container: IPublicModelNode,
|
||||
nodes: IPublicModelNode[] | IPublicTypeNodeData[],
|
||||
at?: number | null,
|
||||
): IPublicModelNode[] {
|
||||
let index = at;
|
||||
let node: any;
|
||||
const results: IPublicModelNode[] = [];
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while ((node = nodes.pop())) {
|
||||
node = insertChild(container, node, index);
|
||||
results.push(node);
|
||||
index = node.index;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得合适的插入位置
|
||||
*/
|
||||
function getSuitableInsertion(
|
||||
pluginContext: IPublicModelPluginContext,
|
||||
insertNode?: IPublicModelNode | IPublicTypeNodeSchema | IPublicTypeNodeSchema[],
|
||||
): { target: IPublicModelNode; index?: number } | null {
|
||||
const { project, material } = pluginContext;
|
||||
const activeDoc = project.currentDocument;
|
||||
if (!activeDoc) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
Array.isArray(insertNode) &&
|
||||
isNodeSchema(insertNode[0]) &&
|
||||
material.getComponentMeta(insertNode[0].componentName)?.isModal
|
||||
) {
|
||||
if (!activeDoc.root) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
target: activeDoc.root,
|
||||
};
|
||||
}
|
||||
|
||||
const focusNode = activeDoc.focusNode!;
|
||||
const nodes = activeDoc.selection.getNodes();
|
||||
const refNode = nodes.find((item) => focusNode.contains(item));
|
||||
let target;
|
||||
let index: number | undefined;
|
||||
if (!refNode || refNode === focusNode) {
|
||||
target = focusNode;
|
||||
} else if (refNode.componentMeta?.isContainer) {
|
||||
target = refNode;
|
||||
} else {
|
||||
// FIXME!!, parent maybe null
|
||||
target = refNode.parent!;
|
||||
index = refNode.index + 1;
|
||||
}
|
||||
|
||||
if (target && insertNode && !target.componentMeta?.checkNestingDown(target, insertNode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { target, index };
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function getNextForSelect(next: IPublicModelNode | null, head?: any, parent?: IPublicModelNode | null): any {
|
||||
@ -76,11 +160,73 @@ function getPrevForSelect(prev: IPublicModelNode | null, head?: any, parent?: IP
|
||||
return null;
|
||||
}
|
||||
|
||||
function getSuitablePlaceForNode(targetNode: IPublicModelNode, node: IPublicModelNode, ref: any): any {
|
||||
const { document } = targetNode;
|
||||
if (!document) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dragNodeObject: IPublicTypeDragNodeObject = {
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [node],
|
||||
};
|
||||
|
||||
const focusNode = document?.focusNode;
|
||||
// 如果节点是模态框,插入到根节点下
|
||||
if (node?.componentMeta?.isModal) {
|
||||
return { container: focusNode, ref };
|
||||
}
|
||||
const canDropInFn = document.checkNesting;
|
||||
|
||||
if (!ref && focusNode && targetNode.contains(focusNode)) {
|
||||
if (canDropInFn(focusNode, dragNodeObject)) {
|
||||
return { container: focusNode };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (targetNode.isRootNode && targetNode.children) {
|
||||
const dropElement = targetNode.children.filter((c) => {
|
||||
if (!c.isContainerNode) {
|
||||
return false;
|
||||
}
|
||||
if (canDropInFn(c, dragNodeObject)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})[0];
|
||||
|
||||
if (dropElement) {
|
||||
return { container: dropElement, ref };
|
||||
}
|
||||
|
||||
if (canDropInFn(targetNode, dragNodeObject)) {
|
||||
return { container: targetNode, ref };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (targetNode.isContainerNode) {
|
||||
if (canDropInFn(targetNode, dragNodeObject)) {
|
||||
return { container: targetNode, ref };
|
||||
}
|
||||
}
|
||||
|
||||
if (targetNode.parent) {
|
||||
return getSuitablePlaceForNode(targetNode.parent, node, { index: targetNode.index });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 注册默认的 setters
|
||||
export const builtinHotkey = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init() {
|
||||
const { hotkey, project, logger, canvas } = ctx;
|
||||
const { clipboard } = canvas;
|
||||
// hotkey binding
|
||||
hotkey.bind(['backspace', 'del'], (e: KeyboardEvent, action) => {
|
||||
logger.info(`action ${action} is triggered`);
|
||||
@ -108,11 +254,11 @@ export const builtinHotkey = (ctx: IPublicModelPluginContext) => {
|
||||
|
||||
hotkey.bind('escape', (e: KeyboardEvent, action) => {
|
||||
logger.info(`action ${action} is triggered`);
|
||||
// const currentFocus = focusing.current;
|
||||
|
||||
if (canvas.isInLiveEditing) {
|
||||
return;
|
||||
}
|
||||
const sel = focusing.focusDesigner?.currentDocument?.selection;
|
||||
const sel = project.currentDocument?.selection;
|
||||
if (isFormEvent(e) || !sel) {
|
||||
return;
|
||||
}
|
||||
@ -168,26 +314,31 @@ export const builtinHotkey = (ctx: IPublicModelPluginContext) => {
|
||||
return;
|
||||
}
|
||||
// TODO
|
||||
const designer = focusing.focusDesigner;
|
||||
const doc = project?.currentDocument;
|
||||
if (isFormEvent(e) || !designer || !doc) {
|
||||
if (isFormEvent(e) || !doc) {
|
||||
return;
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
clipboard.waitPasteData(e, ({ componentsTree }) => {
|
||||
if (componentsTree) {
|
||||
const { target, index } = designer.getSuitableInsertion(componentsTree) || {};
|
||||
const { target, index } = getSuitableInsertion(ctx, componentsTree) || {};
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
let canAddComponentsTree = componentsTree.filter((i) => {
|
||||
return (doc as any)[documentSymbol].checkNestingUp(target, i);
|
||||
let canAddComponentsTree = componentsTree.filter((node: IPublicModelNode) => {
|
||||
const dragNodeObject: IPublicTypeDragNodeObject = {
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [node],
|
||||
};
|
||||
return doc.checkNesting(target, dragNodeObject);
|
||||
});
|
||||
if (canAddComponentsTree.length === 0) return;
|
||||
if (canAddComponentsTree.length === 0) {
|
||||
return;
|
||||
}
|
||||
const nodes = insertChildren(target, canAddComponentsTree, index);
|
||||
if (nodes) {
|
||||
doc.selection.selectAll(nodes.map((o) => o.id));
|
||||
setTimeout(() => designer.activeTracker.track(nodes[0]), 10);
|
||||
setTimeout(() => canvas.activeTracker?.track(nodes[0]), 10);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -333,14 +484,14 @@ export const builtinHotkey = (ctx: IPublicModelPluginContext) => {
|
||||
const silbing = firstNode.prevSibling;
|
||||
if (silbing) {
|
||||
if (silbing.isContainerNode) {
|
||||
const place = (silbing as any)[nodeSymbol].getSuitablePlace(firstNode, null);
|
||||
const place = getSuitablePlaceForNode(silbing, firstNode, null);
|
||||
silbing.insertAfter(firstNode, place.ref, true);
|
||||
} else {
|
||||
parent.insertBefore(firstNode, silbing, true);
|
||||
}
|
||||
firstNode?.select();
|
||||
} else {
|
||||
const place = (parent as any)[nodeSymbol].getSuitablePlace(firstNode, null); // upwards
|
||||
const place = getSuitablePlaceForNode(parent, firstNode, null); // upwards
|
||||
if (place) {
|
||||
const container = place.container.internalToShellNode();
|
||||
container.insertBefore(firstNode, place.ref);
|
||||
@ -381,7 +532,7 @@ export const builtinHotkey = (ctx: IPublicModelPluginContext) => {
|
||||
}
|
||||
firstNode?.select();
|
||||
} else {
|
||||
const place = (parent as any)[nodeSymbol].getSuitablePlace(firstNode, null); // upwards
|
||||
const place = getSuitablePlaceForNode(parent, firstNode, null); // upwards
|
||||
if (place) {
|
||||
const container = place.container.internalToShellNode();
|
||||
container.insertAfter(firstNode, place.ref, true);
|
||||
|
||||
@ -11,5 +11,12 @@
|
||||
"Slots": "Slots",
|
||||
"Slot for {prop}": "Slot for {prop}",
|
||||
"Outline Tree": "Outline Tree",
|
||||
"Filter Node": "Filter Node",
|
||||
"Check All": "Check All",
|
||||
"Conditional rendering": "Conditional rendering",
|
||||
"Loop rendering": "Loop rendering",
|
||||
"Locked": "Locked",
|
||||
"Hidden": "Hidden",
|
||||
"Modal View": "Modal View",
|
||||
"Rename": "Rename"
|
||||
}
|
||||
|
||||
@ -11,5 +11,12 @@
|
||||
"Slots": "插槽",
|
||||
"Slot for {prop}": "属性 {prop} 的插槽",
|
||||
"Outline Tree": "大纲树",
|
||||
"Filter Node": "过滤节点",
|
||||
"Check All": "全选",
|
||||
"Conditional rendering": "条件渲染",
|
||||
"Loop rendering": "循环渲染",
|
||||
"Locked": "已锁定",
|
||||
"Hidden": "已隐藏",
|
||||
"Modal View": "模态视图层",
|
||||
"Rename": "重命名"
|
||||
}
|
||||
|
||||
@ -9,16 +9,16 @@ export const FilterType = {
|
||||
|
||||
export const FILTER_OPTIONS = [{
|
||||
value: FilterType.CONDITION,
|
||||
label: '条件渲染',
|
||||
label: 'Conditional rendering',
|
||||
}, {
|
||||
value: FilterType.LOOP,
|
||||
label: '循环渲染',
|
||||
label: 'Loop rendering',
|
||||
}, {
|
||||
value: FilterType.LOCKED,
|
||||
label: '已锁定',
|
||||
label: 'Locked',
|
||||
}, {
|
||||
value: FilterType.HIDDEN,
|
||||
label: '已隐藏',
|
||||
label: 'Hidden',
|
||||
}];
|
||||
|
||||
export const matchTreeNode = (
|
||||
|
||||
@ -5,10 +5,11 @@ import { Search, Checkbox, Balloon, Divider } from '@alifd/next';
|
||||
import TreeNode from '../controllers/tree-node';
|
||||
import { Tree } from '../controllers/tree';
|
||||
import { matchTreeNode, FILTER_OPTIONS } from './filter-tree';
|
||||
|
||||
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
|
||||
|
||||
export default class Filter extends Component<{
|
||||
tree: Tree;
|
||||
pluginContext: IPublicModelPluginContext;
|
||||
}, {
|
||||
keywords: string;
|
||||
filterOps: string[];
|
||||
@ -55,7 +56,7 @@ export default class Filter extends Component<{
|
||||
<Search
|
||||
hasClear
|
||||
shape="simple"
|
||||
placeholder="过滤节点"
|
||||
placeholder={this.props.pluginContext.intl('Filter Node')}
|
||||
className="lc-outline-filter-search-input"
|
||||
value={keywords}
|
||||
onChange={this.handleSearchChange}
|
||||
@ -76,7 +77,7 @@ export default class Filter extends Component<{
|
||||
indeterminate={indeterminate}
|
||||
onChange={this.handleCheckAll}
|
||||
>
|
||||
全选
|
||||
{this.props.pluginContext.intlNode('Check All')}
|
||||
</Checkbox>
|
||||
<Divider />
|
||||
<Checkbox.Group
|
||||
@ -90,7 +91,7 @@ export default class Filter extends Component<{
|
||||
value={op.value}
|
||||
key={op.value}
|
||||
>
|
||||
{op.label}
|
||||
{this.props.pluginContext.intlNode(op.label)}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
|
||||
@ -6,7 +6,6 @@ import { IPublicModelPluginContext } from '@alilc/lowcode-types';
|
||||
import Filter from './filter';
|
||||
import { TreeMaster } from '../controllers/tree-master';
|
||||
|
||||
|
||||
export class Pane extends Component<{
|
||||
config: any;
|
||||
pluginContext: IPublicModelPluginContext;
|
||||
@ -40,7 +39,7 @@ export class Pane extends Component<{
|
||||
|
||||
return (
|
||||
<div className="lc-outline-pane">
|
||||
<Filter tree={tree} />
|
||||
<Filter tree={tree} pluginContext={this.props.pluginContext} />
|
||||
<div ref={(shell) => this.controller.mount(shell)} className="lc-outline-tree-container">
|
||||
<TreeView key={tree.id} tree={tree} pluginContext={this.props.pluginContext} />
|
||||
</div>
|
||||
|
||||
@ -38,7 +38,7 @@ class ModalTreeNodeView extends Component<{
|
||||
return (
|
||||
<div className="tree-node-modal">
|
||||
<div className="tree-node-modal-title">
|
||||
<span>模态视图层</span>
|
||||
<span>{this.pluginContext.intlNode('Modal View')}</span>
|
||||
<div
|
||||
className="tree-node-modal-title-visible-icon"
|
||||
onClick={this.hideAllNodes.bind(this)}
|
||||
|
||||
4
packages/react-simulator-renderer/src/locale/en-US.json
Normal file
4
packages/react-simulator-renderer/src/locale/en-US.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"Drag and drop components or templates here": "Drag and drop components or templates here",
|
||||
"Locked elements and child elements cannot be edited": "Locked elements and child elements cannot be edited"
|
||||
}
|
||||
21
packages/react-simulator-renderer/src/locale/index.ts
Normal file
21
packages/react-simulator-renderer/src/locale/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { createElement } from 'react';
|
||||
import enUS from './en-US.json';
|
||||
import zhCN from './zh-CN.json';
|
||||
|
||||
const instance: Record<string, Record<string, string>> = {
|
||||
'zh-CN': zhCN as Record<string, string>,
|
||||
'en-US': enUS as Record<string, string>,
|
||||
};
|
||||
|
||||
export function createIntl(locale: string = 'zh-CN') {
|
||||
const intl = (id: string) => {
|
||||
return instance[locale][id];
|
||||
};
|
||||
|
||||
const intlNode = (id: string) => createElement('span', instance[locale][id]);
|
||||
|
||||
return {
|
||||
intl,
|
||||
intlNode,
|
||||
};
|
||||
}
|
||||
4
packages/react-simulator-renderer/src/locale/zh-CN.json
Normal file
4
packages/react-simulator-renderer/src/locale/zh-CN.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"Drag and drop components or templates here": "拖拽组件或模板到这里",
|
||||
"Locked elements and child elements cannot be edited": "锁定元素及子元素无法编辑"
|
||||
}
|
||||
@ -10,6 +10,7 @@ import { SimulatorRendererContainer, DocumentInstance } from './renderer';
|
||||
import { host } from './host';
|
||||
import { isRendererDetached } from './utils/misc';
|
||||
import './renderer.less';
|
||||
import { createIntl } from './locale';
|
||||
|
||||
// patch cloneElement avoid lost keyProps
|
||||
const originCloneElement = window.React.cloneElement;
|
||||
@ -130,6 +131,7 @@ class Renderer extends Component<{
|
||||
documentInstance: DocumentInstance;
|
||||
}> {
|
||||
startTime: number | null = null;
|
||||
schemaChangedSymbol = false;
|
||||
|
||||
componentDidUpdate() {
|
||||
this.recordTime();
|
||||
@ -152,8 +154,6 @@ class Renderer extends Component<{
|
||||
this.recordTime();
|
||||
}
|
||||
|
||||
schemaChangedSymbol = false;
|
||||
|
||||
getSchemaChangedSymbol = () => {
|
||||
return this.schemaChangedSymbol;
|
||||
};
|
||||
@ -172,6 +172,8 @@ class Renderer extends Component<{
|
||||
|
||||
if (!container.autoRender || isRendererDetached()) return null;
|
||||
|
||||
const { intl } = createIntl(locale);
|
||||
|
||||
return (
|
||||
<LowCodeRenderer
|
||||
locale={locale}
|
||||
@ -206,12 +208,12 @@ class Renderer extends Component<{
|
||||
(children == null || (Array.isArray(children) && !children.length)) &&
|
||||
(!viewProps.style || Object.keys(viewProps.style).length === 0)
|
||||
) {
|
||||
let defaultPlaceholder = '拖拽组件或模板到这里';
|
||||
let defaultPlaceholder = intl('Drag and drop components or templates here');
|
||||
const lockedNode = getClosestNode(leaf, (node) => {
|
||||
return node?.getExtraProp('isLocked')?.getValue() === true;
|
||||
});
|
||||
if (lockedNode) {
|
||||
defaultPlaceholder = '锁定元素及子元素无法编辑';
|
||||
defaultPlaceholder = intl('Locked elements and child elements cannot be edited');
|
||||
}
|
||||
children = (
|
||||
<div className={cn('lc-container-placeholder', { 'lc-container-locked': !!lockedNode })} style={viewProps.placeholderStyle}>
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
IPublicModelEditor,
|
||||
IPublicModelDragon,
|
||||
IPublicModelActiveTracker,
|
||||
IPublicModelClipboard,
|
||||
} from '@alilc/lowcode-types';
|
||||
import {
|
||||
ScrollTarget as InnerScrollTarget,
|
||||
@ -18,10 +19,14 @@ import {
|
||||
Dragon as ShellDragon,
|
||||
DropLocation as ShellDropLocation,
|
||||
ActiveTracker as ShellActiveTracker,
|
||||
Clipboard as ShellClipboard,
|
||||
} from '../model';
|
||||
|
||||
const clipboardInstanceSymbol = Symbol('clipboardInstace');
|
||||
|
||||
export class Canvas implements IPublicApiCanvas {
|
||||
private readonly [editorSymbol]: IPublicModelEditor;
|
||||
private readonly [clipboardInstanceSymbol]: IPublicModelClipboard;
|
||||
|
||||
private get [designerSymbol](): IDesigner {
|
||||
return this[editorSymbol].get('designer') as IDesigner;
|
||||
@ -40,8 +45,13 @@ export class Canvas implements IPublicApiCanvas {
|
||||
return Boolean(this[editorSymbol].get('designer')?.project?.simulator?.liveEditing?.editing);
|
||||
}
|
||||
|
||||
get clipboard(): IPublicModelClipboard {
|
||||
return this[clipboardInstanceSymbol];
|
||||
}
|
||||
|
||||
constructor(editor: IPublicModelEditor, readonly workspaceMode: boolean = false) {
|
||||
this[editorSymbol] = editor;
|
||||
this[clipboardInstanceSymbol] = new ShellClipboard();
|
||||
}
|
||||
|
||||
createScrollTarget(shell: HTMLDivElement): IPublicModelScrollTarget {
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
Dragon,
|
||||
SettingPropEntry,
|
||||
SettingTopEntry,
|
||||
Clipboard,
|
||||
} from './model';
|
||||
import {
|
||||
Project,
|
||||
@ -57,4 +58,5 @@ export {
|
||||
Logger,
|
||||
Canvas,
|
||||
Workspace,
|
||||
Clipboard,
|
||||
};
|
||||
22
packages/shell/src/model/clipboard.ts
Normal file
22
packages/shell/src/model/clipboard.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { IPublicModelClipboard } from '@alilc/lowcode-types';
|
||||
import { clipboardSymbol } from '../symbols';
|
||||
import { IClipboard, clipboard } from '@alilc/lowcode-designer';
|
||||
|
||||
export class Clipboard implements IPublicModelClipboard {
|
||||
private readonly [clipboardSymbol]: IClipboard;
|
||||
|
||||
constructor() {
|
||||
this[clipboardSymbol] = clipboard;
|
||||
}
|
||||
|
||||
setData(data: any): void {
|
||||
this[clipboardSymbol].setData(data);
|
||||
}
|
||||
|
||||
waitPasteData(
|
||||
keyboardEvent: KeyboardEvent,
|
||||
cb: (data: any, clipboardEvent: ClipboardEvent) => void,
|
||||
): void {
|
||||
this[clipboardSymbol].waitPasteData(keyboardEvent, cb);
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,6 @@ import {
|
||||
GlobalEvent,
|
||||
IPublicModelDocumentModel,
|
||||
IPublicTypeOnChangeOptions,
|
||||
IPublicModelDragObject,
|
||||
IPublicTypeDragNodeObject,
|
||||
IPublicTypeDragNodeDataObject,
|
||||
IPublicModelNode,
|
||||
@ -227,9 +226,11 @@ export class DocumentModel implements IPublicModelDocumentModel {
|
||||
dropTarget: IPublicModelNode,
|
||||
dragObject: IPublicTypeDragNodeObject | IPublicTypeDragNodeDataObject,
|
||||
): boolean {
|
||||
let innerDragObject: IPublicModelDragObject = dragObject;
|
||||
let innerDragObject = dragObject;
|
||||
if (isDragNodeObject(dragObject)) {
|
||||
innerDragObject.nodes = innerDragObject.nodes.map((node: Node) => (node[nodeSymbol] || node));
|
||||
innerDragObject.nodes = innerDragObject.nodes?.map(
|
||||
(node: IPublicModelNode) => ((node as any)[nodeSymbol] || node),
|
||||
);
|
||||
}
|
||||
return this[documentSymbol].checkNesting(
|
||||
((dropTarget as any)[nodeSymbol] || dropTarget) as any,
|
||||
|
||||
@ -17,4 +17,5 @@ export * from './setting-top-entry';
|
||||
export * from './resource';
|
||||
export * from './active-tracker';
|
||||
export * from './plugin-instance';
|
||||
export * from './window';
|
||||
export * from './window';
|
||||
export * from './clipboard';
|
||||
@ -30,4 +30,5 @@ export const workspaceSymbol = Symbol('workspace');
|
||||
export const windowSymbol = Symbol('window');
|
||||
export const pluginInstanceSymbol = Symbol('plugin-instance');
|
||||
export const resourceTypeSymbol = Symbol('resourceType');
|
||||
export const resourceSymbol = Symbol('resource');
|
||||
export const resourceSymbol = Symbol('resource');
|
||||
export const clipboardSymbol = Symbol('clipboard');
|
||||
@ -1,4 +1,4 @@
|
||||
import { IPublicModelDragon, IPublicModelDropLocation, IPublicModelScrollTarget, IPublicModelScrollable, IPublicModelScroller, IPublicModelActiveTracker } from '../model';
|
||||
import { IPublicModelDragon, IPublicModelDropLocation, IPublicModelScrollTarget, IPublicModelScrollable, IPublicModelScroller, IPublicModelActiveTracker, IPublicModelClipboard } from '../model';
|
||||
import { IPublicTypeLocationData } from '../type';
|
||||
|
||||
/**
|
||||
@ -54,4 +54,12 @@ export interface IPublicApiCanvas {
|
||||
* @since v1.1.0
|
||||
*/
|
||||
get isInLiveEditing(): boolean;
|
||||
|
||||
/**
|
||||
* 获取全局剪贴板实例
|
||||
* get clipboard instance
|
||||
*
|
||||
* @since v1.1.0
|
||||
*/
|
||||
get clipboard(): IPublicModelClipboard;
|
||||
}
|
||||
|
||||
25
packages/types/src/shell/model/clipboard.ts
Normal file
25
packages/types/src/shell/model/clipboard.ts
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
export interface IPublicModelClipboard {
|
||||
|
||||
/**
|
||||
* 给剪贴板赋值
|
||||
* set data to clipboard
|
||||
*
|
||||
* @param {*} data
|
||||
* @since v1.1.0
|
||||
*/
|
||||
setData(data: any): void;
|
||||
|
||||
/**
|
||||
* 设置剪贴板数据设置的回调
|
||||
* set callback for clipboard provide paste data
|
||||
*
|
||||
* @param {KeyboardEvent} keyboardEvent
|
||||
* @param {(data: any, clipboardEvent: ClipboardEvent) => void} cb
|
||||
* @since v1.1.0
|
||||
*/
|
||||
waitPasteData(
|
||||
keyboardEvent: KeyboardEvent,
|
||||
cb: (data: any, clipboardEvent: ClipboardEvent) => void,
|
||||
): void;
|
||||
}
|
||||
@ -29,3 +29,4 @@ export * from './preference';
|
||||
export * from './plugin-instance';
|
||||
export * from './sensor';
|
||||
export * from './resource';
|
||||
export * from './clipboard';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
export interface IPublicResourceData {
|
||||
resourceName: string;
|
||||
title: string;
|
||||
category: string;
|
||||
category?: string;
|
||||
options: {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable max-len */
|
||||
import {
|
||||
Editor,
|
||||
engineConfig, Setters as InnerSetters,
|
||||
@ -33,8 +31,8 @@ import {
|
||||
IPublicTypePluginMeta,
|
||||
} from '@alilc/lowcode-types';
|
||||
import { getLogger } from '@alilc/lowcode-utils';
|
||||
import { Workspace as InnerWorkspace } from './workspace';
|
||||
import { EditorWindow } from './editor-window/context';
|
||||
import { Workspace as InnerWorkspace } from '../workspace';
|
||||
import { EditorWindow } from '../window';
|
||||
|
||||
export class BasicContext {
|
||||
skeleton: Skeleton;
|
||||
@ -2,8 +2,8 @@ import { makeObservable, obx } from '@alilc/lowcode-editor-core';
|
||||
import { IPublicEditorViewConfig, IPublicTypeEditorView } from '@alilc/lowcode-types';
|
||||
import { flow } from 'mobx';
|
||||
import { Workspace as InnerWorkspace } from '../workspace';
|
||||
import { BasicContext } from '../base-context';
|
||||
import { EditorWindow } from '../editor-window/context';
|
||||
import { BasicContext } from './base-context';
|
||||
import { EditorWindow } from '../window';
|
||||
import { getWebviewPlugin } from '../inner-plugins/webview';
|
||||
|
||||
export class Context extends BasicContext {
|
||||
@ -17,6 +17,10 @@ export class Context extends BasicContext {
|
||||
|
||||
@obx isInit: boolean = false;
|
||||
|
||||
get active() {
|
||||
return this._activate;
|
||||
}
|
||||
|
||||
init = flow(function* (this: any) {
|
||||
if (this.viewType === 'webview') {
|
||||
const url = yield this.instance?.url?.();
|
||||
@ -44,10 +48,6 @@ export class Context extends BasicContext {
|
||||
this.innerHotkey.activate(this._activate);
|
||||
};
|
||||
|
||||
get active() {
|
||||
return this._activate;
|
||||
}
|
||||
|
||||
async save() {
|
||||
return await this.instance?.save?.();
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
export { Workspace } from './workspace';
|
||||
export * from './editor-window/context';
|
||||
export * from './window';
|
||||
export * from './layouts/workbench';
|
||||
export { Resource } from './resource';
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component } from 'react';
|
||||
import { TipContainer, observer } from '@alilc/lowcode-editor-core';
|
||||
import { EditorWindowView } from '../editor-window/view';
|
||||
import { WindowView } from '../view/window-view';
|
||||
import classNames from 'classnames';
|
||||
import TopArea from './top-area';
|
||||
import LeftArea from './left-area';
|
||||
@ -46,9 +46,9 @@ export class Workbench extends Component<{
|
||||
<div className="lc-workspace-workbench-window">
|
||||
{
|
||||
workspace.windows.map(d => (
|
||||
<EditorWindowView
|
||||
<WindowView
|
||||
active={d.id === workspace.window.id}
|
||||
editorWindow={d}
|
||||
window={d}
|
||||
key={d.id}
|
||||
/>
|
||||
))
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { IPublicTypeEditorView, IPublicModelResource, IPublicResourceData, IPublicResourceTypeConfig } from '@alilc/lowcode-types';
|
||||
import { Logger } from '@alilc/lowcode-utils';
|
||||
import { BasicContext } from './base-context';
|
||||
import { BasicContext } from './context/base-context';
|
||||
import { ResourceType } from './resource-type';
|
||||
import { Workspace as InnerWorkSpace } from './workspace';
|
||||
|
||||
@ -13,20 +13,6 @@ export class Resource implements IPublicModelResource {
|
||||
|
||||
editorViewMap: Map<string, IPublicTypeEditorView> = new Map<string, IPublicTypeEditorView>();
|
||||
|
||||
constructor(readonly resourceData: IPublicResourceData, readonly resourceType: ResourceType, workspace: InnerWorkSpace) {
|
||||
this.context = new BasicContext(workspace, '');
|
||||
this.resourceTypeInstance = resourceType.resourceTypeModel(this.context, {});
|
||||
this.init();
|
||||
if (this.resourceTypeInstance.editorViews) {
|
||||
this.resourceTypeInstance.editorViews.forEach((d: any) => {
|
||||
this.editorViewMap.set(d.viewName, d);
|
||||
});
|
||||
}
|
||||
if (!resourceType) {
|
||||
logger.error(`resourceType[${resourceType}] is unValid.`);
|
||||
}
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.resourceType.name;
|
||||
}
|
||||
@ -55,6 +41,24 @@ export class Resource implements IPublicModelResource {
|
||||
return this.resourceData?.category;
|
||||
}
|
||||
|
||||
get skeleton() {
|
||||
return this.context.innerSkeleton;
|
||||
}
|
||||
|
||||
constructor(readonly resourceData: IPublicResourceData, readonly resourceType: ResourceType, workspace: InnerWorkSpace) {
|
||||
this.context = new BasicContext(workspace, `resource-${resourceData.resourceName || resourceType.name}`);
|
||||
this.resourceTypeInstance = resourceType.resourceTypeModel(this.context, {});
|
||||
this.init();
|
||||
if (this.resourceTypeInstance.editorViews) {
|
||||
this.resourceTypeInstance.editorViews.forEach((d: any) => {
|
||||
this.editorViewMap.set(d.viewName, d);
|
||||
});
|
||||
}
|
||||
if (!resourceType) {
|
||||
logger.error(`resourceType[${resourceType}] is unValid.`);
|
||||
}
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.resourceTypeInstance.init?.();
|
||||
await this.context.innerPlugins.init();
|
||||
@ -63,6 +67,7 @@ export class Resource implements IPublicModelResource {
|
||||
async import(schema: any) {
|
||||
return await this.resourceTypeInstance.import?.(schema);
|
||||
}
|
||||
|
||||
async save(value: any) {
|
||||
return await this.resourceTypeInstance.save?.(value);
|
||||
}
|
||||
|
||||
@ -4,9 +4,9 @@ import {
|
||||
Workbench,
|
||||
} from '@alilc/lowcode-editor-skeleton';
|
||||
import { PureComponent } from 'react';
|
||||
import { Context } from './context';
|
||||
import { Context } from '../context/view-context';
|
||||
|
||||
export * from '../base-context';
|
||||
export * from '../context/base-context';
|
||||
|
||||
@observer
|
||||
export class EditorView extends PureComponent<{
|
||||
@ -23,13 +23,11 @@ export class EditorView extends PureComponent<{
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Workbench
|
||||
skeleton={skeleton}
|
||||
className={active ? 'active engine-editor-view' : 'engine-editor-view'}
|
||||
topAreaItemClassName="engine-actionitem"
|
||||
/>
|
||||
</>
|
||||
<Workbench
|
||||
skeleton={skeleton}
|
||||
className={active ? 'active engine-editor-view' : 'engine-editor-view'}
|
||||
topAreaItemClassName="engine-actionitem"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
14
packages/workspace/src/view/resource-view.less
Normal file
14
packages/workspace/src/view/resource-view.less
Normal file
@ -0,0 +1,14 @@
|
||||
.workspace-resource-view {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
flex-direction: column;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.workspace-editor-body {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
36
packages/workspace/src/view/resource-view.tsx
Normal file
36
packages/workspace/src/view/resource-view.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { PureComponent } from 'react';
|
||||
import { EditorView } from './editor-view';
|
||||
import { observer } from '@alilc/lowcode-editor-core';
|
||||
import TopArea from '../layouts/top-area';
|
||||
import { Resource } from '../resource';
|
||||
import { EditorWindow } from '../window';
|
||||
import './resource-view.less';
|
||||
|
||||
@observer
|
||||
export class ResourceView extends PureComponent<{
|
||||
window: EditorWindow;
|
||||
resource: Resource;
|
||||
}, any> {
|
||||
render() {
|
||||
const { skeleton } = this.props.resource;
|
||||
const { editorViews } = this.props.window;
|
||||
return (
|
||||
<div className="workspace-resource-view">
|
||||
<TopArea area={skeleton.topArea} itemClassName="engine-actionitem" />
|
||||
<div className="workspace-editor-body">
|
||||
{
|
||||
Array.from(editorViews.values()).map((editorView: any) => {
|
||||
return (
|
||||
<EditorView
|
||||
key={editorView.name}
|
||||
active={editorView.active}
|
||||
editorView={editorView}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,17 @@
|
||||
import { PureComponent } from 'react';
|
||||
import { EditorView } from '../editor-view/view';
|
||||
import { ResourceView } from './resource-view';
|
||||
import { engineConfig, observer } from '@alilc/lowcode-editor-core';
|
||||
import { EditorWindow } from './context';
|
||||
import { EditorWindow } from '../window';
|
||||
import { BuiltinLoading } from '@alilc/lowcode-designer';
|
||||
|
||||
@observer
|
||||
export class EditorWindowView extends PureComponent<{
|
||||
editorWindow: EditorWindow;
|
||||
export class WindowView extends PureComponent<{
|
||||
window: EditorWindow;
|
||||
active: boolean;
|
||||
}, any> {
|
||||
render() {
|
||||
const { active } = this.props;
|
||||
const { editorView, editorViews } = this.props.editorWindow;
|
||||
const { editorView, resource } = this.props.window;
|
||||
if (!editorView) {
|
||||
const Loading = engineConfig.get('loadingComponent', BuiltinLoading);
|
||||
return (
|
||||
@ -23,17 +23,10 @@ export class EditorWindowView extends PureComponent<{
|
||||
|
||||
return (
|
||||
<div className={`workspace-engine-main ${active ? 'active' : ''}`}>
|
||||
{
|
||||
Array.from(editorViews.values()).map((editorView: any) => {
|
||||
return (
|
||||
<EditorView
|
||||
key={editorView.name}
|
||||
active={editorView.active}
|
||||
editorView={editorView}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
<ResourceView
|
||||
resource={resource}
|
||||
window={this.props.window}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import { uniqueId } from '@alilc/lowcode-utils';
|
||||
import { makeObservable, obx } from '@alilc/lowcode-editor-core';
|
||||
import { Context } from '../editor-view/context';
|
||||
import { Workspace } from '../workspace';
|
||||
import { Resource } from '../resource';
|
||||
import { Context } from './context/view-context';
|
||||
import { Workspace } from './workspace';
|
||||
import { Resource } from './resource';
|
||||
|
||||
export class EditorWindow {
|
||||
id: string = uniqueId('window');
|
||||
@ -2,8 +2,8 @@ import { Designer } from '@alilc/lowcode-designer';
|
||||
import { createModuleEventBus, Editor, IEventBus, makeObservable, obx } from '@alilc/lowcode-editor-core';
|
||||
import { Plugins } from '@alilc/lowcode-shell';
|
||||
import { IPublicApiWorkspace, IPublicResourceList, IPublicTypeResourceType } from '@alilc/lowcode-types';
|
||||
import { BasicContext } from './base-context';
|
||||
import { EditorWindow } from './editor-window/context';
|
||||
import { BasicContext } from './context/base-context';
|
||||
import { EditorWindow } from './window';
|
||||
import { Resource } from './resource';
|
||||
import { ResourceType } from './resource-type';
|
||||
|
||||
@ -20,6 +20,12 @@ export class Workspace implements IPublicApiWorkspace {
|
||||
|
||||
private emitter: IEventBus = createModuleEventBus('workspace');
|
||||
|
||||
private _isActive = false;
|
||||
|
||||
private resourceTypeMap: Map<string, ResourceType> = new Map();
|
||||
|
||||
private resourceList: Resource[] = [];
|
||||
|
||||
get skeleton() {
|
||||
return this.context.innerSkeleton;
|
||||
}
|
||||
@ -28,7 +34,17 @@ export class Workspace implements IPublicApiWorkspace {
|
||||
return this.context.innerPlugins;
|
||||
}
|
||||
|
||||
private _isActive = false;
|
||||
get isActive() {
|
||||
return this._isActive;
|
||||
}
|
||||
|
||||
get defaultResourceType(): ResourceType | null {
|
||||
if (this.resourceTypeMap.size >= 1) {
|
||||
return Array.from(this.resourceTypeMap.values())[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
windows: EditorWindow[] = [];
|
||||
|
||||
@ -36,10 +52,6 @@ export class Workspace implements IPublicApiWorkspace {
|
||||
|
||||
@obx.ref window: EditorWindow;
|
||||
|
||||
private resourceTypeMap: Map<string, ResourceType> = new Map();
|
||||
|
||||
private resourceList: Resource[] = [];
|
||||
|
||||
constructor(
|
||||
readonly registryInnerPlugin: (designer: Designer, editor: Editor, plugins: Plugins) => Promise<void>,
|
||||
readonly shellModelFactory: any,
|
||||
@ -66,10 +78,6 @@ export class Workspace implements IPublicApiWorkspace {
|
||||
this.emitChangeActiveWindow();
|
||||
}
|
||||
|
||||
get isActive() {
|
||||
return this._isActive;
|
||||
}
|
||||
|
||||
setActive(value: boolean) {
|
||||
this._isActive = value;
|
||||
}
|
||||
@ -105,14 +113,6 @@ export class Workspace implements IPublicApiWorkspace {
|
||||
return this.resourceTypeMap.get(resourceName)!;
|
||||
}
|
||||
|
||||
get defaultResourceType(): ResourceType | null {
|
||||
if (this.resourceTypeMap.size >= 1) {
|
||||
return Array.from(this.resourceTypeMap.values())[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
removeResourceType(resourceName: string) {
|
||||
if (this.resourceTypeMap.has(resourceName)) {
|
||||
this.resourceTypeMap.delete(resourceName);
|
||||
@ -153,13 +153,17 @@ export class Workspace implements IPublicApiWorkspace {
|
||||
console.error(`${name} is not available`);
|
||||
return;
|
||||
}
|
||||
const filterWindows = this.windows.filter(d => (d.resource.name === name && d.title == title));
|
||||
const filterWindows = this.windows.filter(d => (d.resource.name === name && d.resource.title == title));
|
||||
if (filterWindows && filterWindows.length) {
|
||||
this.window = filterWindows[0];
|
||||
this.emitChangeActiveWindow();
|
||||
return;
|
||||
}
|
||||
const resource = new Resource({}, resourceType, this);
|
||||
const resource = new Resource({
|
||||
resourceName: name,
|
||||
title,
|
||||
options,
|
||||
}, resourceType, this);
|
||||
this.window = new EditorWindow(resource, this, title, options);
|
||||
this.windows.push(this.window);
|
||||
this.editorWindowMap.set(this.window.id, this.window);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user