import { TREE_KEY } from '../constants';
import type { NodeData } from '../spec';
import type { ElementDatum, ElementType, ID } from '../types';
import { isCollapsed } from '../utils/collapsibility';
import { idOf } from '../utils/id';
import { BaseTransform } from './base-transform';
import type { DrawData, ProcedureData } from './types';
import { reassignTo } from './utils';
// 如果在任务列表中不存在,则添加到任务列表
// If it does not exist in the task list, add it to the task list
const weakAssignTo = (input: DrawData, type: 'add' | 'update', elementType: ElementType, datum: ElementDatum) => {
const typeName = `${elementType}s` as keyof ProcedureData;
const id = idOf(datum);
if (!input.add[typeName].has(id) && !input.update[typeName].has(id)) {
input[type][typeName].set(idOf(datum), datum as any);
}
};
/**
* 处理(树图)节点的收起和展开
*
* Process the collapse and expand of (tree)nodes
*/
export class CollapseExpandNode extends BaseTransform {
private getElement(id: ID) {
return this.context.element!.getElement(id);
}
private handleExpand(node: NodeData, input: DrawData) {
weakAssignTo(input, 'add', 'node', node);
if (isCollapsed(node)) return;
const id = idOf(node);
weakAssignTo(input, 'add', 'node', node);
const relatedEdges = this.context.model.getRelatedEdgesData(id);
relatedEdges.forEach((edge) => {
reassignTo(input, 'add', 'edge', edge);
});
const children = this.context.model.getChildrenData(id);
children.forEach((child) => {
this.handleExpand(child, input);
});
}
public beforeDraw(input: DrawData): DrawData {
const { graph, model } = this.context;
if (!model.model.hasTreeStructure(TREE_KEY)) return input;
const {
add: { nodes: nodesToAdd, edges: edgesToAdd },
update: { nodes: nodesToUpdate },
} = input;
const nodesToCollapse = new Map();
const nodesToExpand = new Map();
nodesToAdd.forEach((node, id) => {
if (isCollapsed(node)) nodesToCollapse.set(id, node);
});
// 如果创建了一条连接到收起的节点的边,则将其添加到待展开列表
// If an edge is created that connects to a collapsed node, add it to the list to be expanded
edgesToAdd.forEach((edge) => {
if (graph.getElementType(edge.source) !== 'node') return;
const source = graph.getNodeData(edge.source);
if (isCollapsed(source)) nodesToCollapse.set(edge.source, source);
});
nodesToUpdate.forEach((node, id) => {
const nodeElement = this.getElement(id);
if (!nodeElement) return;
const isCurrentCollapsed = nodeElement.attributes.collapsed;
if (isCollapsed(node)) {
if (!isCurrentCollapsed) nodesToCollapse.set(id, node);
} else {
if (isCurrentCollapsed) nodesToExpand.set(id, node);
}
});
const handledNodes = new Set();
nodesToCollapse.forEach((node, id) => {
// 将子节点添加到待删除列表,并删除关联的边
// Add child nodes to the list to be deleted,and delete the associated edges
const descendants = model.getDescendantsData(id);
descendants.forEach((descendant) => {
const id = idOf(descendant);
if (handledNodes.has(id)) return;
reassignTo(input, 'remove', 'node', descendant);
const relatedEdges = model.getRelatedEdgesData(id);
relatedEdges.forEach((edge) => {
reassignTo(input, 'remove', 'edge', edge);
});
handledNodes.add(id);
});
});
nodesToExpand.forEach((node, id) => {
const ancestors = model.getAncestorsData(id, TREE_KEY);
// 如果祖先节点是收起的,添加到移除列表
// If the ancestor node is collapsed, add it to the removal list
if (ancestors.some(isCollapsed)) {
reassignTo(input, 'remove', 'node', node);
return;
}
this.handleExpand(node, input);
});
return input;
}
}