UNPKG

@antv/g6

Version:

A Graph Visualization Framework in JavaScript

320 lines 12.2 kB
"use strict"; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Legend = void 0; const component_1 = require("@antv/component"); const util_1 = require("@antv/util"); const constants_1 = require("../constants"); const base_plugin_1 = require("./base-plugin"); const canvas_1 = require("./utils/canvas"); /** * <zh/> 图例 * * <en/> Legend * @remarks * <zh/> 图例插件用于展示图中元素的分类信息,支持节点、边、组合的分类信息展示。 * * <en/> The legend plugin is used to display the classification information of elements in the graph, and supports the display of classification information of nodes, edges, and combos. */ class Legend extends base_plugin_1.BasePlugin { constructor(context, options) { super(context, Object.assign({}, Legend.defaultOptions, options)); this.typePrefix = '__data__'; this.draw = false; this.fieldMap = { node: new Map(), edge: new Map(), combo: new Map(), }; this.selectedItems = []; this.bindEvents = () => { const { graph } = this.context; graph.on(constants_1.GraphEvent.AFTER_DRAW, this.createElement); }; this.changeState = (el, state) => { const { graph } = this.context; const { typePrefix } = this; const composeId = (0, util_1.get)(el, [typePrefix, 'id']); const category = (0, util_1.get)(el, [typePrefix, 'style', 'labelText']); const [type] = composeId.split('__'); const ids = this.fieldMap[type].get(category) || []; graph.setElementState(Object.fromEntries(ids === null || ids === void 0 ? void 0 : ids.map((id) => [id, state]))); }; /** * <zh/> 图例元素点击事件 * * <en/> Legend element click event * @param event - <zh/> 点击的元素 | <en/> The element that is clicked */ this.click = (event) => { if (this.options.trigger === 'hover') return; const composeId = (0, util_1.get)(event, [this.typePrefix, 'id']); if (!this.selectedItems.includes(composeId)) { this.selectedItems.push(composeId); this.changeState(event, 'selected'); } else { this.selectedItems = this.selectedItems.filter((item) => item !== composeId); this.changeState(event, []); } }; /** * <zh/> 图例元素移出事件 * * <en/> Legend element mouseleave event * @param event - <zh/> 移出的元素 | <en/> The element that is moved out */ this.mouseleave = (event) => { if (this.options.trigger === 'click') return; this.selectedItems = []; this.changeState(event, []); }; /** * <zh/> 图例元素移入事件 * * <en/> Legend element mouseenter event * @param event - <zh/> 移入的元素 | <en/> The element that is moved in */ this.mouseenter = (event) => { if (this.options.trigger === 'click') return; const composeId = (0, util_1.get)(event, [this.typePrefix, 'id']); if (!this.selectedItems.includes(composeId)) { this.selectedItems.push(composeId); this.changeState(event, 'active'); } else { this.selectedItems = this.selectedItems.filter((item) => item !== composeId); } }; this.setFieldMap = (field, id, type) => { if (!field) return; const map = this.fieldMap[type]; if (!map) return; if (!map.has(field)) { map.set(field, [id]); } else { const ids = map.get(field); if (ids) { ids.push(id); map.set(field, ids); } } }; this.getEvents = () => { return { mouseenter: this.mouseenter, mouseleave: this.mouseleave, click: this.click, }; }; this.getMarkerData = (field, elementType) => { if (!field) return []; const { model, element } = this.context; const { nodes, edges, combos } = model.getData(); const items = {}; const getField = (item) => { if ((0, util_1.isFunction)(field)) return field(item); return field; }; const defaultType = { node: 'circle', edge: 'line', combo: 'rect', }; // 用于将 G6 element 转换为 components 支持的类型 // Used to convert G6 element to types supported by components const markerMapping = { circle: 'circle', ellipse: 'circle', // 待 components 支持 ellipse image: 'bowtie', rect: 'square', star: 'cross', triangle: 'triangle', diamond: 'diamond', cubic: 'dot', line: 'hyphen', polyline: 'hyphen', quadratic: 'hv', 'cubic-horizontal': 'hyphen', 'cubic-vertical': 'line', }; const getElementStyle = (type, datum) => { const style = element === null || element === void 0 ? void 0 : element.getElementComputedStyle(type, datum); return style; }; const getElementModel = (data, type) => { data.forEach((item) => { const { id } = item; const value = (0, util_1.get)(item, ['data', getField(item)]); const marker = (element === null || element === void 0 ? void 0 : element.getElementType(type, item)) || 'circle'; const style = getElementStyle(type, item); const color = (type === 'edge' ? style === null || style === void 0 ? void 0 : style.stroke : style === null || style === void 0 ? void 0 : style.fill) || '#1783ff'; if (id && value && value.replace(/\s+/g, '')) { this.setFieldMap(value, id, type); if (!items[value]) { items[value] = { id: `${type}__${id}`, label: value, marker: markerMapping[marker] || defaultType[type], elementType: type, lineWidth: 1, stroke: color, fill: color, }; } } }); }; switch (elementType) { case 'node': getElementModel(nodes, 'node'); break; case 'edge': getElementModel(edges, 'edge'); break; case 'combo': getElementModel(combos, 'combo'); break; default: return []; } return Object.values(items); }; this.createElement = () => { if (this.draw) { this.updateElement(); return; } const _a = this.options, { width, height, nodeField, edgeField, comboField, trigger, position, container, containerStyle, className } = _a, rest = __rest(_a, ["width", "height", "nodeField", "edgeField", "comboField", "trigger", "position", "container", "containerStyle", "className"]); const nodeItems = this.getMarkerData(nodeField, 'node'); const edgeItems = this.getMarkerData(edgeField, 'edge'); const comboItems = this.getMarkerData(comboField, 'combo'); const items = [...nodeItems, ...comboItems, ...edgeItems]; const categoryStyle = Object.assign({ width, height, data: items, itemMarkerLineWidth: ({ lineWidth }) => lineWidth, itemMarker: ({ marker }) => marker, itemMarkerStroke: ({ stroke }) => stroke, itemMarkerFill: ({ fill }) => fill, gridCol: nodeItems.length, }, rest, this.getEvents()); const category = new component_1.Category({ className: 'legend', style: categoryStyle, }); this.category = category; const canvas = this.upsertCanvas(); canvas.appendChild(category); this.draw = true; }; this.bindEvents(); } /** * <zh/> 更新图例配置 * * <en/> Update the legend configuration * @param options - <zh/> 图例配置项 | <en/> Legend options * @internal */ update(options) { super.update(options); this.clear(); this.createElement(); } clear() { var _a, _b; (_a = this.canvas) === null || _a === void 0 ? void 0 : _a.destroy(); (_b = this.container) === null || _b === void 0 ? void 0 : _b.remove(); this.canvas = undefined; this.container = undefined; this.draw = false; } /** * <zh/> 刷新图例元素状态 * * <en/> Refresh the status of the legend element */ updateElement() { if (!this.category) return; this.category.update({ itemMarkerOpacity: ({ id }) => { if (!this.selectedItems.length || this.selectedItems.includes(id)) return 1; return 0.5; }, itemLabelOpacity: ({ id }) => { if (!this.selectedItems.length || this.selectedItems.includes(id)) return 1; return 0.5; }, }); } upsertCanvas() { if (this.canvas) return this.canvas; const graphCanvas = this.context.canvas; const [canvasWidth, canvasHeight] = graphCanvas.getSize(); const { width = canvasWidth, height = canvasHeight, position, container, containerStyle, className } = this.options; const [$container, canvas] = (0, canvas_1.createPluginCanvas)({ width, height, graphCanvas, container, containerStyle, placement: position, className: 'legend', }); this.container = $container; if (className) $container.classList.add(className); this.canvas = canvas; return this.canvas; } /** * <zh/>销毁图例 * * <en/> Destroy the legend * @internal */ destroy() { this.clear(); this.context.graph.off(constants_1.GraphEvent.AFTER_DRAW, this.createElement); super.destroy(); } } exports.Legend = Legend; Legend.defaultOptions = { position: 'bottom', trigger: 'hover', orientation: 'horizontal', layout: 'flex', itemSpacing: 4, rowPadding: 10, colPadding: 10, itemMarkerSize: 16, itemLabelFontSize: 16, width: 240, height: 160, }; //# sourceMappingURL=legend.js.map