move setting to designer

This commit is contained in:
kangwei 2020-04-24 17:26:46 +08:00
parent f1b686bad5
commit 8a768177ce
20 changed files with 592 additions and 501 deletions

View File

@ -8,6 +8,7 @@ import {
obx,
computed,
autorun,
IEditor,
} from '@ali/lowcode-globals';
import { Project } from '../project';
import { Node, DocumentModel, insertChildren, isRootNode, ParentalNode } from '../document';
@ -20,6 +21,7 @@ import { Hovering } from './hovering';
import { DropLocation, LocationData, isLocationChildrenDetail } from './location';
import { OffsetObserver, createOffsetObserver } from './offset-observer';
import { focusing } from './focusing';
import { SettingTopEntry } from './setting';
export interface DesignerProps {
className?: string;
@ -198,6 +200,10 @@ export class Designer {
return createOffsetObserver(nodeInstance);
}
createSettingEntry(editor: IEditor, nodes: Node[]) {
return new SettingTopEntry(editor, nodes);
}
/**
*
*/

View File

@ -6,3 +6,4 @@ export * from './hovering';
export * from './location';
export * from './offset-observer';
export * from './scroller';
export * from './setting';

View File

@ -0,0 +1,3 @@
export * from './setting-field';
export * from './setting-top-entry';
export * from './setting-entry';

View 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;
}

View File

@ -1,9 +1,9 @@
import { TitleContent, computed, isDynamicSetter, SetterType, DynamicSetter, FieldExtraProps, FieldConfig, CustomView, isCustomView, obx } from '@ali/lowcode-globals';
import { Transducer } from '../utils';
import { SettingPropEntry } from './setting-entry';
import { SettingTarget } from './setting-target';
import { Transducer } from './utils';
import { SettingPropEntry } from './setting-prop-entry';
import { SettingEntry } from './setting-entry';
export class SettingField extends SettingPropEntry implements SettingTarget {
export class SettingField extends SettingPropEntry implements SettingEntry {
readonly isSettingField = true;
readonly isRequired: boolean;
readonly transducer: Transducer;
@ -35,7 +35,7 @@ export class SettingField extends SettingPropEntry implements SettingTarget {
this._expanded = value;
}
constructor(readonly parent: SettingTarget, config: FieldConfig) {
constructor(readonly parent: SettingEntry, config: FieldConfig) {
super(parent, config.name, config.type);
const { title, items, setter, extraProps, ...rest } = config;
@ -53,6 +53,7 @@ export class SettingField extends SettingPropEntry implements SettingTarget {
this.initItems(items);
}
// compatiable old config
this.transducer = new Transducer(this, { setter });
}

View 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;
}
*/
}

View 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;
}

View 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);
}
}

View File

@ -142,18 +142,24 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
children: isDOMText(children) || isJSExpression(children) ? children : '',
});
} else {
_props = new Props(this, this.buildProps(props), extras);
// run initialChildren
this._children = new NodeChildren(this as ParentalNode, children || []);
this._children.interalInitParent();
_props = new Props(this, this.upgradeProps(props), extras);
}
this.props = _props;
}
private buildProps(props: any): any {
private upgradeProps(props: any): any {
// TODO: run componentMeta(initials|initialValue|accessor)
// run transform
return props;
}
private transformOut() {
}
isContainer(): boolean {
return this.isParental() && this.componentMeta.isContainer;
}
@ -525,7 +531,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
this.document.internalRemoveAndPurgeNode(this);
}
// ======= compatibles ====
// ======= compatible apis ====
isEmpty(): boolean {
return this.children ? this.children.isEmpty() : true;
}
@ -544,6 +550,12 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
getParent() {
return this.parent;
}
getId() {
return this.id;
}
getNode() {
return this;
}
/**
* @deprecated

View 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;
}

View File

@ -1,3 +1,4 @@
export * from './setter';
export * from './transducer';
export * from './ioc-context';
export * from './editor';

View File

@ -1,5 +1,6 @@
import { TitleContent } from './title';
import { SetterType, DynamicSetter } from './setter-config';
import { SettingTarget } from './setting-target';
export interface FieldExtraProps {
@ -11,21 +12,21 @@ export interface FieldExtraProps {
* default value of target prop for setter use
*/
defaultValue?: any;
getValue?: (field: any, fieldValue: any) => any;
setValue?: (field: any, value: any) => void;
getValue?: (target: SettingTarget, fieldValue: any) => any;
setValue?: (target: SettingTarget, value: any) => void;
/**
* the field conditional show, is not set always true
* @default undefined
*/
condition?: (field: any) => boolean;
condition?: (target: SettingTarget) => boolean;
/**
* autorun when something change
*/
autorun?: (field: any) => void;
autorun?: (target: SettingTarget) => void;
/**
* 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
*/

View File

@ -11,3 +11,4 @@ export * from './title';
export * from './utils';
export * from './value-type';
export * from './setter-config';
export * from './setting-target';

View File

@ -1,11 +1,12 @@
import { isReactComponent } from '../utils';
import { ComponentType, ReactElement, isValidElement } from 'react';
import { TitleContent } from './title';
import { SettingTarget } from './setting-target';
export type CustomView = ReactElement | ComponentType<any>;
export type DynamicProps = (field: any) => object;
export type DynamicSetter = (field: any) => string | SetterConfig | CustomView;
export type DynamicProps = (target: SettingTarget) => object;
export type DynamicSetter = (target: SettingTarget) => string | SetterConfig | CustomView;
export interface SetterConfig {
/**
@ -18,11 +19,11 @@ export interface SetterConfig {
props?: object | DynamicProps;
children?: any;
isRequired?: boolean;
initialValue?: any | ((field: any) => any);
initialValue?: any | ((target: SettingTarget) => any);
/* for MixedSetter */
title?: TitleContent;
// for MixedSetter check this is available
condition?: (field: any) => boolean;
condition?: (target: SettingTarget) => boolean;
}
/**

View File

@ -1,12 +1,6 @@
import { ComponentMeta, Designer, Node } from '@ali/lowcode-designer';
import Editor from '@ali/lowcode-editor-core';
import { IEditor } from '../di';
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 designer: Designer;
readonly editor: IEditor;
/**
* 访
*/
readonly path: Array<string| number>;
// 顶端对应 Props
/**
*
*/
readonly top: SettingTarget;
// 父级
@ -53,15 +50,6 @@ export interface SettingTarget {
// 设置子项属性值
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;

View File

@ -1,12 +1,11 @@
import React, { Component, PureComponent } from 'react';
import { Tab, Breadcrumb } from '@alifd/next';
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 Editor from '@ali/lowcode-editor-core';
import { SettingsMain } from './main';
import SettingsPane from './settings-pane';
import { isSettingField, SettingField } from './setting-field';
@observer
export default class SettingsMainView extends Component<{ editor: Editor }> {
@ -25,7 +24,7 @@ export default class SettingsMainView extends Component<{ editor: Editor }> {
if (!settings) {
return null;
}
if (settings.isMultiNodes) {
if (settings.isMultiple) {
return (
<div className="lc-settings-navigator">
{createIcon(settings.componentMeta?.icon, { className: 'lc-settings-navigator-icon'})}

View File

@ -1,9 +1,15 @@
import { EventEmitter } from 'events';
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 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 {
private emitter = new EventEmitter();
@ -24,7 +30,13 @@ export class SettingsMain {
private disposeListener: () => void;
private designer?: Designer;
constructor(readonly editor: Editor) {
this.init();
}
private async init() {
const setupSelection = (selection?: Selection) => {
if (selection) {
this.setup(selection.getNodes());
@ -32,17 +44,16 @@ export class SettingsMain {
this.setup([]);
}
};
editor.on('designer.selection.change', setupSelection);
this.editor.on('designer.selection.change', setupSelection);
this.disposeListener = () => {
editor.removeListener('designer.selection.change', setupSelection);
this.editor.removeListener('designer.selection.change', setupSelection);
};
(async () => {
const designer = await editor.onceGot(Designer);
getTreeMaster(designer).onceEnableBuiltin(() => {
this.emitter.emit('outline-visible');
});
setupSelection(designer.currentSelection);
})();
const designer = await this.editor.onceGot(Designer);
this.designer = designer;
getTreeMaster(designer).onceEnableBuiltin(() => {
this.emitter.emit('outline-visible');
});
setupSelection(designer.currentSelection);
}
private setup(nodes: Node[]) {
@ -57,7 +68,11 @@ export class SettingsMain {
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 {

View File

@ -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;
}
}

View File

@ -10,9 +10,7 @@ import {
} from '@ali/lowcode-globals';
import { Field, createField } from '../field';
import PopupService from '../popup';
import { SettingField, isSettingField } from './setting-field';
import { SettingTarget } from './setting-target';
import { SettingTopEntry } from './setting-entry';
import { SettingField, isSettingField, SettingTopEntry, SettingEntry } from '@ali/lowcode-designer';
@observer
class SettingFieldView extends Component<{ field: SettingField }> {
@ -20,7 +18,7 @@ class SettingFieldView extends Component<{ field: SettingField }> {
const { field } = this.props;
const { extraProps } = field;
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) {
return null;
}
@ -99,7 +97,7 @@ class SettingGroupView extends Component<{ field: SettingField }> {
const { field } = this.props;
const { extraProps } = field;
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) {
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 (item.isGroup) {
return <SettingGroupView field={item} key={item.id} />;

View File

@ -1,7 +1,7 @@
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 {
NONE = 'none', // => condition'plain'
@ -282,7 +282,6 @@ export function upgradePropConfig(config: OldPropConfig) {
componentName: 'SlotSetter',
initialValue: () => ({
type: 'JSSlot',
// params:
value: initialChildren
}),
}
@ -315,14 +314,14 @@ export function upgradePropConfig(config: OldPropConfig) {
initialFn
}
}
extraProps.initialValue = (field: Field, defaultValue?: any) => {
extraProps.initialValue = (field: Field, currentValue: any, defaultValue?: any) => {
if (defaultValue === undefined) {
defaultValue = extraProps.defaultValue;
}
if (typeof initialFn === 'function') {
// ?
return initialFn(null, defaultValue);
return initialFn.call(field, currentValue, defaultValue);
}
return defaultValue;