mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-03-01 22:10:27 +00:00
282 lines
6.6 KiB
TypeScript
282 lines
6.6 KiB
TypeScript
import { obx, computed } from '@ali/lowcode-editor-core';
|
|
import { Node, ParentalNode } from './node';
|
|
import { TransformStage } from './transform-stage';
|
|
import { NodeData, isNodeSchema } from '@ali/lowcode-types';
|
|
import { shallowEqual } from '@ali/lowcode-utils';
|
|
import { EventEmitter } from 'events';
|
|
|
|
export class NodeChildren {
|
|
@obx.val private children: Node[];
|
|
private emitter = new EventEmitter();
|
|
|
|
constructor(readonly owner: ParentalNode, data: NodeData | NodeData[]) {
|
|
this.children = (Array.isArray(data) ? data : [data]).map(child => {
|
|
return this.owner.document.createNode(child);
|
|
});
|
|
}
|
|
|
|
interalInitParent() {
|
|
this.children.forEach(child => child.internalSetParent(this.owner));
|
|
}
|
|
|
|
/**
|
|
* 导出 schema
|
|
*/
|
|
export(stage: TransformStage = TransformStage.Save): NodeData[] {
|
|
return this.children.map(node => {
|
|
const data = node.export(stage);
|
|
if (node.isLeaf() && TransformStage.Save === stage) {
|
|
// FIXME: filter empty
|
|
return data.children as NodeData;
|
|
}
|
|
return data;
|
|
});
|
|
}
|
|
|
|
import(data?: NodeData | NodeData[], checkId = false) {
|
|
data = data ? (Array.isArray(data) ? data : [data]) : [];
|
|
|
|
const originChildren = this.children.slice();
|
|
this.children.forEach(child => child.internalSetParent(null));
|
|
|
|
const children = new Array<Node>(data.length);
|
|
for (let i = 0, l = data.length; i < l; i++) {
|
|
const child = originChildren[i];
|
|
const item = data[i];
|
|
|
|
let node: Node | undefined;
|
|
if (isNodeSchema(item) && !checkId && child && child.componentName === item.componentName) {
|
|
node = child;
|
|
node.import(item);
|
|
} else {
|
|
node = this.owner.document.createNode(item);
|
|
}
|
|
children[i] = node;
|
|
}
|
|
|
|
this.children = children;
|
|
this.interalInitParent();
|
|
if (!shallowEqual(children, originChildren)) {
|
|
this.emitter.emit('change');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 元素个数
|
|
*/
|
|
@computed get size(): number {
|
|
return this.children.length;
|
|
}
|
|
|
|
/**
|
|
* 是否空
|
|
*/
|
|
@computed isEmpty() {
|
|
return this.size < 1;
|
|
}
|
|
|
|
@computed notEmpty() {
|
|
return this.size > 0;
|
|
}
|
|
|
|
/**
|
|
* 删除一个节点
|
|
*/
|
|
delete(node: Node, purge = false): boolean {
|
|
const i = this.children.indexOf(node);
|
|
if (i < 0) {
|
|
return false;
|
|
}
|
|
const deleted = this.children.splice(i, 1)[0];
|
|
if (purge) {
|
|
// should set parent null
|
|
deleted.internalSetParent(null);
|
|
deleted.purge();
|
|
}
|
|
this.emitter.emit('change');
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 插入一个节点,返回新长度
|
|
*/
|
|
insert(node: Node, at?: number | null): void {
|
|
const children = this.children;
|
|
let index = at == null || at === -1 ? children.length : at;
|
|
|
|
const i = children.indexOf(node);
|
|
|
|
if (i < 0) {
|
|
if (index < children.length) {
|
|
children.splice(index, 0, node);
|
|
} else {
|
|
children.push(node);
|
|
}
|
|
node.internalSetParent(this.owner);
|
|
} else {
|
|
if (index > i) {
|
|
index -= 1;
|
|
}
|
|
|
|
if (index === i) {
|
|
return;
|
|
}
|
|
|
|
children.splice(i, 1);
|
|
children.splice(index, 0, node);
|
|
}
|
|
|
|
this.emitter.emit('change');
|
|
|
|
// check condition group
|
|
if (node.conditionGroup) {
|
|
if (
|
|
!(
|
|
// just sort at condition group
|
|
(
|
|
(node.prevSibling && node.prevSibling.conditionGroup === node.conditionGroup) ||
|
|
(node.nextSibling && node.nextSibling.conditionGroup === node.conditionGroup)
|
|
)
|
|
)
|
|
) {
|
|
node.setConditionGroup(null);
|
|
}
|
|
}
|
|
if (node.prevSibling && node.nextSibling) {
|
|
const conditionGroup = node.prevSibling.conditionGroup;
|
|
// insert at condition group
|
|
if (conditionGroup && conditionGroup === node.nextSibling.conditionGroup) {
|
|
node.setConditionGroup(conditionGroup);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 取得节点索引编号
|
|
*/
|
|
indexOf(node: Node): number {
|
|
return this.children.indexOf(node);
|
|
}
|
|
|
|
/**
|
|
* 根据索引获得节点
|
|
*/
|
|
get(index: number): Node | null {
|
|
return this.children[index] || null;
|
|
}
|
|
|
|
/**
|
|
* 是否存在节点
|
|
*/
|
|
has(node: Node) {
|
|
return this.indexOf(node) > -1;
|
|
}
|
|
|
|
/**
|
|
* 迭代器
|
|
*/
|
|
[Symbol.iterator](): { next(): { value: Node } } {
|
|
let index = 0;
|
|
const children = this.children;
|
|
const length = children.length || 0;
|
|
return {
|
|
next() {
|
|
if (index < length) {
|
|
return {
|
|
value: children[index++],
|
|
done: false,
|
|
};
|
|
}
|
|
return {
|
|
value: undefined as any,
|
|
done: true,
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 遍历
|
|
*/
|
|
forEach(fn: (item: Node, index: number) => void): void {
|
|
this.children.forEach((child, index) => {
|
|
return fn(child, index);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 遍历
|
|
*/
|
|
map<T>(fn: (item: Node, index: number) => T): T[] | null {
|
|
return this.children.map((child, index) => {
|
|
return fn(child, index);
|
|
});
|
|
}
|
|
|
|
every(fn: (item: Node, index: number) => any): boolean {
|
|
return this.children.every((child, index) => fn(child, index));
|
|
}
|
|
|
|
some(fn: (item: Node, index: number) => any): boolean {
|
|
return this.children.some((child, index) => fn(child, index));
|
|
}
|
|
|
|
filter(fn: (item: Node, index: number) => item is Node) {
|
|
return this.children.filter(fn);
|
|
}
|
|
|
|
mergeChildren(remover: () => any, adder: (children: Node[]) => NodeData[] | null, sorter: () => any) {
|
|
let changed = false;
|
|
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();
|
|
}
|
|
});
|
|
changed = true;
|
|
}
|
|
}
|
|
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);
|
|
});
|
|
changed = true;
|
|
}
|
|
}
|
|
if (sorter) {
|
|
this.children = this.children.sort(sorter);
|
|
changed = true;
|
|
}
|
|
if (changed) {
|
|
this.emitter.emit('change');
|
|
}
|
|
}
|
|
|
|
onChange(fn: () => void) {
|
|
this.emitter.on('change', fn);
|
|
return () => {
|
|
this.emitter.removeListener('change', fn);
|
|
};
|
|
}
|
|
|
|
private purged = false;
|
|
/**
|
|
* 回收销毁
|
|
*/
|
|
purge() {
|
|
if (this.purged) {
|
|
return;
|
|
}
|
|
this.purged = true;
|
|
this.children.forEach(child => child.purge());
|
|
}
|
|
}
|