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 { 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 {
}
/**
*
*/

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 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<Node>;
readonly directives?: Props<Node>;
readonly extras?: Props<Node>;
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...
}
}

View File

@ -450,8 +450,8 @@ export function isProp(obj: any): obj is Prop {
return obj && obj.isProp;
}
export default class Props<T> implements IPropParent {
@obx.val private readonly items: Prop[] = [];
export default class Props<O = any> implements IPropParent {
@obx.val private items: Prop[] = [];
@obx.ref private get maps(): Map<string, Prop> {
const maps = new Map();
if (this.items.length > 0) {
@ -474,32 +474,64 @@ export default class Props<T> 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<T> 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<T>(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;