From ffeebdfad358127a07c9a1ce69d93e74dcdbc4c6 Mon Sep 17 00:00:00 2001 From: kangwei Date: Fri, 14 Feb 2020 03:49:28 +0800 Subject: [PATCH] initial --- .../src/designer/document/document-context.ts | 5 +- .../designer/src/designer/document/node.ts | 74 ++++---- .../designer/src/designer/document/props.ts | 165 +++++++++++++++--- 3 files changed, 192 insertions(+), 52 deletions(-) diff --git a/packages/designer/src/designer/document/document-context.ts b/packages/designer/src/designer/document/document-context.ts index 52ae1ccc6..5ede0d7f5 100644 --- a/packages/designer/src/designer/document/document-context.ts +++ b/packages/designer/src/designer/document/document-context.ts @@ -1,5 +1,6 @@ import Project from '../project'; import { RootSchema, NodeData, isDOMText, isJSExpression } from '../schema'; +import Node from './node'; export default class DocumentContext { /** @@ -65,7 +66,9 @@ export default class DocumentContext { /** * 插入一个节点 */ - insertNode(parent: Node, thing: Node | Schema, at?: number | null, copy?: boolean): Node; + insertNode(parent: Node, thing: Node | Schema, at?: number | null, copy?: boolean): Node { + + } /** * 移除一个节点 */ diff --git a/packages/designer/src/designer/document/node.ts b/packages/designer/src/designer/document/node.ts index ab6cf0f2d..21a69d88d 100644 --- a/packages/designer/src/designer/document/node.ts +++ b/packages/designer/src/designer/document/node.ts @@ -1,5 +1,7 @@ -import { NodeSchema, isNodeSchema, NodeData, DOMText, JSExpression, PropsMap } from '../schema'; +import { obx } from '@recore/obx'; +import { NodeSchema, NodeData, PropsMap, PropsList } from '../schema'; import Props, { Prop } from './props'; +import DocumentContext from './document-context'; /** * nodeSchema are: @@ -18,6 +20,7 @@ import Props, { Prop } from './props'; * .hidden * .locked */ +const DIRECTIVES = ['condition', 'conditionGroup', 'loop', 'loopArgs', 'title', 'ignore', 'hidden', 'locked']; export default class Node { /** * 是节点实例 @@ -40,18 +43,26 @@ export default class Node { */ readonly componentName: string; + readonly props?: Props; + readonly directives?: Props; + readonly extras?: Props; + constructor(readonly document: DocumentContext, private nodeSchema: NodeSchema) { - const { componentName, id, children, props, leadingComponents, ...directives } = nodeSchema; - // clone + const { componentName, id, children, props, ...extras } = nodeSchema; this.id = id || `node$${document.nextId()}`; this.componentName = componentName; if (this.isNodeParent()) { - this._props = new Props(this, props); - this._directives = new Props(this, directives as PropsMap); + this.props = new Props(this, props); + this.directives = new Props(this, {}); + Object.keys(extras).forEach(key => { + this.directives!.add((extras as any)[key], key); + delete (extras as any)[key]; + }); + this.extras = new Props(this, extras as any); if (children) { this._children = (Array.isArray(children) ? children : [children]).map(child => { const node = this.document.createNode(child); - node.internalSetParent(this); + node.internalSetParent(this as INodeParent); return node; }); } @@ -65,20 +76,20 @@ export default class Node { return this.componentName.charAt(0) !== '#'; } - private _parent: Node | null = null; + private _parent: INodeParent | null = null; /** * 父级节点 */ - get parent(): Node | null { + get parent(): INodeParent | null { return this._parent; } /** - * 内部方法 + * 内部方法,请勿使用 * * @ignore */ - internalSetParent(parent: Node | null) { + internalSetParent(parent: INodeParent | null) { if (this._parent === parent) { return; } @@ -108,34 +119,32 @@ export default class Node { */ get children(): Node[] | null { if (this.purged) { - return []; - } - if (this._children) { - return this._children; + return null; } + return this._children; } + /* @obx.ref get component(): ReactType { return this.document.getComponent(this.tagName); } @obx.ref get prototype(): Prototype { return this.document.getPrototype(this.component, this.tagName); } + */ - @obx.ref get props(): object { + @obx.ref get propsData(): PropsMap | PropsList | null { if (!this.isNodeParent() || this.componentName === 'Fragment') { - return {}; + return null; } - // ... + return this.props?.value || null; } - private _directives: any = {}; - get directives() { - return { - condition: this.condition, - conditionGroup: this.conditionGroup, - loop: '', - }; + get directivesData(): PropsMap | null { + if (!this.isNodeParent()) { + return null; + } + return this.directives?.value as PropsMap || null; } private _conditionGroup: string | null = null; @@ -171,6 +180,8 @@ export default class Node { return this._condition; } + /* + // TODO // 外部修改,merge 进来,产生一次可恢复的历史数据 merge(data: ElementData) { this.elementData = data; @@ -197,15 +208,14 @@ export default class Node { this.children.splice(data.length).forEach(child => child.purge()); } } + */ - getProp(path: string): Prop; - getProp(path: string, useStash: true): Prop; - getProp(path: string, useStash = true): Prop | null { - return this._props!.query(path, useStash)!; + getProp(path: string, useStash: boolean = true): Prop | null { + return this.props?.query(path, useStash as any) || null; } - getProps(): Props { - return this._props; + getDirective(name: string, useStash: boolean = true): Prop | null { + return this.directives?.get(name, useStash as any) || null; } /** @@ -313,7 +323,9 @@ export default class Node { return; } this.purged = true; - this.children.forEach(child => child.purge()); + if (this._children) { + this._children.forEach(child => child.purge()); + } // TODO: others dispose... } } diff --git a/packages/designer/src/designer/document/props.ts b/packages/designer/src/designer/document/props.ts index d466656e0..6e48015e4 100644 --- a/packages/designer/src/designer/document/props.ts +++ b/packages/designer/src/designer/document/props.ts @@ -450,8 +450,8 @@ export function isProp(obj: any): obj is Prop { return obj && obj.isProp; } -export default class Props implements IPropParent { - @obx.val private readonly items: Prop[] = []; +export default class Props implements IPropParent { + @obx.val private items: Prop[] = []; @obx.ref private get maps(): Map { const maps = new Map(); if (this.items.length > 0) { @@ -474,32 +474,64 @@ export default class Props implements IPropParent { }, ); + /** + * 元素个数 + */ get size() { return this.items.length; } - private _type: 'map' | 'list' | 'unset' = 'unset'; + @computed get value(): PropsMap | PropsList | null { + if (this.items.length < 1) { + return null; + } + if (this.type === 'list') { + return this.items.map(item => ({ + spread: item.spread, + name: item.key as string, + value: item.value, + })); + } + const maps: any = {}; + this.items.forEach(prop => { + if (prop.key) { + maps[prop.key] = prop.value; + } + }); + return maps; + } - constructor(owner: T, value?: PropsMap | PropsList | null) { - if (value == null) { - this._type = 'unset'; - } else if (Array.isArray(value)) { - this._type = 'list'; + @obx type: 'map' | 'list' = 'map'; + + constructor(readonly owner: O, value?: PropsMap | PropsList | null) { + if (Array.isArray(value)) { + this.type = 'list'; value.forEach(item => {}); - } else { - this._type = 'map'; + } else if (value != null) { + this.type = 'map'; } } - delete(prop: Prop) { - const i = this.items.indexOf(prop); - if (i > -1) { - this.items.splice(i, 1); - prop.purge(); - } - } - - query(path: string, useStash = true) { + /** + * 根据 path 路径查询属性,如果没有则临时生成一个 + */ + query(path: string): Prop; + /** + * 根据 path 路径查询属性 + * + * @useStash 如果没有则临时生成一个 + */ + query(path: string, useStash: true): Prop; + /** + * 根据 path 路径查询属性 + */ + query(path: string, useStash: false): Prop | null; + /** + * 根据 path 路径查询属性 + * + * @useStash 如果没有则临时生成一个 + */ + query(path: string, useStash: boolean = true) { let matchedLength = 0; let firstMatched = null; if (this.items) { @@ -537,16 +569,109 @@ export default class Props implements IPropParent { return ret; } + /** + * 获取某个属性, 如果不存在,临时获取一个待写入 + * @param useStash 强制 + */ + get(path: string, useStash: true): Prop; + /** + * 获取某个属性 + * @param useStash 强制 + */ + get(path: string, useStash: false): Prop | null; + /** + * 获取某个属性 + */ + get(path: string): Prop | null; get(name: string, useStash = false) { return this.maps.get(name) || (useStash && this.stash.get(name)) || null; } - add(value: CompositeValue | null, key?: string | number, spread = false) { + /** + * 删除项 + */ + delete(prop: Prop): void { + const i = this.items.indexOf(prop); + if (i > -1) { + this.items.splice(i, 1); + prop.purge(); + } + } + + /** + * 删除 key + */ + deleteKey(key: string): void { + this.items = this.items.filter(item => { + if (item.key === key) { + item.purge(); + return false; + } + return true; + }); + } + + /** + * 添加值 + */ + add(value: CompositeValue | null, key?: string | number, spread = false): Prop { const prop = new Prop(this, value, key, spread); this.items.push(prop); + return prop; + } + + /** + * 是否存在 key + */ + has(key: string): boolean { + return this.maps.has(key); + } + + /** + * 迭代器 + */ + [Symbol.iterator](): { next(): { value: Prop } } { + let index = 0; + const items = this.items; + const length = items.length || 0; + return { + next() { + if (index < length) { + return { + value: items[index++], + done: false, + }; + } + return { + value: undefined as any, + done: true, + }; + }, + }; + } + + /** + * 遍历 + */ + forEach(fn: (item: Prop, key: number | string | undefined) => void): void { + this.items.forEach(item => { + return fn(item, item.key); + }); + } + + /** + * 遍历 + */ + map(fn: (item: Prop, key: number | string | undefined) => T): T[] | null { + return this.items.map(item => { + return fn(item, item.key); + }); } private purged = false; + /** + * 回收销毁 + */ purge() { if (this.purged) { return;