mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-04-20 20:38:06 +00:00
move setting to designer
This commit is contained in:
parent
f1b686bad5
commit
8a768177ce
@ -8,6 +8,7 @@ import {
|
|||||||
obx,
|
obx,
|
||||||
computed,
|
computed,
|
||||||
autorun,
|
autorun,
|
||||||
|
IEditor,
|
||||||
} from '@ali/lowcode-globals';
|
} from '@ali/lowcode-globals';
|
||||||
import { Project } from '../project';
|
import { Project } from '../project';
|
||||||
import { Node, DocumentModel, insertChildren, isRootNode, ParentalNode } from '../document';
|
import { Node, DocumentModel, insertChildren, isRootNode, ParentalNode } from '../document';
|
||||||
@ -20,6 +21,7 @@ import { Hovering } from './hovering';
|
|||||||
import { DropLocation, LocationData, isLocationChildrenDetail } from './location';
|
import { DropLocation, LocationData, isLocationChildrenDetail } from './location';
|
||||||
import { OffsetObserver, createOffsetObserver } from './offset-observer';
|
import { OffsetObserver, createOffsetObserver } from './offset-observer';
|
||||||
import { focusing } from './focusing';
|
import { focusing } from './focusing';
|
||||||
|
import { SettingTopEntry } from './setting';
|
||||||
|
|
||||||
export interface DesignerProps {
|
export interface DesignerProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -198,6 +200,10 @@ export class Designer {
|
|||||||
return createOffsetObserver(nodeInstance);
|
return createOffsetObserver(nodeInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createSettingEntry(editor: IEditor, nodes: Node[]) {
|
||||||
|
return new SettingTopEntry(editor, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得合适的插入位置
|
* 获得合适的插入位置
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -6,3 +6,4 @@ export * from './hovering';
|
|||||||
export * from './location';
|
export * from './location';
|
||||||
export * from './offset-observer';
|
export * from './offset-observer';
|
||||||
export * from './scroller';
|
export * from './scroller';
|
||||||
|
export * from './setting';
|
||||||
|
|||||||
3
packages/designer/src/designer/setting/index.ts
Normal file
3
packages/designer/src/designer/setting/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './setting-field';
|
||||||
|
export * from './setting-top-entry';
|
||||||
|
export * from './setting-entry';
|
||||||
17
packages/designer/src/designer/setting/setting-entry.ts
Normal file
17
packages/designer/src/designer/setting/setting-entry.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { SettingTarget } from '@ali/lowcode-globals';
|
||||||
|
import { ComponentMeta } from '../../component-meta';
|
||||||
|
import { Designer } from '../designer';
|
||||||
|
import { Node } from '../../document';
|
||||||
|
|
||||||
|
export interface SettingEntry extends SettingTarget {
|
||||||
|
readonly nodes: Node[];
|
||||||
|
readonly componentMeta: ComponentMeta | null;
|
||||||
|
readonly designer: Designer;
|
||||||
|
|
||||||
|
// 顶端
|
||||||
|
readonly top: SettingEntry;
|
||||||
|
// 父级
|
||||||
|
readonly parent: SettingEntry;
|
||||||
|
|
||||||
|
get(propName: string | number): SettingEntry;
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { TitleContent, computed, isDynamicSetter, SetterType, DynamicSetter, FieldExtraProps, FieldConfig, CustomView, isCustomView, obx } from '@ali/lowcode-globals';
|
import { TitleContent, computed, isDynamicSetter, SetterType, DynamicSetter, FieldExtraProps, FieldConfig, CustomView, isCustomView, obx } from '@ali/lowcode-globals';
|
||||||
import { Transducer } from '../utils';
|
import { Transducer } from './utils';
|
||||||
import { SettingPropEntry } from './setting-entry';
|
import { SettingPropEntry } from './setting-prop-entry';
|
||||||
import { SettingTarget } from './setting-target';
|
import { SettingEntry } from './setting-entry';
|
||||||
|
|
||||||
export class SettingField extends SettingPropEntry implements SettingTarget {
|
export class SettingField extends SettingPropEntry implements SettingEntry {
|
||||||
readonly isSettingField = true;
|
readonly isSettingField = true;
|
||||||
readonly isRequired: boolean;
|
readonly isRequired: boolean;
|
||||||
readonly transducer: Transducer;
|
readonly transducer: Transducer;
|
||||||
@ -35,7 +35,7 @@ export class SettingField extends SettingPropEntry implements SettingTarget {
|
|||||||
this._expanded = value;
|
this._expanded = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(readonly parent: SettingTarget, config: FieldConfig) {
|
constructor(readonly parent: SettingEntry, config: FieldConfig) {
|
||||||
super(parent, config.name, config.type);
|
super(parent, config.name, config.type);
|
||||||
|
|
||||||
const { title, items, setter, extraProps, ...rest } = config;
|
const { title, items, setter, extraProps, ...rest } = config;
|
||||||
@ -53,6 +53,7 @@ export class SettingField extends SettingPropEntry implements SettingTarget {
|
|||||||
this.initItems(items);
|
this.initItems(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// compatiable old config
|
||||||
this.transducer = new Transducer(this, { setter });
|
this.transducer = new Transducer(this, { setter });
|
||||||
}
|
}
|
||||||
|
|
||||||
192
packages/designer/src/designer/setting/setting-prop-entry.ts
Normal file
192
packages/designer/src/designer/setting/setting-prop-entry.ts
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import { obx, uniqueId, computed, IEditor } from '@ali/lowcode-globals';
|
||||||
|
import { SettingEntry } from './setting-entry';
|
||||||
|
import { Node } from '../../document';
|
||||||
|
import { ComponentMeta } from '../../component-meta';
|
||||||
|
import { Designer } from '../designer';
|
||||||
|
|
||||||
|
export class SettingPropEntry implements SettingEntry {
|
||||||
|
// === static properties ===
|
||||||
|
readonly editor: IEditor;
|
||||||
|
readonly isSameComponent: boolean;
|
||||||
|
readonly isMultiple: boolean;
|
||||||
|
readonly isSingle: boolean;
|
||||||
|
readonly nodes: Node[];
|
||||||
|
readonly componentMeta: ComponentMeta | null;
|
||||||
|
readonly designer: Designer;
|
||||||
|
readonly top: SettingEntry;
|
||||||
|
readonly isGroup: boolean;
|
||||||
|
readonly type: 'field' | 'group';
|
||||||
|
readonly id = uniqueId('entry');
|
||||||
|
|
||||||
|
// ==== dynamic properties ====
|
||||||
|
@obx.ref private _name: string | number;
|
||||||
|
get name() {
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
@computed get path() {
|
||||||
|
const path = this.parent.path.slice();
|
||||||
|
if (this.type === 'field') {
|
||||||
|
path.push(this.name);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
extraProps: any = {};
|
||||||
|
|
||||||
|
constructor(readonly parent: SettingEntry, name: string | number, type?: 'field' | 'group') {
|
||||||
|
if (type == null) {
|
||||||
|
const c = typeof name === 'string' ? name.substr(0, 1) : '';
|
||||||
|
if (c === '#') {
|
||||||
|
this.type = 'group';
|
||||||
|
} else {
|
||||||
|
this.type = 'field';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
// initial self properties
|
||||||
|
this._name = name;
|
||||||
|
this.isGroup = this.type === 'group';
|
||||||
|
|
||||||
|
// copy parent static properties
|
||||||
|
this.editor = parent.editor;
|
||||||
|
this.nodes = parent.nodes;
|
||||||
|
this.componentMeta = parent.componentMeta;
|
||||||
|
this.isSameComponent = parent.isSameComponent;
|
||||||
|
this.isMultiple = parent.isMultiple;
|
||||||
|
this.isSingle = parent.isSingle;
|
||||||
|
this.designer = parent.designer;
|
||||||
|
this.top = parent.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
setKey(key: string | number) {
|
||||||
|
if (this.type !== 'field') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const propName = this.path.join('.');
|
||||||
|
let l = this.nodes.length;
|
||||||
|
while (l-- > 1) {
|
||||||
|
this.nodes[l].getProp(propName, true)!.key = key;
|
||||||
|
}
|
||||||
|
this._name = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
getKey() {
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
if (this.type !== 'field') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const propName = this.path.join('.');
|
||||||
|
let l = this.nodes.length;
|
||||||
|
while (l-- > 1) {
|
||||||
|
this.nodes[l].getProp(propName)?.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 当前属性读写 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前属性值
|
||||||
|
*/
|
||||||
|
@computed getValue(): any {
|
||||||
|
let val: any = null;
|
||||||
|
if (this.type === 'field') {
|
||||||
|
val = this.parent.getPropValue(this.name);
|
||||||
|
}
|
||||||
|
const { getValue } = this.extraProps;
|
||||||
|
return getValue ? getValue(this, val) : val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前属性值
|
||||||
|
*/
|
||||||
|
setValue(val: any) {
|
||||||
|
if (this.type === 'field') {
|
||||||
|
this.parent.setPropValue(this.name, val);
|
||||||
|
}
|
||||||
|
const { setValue } = this.extraProps;
|
||||||
|
if (setValue) {
|
||||||
|
setValue(this, val);
|
||||||
|
}
|
||||||
|
// TODO: emit value change
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取子项
|
||||||
|
*/
|
||||||
|
get(propName: string | number) {
|
||||||
|
const path = this.path.concat(propName).join('.');
|
||||||
|
return this.top.get(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置子级属性值
|
||||||
|
*/
|
||||||
|
setPropValue(propName: string | number, value: any) {
|
||||||
|
const path = this.path.concat(propName).join('.');
|
||||||
|
this.top.setPropValue(path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取子级属性值
|
||||||
|
*/
|
||||||
|
getPropValue(propName: string | number): any {
|
||||||
|
return this.top.getPropValue(this.path.concat(propName).join('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取顶层附属属性值
|
||||||
|
*/
|
||||||
|
getExtraPropValue(propName: string) {
|
||||||
|
return this.top.getExtraPropValue(propName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置顶层附属属性值
|
||||||
|
*/
|
||||||
|
setExtraPropValue(propName: string, value: any) {
|
||||||
|
this.top.setExtraPropValue(propName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======= compatibles for vision ======
|
||||||
|
getNode() {
|
||||||
|
return this.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return this.path.join('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
getProps() {
|
||||||
|
return this.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
onValueChange() {
|
||||||
|
// TODO:
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultValue() {
|
||||||
|
return this.extraProps.defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
isIgnore() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
getConfig<K extends keyof IPropConfig>(configName?: K): IPropConfig[K] | IPropConfig {
|
||||||
|
if (configName) {
|
||||||
|
return this.config[configName];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
218
packages/designer/src/designer/setting/setting-top-entry.ts
Normal file
218
packages/designer/src/designer/setting/setting-top-entry.ts
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
import { EventEmitter } from 'events';
|
||||||
|
import { CustomView, computed, isCustomView, IEditor } from '@ali/lowcode-globals';
|
||||||
|
import { SettingEntry } from './setting-entry';
|
||||||
|
import { SettingField } from './setting-field';
|
||||||
|
import { SettingPropEntry } from './setting-prop-entry';
|
||||||
|
import { Node } from '../../document';
|
||||||
|
import { ComponentMeta } from '../../component-meta';
|
||||||
|
import { Designer } from '../designer';
|
||||||
|
|
||||||
|
function generateSessionId(nodes: Node[]) {
|
||||||
|
return nodes
|
||||||
|
.map((node) => node.id)
|
||||||
|
.sort()
|
||||||
|
.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SettingTopEntry implements SettingEntry {
|
||||||
|
private emitter = new EventEmitter();
|
||||||
|
private _items: Array<SettingField | CustomView> = [];
|
||||||
|
private _componentMeta: ComponentMeta | null = null;
|
||||||
|
private _isSame: boolean = true;
|
||||||
|
readonly path = [];
|
||||||
|
readonly top = this;
|
||||||
|
readonly parent = this;
|
||||||
|
|
||||||
|
get componentMeta() {
|
||||||
|
return this._componentMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
get items() {
|
||||||
|
return this._items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同样的
|
||||||
|
*/
|
||||||
|
get isSameComponent(): boolean {
|
||||||
|
return this._isSame;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一个
|
||||||
|
*/
|
||||||
|
get isSingle(): boolean {
|
||||||
|
return this.nodes.length === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多个
|
||||||
|
*/
|
||||||
|
get isMultiple(): boolean {
|
||||||
|
return this.nodes.length > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly id: string;
|
||||||
|
readonly first: Node;
|
||||||
|
readonly designer: Designer;
|
||||||
|
|
||||||
|
constructor(readonly editor: IEditor, readonly nodes: Node[]) {
|
||||||
|
if (nodes.length < 1) {
|
||||||
|
throw new ReferenceError('nodes should not be empty');
|
||||||
|
}
|
||||||
|
this.id = generateSessionId(nodes);
|
||||||
|
this.first = nodes[0];
|
||||||
|
this.designer = this.first.document.designer;
|
||||||
|
|
||||||
|
// setups
|
||||||
|
this.setupComponentMeta();
|
||||||
|
|
||||||
|
// clear fields
|
||||||
|
this.setupItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupComponentMeta() {
|
||||||
|
// todo: enhance compile a temp configure.compiled
|
||||||
|
const first = this.first;
|
||||||
|
const meta = first.componentMeta;
|
||||||
|
const l = this.nodes.length;
|
||||||
|
let theSame = true;
|
||||||
|
for (let i = 1; i < l; i++) {
|
||||||
|
const other = this.nodes[i];
|
||||||
|
if (other.componentMeta !== meta) {
|
||||||
|
theSame = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (theSame) {
|
||||||
|
this._isSame = true;
|
||||||
|
this._componentMeta = meta;
|
||||||
|
} else {
|
||||||
|
this._isSame = false;
|
||||||
|
this._componentMeta = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupItems() {
|
||||||
|
if (this.componentMeta) {
|
||||||
|
this._items = this.componentMeta.configure.map((item) => {
|
||||||
|
if (isCustomView(item)) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
return new SettingField(this, item as any);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前属性值
|
||||||
|
*/
|
||||||
|
@computed getValue(): any {
|
||||||
|
this.first.propsData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前属性值
|
||||||
|
*/
|
||||||
|
setValue(val: any) {
|
||||||
|
this.setProps(val);
|
||||||
|
// TODO: emit value change
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取子项
|
||||||
|
*/
|
||||||
|
get(propName: string | number): SettingPropEntry {
|
||||||
|
return new SettingPropEntry(this, propName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置子级属性值
|
||||||
|
*/
|
||||||
|
setPropValue(propName: string, value: any) {
|
||||||
|
this.nodes.forEach((node) => {
|
||||||
|
node.setPropValue(propName, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取子级属性值
|
||||||
|
*/
|
||||||
|
getPropValue(propName: string): any {
|
||||||
|
return this.first.getProp(propName, true)?.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取顶层附属属性值
|
||||||
|
*/
|
||||||
|
getExtraPropValue(propName: string) {
|
||||||
|
return this.first.getExtraProp(propName, false)?.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置顶层附属属性值
|
||||||
|
*/
|
||||||
|
setExtraPropValue(propName: string, value: any) {
|
||||||
|
this.nodes.forEach((node) => {
|
||||||
|
node.getExtraProp(propName, true)?.setValue(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置多个属性值,替换原有值
|
||||||
|
setProps(data: object) {
|
||||||
|
this.nodes.forEach((node) => {
|
||||||
|
node.setProps(data as any);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置多个属性值,和原有值合并
|
||||||
|
mergeProps(data: object) {
|
||||||
|
this.nodes.forEach((node) => {
|
||||||
|
node.mergeProps(data as any);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private disposeItems() {
|
||||||
|
this._items.forEach((item) => isPurgeable(item) && item.purge());
|
||||||
|
this._items = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
purge() {
|
||||||
|
this.disposeItems();
|
||||||
|
this.emitter.removeAllListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==== compatibles for vision =====
|
||||||
|
getProp(propName: string | number) {
|
||||||
|
return this.get(propName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== copy some Node api =====
|
||||||
|
// `VE.Node.getProps`
|
||||||
|
getStatus() {
|
||||||
|
|
||||||
|
}
|
||||||
|
setStatus() {
|
||||||
|
|
||||||
|
}
|
||||||
|
getChildren() {
|
||||||
|
// this.nodes.map()
|
||||||
|
}
|
||||||
|
getDOMNode() {
|
||||||
|
|
||||||
|
}
|
||||||
|
getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
getPage() {
|
||||||
|
return this.first.document;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Purgeable {
|
||||||
|
purge(): void;
|
||||||
|
}
|
||||||
|
function isPurgeable(obj: any): obj is Purgeable {
|
||||||
|
return obj && obj.purge;
|
||||||
|
}
|
||||||
41
packages/designer/src/designer/setting/utils.js
Normal file
41
packages/designer/src/designer/setting/utils.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
function getHotterFromSetter(setter) {
|
||||||
|
return setter && (setter.Hotter || (setter.type && setter.type.Hotter)) || []; // eslint-disable-line
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTransducerFromSetter(setter) {
|
||||||
|
return setter && (
|
||||||
|
setter.transducer || setter.Transducer
|
||||||
|
|| (setter.type && (setter.type.transducer || setter.type.Transducer))
|
||||||
|
) || null; // eslint-disable-line
|
||||||
|
}
|
||||||
|
|
||||||
|
function combineTransducer(transducer, arr, context) {
|
||||||
|
if (!transducer && Array.isArray(arr)) {
|
||||||
|
const [toHot, toNative] = arr;
|
||||||
|
transducer = { toHot, toNative };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
toHot: (transducer && transducer.toHot || (x => x)).bind(context), // eslint-disable-line
|
||||||
|
toNative: (transducer && transducer.toNative || (x => x)).bind(context), // eslint-disable-line
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Transducer {
|
||||||
|
constructor(context, config) {
|
||||||
|
this.setterTransducer = combineTransducer(
|
||||||
|
getTransducerFromSetter(config.setter),
|
||||||
|
getHotterFromSetter(config.setter),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
toHot(data) {
|
||||||
|
return this.setterTransducer.toHot(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
toNative(data) {
|
||||||
|
return this.setterTransducer.toNative(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -142,18 +142,24 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
|||||||
children: isDOMText(children) || isJSExpression(children) ? children : '',
|
children: isDOMText(children) || isJSExpression(children) ? children : '',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
_props = new Props(this, this.buildProps(props), extras);
|
// run initialChildren
|
||||||
this._children = new NodeChildren(this as ParentalNode, children || []);
|
this._children = new NodeChildren(this as ParentalNode, children || []);
|
||||||
this._children.interalInitParent();
|
this._children.interalInitParent();
|
||||||
|
_props = new Props(this, this.upgradeProps(props), extras);
|
||||||
}
|
}
|
||||||
this.props = _props;
|
this.props = _props;
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildProps(props: any): any {
|
private upgradeProps(props: any): any {
|
||||||
// TODO: run componentMeta(initials|initialValue|accessor)
|
// TODO: run componentMeta(initials|initialValue|accessor)
|
||||||
|
// run transform
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private transformOut() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
isContainer(): boolean {
|
isContainer(): boolean {
|
||||||
return this.isParental() && this.componentMeta.isContainer;
|
return this.isParental() && this.componentMeta.isContainer;
|
||||||
}
|
}
|
||||||
@ -525,7 +531,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
|||||||
this.document.internalRemoveAndPurgeNode(this);
|
this.document.internalRemoveAndPurgeNode(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======= compatibles ====
|
// ======= compatible apis ====
|
||||||
isEmpty(): boolean {
|
isEmpty(): boolean {
|
||||||
return this.children ? this.children.isEmpty() : true;
|
return this.children ? this.children.isEmpty() : true;
|
||||||
}
|
}
|
||||||
@ -544,6 +550,12 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
|||||||
getParent() {
|
getParent() {
|
||||||
return this.parent;
|
return this.parent;
|
||||||
}
|
}
|
||||||
|
getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
getNode() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
|
|||||||
33
packages/globals/src/di/editor.ts
Normal file
33
packages/globals/src/di/editor.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { EventEmitter } from 'events';
|
||||||
|
import { RegisterOptions } from 'power-di';
|
||||||
|
|
||||||
|
export type KeyType = Function | symbol | string;
|
||||||
|
export type ClassType = Function | (new (...args: any[]) => any);
|
||||||
|
export interface GetOptions {
|
||||||
|
forceNew?: boolean;
|
||||||
|
sourceCls?: ClassType;
|
||||||
|
}
|
||||||
|
export type GetReturnType<T, ClsType> = T extends undefined
|
||||||
|
? ClsType extends {
|
||||||
|
prototype: infer R;
|
||||||
|
}
|
||||||
|
? R
|
||||||
|
: any
|
||||||
|
: T;
|
||||||
|
|
||||||
|
export interface IEditor extends EventEmitter {
|
||||||
|
get<T = undefined, KeyOrType = any>(keyOrType: KeyOrType, opt?: GetOptions): GetReturnType<T, KeyOrType> | undefined;
|
||||||
|
|
||||||
|
has(keyOrType: KeyType): boolean;
|
||||||
|
|
||||||
|
set(key: KeyType, data: any): void;
|
||||||
|
|
||||||
|
onceGot<T = undefined, KeyOrType extends KeyType = any>(keyOrType: KeyOrType): Promise<GetReturnType<T, KeyOrType>>;
|
||||||
|
|
||||||
|
onGot<T = undefined, KeyOrType extends KeyType = any>(
|
||||||
|
keyOrType: KeyOrType,
|
||||||
|
fn: (data: GetReturnType<T, KeyOrType>) => void,
|
||||||
|
): () => void;
|
||||||
|
|
||||||
|
register(data: any, key?: KeyType, options?: RegisterOptions): void;
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
export * from './setter';
|
export * from './setter';
|
||||||
export * from './transducer';
|
export * from './transducer';
|
||||||
export * from './ioc-context';
|
export * from './ioc-context';
|
||||||
|
export * from './editor';
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { TitleContent } from './title';
|
import { TitleContent } from './title';
|
||||||
import { SetterType, DynamicSetter } from './setter-config';
|
import { SetterType, DynamicSetter } from './setter-config';
|
||||||
|
import { SettingTarget } from './setting-target';
|
||||||
|
|
||||||
|
|
||||||
export interface FieldExtraProps {
|
export interface FieldExtraProps {
|
||||||
@ -11,21 +12,21 @@ export interface FieldExtraProps {
|
|||||||
* default value of target prop for setter use
|
* default value of target prop for setter use
|
||||||
*/
|
*/
|
||||||
defaultValue?: any;
|
defaultValue?: any;
|
||||||
getValue?: (field: any, fieldValue: any) => any;
|
getValue?: (target: SettingTarget, fieldValue: any) => any;
|
||||||
setValue?: (field: any, value: any) => void;
|
setValue?: (target: SettingTarget, value: any) => void;
|
||||||
/**
|
/**
|
||||||
* the field conditional show, is not set always true
|
* the field conditional show, is not set always true
|
||||||
* @default undefined
|
* @default undefined
|
||||||
*/
|
*/
|
||||||
condition?: (field: any) => boolean;
|
condition?: (target: SettingTarget) => boolean;
|
||||||
/**
|
/**
|
||||||
* autorun when something change
|
* autorun when something change
|
||||||
*/
|
*/
|
||||||
autorun?: (field: any) => void;
|
autorun?: (target: SettingTarget) => void;
|
||||||
/**
|
/**
|
||||||
* is this field is a virtual field that not save to schema
|
* is this field is a virtual field that not save to schema
|
||||||
*/
|
*/
|
||||||
virtual?: (field: any) => boolean;
|
virtual?: (target: SettingTarget) => boolean;
|
||||||
/**
|
/**
|
||||||
* default collapsed when display accordion
|
* default collapsed when display accordion
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -11,3 +11,4 @@ export * from './title';
|
|||||||
export * from './utils';
|
export * from './utils';
|
||||||
export * from './value-type';
|
export * from './value-type';
|
||||||
export * from './setter-config';
|
export * from './setter-config';
|
||||||
|
export * from './setting-target';
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
import { isReactComponent } from '../utils';
|
import { isReactComponent } from '../utils';
|
||||||
import { ComponentType, ReactElement, isValidElement } from 'react';
|
import { ComponentType, ReactElement, isValidElement } from 'react';
|
||||||
import { TitleContent } from './title';
|
import { TitleContent } from './title';
|
||||||
|
import { SettingTarget } from './setting-target';
|
||||||
|
|
||||||
export type CustomView = ReactElement | ComponentType<any>;
|
export type CustomView = ReactElement | ComponentType<any>;
|
||||||
|
|
||||||
export type DynamicProps = (field: any) => object;
|
export type DynamicProps = (target: SettingTarget) => object;
|
||||||
export type DynamicSetter = (field: any) => string | SetterConfig | CustomView;
|
export type DynamicSetter = (target: SettingTarget) => string | SetterConfig | CustomView;
|
||||||
|
|
||||||
export interface SetterConfig {
|
export interface SetterConfig {
|
||||||
/**
|
/**
|
||||||
@ -18,11 +19,11 @@ export interface SetterConfig {
|
|||||||
props?: object | DynamicProps;
|
props?: object | DynamicProps;
|
||||||
children?: any;
|
children?: any;
|
||||||
isRequired?: boolean;
|
isRequired?: boolean;
|
||||||
initialValue?: any | ((field: any) => any);
|
initialValue?: any | ((target: SettingTarget) => any);
|
||||||
/* for MixedSetter */
|
/* for MixedSetter */
|
||||||
title?: TitleContent;
|
title?: TitleContent;
|
||||||
// for MixedSetter check this is available
|
// for MixedSetter check this is available
|
||||||
condition?: (field: any) => boolean;
|
condition?: (target: SettingTarget) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,12 +1,6 @@
|
|||||||
import { ComponentMeta, Designer, Node } from '@ali/lowcode-designer';
|
import { IEditor } from '../di';
|
||||||
import Editor from '@ali/lowcode-editor-core';
|
|
||||||
|
|
||||||
export interface SettingTarget {
|
export interface SettingTarget {
|
||||||
|
|
||||||
readonly nodes: Node[];
|
|
||||||
|
|
||||||
readonly componentMeta: ComponentMeta | null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同样类型的节点
|
* 同样类型的节点
|
||||||
*/
|
*/
|
||||||
@ -15,23 +9,26 @@ export interface SettingTarget {
|
|||||||
/**
|
/**
|
||||||
* 一个
|
* 一个
|
||||||
*/
|
*/
|
||||||
readonly isOneNode: boolean;
|
readonly isSingle: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多个
|
* 多个
|
||||||
*/
|
*/
|
||||||
readonly isMultiNodes: boolean;
|
readonly isMultiple: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编辑器引用
|
* 编辑器引用
|
||||||
*/
|
*/
|
||||||
readonly editor: Editor;
|
readonly editor: IEditor;
|
||||||
|
|
||||||
readonly designer: Designer;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问路径
|
||||||
|
*/
|
||||||
readonly path: Array<string| number>;
|
readonly path: Array<string| number>;
|
||||||
|
|
||||||
// 顶端对应 Props
|
/**
|
||||||
|
* 顶端
|
||||||
|
*/
|
||||||
readonly top: SettingTarget;
|
readonly top: SettingTarget;
|
||||||
|
|
||||||
// 父级
|
// 父级
|
||||||
@ -53,15 +50,6 @@ export interface SettingTarget {
|
|||||||
// 设置子项属性值
|
// 设置子项属性值
|
||||||
setPropValue(propName: string | number, value: any): void;
|
setPropValue(propName: string | number, value: any): void;
|
||||||
|
|
||||||
// 取得兄弟项
|
|
||||||
getSibling(propName: string | number): SettingTarget | null;
|
|
||||||
|
|
||||||
// 取得兄弟属性值
|
|
||||||
getSiblingValue(propName: string | number): any;
|
|
||||||
|
|
||||||
// 设置兄弟属性值
|
|
||||||
setSiblingValue(propName: string | number, value: any): void;
|
|
||||||
|
|
||||||
// 获取顶层附属属性值
|
// 获取顶层附属属性值
|
||||||
getExtraPropValue(propName: string): any;
|
getExtraPropValue(propName: string): any;
|
||||||
|
|
||||||
@ -1,12 +1,11 @@
|
|||||||
import React, { Component, PureComponent } from 'react';
|
import React, { Component, PureComponent } from 'react';
|
||||||
import { Tab, Breadcrumb } from '@alifd/next';
|
import { Tab, Breadcrumb } from '@alifd/next';
|
||||||
import { Title, createIcon, observer } from '@ali/lowcode-globals';
|
import { Title, createIcon, observer } from '@ali/lowcode-globals';
|
||||||
import { Node } from '@ali/lowcode-designer';
|
import { Node, isSettingField, SettingField } from '@ali/lowcode-designer';
|
||||||
import { Pane as OutlinePane } from '@ali/lowcode-plugin-outline-pane';
|
import { Pane as OutlinePane } from '@ali/lowcode-plugin-outline-pane';
|
||||||
import Editor from '@ali/lowcode-editor-core';
|
import Editor from '@ali/lowcode-editor-core';
|
||||||
import { SettingsMain } from './main';
|
import { SettingsMain } from './main';
|
||||||
import SettingsPane from './settings-pane';
|
import SettingsPane from './settings-pane';
|
||||||
import { isSettingField, SettingField } from './setting-field';
|
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export default class SettingsMainView extends Component<{ editor: Editor }> {
|
export default class SettingsMainView extends Component<{ editor: Editor }> {
|
||||||
@ -25,7 +24,7 @@ export default class SettingsMainView extends Component<{ editor: Editor }> {
|
|||||||
if (!settings) {
|
if (!settings) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (settings.isMultiNodes) {
|
if (settings.isMultiple) {
|
||||||
return (
|
return (
|
||||||
<div className="lc-settings-navigator">
|
<div className="lc-settings-navigator">
|
||||||
{createIcon(settings.componentMeta?.icon, { className: 'lc-settings-navigator-icon'})}
|
{createIcon(settings.componentMeta?.icon, { className: 'lc-settings-navigator-icon'})}
|
||||||
|
|||||||
@ -1,9 +1,15 @@
|
|||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { obx, computed } from '@ali/lowcode-globals';
|
import { obx, computed } from '@ali/lowcode-globals';
|
||||||
import { Node, Designer, Selection } from '@ali/lowcode-designer';
|
import { Node, Designer, Selection, SettingTopEntry } from '@ali/lowcode-designer';
|
||||||
import { getTreeMaster } from '@ali/lowcode-plugin-outline-pane';
|
import { getTreeMaster } from '@ali/lowcode-plugin-outline-pane';
|
||||||
import Editor from '@ali/lowcode-editor-core';
|
import Editor from '@ali/lowcode-editor-core';
|
||||||
import { SettingTopEntry, generateSessionId } from './setting-entry';
|
|
||||||
|
function generateSessionId(nodes: Node[]) {
|
||||||
|
return nodes
|
||||||
|
.map((node) => node.id)
|
||||||
|
.sort()
|
||||||
|
.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
export class SettingsMain {
|
export class SettingsMain {
|
||||||
private emitter = new EventEmitter();
|
private emitter = new EventEmitter();
|
||||||
@ -24,7 +30,13 @@ export class SettingsMain {
|
|||||||
|
|
||||||
private disposeListener: () => void;
|
private disposeListener: () => void;
|
||||||
|
|
||||||
|
private designer?: Designer;
|
||||||
|
|
||||||
constructor(readonly editor: Editor) {
|
constructor(readonly editor: Editor) {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async init() {
|
||||||
const setupSelection = (selection?: Selection) => {
|
const setupSelection = (selection?: Selection) => {
|
||||||
if (selection) {
|
if (selection) {
|
||||||
this.setup(selection.getNodes());
|
this.setup(selection.getNodes());
|
||||||
@ -32,17 +44,16 @@ export class SettingsMain {
|
|||||||
this.setup([]);
|
this.setup([]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
editor.on('designer.selection.change', setupSelection);
|
this.editor.on('designer.selection.change', setupSelection);
|
||||||
this.disposeListener = () => {
|
this.disposeListener = () => {
|
||||||
editor.removeListener('designer.selection.change', setupSelection);
|
this.editor.removeListener('designer.selection.change', setupSelection);
|
||||||
};
|
};
|
||||||
(async () => {
|
const designer = await this.editor.onceGot(Designer);
|
||||||
const designer = await editor.onceGot(Designer);
|
this.designer = designer;
|
||||||
getTreeMaster(designer).onceEnableBuiltin(() => {
|
getTreeMaster(designer).onceEnableBuiltin(() => {
|
||||||
this.emitter.emit('outline-visible');
|
this.emitter.emit('outline-visible');
|
||||||
});
|
});
|
||||||
setupSelection(designer.currentSelection);
|
setupSelection(designer.currentSelection);
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private setup(nodes: Node[]) {
|
private setup(nodes: Node[]) {
|
||||||
@ -57,7 +68,11 @@ export class SettingsMain {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._settings = new SettingTopEntry(this.editor, nodes);
|
if (!this.designer) {
|
||||||
|
this.designer = nodes[0].document.designer;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._settings = this.designer.createSettingEntry(this.editor, nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
onceOutlineVisible(fn: () => void): () => void {
|
onceOutlineVisible(fn: () => void): () => void {
|
||||||
|
|||||||
@ -1,436 +0,0 @@
|
|||||||
import { EventEmitter } from 'events';
|
|
||||||
import { Node, ComponentMeta, Designer } from '@ali/lowcode-designer';
|
|
||||||
import { CustomView, obx, uniqueId, computed, isCustomView } from '@ali/lowcode-globals';
|
|
||||||
import Editor from '@ali/lowcode-editor-core';
|
|
||||||
import { SettingTarget } from './setting-target';
|
|
||||||
import { SettingField } from './setting-field';
|
|
||||||
|
|
||||||
export function generateSessionId(nodes: Node[]) {
|
|
||||||
return nodes
|
|
||||||
.map((node) => node.id)
|
|
||||||
.sort()
|
|
||||||
.join(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SettingTopEntry implements SettingTarget {
|
|
||||||
private emitter = new EventEmitter();
|
|
||||||
private _items: Array<SettingField | CustomView> = [];
|
|
||||||
private _componentMeta: ComponentMeta | null = null;
|
|
||||||
private _isSame: boolean = true;
|
|
||||||
readonly path = [];
|
|
||||||
readonly top = this;
|
|
||||||
readonly parent = this;
|
|
||||||
|
|
||||||
get componentMeta() {
|
|
||||||
return this._componentMeta;
|
|
||||||
}
|
|
||||||
|
|
||||||
get items() {
|
|
||||||
return this._items;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同样的
|
|
||||||
*/
|
|
||||||
get isSameComponent(): boolean {
|
|
||||||
return this._isSame;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一个
|
|
||||||
*/
|
|
||||||
get isOneNode(): boolean {
|
|
||||||
return this.nodes.length === 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 多个
|
|
||||||
*/
|
|
||||||
get isMultiNodes(): boolean {
|
|
||||||
return this.nodes.length > 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly id: string;
|
|
||||||
readonly first: Node;
|
|
||||||
readonly designer: Designer;
|
|
||||||
|
|
||||||
constructor(readonly editor: Editor, readonly nodes: Node[], ) {
|
|
||||||
if (nodes.length < 1) {
|
|
||||||
throw new ReferenceError('nodes should not be empty');
|
|
||||||
}
|
|
||||||
this.id = generateSessionId(nodes);
|
|
||||||
this.first = nodes[0];
|
|
||||||
this.designer = this.first.document.designer;
|
|
||||||
|
|
||||||
// setups
|
|
||||||
this.setupComponentMeta();
|
|
||||||
|
|
||||||
// clear fields
|
|
||||||
this.setupItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupComponentMeta() {
|
|
||||||
// todo: enhance compile a temp configure.compiled
|
|
||||||
const first = this.first;
|
|
||||||
const meta = first.componentMeta;
|
|
||||||
const l = this.nodes.length;
|
|
||||||
let theSame = true;
|
|
||||||
for (let i = 1; i < l; i++) {
|
|
||||||
const other = this.nodes[i];
|
|
||||||
if (other.componentMeta !== meta) {
|
|
||||||
theSame = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (theSame) {
|
|
||||||
this._isSame = true;
|
|
||||||
this._componentMeta = meta;
|
|
||||||
} else {
|
|
||||||
this._isSame = false;
|
|
||||||
this._componentMeta = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupItems() {
|
|
||||||
if (this.componentMeta) {
|
|
||||||
this._items = this.componentMeta.configure.map((item) => {
|
|
||||||
if (isCustomView(item)) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
return new SettingField(this, item as any);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前属性值
|
|
||||||
*/
|
|
||||||
@computed getValue(): any {
|
|
||||||
this.first.propsData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置当前属性值
|
|
||||||
*/
|
|
||||||
setValue(val: any) {
|
|
||||||
this.setProps(val);
|
|
||||||
// TODO: emit value change
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取子项
|
|
||||||
*/
|
|
||||||
get(propName: string | number): SettingPropEntry {
|
|
||||||
return new SettingPropEntry(this, propName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置子级属性值
|
|
||||||
*/
|
|
||||||
setPropValue(propName: string, value: any) {
|
|
||||||
this.nodes.forEach((node) => {
|
|
||||||
node.setPropValue(propName, value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取子级属性值
|
|
||||||
*/
|
|
||||||
getPropValue(propName: string): any {
|
|
||||||
return this.first.getProp(propName, true)?.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取兄弟项
|
|
||||||
*/
|
|
||||||
getSibling(propName: string | number) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 取得兄弟属性值
|
|
||||||
*/
|
|
||||||
getSiblingValue(propName: string | number): any {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置兄弟属性值
|
|
||||||
*/
|
|
||||||
setSiblingValue(propName: string | number, value: any): void {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取顶层附属属性值
|
|
||||||
*/
|
|
||||||
getExtraPropValue(propName: string) {
|
|
||||||
return this.first.getExtraProp(propName, false)?.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置顶层附属属性值
|
|
||||||
*/
|
|
||||||
setExtraPropValue(propName: string, value: any) {
|
|
||||||
this.nodes.forEach((node) => {
|
|
||||||
node.getExtraProp(propName, true)?.setValue(value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置多个属性值,替换原有值
|
|
||||||
setProps(data: object) {
|
|
||||||
this.nodes.forEach((node) => {
|
|
||||||
node.setProps(data as any);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置多个属性值,和原有值合并
|
|
||||||
mergeProps(data: object) {
|
|
||||||
this.nodes.forEach((node) => {
|
|
||||||
node.mergeProps(data as any);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private disposeItems() {
|
|
||||||
this._items.forEach((item) => isPurgeable(item) && item.purge());
|
|
||||||
this._items = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
purge() {
|
|
||||||
this.disposeItems();
|
|
||||||
this.emitter.removeAllListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==== compatibles for vision =====
|
|
||||||
getProp(propName: string | number) {
|
|
||||||
return this.get(propName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Purgeable {
|
|
||||||
purge(): void;
|
|
||||||
}
|
|
||||||
export function isPurgeable(obj: any): obj is Purgeable {
|
|
||||||
return obj && obj.purge;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class SettingPropEntry implements SettingTarget {
|
|
||||||
// === static properties ===
|
|
||||||
readonly editor: Editor;
|
|
||||||
readonly isSameComponent: boolean;
|
|
||||||
readonly isMultiNodes: boolean;
|
|
||||||
readonly isOneNode: boolean;
|
|
||||||
readonly nodes: Node[];
|
|
||||||
readonly componentMeta: ComponentMeta | null;
|
|
||||||
readonly designer: Designer;
|
|
||||||
readonly top: SettingTarget;
|
|
||||||
readonly isGroup: boolean;
|
|
||||||
readonly type: 'field' | 'group';
|
|
||||||
readonly id = uniqueId('entry');
|
|
||||||
|
|
||||||
// ==== dynamic properties ====
|
|
||||||
@obx.ref private _name: string | number;
|
|
||||||
get name() {
|
|
||||||
return this._name;
|
|
||||||
}
|
|
||||||
@computed get path() {
|
|
||||||
const path = this.parent.path.slice();
|
|
||||||
if (this.type === 'field') {
|
|
||||||
path.push(this.name);
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
extraProps: any = {};
|
|
||||||
|
|
||||||
constructor(readonly parent: SettingTarget, name: string | number, type?: 'field' | 'group') {
|
|
||||||
if (type == null) {
|
|
||||||
const c = typeof name === 'string' ? name.substr(0, 1) : '';
|
|
||||||
if (c === '#') {
|
|
||||||
this.type = 'group';
|
|
||||||
} else {
|
|
||||||
this.type = 'field';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
// initial self properties
|
|
||||||
this._name = name;
|
|
||||||
this.isGroup = this.type === 'group';
|
|
||||||
|
|
||||||
// copy parent static properties
|
|
||||||
this.editor = parent.editor;
|
|
||||||
this.nodes = parent.nodes;
|
|
||||||
this.componentMeta = parent.componentMeta;
|
|
||||||
this.isSameComponent = parent.isSameComponent;
|
|
||||||
this.isMultiNodes = parent.isMultiNodes;
|
|
||||||
this.isOneNode = parent.isOneNode;
|
|
||||||
this.designer = parent.designer;
|
|
||||||
this.top = parent.top;
|
|
||||||
}
|
|
||||||
|
|
||||||
setKey(key: string | number) {
|
|
||||||
if (this.type !== 'field') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const propName = this.path.join('.');
|
|
||||||
let l = this.nodes.length;
|
|
||||||
while (l-- > 1) {
|
|
||||||
this.nodes[l].getProp(propName, true)!.key = key;
|
|
||||||
}
|
|
||||||
this._name = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
remove() {
|
|
||||||
if (this.type !== 'field') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const propName = this.path.join('.');
|
|
||||||
let l = this.nodes.length;
|
|
||||||
while (l-- > 1) {
|
|
||||||
this.nodes[l].getProp(propName)?.remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====== 当前属性读写 =====
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前属性值
|
|
||||||
*/
|
|
||||||
@computed getValue(): any {
|
|
||||||
let val: any = null;
|
|
||||||
if (this.type === 'field') {
|
|
||||||
val = this.parent.getPropValue(this.name);
|
|
||||||
}
|
|
||||||
const { getValue } = this.extraProps;
|
|
||||||
return getValue ? getValue(this, val) : val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置当前属性值
|
|
||||||
*/
|
|
||||||
setValue(val: any) {
|
|
||||||
if (this.type === 'field') {
|
|
||||||
this.parent.setPropValue(this.name, val);
|
|
||||||
}
|
|
||||||
const { setValue } = this.extraProps;
|
|
||||||
if (setValue) {
|
|
||||||
setValue(this, val);
|
|
||||||
}
|
|
||||||
// TODO: emit value change
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取子项
|
|
||||||
*/
|
|
||||||
get(propName: string | number) {
|
|
||||||
const path = this.path.concat(propName).join('.');
|
|
||||||
return this.top.get(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置子级属性值
|
|
||||||
*/
|
|
||||||
setPropValue(propName: string | number, value: any) {
|
|
||||||
const path = this.path.concat(propName).join('.');
|
|
||||||
this.top.setPropValue(path, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取子级属性值
|
|
||||||
*/
|
|
||||||
getPropValue(propName: string | number): any {
|
|
||||||
return this.top.getPropValue(this.path.concat(propName).join('.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取兄弟项
|
|
||||||
*/
|
|
||||||
getSibling(propName: string | number) {
|
|
||||||
return this.parent.get(propName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 取得兄弟属性值
|
|
||||||
*/
|
|
||||||
getSiblingValue(propName: string | number): any {
|
|
||||||
return this.parent.getPropValue(propName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置兄弟属性值
|
|
||||||
*/
|
|
||||||
setSiblingValue(propName: string | number, value: any): void {
|
|
||||||
this.parent.setPropValue(propName, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取顶层附属属性值
|
|
||||||
*/
|
|
||||||
getExtraPropValue(propName: string) {
|
|
||||||
return this.top.getExtraPropValue(propName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置顶层附属属性值
|
|
||||||
*/
|
|
||||||
setExtraPropValue(propName: string, value: any) {
|
|
||||||
this.top.setExtraPropValue(propName, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======= compatibles for vision ======
|
|
||||||
getNode() {
|
|
||||||
return this.nodes[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
getProps() {
|
|
||||||
return this.top;
|
|
||||||
}
|
|
||||||
|
|
||||||
onValueChange() {
|
|
||||||
return () => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
getId() {
|
|
||||||
return this.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
getName(): string {
|
|
||||||
return this.path.join('.');
|
|
||||||
}
|
|
||||||
|
|
||||||
getKey() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDefaultValue() {
|
|
||||||
return this.extraProps.defaultValue;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
getConfig<K extends keyof IPropConfig>(configName?: K): IPropConfig[K] | IPropConfig {
|
|
||||||
if (configName) {
|
|
||||||
return this.config[configName];
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.config;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
isHidden() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
isDisabled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
getSetter() {
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
isIgnore() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -10,9 +10,7 @@ import {
|
|||||||
} from '@ali/lowcode-globals';
|
} from '@ali/lowcode-globals';
|
||||||
import { Field, createField } from '../field';
|
import { Field, createField } from '../field';
|
||||||
import PopupService from '../popup';
|
import PopupService from '../popup';
|
||||||
import { SettingField, isSettingField } from './setting-field';
|
import { SettingField, isSettingField, SettingTopEntry, SettingEntry } from '@ali/lowcode-designer';
|
||||||
import { SettingTarget } from './setting-target';
|
|
||||||
import { SettingTopEntry } from './setting-entry';
|
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class SettingFieldView extends Component<{ field: SettingField }> {
|
class SettingFieldView extends Component<{ field: SettingField }> {
|
||||||
@ -20,7 +18,7 @@ class SettingFieldView extends Component<{ field: SettingField }> {
|
|||||||
const { field } = this.props;
|
const { field } = this.props;
|
||||||
const { extraProps } = field;
|
const { extraProps } = field;
|
||||||
const { condition, defaultValue } = extraProps;
|
const { condition, defaultValue } = extraProps;
|
||||||
const visible = field.isOneNode && typeof condition === 'function' ? condition(field) !== false : true;
|
const visible = field.isSingle && typeof condition === 'function' ? condition(field) !== false : true;
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -99,7 +97,7 @@ class SettingGroupView extends Component<{ field: SettingField }> {
|
|||||||
const { field } = this.props;
|
const { field } = this.props;
|
||||||
const { extraProps } = field;
|
const { extraProps } = field;
|
||||||
const { condition } = extraProps;
|
const { condition } = extraProps;
|
||||||
const visible = field.isOneNode && typeof condition === 'function' ? condition(field) !== false : true;
|
const visible = field.isSingle && typeof condition === 'function' ? condition(field) !== false : true;
|
||||||
|
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
return null;
|
return null;
|
||||||
@ -116,7 +114,7 @@ class SettingGroupView extends Component<{ field: SettingField }> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSettingFieldView(item: SettingField | CustomView, field: SettingTarget, index?: number) {
|
export function createSettingFieldView(item: SettingField | CustomView, field: SettingEntry, index?: number) {
|
||||||
if (isSettingField(item)) {
|
if (isSettingField(item)) {
|
||||||
if (item.isGroup) {
|
if (item.isGroup) {
|
||||||
return <SettingGroupView field={item} key={item.id} />;
|
return <SettingGroupView field={item} key={item.id} />;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ComponentType, ReactElement, isValidElement, ComponentClass } from 'react';
|
import { ComponentType, ReactElement, isValidElement, ComponentClass } from 'react';
|
||||||
import { isI18nData } from '@ali/lowcode-globals';
|
import { isI18nData, SettingTarget } from '@ali/lowcode-globals';
|
||||||
|
|
||||||
type Field = any;
|
type Field = SettingTarget;
|
||||||
|
|
||||||
export enum DISPLAY_TYPE {
|
export enum DISPLAY_TYPE {
|
||||||
NONE = 'none', // => condition'plain'
|
NONE = 'none', // => condition'plain'
|
||||||
@ -282,7 +282,6 @@ export function upgradePropConfig(config: OldPropConfig) {
|
|||||||
componentName: 'SlotSetter',
|
componentName: 'SlotSetter',
|
||||||
initialValue: () => ({
|
initialValue: () => ({
|
||||||
type: 'JSSlot',
|
type: 'JSSlot',
|
||||||
// params:
|
|
||||||
value: initialChildren
|
value: initialChildren
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -315,14 +314,14 @@ export function upgradePropConfig(config: OldPropConfig) {
|
|||||||
initialFn
|
initialFn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extraProps.initialValue = (field: Field, defaultValue?: any) => {
|
extraProps.initialValue = (field: Field, currentValue: any, defaultValue?: any) => {
|
||||||
if (defaultValue === undefined) {
|
if (defaultValue === undefined) {
|
||||||
defaultValue = extraProps.defaultValue;
|
defaultValue = extraProps.defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof initialFn === 'function') {
|
if (typeof initialFn === 'function') {
|
||||||
// ?
|
// ?
|
||||||
return initialFn(null, defaultValue);
|
return initialFn.call(field, currentValue, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user