From f033c9f8e263f1b6868030d394299a9df3fcc204 Mon Sep 17 00:00:00 2001 From: Yanyan-Wang Date: Mon, 9 Nov 2020 10:16:48 +0800 Subject: [PATCH] feat: container of plugins with dom id; fix: combo zIndex problem; fix: webworker updateLayoutCfg problem; --- CHANGELOG.md | 4 + src/behavior/drag-combo.ts | 1 + src/graph/graph.ts | 39 ++- src/interface/graph.ts | 2 +- src/interface/item.ts | 3 +- src/layout/gpu/graphinForce.ts | 14 +- src/layout/gpu/graphinForceShader.ts | 38 +-- src/plugins/base.ts | 2 +- src/plugins/imageMinimap/index.ts | 5 +- src/plugins/menu/index.ts | 6 + src/plugins/timeBar/index.ts | 9 +- src/plugins/toolBar/index.ts | 24 ++ src/plugins/tooltip/index.ts | 6 + src/types/index.ts | 1 + src/util/graphic.ts | 10 +- src/util/math.ts | 13 + stories/Case/component/decision-tree.tsx | 13 +- stories/Combo/component/dagre-combo.tsx | 3 +- stories/Combo/component/drag-node-out.tsx | 40 +-- stories/Combo/component/edges2.tsx | 3 +- stories/Issues/forceLayout/multiLayout.tsx | 325 +++++++++++---------- stories/Plugins/component/toolbar.tsx | 19 +- stories/Tree/component/flow-component.tsx | 23 +- 23 files changed, 352 insertions(+), 251 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db15f0d240..1d85d5fa36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,12 @@ - feat: graphin force; - feat: updateChildren API for TreeGraph; - feat: louvain clustering algorithm; +- feat: container of plugins with dom id; - fix: text redidual problem, closes: #2045 #2193; - fix: graph on callback parameter type problem, closes: #2250; +- fix: combo zIndex problem; +- fix: webworker updateLayoutCfg problem; +- fix: drag-canvas and click node on mobile; #### 3.8.5 - fix: get fontFamily of the window in global leads to DOM depending when using bigfish; diff --git a/src/behavior/drag-combo.ts b/src/behavior/drag-combo.ts index 4b25a1e3ca..05889b3c90 100644 --- a/src/behavior/drag-combo.ts +++ b/src/behavior/drag-combo.ts @@ -92,6 +92,7 @@ export default { if (dragCombos.length === 0) { this.targets.push(item); + } else { this.targets = combos; } diff --git a/src/graph/graph.ts b/src/graph/graph.ts index b053828faa..0dcf6f7ca4 100644 --- a/src/graph/graph.ts +++ b/src/graph/graph.ts @@ -1731,7 +1731,7 @@ export default class Graph extends EventEmitter implements IGraph { * @param {String | INode | ICombo} item 需要被更新的 Combo 或 节点 id * @param {string | undefined} parentId 新的父 combo id,undefined 代表没有父 combo */ - public updateComboTree(item: string | INode | ICombo, parentId?: string | undefined) { + public updateComboTree(item: string | INode | ICombo, parentId?: string | undefined, stack: boolean = true) { const self = this; this.set('comboSorted', false); let uItem: INode | ICombo; @@ -1744,8 +1744,11 @@ export default class Graph extends EventEmitter implements IGraph { const model = uItem.getModel(); const oldParentId = (model.comboId as string) || (model.parentId as string); + let type = ''; + if (uItem.getType) type = uItem.getType(); + // 若 item 是 Combo,且 parentId 是其子孙 combo 的 id,则警告并终止 - if (parentId && uItem.getType && uItem.getType() === 'combo') { + if (parentId && type === 'combo') { const comboTrees = this.get('comboTrees'); let valid = true; let itemSubTree; @@ -1775,7 +1778,34 @@ export default class Graph extends EventEmitter implements IGraph { } } - // 当 combo 存在parentId 或 comboId 时,才将其移除 + if (stack && this.get('enabledStack')) { + const beforeData: GraphData = {}, afterData: GraphData = {}; + if (type === 'combo') { + beforeData.combos = [{ + id: model.id, + parentId: (model as ComboConfig).parentId + }]; + afterData.combos = [{ + id: model.id, + parentId + }]; + } else if (type === 'node') { + beforeData.nodes = [{ + id: model.id, + parentId: (model as NodeConfig).comboId + }]; + afterData.nodes = [{ + id: model.id, + parentId + }]; + } + this.pushStack('updateComboTree', { + before: beforeData, + after: afterData + }); + } + + // 当 combo 存在 parentId 或 comboId 时,才将其移除 if (model.parentId || model.comboId) { const combo = this.findById((model.parentId || model.comboId) as string) as ICombo; if (combo) { @@ -1783,9 +1813,6 @@ export default class Graph extends EventEmitter implements IGraph { } } - let type = ''; - if (uItem.getType) type = uItem.getType(); - if (type === 'combo') { model.parentId = parentId; } else if (type === 'node') { diff --git a/src/interface/graph.ts b/src/interface/graph.ts index 658f0073f5..2addac9090 100644 --- a/src/interface/graph.ts +++ b/src/interface/graph.ts @@ -220,7 +220,7 @@ export interface IGraph extends EventEmitter { * @param {string | INode | ICombo} item 需要被更新的 Combo 或 节点 id * @param {string | undefined} parentId 新的父 combo id,undefined 代表没有父 combo */ - updateComboTree(item: string | INode | ICombo, parentId?: string | undefined): void; + updateComboTree(item: string | INode | ICombo, parentId?: string | undefined, stack?: boolean): void; /** * 解散 combo diff --git a/src/interface/item.ts b/src/interface/item.ts index f4c7c05fe8..1c424802db 100644 --- a/src/interface/item.ts +++ b/src/interface/item.ts @@ -15,6 +15,7 @@ import { Indexable, ComboConfig, ITEM_TYPE, + TreeGraphData } from '../types'; // item 的配置项 @@ -133,7 +134,7 @@ export interface IItemBase { * 节点 / 边 / Combo 的数据模型 * @return {Object} 数据模型 */ - getModel(): NodeConfig | EdgeConfig | ComboConfig; + getModel(): NodeConfig | EdgeConfig | ComboConfig | TreeGraphData; /** * 节点类型 diff --git a/src/layout/gpu/graphinForce.ts b/src/layout/gpu/graphinForce.ts index f342c01f95..95b4891fc0 100644 --- a/src/layout/gpu/graphinForce.ts +++ b/src/layout/gpu/graphinForce.ts @@ -209,9 +209,12 @@ export default class GraphinForceGPULayout extends BaseLayout { centerGravities.push(nodeGravity[2]); }) - // 每个几点的额外属性占两格,分别是:mass, degree, nodeSterngth, centerX, centerY, gravity, 0, 0 - const nodeAttributeArray = arrayToTextureData([ - masses, self.degrees, nodeStrengths, + // 每个节点的额外属性占两个数组各一格,nodeAttributeArray1 中是:mass, degree, nodeSterngth, 0 + const nodeAttributeArray1 = arrayToTextureData([ + masses, self.degrees, nodeStrengths + ]); + // nodeAttributeArray2 中是:centerX, centerY, gravity, 0, + const nodeAttributeArray2 = arrayToTextureData([ centerXs, centerYs, centerGravities ]); @@ -236,7 +239,7 @@ export default class GraphinForceGPULayout extends BaseLayout { // TODO: 最终的预编译代码放入到 graphinForceShader.ts 中直接引入,不再需要下面三行 const compiler = new Compiler(); const graphinForceBundle = compiler.compileBundle(graphinForceCode); - console.log(graphinForceBundle); + console.log(graphinForceBundle.toString()); const onLayoutEnd = self.onLayoutEnd; @@ -250,7 +253,8 @@ export default class GraphinForceGPULayout extends BaseLayout { u_minMovement: self.minMovement, u_coulombDisScale: self.coulombDisScale, u_factor: self.factor, - u_NodeAttributeArray: nodeAttributeArray, + u_NodeAttributeArray1: nodeAttributeArray1, + u_NodeAttributeArray2: nodeAttributeArray2, MAX_EDGE_PER_VERTEX: maxEdgePerVetex, VERTEX_COUNT: numParticles, u_interval: self.interval // 每次迭代更新,首次设置为 interval,在 onIterationCompleted 中更新 diff --git a/src/layout/gpu/graphinForceShader.ts b/src/layout/gpu/graphinForceShader.ts index 8146c7c77a..96150387f8 100644 --- a/src/layout/gpu/graphinForceShader.ts +++ b/src/layout/gpu/graphinForceShader.ts @@ -1,4 +1,3 @@ - export const graphinForceCode = ` import { globalInvocationID } from 'g-webgpu'; @@ -26,7 +25,10 @@ class GraphinForce { u_factor: float; @in - u_NodeAttributeArray: vec4[]; + u_NodeAttributeArray1: vec4[]; + + @in + u_NodeAttributeArray2: vec4[]; @in u_EdgeAttributeArray: vec4[]; @@ -48,8 +50,8 @@ class GraphinForce { const n_dist = dist * this.u_coulombDisScale; const direx = vx / dist; const direy = vy / dist; - const attributesi = this.u_NodeAttributeArray[2 * i]; - const attributesj = this.u_NodeAttributeArray[2 * j]; + const attributesi = this.u_NodeAttributeArray1[2 * i]; + const attributesj = this.u_NodeAttributeArray1[(int)2 * j]; const massi = attributesi[0]; const nodeStrengthi = attributesi[2]; const nodeStrengthj = attributesj[2]; @@ -63,14 +65,14 @@ class GraphinForce { return [ax, ay]; } - calcGravity(i: int, currentNode: vec4, attributes: vec4): vec2 { - // note: attributes = [mass, degree, nodeSterngth, centerX, centerY, gravity, 0, 0] + calcGravity(i: int, currentNode: vec4, attributes2: vec4): vec2 { + // note: attributes2 = [centerX, centerY, gravity, 0] - const vx = currentNode[0] - attributes[3]; - const vy = currentNode[1] - attributes[4]; + const vx = currentNode[0] - attributes2[0]; + const vy = currentNode[1] - attributes2[1]; - const ax = vx * attributes[5]; - const ay = vy * attributes[5]; + const ax = vx * attributes[3]; + const ay = vy * attributes[3]; @@ -85,10 +87,10 @@ class GraphinForce { return [ax, ay]; } - calcAttractive(i: int, currentNode: vec4, attributes: vec4): vec2 { - // note: attributes = [mass, degree, nodeSterngth, centerX, centerY, gravity, 0, 0] + calcAttractive(i: int, currentNode: vec4, attributes1: vec4): vec2 { + // note: attributes = [mass, degree, nodeSterngth, 0] - const mass = attributes[0]; + const mass = attributes1[0]; let ax = 0, ay = 0; const arr_offset = int(floor(currentNode[2] + 0.5)); const length = int(floor(currentNode[3] + 0.5)); @@ -131,8 +133,9 @@ class GraphinForce { return; } - // 每个节点属性占两小格 - const nodeAttributes = this.u_NodeAttributeArray[2 * i]; + // 每个节点属性占两个数组中各一格 + const nodeAttributes1 = this.u_NodeAttributeArray1[2 * i]; + const nodeAttributes2 = this.u_NodeAttributeArray2[2 * i]; // repulsive const repulsive = this.calcRepulsive(i, currentNode); @@ -140,17 +143,16 @@ class GraphinForce { ay += repulsive[1]; // attractive - const attractive = this.calcAttractive(i, currentNode, nodeAttributes); + const attractive = this.calcAttractive(i, currentNode, nodeAttributes1); ax += attractive[0]; ay += attractive[1]; // gravity - const gravity = this.calcGravity(i, currentNode, nodeAttributes); + const gravity = this.calcGravity(i, currentNode, nodeAttributes2); ax -= gravity[0]; ay -= gravity[1]; // speed - const interval = 0.02; // max(0.02, 0.22 - 0.002 * this.u_iter); const param = this.u_interval * this.u_damping; let vx = ax * param; let vy = ay * param; diff --git a/src/plugins/base.ts b/src/plugins/base.ts index 17dd5dcd7c..e1e0259c95 100644 --- a/src/plugins/base.ts +++ b/src/plugins/base.ts @@ -5,7 +5,7 @@ import Graph from '../graph/graph'; import { IG6GraphEvent } from '../types'; export interface IPluginBaseConfig { - container?: HTMLDivElement | null; + container?: HTMLDivElement | string | null; className?: string; graph?: Graph; [key: string]: any; diff --git a/src/plugins/imageMinimap/index.ts b/src/plugins/imageMinimap/index.ts index e33afbf067..d207f93c1d 100644 --- a/src/plugins/imageMinimap/index.ts +++ b/src/plugins/imageMinimap/index.ts @@ -81,7 +81,10 @@ export default class ImageMiniMap extends Base { const { graph } = cfgs; if (this.destroyed) return; - const containerDOM = this.get('container'); + let containerDOM = this.get('container'); + if (isString(containerDOM)) { + containerDOM = document.getElementById(containerDOM) as HTMLDivElement; + } const viewport = createDOM( `
`); modifyCSS(timeBarContainer, { position: 'relative' }); } else { + if (isString(container)) { + container = document.getElementById(container) as HTMLDivElement; + } timeBarContainer = container; } @@ -308,6 +312,9 @@ export default class TimeBar extends Base { if (!container) { container = this.get('graph').get('container'); } + if (isString(container)) { + container = document.getElementById(container) as HTMLDivElement; + } container.removeChild(timeBarContainer); } } diff --git a/src/plugins/toolBar/index.ts b/src/plugins/toolBar/index.ts index b638c23f50..f378ae433b 100644 --- a/src/plugins/toolBar/index.ts +++ b/src/plugins/toolBar/index.ts @@ -104,6 +104,9 @@ export default class ToolBar extends Base { if (!container) { container = this.get('graph').get('container'); } + if (isString(container)) { + container = document.getElementById(container) as HTMLDivElement; + } container!.appendChild(toolBarDOM); this.set('toolBar', toolBarDOM); @@ -249,6 +252,15 @@ export default class ToolBar extends Base { }); }); break; + case 'updateComboTree': + Object.keys(data).forEach(key => { + const array = data[key]; + if (!array) return; + array.forEach(model => { + graph.updateComboTree(model.id, model.parentId, false); + }); + }); + break; default: } } @@ -334,6 +346,15 @@ export default class ToolBar extends Base { }); break; } + case 'updateComboTree': + Object.keys(data).forEach(key => { + const array = data[key]; + if (!array) return; + array.forEach(model => { + graph.updateComboTree(model.id, model.parentId, false); + }); + }); + break; default: } } @@ -387,6 +408,9 @@ export default class ToolBar extends Base { if (!container) { container = this.get('graph').get('container'); } + if (isString(container)) { + container = document.getElementById(container) as HTMLDivElement; + } container.removeChild(toolBar); } diff --git a/src/plugins/tooltip/index.ts b/src/plugins/tooltip/index.ts index 132418fe17..f019633227 100644 --- a/src/plugins/tooltip/index.ts +++ b/src/plugins/tooltip/index.ts @@ -79,6 +79,9 @@ export default class Tooltip extends Base { if (!container) { container = this.get('graph').get('container'); } + if (isString(container)) { + container = document.getElementById(container) as HTMLDivElement; + } modifyCSS(tooltip, { position: 'absolute', visibility: 'hidden' }); container.appendChild(tooltip); @@ -196,6 +199,9 @@ export default class Tooltip extends Base { if (!container) { container = this.get('graph').get('container'); } + if (isString(container)) { + container = document.getElementById(container) as HTMLDivElement; + } container.removeChild(tooltip); } } diff --git a/src/types/index.ts b/src/types/index.ts index 47452c8a92..d1e609972e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -400,6 +400,7 @@ export interface TreeGraphData { [key: string]: ShapeStyle; }; stateStyles?: StateStyles; + [key: string]: unknown; } export interface NodeConfig extends ModelConfig { diff --git a/src/util/graphic.ts b/src/util/graphic.ts index 9728f57ddd..b18630decf 100644 --- a/src/util/graphic.ts +++ b/src/util/graphic.ts @@ -530,8 +530,9 @@ export const plainCombosToTrees = (array: ComboConfig[], nodes?: NodeConfig[]) = }); // assign the depth for each element + let maxDepth = 0; result.forEach((tree: ComboTree) => { - tree.depth = 0; + tree.depth = maxDepth + 10; traverse(tree, (child) => { let parent; const itemType = addedMap[child.id].itemType; @@ -541,11 +542,12 @@ export const plainCombosToTrees = (array: ComboConfig[], nodes?: NodeConfig[]) = parent = addedMap[child.parentId]; } if (parent) { - if (itemType === 'node') child.depth = parent.depth + 1; - else child.depth = parent.depth + 2; + if (itemType === 'node') child.depth = maxDepth + 1; + else child.depth = maxDepth + 10; } else { - child.depth = 0; + child.depth = maxDepth + 10; } + if (maxDepth < child.depth) maxDepth = child.depth; const oriNodeModel = nodeMap[child.id]; if (oriNodeModel) { oriNodeModel.depth = child.depth; diff --git a/src/util/math.ts b/src/util/math.ts index 6b636fd45d..941a2ac454 100644 --- a/src/util/math.ts +++ b/src/util/math.ts @@ -16,6 +16,19 @@ import { } from '../types'; import { each } from '@antv/util'; +/** + * 对比对象,用于对象数组排序 + * @param {string} attributeName 排序依据的字段名称 + * @param {number} min 最小值 + * @param {number} max 最大值 + * @return {boolean} bool 布尔 + */ +export const compare = (attributeName: string) => { + return (m, n) => { + return m[attributeName] - n[attributeName]; + } +}; + /** * 是否在区间内 * @param {number} value 值 diff --git a/stories/Case/component/decision-tree.tsx b/stories/Case/component/decision-tree.tsx index d9687ba56b..92e2aa38af 100644 --- a/stories/Case/component/decision-tree.tsx +++ b/stories/Case/component/decision-tree.tsx @@ -1,6 +1,7 @@ import React, { useEffect } from 'react'; import G6 from '../../../src'; import { IGraph } from '../../../src/interface/graph'; +import { NodeConfig } from '../../../src/types'; let graph: IGraph = null; let showNodes = []; @@ -256,7 +257,7 @@ const DecisionTree = () => { graph.on('node:mouseenter', (e) => { const item = e.item; - const model = item.getModel(); + const model = item.getModel() as NodeConfig; if (model.level === 0) { return; } @@ -270,14 +271,14 @@ const DecisionTree = () => { }); graph.setItemState(item, 'dark', false); model.light = true; - const tags = model.tags; + const tags: string[] = model.tags as string[]; const findTagsMap = new Map(); let mid = 0; let fTag = ''; // if the model is F node, find the leaves of it if (!model.isLeaf && model.level !== 0) { - fTag = model.tag; + fTag = model.tag as string; nodeItems.forEach((item) => { const itemModel = item.getModel(); if (!itemModel.isLeaf) return; @@ -350,7 +351,7 @@ const DecisionTree = () => { curShowNodes = []; curShowEdges = []; const item = e.item; - const model = item.getModel(); + const model = item.getModel() as NodeConfig; if (!model.isLeaf && model.level !== 0) { return; } @@ -431,8 +432,8 @@ const DecisionTree = () => { } if (isChild) { const randomAngle = Math.random() * 2 * Math.PI; - node.x = model.x + (Math.cos(randomAngle) * model.size) / 2 + 10; - node.y = model.y + (Math.sin(randomAngle) * model.size) / 2 + 10; + node.x = model.x + (Math.cos(randomAngle) * (model.size as number)) / 2 + 10; + node.y = model.y + (Math.sin(randomAngle) * (model.size as number)) / 2 + 10; // const dist = (model.x - node.x) * (model.x - node.x) + (model.y - node.y) * (model.y - node.y); if (!node.style) node.style = {}; diff --git a/stories/Combo/component/dagre-combo.tsx b/stories/Combo/component/dagre-combo.tsx index d491e02d59..89d4006d09 100644 --- a/stories/Combo/component/dagre-combo.tsx +++ b/stories/Combo/component/dagre-combo.tsx @@ -2,6 +2,7 @@ import React, { useEffect } from 'react'; import G6 from '../../../src'; import { IGraph } from '../../../src/interface/graph'; import { GraphData } from '../../../src/types'; +import { ICombo } from '../../../src/interface/item'; let graph: IGraph = null; @@ -327,7 +328,7 @@ const DagreCombo = () => { // }, 500); graph.on('combo:click', function (e) { - graph.collapseExpandCombo(e.item); + graph.collapseExpandCombo(e.item as ICombo); graph.refreshPositions(); }); diff --git a/stories/Combo/component/drag-node-out.tsx b/stories/Combo/component/drag-node-out.tsx index 10cb694d90..c49d7db51c 100644 --- a/stories/Combo/component/drag-node-out.tsx +++ b/stories/Combo/component/drag-node-out.tsx @@ -7,37 +7,19 @@ let graph: IGraph = null; const data: GraphData = { nodes: [ - { - id: 'node1', - label: 'node1', - x: 250, - y: 150, - comboId: 'combo', - }, - { - id: 'node2', - label: 'node2', - x: 350, - y: 150, - comboId: 'combo', - }, - ], - combos: [ - { - id: 'combo', - label: 'Combo ', - }, - { - id: 'combo1', - label: 'Combo', - }, + { id: 'node1', x: 350, y: 200, comboId: 'combo1', label: 'node1' }, + { id: 'node2', x: 350, y: 250, comboId: 'combo1', label: 'node2' }, + { id: 'node3', x: 100, y: 200, comboId: 'combo3', label: 'node3' }, ], edges: [ - { - id: 'edge1', - source: 'combo', - target: 'combo1', - }, + { source: 'node1', target: 'node2' }, + { source: 'node1', target: 'node3' }, + { source: 'combo1', target: 'node3' }, + ], + combos: [ + { id: 'combo1', label: 'Combo 1', parentId: 'combo2' }, + { id: 'combo2', label: 'Combo 2' }, + { id: 'combo3', label: 'Combo 3' }, ], }; diff --git a/stories/Combo/component/edges2.tsx b/stories/Combo/component/edges2.tsx index 397682d461..164a74908e 100644 --- a/stories/Combo/component/edges2.tsx +++ b/stories/Combo/component/edges2.tsx @@ -2,6 +2,7 @@ import React, { useEffect } from 'react'; import G6 from '../../../src'; import { IGraph } from '../../../src/interface/graph'; import { GraphData } from '../../../src/types'; +import { ICombo } from '../../../src/interface/item'; let graph: IGraph = null; @@ -140,7 +141,7 @@ const Edges2 = () => { graph.data(data); graph.render(); graph.on('combo:click', function (e) { - graph.collapseExpandCombo(e.item); + graph.collapseExpandCombo(e.item as ICombo); graph.refreshPositions(); }); diff --git a/stories/Issues/forceLayout/multiLayout.tsx b/stories/Issues/forceLayout/multiLayout.tsx index 4c1b196986..49ee98c59a 100644 --- a/stories/Issues/forceLayout/multiLayout.tsx +++ b/stories/Issues/forceLayout/multiLayout.tsx @@ -1,5 +1,6 @@ import React, { Component } from 'react'; import G6 from '../../../src'; +import { IEdge } from '../../../src/interface/item'; // import "./styles.css"; export default class ForceLayout extends Component { @@ -823,7 +824,7 @@ export default class ForceLayout extends Component { const part1NodeMap = new Map(); const part2NodeMap = new Map(); // separate the nodes and init the positions - nodes.forEach(function(node, i) { + nodes.forEach(function (node, i) { if (node.name === "MySQL") { part1Nodes.push(node); part1NodeMap.set(node.id, i); @@ -843,7 +844,7 @@ export default class ForceLayout extends Component { // if (direction === "vertical") { // begin = center[0] - height / 2; // } - part1Nodes.forEach(function(p1n, i) { + part1Nodes.forEach(function (p1n, i) { if (direction === "horizontal") { p1n.x = part1Pos; p1n.y = begin + i * (nodeSep + nodeSize); @@ -852,7 +853,7 @@ export default class ForceLayout extends Component { p1n.y = part1Pos; } }); - part2Nodes.forEach(function(p2n, i) { + part2Nodes.forEach(function (p2n, i) { if (direction === "horizontal") { p2n.x = part2Pos; p2n.y = begin + i * (nodeSep + nodeSize); @@ -902,7 +903,7 @@ export default class ForceLayout extends Component { const part3NodeMap = new Map(); const part4NodeMap = new Map(); // separate the nodes and init the positions - nodes.forEach(function(node, i) { + nodes.forEach(function (node, i) { if (node.name === "MySQL") { part1Nodes.push(node); part1NodeMap.set(node.id, i); @@ -941,27 +942,27 @@ export default class ForceLayout extends Component { // if (direction === "vertical") { // begin = center[0] - height / 2; // } - part1Nodes.forEach(function(p1n, i) { + part1Nodes.forEach(function (p1n, i) { if (direction === "rect") { p1n.x = xbegin - nodeSize; p1n.y = hbegin + i * (nodeSep + nodeSize); // console.log(p1n); } }); - part2Nodes.forEach(function(p2n, i) { + part2Nodes.forEach(function (p2n, i) { if (direction === "rect") { p2n.x = xbegin + width; p2n.y = hbegin + i * (nodeSep + nodeSize); // console.log(p2n); } }); - part3Nodes.forEach(function(p3n, i) { + part3Nodes.forEach(function (p3n, i) { if (direction === "rect") { p3n.x = xbegin + i * (nodeSep + nodeSize); p3n.y = part3Pos; } }); - part4Nodes.forEach(function(p4n, i) { + part4Nodes.forEach(function (p4n, i) { if (direction === "rect") { p4n.x = xbegin + i * (nodeSep + nodeSize); p4n.y = part4Pos; @@ -979,121 +980,121 @@ export default class ForceLayout extends Component { }; G6.registerNode( - 'icon-node', - { - options: { - size: [60, 20], - stroke: '#91d5ff', - fill: '#91d5ff', - }, - draw(cfg, group) { - const styles = this.getShapeStyle(cfg); - const { labelCfg = {} } = cfg; - - const keyShape = group.addShape('rect', { - attrs: { - ...styles, - x: 0, - y: 0, - }, - }); - - /** - * leftIcon 格式如下: - * { - * style: ShapeStyle; - * img: '' - * } - */ - // console.log('cfg.leftIcon', cfg.leftIcon); - // if (cfg.leftIcon) { - // const { style, img } = cfg.leftIcon; - group.addShape('rect', { - attrs: { - x: 1, - y: 1, - width: 38, - height: styles.height - 2, - fill: '#ffffff', - // ...style, - }, - }); - // group.addShape('rect', { - // attrs: { - // x: 180, - // y: 1, - // width: 22, - // height: styles.height - 2, - // fill: '#ffffff', - // // ...style, - // }, - // }); - - group.addShape('image', { - attrs: { - x: 1, - y: 1, - width: 38, - height: 30, - // img: - // img || - // 'https://g.alicdn.com/cm-design/arms-trace/1.0.155/styles/armsTrace/images/TAIR.png', - }, - name: 'image-shape', - }); - // } - - // 如果不需要动态增加或删除元素,则不需要 add 这两个 marker - group.addShape('marker', { - attrs: { - x: -6, - y: 15, - r: 6, - stroke: '#73d13d', - cursor: 'pointer', - symbol: EXPAND_ICON, - }, - name: 'add-tableData', - }); - group.addShape('marker', { - attrs: { - x: 46, - y: 15, - r: 6, - stroke: '#73d13d', - cursor: 'pointer', - symbol: EXPAND_ICON, - }, - name: 'add-fieldData', - }); - group.addShape('marker', { - attrs: { - x: 190, - y: 22, - r: 6, - stroke: '#ffffff', - cursor: 'pointer', - symbol: EXPAND_ICON, - }, - name: 'show-tableData', - }); - - if (cfg.label) { - group.addShape('text', { - attrs: { - ...labelCfg.style, - text: cfg.label, - x: 100, - y: 20, - }, - }); - } - - return keyShape; - }, + 'icon-node', + { + options: { + size: [60, 20], + stroke: '#91d5ff', + fill: '#91d5ff', }, - 'rect', - ); + draw(cfg, group) { + const styles = this.getShapeStyle(cfg); + const { labelCfg = {} } = cfg; + + const keyShape = group.addShape('rect', { + attrs: { + ...styles, + x: 0, + y: 0, + }, + }); + + /** + * leftIcon 格式如下: + * { + * style: ShapeStyle; + * img: '' + * } + */ + // console.log('cfg.leftIcon', cfg.leftIcon); + // if (cfg.leftIcon) { + // const { style, img } = cfg.leftIcon; + group.addShape('rect', { + attrs: { + x: 1, + y: 1, + width: 38, + height: styles.height - 2, + fill: '#ffffff', + // ...style, + }, + }); + // group.addShape('rect', { + // attrs: { + // x: 180, + // y: 1, + // width: 22, + // height: styles.height - 2, + // fill: '#ffffff', + // // ...style, + // }, + // }); + + group.addShape('image', { + attrs: { + x: 1, + y: 1, + width: 38, + height: 30, + // img: + // img || + // 'https://g.alicdn.com/cm-design/arms-trace/1.0.155/styles/armsTrace/images/TAIR.png', + }, + name: 'image-shape', + }); + // } + + // 如果不需要动态增加或删除元素,则不需要 add 这两个 marker + group.addShape('marker', { + attrs: { + x: -6, + y: 15, + r: 6, + stroke: '#73d13d', + cursor: 'pointer', + symbol: EXPAND_ICON, + }, + name: 'add-tableData', + }); + group.addShape('marker', { + attrs: { + x: 46, + y: 15, + r: 6, + stroke: '#73d13d', + cursor: 'pointer', + symbol: EXPAND_ICON, + }, + name: 'add-fieldData', + }); + group.addShape('marker', { + attrs: { + x: 190, + y: 22, + r: 6, + stroke: '#ffffff', + cursor: 'pointer', + symbol: EXPAND_ICON, + }, + name: 'show-tableData', + }); + + if (cfg.label) { + group.addShape('text', { + attrs: { + ...labelCfg.style, + text: cfg.label, + x: 100, + y: 20, + }, + }); + } + + return keyShape; + }, + }, + 'rect', + ); G6.registerEdge( 'circle-running', @@ -1140,7 +1141,7 @@ export default class ForceLayout extends Component { // 3.图实例化 const width = document.getElementById('container').scrollWidth; const height = document.getElementById('container').scrollHeight || 500; - + const minimap = new G6.Minimap({ size: [300, 300], }); @@ -1157,14 +1158,14 @@ export default class ForceLayout extends Component { // }, offset: 30 }, { - type: 'edge-tooltip', - // formatText: function formatText(model, e) { - // const edge = e.item; - // return ('来源:' + edge.getSource().getModel().name + - // '
去向:' + edge.getTarget().getModel().name) - // }, - offset: 30 - }, 'activate-relations'], + type: 'edge-tooltip', + // formatText: function formatText(model, e) { + // const edge = e.item; + // return ('来源:' + edge.getSource().getModel().name + + // '
去向:' + edge.getTarget().getModel().name) + // }, + offset: 30 + }, 'activate-relations'], }, layout: { type: 'circular', @@ -1292,15 +1293,15 @@ export default class ForceLayout extends Component { // }, // }, // }); - } + } }) // 7.连线的交互事件 graph.on('edge:mouseenter', (evt) => { const { item } = evt; graph.setItemState(item, 'hover', true); // 获取连线起点和终点坐标的对象后调用函数 - const edge = evt.item; - getCubicController (edge.getSource().getModel(), edge.getTarget().getModel()); + const edge: IEdge = evt.item as IEdge; + getCubicController(edge.getSource().getModel(), edge.getTarget().getModel()); }); graph.on('edge:mouseleave', (evt) => { const { item } = evt; @@ -1312,17 +1313,17 @@ export default class ForceLayout extends Component { // console.log(item.get('model').style); // item.get('model').style = {fill: "black"}; // 获取连线起点和终点坐标的对象后调用函数 - const edge = evt.item; + const edge: IEdge = evt.item as IEdge; console.log(edge.getSource().getModel().name); console.log(edge.getSource().getModel()); console.log(edge.getTarget().getModel().name); console.log(edge.getTarget().getModel()); - var temp = getCubicController (edge.getSource().getModel(), edge.getTarget().getModel()); + var temp = getCubicController(edge.getSource().getModel(), edge.getTarget().getModel()); console.log(temp); }) - function circleFun () { - graph.updateLayout({type: 'circular', preventOverlap: true, radius: 200}); + function circleFun() { + graph.updateLayout({ type: 'circular', preventOverlap: true, radius: 200 }); document.getElementById('radial').style.backgroundColor = '#e2e2e2'; document.getElementById('bigraph').style.backgroundColor = '#e2e2e2'; document.getElementById('grid').style.backgroundColor = '#e2e2e2'; @@ -1333,8 +1334,8 @@ export default class ForceLayout extends Component { document.getElementById('dagre').style.backgroundColor = '#e2e2e2 '; document.getElementById('rect').style.backgroundColor = '#e2e2e2'; } - function radialFun () { - graph.updateLayout({type: 'radial', preventOverlap: true, nodeSize: 203, strictRadial: true, linkDistance: 50, nodeSpacing: 30}); + function radialFun() { + graph.updateLayout({ type: 'radial', preventOverlap: true, nodeSize: 203, strictRadial: true, linkDistance: 50, nodeSpacing: 30 }); document.getElementById('circular').style.backgroundColor = '#e2e2e2 '; document.getElementById('bigraph').style.backgroundColor = '#e2e2e2'; document.getElementById('grid').style.backgroundColor = '#e2e2e2'; @@ -1345,8 +1346,8 @@ export default class ForceLayout extends Component { document.getElementById('dagre').style.backgroundColor = '#e2e2e2 '; document.getElementById('rect').style.backgroundColor = '#e2e2e2'; } - function bigraphFun () { - graph.updateLayout({type: 'bigraph-layout'}); + function bigraphFun() { + graph.updateLayout({ type: 'bigraph-layout' }); document.getElementById('radial').style.backgroundColor = '#e2e2e2'; document.getElementById('grid').style.backgroundColor = '#e2e2e2'; document.getElementById('circular').style.backgroundColor = '#e2e2e2 '; @@ -1357,7 +1358,7 @@ export default class ForceLayout extends Component { document.getElementById('dagre').style.backgroundColor = '#e2e2e2 '; document.getElementById('rect').style.backgroundColor = '#e2e2e2'; } - function gridFun () { + function gridFun() { graph.updateLayout({ type: 'grid', begin: [20, 20], @@ -1378,7 +1379,7 @@ export default class ForceLayout extends Component { document.getElementById('dagre').style.backgroundColor = '#e2e2e2 '; document.getElementById('rect').style.backgroundColor = '#e2e2e2'; } - function fruchtermanFun () { + function fruchtermanFun() { graph.updateLayout({ type: 'fruchterman', gravity: 5, @@ -1394,7 +1395,7 @@ export default class ForceLayout extends Component { document.getElementById('dagre').style.backgroundColor = '#e2e2e2 '; document.getElementById('rect').style.backgroundColor = '#e2e2e2'; } - function concentricFun () { + function concentricFun() { graph.updateLayout({ type: 'concentric', preventOverlap: true, @@ -1411,7 +1412,7 @@ export default class ForceLayout extends Component { document.getElementById('dagre').style.backgroundColor = '#e2e2e2 '; document.getElementById('rect').style.backgroundColor = '#e2e2e2'; } - function MDSFun () { + function MDSFun() { graph.updateLayout({ type: 'mds', linkDistance: 200 @@ -1426,7 +1427,7 @@ export default class ForceLayout extends Component { document.getElementById('dagre').style.backgroundColor = '#e2e2e2 '; document.getElementById('rect').style.backgroundColor = '#e2e2e2'; } - function DagreFun () { + function DagreFun() { graph.updateLayout({ type: 'dagre', rankdir: 'LR', @@ -1445,8 +1446,8 @@ export default class ForceLayout extends Component { document.getElementById('dagre').style.backgroundColor = '#99FFFF '; document.getElementById('rect').style.backgroundColor = '#e2e2e2'; } - function rectFun () { - graph.updateLayout({type: 'rect-layout'}); + function rectFun() { + graph.updateLayout({ type: 'rect-layout' }); document.getElementById('radial').style.backgroundColor = '#e2e2e2'; document.getElementById('grid').style.backgroundColor = '#e2e2e2'; document.getElementById('circular').style.backgroundColor = '#e2e2e2 '; @@ -1457,36 +1458,36 @@ export default class ForceLayout extends Component { document.getElementById('dagre').style.backgroundColor = '#e2e2e2 '; document.getElementById('rect').style.backgroundColor = '#99FFFF'; } - function getCubicController (source, target) { + function getCubicController(source, target) { let c1, c2; - let distance = (target.y - source.y)/5; - if( distance > 0 ) distance = Math.max(distance, 80); - if( distance < 0 ) distance = Math.min(distance, -80); + let distance = (target.y - source.y) / 5; + if (distance > 0) distance = Math.max(distance, 80); + if (distance < 0) distance = Math.min(distance, -80); let p1 = [ source.x, - source.y + source.y ]; let p4 = [ - target.x, - target.y + target.x, + target.y ] - if( target.y > source.y ) { - c1 = [ source.x, source.y + distance ]; - c2 = [ target.x, target.y - distance ]; + if (target.y > source.y) { + c1 = [source.x, source.y + distance]; + c2 = [target.x, target.y - distance]; } else { - c1 = [ source.x, source.y - distance ]; - c2 = [ target.x, target.y + distance ]; + c1 = [source.x, source.y - distance]; + c2 = [target.x, target.y + distance]; } return { c1, c2 } - + } } diff --git a/stories/Plugins/component/toolbar.tsx b/stories/Plugins/component/toolbar.tsx index 13b7f35c25..6d6c602baf 100644 --- a/stories/Plugins/component/toolbar.tsx +++ b/stories/Plugins/component/toolbar.tsx @@ -6,7 +6,8 @@ let graph: IGraph = null; const data = { nodes: [{ - id: '1' + id: '1', + comboId: 'c1' }, { id: '2' },], @@ -14,6 +15,11 @@ const data = { source: '1', target: '2' }], + combos: [{ + id: 'c1' + }, { + id: 'c2' + }] }; const data2 = { nodes: [{ @@ -35,7 +41,14 @@ const ToolBar = () => { const container = React.useRef(); useEffect(() => { if (!graph) { - const toolbar = new G6.ToolBar(); + const toolbarDiv = document.createElement('div'); + toolbarDiv.id = 'toolbarContainer'; + const graphContainer = container.current as HTMLElement; + graphContainer.parentElement.appendChild(toolbarDiv); + + const toolbar = new G6.ToolBar({ + container: 'toolbarContainer' + }); graph = new Graph({ container: container.current as string | HTMLElement, width: 500, @@ -44,7 +57,7 @@ const ToolBar = () => { // 设置为true,启用 redo & undo 栈功能 enabledStack: true, modes: { - default: ['zoom-canvas', 'drag-node', { type: 'brush-select', }], + default: ['zoom-canvas', 'drag-node', { type: 'brush-select', }, 'drag-combo'], }, defaultNode: { size: 50 diff --git a/stories/Tree/component/flow-component.tsx b/stories/Tree/component/flow-component.tsx index ba971af68d..d40ce4c998 100644 --- a/stories/Tree/component/flow-component.tsx +++ b/stories/Tree/component/flow-component.tsx @@ -28,8 +28,8 @@ interface IFlowCharts { handleEdgeClick?: (item: IEdge, graph: IGraph) => void; handleNodeHover?: (item: INode, graph: IGraph) => void; handleNodeUnHover?: (item: INode, graph: IGraph) => void; - handleEdgeHover?: (item: INode, graph: IGraph) => void; - handleEdgeUnHover?: (item: INode, graph: IGraph) => void; + handleEdgeHover?: (item: IEdge, graph: IGraph) => void; + handleEdgeUnHover?: (item: IEdge, graph: IGraph) => void; collapseExpand?: boolean; } @@ -375,7 +375,7 @@ const FlowComponent: React.SFC = ({ } graph.on('node:mouseenter', (evt) => { - const { item } = evt; + const item: INode = evt.item as INode; graph.setItemState(item, 'hover', true); if (handleNodeHover) { handleNodeHover(item, graph); @@ -383,7 +383,7 @@ const FlowComponent: React.SFC = ({ }); graph.on('node:mouseleave', (evt) => { - const { item } = evt; + const item: INode = evt.item as INode; graph.setItemState(item, 'hover', false); if (handleNodeUnHover) { handleNodeUnHover(item, graph); @@ -391,20 +391,21 @@ const FlowComponent: React.SFC = ({ }); graph.on('node:click', (evt) => { - const { item, target } = evt; + const item: INode = evt.item as INode; + const { target } = evt; const targetType = target.get('type'); const name = target.get('name'); // 增加元素 if (targetType === 'marker') { - const model = item.getModel(); + const model: TreeGraphData = item.getModel() as TreeGraphData; if (name === 'add-item') { if (!model.children) { model.children = []; } model.children.push({ - id: Math.random(), - label: Math.random(), + id: `${Math.random()}`, + label: `${Math.random()}` }); graph.updateChild(model, model.id); } else if (name === 'remove-item') { @@ -418,7 +419,7 @@ const FlowComponent: React.SFC = ({ }); graph.on('edge:mouseenter', (evt) => { - const { item } = evt; + const item: IEdge = evt.item as IEdge; graph.setItemState(item, 'hover', true); if (handleEdgeHover) { handleEdgeHover(item, graph); @@ -426,7 +427,7 @@ const FlowComponent: React.SFC = ({ }); graph.on('edge:mouseleave', (evt) => { - const { item } = evt; + const item: IEdge = evt.item as IEdge; graph.setItemState(item, 'hover', false); if (handleEdgeUnHover) { handleEdgeUnHover(item, graph); @@ -434,7 +435,7 @@ const FlowComponent: React.SFC = ({ }); graph.on('edge:click', (evt) => { - const { item } = evt; + const item: IEdge = evt.item as IEdge; if (handleEdgeClick) { handleEdgeClick(item, graph); }