@antv/g6-pc
Version:
A Graph Visualization Framework in JavaScript
514 lines (512 loc) • 15.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _tslib = require("tslib");
var _util = require("@antv/util");
var _util2 = _interopRequireDefault(require("../util"));
var _global = _interopRequireDefault(require("../global"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/*
* @Author: moyee
* @LastEditors: moyee
* @Description: 拖动 Combo
*/
var calculationItemsBBox = _util2.default.calculationItemsBBox;
/**
* 遍历拖动的 Combo 下的所有 Combo
* @param data 拖动的 Combo
* @param fn
*/
var _traverseCombo = function traverseCombo(data, fn) {
if (fn(data) === false) {
return;
}
if (data) {
var combos = data.get('combos');
if (combos.length === 0) {
return false;
}
(0, _util.each)(combos, function (child) {
_traverseCombo(child, fn);
});
}
};
var _default = exports.default = {
getDefaultCfg: function getDefaultCfg() {
return {
enableDelegate: false,
delegateStyle: {},
// 拖动节点过程中是否只改变 Combo 的大小,而不改变其结构
onlyChangeComboSize: false,
// 拖动过程中目标 combo 状态样式
activeState: '',
selectedState: 'selected',
enableStack: true
};
},
getEvents: function getEvents() {
return {
'combo:mousedown': 'onMouseDown',
'combo:dragstart': 'onDragStart',
'combo:drag': 'onDrag',
'combo:dragend': 'onDragEnd',
'combo:drop': 'onDrop',
'node:drop': 'onNodeDrop',
'combo:dragenter': 'onDragEnter',
'combo:dragleave': 'onDragLeave'
};
},
validationCombo: function validationCombo(evt) {
var item = evt.item;
if (!item || item.destroyed) {
return false;
}
if (!this.shouldUpdate(evt, this)) {
return false;
}
var type = item.getType();
if (type !== 'combo') {
return false;
}
return true;
},
onMouseDown: function onMouseDown(evt) {
this.origin = {
x: evt.x,
y: evt.y
};
},
onDragStart: function onDragStart(evt) {
var _this = this;
var graph = this.graph;
var item = evt.item;
this.currentShouldEnd = true;
if (!this.validationCombo(evt)) return;
if (item.hasLocked()) {
return;
}
this.targets = [];
// 获取所有选中的 Combo
var combos = graph.findAllByState('combo', this.selectedState);
var currentCombo = item.get('id');
var dragCombos = combos.filter(function (combo) {
var comboId = combo.get('id');
return currentCombo === comboId;
});
if (dragCombos.length === 0) {
this.targets.push(item);
} else {
this.targets = combos.filter(function (combo) {
return !combo.hasLocked();
});
}
var beforeDragItems = [];
this.targets.forEach(function (t) {
var _a = t.getModel(),
x = _a.x,
y = _a.y,
id = _a.id;
beforeDragItems.push({
x: x,
y: y,
id: id
});
});
this.set('beforeDragItems', beforeDragItems);
if (this.activeState) {
this.targets.map(function (combo) {
var model = combo.getModel();
if (model.parentId) {
var parentCombo = graph.findById(model.parentId);
if (parentCombo) {
graph.setItemState(parentCombo, _this.activeState, true);
}
}
});
}
this.point = {};
this.originPoint = {};
this.currentItemChildCombos = [];
_traverseCombo(item, function (param) {
if (param.destroyed) {
return false;
}
var model = param.getModel();
_this.currentItemChildCombos.push(model.id);
return true;
});
},
onDrag: function onDrag(evt) {
var _this = this;
if (!this.origin) {
return;
}
if (!this.validationCombo(evt)) return;
if (this.enableDelegate) {
this.updateDelegate(evt);
} else {
if (this.activeState) {
var graph_1 = this.graph;
var item = evt.item;
var model_1 = item.getModel();
// 拖动过程中实时计算距离
var combos = graph_1.getCombos();
var sourceBBox = item.getBBox();
var centerX_1 = sourceBBox.centerX,
centerY_1 = sourceBBox.centerY,
width_1 = sourceBBox.width;
// 参与计算的 Combo,需要排除掉:
// 1、拖动 combo 自己
// 2、拖动 combo 的 parent
// 3、拖动 Combo 的 children
var calcCombos = combos.filter(function (combo) {
var cmodel = combo.getModel();
// 被拖动的是最外层的 Combo,无 parent,排除自身和子元素
if (!model_1.parentId) {
return cmodel.id !== model_1.id && !_this.currentItemChildCombos.includes(cmodel.id);
}
return cmodel.id !== model_1.id && !_this.currentItemChildCombos.includes(cmodel.id);
});
calcCombos.map(function (combo) {
var _a = combo.getBBox(),
cx = _a.centerX,
cy = _a.centerY,
w = _a.width;
// 拖动的 combo 和要进入的 combo 之间的距离
var disX = centerX_1 - cx;
var disY = centerY_1 - cy;
// 圆心距离
var distance = 2 * Math.sqrt(disX * disX + disY * disY);
if (width_1 + w - distance > 0.8 * width_1) {
graph_1.setItemState(combo, _this.activeState, true);
} else {
graph_1.setItemState(combo, _this.activeState, false);
}
});
}
(0, _util.each)(this.targets, function (item) {
_this.updateCombo(item, evt);
});
if (this.onlyChangeComboSize) {
// 拖动节点过程中,动态改变 Combo 的大小
this.updateParentCombos();
}
}
},
updatePositions: function updatePositions(evt, restore) {
var _this = this;
// 当启用 delegate 时,拖动结束时需要更新 combo
if (this.enableDelegate || restore) {
(0, _util.each)(this.targets, function (item) {
_this.updateCombo(item, evt, restore);
});
}
},
onDrop: function onDrop(evt) {
var _this = this;
// 被放下的目标 combo
var item = evt.item;
this.currentShouldEnd = this.shouldEnd(evt, item, this);
this.updatePositions(evt, !this.currentShouldEnd);
if (!this.currentShouldEnd || !item || !this.targets || item.destroyed) return;
var graph = this.graph;
var targetModel = item.getModel();
this.targets.map(function (combo) {
var model = combo.getModel();
if (model.parentId !== targetModel.id) {
if (_this.activeState) {
graph.setItemState(item, _this.activeState, false);
}
// 将 Combo 放置到某个 Combo 上面时,只有当 onlyChangeComboSize 为 false 时候才更新 Combo 结构
if (!_this.onlyChangeComboSize) {
graph.updateComboTree(combo, targetModel.id, false);
} else {
graph.updateCombo(combo);
}
} else {
graph.updateCombo(item);
}
});
this.end(item, evt);
// 如果已经拖放下了,则不需要再通过距离判断了
this.endComparison = true;
},
onNodeDrop: function onNodeDrop(evt) {
var _this = this;
if (!this.targets || this.targets.length === 0) return;
var graph = this.graph;
var item = evt.item;
var comboId = item.getModel().comboId;
var newParentCombo = comboId ? graph.findById(comboId) : undefined;
this.currentShouldEnd = this.shouldEnd(evt, newParentCombo, this);
this.updatePositions(evt, !this.currentShouldEnd);
if (!this.currentShouldEnd) return;
var droppedCombo;
// 如果被放置的的节点有 comboId,且这个 comboId 与正在被拖拽的 combo 的父 id 不相同,则更新父亲为 comboId
if (comboId) {
if (this.activeState) {
var combo = graph.findById(comboId);
graph.setItemState(combo, this.activeState, false);
}
this.targets.map(function (combo) {
if (!_this.onlyChangeComboSize) {
if (comboId !== combo.getID()) {
droppedCombo = graph.findById(comboId);
if (comboId !== combo.getModel().parentId) graph.updateComboTree(combo, comboId, false);
}
} else {
graph.updateCombo(combo);
}
});
} else {
// 如果被放置的节点没有 comboId,且正在被拖拽的 combo 有父 id,则更新父亲为 undefined
this.targets.map(function (combo) {
if (!_this.onlyChangeComboSize) {
var model = combo.getModel();
if (model.comboId) {
graph.updateComboTree(combo, undefined, false);
}
} else {
graph.updateCombo(combo);
}
});
}
// 如果已经拖放下了,则不需要再通过距离判断了
this.endComparison = true;
this.end(droppedCombo, evt);
},
onDragEnter: function onDragEnter(evt) {
if (!this.origin) {
return;
}
if (!this.validationCombo(evt)) return;
var item = evt.item;
var graph = this.graph;
if (this.activeState) {
graph.setItemState(item, this.activeState, true);
}
},
onDragLeave: function onDragLeave(evt) {
if (!this.origin) {
return;
}
if (!this.validationCombo(evt)) return;
var item = evt.item;
var graph = this.graph;
if (this.activeState) {
graph.setItemState(item, this.activeState, false);
}
},
onDragEnd: function onDragEnd(evt) {
if (!this.targets || this.targets.length === 0) return;
var item = evt.item;
if (this.currentShouldEnd) {
this.updatePositions(evt);
}
var parentCombo = this.getParentCombo(item.getModel().parentId);
var graph = this.graph;
if (parentCombo && this.activeState) {
graph.setItemState(parentCombo, this.activeState, false);
}
this.end(undefined, evt);
},
end: function end(comboDropedOn, evt) {
var _this = this;
if (!this.origin) return;
var graph = this.graph;
// 删除delegate shape
if (this.delegateShape) {
var delegateGroup = graph.get('delegateGroup');
delegateGroup.clear();
this.delegateShape = null;
}
if (comboDropedOn && this.activeState) {
graph.setItemState(comboDropedOn, this.activeState, false);
}
// 若没有被放置的 combo,则是被放置在画布上
if (!comboDropedOn) {
var stack_1 = graph.get('enabledStack') && this.enableStack;
var stackData_1 = {
before: {
nodes: [],
edges: [],
combos: [].concat(this.get('beforeDragItems'))
},
after: {
nodes: [],
edges: [],
combos: []
}
};
this.targets.map(function (combo) {
// 将 Combo 放置到某个 Combo 上面时,只有当 onlyChangeComboSize 为 false 时候才更新 Combo 结构
if (!_this.onlyChangeComboSize) {
graph.updateComboTree(combo, undefined, stack_1);
} else {
graph.updateCombo(combo);
var _a = combo.getModel(),
x = _a.x,
y = _a.y,
id = _a.id;
stackData_1.after.combos.push({
x: x,
y: y,
id: id
});
graph.pushStack('update', stackData_1);
}
});
}
this.point = [];
this.origin = null;
this.originPoint = null;
this.targets.length = 0;
},
/**
* 遍历 comboTree,分别更新 node 和 combo
* @param data
* @param fn
*/
traverse: function traverse(data, fn, edgesToBeUpdate) {
var _this = this;
if (edgesToBeUpdate === void 0) {
edgesToBeUpdate = {};
}
if (fn(data, edgesToBeUpdate) === false) {
return;
}
if (data) {
var combos = data.get('combos');
(0, _util.each)(combos, function (child) {
_this.traverse(child, fn, edgesToBeUpdate);
});
var nodes = data.get('nodes');
(0, _util.each)(nodes, function (child) {
_this.traverse(child, fn, edgesToBeUpdate);
});
}
},
updateCombo: function updateCombo(item, evt, restore) {
this.updateSingleItem(item, evt, restore);
var edgesToBeUpdate = {};
this.traverse(item, function (paramItem, paramEdgesMap) {
if (paramItem.destroyed) {
return false;
}
paramItem.getEdges().forEach(function (edge) {
return paramEdgesMap[edge.getID()] = edge;
});
return true;
}, edgesToBeUpdate);
Object.values(edgesToBeUpdate).forEach(function (edge) {
return edge.refresh();
});
},
/**
*
* @param item 当前正在拖动的元素
* @param evt
*/
updateSingleItem: function updateSingleItem(item, evt, restore) {
var origin = this.origin;
var graph = this.graph;
var model = item.getModel();
var itemId = item.get('id');
if (!this.point[itemId]) {
this.point[itemId] = {
x: model.x,
y: model.y
};
}
var x = evt.x - origin.x + this.point[itemId].x;
var y = evt.y - origin.y + this.point[itemId].y;
if (restore) {
x += origin.x - evt.x;
y += origin.y - evt.y;
}
graph.updateItem(item, {
x: x,
y: y
}, false);
// item.getEdges()?.forEach(edge => edge.refresh());
},
/**
* 根据 ID 获取父 Combo
* @param parentId 父 Combo ID
*/
getParentCombo: function getParentCombo(parentId) {
var graph = this.graph;
if (!parentId) {
return undefined;
}
var parentCombo = graph.findById(parentId);
if (!parentCombo) {
return undefined;
}
return parentCombo;
},
updateDelegate: function updateDelegate(evt) {
var graph = this.graph;
// 当没有 delegate shape 时创建
if (!this.delegateShape) {
var delegateGroup = graph.get('delegateGroup');
var bbox = null;
if (this.targets.length > 1) {
bbox = calculationItemsBBox(this.targets);
} else {
bbox = this.targets[0].getBBox();
}
var x = bbox.x,
y = bbox.y,
width = bbox.width,
height = bbox.height,
minX = bbox.minX,
minY = bbox.minY;
this.originPoint = {
x: x,
y: y,
width: width,
height: height,
minX: minX,
minY: minY
};
var attrs = (0, _tslib.__assign)((0, _tslib.__assign)({}, _global.default.delegateStyle), this.delegateStyle);
this.delegateShape = delegateGroup.addShape('rect', {
attrs: (0, _tslib.__assign)({
width: bbox.width,
height: bbox.height,
x: bbox.x,
y: bbox.y
}, attrs),
name: 'combo-delegate-shape'
});
this.delegateShape.set('capture', false);
this.delegate = this.delegateShape;
} else {
var clientX = evt.x - this.origin.x + this.originPoint.minX;
var clientY = evt.y - this.origin.y + this.originPoint.minY;
this.delegateShape.attr({
x: clientX,
y: clientY
});
}
},
/**
* updates the parent combos' size and position
*/
updateParentCombos: function updateParentCombos() {
var _a = this,
graph = _a.graph,
targets = _a.targets;
var comboParentMap = {};
targets === null || targets === void 0 ? void 0 : targets.forEach(function (target) {
var comboId = target.getModel().parentId;
if (comboId) comboParentMap[comboId] = graph.findById(comboId);
});
Object.values(comboParentMap).forEach(function (combo) {
if (combo) graph.updateCombo(combo);
});
}
};