mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-12-21 10:40:20 +00:00
180 lines
4.7 KiB
TypeScript
180 lines
4.7 KiB
TypeScript
import { isObject } from '@tmagic/utils';
|
|
|
|
import type Target from './Target';
|
|
import { DepTargetType, type TargetList } from './types';
|
|
|
|
export default class Watcher {
|
|
private targetsList: TargetList = {};
|
|
|
|
constructor(options?: { initialTargets?: TargetList }) {
|
|
if (options?.initialTargets) {
|
|
this.targetsList = options.initialTargets;
|
|
}
|
|
}
|
|
|
|
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: Record<string | number, any>[], deep = false, type?: DepTargetType) {
|
|
Object.values(this.targetsList).forEach((targets) => {
|
|
Object.values(targets).forEach((target) => {
|
|
if ((!type && !target.isCollectByDefault) || (type && target.type !== type)) return;
|
|
nodes.forEach((node) => {
|
|
// 先删除原有依赖,重新收集
|
|
target.removeDep(node);
|
|
this.collectItem(node, target, deep);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 清除依赖
|
|
* @param nodes 需要清除依赖的节点
|
|
*/
|
|
public clear(nodes?: Record<string | number, any>[]) {
|
|
const clearedItemsNodeIds: (string | number)[] = [];
|
|
Object.values(this.targetsList).forEach((targets) => {
|
|
Object.values(targets).forEach((target) => {
|
|
if (nodes) {
|
|
nodes.forEach((node) => {
|
|
target.removeDep(node);
|
|
|
|
if (Array.isArray(node.items) && node.items.length && !clearedItemsNodeIds.includes(node.id)) {
|
|
clearedItemsNodeIds.push(node.id);
|
|
this.clear(node.items);
|
|
}
|
|
});
|
|
} else {
|
|
target.removeDep();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
private collectItem(node: Record<string | number, any>, target: Target, deep = false) {
|
|
const collectTarget = (config: Record<string | number, any>, prop = '') => {
|
|
const doCollect = (key: string, value: any) => {
|
|
const keyIsItems = key === 'items';
|
|
const fullKey = prop ? `${prop}.${key}` : key;
|
|
|
|
if (target.isTarget(fullKey, value)) {
|
|
target.updateDep(node, 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, deep);
|
|
});
|
|
}
|
|
};
|
|
|
|
Object.entries(config).forEach(([key, value]) => {
|
|
if (typeof value === 'undefined' || value === '') return;
|
|
doCollect(key, value);
|
|
});
|
|
};
|
|
|
|
collectTarget(node);
|
|
}
|
|
}
|