This commit is contained in:
kangwei 2020-02-14 03:49:28 +08:00
parent 625cba310c
commit ffeebdfad3
3 changed files with 192 additions and 52 deletions

View File

@ -1,5 +1,6 @@
import Project from '../project'; import Project from '../project';
import { RootSchema, NodeData, isDOMText, isJSExpression } from '../schema'; import { RootSchema, NodeData, isDOMText, isJSExpression } from '../schema';
import Node from './node';
export default class DocumentContext { 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 {
}
/** /**
* *
*/ */

View File

@ -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 Props, { Prop } from './props';
import DocumentContext from './document-context';
/** /**
* nodeSchema are: * nodeSchema are:
@ -18,6 +20,7 @@ import Props, { Prop } from './props';
* .hidden * .hidden
* .locked * .locked
*/ */
const DIRECTIVES = ['condition', 'conditionGroup', 'loop', 'loopArgs', 'title', 'ignore', 'hidden', 'locked'];
export default class Node { export default class Node {
/** /**
* *
@ -40,18 +43,26 @@ export default class Node {
*/ */
readonly componentName: string; readonly componentName: string;
readonly props?: Props<Node>;
readonly directives?: Props<Node>;
readonly extras?: Props<Node>;
constructor(readonly document: DocumentContext, private nodeSchema: NodeSchema) { constructor(readonly document: DocumentContext, private nodeSchema: NodeSchema) {
const { componentName, id, children, props, leadingComponents, ...directives } = nodeSchema; const { componentName, id, children, props, ...extras } = nodeSchema;
// clone
this.id = id || `node$${document.nextId()}`; this.id = id || `node$${document.nextId()}`;
this.componentName = componentName; this.componentName = componentName;
if (this.isNodeParent()) { if (this.isNodeParent()) {
this._props = new Props(this, props); this.props = new Props(this, props);
this._directives = new Props(this, directives as PropsMap); 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) { if (children) {
this._children = (Array.isArray(children) ? children : [children]).map(child => { this._children = (Array.isArray(children) ? children : [children]).map(child => {
const node = this.document.createNode(child); const node = this.document.createNode(child);
node.internalSetParent(this); node.internalSetParent(this as INodeParent);
return node; return node;
}); });
} }
@ -65,20 +76,20 @@ export default class Node {
return this.componentName.charAt(0) !== '#'; 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; return this._parent;
} }
/** /**
* * 使
* *
* @ignore * @ignore
*/ */
internalSetParent(parent: Node | null) { internalSetParent(parent: INodeParent | null) {
if (this._parent === parent) { if (this._parent === parent) {
return; return;
} }
@ -108,34 +119,32 @@ export default class Node {
*/ */
get children(): Node[] | null { get children(): Node[] | null {
if (this.purged) { if (this.purged) {
return []; return null;
}
if (this._children) {
return this._children;
} }
return this._children;
} }
/*
@obx.ref get component(): ReactType { @obx.ref get component(): ReactType {
return this.document.getComponent(this.tagName); return this.document.getComponent(this.tagName);
} }
@obx.ref get prototype(): Prototype { @obx.ref get prototype(): Prototype {
return this.document.getPrototype(this.component, this.tagName); 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') { if (!this.isNodeParent() || this.componentName === 'Fragment') {
return {}; return null;
} }
// ... return this.props?.value || null;
} }
private _directives: any = {}; get directivesData(): PropsMap | null {
get directives() { if (!this.isNodeParent()) {
return { return null;
condition: this.condition, }
conditionGroup: this.conditionGroup, return this.directives?.value as PropsMap || null;
loop: '',
};
} }
private _conditionGroup: string | null = null; private _conditionGroup: string | null = null;
@ -171,6 +180,8 @@ export default class Node {
return this._condition; return this._condition;
} }
/*
// TODO
// 外部修改merge 进来,产生一次可恢复的历史数据 // 外部修改merge 进来,产生一次可恢复的历史数据
merge(data: ElementData) { merge(data: ElementData) {
this.elementData = data; this.elementData = data;
@ -197,15 +208,14 @@ export default class Node {
this.children.splice(data.length).forEach(child => child.purge()); this.children.splice(data.length).forEach(child => child.purge());
} }
} }
*/
getProp(path: string): Prop; getProp(path: string, useStash: boolean = true): Prop | null {
getProp(path: string, useStash: true): Prop; return this.props?.query(path, useStash as any) || null;
getProp(path: string, useStash = true): Prop | null {
return this._props!.query(path, useStash)!;
} }
getProps(): Props { getDirective(name: string, useStash: boolean = true): Prop | null {
return this._props; return this.directives?.get(name, useStash as any) || null;
} }
/** /**
@ -313,7 +323,9 @@ export default class Node {
return; return;
} }
this.purged = true; this.purged = true;
this.children.forEach(child => child.purge()); if (this._children) {
this._children.forEach(child => child.purge());
}
// TODO: others dispose... // TODO: others dispose...
} }
} }

View File

@ -450,8 +450,8 @@ export function isProp(obj: any): obj is Prop {
return obj && obj.isProp; return obj && obj.isProp;
} }
export default class Props<T> implements IPropParent { export default class Props<O = any> implements IPropParent {
@obx.val private readonly items: Prop[] = []; @obx.val private items: Prop[] = [];
@obx.ref private get maps(): Map<string, Prop> { @obx.ref private get maps(): Map<string, Prop> {
const maps = new Map(); const maps = new Map();
if (this.items.length > 0) { if (this.items.length > 0) {
@ -474,32 +474,64 @@ export default class Props<T> implements IPropParent {
}, },
); );
/**
*
*/
get size() { get size() {
return this.items.length; 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) { @obx type: 'map' | 'list' = 'map';
if (value == null) {
this._type = 'unset'; constructor(readonly owner: O, value?: PropsMap | PropsList | null) {
} else if (Array.isArray(value)) { if (Array.isArray(value)) {
this._type = 'list'; this.type = 'list';
value.forEach(item => {}); value.forEach(item => {});
} else { } else if (value != null) {
this._type = 'map'; this.type = 'map';
} }
} }
delete(prop: Prop) { /**
const i = this.items.indexOf(prop); * path
if (i > -1) { */
this.items.splice(i, 1); query(path: string): Prop;
prop.purge(); /**
} * path
} *
* @useStash
query(path: string, useStash = true) { */
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 matchedLength = 0;
let firstMatched = null; let firstMatched = null;
if (this.items) { if (this.items) {
@ -537,16 +569,109 @@ export default class Props<T> implements IPropParent {
return ret; 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) { get(name: string, useStash = false) {
return this.maps.get(name) || (useStash && this.stash.get(name)) || null; 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); const prop = new Prop(this, value, key, spread);
this.items.push(prop); 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<T>(fn: (item: Prop, key: number | string | undefined) => T): T[] | null {
return this.items.map(item => {
return fn(item, item.key);
});
} }
private purged = false; private purged = false;
/**
*
*/
purge() { purge() {
if (this.purged) { if (this.purged) {
return; return;