@antv/g6
Version:
graph visualization frame work
230 lines (224 loc) • 7.36 kB
JavaScript
const Util = require('../../util');
const Item = require('../../item');
const deepMix = require('@antv/util/lib/deep-mix');
const NODE = 'node';
const EDGE = 'edge';
const CFG_PREFIX = 'default';
const MAPPER_SUFFIX = 'Mapper';
const STATE_SUFFIX = 'stateStyles';
const hasOwnProperty = Object.hasOwnProperty;
class ItemController {
constructor(graph) {
this.graph = graph;
}
addItem(type, model) {
const graph = this.graph;
const parent = graph.get(type + 'Group') || graph.get('group');
const upperType = Util.upperFirst(type);
let item;
let styles = graph.get(type + Util.upperFirst(STATE_SUFFIX)) || {};
const defaultModel = graph.get(CFG_PREFIX + upperType);
const mapper = graph.get(type + MAPPER_SUFFIX);
if (mapper) {
const mappedModel = mapper(model);
if (mappedModel[STATE_SUFFIX]) {
styles = mappedModel[STATE_SUFFIX];
delete mappedModel[STATE_SUFFIX];
}
// 如果配置了 defaultEdge 或 defaultNode,则将默认配置的数据也合并进去
model = deepMix({}, defaultModel, model, mappedModel);
} else if (defaultModel) {
// 很多布局会直接修改原数据模型,所以不能用 merge 的形式,逐个写入原 model 中
Util.each(defaultModel, (val, cfg) => {
if (!hasOwnProperty.call(model, cfg)) {
if (Util.isObject(val)) {
model[cfg] = Util.clone(val);
} else {
model[cfg] = defaultModel[cfg];
}
}
});
}
graph.emit('beforeadditem', { type, model });
if (type === EDGE) {
let source = model.source;
let target = model.target;
if (source && Util.isString(source)) {
source = graph.findById(source);
}
if (target && Util.isString(target)) {
target = graph.findById(target);
}
if (!source || !target) {
console.warn('The source or target node of edge ' + model.id + ' does not exist!');
return;
}
item = new Item[upperType]({
model,
source,
target,
styles,
linkCenter: graph.get('linkCenter'),
group: parent.addGroup()
});
} else {
item = new Item[upperType]({
model,
styles,
group: parent.addGroup()
});
}
graph.get(type + 's').push(item);
graph.get('itemMap')[item.get('id')] = item;
graph.autoPaint();
graph.emit('afteradditem', { item, model });
return item;
}
updateItem(item, cfg) {
const graph = this.graph;
if (Util.isString(item)) {
item = graph.findById(item);
}
if (!item || item.destroyed) {
return;
}
// 如果修改了与映射属性有关的数据项,映射的属性相应也需要变化
const mapper = graph.get(item.getType() + MAPPER_SUFFIX);
const model = item.getModel();
if (mapper) {
const result = deepMix({}, model, cfg);
const mappedModel = mapper(result);
// 将 update 时候用户传入的参数与mapperModel做deepMix,以便复用之前设置的参数值
const newModel = deepMix({}, model, mappedModel, cfg);
if (mappedModel[STATE_SUFFIX]) {
item.set('styles', newModel[STATE_SUFFIX]);
delete newModel[STATE_SUFFIX];
}
Util.each(newModel, (val, key) => {
cfg[key] = val;
});
} else {
// merge update传进来的对象参数,model中没有的数据不做处理,对象和字符串值也不做处理,直接替换原来的
Util.each(cfg, (val, key) => {
if (model[key]) {
if (Util.isObject(val) && !Util.isArray(val)) {
cfg[key] = Util.mix({}, model[key], cfg[key]);
}
}
});
}
graph.emit('beforeupdateitem', { item, cfg });
if (item.getType() === EDGE) {
// 若是边要更新source || target, 为了不影响示例内部model,并且重新计算startPoint和endPoint,手动设置
if (cfg.source) {
let source = cfg.source;
if (Util.isString(source)) {
source = graph.findById(source);
}
item.setSource(source);
}
if (cfg.target) {
let target = cfg.target;
if (Util.isString(target)) {
target = graph.findById(target);
}
item.setTarget(target);
}
}
item.update(cfg);
if (item.getType() === NODE) {
const autoPaint = graph.get('autoPaint');
graph.setAutoPaint(false);
Util.each(item.getEdges(), edge => {
graph.refreshItem(edge);
});
graph.setAutoPaint(autoPaint);
}
graph.autoPaint();
graph.emit('afterupdateitem', { item, cfg });
}
removeItem(item) {
const graph = this.graph;
if (Util.isString(item)) {
item = graph.findById(item);
}
if (!item || item.destroyed) {
return;
}
graph.emit('beforeremoveitem', { item });
const type = item.getType();
const items = graph.get(item.getType() + 's');
const index = items.indexOf(item);
items.splice(index, 1);
delete graph.get('itemMap')[item.get('id')];
if (type === NODE) {
// 若移除的是节点,需要将与之相连的边一同删除
const edges = item.getEdges();
for (let i = edges.length; i >= 0; i--) {
graph.removeItem(edges[i]);
}
}
item.destroy();
graph.autoPaint();
graph.emit('afterremoveitem', { item });
}
setItemState(item, state, enabled) {
const graph = this.graph;
if (item.hasState(state) === enabled) {
return;
}
graph.emit('beforeitemstatechange', { item, state, enabled });
item.setState(state, enabled);
graph.autoPaint();
graph.emit('afteritemstatechange', { item, state, enabled });
}
clearItemStates(item, states) {
const graph = this.graph;
if (Util.isString(item)) {
item = graph.findById(item);
}
graph.emit('beforeitemstatesclear', { item, states });
item.clearStates(states);
graph.autoPaint();
graph.emit('afteritemstatesclear', { item, states });
}
refreshItem(item) {
const graph = this.graph;
if (Util.isString(item)) {
item = graph.findById(item);
}
graph.emit('beforeitemrefresh', { item });
item.refresh();
graph.autoPaint();
graph.emit('afteritemrefresh', { item });
}
changeItemVisibility(item, visible) {
const self = this;
const graph = self.graph;
if (Util.isString(item)) {
item = graph.findById(item);
}
graph.emit('beforeitemvisibilitychange', { item, visible });
item.changeVisibility(visible);
if (item.getType() === NODE) {
const autoPaint = graph.get('autoPaint');
graph.setAutoPaint(false);
Util.each(item.getEdges(), edge => {
// 若隐藏节点,则将与之关联的边也隐藏
// 若显示节点,则将与之关联的边也显示,但是需要判断边两端的节点都是可见的
if (visible && (!(edge.get('source').isVisible() && edge.get('target').isVisible()))) {
return;
}
self.changeItemVisibility(edge, visible);
});
graph.setAutoPaint(autoPaint);
}
graph.autoPaint();
graph.emit('afteritemvisibilitychange', { item, visible });
}
destroy() {
this.graph = null;
this.destroyed = true;
}
}
module.exports = ItemController;