From df2362c67d1781c8f85cf03ececaf6252b9a6dcc Mon Sep 17 00:00:00 2001 From: kangwei Date: Fri, 24 Apr 2020 23:49:37 +0800 Subject: [PATCH] complete transform logic --- packages/designer/src/designer/designer.ts | 39 ++++- .../designer/setting/setting-prop-entry.ts | 2 +- .../designer/src/document/document-model.ts | 6 +- .../designer/src/document/node/export-type.ts | 5 - packages/designer/src/document/node/index.ts | 2 +- .../src/document/node/node-children.ts | 48 +++++- packages/designer/src/document/node/node.ts | 58 +++++--- .../designer/src/document/node/props/prop.ts | 18 +-- .../designer/src/document/node/props/props.ts | 19 ++- .../src/document/node/transform-stage.ts | 6 + packages/globals/src/types/field-config.ts | 3 + packages/globals/src/types/metadata.ts | 11 +- packages/globals/src/types/value-type.ts | 9 ++ packages/plugin-designer/src/index.tsx | 4 +- .../src/renderer-view.tsx | 1 - packages/setters/src/index.tsx | 39 +++-- .../vision-polyfill/src/bundle/prototype.ts | 54 +++++-- .../src/bundle/upgrade-metadata.ts | 140 +++++++++++------- packages/vision-polyfill/src/drag-engine.ts | 4 +- packages/vision-polyfill/src/editor.ts | 53 ++++++- packages/vision-polyfill/src/i18n-reducer.ts | 35 +++++ 21 files changed, 416 insertions(+), 140 deletions(-) delete mode 100644 packages/designer/src/document/node/export-type.ts create mode 100644 packages/designer/src/document/node/transform-stage.ts create mode 100644 packages/vision-polyfill/src/i18n-reducer.ts diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts index 0c75d8d81..1e1ce573d 100644 --- a/packages/designer/src/designer/designer.ts +++ b/packages/designer/src/designer/designer.ts @@ -1,5 +1,4 @@ import { ComponentType } from 'react'; -import { EventEmitter } from 'events'; import { ProjectSchema, ComponentMetadata, @@ -9,9 +8,11 @@ import { computed, autorun, IEditor, + CompositeObject, + PropsList, } from '@ali/lowcode-globals'; import { Project } from '../project'; -import { Node, DocumentModel, insertChildren, isRootNode, ParentalNode } from '../document'; +import { Node, DocumentModel, insertChildren, isRootNode, ParentalNode, TransformStage } from '../document'; import { ComponentMeta } from '../component-meta'; import { INodeSelector, Component } from '../simulator'; import { Scroller, IScrollable } from './scroller'; @@ -24,6 +25,7 @@ import { focusing } from './focusing'; import { SettingTopEntry } from './setting'; export interface DesignerProps { + editor: IEditor; className?: string; style?: object; defaultSchema?: ProjectSchema; @@ -33,7 +35,6 @@ export interface DesignerProps { dragGhostComponent?: ComponentType; suspensed?: boolean; componentMetadatas?: ComponentMetadata[]; - eventPipe?: EventEmitter; globalComponentActions?: ComponentAction[]; onMount?: (designer: Designer) => void; onDragstart?: (e: LocateEvent) => void; @@ -47,6 +48,7 @@ export class Designer { readonly activeTracker = new ActiveTracker(); readonly hovering = new Hovering(); readonly project: Project; + readonly editor: IEditor; get currentDocument() { return this.project.currentDocument; @@ -61,6 +63,8 @@ export class Designer { } constructor(props: DesignerProps) { + const { editor } = props; + this.editor = editor; this.setProps(props); this.project = new Project(this, props.defaultSchema); @@ -164,7 +168,7 @@ export class Designer { } postEvent(event: string, ...args: any[]) { - this.props?.eventPipe?.emit(`designer.${event}`, ...args); + this.editor.emit(`designer.${event}`, ...args); } private _dropLocation?: DropLocation; @@ -312,7 +316,6 @@ export class Designer { private _lostComponentMetasMap = new Map(); private buildComponentMetasMap(metas: ComponentMetadata[]) { - console.info(this._componentMetasMap); metas.forEach((data) => this.createComponentMeta(data)); } @@ -372,6 +375,30 @@ export class Designer { return maps; } + private propsReducers = new Map(); + transformProps(props: CompositeObject | PropsList, node: Node, stage: TransformStage) { + if (Array.isArray(props)) { + // current not support, make this future + return props; + } + + const reducers = this.propsReducers.get(stage); + if (!reducers) { + return props; + } + + return reducers.reduce((xprops, reducer) => reducer(xprops, node), props); + } + + addPropsReducer(reducer: PropsReducer, stage: TransformStage) { + const reducers = this.propsReducers.get(stage); + if (reducers) { + reducers.push(reducer); + } else { + this.propsReducers.set(stage, [reducer]); + } + } + autorun(action: (context: { firstRun: boolean }) => void, sync = false): () => void { return autorun(action, sync as true); } @@ -380,3 +407,5 @@ export class Designer { // todo: } } + +export type PropsReducer = (props: CompositeObject, node: Node) => CompositeObject; diff --git a/packages/designer/src/designer/setting/setting-prop-entry.ts b/packages/designer/src/designer/setting/setting-prop-entry.ts index d8fd7daf4..87eae902a 100644 --- a/packages/designer/src/designer/setting/setting-prop-entry.ts +++ b/packages/designer/src/designer/setting/setting-prop-entry.ts @@ -157,7 +157,7 @@ export class SettingPropEntry implements SettingEntry { // ======= compatibles for vision ====== getNode() { - return this.top; + return this.nodes[0]; } getName(): string { diff --git a/packages/designer/src/document/document-model.ts b/packages/designer/src/document/document-model.ts index b0129f51c..2a62b2c68 100644 --- a/packages/designer/src/document/document-model.ts +++ b/packages/designer/src/document/document-model.ts @@ -19,7 +19,7 @@ import { isDragNodeDataObject, DragNodeObject, DragNodeDataObject, DropLocation import { Node, insertChildren, insertChild, isNode, RootNode, ParentalNode } from './node/node'; import { Selection } from './selection'; import { History } from './history'; -import { ExportType } from './node'; +import { TransformStage } from './node'; export type GetDataType = T extends undefined ? NodeType extends { @@ -277,8 +277,8 @@ export class DocumentModel { // todo: select added and active track added } - export(exportType: ExportType = ExportType.ForSerilize) { - return this.rootNode.export(exportType); + export(stage: TransformStage = TransformStage.Serilize) { + return this.rootNode.export(stage); } /** diff --git a/packages/designer/src/document/node/export-type.ts b/packages/designer/src/document/node/export-type.ts deleted file mode 100644 index b57e52fff..000000000 --- a/packages/designer/src/document/node/export-type.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum ExportType { - ForRender = 1, - ForSerilize = 2, - ForSave = 3, -} diff --git a/packages/designer/src/document/node/index.ts b/packages/designer/src/document/node/index.ts index 7251d43c8..67fac69bf 100644 --- a/packages/designer/src/document/node/index.ts +++ b/packages/designer/src/document/node/index.ts @@ -4,4 +4,4 @@ export * from './node-children'; export * from './props/prop'; export * from './props/prop-stash'; export * from './props/props'; -export * from './export-type'; +export * from './transform-stage'; diff --git a/packages/designer/src/document/node/node-children.ts b/packages/designer/src/document/node/node-children.ts index 40d448420..712ed1374 100644 --- a/packages/designer/src/document/node/node-children.ts +++ b/packages/designer/src/document/node/node-children.ts @@ -1,6 +1,6 @@ import { NodeData, isNodeSchema, obx, computed } from '@ali/lowcode-globals'; import { Node, ParentalNode } from './node'; -import { ExportType } from './export-type'; +import { TransformStage } from './transform-stage'; export class NodeChildren { @obx.val private children: Node[]; @@ -17,10 +17,10 @@ export class NodeChildren { /** * 导出 schema */ - export(exportType: ExportType = ExportType.ForSave): NodeData[] { + export(stage: TransformStage = TransformStage.Save): NodeData[] { return this.children.map(node => { - const data = node.export(exportType); - if (node.isLeaf() && ExportType.ForSave === exportType) { + const data = node.export(stage); + if (node.isLeaf() && TransformStage.Save === stage) { // FIXME: filter empty return data.children as NodeData; } @@ -202,6 +202,46 @@ export class NodeChildren { }); } + some(fn: (item: Node, index: number) => any): boolean { + return this.children.some((child, index) => fn(child, index)); + } + + mergeChildren(remover: () => any, adder: (children: Node[]) => NodeData[] | null, sorter: () => any) { + /* + const children = this.children.slice(); + children.forEach(child => child.internalSetParent(null)); + + this.children = children; + this.interalInitParent(); + */ + + if (remover) { + const willRemove = this.children.filter(remover); + if (willRemove.length > 0) { + willRemove.forEach((node) => { + const i = this.children.indexOf(node); + if (i > -1) { + this.children.splice(i, 1); + node.remove(); + } + }); + } + } + if (adder) { + const items = adder(this.children); + if (items && items.length > 0) { + items.forEach((child: NodeData) => { + const node = this.owner.document.createNode(child); + this.children.push(node); + node.internalSetParent(this.owner); + }); + } + } + if (sorter) { + this.children = this.children.sort(sorter); + } + } + private purged = false; /** * 回收销毁 diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index ab206662f..8d7d93d61 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -18,7 +18,7 @@ import { NodeChildren } from './node-children'; import { Prop } from './props/prop'; import { ComponentMeta } from '../../component-meta'; import { ExclusiveGroup, isExclusiveGroup } from './exclusive-group'; -import { ExportType } from './export-type'; +import { TransformStage } from './transform-stage'; /** * 基础节点 @@ -136,28 +136,31 @@ export class Node { const { componentName, id, children, props, ...extras } = nodeSchema; this.id = id || `node$${document.nextId()}`; this.componentName = componentName; - let _props: Props; if (this.componentName === 'Leaf') { - _props = new Props(this, { + this.props = new Props(this, { children: isDOMText(children) || isJSExpression(children) ? children : '', }); } else { - // run initialChildren - this._children = new NodeChildren(this as ParentalNode, children || []); + this.props = new Props(this, props, extras); + this._children = new NodeChildren(this as ParentalNode, this.initialChildren(children)); this._children.interalInitParent(); - _props = new Props(this, this.upgradeProps(props), extras); + this.props.import(this.transformProps(props || {}), extras); } - this.props = _props; } - private upgradeProps(props: any): any { - // TODO: run componentMeta(initials|initialValue|accessor) - // run transform - return props; + private transformProps(props: any): any { + // FIXME! support PropsList + const x = this.document.designer.transformProps(props, this, TransformStage.Init); + // TODO: run transducers in metadata.experimental + console.info(x); + return x; } - private transformOut() { - + private initialChildren(children: any): NodeData[] { + if (children == null) { + return this.componentMeta.getMetadata().experimental?.initialChildren || []; + } + return children; } isContainer(): boolean { @@ -266,7 +269,7 @@ export class Node { if (!this.isParental() || this.componentName === 'Fragment') { return null; } - return this.props.export(ExportType.ForSerilize).props || null; + return this.props.export(TransformStage.Serilize).props || null; } @computed hasSlots() { @@ -427,7 +430,7 @@ export class Node { * 获取符合搭建协议-节点 schema 结构 */ get schema(): Schema { - return this.export(ExportType.ForSave); + return this.export(TransformStage.Save); } set schema(data: Schema) { @@ -448,31 +451,32 @@ export class Node { /** * 导出 schema */ - export(exportType: ExportType = ExportType.ForSave): Schema { + export(stage: TransformStage = TransformStage.Save): Schema { // run transducers // run const baseSchema: any = { componentName: this.componentName, }; - if (exportType !== ExportType.ForSave) { + if (stage !== TransformStage.Save) { baseSchema.id = this.id; } if (this.isLeaf()) { - baseSchema.children = this.props.get('children')?.export(exportType); + baseSchema.children = this.props.get('children')?.export(stage); return baseSchema; } - const { props = {}, extras } = this.props.export(exportType) || {}; + const { props = {}, extras } = this.props.export(stage) || {}; + const schema: any = { ...baseSchema, - props, + props: this.document.designer.transformProps(props, this, stage), ...extras, }; if (this.isParental() && this.children.size > 0) { - schema.children = this.children.export(exportType); + schema.children = this.children.export(stage); } return schema; @@ -556,6 +560,13 @@ export class Node { getNode() { return this; } + getProps() { + return this.props; + } + + mergeChildren(remover: () => any, adder: (children: Node[]) => NodeData[] | null, sorter: () => any) { + this.children?.mergeChildren(remover, adder, sorter); + } /** * @deprecated @@ -595,6 +606,9 @@ export class Node { } return { container: this.parent, ref: this }; } + toString() { + return this.id; + } } export interface ParentalNode extends Node { @@ -689,7 +703,7 @@ export function comparePosition(node1: Node, node2: Node): PositionNO { export function insertChild(container: ParentalNode, thing: Node | NodeData, at?: number | null, copy?: boolean): Node { let node: Node; if (isNode(thing) && (copy || thing.isSlot())) { - thing = thing.export(ExportType.ForSave); + thing = thing.export(TransformStage.Save); } if (isNode(thing)) { node = thing; diff --git a/packages/designer/src/document/node/props/prop.ts b/packages/designer/src/document/node/props/prop.ts index 4ec68c44c..f82eb013a 100644 --- a/packages/designer/src/document/node/props/prop.ts +++ b/packages/designer/src/document/node/props/prop.ts @@ -15,7 +15,7 @@ import { PropStash } from './prop-stash'; import { valueToSource } from './value-to-source'; import { Props } from './props'; import { SlotNode } from '../node'; -import { ExportType } from '../export-type'; +import { TransformStage } from '../transform-stage'; export const UNSET = Symbol.for('unset'); export type UNSET = typeof UNSET; @@ -46,10 +46,10 @@ export class Prop implements IPropParent { * 属性值 */ @computed get value(): CompositeValue | UNSET { - return this.export(ExportType.ForSerilize); + return this.export(TransformStage.Serilize); } - export(exporType: ExportType = ExportType.ForSave): CompositeValue | UNSET { + export(stage: TransformStage = TransformStage.Save): CompositeValue | UNSET { const type = this._type; if (type === 'unset') { @@ -61,8 +61,8 @@ export class Prop implements IPropParent { } if (type === 'slot') { - const schema = this._slotNode!.export(exporType); - if (exporType === ExportType.ForSave) { + const schema = this._slotNode!.export(stage); + if (stage === TransformStage.Save) { return { type: 'JSSlot', params: schema.params, @@ -82,7 +82,7 @@ export class Prop implements IPropParent { } const maps: any = {}; this.items!.forEach((prop, key) => { - const v = prop.export(exporType); + const v = prop.export(stage); if (v !== UNSET) { maps[key] = v; } @@ -95,7 +95,7 @@ export class Prop implements IPropParent { return this._value; } return this.items!.map((prop) => { - const v = prop.export(exporType); + const v = prop.export(stage); return v === UNSET ? null : v; }); } @@ -113,7 +113,7 @@ export class Prop implements IPropParent { } // todo: JSFunction ... if (this.type === 'slot') { - return JSON.stringify(this._slotNode!.export(ExportType.ForSave)); + return JSON.stringify(this._slotNode!.export(TransformStage.Save)); } return this._code != null ? this._code : JSON.stringify(this.value); } @@ -191,7 +191,7 @@ export class Prop implements IPropParent { } @computed getValue(): CompositeValue { - const v = this.export(ExportType.ForSerilize); + const v = this.export(TransformStage.Serilize); if (v === UNSET) { return null; } diff --git a/packages/designer/src/document/node/props/props.ts b/packages/designer/src/document/node/props/props.ts index 57200134b..bfdead44b 100644 --- a/packages/designer/src/document/node/props/props.ts +++ b/packages/designer/src/document/node/props/props.ts @@ -2,7 +2,7 @@ import { PropsMap, PropsList, CompositeValue, computed, obx, uniqueId } from '@a import { PropStash } from './prop-stash'; import { Prop, IPropParent, UNSET } from './prop'; import { Node } from '../node'; -import { ExportType } from '../export-type'; +import { TransformStage } from '../transform-stage'; export const EXTRA_KEY_PREFIX = '__'; @@ -80,7 +80,7 @@ export class Props implements IPropParent { }); } - export(exportType: ExportType = ExportType.ForSave): { props?: PropsMap | PropsList; extras?: object } { + export(stage: TransformStage = TransformStage.Save): { props?: PropsMap | PropsList; extras?: object } { if (this.items.length < 1) { return {}; } @@ -89,7 +89,7 @@ export class Props implements IPropParent { if (this.type === 'list') { props = []; this.items.forEach(item => { - let value = item.export(exportType); + let value = item.export(stage); if (value === UNSET) { value = null; } @@ -112,7 +112,7 @@ export class Props implements IPropParent { // todo ...spread return; } - let value = item.export(exportType); + let value = item.export(stage); if (value === UNSET) { value = null; } @@ -296,4 +296,15 @@ export class Props implements IPropParent { this.stash.purge(); this.items.forEach(item => item.purge()); } + + getProp(path: string, stash = true): Prop | null { + return this.query(path, stash as any) || null; + } + + /** + * 获取单个属性值 + */ + getPropValue(path: string): any { + return this.getProp(path, false)?.value; + } } diff --git a/packages/designer/src/document/node/transform-stage.ts b/packages/designer/src/document/node/transform-stage.ts new file mode 100644 index 000000000..5baec4675 --- /dev/null +++ b/packages/designer/src/document/node/transform-stage.ts @@ -0,0 +1,6 @@ +export enum TransformStage { + Render = 1, + Serilize = 2, + Save = 3, + Init = 4, +} diff --git a/packages/globals/src/types/field-config.ts b/packages/globals/src/types/field-config.ts index d003ec1b6..17a4cfd78 100644 --- a/packages/globals/src/types/field-config.ts +++ b/packages/globals/src/types/field-config.ts @@ -12,6 +12,9 @@ export interface FieldExtraProps { * default value of target prop for setter use */ defaultValue?: any; + /** + * get value for field + */ getValue?: (target: SettingTarget, fieldValue: any) => any; setValue?: (target: SettingTarget, value: any) => void; /** diff --git a/packages/globals/src/types/metadata.ts b/packages/globals/src/types/metadata.ts index c7377753e..76057faf4 100644 --- a/packages/globals/src/types/metadata.ts +++ b/packages/globals/src/types/metadata.ts @@ -5,7 +5,8 @@ import { TitleContent } from './title'; import { PropConfig } from './prop-config'; import { NpmInfo } from './npm'; import { FieldConfig } from './field-config'; -import { NodeSchema } from './schema'; +import { NodeSchema, NodeData } from './schema'; +import { SettingTarget } from './setting-target'; export type NestingFilter = (testNode: any, currentNode: any) => boolean; export interface NestingRule { @@ -40,12 +41,20 @@ export interface Snippet { schema: NodeSchema; } +export interface InitialItem { + name: string; + initial: (target: SettingTarget, currentValue: any) => any; +} + export interface Experimental { context?: { [contextInfoName: string]: any }; snippets?: Snippet[]; view?: ComponentType; transducers?: any; // ? should support + initials?: InitialItem[]; callbacks?: Callbacks; + // TODO: thinkof function + initialChildren?: NodeData[]; // 样式 及 位置,handle上必须有明确的标识以便事件路由判断,或者主动设置事件独占模式 // NWSE 是交给引擎计算放置位置,ReactElement 必须自己控制初始位置 diff --git a/packages/globals/src/types/value-type.ts b/packages/globals/src/types/value-type.ts index 145722eff..d0e083160 100644 --- a/packages/globals/src/types/value-type.ts +++ b/packages/globals/src/types/value-type.ts @@ -21,6 +21,11 @@ export interface JSSlot { value?: NodeData[] | NodeData; } +export interface JSBlock { + type: 'JSBlock'; + value: NodeSchema; +} + // JSON 基本类型 export type JSONValue = boolean | string | number | null | JSONArray | JSONObject; export type JSONArray = JSONValue[]; @@ -43,3 +48,7 @@ export function isJSExpression(data: any): data is JSExpression { export function isJSSlot(data: any): data is JSSlot { return data && data.type === 'JSSlot'; } + +export function isJSBlock(data: any): data is JSBlock { + return data && data.type === 'JSBlock' +} diff --git a/packages/plugin-designer/src/index.tsx b/packages/plugin-designer/src/index.tsx index c05b32500..88647e809 100644 --- a/packages/plugin-designer/src/index.tsx +++ b/packages/plugin-designer/src/index.tsx @@ -63,13 +63,11 @@ export default class DesignerPlugin extends PureComponent { } render() { const { renderer } = this.props; - console.info(renderer.schema) return ( { - const v = field.getValue(); - return v == null || typeof v === 'string'; - }, - initialValue: '', + title: 'StringSetter', recommend: true, }; export const NumberSetter = NumberPicker; @@ -43,12 +38,38 @@ export const ClassNameSetter = () => { return
这里是类名绑定
; }; -const builtinSetters = { +export const SlotSetter = () => { + return
这里是 SlotSetter
; +}; + +const builtinSetters: any = { StringSetter, NumberSetter, BoolSetter, SelectSetter, - ExpressionSetter: ExpressionSetter as any, + ExpressionSetter: { + component: ExpressionSetter, + defaultProps: { placeholder: '请输入表达式' }, + title: '表达式输入', + recommend: true, + }, + SlotSetter: { + component: SlotSetter, + title: '插槽输入', + condition: (field: any) => { + return isJSSlot(field.getValue()); + }, + initialValue: (field: any, value: any) => { + if (isJSSlot(value)) { + return value; + } + return { + type: 'JSSlot', + value: value + }; + }, + recommend: true, + }, MixinSetter, RadioGroupSetter, TextAreaSetter, diff --git a/packages/vision-polyfill/src/bundle/prototype.ts b/packages/vision-polyfill/src/bundle/prototype.ts index 090b58031..11030b8f9 100644 --- a/packages/vision-polyfill/src/bundle/prototype.ts +++ b/packages/vision-polyfill/src/bundle/prototype.ts @@ -1,5 +1,11 @@ import { ComponentType, ReactElement } from 'react'; -import { ComponentMetadata, uniqueId, registerMetadataTransducer, FieldConfig } from '@ali/lowcode-globals'; +import { + ComponentMetadata, + uniqueId, + registerMetadataTransducer, + FieldConfig, + InitialItem, +} from '@ali/lowcode-globals'; import { ComponentMeta, addBuiltinComponentAction, isComponentMeta } from '@ali/lowcode-designer'; import { OldPropConfig, @@ -11,25 +17,41 @@ import { } from './upgrade-metadata'; import { designer } from '../editor'; -const GlobalPropsConfigure: Array = []; -const Overrides: any = {}; +const GlobalPropsConfigure: Array<{ position: string; initials?: InitialItem[]; config: FieldConfig }> = []; +const Overrides: { + [componentName: string]: { + initials?: InitialItem[]; + config: any; + }; +} = {}; function addGlobalPropsConfigure(config: OldGlobalPropConfig) { + const initials: InitialItem[] = []; GlobalPropsConfigure.push({ - position: config.position, - ...upgradePropConfig(config), + position: config.position || 'bottom', + initials, + config: upgradePropConfig(config, (item) => { + initials.push(item); + }), }); } function removeGlobalPropsConfigure(name: string) { let l = GlobalPropsConfigure.length; while (l-- > 0) { - if (GlobalPropsConfigure[l].name === name) { + if (GlobalPropsConfigure[l].config.name === name) { GlobalPropsConfigure.splice(l, 1); } } } function overridePropsConfigure(componentName: string, config: OldPropConfig | OldPropConfig[]) { - Overrides[componentName] = Array.isArray(config) ? upgradeConfigure(config) : upgradePropConfig(config); + const initials: InitialItem[] = []; + const addInitial = (item: InitialItem) => { + initials.push(item); + }; + Overrides[componentName] = { + initials, + config: Array.isArray(config) ? upgradeConfigure(config, addInitial) : upgradePropConfig(config, addInitial), + }; } registerMetadataTransducer( (metadata) => { @@ -49,28 +71,28 @@ registerMetadataTransducer( metadata.configure.props = top = bottom = []; } - GlobalPropsConfigure.forEach((config) => { - const position = config.position || 'bottom'; + GlobalPropsConfigure.forEach((item) => { + const position = item.position || 'bottom'; if (position === 'top') { - top.unshift(config); + top.unshift(item.config); } else if (position === 'bottom') { - bottom.push(config); + bottom.push(item.config); } }); const override = Overrides[componentName]; if (override) { - if (Array.isArray(override)) { - metadata.configure.combined = override; + if (Array.isArray(override.config)) { + metadata.configure.combined = override.config; } else { let l = top.length; let item; while (l-- > 0) { item = top[l]; if (item.name in override) { - if (override[item.name]) { - top.splice(l, 1, override[item.name]); + if (override.config[item.name]) { + top.splice(l, 1, override.config[item.name]); } else { top.splice(l, 1); } @@ -78,6 +100,8 @@ registerMetadataTransducer( } } } + + // TODO FIXME! append override & globalConfigure initials and then unique return metadata; }, 100, diff --git a/packages/vision-polyfill/src/bundle/upgrade-metadata.ts b/packages/vision-polyfill/src/bundle/upgrade-metadata.ts index aa0bcaace..0bc665836 100644 --- a/packages/vision-polyfill/src/bundle/upgrade-metadata.ts +++ b/packages/vision-polyfill/src/bundle/upgrade-metadata.ts @@ -1,5 +1,5 @@ import { ComponentType, ReactElement, isValidElement, ComponentClass } from 'react'; -import { isI18nData, SettingTarget } from '@ali/lowcode-globals'; +import { isI18nData, SettingTarget, InitialItem, isPlainObject, isJSSlot, isJSExpression } from '@ali/lowcode-globals'; type Field = SettingTarget; @@ -34,8 +34,8 @@ export interface OldPropConfig { url?: string; }; defaultValue?: any; // => extraProps.defaultValue - initialValue?: any | ((value: any, defaultValue: any) => any); // => extraProps.initialValue - initial?: (value: any, defaultValue: any) => any // => extraProps.initialValue + initialValue?: any | ((value: any, defaultValue: any) => any); // => initials.initialValue + initial?: (value: any, defaultValue: any) => any // => initials.initialValue display?: DISPLAY_TYPE; // => fieldExtraProps fieldStyle?: DISPLAY_TYPE; // => fieldExtraProps @@ -175,7 +175,7 @@ type SetterGetter = (this: Field, value: any) => ComponentClass; type ReturnBooleanFunction = (this: Field, value: any) => boolean; -export function upgradePropConfig(config: OldPropConfig) { +export function upgradePropConfig(config: OldPropConfig, addInitial: AddIntial) { const { type, name, @@ -258,6 +258,7 @@ export function upgradePropConfig(config: OldPropConfig) { extraProps.condition = (field: Field) => !(isHidden(field) || isDisabled(field)); } if (ignore != null || disabled != null) { + // FIXME! addFilter extraProps.virtual = (field: Field) => { if (isDisabled(field)) { return true; } @@ -269,31 +270,7 @@ export function upgradePropConfig(config: OldPropConfig) { } if (type === 'group') { - newConfig.items = items ? upgradeConfigure(items) : []; - return newConfig; - } - - if (slotName) { - newConfig.name = slotName; - if (!newConfig.title && slotTitle) { - newConfig.title = slotTitle; - } - const slotSetter = { - componentName: 'SlotSetter', - initialValue: () => ({ - type: 'JSSlot', - value: initialChildren - }), - } - if (allowTextInput === false) { - newConfig.setter = slotSetter; - } else { - newConfig.setter = [{ - componentName: 'StringSetter', - initialValue, - }, slotSetter]; - } - + newConfig.items = items ? upgradeConfigure(items, addInitial) : []; return newConfig; } @@ -303,29 +280,30 @@ export function upgradePropConfig(config: OldPropConfig) { extraProps.defaultValue = initialValue; } - let initialFn = initial || initialValue; + let initialFn = (slotName ? null : initial) || initialValue; - if (accessor) { + if (accessor && !slotName) { extraProps.getValue = (field: Field, fieldValue: any) => { return accessor.call(field, fieldValue); }; if (!initialFn) { - // FIXME! - initialFn + initialFn = accessor; } } - extraProps.initialValue = (field: Field, currentValue: any, defaultValue?: any) => { - if (defaultValue === undefined) { - defaultValue = extraProps.defaultValue; - } - if (typeof initialFn === 'function') { - // ? - return initialFn.call(field, currentValue, defaultValue); - } + addInitial({ + name: slotName || name, + initial: (field: Field, currentValue: any) => { + // FIXME! read from prototype.defaultProps + const defaults = extraProps.defaultValue; - return defaultValue; - }; + if (typeof initialFn === 'function') { + return initialFn.call(field, currentValue, defaults); + } + + return currentValue == null ? defaults : currentValue; + } + }); if (sync) { extraProps.autorun = (field: Field) => { @@ -335,15 +313,61 @@ export function upgradePropConfig(config: OldPropConfig) { } } } - if (mutator) { + if (mutator && !slotName) { extraProps.setValue = (field: Field, value: any) => { mutator.call(field, value); }; } + if (slotName) { + newConfig.name = slotName; + if (!newConfig.title && slotTitle) { + newConfig.title = slotTitle; + } + const setters: any[] = [{ + componentName: 'SlotSetter', + initialValue: (field: any, value: any) => { + if (isJSSlot(value)) { + return value; + } + return { + type: 'JSSlot', + value: value == null ? initialChildren : value + }; + }, + }]; + if (allowTextInput !== false) { + setters.unshift('StringSetter'); + // FIXME: use I18nSetter + } + if (supportVariable) { + setters.push('ExpressionSetter'); + } + newConfig.setter = setters.length > 1 ? setters : setters[0]; + + return newConfig; + } + let primarySetter: any; if (type === 'composite') { - const objItems = items ? upgradeConfigure(items) : []; + const initials: InitialItem[] = []; + const objItems = items ? upgradeConfigure(items, (item) => { + initials.push(item); + }) : []; + const initial = (target: SettingTarget, value?: any) => { + // TODO: + const defaults = extraProps.defaultValue; + const data: any = {}; + initials.forEach(item => { + // FIXME! Target may be a wrong + data[item.name] = item.initial(target, isPlainObject(value) ? value[item.name] : null); + }); + return data; + } + addInitial({ + name, + initial, + }); primarySetter = { componentName: 'ObjectSetter', props: { @@ -352,12 +376,12 @@ export function upgradePropConfig(config: OldPropConfig) { }, }, initialValue: (field: Field) => { - // FIXME: read from objItems - return extraProps.initialValue(field, {}); + return initial(field, field.getValue()); }, }; } else if (setter) { if (Array.isArray(setter)) { + // FIXME! read initial from setter primarySetter = setter.map(({ setter, condition }) => { return { componentName: setter, @@ -393,7 +417,9 @@ export function upgradePropConfig(config: OldPropConfig) { return newConfig; } -export function upgradeConfigure(items: OldPropConfig[]) { +type AddIntial = (initialItem: InitialItem) => void; + +export function upgradeConfigure(items: OldPropConfig[], addInitial: AddIntial) { const configure: any[] = []; let ignoreSlotName: any = null; items.forEach((config) => { @@ -406,7 +432,7 @@ export function upgradeConfigure(items: OldPropConfig[]) { } ignoreSlotName = null; } - configure.push(upgradePropConfig(config)); + configure.push(upgradePropConfig(config, addInitial)); }); return configure; } @@ -550,7 +576,10 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) { schema, }; }); - } else if (defaultProps || initialChildren) { + } + // FIXME! defaultProps for initial input + // initialChildren maybe a function + else if (defaultProps || initialChildren) { const snippet = { screenshot: icon, label: title, @@ -566,6 +595,9 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) { experimental.snippets = [snippet]; } } + if (initialChildren) { + experimental.initialChildren = initialChildren; + } if (view) { experimental.view = view; } @@ -629,11 +661,17 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) { experimental.callbacks = callbacks; - const props = upgradeConfigure(configure || []); + const initials: InitialItem[] = []; + const props = upgradeConfigure(configure || [], (item) => { + initials.push(item); + }); + experimental.initials = initials; + const events = {}; const styles = {}; meta.configure = { props, component, events, styles }; meta.experimental = experimental; + console.info(meta); return meta; } diff --git a/packages/vision-polyfill/src/drag-engine.ts b/packages/vision-polyfill/src/drag-engine.ts index 6c3bf752b..8370297ca 100644 --- a/packages/vision-polyfill/src/drag-engine.ts +++ b/packages/vision-polyfill/src/drag-engine.ts @@ -1,5 +1,5 @@ import { designer } from './editor'; -import { DragObjectType, isNode, ExportType } from '@ali/lowcode-designer'; +import { DragObjectType, isNode, TransformStage } from '@ali/lowcode-designer'; const dragon = designer.dragon; const DragEngine = { @@ -12,7 +12,7 @@ const DragEngine = { if (isNode(r)) { return { type: DragObjectType.NodeData, - data: r.export(ExportType.ForSave), + data: r.export(TransformStage.Save), }; // FIXME! designer has bug diff --git a/packages/vision-polyfill/src/editor.ts b/packages/vision-polyfill/src/editor.ts index 2295d4e11..8c5a1afed 100644 --- a/packages/vision-polyfill/src/editor.ts +++ b/packages/vision-polyfill/src/editor.ts @@ -1,6 +1,6 @@ -import { globalContext } from '@ali/lowcode-globals'; +import { globalContext, isPlainObject, isJSBlock } from '@ali/lowcode-globals'; import Editor from '@ali/lowcode-editor-core'; -import { Designer } from '@ali/lowcode-designer'; +import { Designer, TransformStage } from '@ali/lowcode-designer'; import { registerSetters } from '@ali/lowcode-setters'; import Outline from '@ali/lowcode-plugin-outline-pane'; import SettingsPane from '@ali/lowcode-plugin-settings-pane'; @@ -10,6 +10,7 @@ import { Skeleton } from './skeleton/skeleton'; import Preview from '@ali/lowcode-plugin-sample-preview'; import SourceEditor from '@ali/lowcode-plugin-source-editor'; +import { i18nReducer } from './i18n-reducer'; registerSetters(); @@ -19,9 +20,54 @@ globalContext.register(editor, Editor); export const skeleton = new Skeleton(editor); editor.set(Skeleton, skeleton); -export const designer = new Designer({ eventPipe: editor }); +export const designer = new Designer({ editor: editor }); editor.set(Designer, designer); +designer.addPropsReducer((props, node) => { + // run initials + const initials = node.componentMeta.getMetadata().experimental?.initials; + if (initials) { + const newProps: any = {}; + initials.forEach((item) => { + // FIXME! this implements SettingTarget + const v = item.initial(node as any, props[item.name]); + if (v !== undefined) { + newProps[item.name] = v; + } + }); + return newProps; + } + return props; +}, TransformStage.Init); + +designer.addPropsReducer(i18nReducer, TransformStage.Render); + +function upgradePropsReducer(props: any) { + if (!isPlainObject(props)) { + return props; + } + const newProps: any = {}; + Object.entries(props).forEach(([key, val]) => { + if (/^__slot__/.test(key) && val === true) { + return; + } + if (isJSBlock(val)) { + if (val.value.componentName === 'Slot') { + val = { + type: 'JSSlot', + title: (val.value.props as any)?.slotTitle, + value: val.value.children + }; + } else { + val = val.value; + } + } + newProps[key] = val; + }); + return newProps; +} +designer.addPropsReducer(upgradePropsReducer, TransformStage.Init); + skeleton.add({ area: 'mainArea', name: 'designer', @@ -44,7 +90,6 @@ skeleton.add({ }, }); - skeleton.add({ area: 'topArea', type: 'Dock', diff --git a/packages/vision-polyfill/src/i18n-reducer.ts b/packages/vision-polyfill/src/i18n-reducer.ts new file mode 100644 index 000000000..693ee0cae --- /dev/null +++ b/packages/vision-polyfill/src/i18n-reducer.ts @@ -0,0 +1,35 @@ +const I18nUtil = require('@ali/ve-i18n-util'); +import Env from './env'; + +interface I18nObject { + type?: string; + use?: string; + key?: string; + [lang: string]: string | undefined; +} + +export function i18nReducer(obj?: any): any { + if (!obj) { return obj; } + if (Array.isArray(obj)) { + return obj.map((item) => i18nReducer(item)); + } + if (typeof obj === 'object') { + if (obj.type === 'i18n') { + // FIXME! use editor.get + let locale = Env.getLocale(); + if (obj.key) { + return I18nUtil.get(obj.key, locale); + } + if (locale !== 'zh_CN' && locale !== 'zh_TW' && !obj[locale]) { + locale = 'en_US'; + } + return obj[obj.use || locale] || obj.zh_CN; + } + const out: I18nObject = {}; + Object.keys(obj).forEach((key) => { + out[key] = i18nReducer(obj[key]); + }); + return out; + } + return obj; +}