mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
239 lines
6.2 KiB
TypeScript
239 lines
6.2 KiB
TypeScript
import { isObject } from '@tmagic/utils';
|
|
|
|
import type Target from './Target';
|
|
import { type DepExtendedData, DepTargetType, type TargetList, TargetNode } from './types';
|
|
import { traverseTarget } from './utils';
|
|
|
|
export default class Watcher {
|
|
private targetsList: TargetList = {};
|
|
private childrenProp = 'items';
|
|
private idProp = 'id';
|
|
private nameProp = 'name';
|
|
|
|
constructor(options?: { initialTargets?: TargetList; childrenProp?: string }) {
|
|
if (options?.initialTargets) {
|
|
this.targetsList = options.initialTargets;
|
|
}
|
|
|
|
if (options?.childrenProp) {
|
|
this.childrenProp = options.childrenProp;
|
|
}
|
|
}
|
|
|
|
public getTargetsList() {
|
|
return this.targetsList;
|
|
}
|
|
|
|
/**
|
|
* 获取指定类型中的所有target
|
|
* @param type 分类
|
|
* @returns Target[]
|
|
*/
|
|
public getTargets(type: string = DepTargetType.DEFAULT) {
|
|
return this.targetsList[type] || {};
|
|
}
|
|
|
|
/**
|
|
* 添加新的目标
|
|
* @param target Target
|
|
*/
|
|
public addTarget(target: Target) {
|
|
const targets = this.getTargets(target.type) || {};
|
|
this.targetsList[target.type] = targets;
|
|
targets[target.id] = target;
|
|
}
|
|
|
|
/**
|
|
* 获取指定id的target
|
|
* @param id target id
|
|
* @returns Target
|
|
*/
|
|
public getTarget(id: string | number, type: string = DepTargetType.DEFAULT) {
|
|
return this.getTargets(type)[id];
|
|
}
|
|
|
|
/**
|
|
* 判断是否存在指定id的target
|
|
* @param id target id
|
|
* @returns boolean
|
|
*/
|
|
public hasTarget(id: string | number, type: string = DepTargetType.DEFAULT) {
|
|
return Boolean(this.getTarget(id, type));
|
|
}
|
|
|
|
/**
|
|
* 判断是否存在指定类型的target
|
|
* @param type target type
|
|
* @returns boolean
|
|
*/
|
|
public hasSpecifiedTypeTarget(type: string = DepTargetType.DEFAULT): boolean {
|
|
return Object.keys(this.getTargets(type)).length > 0;
|
|
}
|
|
|
|
/**
|
|
* 删除指定id的target
|
|
* @param id target id
|
|
*/
|
|
public removeTarget(id: string | number, type: string = DepTargetType.DEFAULT) {
|
|
const targets = this.getTargets(type);
|
|
if (targets[id]) {
|
|
targets[id].destroy();
|
|
delete targets[id];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除指定分类的所有target
|
|
* @param type 分类
|
|
* @returns void
|
|
*/
|
|
public removeTargets(type: string = DepTargetType.DEFAULT) {
|
|
const targets = this.targetsList[type];
|
|
|
|
if (!targets) return;
|
|
|
|
for (const target of Object.values(targets)) {
|
|
target.destroy();
|
|
}
|
|
|
|
delete this.targetsList[type];
|
|
}
|
|
|
|
/**
|
|
* 删除所有target
|
|
*/
|
|
public clearTargets() {
|
|
Object.keys(this.targetsList).forEach((key) => {
|
|
delete this.targetsList[key];
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 收集依赖
|
|
* @param nodes 需要收集的节点
|
|
* @param deep 是否需要收集子节点
|
|
* @param type 强制收集指定类型的依赖
|
|
*/
|
|
public collect(
|
|
nodes: TargetNode[],
|
|
depExtendedData: DepExtendedData = {},
|
|
deep = false,
|
|
type?: DepTargetType | string,
|
|
) {
|
|
this.collectByCallback(nodes, type, ({ node, target }) => {
|
|
this.removeTargetDep(target, node);
|
|
this.collectItem(node, target, depExtendedData, deep);
|
|
});
|
|
}
|
|
|
|
public collectByCallback(
|
|
nodes: TargetNode[],
|
|
type: DepTargetType | string | undefined,
|
|
cb: (data: { node: TargetNode; target: Target }) => void,
|
|
) {
|
|
traverseTarget(
|
|
this.targetsList,
|
|
(target) => {
|
|
if (!type && !target.isCollectByDefault) {
|
|
return;
|
|
}
|
|
nodes.forEach((node) => {
|
|
cb({ node, target });
|
|
});
|
|
},
|
|
type,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 清除所有目标的依赖
|
|
* @param nodes 需要清除依赖的节点
|
|
*/
|
|
public clear(nodes?: TargetNode[], type?: DepTargetType | string) {
|
|
let { targetsList } = this;
|
|
|
|
if (type) {
|
|
targetsList = {
|
|
[type]: this.getTargets(type),
|
|
};
|
|
}
|
|
|
|
const clearedItemsNodeIds: (string | number)[] = [];
|
|
traverseTarget(targetsList, (target) => {
|
|
if (nodes) {
|
|
nodes.forEach((node) => {
|
|
target.removeDep(node[this.idProp]);
|
|
|
|
if (
|
|
Array.isArray(node[this.childrenProp]) &&
|
|
node[this.childrenProp].length &&
|
|
!clearedItemsNodeIds.includes(node[this.idProp])
|
|
) {
|
|
clearedItemsNodeIds.push(node[this.idProp]);
|
|
this.clear(node[this.childrenProp]);
|
|
}
|
|
});
|
|
} else {
|
|
target.removeDep();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 清除指定类型的依赖
|
|
* @param type 类型
|
|
* @param nodes 需要清除依赖的节点
|
|
*/
|
|
public clearByType(type: DepTargetType | string, nodes?: TargetNode[]) {
|
|
this.clear(nodes, type);
|
|
}
|
|
|
|
public collectItem(node: TargetNode, target: Target, depExtendedData: DepExtendedData = {}, deep = false) {
|
|
const collectTarget = (config: Record<string | number, any>, prop = '') => {
|
|
const doCollect = (key: string, value: any) => {
|
|
const keyIsItems = key === this.childrenProp;
|
|
const fullKey = prop ? `${prop}.${key}` : key;
|
|
|
|
if (target.isTarget(fullKey, value)) {
|
|
target.updateDep({
|
|
id: node[this.idProp],
|
|
name: `${node[this.nameProp] || node[this.idProp]}`,
|
|
data: depExtendedData,
|
|
key: fullKey,
|
|
});
|
|
} else if (!keyIsItems && Array.isArray(value)) {
|
|
value.forEach((item, index) => {
|
|
if (isObject(item)) {
|
|
collectTarget(item, `${fullKey}[${index}]`);
|
|
}
|
|
});
|
|
} else if (isObject(value)) {
|
|
collectTarget(value, fullKey);
|
|
}
|
|
|
|
if (keyIsItems && deep && Array.isArray(value)) {
|
|
value.forEach((child) => {
|
|
this.collectItem(child, target, depExtendedData, deep);
|
|
});
|
|
}
|
|
};
|
|
|
|
Object.entries(config).forEach(([key, value]) => {
|
|
if (typeof value === 'undefined' || value === '') return;
|
|
doCollect(key, value);
|
|
});
|
|
};
|
|
|
|
collectTarget(node);
|
|
}
|
|
|
|
public removeTargetDep(target: Target, node: TargetNode, key?: string | number) {
|
|
target.removeDep(node[this.idProp], key);
|
|
if (typeof key === 'undefined' && Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length) {
|
|
node[this.childrenProp].forEach((item: TargetNode) => {
|
|
this.removeTargetDep(target, item, key);
|
|
});
|
|
}
|
|
}
|
|
}
|