UNPKG

@antv/g6

Version:

graph visualization frame work

943 lines (815 loc) 24.4 kB
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** * @fileOverview graph * @author huangtonger@aliyun.com */ require('./extend/g/html'); require('./extend/g/canvas'); require('./extend/g/group'); require('./extend/g/shape'); require('./extend/g/root-group'); require('./extend/g/html'); var Base = require('./base'); var Item = require('./item/'); var Shape = require('./shape/'); var Util = require('./util/'); var G = require('@antv/g'); var LayoutMixin = require('./mixin/layout'); var MappingMixin = require('./mixin/mapping'); var QueryMixin = require('./mixin/query'); var EventMixin = require('./mixin/event'); var ModeMixin = require('./mixin/mode'); var FilterMixin = require('./mixin/filter'); var AnimateMixin = require('./mixin/animate'); var FitView = require('./mixin/fit-view'); var ForceFit = require('./mixin/force-fit'); var Mixins = [FilterMixin, MappingMixin, QueryMixin, AnimateMixin, ForceFit, LayoutMixin, FitView, EventMixin, ModeMixin]; var TAB_INDEX = 20; var Graph = function (_Base) { _inherits(Graph, _Base); /** * Access to the default configuration properties * @return {object} default configuration */ Graph.prototype.getDefaultCfg = function getDefaultCfg() { return { /** * Container could be dom object or dom id * @type {object|string|undefined} */ container: undefined, /** * Canvas width * @type {number|undefined} * unit pixel if undefined force fit width */ width: undefined, /** * Canvas height * @type {number|undefined} * unit pixel if undefined force fit height */ height: undefined, /** * Plugins * @type {array} */ plugins: [], /** * FontFamily * @type {string} */ fontFamily: '"Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", SimSun, "sans-serif"', /** * default node shape * @type {string|undefined} */ nodeDefaultShape: undefined, /** * default edge shape * @type {string|undefined} */ edgeDefaultShape: undefined, /** * default group shape * @type {string|undefined} */ groupDefaultShape: undefined, /** * default edge node intersect box * @type {string} */ defaultIntersectBox: 'circle', render: 'canvas', _controllers: {}, _timers: {}, _dataMap: {}, _itemMap: {}, _data: {}, _delayRunObj: {} }; }; function Graph(inputCfg) { _classCallCheck(this, Graph); var cfg = {}; Mixins.forEach(function (Mixin) { Util.mix(cfg, Mixin.CFG, inputCfg); }); // plugin should init before all var _this = _possibleConstructorReturn(this, _Base.call(this, cfg)); _this._pluginInit(); _this.emit('beforeinit'); _this._init(); _this.emit('afterinit'); return _this; } Graph.prototype._init = function _init() { var _this2 = this; this._initData(); this._initContainer(); this._initCanvas(); Mixins.forEach(function (Mixin) { Mixin.INIT && _this2[Mixin.INIT](); }); this.initEvent(); }; Graph.prototype.initEvent = function initEvent() {}; Graph.prototype._executeLayout = function _executeLayout(processer, nodes, edges, groups) { if (Util.isFunction(processer)) { processer(nodes, edges, this); } else if (Util.isObject(processer)) { processer.nodes = nodes; processer.edges = edges; processer.groups = groups; processer.graph = this; processer.execute(); } }; Graph.prototype._pluginInit = function _pluginInit() { var _this3 = this; var plugins = this.get('plugins'); plugins.forEach(function (plugin) { _this3._initPlugin(plugin); }); }; Graph.prototype._initPlugin = function _initPlugin(plugin) { plugin.graph = this; plugin.init && plugin.init(); }; Graph.prototype._getTimer = function _getTimer(name) { return this.get('_timers')[name]; }; Graph.prototype._setTimer = function _setTimer(name, value) { this.get('_timers')[name] = value; }; Graph.prototype._getController = function _getController(name) { return this.get('_controllers')[name]; }; Graph.prototype._initContainer = function _initContainer() { var container = this.get('container'); if (!container) { // Compatible with id written container = this.get('id'); } if (container) { if (Util.isString(container)) { container = document.getElementById(container); } } else { throw new Error('please set the container for the graph !'); } var graphContainer = Util.createDOM('<div class="graph-container"></div>', { position: 'relative' }); container.appendChild(graphContainer); this.set('_containerDOM', container); this.set('_graphContainer', graphContainer); }; Graph.prototype._initCanvas = function _initCanvas() { var _this4 = this; var graphContainer = this.get('_graphContainer'); var width = this.get('width'); var height = this.get('height'); var fontFamily = this.get('fontFamily'); var canvasCfg = { width: width, height: height, fontFamily: fontFamily, eventEnable: false, containerDOM: graphContainer }; var Canvas = this.getConstructor('Canvas'); var canvas = new Canvas(canvasCfg); var frontCanvas = new Canvas(canvasCfg); var frontEl = frontCanvas.get('el'); var htmlElementContaniner = graphContainer.appendChild(Util.createDOM('<div class="graph-container-html-Elements"></div>')); canvas.on('beforedraw', function () { _this4.emit('beforecanvasdraw'); }); frontEl.style.position = 'absolute'; frontEl.style.top = 0; frontEl.style.left = 0; htmlElementContaniner.style.overflow = 'hidden'; htmlElementContaniner.style.width = width + 'px'; htmlElementContaniner.style.height = height + 'px'; htmlElementContaniner.style.position = 'absolute'; htmlElementContaniner.style.top = 0; htmlElementContaniner.style.left = 0; this.set('_canvas', canvas); this.set('_frontCanvas', frontCanvas); this.set('_htmlElementContaniner', htmlElementContaniner); var mouseEventWrapper = this.getMouseEventWrapper(); mouseEventWrapper.style.outline = 'none'; mouseEventWrapper.style['user-select'] = 'none'; mouseEventWrapper.setAttribute('tabindex', TAB_INDEX); canvas.set('htmlElementContaniner', htmlElementContaniner); var RootGroup = this.getConstructor('RootGroup'); var rootGroup = canvas.addGroup(RootGroup); var frontRootGroup = frontCanvas.addGroup(RootGroup); var itemGroup = rootGroup.addGroup(); this.set('_itemGroup', itemGroup); this.set('_rootGroup', rootGroup); this.set('_frontRootGroup', frontRootGroup); }; Graph.prototype._initData = function _initData() { this.set('_dataMap', {}); this.set('_itemMap', { _nodes: [], _edges: [], _groups: [], _guides: [] }); this.set('_data', {}); }; Graph.prototype._refresh = function _refresh() {}; Graph.prototype.getKeyboardEventWrapper = function getKeyboardEventWrapper() { var keyboardEventWrapper = this.get('keyboardEventWrapper'); return keyboardEventWrapper ? keyboardEventWrapper : this.getMouseEventWrapper(); }; Graph.prototype.getMouseEventWrapper = function getMouseEventWrapper() { return this.get('_htmlElementContaniner'); }; /** * @param {object} plugin - plugin instance */ Graph.prototype.addPlugin = function addPlugin(plugin) { var plugins = this.get('plugins'); this._initPlugin(plugin); plugins.push(plugin); }; /** * @return {domobject} graphcontainer */ Graph.prototype.getGraphContainer = function getGraphContainer() { return this.get('_graphContainer'); }; /** * @param {string} type item type * @param {array} models models */ Graph.prototype.addItems = function addItems(type, models) { var _this5 = this; this._addDatas(type, models); var Type = Util.upperFirst(type); var Constructor = Item[Type]; var itemMap = this.get('_itemMap'); var itemGroup = this.get('_itemGroup'); var dataMap = this.get('_dataMap'); var animate = this.get('animate'); var defaultIntersectBox = this.get('defaultIntersectBox'); if (!Constructor) { throw new Error('please set valid item type!'); } models.forEach(function (model) { var item = new Constructor({ id: model.id, type: type, model: model, group: itemGroup.addGroup(), graph: _this5, mapper: _this5._getController(type + 'Mapper'), itemMap: itemMap, animate: animate, dataMap: dataMap, defaultIntersectBox: defaultIntersectBox }); itemMap[model.id] = item; itemMap['_' + type + 's'].push(item); }); }; /** * @param {array} items remove items */ Graph.prototype.removeItems = function removeItems(items) { var dataMap = this.get('_dataMap'); var itemMap = this.get('_itemMap'); items.forEach(function (item) { delete dataMap[item.id]; delete itemMap[item.id]; Util.Array.remove(itemMap['_' + item.type + 's'], item); item.destroy(); }); }; /** * @param {object} item item * @param {object} model update model */ Graph.prototype.updateItem = function updateItem(item, model) { Util.mix(item.getModel(), model); item.update(); }; Graph.prototype._addDatas = function _addDatas(type, models) { var dataMap = this.get('_dataMap'); models.forEach(function (model) { if (Util.isNil(model.id)) { model.id = Util.guid(); } if (dataMap[model.id]) { throw new Error('id:' + model.id + ' has already been set, please set new one'); } dataMap[model.id] = model; }); }; Graph.prototype._drawInner = function _drawInner() { var data = this.get('_data'); var itemGroup = this.get('_itemGroup'); var dataMap = this.get('_dataMap'); if (data.nodes) { this.addItems('node', data.nodes); } if (data.groups) { this.addItems('group', data.groups); } if (data.edges) { this.addItems('edge', data.edges); } if (data.guides) { this.addItems('guide', data.guides); } itemGroup.sortBy(function (child) { var id = child.id; var model = dataMap[id]; return model.index; }); }; Graph.prototype._clearInner = function _clearInner() { var items = this.getItems(); items.forEach(function (item) { item && !item.destroyed && item.destroy(); }); }; /** * @param {string} name option 1 * @return {function} function */ Graph.prototype.getConstructor = function getConstructor(name) { var render = this.get('render'); if (render === 'svg') { return G.svg[name]; } return G.canvas[name]; }; /** * @param {string} type item type * @param {object} model data model * @return {object} shapeObj */ Graph.prototype.getShapeObj = function getShapeObj(type, model) { if (!Util.isObject(type)) { var Type = Util.upperFirst(type); var shapeManager = Shape[Type]; var defaultShape = this.get(type + 'DefaultShape'); return shapeManager.getShape(model.shape, defaultShape); } return type.getShapeObj(); }; /** * @return {object} source data */ Graph.prototype.getSource = function getSource() { return this.get('_sourceData'); }; /** * @param {object} data source data * @return {object} plain data */ Graph.prototype.parseSource = function parseSource(data) { return data; }; /** * @return {G.Canvas} canvas */ Graph.prototype.getCanvas = function getCanvas() { return this.get('_canvas'); }; /** * @return {G.Group} rootGroup */ Graph.prototype.getRootGroup = function getRootGroup() { return this.get('_rootGroup'); }; /** * @return {G.Group} itemGroup */ Graph.prototype.getItemGroup = function getItemGroup() { return this.get('_itemGroup'); }; /** * @return {G.Group} frontRootGroup */ Graph.prototype.getFrontRootGroup = function getFrontRootGroup() { return this.get('_frontRootGroup'); }; /** * @return {G.Canvas} canvas */ Graph.prototype.getFrontCanvas = function getFrontCanvas() { return this.get('_frontCanvas'); }; /** * @param {object} data source data * @return {Graph} this */ Graph.prototype.source = function source(data) { this.emit('beforesource'); this.set('_data', data); this.set('_sourceData', data); this.emit('aftersource'); return this; }; /** * @return {Graph} this */ Graph.prototype.render = function render() { this.emit('beforerender'); this.emit('beforedrawinner'); this._drawInner(); this.emit('afterdrawinner'); this.draw(); this.emit('afterrender'); return this; }; /** * @param {boolean} bool if force prevent animate */ Graph.prototype.forcePreventAnimate = function forcePreventAnimate(bool) { this.set('forcePreventAnimate', bool); }; /** * @return {Graph} this */ Graph.prototype.reRender = function reRender() { var data = this.get('_sourceData'); this.read(data); return this; }; /** * @return {Graph} this */ Graph.prototype.destroy = function destroy() { var canvas = this.get('_canvas'); var frontCanvas = this.get('_frontCanvas'); var graphContainer = this.get('_graphContainer'); var controllers = this.get('_controllers'); var timers = this.get('_timers'); var windowForceResizeEvent = this.get('_windowForceResizeEvent'); var plugins = this.get('plugins'); Util.each(timers, function (timer) { clearTimeout(timer); }); Util.each(controllers, function (controller) { controller.destroy(); }); plugins.forEach(function (plugin) { plugin.destroy && plugin.destroy(); }); canvas && canvas.destroy(); frontCanvas && frontCanvas.destroy(); graphContainer.destroy(); window.removeEventListener('resize', windowForceResizeEvent); _Base.prototype.destroy.call(this); return this; }; /** * @return {object} data */ Graph.prototype.save = function save() { var itemGroup = this.get('_itemGroup'); var children = itemGroup.get('children'); var rst = { nodes: [], edges: [], groups: [], guides: [] }; children.forEach(function (child, index) { var model = child.model; if (model) { var type = child.itemType; var saveModel = Util.cloneDeep(model); saveModel.index = index; rst[type + 's'].push(saveModel); } }); rst.nodes.length === 0 && delete rst.nodes; rst.edges.length === 0 && delete rst.edges; rst.groups.length === 0 && delete rst.groups; rst.guides.length === 0 && delete rst.guides; return rst; }; /** * @param {string} type item type * @param {object} model data model * @return {Graph} this */ Graph.prototype.add = function add(type, model) { var ev = { action: 'add', model: model }; this.emit('beforechange', ev); var itemMap = this.get('_itemMap'); this.addItems(type, [model]); var item = itemMap[model.id]; item.getAllParents().forEach(function (parent) { parent.update(); }); ev.item = item; this.emit('afterchange', ev); this.draw(); return item; }; /** * @param {String|Item} item target item * @return {Graph} this */ Graph.prototype.remove = function remove(item) { var _this6 = this; item = this.getItem(item); if (!item || item.destroyed) { return; } var ev = { action: 'remove', item: item }; this.emit('beforechange', ev); if (item.isNode || item.isGroup) { var edges = item.getEdges(); edges.forEach(function (edge) { _this6.remove(edge); }); } if (item.isGroup) { var children = item.getChildren(); children.forEach(function (child) { _this6.remove(child); }); } this.removeItems([item]); item.getAllParents().forEach(function (parent) { parent.update(); }); this.emit('afterchange', ev); this.draw(); return this; }; /** * @param {String|Item} item target item * @param {object} model data model * @return {Graph} this */ Graph.prototype.simpleUpdate = function simpleUpdate(item, model) { this.updateItem(item, model); this.draw(); return this; }; /** * @param {String|Item|Undefined} item target item * @param {object} model data model * @return {Graph} this */ Graph.prototype.update = function update(item, model) { var itemMap = this.get('_itemMap'); item = this.getItem(item); if (!item || item.destroyed) { return; } var itemModel = item.getModel(); var originModel = Util.mix({}, itemModel); var ev = { action: 'update', item: item, originModel: originModel, updateModel: model }; var originParent = itemMap[originModel.parent]; if (originParent && originParent !== parent && Util.isGroup(originParent)) { item.getAllParents().forEach(function (parent) { parent.update(); }); } model && this.emit('beforechange', ev); this.updateItem(item, model); // If the update nodes or group, update their parent item.getAllParents().forEach(function (parent) { parent.update(); }); // If the update nodes or group, update the connection edge if ((item.isNode || item.isGroup) && !item.collapsedParent) { var edges = item.getEdges(); edges.forEach(function (edge) { edge.update(); }); } // update group relative items if (item.isGroup && model) { item.deepEach(function (child) { child.updateCollapsedParent(); if (child.collapsedParent) { child.hide(); } else { child.show(); } child.update(); }); item.getInnerEdges().forEach(function (child) { var bool = child.linkedItemVisible(); if (bool) { child.show(); } else { child.hide(); } child.update(); }); } model && this.emit('afterchange', ev); this.draw(); return this; }; /** * change data * @param {object} data source data * @return {Graph} this */ Graph.prototype.read = function read(data) { if (!data) { throw new Error('please read valid data!'); } var fitView = this.get('fitView'); var ev = { action: 'changeData', data: data }; this.emit('beforechange', ev); this.clear(); this.source(data); this.render(); this.emit('afterchange', ev); fitView && this.setFitView(fitView); return this; }; /** * @return {Graph} this */ Graph.prototype.clear = function clear() { this.emit('beforeclear'); this._clearInner(); this._initData(); this.emit('afterclear'); this.draw(); return this; }; /** * hide item * @param {number} item input item * @return {object} this */ Graph.prototype.hide = function hide(item) { item = this.getItem(item); item.hide(); if (item.isNode) { item.getEdges().forEach(function (edge) { edge.hide(); }); } if (item.isGroup) { item.getEdges().forEach(function (edge) { edge.hide(); }); // item.getInnerEdges().forEach(edge => { // edge.hide(); // }); item.deepEach(function (child) { child.hide(); }); } this.draw(); return this; }; Graph.prototype._tryShowEdge = function _tryShowEdge(edge) { var source = edge.getSource(); var target = edge.getTarget(); return (source.linkable && source.isVisible() || !source.linkable) && (target.linkable && target.isVisible() || !target.linkable) && edge.show(); }; /** * show item * @param {number} item input item * @return {object} this */ Graph.prototype.show = function show(item) { var _this7 = this; item = this.getItem(item); if (item.isEdge) { this._tryShowEdge(item); } else { item.show(); } if (item.isNode) { item.getEdges().forEach(function (edge) { _this7._tryShowEdge(edge); }); } if (item.isGroup) { item.getEdges().forEach(function (edge) { _this7._tryShowEdge(edge); }); item.deepEach(function (child) { child.show(); }); } this.draw(); return this; }; /** * @return {Graph} this */ Graph.prototype.getWidth = function getWidth() { return this.get('width'); }; /** * @return {Graph} this */ Graph.prototype.getHeight = function getHeight() { return this.get('height'); }; /** * change canvas size * @param {number} width input width * @param {number} height input height * @return {object} this */ Graph.prototype.changeSize = function changeSize(width, height) { if (Math.abs(width) >= Infinity || Math.abs(height) >= Infinity) { console.warn('size parameter more than the maximum'); return; } var canvas = this.get('_canvas'); var frontCanvas = this.get('_frontCanvas'); var htmlElementContaniner = this.get('_htmlElementContaniner'); if (width !== this.get('width') || height !== this.get('height')) { this.emit('beforechangesize'); canvas.changeSize(width, height); frontCanvas.changeSize(width, height); htmlElementContaniner.css({ width: width + 'px', height: height + 'px' }); this.set('width', width); this.set('height', height); this.emit('afterchangesize'); this.draw(); } return this; }; /** * item to front * @param {object} item item */ Graph.prototype.toFront = function toFront(item) { item = this.getItem(item); var itemGroup = this.get('_itemGroup'); var group = item.getGraphicGroup(); Util.toFront(group, itemGroup); this.draw(); }; /** * item to back * @param {object} item item */ Graph.prototype.toBack = function toBack(item) { item = this.getItem(item); var itemGroup = this.get('_itemGroup'); var group = item.getGraphicGroup(); Util.toBack(group, itemGroup); this.draw(); }; /** * set cantainer css * @param {object} style container dom css */ Graph.prototype.css = function css(style) { var graphContainer = this.getGraphContainer(); Util.modifyCSS(graphContainer, style); }; /** * save graph image * @return {object} canvas dom */ Graph.prototype.saveImage = function saveImage() { var box = this.getBBox(); var padding = this.getFitViewPadding(); return Util.graph2Canvas({ graph: this, width: box.width + padding[1] + padding[3], height: box.height + padding[0] + padding[2] }); }; return Graph; }(Base); Mixins.forEach(function (Mixin) { Util.mix(Graph.prototype, Mixin.AUGMENT); }); module.exports = Graph;