@antv/g6
Version:
A Graph Visualization Framework in JavaScript
716 lines • 33.3 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ElementController = void 0;
const g_1 = require("@antv/g");
const util_1 = require("@antv/util");
const constants_1 = require("../constants");
const element_1 = require("../constants/element");
const get_1 = require("../registry/get");
const cache_1 = require("../utils/cache");
const change_1 = require("../utils/change");
const collapsibility_1 = require("../utils/collapsibility");
const element_2 = require("../utils/element");
const event_1 = require("../utils/event");
const id_1 = require("../utils/id");
const palette_1 = require("../utils/palette");
const position_1 = require("../utils/position");
const print_1 = require("../utils/print");
const style_1 = require("../utils/style");
const theme_1 = require("../utils/theme");
const vector_1 = require("../utils/vector");
const visibility_1 = require("../utils/visibility");
class ElementController {
constructor(context) {
this.elementMap = {};
this.shapeTypeMap = {};
this.paletteStyle = {};
this.defaultStyle = {};
this.stateStyle = {};
this.visibilityCache = new WeakMap();
this.context = context;
}
init() {
this.initContainer();
}
initContainer() {
if (!this.container || this.container.destroyed) {
const { canvas } = this.context;
this.container = canvas.appendChild(new g_1.Group({ className: 'elements' }));
}
}
emit(event, context) {
if (context.silence)
return;
(0, event_1.emit)(this.context.graph, event);
}
forEachElementData(callback) {
element_1.ELEMENT_TYPES.forEach((elementType) => {
const elementData = this.context.model.getElementsDataByType(elementType);
callback(elementType, elementData);
});
}
getElementType(elementType, datum) {
var _a;
const { options, graph } = this.context;
const userDefinedType = ((_a = options[elementType]) === null || _a === void 0 ? void 0 : _a.type) || datum.type;
if (!userDefinedType) {
if (elementType === 'edge')
return 'line';
// node / combo
else
return 'circle';
}
if (typeof userDefinedType === 'string')
return userDefinedType;
// @ts-expect-error skip type check
return userDefinedType.call(graph, datum);
}
getTheme(elementType) {
return (0, theme_1.themeOf)(this.context.options)[elementType] || {};
}
getThemeStyle(elementType) {
return this.getTheme(elementType).style || {};
}
getThemeStateStyle(elementType, states) {
const { state = {} } = this.getTheme(elementType);
return Object.assign({}, ...states.map((name) => state[name] || {}));
}
computePaletteStyle() {
const { options } = this.context;
this.paletteStyle = {};
this.forEachElementData((elementType, elementData) => {
var _a, _b;
const palette = Object.assign({}, (0, palette_1.parsePalette)((_a = this.getTheme(elementType)) === null || _a === void 0 ? void 0 : _a.palette), (0, palette_1.parsePalette)((_b = options[elementType]) === null || _b === void 0 ? void 0 : _b.palette));
if (palette === null || palette === void 0 ? void 0 : palette.field) {
Object.assign(this.paletteStyle, (0, palette_1.assignColorByPalette)(elementData, palette));
}
});
}
getPaletteStyle(elementType, id) {
const color = this.paletteStyle[id];
if (!color)
return {};
if (elementType === 'edge')
return { stroke: color };
return { fill: color };
}
/**
* <zh/> 计算单个元素的默认样式
*
* <en/> compute default style of single element
*/
computeElementDefaultStyle(elementType, context) {
var _a;
const { options } = this.context;
const defaultStyle = ((_a = options[elementType]) === null || _a === void 0 ? void 0 : _a.style) || {};
if ('transform' in defaultStyle && Array.isArray(defaultStyle.transform)) {
defaultStyle.transform = [...defaultStyle.transform];
}
this.defaultStyle[(0, id_1.idOf)(context.datum)] = (0, style_1.computeElementCallbackStyle)(defaultStyle, context);
}
computeElementsDefaultStyle(ids) {
const { graph } = this.context;
this.forEachElementData((elementType, elementData) => {
const length = elementData.length;
for (let i = 0; i < length; i++) {
const datum = elementData[i];
if (ids === undefined || ids.includes((0, id_1.idOf)(datum))) {
this.computeElementDefaultStyle(elementType, { datum, graph });
}
}
});
}
getDefaultStyle(id) {
return this.defaultStyle[id] || {};
}
getElementState(id) {
try {
const { model } = this.context;
return model.getElementState(id);
}
catch (_a) {
return [];
}
}
/**
* <zh/> 获取单个元素的单个状态的样式
*
* <en/> get single state style of single element
*/
getElementStateStyle(elementType, state, context) {
var _a, _b;
const { options } = this.context;
const stateStyle = ((_b = (_a = options[elementType]) === null || _a === void 0 ? void 0 : _a.state) === null || _b === void 0 ? void 0 : _b[state]) || {};
return (0, style_1.computeElementCallbackStyle)(stateStyle, context);
}
/**
* <zh/> 计算单个元素的合并状态样式
*
* <en/> compute merged state style of single element
*/
computeElementStatesStyle(elementType, states, context) {
this.stateStyle[(0, id_1.idOf)(context.datum)] = Object.assign({}, ...states.map((state) => this.getElementStateStyle(elementType, state, context)));
}
/**
* <zh/> 计算全部元素的状态样式
*
* <en/> compute state style of all elements
* @param ids - <zh/> 计算指定元素的状态样式 | <en/> compute state style of specified elements
*/
computeElementsStatesStyle(ids) {
const { graph } = this.context;
this.forEachElementData((elementType, elementData) => {
const length = elementData.length;
for (let i = 0; i < length; i++) {
const datum = elementData[i];
if (ids === undefined || ids.includes((0, id_1.idOf)(datum))) {
const states = this.getElementState((0, id_1.idOf)(datum));
this.computeElementStatesStyle(elementType, states, { datum, graph });
}
}
});
}
getStateStyle(id) {
return this.stateStyle[id] || {};
}
computeStyle(stage, ids) {
const skip = ['translate', 'zIndex'];
if (stage && skip.includes(stage))
return;
this.computePaletteStyle();
this.computeElementsDefaultStyle(ids);
this.computeElementsStatesStyle(ids);
}
getElement(id) {
return this.elementMap[id];
}
getNodes() {
return this.context.model.getNodeData().map(({ id }) => this.elementMap[id]);
}
getEdges() {
return this.context.model.getEdgeData().map((edge) => this.elementMap[(0, id_1.idOf)(edge)]);
}
getCombos() {
return this.context.model.getComboData().map(({ id }) => this.elementMap[id]);
}
getElementComputedStyle(elementType, datum) {
const id = (0, id_1.idOf)(datum);
// 优先级(从低到高) Priority (from low to high):
const themeStyle = this.getThemeStyle(elementType);
const paletteStyle = this.getPaletteStyle(elementType, id);
const dataStyle = datum.style || {};
const defaultStyle = this.getDefaultStyle(id);
const themeStateStyle = this.getThemeStateStyle(elementType, this.getElementState(id));
const stateStyle = this.getStateStyle(id);
const style = Object.assign({}, themeStyle, paletteStyle, dataStyle, defaultStyle, themeStateStyle, stateStyle);
if (elementType === 'combo') {
const childrenData = this.context.model.getChildrenData(id);
const isCollapsed = !!style.collapsed;
const childrenNode = isCollapsed ? [] : childrenData.map(id_1.idOf).filter((id) => this.getElement(id));
Object.assign(style, { childrenNode, childrenData });
}
return style;
}
getDrawData(context) {
this.init();
const data = this.computeChangesAndDrawData(context);
if (!data)
return null;
const { type = 'draw', stage = type } = context;
this.markDestroyElement(data.drawData);
// 计算样式 / Calculate style
this.computeStyle(stage);
return { type, stage, data };
}
/**
* <zh/> 开始绘制流程
*
* <en/> start render process
*/
draw(context = { animation: true }) {
const drawData = this.getDrawData(context);
if (!drawData)
return;
const { data: { drawData: { add, update, remove }, }, } = drawData;
this.destroyElements(remove, context);
this.createElements(add, context);
this.updateElements(update, context);
return this.setAnimationTask(context, drawData);
}
preLayoutDraw() {
return __awaiter(this, arguments, void 0, function* (context = { animation: true }) {
var _a, _b;
const preResult = this.getDrawData(context);
if (!preResult)
return;
const { data: { drawData }, } = preResult;
yield ((_b = (_a = this.context.layout) === null || _a === void 0 ? void 0 : _a.preLayout) === null || _b === void 0 ? void 0 : _b.call(_a, drawData));
const { add, update, remove } = drawData;
this.destroyElements(remove, context);
this.createElements(add, context);
this.updateElements(update, context);
return this.setAnimationTask(context, preResult);
});
}
setAnimationTask(context, data) {
const { animation, silence } = context;
const { data: { dataChanges, drawData }, stage, type, } = data;
return this.context.animation.animate(animation, silence
? {}
: {
before: () => this.emit(new event_1.GraphLifeCycleEvent(constants_1.GraphEvent.BEFORE_DRAW, {
dataChanges,
animation,
stage,
render: type === 'render',
}), context),
beforeAnimate: (animation) => this.emit(new event_1.AnimateEvent(constants_1.GraphEvent.BEFORE_ANIMATE, constants_1.AnimationType.DRAW, animation, drawData), context),
afterAnimate: (animation) => this.emit(new event_1.AnimateEvent(constants_1.GraphEvent.AFTER_ANIMATE, constants_1.AnimationType.DRAW, animation, drawData), context),
after: () => this.emit(new event_1.GraphLifeCycleEvent(constants_1.GraphEvent.AFTER_DRAW, {
dataChanges,
animation,
stage,
render: type === 'render',
firstRender: this.context.graph.rendered === false,
}), context),
});
}
computeChangesAndDrawData(context) {
const { model } = this.context;
const dataChanges = model.getChanges();
const tasks = (0, change_1.reduceDataChanges)(dataChanges);
if (tasks.length === 0)
return null;
const { NodeAdded = [], NodeUpdated = [], NodeRemoved = [], EdgeAdded = [], EdgeUpdated = [], EdgeRemoved = [], ComboAdded = [], ComboUpdated = [], ComboRemoved = [], } = (0, util_1.groupBy)(tasks, (change) => change.type);
const dataOf = (data) => new Map(data.map((datum) => {
const data = datum.value;
return [(0, id_1.idOf)(data), data];
}));
const input = {
add: {
nodes: dataOf(NodeAdded),
edges: dataOf(EdgeAdded),
combos: dataOf(ComboAdded),
},
update: {
nodes: dataOf(NodeUpdated),
edges: dataOf(EdgeUpdated),
combos: dataOf(ComboUpdated),
},
remove: {
nodes: dataOf(NodeRemoved),
edges: dataOf(EdgeRemoved),
combos: dataOf(ComboRemoved),
},
};
const drawData = this.transformData(input, context);
// 清空变更 / Clear changes
model.clearChanges();
return { dataChanges, drawData };
}
transformData(input, context) {
const transforms = this.context.transform.getTransformInstance();
return Object.values(transforms).reduce((data, transform) => transform.beforeDraw(data, context), input);
}
createElement(elementType, datum, context) {
var _a;
const id = (0, id_1.idOf)(datum);
const currentElement = this.getElement(id);
if (currentElement)
return;
const type = this.getElementType(elementType, datum);
const style = this.getElementComputedStyle(elementType, datum);
// get shape constructor
const Ctor = (0, get_1.getExtension)(elementType, type);
if (!Ctor)
return print_1.print.warn(`The element ${type} of ${elementType} is not registered.`);
this.emit(new event_1.ElementLifeCycleEvent(constants_1.GraphEvent.BEFORE_ELEMENT_CREATE, elementType, datum), context);
const element = this.container.appendChild(new Ctor({
id,
context: this.context,
style,
}));
this.shapeTypeMap[id] = type;
this.elementMap[id] = element;
const { stage = 'enter' } = context;
(_a = this.context.animation) === null || _a === void 0 ? void 0 : _a.add({
element,
elementType,
stage,
originalStyle: Object.assign({}, element.attributes),
updatedStyle: style,
}, {
after: () => {
var _a;
this.emit(new event_1.ElementLifeCycleEvent(constants_1.GraphEvent.AFTER_ELEMENT_CREATE, elementType, datum), context);
(_a = element.onCreate) === null || _a === void 0 ? void 0 : _a.call(element);
},
});
}
createElements(data, context) {
const { nodes, edges, combos } = data;
const iteration = [
['node', nodes],
['combo', combos],
['edge', edges],
];
iteration.forEach(([elementType, elementData]) => {
elementData.forEach((datum) => this.createElement(elementType, datum, context));
});
}
getUpdateStageStyle(elementType, datum, context) {
const { stage = 'update' } = context;
// 优化 translate 阶段,直接返回 x, y, z,避免计算样式
// Optimize the translate stage, return x, y, z directly to avoid calculating style
if (stage === 'translate') {
if (elementType === 'node' || elementType === 'combo') {
const { style: { x = 0, y = 0, z = 0 } = {} } = datum;
return { x, y, z };
}
else
return {};
}
return this.getElementComputedStyle(elementType, datum);
}
updateElement(elementType, datum, context) {
var _a;
const id = (0, id_1.idOf)(datum);
const { stage = 'update' } = context;
const element = this.getElement(id);
if (!element)
return () => null;
this.emit(new event_1.ElementLifeCycleEvent(constants_1.GraphEvent.BEFORE_ELEMENT_UPDATE, elementType, datum), context);
const type = this.getElementType(elementType, datum);
const style = this.getUpdateStageStyle(elementType, datum, context);
// 如果类型不同,需要先销毁原有元素,再创建新元素
// If the type is different, you need to destroy the original element first, and then create a new element
if (this.shapeTypeMap[id] !== type) {
element.destroy();
delete this.shapeTypeMap[id];
delete this.elementMap[id];
this.createElement(elementType, datum, { animation: false, silence: true });
}
const exactStage = stage !== 'visibility' ? stage : style.visibility === 'hidden' ? 'hide' : 'show';
// 避免立即将 visibility 设置为 hidden,导致元素不可见,而是在 after 阶段再设置
// Avoid setting visibility to hidden immediately, causing the element to be invisible, but set it in the after phase
if (exactStage === 'hide')
delete style['visibility'];
(_a = this.context.animation) === null || _a === void 0 ? void 0 : _a.add({
element,
elementType,
stage: exactStage,
originalStyle: Object.assign({}, element.attributes),
updatedStyle: style,
}, {
before: () => {
// 通过 elementMap[id] 访问最新的 element,防止 type 不同导致的 element 丢失
// Access the latest element through elementMap[id] to prevent the loss of element caused by different types
const element = this.elementMap[id];
if (stage !== 'collapse')
(0, element_2.updateStyle)(element, style);
if (stage === 'visibility') {
// 缓存原始透明度 / Cache original opacity
// 会在 animation controller 中访问该缓存值 / The cached value will be accessed in the animation controller
if (!(0, cache_1.hasCachedStyle)(element, 'opacity'))
(0, cache_1.cacheStyle)(element, 'opacity');
this.visibilityCache.set(element, exactStage === 'show' ? 'visible' : 'hidden');
if (exactStage === 'show')
(0, visibility_1.setVisibility)(element, 'visible');
}
},
after: () => {
var _a;
const element = this.elementMap[id];
if (stage === 'collapse')
(0, element_2.updateStyle)(element, style);
if (exactStage === 'hide')
(0, visibility_1.setVisibility)(element, this.visibilityCache.get(element));
this.emit(new event_1.ElementLifeCycleEvent(constants_1.GraphEvent.AFTER_ELEMENT_UPDATE, elementType, datum), context);
(_a = element.onUpdate) === null || _a === void 0 ? void 0 : _a.call(element);
},
});
}
updateElements(data, context) {
const { nodes, edges, combos } = data;
const iteration = [
['node', nodes],
['combo', combos],
['edge', edges],
];
iteration.forEach(([elementType, elementData]) => {
elementData.forEach((datum) => this.updateElement(elementType, datum, context));
});
}
/**
* <zh/> 标记销毁元素
*
* <en/> mark destroy element
* @param data - <zh/> 绘制数据 | <en/> draw data
*/
markDestroyElement(data) {
Object.values(data.remove).forEach((elementData) => {
elementData.forEach((datum) => {
const id = (0, id_1.idOf)(datum);
const element = this.getElement(id);
if (element)
(0, element_2.markToBeDestroyed)(element);
});
});
}
destroyElement(elementType, datum, context) {
var _a;
const { stage = 'exit' } = context;
const id = (0, id_1.idOf)(datum);
const element = this.elementMap[id];
if (!element)
return () => null;
this.emit(new event_1.ElementLifeCycleEvent(constants_1.GraphEvent.BEFORE_ELEMENT_DESTROY, elementType, datum), context);
(_a = this.context.animation) === null || _a === void 0 ? void 0 : _a.add({
element,
elementType,
stage,
originalStyle: Object.assign({}, element.attributes),
updatedStyle: {},
}, {
after: () => {
var _a;
this.clearElement(id);
element.destroy();
(_a = element.onDestroy) === null || _a === void 0 ? void 0 : _a.call(element);
this.emit(new event_1.ElementLifeCycleEvent(constants_1.GraphEvent.AFTER_ELEMENT_DESTROY, elementType, datum), context);
},
});
}
destroyElements(data, context) {
const { nodes, edges, combos } = data;
const iteration = [
['combo', combos],
['edge', edges],
['node', nodes],
];
iteration.forEach(([elementType, elementData]) => {
elementData.forEach((datum) => this.destroyElement(elementType, datum, context));
});
// TODO 重新计算色板样式,如果是分组色板,则不需要重新计算
}
clearElement(id) {
delete this.paletteStyle[id];
delete this.defaultStyle[id];
delete this.stateStyle[id];
delete this.elementMap[id];
delete this.shapeTypeMap[id];
}
/**
* <zh/> 将布局结果对齐到元素,避免视图偏移。会修改布局结果
*
* <en/> Align the layout result to the element to avoid view offset. Will modify the layout result
* @param layoutResult - <zh/> 布局结果 | <en/> layout result
* @param id - <zh/> 元素 ID | <en/> element ID
*/
alignLayoutResultToElement(layoutResult, id) {
var _a, _b;
const target = (_a = layoutResult.nodes) === null || _a === void 0 ? void 0 : _a.find((node) => (0, id_1.idOf)(node) === id);
if (target) {
const originalPosition = (0, position_1.positionOf)(this.context.model.getNodeLikeDatum(id));
const modifiedPosition = (0, position_1.positionOf)(target);
const delta = (0, vector_1.subtract)(originalPosition, modifiedPosition);
(_b = layoutResult.nodes) === null || _b === void 0 ? void 0 : _b.forEach((node) => {
var _a, _b, _c;
if ((_a = node.style) === null || _a === void 0 ? void 0 : _a.x)
node.style.x += delta[0];
if ((_b = node.style) === null || _b === void 0 ? void 0 : _b.y)
node.style.y += delta[1];
if ((_c = node.style) === null || _c === void 0 ? void 0 : _c.z)
node.style.z += delta[2] || 0;
});
}
}
/**
* <zh/> 收起节点
*
* <en/> collapse node
* @param id - <zh/> 元素 ID | <en/> element ID
* @param options - <zh/> 选项 | <en/> options
*/
collapseNode(id, options) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const { animation, align } = options;
const { model, layout } = this.context;
const simulateLayoutData = this.computeChangesAndDrawData({ stage: 'collapse', animation });
if (!simulateLayoutData)
return;
this.markDestroyElement(simulateLayoutData.drawData);
// 进行预布局,计算出所有元素的位置
// Perform pre-layout to calculate the position of all elements
const result = yield layout.simulate();
if (align)
this.alignLayoutResultToElement(result, id);
model.updateData(result);
// 重新计算数据 / Recalculate data
const data = this.computeChangesAndDrawData({ stage: 'collapse', animation });
if (!data)
return;
const { drawData } = data;
const { add, remove, update } = drawData;
this.markDestroyElement(drawData);
const context = { animation, stage: 'collapse', data: drawData };
this.destroyElements(remove, context);
this.createElements(add, context);
this.updateElements(update, context);
yield ((_a = this.context.animation.animate(animation, {
beforeAnimate: (animation) => this.emit(new event_1.AnimateEvent(constants_1.GraphEvent.BEFORE_ANIMATE, constants_1.AnimationType.COLLAPSE, animation, drawData), context),
afterAnimate: (animation) => this.emit(new event_1.AnimateEvent(constants_1.GraphEvent.AFTER_ANIMATE, constants_1.AnimationType.COLLAPSE, animation, drawData), context),
}, {
collapse: {
target: id,
descendants: Array.from(remove.nodes).map(([, node]) => (0, id_1.idOf)(node)),
position: (0, position_1.positionOf)(update.nodes.get(id)),
},
})) === null || _a === void 0 ? void 0 : _a.finished);
});
}
/**
* <zh/> 展开节点
*
* <en/> expand node
* @param id - <zh/> 元素 ID | <en/> element ID
* @param animation - <zh/> 是否使用动画,默认为 true | <en/> Whether to use animation, default is true
*/
expandNode(id, options) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const { model, layout } = this.context;
const { animation, align } = options;
const position = (0, position_1.positionOf)(model.getNodeData([id])[0]);
const preLayoutData = this.computeChangesAndDrawData({ stage: 'expand', animation });
if (!preLayoutData)
return;
// 首先创建展开的元素,然后进行预布局
// First create the expanded element, then perform pre-layout
const { drawData: { add }, } = preLayoutData;
this.createElements(add, { animation: false, stage: 'expand', target: id });
// 重置动画 / Reset animation
this.context.animation.clear();
const result = yield layout.simulate();
if (align)
this.alignLayoutResultToElement(result, id);
model.updateData(result);
// 重新计算数据 / Recalculate data
this.computeStyle('expand');
const data = this.computeChangesAndDrawData({ stage: 'collapse', animation });
if (!data)
return;
const { drawData } = data;
const { update } = drawData;
const context = { animation, stage: 'expand', data: drawData };
// 将新增节点/边添加到更新列表 / Add new nodes/edges to the update list
add.edges.forEach((edge) => update.edges.set((0, id_1.idOf)(edge), edge));
add.nodes.forEach((node) => update.nodes.set((0, id_1.idOf)(node), node));
this.updateElements(update, context);
yield ((_a = this.context.animation.animate(animation, {
beforeAnimate: (animation) => this.emit(new event_1.AnimateEvent(constants_1.GraphEvent.BEFORE_ANIMATE, constants_1.AnimationType.EXPAND, animation, drawData), context),
afterAnimate: (animation) => this.emit(new event_1.AnimateEvent(constants_1.GraphEvent.AFTER_ANIMATE, constants_1.AnimationType.EXPAND, animation, drawData), context),
}, {
expand: {
target: id,
descendants: Array.from(add.nodes).map(([, node]) => (0, id_1.idOf)(node)),
position,
},
})) === null || _a === void 0 ? void 0 : _a.finished);
});
}
collapseCombo(id, animation) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const { model, element } = this.context;
if (model.getAncestorsData(id, constants_1.COMBO_KEY).some((datum) => (0, collapsibility_1.isCollapsed)(datum)))
return;
const combo = element.getElement(id);
const position = combo.getComboPosition(Object.assign(Object.assign({}, combo.attributes), { collapsed: true }));
const data = this.computeChangesAndDrawData({ stage: 'collapse', animation });
if (!data)
return;
const { dataChanges, drawData } = data;
this.markDestroyElement(drawData);
const { update, remove } = drawData;
const context = { animation, stage: 'collapse', data: drawData };
this.destroyElements(remove, context);
this.updateElements(update, context);
const idsOf = (data) => Array.from(data).map(([, node]) => (0, id_1.idOf)(node));
yield ((_a = this.context.animation.animate(animation, {
before: () => this.emit(new event_1.GraphLifeCycleEvent(constants_1.GraphEvent.BEFORE_DRAW, { dataChanges, animation }), context),
beforeAnimate: (animation) => this.emit(new event_1.AnimateEvent(constants_1.GraphEvent.BEFORE_ANIMATE, constants_1.AnimationType.COLLAPSE, animation, drawData), context),
afterAnimate: (animation) => this.emit(new event_1.AnimateEvent(constants_1.GraphEvent.AFTER_ANIMATE, constants_1.AnimationType.COLLAPSE, animation, drawData), context),
after: () => this.emit(new event_1.GraphLifeCycleEvent(constants_1.GraphEvent.AFTER_DRAW, { dataChanges, animation }), context),
}, {
collapse: {
target: id,
descendants: [...idsOf(remove.nodes), ...idsOf(remove.combos)],
position,
},
})) === null || _a === void 0 ? void 0 : _a.finished);
});
}
expandCombo(id, animation) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const { model } = this.context;
const position = (0, position_1.positionOf)(model.getComboData([id])[0]);
// 重新计算数据 / Recalculate data
this.computeStyle('expand');
const data = this.computeChangesAndDrawData({ stage: 'expand', animation });
if (!data)
return;
const { dataChanges, drawData } = data;
const { add, update } = drawData;
const context = { animation, stage: 'expand', data: drawData, target: id };
this.createElements(add, context);
this.updateElements(update, context);
const idsOf = (data) => Array.from(data).map(([, node]) => (0, id_1.idOf)(node));
yield ((_a = this.context.animation.animate(animation, {
before: () => this.emit(new event_1.GraphLifeCycleEvent(constants_1.GraphEvent.BEFORE_DRAW, { dataChanges, animation }), context),
beforeAnimate: (animation) => this.emit(new event_1.AnimateEvent(constants_1.GraphEvent.BEFORE_ANIMATE, constants_1.AnimationType.EXPAND, animation, drawData), context),
afterAnimate: (animation) => this.emit(new event_1.AnimateEvent(constants_1.GraphEvent.AFTER_ANIMATE, constants_1.AnimationType.EXPAND, animation, drawData), context),
after: () => this.emit(new event_1.GraphLifeCycleEvent(constants_1.GraphEvent.AFTER_DRAW, { dataChanges, animation }), context),
}, {
expand: {
target: id,
descendants: [...idsOf(add.nodes), ...idsOf(add.combos)],
position,
},
})) === null || _a === void 0 ? void 0 : _a.finished);
});
}
/**
* <zh/> 清空所有元素
*
* <en/> clear all elements
*/
clear() {
this.container.destroy();
this.initContainer();
this.elementMap = {};
this.shapeTypeMap = {};
this.defaultStyle = {};
this.stateStyle = {};
this.paletteStyle = {};
}
destroy() {
this.clear();
this.container.destroy();
// @ts-expect-error force delete
this.context = {};
}
}
exports.ElementController = ElementController;
//# sourceMappingURL=element.js.map