fix: remove custom group code

This commit is contained in:
baizn 2020-11-09 16:25:34 +08:00 committed by Moyee
parent 1a7ef1d119
commit 4fd48827df
42 changed files with 45 additions and 5903 deletions

View File

@ -1,241 +0,0 @@
---
title: Node Group
order: 1
---
> The title of Node Group is supported from G6 V3.1.2.
Node Group is a practical function for graph visualization. It is supported from G6 V3.0.5. Refer to the demo <a href='/en/examples/interaction/nodeGroup' target='_blank'>Demo</a>. <br /><img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*G1OBSJf672QAAAAAAAAAAABkARQnAQ' width=400 alt='img'/>
### Data Structure
In data, Node Group are defined as array `groups`, and each node data has a property `groupId`.
The `title` for a group can be a string or an object:
- When the `title` is a string, it indicates the text of the group title. The styles and the position are fixed;
- When the `title` is an object, users are able to define the styles and position for the group title. The object contains:
- text: required, a string indicates the text of the group title;
- offsetX: the x offset of the title, `0` by default;
- offsetY: the y offset of the title, `0` by default;
- stroke: the stroke color. The `fill`, `fontSize`, and other [Label Styles on Node](/en/docs/manual/middle/elements/nodes/defaultNode/#label-and-labelcfg).
```javascript
{
nodes: [
{
id: 'node1',
label: 'node1',
groupId: 'group1',
x: 100,
y: 100
},
{
id: 'node2',
label: 'node2',
groupId: 'group1',
x: 150,
y: 100
},
{
id: 'node3',
label: 'node3',
groupId: 'group2',
x: 300,
y: 100
},
],
edges: [
{
source: 'node1',
target: 'node2'
}
],
groups: [
{
id: 'group1',
title: {
text: 'The 1st group',
stroke: '#444',
offsetX: 0,
offsetY: 0
},
parentId: 'p1'
},
{
id: 'group2',
parentId: 'p1'
},
{
id: 'p1'
}
]
}
```
### Render the Node Group
If there are `groupId` in node data, G6 will render the group for the node automatically. You need to defined the `x` and `y` for nodes when there is no layout method for the graph.
```javascript
const data = {
nodes: [
{
id: 'node6',
groupId: 'group3',
label: 'rect',
x: 100,
y: 300,
},
{
id: 'node1',
label: 'fck',
groupId: 'group1',
x: 100,
y: 100,
},
{
id: 'node9',
label: 'noGroup1',
groupId: 'p1',
x: 300,
y: 210,
},
{
id: 'node2',
label: 'node2',
groupId: 'group1',
x: 150,
y: 200,
},
{
id: 'node3',
label: 'node3',
groupId: 'group2',
x: 300,
y: 100,
},
{
id: 'node7',
groupId: 'p1',
label: 'node7-p1',
x: 200,
y: 200,
},
{
id: 'node10',
label: 'noGroup',
groupId: 'p2',
x: 300,
y: 210,
},
],
edges: [
{
source: 'node1',
target: 'node2',
},
{
source: 'node2',
target: 'node3',
},
],
groups: [
{
id: 'group1',
title: {
text: 'The first group',
stroke: '#444',
offsetX: -30,
offsetY: 30,
},
parentId: 'p1',
},
{
id: 'group2',
parentId: 'p1',
},
{
id: 'group3',
parentId: 'p2',
},
{
id: 'p1',
},
{
id: 'p2',
},
],
};
const graph = new G6.Graph({
container: 'mountNode',
width: 800,
height: 600,
modes: {
default: ['drag-canvas'],
},
defaultNode: {
type: 'circle',
},
defaultEdge: {
color: '#bae7ff',
},
});
graph.data(data);
graph.render();
```
The result: <br /><img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*iftmRrdqR7cAAAAAAAAAAABkARQnAQ' width=400 alt='img'/>
<br />To controll the nodes and the groups, we now assign the behaviors to them.
### Manipulate the Group
The built-in [Behavior](/en/docs/manual/middle/states/defaultBehavior)s `drag-group`, `collapse-expand-group`, and `drag-node-with-group` allow user to manipulate the group:
- Drag the group;
- Unrelate the node and its group by dragging the node out of the group;
- Double click to expand/collapse the group:
- When the group is collapsed, the edges linked to the inner nodes will link to the group;
- When the group is expanded, the related edges are restored.
- When user is dragging a node, the node's group will be highlighted; when the node is dragged to another group, the corresponding group will be highlighted
- **Do not Support** ~~Dropping a node into another group~~.
#### drag-group
`drag-group` is a Behavior which supports the group dragging.
#### collapse-expand-group
`collapse-expand-group` is a Behavior which supports expand and collapse the group by double click.
Now, we only support collapse and expand the group by double click.
#### drag-node-with-group
`drag-node-with-group` is a Behavior that similar to `drag-node`. But it is applied on the node which has a group. The group of the node will be changed by dragging.
We do not support dragging multiple nodes in/out a group.
```javascript
const graph = new G6.Graph({
container: 'mountNode',
width: 800,
height: 600,
modes: {
default: ['drag-group', 'collapse-expand-group', 'drag-node-with-group'],
},
defaultNode: {
type: 'circleNode',
},
defaultEdge: {
color: '#bae7ff',
},
});
```
Assigning the three built-in Behavior into `modes` of the graph instance results in:
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*VsMbRqOJe2sAAAAAAAAAAABkARQnAQ' width=400 alt='img'/>

View File

@ -1,245 +0,0 @@
---
title: 节点分组 Group
order: 1
---
> New Feature自 G6 3.1.2 开始支持自定义节点分组的标题了,可以渲染带有标题的分组。
对于熟悉图可视化类库的用户来说,节点分组可能是比较实用的一个功能。自 G6 3.0.5 版本开始G6 加入了节点分组的功能,详情参考 <a href='/zh/examples/interaction/nodeGroup' target='_blank'>Demo</a><br /><img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*G1OBSJf672QAAAAAAAAAAABkARQnAQ' width=400 alt='img'/>
### 数据结构
新增节点分组功能时,尽量保持了 G6 数据结构的稳定性。为了体现分组的特性,我们在 nodes 数据项中加入了 groupId 属性,另外新增了 `groups` 字段,用于表示数据中所包括的分组及各分组之间的层级关系。
当 groups 中的对象包括 title 属性字段时表示要渲染带有标题的分组title 字段的类型可以是 string 或 object
- title类型为 string 时,值表示标题名称,不能设置任何样式,也不能调整标题位置;
- title 为 object 时可以设置标题的样式及位置title 中各字段含义:
- text必选分组的标题类型为 string
- offsetX可选默认为 0表示 x 方向上的偏移量;
- offsetY可选默认为 0表示 y 方向上的偏移量;
- stroke字体边框颜色同时也支持 fill、fontSize 等所有的 [节点上的文本样式属性](/zh/docs/manual/middle/elements/nodes/defaultNode/#标签文本-label-及其配置-labelcfg)。
```javascript
{
nodes: [
{
id: 'node1',
label: 'node1',
groupId: 'group1',
x: 100,
y: 100
},
{
id: 'node2',
label: 'node2',
groupId: 'group1',
x: 150,
y: 100
},
{
id: 'node3',
label: 'node3',
groupId: 'group2',
x: 300,
y: 100
},
],
edges: [
{
source: 'node1',
target: 'node2'
}
],
groups: [
{
id: 'group1',
title: {
text: '第一个分组',
stroke: '#444',
offsetX: 0,
offsetY: 0
},
parentId: 'p1'
},
{
id: 'group2',
parentId: 'p1'
},
{
id: 'p1'
}
]
}
```
### 如何渲染 group
当 nodes 中存在 `groupId` 属性字段时在渲染过程中G6 会自动渲染分组。当存在 groups 属性时G6 会自动判断各分组之间的层级关系,并渲染出嵌套的分组。但当没有使用任何布局的时候,需要在 nodes 中指定各个节点的坐标信息,即节点的 `x``y` 属性值。
```javascript
const data = {
nodes: [
{
id: 'node6',
groupId: 'group3',
label: 'rect',
x: 100,
y: 300,
},
{
id: 'node1',
label: 'fck',
groupId: 'group1',
x: 100,
y: 100,
},
{
id: 'node9',
label: 'noGroup1',
groupId: 'p1',
x: 300,
y: 210,
},
{
id: 'node2',
label: 'node2',
groupId: 'group1',
x: 150,
y: 200,
},
{
id: 'node3',
label: 'node3',
groupId: 'group2',
x: 300,
y: 100,
},
{
id: 'node7',
groupId: 'p1',
label: 'node7-p1',
x: 200,
y: 200,
},
{
id: 'node10',
label: 'noGroup',
groupId: 'p2',
x: 300,
y: 210,
},
],
edges: [
{
source: 'node1',
target: 'node2',
},
{
source: 'node2',
target: 'node3',
},
],
groups: [
{
id: 'group1',
title: {
text: '第一个分组',
stroke: '#444',
offsetX: -30,
offsetY: 30,
},
parentId: 'p1',
},
{
id: 'group2',
parentId: 'p1',
},
{
id: 'group3',
parentId: 'p2',
},
{
id: 'p1',
},
{
id: 'p2',
},
],
};
const graph = new G6.Graph({
container: 'mountNode',
width: 800,
height: 600,
modes: {
default: ['drag-canvas'],
},
defaultEdge: {
color: '#bae7ff',
},
});
graph.data(data);
graph.render();
```
渲染的效果如下图所示:<br /><img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*iftmRrdqR7cAAAAAAAAAAABkARQnAQ' width=400 alt='img'/><br />此时,不能对分组中的节点及分组进行任何操作,接下来,我们介绍可以对分组进行的各种操作。
### 操作分组
只是简单地将分组渲染出来,并没有多大的实用价值,只有支持一系列的交互操作后,才能最大程度地体现分组的价值。
在 G6 中,我们内置了 `drag-group`、`collapse-expand-group` 及   `drag-node-with-group`  三个 [Behavior](/zh/docs/manual/middle/states/defaultBehavior),共支持以下的交互行为:
- 拖动分组;
- 通过拖拽,动态改变分组中的节点数量及分组大小;
- 将一个分组从父分组中拖拽出来,并取消分组直接的关联关系,动态改变父分组的大小;
- 双击分组,收起和展开分组:
- 当收起分组后,与分组节点中的连线会自动连到分组上;
- 展开分组后,恢复之前的连接和位置。
- 拖动节点,所在的分组高亮,当拖到其他分组时,其他分组高亮;
- [暂不支持] ~~将分组拖入到另外个分组,并改变分组层级的所属关系~~
#### drag-group
`drag-group` Behavior支持拖动分组拖动分组过程中会动态改变分组中节点和边的位置在拖拽完成以后保持分组和节点的相对位置不变。
#### collapse-expand-group
`collapse-expand-group` Behavior支持双击分组收起和展开分组收起分组以后隐藏分组中的所有节点外部节点和分组中节点有连线的情况下所有连接会连接到分组上面。
优化目前只支持双击交互,正式发布时,会支持用户自定义交互方式,来实现分组的收起和展开。
#### drag-node-with-group
`drag-node-with-group` Behavior `drag-node`  类似,但该 Behavior 仅用于用 group 时 node 的拖拽。拖拽 node 过程中,会动态改变 node 所在的分组。
优化目前暂不支持将多个节点拖出拖入到分组中。
```javascript
const graph = new G6.Graph({
container: 'mountNode',
width: 800,
height: 600,
modes: {
default: ['drag-group', 'collapse-expand-group', 'drag-node-with-group'],
},
defaultNode: {
type: 'circleNode',
},
defaultEdge: {
color: '#bae7ff',
},
});
```
将这三个内置提供的 Behavior 加入到 `modes` 中以后的效果如下图所示。
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*VsMbRqOJe2sAAAAAAAAAAABkARQnAQ' width=400 alt='img'/>
### 适用场景
1. 风控、反洗钱、保险骗保、网络诈骗、信用卡诈骗等场景下团伙分析;
2. 特征分析:同一个分组中的节点在某些特征上面比较相似;
3. 整理节点:当类似的节点放到一个分组中,只渲染分组,不渲染节点,减少干扰元素。

View File

@ -3,7 +3,7 @@ title: The Visual Level of Node and Edge
order: 4 order: 4
--- ---
The visual levels (zIndex) of nodes and edges are refered to their [Graphics Group](/en/docs/manual/middle/elements/shape/graphics-group) (hereinafter referred to as Shape). (<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>Attention:</strong></span> The Graphics Group is different from the [Node Group](/en/docs/manual/middle/discard/nodeGroup), the differences are described in [Graphics Group](/en/docs/manual/middle/elements/shape/graphics-group)). The visual levels (zIndex) of nodes and edges are refered to their [Graphics Group](/en/docs/manual/middle/elements/shape/graphics-group) (hereinafter referred to as Shape). (<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>Attention:</strong></span> The Graphics Group is different from the [Node Combo](/en/docs/manual/middle/discard/nodeGroup), the differences are described in [Graphics Group](/en/docs/manual/middle/elements/shape/graphics-group)).
In [Graphics Group](/en/docs/manual/middle/elements/shape/graphics-group), we stated: All the nodes instances in a Graph is grouped by a Group named `nodeGroup`, all the edges instances are grouped by `edgeGroup`. And the visual level (zIndex) of `nodeGroup` is higher than `edgeGroup`, which means all the nodes will be drawed on the top of all the edges. In [Graphics Group](/en/docs/manual/middle/elements/shape/graphics-group), we stated: All the nodes instances in a Graph is grouped by a Group named `nodeGroup`, all the edges instances are grouped by `edgeGroup`. And the visual level (zIndex) of `nodeGroup` is higher than `edgeGroup`, which means all the nodes will be drawed on the top of all the edges.

View File

@ -3,7 +3,7 @@ title: 节点与边的层级
order: 4 order: 4
--- ---
节点与边在视觉上的层级涉及到了它们相对应的 [图形分组 Group](/zh/docs/manual/middle/elements/shape/graphics-group)。本文提到的所有分组 Group 都为 G6 的图形分组 Group而非 G6 的  [节点分组 Group](/zh/docs/manual/middle/discard/nodeGroup),请注意区分这两种 Group,其区别在 [图形分组 Group](/zh/docs/manual/middle/elements/shape/graphics-group)  中说明。 节点与边在视觉上的层级涉及到了它们相对应的 [图形分组 Group](/zh/docs/manual/middle/elements/shape/graphics-group)。本文提到的所有分组 Group 都为 G6 的图形分组 Group而非 G6 的  [节点分组 Combo](/zh/docs/manual/middle/discard/nodeGroup),其区别在 [图形分组 Group](/zh/docs/manual/middle/elements/shape/graphics-group)  中说明。
在 [图形分组 Group](/zh/docs/manual/middle/elements/shape/graphics-group) 中我们提到:在 G6 中Graph 的一个实例中的所有节点属于同一个变量名为 `nodeGroup` 的 group所有的边属于同一个变量名为 `edgeGroup` 的 group。节点 group 在视觉上的层级zIndex高于边 group即所有节点会绘制在所有边的上层。 在 [图形分组 Group](/zh/docs/manual/middle/elements/shape/graphics-group) 中我们提到:在 G6 中Graph 的一个实例中的所有节点属于同一个变量名为 `nodeGroup` 的 group所有的边属于同一个变量名为 `edgeGroup` 的 group。节点 group 在视觉上的层级zIndex高于边 group即所有节点会绘制在所有边的上层。

View File

@ -3,10 +3,10 @@ title: 图形分组 Group
order: 2 order: 2
--- ---
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"> &nbsp;&nbsp;<strong>⚠️ 注意:</strong></span> <br /> 图形分组 Group 与 [节点分组 Group](/zh/docs/manual/middle/discard/nodeGroup) 虽然都名为 Group属于不同层次的概念。 <span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"> &nbsp;&nbsp;<strong>⚠️ 注意:</strong></span> <br /> 图形分组 Group 与 [节点分组 Combo](/zh/docs/manual/middle/discard/nodeGroup) 属于不同层次的概念。
- 图形分组针对 [图形 Shape](/zh/docs/manual/middle/elements/shape/shape-keyshape) 层次的分组; - 图形分组针对 [图形 Shape](/zh/docs/manual/middle/elements/shape/shape-keyshape) 层次的分组;
- [节点分组 Group](/zh/docs/manual/middle/discard/nodeGroup)  是针对 [节点](/zh/docs/manual/middle/elements/nodes/defaultNode) 的分组,与数据结构中的层次、分组对应。 - [节点分组 Combo](/zh/docs/manual/middle/discard/nodeGroup)  是针对 [节点](/zh/docs/manual/middle/elements/nodes/defaultNode) 的分组,与数据结构中的层次、分组对应。
<br /> <br />

View File

@ -590,77 +590,6 @@ graph.on('itemcollapsed', (e) => {
}); });
``` ```
### collapse-expand-group
- 含义:收起和展开群组;
- 配置项:
- `type'collapse-expand-group'`
- `trigger`:收起和展开节点分组的方式。支持 `'click'``'dblclick'` 两种方式。默认为 `'dblclick'`,即双击。
**使用默认配置**
```javascript
const graph = new G6.Graph({
modes: {
default: ['collapse-expand-group'],
},
});
```
**使用自定义参数**<br />配置 `trigger` 参数为 **`click`** 后,单击节点分组即可收起或展开分组。
```javascript
const graph = new G6.Graph({
modes: {
default: [
{
type: 'collapse-expand-group',
trigger: 'click',
},
],
},
});
```
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*znCaS48_BpgAAAAAAAAAAABkARQnAQ' width=400 alt='img'/>
### drag-group
- 含义:拖动节点分组;
- 配置项:
- `type: 'drag-group'`
- `delegateStyle`:拖动节点分组时 `delegate` 的样式。
**使用默认配置**
```javascript
const graph = new G6.Graph({
modes: {
default: ['drag-group'],
},
});
```
### drag-node-with-group
- 含义:拖动节点分组中的节点;
- 配置项:
- `type'drag-node-with-group'`
- `delegateStyle`:拖动节点时 `delegate` 的样式;
- `maxMultiple`
- `minMultiple`
- `shouldBegin(e)`:是否允许当前被操作的节点被拖拽。
**使用默认配置**
```javascript
const graph = new G6.Graph({
modes: {
default: ['drag-node-with-group'],
},
});
```
### create-edge ### create-edge
- 含义:通过交互创建边; - 含义:通过交互创建边;

View File

@ -229,17 +229,17 @@ graph.on('edge:click', (e) => {
node.style.fill = 'steelblue'; node.style.fill = 'steelblue';
switch (node.class) { switch (node.class) {
case 'c0': { case 'c0': {
node.shape = 'circle'; node.type = 'circle';
node.size = 30; node.size = 30;
break; break;
} }
case 'c1': { case 'c1': {
node.shape = 'rect'; node.type = 'rect';
node.size = [35, 20]; node.size = [35, 20];
break; break;
} }
case 'c2': { case 'c2': {
node.shape = 'ellipse'; node.type = 'ellipse';
node.size = [35, 20]; node.size = [35, 20];
break; break;
} }

View File

@ -231,17 +231,17 @@ graph.on('edge:click', (e) => {
node.style.fill = 'steelblue'; node.style.fill = 'steelblue';
switch (node.class) { switch (node.class) {
case 'c0': { case 'c0': {
node.shape = 'circle'; node.type = 'circle';
node.size = 30; node.size = 30;
break; break;
} }
case 'c1': { case 'c1': {
node.shape = 'rect'; node.type = 'rect';
node.size = [35, 20]; node.size = [35, 20];
break; break;
} }
case 'c2': { case 'c2': {
node.shape = 'ellipse'; node.type = 'ellipse';
node.size = [35, 20]; node.size = [35, 20];
break; break;
} }

View File

@ -466,7 +466,7 @@ const loadData = (data) => {
mapNodeSizeAndFontSize(showNodes, 'count', [40, 120]); mapNodeSizeAndFontSize(showNodes, 'count', [40, 120]);
showNodes.forEach((snode) => { showNodes.forEach((snode) => {
if (snode.size < 80) { if (snode.size < 80) {
snode.shape = 'circle'; snode.type = 'circle';
} }
}); });
@ -584,7 +584,7 @@ graph.on('node:click', (e) => {
node.style.lineWidth = 0; node.style.lineWidth = 0;
node.style.opacity = 1; node.style.opacity = 1;
if (node.neighbor) { if (node.neighbor) {
node.shape = 'animate-circle'; node.type = 'animate-circle';
node.label = node.text; node.label = node.text;
const color = model.style.fill; const color = model.style.fill;
node.color = color; node.color = color;

View File

@ -760,7 +760,7 @@ graph.on('node:click', (e) => {
node.style.lineWidth = 0; node.style.lineWidth = 0;
node.style.opacity = 1; node.style.opacity = 1;
if (node.isLeaf) { if (node.isLeaf) {
node.shape = 'animate-circle'; node.type = 'animate-circle';
let color = 'l(0)'; let color = 'l(0)';
const parentsNum = parents.length; const parentsNum = parents.length;
parents.forEach((parent, i) => { parents.forEach((parent, i) => {
@ -784,7 +784,7 @@ graph.on('node:click', (e) => {
position: 'center', position: 'center',
}; };
} else if (node.level !== 0) { } else if (node.level !== 0) {
node.shape = 'circle'; // 'bubble'; node.type = 'circle'; // 'bubble';
node.size = 95; node.size = 95;
if (!node.style) node.style = {}; if (!node.style) node.style = {};
node.color = model.color; node.color = model.color;

View File

@ -1,105 +0,0 @@
import G6 from '@antv/g6';
/**
* 该案例演示以下功能
* 1渲染群组所需要的数据结构
* 2如何拖动一个群组
* 3将节点从群组中拖出
* 4将节点拖入到某个群组中
* 5拖出拖入节点后动态改变群组大小
*/
const width = document.getElementById('container').scrollWidth;
const height = document.getElementById('container').scrollHeight || 500;
const graph = new G6.Graph({
container: 'container',
width,
height,
defaultNode: {
type: 'circle',
style: {
fill: '#DEE9FF',
stroke: '#5B8FF9',
},
},
defaultEdge: {
color: '#e2e2e2',
},
modes: {
default: ['drag-canvas', 'drag-group', 'drag-node-with-group', 'collapse-expand-group'],
},
});
const data = {
nodes: [
{
id: 'node1',
label: 'node1-group1',
groupId: 'group1',
x: 100,
y: 100,
},
{
id: 'node2',
label: 'node2-group2',
groupId: 'group1',
x: 150,
y: 200,
},
{
id: 'node3',
label: 'node3-group2',
groupId: 'group2',
x: 300,
y: 200,
},
{
id: 'node10',
label: 'node10-p2',
groupId: 'p2',
x: 300,
y: 310,
},
],
edges: [
{
source: 'node1',
target: 'node2',
},
{
source: 'node2',
target: 'node3',
},
{
source: 'node1',
target: 'node3',
},
],
groups: [
{
id: 'group1',
title: {
text: 'Group 1',
stroke: '#444',
offsetX: -20,
offsetY: 30,
},
},
{
id: 'group2',
title: {
text: 'Group 2',
stroke: '#444',
offsetX: -20,
offsetY: 30,
},
parentId: 'p2',
},
{
id: 'p2',
title: 'Group 3',
},
],
};
graph.data(data);
graph.render();

View File

@ -1,24 +0,0 @@
{
"title": {
"zh": "中文分类",
"en": "Category"
},
"demos": [
{
"filename": "circle.js",
"title": {
"zh": "圆形分组",
"en": "Circle Node Group"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*S_-uR6WO-ZgAAAAAAAAAAABkARQnAQ"
},
{
"filename": "rect.js",
"title": {
"zh": "矩形分组",
"en": "Rect Node Group"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*DuaCTKCCUAoAAAAAAAAAAABkARQnAQ"
}
]
}

View File

@ -1,102 +0,0 @@
import G6 from '@antv/g6';
/**
* 该案例演示以下功能
* 1渲染群组所需要的数据结构
* 2如何拖动一个群组
* 3将节点从群组中拖出
* 4将节点拖入到某个群组中
* 5拖出拖入节点后动态改变群组大小
*/
const width = document.getElementById('container').scrollWidth;
const height = document.getElementById('container').scrollHeight || 500;
const graph = new G6.Graph({
container: 'container',
width,
height,
defaultNode: {
type: 'circle',
style: {
fill: '#DEE9FF',
stroke: '#5B8FF9',
},
},
defaultEdge: {
color: '#e2e2e2',
},
modes: {
default: ['drag-canvas', 'drag-group', 'drag-node-with-group', 'collapse-expand-group'],
},
groupType: 'rect',
});
const data = {
nodes: [
{
id: 'node1',
label: 'node1-group1',
groupId: 'group1',
x: 100,
y: 100,
},
{
id: 'node2',
label: 'node2-group2',
groupId: 'group1',
x: 150,
y: 200,
},
{
id: 'node3',
label: 'node3-group2',
groupId: 'group2',
x: 300,
y: 200,
},
{
id: 'node10',
label: 'node10-p2',
groupId: 'p2',
x: 300,
y: 310,
},
],
edges: [
{
source: 'node1',
target: 'node2',
},
{
source: 'node2',
target: 'node3',
},
{
source: 'node1',
target: 'node3',
},
],
groups: [
{
id: 'group1',
title: {
text: 'Group 1',
stroke: '#444',
},
},
{
id: 'group2',
title: {
text: 'Group 2',
stroke: '#444',
},
parentId: 'p2',
},
{
id: 'p2',
title: 'Group 3',
},
],
};
graph.data(data);
graph.render();

View File

@ -1,10 +0,0 @@
---
title: Node Group
order: 5
---
G6 supports node group. We recommend to use the new mechanism [Combo](/en/examples/interaction/combo).
## Usage
G6 supports two kinds of node group: `circle` and `rect`. They can be applied to group navigation and clustering analysis.

View File

@ -1,10 +0,0 @@
---
title: 节点分组 Group
order: 5
---
G6 支持节点分组。建议使用新的分组机制 [Combo](/zh/examples/interaction/combo).
## 使用指南
G6 默认支持 `circle``rect` 两种类型的节点分组,可用于团伙导航或聚类分析。

View File

@ -1,47 +0,0 @@
/*
* @Author: moyee
* @Date: 2019-07-31 14:36:15
* @LastEditors: moyee
* @LastEditTime: 2019-08-22 18:43:24
* @Description:
*/
import { G6Event, IG6GraphEvent } from '../types';
const DEFAULT_TRIGGER = 'dblclick';
const ALLOW_EVENTS = ['click', 'dblclick'];
export default {
getDefaultCfg(): object {
return {
trigger: DEFAULT_TRIGGER,
};
},
getEvents(): { [key in G6Event]?: string } {
let trigger: string;
// 检测输入是否合法
if (ALLOW_EVENTS.includes(this.trigger)) {
trigger = this.trigger;
} else {
trigger = DEFAULT_TRIGGER;
// eslint-disable-next-line no-console
console.warn(
"Behavior collapse-expand-group 的 trigger 参数不合法,请输入 'click' 或 'dblclick'",
);
}
return {
[`${trigger}`]: 'onGroupClick',
};
},
onGroupClick(evt: IG6GraphEvent) {
const { target } = evt;
const { graph } = this;
const groupId = target.get('groupId');
if (!groupId) {
return;
}
const customGroupControll = graph.get('customGroupControll');
customGroupControll.collapseExpandGroup(groupId);
},
};

View File

@ -91,7 +91,7 @@ export default {
return; return;
} }
if (self.keydown || e.shape) { if (self.keydown || e.type) {
return; return;
} }
@ -124,7 +124,7 @@ export default {
}, },
onMouseMove(e: IG6GraphEvent) { onMouseMove(e: IG6GraphEvent) {
const { graph } = this; const { graph } = this;
if (this.keydown || e.shape) { if (this.keydown || e.type) {
return; return;
} }
@ -154,7 +154,7 @@ export default {
onMouseUp(e: IG6GraphEvent) { onMouseUp(e: IG6GraphEvent) {
const { graph } = this; const { graph } = this;
if (this.keydown || e.shape) { if (this.keydown || e.type) {
return; return;
} }

View File

@ -1,170 +0,0 @@
/*
* @Author: moyee
* @Date: 2019-07-31 14:36:15
* @LastEditors: moyee
* @LastEditTime: 2019-08-23 11:13:43
* @Description:
*/
import deepMix from '@antv/util/lib/deep-mix';
import { G6Event, IG6GraphEvent } from '../types';
import Global from '../global';
export default {
getDefaultCfg(): object {
return {
delegate: true,
delegateStyle: {},
delegateShapes: {},
delegateShapeBBoxs: {},
};
},
getEvents(): { [key in G6Event]?: string } {
return {
dragstart: 'onDragStart',
drag: 'onDrag',
dragend: 'onDragEnd',
'canvas:mouseleave': 'onOutOfRange',
};
},
onDragStart(evt: IG6GraphEvent) {
const { graph } = this;
const { target } = evt;
// 获取拖动的group的ID如果拖动的不是group则直接return
const groupId: string = target.get('groupId');
if (!groupId) {
return;
}
const customGroupControll = graph.get('customGroupControll');
const { customGroup } = customGroupControll;
const currentGroup = customGroup[groupId].nodeGroup;
this.targetGroup = currentGroup;
this.mouseOrigin = {
x: evt.x,
y: evt.y,
};
// 获取groupId的父Group的ID
const { groups } = graph.save();
let parentGroupId = null;
for (let i = 0; i < groups.length; i++) {
const group = groups[i];
if (groupId === group.id) {
parentGroupId = group.parentId;
break;
}
}
if (parentGroupId) {
const parentGroup = customGroup[parentGroupId].nodeGroup;
customGroupControll.setGroupStyle(parentGroup.get('keyShape'), 'hover');
}
},
onDrag(evt: IG6GraphEvent) {
if (!this.mouseOrigin) {
return;
}
this.updateDelegate(evt);
},
onDragEnd(evt: IG6GraphEvent) {
const { graph } = this;
// 删除delegate shape
const groupId: string = evt.target.get('groupId');
if (this.delegateShapes[groupId]) {
this.delegateShapeBBox = this.delegateShapes[groupId].getBBox();
this.delegateShapes[groupId].remove();
delete this.delegateShapes[groupId];
}
if (!this.delegateShapeBBox) {
return;
}
// 修改群组位置
const customGroupControll = graph.get('customGroupControll');
const delegateShapeBBoxs = this.delegateShapeBBoxs[groupId];
customGroupControll.updateGroup(groupId, delegateShapeBBoxs, this.mouseOrigin);
this.mouseOrigin = null;
this.shapeOrigin = null;
customGroupControll.resetNodePoint();
this.delegateShapeBBox = null;
},
updateDelegate(evt: IG6GraphEvent) {
const { graph } = this;
const groupId: string = evt.target.get('groupId');
const item = this.targetGroup.get('keyShape');
let delegateShape = this.delegateShapes[groupId];
const groupBbox = item.getBBox();
const delegateType = item.get('type');
if (!delegateShape) {
const delegateGroup = graph.get('delegateGroup');
const { x: bboxX, y: bboxY, width, height } = groupBbox;
const attrs = {
width,
height,
...deepMix({}, Global.delegateStyle, this.delegateStyle),
};
// 如果delegate是circle
if (delegateType === 'circle') {
const r = width > height ? width / 2 : height / 2;
const cx = bboxX + r;
const cy = bboxY + r;
delegateShape = delegateGroup.addShape('circle', {
attrs: {
x: cx,
y: cy,
r,
...attrs,
},
name: 'circle-delegate-shape',
});
this.shapeOrigin = { x: cx, y: cy };
} else {
delegateShape = delegateGroup.addShape('rect', {
attrs: {
x: bboxX,
y: bboxY,
...attrs,
},
name: 'rect-delegate-shape',
});
this.shapeOrigin = { x: bboxX, y: bboxY };
}
// delegateShape.set('capture', false);
this.delegateShapes[groupId] = delegateShape;
this.delegateShapeBBoxs[groupId] = delegateShape.getBBox();
} else {
const { mouseOrigin, shapeOrigin } = this;
const deltaX = evt.x - mouseOrigin.x;
const deltaY = evt.y - mouseOrigin.y;
const x = deltaX + shapeOrigin.x;
const y = deltaY + shapeOrigin.y;
delegateShape.attr({ x, y });
this.delegateShapeBBoxs[groupId] = delegateShape.getBBox();
}
},
onOutOfRange(e: IG6GraphEvent) {
const canvasElement = this.graph.get('canvas').get('el');
const listener = (ev) => {
if (ev.target !== canvasElement) {
this.onDragEnd(e);
// 终止时需要判断此时是否在监听画布外的 mouseup 事件,若有则解绑
document.body.removeEventListener('mouseup', listener, true);
}
};
if (this.mouseOrigin) {
document.body.addEventListener('mouseup', listener, true);
}
},
};

View File

@ -1,356 +0,0 @@
import { IG6GraphEvent, Item, G6Event, NodeConfig } from '../types';
/*
* @Author: moyee
* @Date: 2019-06-27 18:12:06
* @LastEditors: moyee
* @LastEditTime: 2019-08-23 13:54:53
* @Description: group的情况下Behavior
*/
import deepMix from '@antv/util/lib/deep-mix';
import Global from '../global';
const { body } = document;
export default {
getDefaultCfg(): object {
return {
updateEdge: true,
delegate: true,
delegateStyle: {},
maxMultiple: 1.1,
minMultiple: 1,
};
},
getEvents(): { [key in G6Event]?: string } {
return {
'node:dragstart': 'onDragStart',
'node:drag': 'onDrag',
'node:dragend': 'onDragEnd',
'canvas:mouseleave': 'onOutOfRange',
dragover: 'onDragOver',
// FIXME: does not response
dragleave: 'onDragLeave',
};
},
onDragOver(evt: IG6GraphEvent) {
const { graph } = this;
const { target } = evt;
const groupId = target.get('groupId');
if (groupId && this.origin) {
const customGroupControll = graph.get('customGroupControll');
const customGroup = customGroupControll.getDeletageGroupById(groupId);
if (customGroup) {
const { nodeGroup: currentGroup } = customGroup;
const keyShape = currentGroup.get('keyShape');
this.inGroupId = groupId;
customGroupControll.setGroupStyle(keyShape, 'hover');
}
}
},
/**
* Group时的事件
* @param {Event} evt
*/
onDragLeave(evt: IG6GraphEvent) {
const { graph } = this;
const { target } = evt;
const groupId = target.get('groupId');
if (groupId && this.origin) {
const customGroupControll = graph.get('customGroupControll');
const customGroup = customGroupControll.getDeletageGroupById(groupId);
if (customGroup) {
const { nodeGroup: currentGroup } = customGroup;
const keyShape = currentGroup.get('keyShape');
customGroupControll.setGroupStyle(keyShape, 'default');
}
}
if (!groupId) {
this.inGroupId = null;
}
},
onDragStart(e: IG6GraphEvent) {
const { graph } = this;
if (!this.shouldBegin.call(this, e)) {
return;
}
const { item } = e;
this.target = item;
// 拖动节点时如果在Group中则Group高亮
const model = item.getModel();
const { groupId } = model;
if (groupId) {
const customGroupControll = graph.get('customGroupControll');
const customGroup = customGroupControll.getDeletageGroupById(groupId);
if (customGroup) {
const { nodeGroup: currentGroup } = customGroup;
const keyShape = currentGroup.get('keyShape');
customGroupControll.setGroupStyle(keyShape, 'hover');
// 初始拖动时候如果是在当前群组中拖动则赋值为当前groupId
this.inGroupId = groupId;
}
}
this.origin = {
x: e.x,
y: e.y,
};
this.point = {};
this.originPoint = {};
},
onDrag(e: IG6GraphEvent) {
if (!this.origin) {
return;
}
if (!this.get('shouldUpdate').call(this, e)) {
return;
}
this.update(this.target, e, true);
const { item } = e;
const { graph } = this;
const model = item.getModel();
const { groupId } = model;
if (groupId) {
const customGroupControll = graph.get('customGroupControll');
const customGroup = customGroupControll.getDeletageGroupById(groupId);
if (customGroup) {
const { nodeGroup: currentGroup } = customGroup;
const keyShape = currentGroup.get('keyShape');
// 当前
if (this.inGroupId !== groupId) {
customGroupControll.setGroupStyle(keyShape, 'default');
} else {
customGroupControll.setGroupStyle(keyShape, 'hover');
}
}
}
},
onDragEnd(e: IG6GraphEvent) {
if (!this.origin || !this.shouldEnd.call(this, e)) {
return;
}
if (this.shape) {
this.shape.remove();
this.shape = null;
}
if (this.target) {
const delegateShape = this.target.get('delegateShape');
if (delegateShape) {
delegateShape.remove();
this.target.set('delegateShape', null);
}
}
if (this.target) {
this.update(this.target, e);
}
this.point = {};
this.origin = null;
this.originPoint = {};
this.target = null;
this.setCurrentGroupStyle(e);
},
setCurrentGroupStyle(evt: IG6GraphEvent) {
const { graph } = this;
const { item } = evt;
const model = item.getModel() as NodeConfig;
// 节点所在的GroupId
const { groupId, id } = model;
const customGroupControll = graph.get('customGroupControll');
const { customGroup } = customGroupControll;
const groupNodes = graph.get('groupNodes');
if (this.inGroupId && groupId) {
const currentGroup = customGroup[groupId].nodeGroup;
if (!currentGroup) {
return;
}
const keyShape = currentGroup.get('keyShape');
const itemBBox = item.getBBox();
const currentGroupBBox = keyShape.getBBox();
const { centerX, centerY } = itemBBox;
const { minX, minY, maxX, maxY } = currentGroupBBox;
// 在自己的group中拖动判断是否拖出了自己的group
// this.inGroupId !== groupId则说明拖出了原来的group拖到了其他group上面
// 则删除item中的groupId字段同时删除group中的nodeID
if (
!(
centerX < maxX * this.maxMultiple &&
centerX > minX * this.minMultiple &&
centerY < maxY * this.maxMultiple &&
centerY > minY * this.minMultiple
) ||
this.inGroupId !== groupId
) {
// 拖出了group则删除item中的groupId字段同时删除group中的nodeID
const currentGroupNodes = groupNodes[groupId];
groupNodes[groupId] = currentGroupNodes.filter((node) => node !== id);
customGroupControll.dynamicChangeGroupSize(evt, currentGroup, keyShape);
// 同时删除groupID中的节点
delete model.groupId;
}
// 拖动到其他的group上面
if (this.inGroupId !== groupId) {
// 拖动新的group后更新groupNodes及model中的groupId
const nodeInGroup = customGroup[this.inGroupId].nodeGroup;
if (!nodeInGroup) {
return;
}
const targetKeyShape = nodeInGroup.get('keyShape');
// 将该节点添加到inGroupId中
if (groupNodes[this.inGroupId].indexOf(id) === -1) {
groupNodes[this.inGroupId].push(id);
}
// 更新节点的groupId为拖动上去的group Id
model.groupId = this.inGroupId;
// 拖入节点后,根据最新的节点数量,重新计算群组大小
customGroupControll.dynamicChangeGroupSize(evt, nodeInGroup, targetKeyShape);
}
customGroupControll.setGroupStyle(keyShape, 'default');
} else if (this.inGroupId && !groupId) {
// 将节点拖动到群组中
const nodeInGroup = customGroup[this.inGroupId].nodeGroup;
if (!nodeInGroup) {
return;
}
const keyShape = nodeInGroup.get('keyShape');
// 将该节点添加到inGroupId中
if (groupNodes[this.inGroupId].indexOf(id) === -1) {
groupNodes[this.inGroupId].push(id);
}
// 更新节点的groupId为拖动上去的group Id
model.groupId = this.inGroupId;
// 拖入节点后,根据最新的节点数量,重新计算群组大小
customGroupControll.dynamicChangeGroupSize(evt, nodeInGroup, keyShape);
} else if (!this.inGroupId && groupId) {
// 拖出到群组之外了则删除数据中的groupId
Object.keys(groupNodes).forEach((gnode) => {
const currentGroupNodes = groupNodes[gnode];
groupNodes[gnode] = currentGroupNodes.filter((node) => node !== id);
});
const currentGroup = customGroup[groupId].nodeGroup;
if (!currentGroup) {
return;
}
const keyShape = currentGroup.get('keyShape');
customGroupControll.dynamicChangeGroupSize(evt, currentGroup, keyShape);
delete model.groupId;
}
this.inGroupId = null;
},
// 若在拖拽时,鼠标移出画布区域,此时放开鼠标无法终止 drag 行为。在画布外监听 mouseup 事件,放开则终止
onOutOfRange(e: IG6GraphEvent) {
const self = this;
const canvasElement = self.graph.get('canvas').get('el');
function listener(ev) {
if (ev.target !== canvasElement) {
e.item = self.target;
self.onDragEnd(e);
// 终止时需要判断此时是否在监听画布外的 mouseup 事件,若有则解绑
document.body.removeEventListener('mouseup', listener, true);
}
}
if (self.origin) {
body.addEventListener('mouseup', listener, true);
}
},
update(item: Item, e: IG6GraphEvent, force: boolean) {
const { origin } = this;
const model = item.get('model');
const nodeId = item.get('id');
if (!this.point[nodeId]) {
this.point[nodeId] = {
x: model.x,
y: model.y,
};
}
const x = e.x - origin.x + this.point[nodeId].x;
const y = e.y - origin.y + this.point[nodeId].y;
// 拖动单个未选中元素
if (force) {
this.updateDelegate(e, x, y);
return;
}
const pos = { x, y };
if (this.get('updateEdge')) {
this.graph.updateItem(item, pos);
} else {
item.updatePosition(pos);
this.graph.paint();
}
},
/**
* delegate
* @param {Event} e
* @param {number} x x坐标
* @param {number} y y坐标
*/
updateDelegate(e: IG6GraphEvent, x: number, y: number) {
const { graph } = this;
const { item } = e;
const groupType = graph.get('groupType');
const bbox = item.get('keyShape').getBBox();
if (!this.shape) {
const parent = graph.get('delegateGroup');
const attrs = deepMix({}, Global.delegateStyle, this.delegateStyle);
if (this.target) {
this.shape = parent.addShape('rect', {
attrs: {
width: bbox.width,
height: bbox.height,
x: x - bbox.width / 2,
y: y - bbox.height / 2,
...attrs,
},
name: 'delegate-shape',
});
this.target.set('delegateShape', this.shape);
}
this.shape.set('capture', false);
}
if (this.target) {
if (groupType === 'circle') {
this.shape.attr({
x: x - bbox.width / 2,
y: y - bbox.height / 2,
});
} else if (groupType === 'rect') {
this.shape.attr({
x,
y,
});
}
}
},
};

View File

@ -8,9 +8,6 @@ import ClickSelect from './click-select';
import ZoomCanvas from './zoom-canvas'; import ZoomCanvas from './zoom-canvas';
import Tooltip from './tooltip'; import Tooltip from './tooltip';
import EdgeTooltip from './edge-tooltip'; import EdgeTooltip from './edge-tooltip';
import DragGroup from './drag-group';
import DragNodeWidthGroup from './drag-node-with-group';
import CollapseExpandGroup from './collapse-expand-group';
import CollapseExpand from './collapse-expand'; import CollapseExpand from './collapse-expand';
import DragCombo from './drag-combo'; import DragCombo from './drag-combo';
import CollapseExpandCombo from './collapse-expand-combo'; import CollapseExpandCombo from './collapse-expand-combo';
@ -28,9 +25,6 @@ const behaviors = {
'lasso-select': LassoSelect, 'lasso-select': LassoSelect,
tooltip: Tooltip, tooltip: Tooltip,
'edge-tooltip': EdgeTooltip, 'edge-tooltip': EdgeTooltip,
'drag-group': DragGroup,
'drag-node-with-group': DragNodeWidthGroup,
'collapse-expand-group': CollapseExpandGroup,
'collapse-expand': CollapseExpand, 'collapse-expand': CollapseExpand,
'drag-combo': DragCombo, 'drag-combo': DragCombo,
'collapse-expand-combo': CollapseExpandCombo, 'collapse-expand-combo': CollapseExpandCombo,

View File

@ -247,7 +247,7 @@ export default {
const itemStateStyle = node.getStateStyle(fixSelectedItems.fixState); const itemStateStyle = node.getStateStyle(fixSelectedItems.fixState);
const shapeStateStyle = node const shapeStateStyle = node
.get('shapeFactory') .get('shapeFactory')
.getShape(nodeModel.shape || nodeModel.type) .getShape(nodeModel.type)
.getStateStyle(fixSelectedItems.fixState, node)[fixSelectedItems.fixState]; .getStateStyle(fixSelectedItems.fixState, node)[fixSelectedItems.fixState];
if (fixSelectedItems.fixAll) { if (fixSelectedItems.fixAll) {
if (zoom <= 1) { if (zoom <= 1) {
@ -299,7 +299,7 @@ export default {
const itemStateStyle = edge.getStateStyle(fixSelectedItems.fixState); const itemStateStyle = edge.getStateStyle(fixSelectedItems.fixState);
const shapeStateStyle = edge const shapeStateStyle = edge
.get('shapeFactory') .get('shapeFactory')
.getShape(nodeModel.shape || nodeModel.type) .getShape(nodeModel.type)
.getStateStyle(fixSelectedItems.fixState, edge)[fixSelectedItems.fixState]; .getStateStyle(fixSelectedItems.fixState, edge)[fixSelectedItems.fixState];
const childrenLength = children.length; const childrenLength = children.length;

View File

@ -12,7 +12,6 @@ export default {
nodeContainerClassName: 'node-container', nodeContainerClassName: 'node-container',
edgeContainerClassName: 'edge-container', edgeContainerClassName: 'edge-container',
comboContainerClassName: 'combo-container', comboContainerClassName: 'combo-container',
customGroupContainerClassName: 'custom-group-container',
delegateContainerClassName: 'delegate-container', delegateContainerClassName: 'delegate-container',
defaultLoopPosition: 'top', defaultLoopPosition: 'top',
nodeLabel: { nodeLabel: {

File diff suppressed because it is too large Load Diff

View File

@ -4,4 +4,3 @@ export { default as EventController } from './event';
export { default as ItemController } from './item'; export { default as ItemController } from './item';
export { default as LayoutController } from './layout'; export { default as LayoutController } from './layout';
export { default as StateController } from './state'; export { default as StateController } from './state';
export { default as CustomGroup } from './customGroup';

View File

@ -93,10 +93,6 @@ export default class ItemController {
}); });
} }
if (model.shape && !model.type) {
console.warn('shape 字段即将被废弃,请使用 type 代替');
}
graph.emit('beforeadditem', { type, model }); graph.emit('beforeadditem', { type, model });
if (type === EDGE || type === VEDGE) { if (type === EDGE || type === VEDGE) {

View File

@ -12,7 +12,6 @@ import {
GraphOptions, GraphOptions,
EdgeConfig, EdgeConfig,
GraphData, GraphData,
GroupConfig,
Item, Item,
ITEM_TYPE, ITEM_TYPE,
Matrix, Matrix,
@ -29,11 +28,9 @@ import {
HullCfg, HullCfg,
WaterMarkerConfig, WaterMarkerConfig,
} from '../types'; } from '../types';
import { getAllNodeInGroups } from '../util/group';
import { move } from '../util/math'; import { move } from '../util/math';
import Global from '../global'; import Global from '../global';
import { import {
CustomGroup,
EventController, EventController,
ItemController, ItemController,
LayoutController, LayoutController,
@ -70,18 +67,12 @@ export interface PrivateGraphOption extends GraphOptions {
vedges: EdgeConfig[]; vedges: EdgeConfig[];
groups: GroupConfig[];
combos: ComboConfig[]; combos: ComboConfig[];
itemMap: NodeMap; itemMap: NodeMap;
callback: () => void; callback: () => void;
groupBBoxs: IGroupBBox;
groupNodes: NodeMap;
/** /**
* *
* { * {
@ -130,7 +121,6 @@ export default class Graph extends EventEmitter implements IGraph {
const itemController = new ItemController(this); const itemController = new ItemController(this);
const layoutController = new LayoutController(this); const layoutController = new LayoutController(this);
const stateController = new StateController(this); const stateController = new StateController(this);
const customGroupControll = new CustomGroup(this);
this.set({ this.set({
eventController, eventController,
@ -139,7 +129,6 @@ export default class Graph extends EventEmitter implements IGraph {
itemController, itemController,
layoutController, layoutController,
stateController, stateController,
customGroupControll,
}); });
this.initPlugin(); this.initPlugin();
@ -223,15 +212,9 @@ export default class Graph extends EventEmitter implements IGraph {
}); });
// 用于存储自定义的群组 // 用于存储自定义的群组
const customGroup: IGroup = group.addGroup({
id: `${id}-group`,
className: Global.customGroupContainerClassName,
});
customGroup.toBack();
comboGroup.toBack(); comboGroup.toBack();
this.set({ nodeGroup, edgeGroup, customGroup, comboGroup }); this.set({ nodeGroup, edgeGroup, comboGroup });
} }
const delegateGroup: IGroup = group.addGroup({ const delegateGroup: IGroup = group.addGroup({
id: `${id}-delegate`, id: `${id}-delegate`,
@ -391,28 +374,6 @@ export default class Graph extends EventEmitter implements IGraph {
easing: 'easeLinear', easing: 'easeLinear',
}, },
callback: undefined, callback: undefined,
/**
* group类型
*/
groupType: 'circle',
/**
* group bbox
* @private
*/
groupBBoxs: {},
/**
* groupid分组的节点数据
* @private
*/
groupNodes: {},
/**
* group
*/
groups: [],
/**
* group样式
*/
groupStyle: {},
// 默认不启用 undo & redo 功能 // 默认不启用 undo & redo 功能
enabledStack: false, enabledStack: false,
@ -947,14 +908,11 @@ export default class Graph extends EventEmitter implements IGraph {
* @param {boolean} stack true * @param {boolean} stack true
*/ */
public removeItem(item: Item | string, stack: boolean = true): void { public removeItem(item: Item | string, stack: boolean = true): void {
// 如果item是字符串且查询的节点实例不存在则认为是删除group
let nodeItem = item; let nodeItem = item;
if (isString(item)) nodeItem = this.findById(item); if (isString(item)) nodeItem = this.findById(item);
if (!nodeItem && isString(item)) { if (!nodeItem && isString(item)) {
console.warn('The item to be removed does not exist!'); console.warn('The item to be removed does not exist!');
const customGroupControll: CustomGroup = this.get('customGroupControll');
customGroupControll.remove(item);
} else if (nodeItem) { } else if (nodeItem) {
let type = ''; let type = '';
if ((nodeItem as Item).getType) type = (nodeItem as Item).getType(); if ((nodeItem as Item).getType) type = (nodeItem as Item).getType();
@ -1003,8 +961,8 @@ export default class Graph extends EventEmitter implements IGraph {
} }
/** /**
* *
* @param {ITEM_TYPE} type (node | edge | group) * @param {ITEM_TYPE} type (node | edge)
* @param {ModelConfig} model * @param {ModelConfig} model
* @param {boolean} stack true * @param {boolean} stack true
* @param {boolean} sortCombo combo 使 addItem * @param {boolean} sortCombo combo 使 addItem
@ -1014,25 +972,6 @@ export default class Graph extends EventEmitter implements IGraph {
const currentComboSorted = this.get('comboSorted'); const currentComboSorted = this.get('comboSorted');
this.set('comboSorted', currentComboSorted && !sortCombo); this.set('comboSorted', currentComboSorted && !sortCombo);
const itemController: ItemController = this.get('itemController'); const itemController: ItemController = this.get('itemController');
if (type === 'group') {
const { groupId, nodes, type: groupType, zIndex, title } = model;
let groupTitle = title;
if (isString(title)) {
groupTitle = {
text: title,
};
}
return this.get('customGroupControll').create(
groupId,
nodes,
groupType,
zIndex,
true,
groupTitle,
);
}
if (model.id && this.findById(model.id as string)) { if (model.id && this.findById(model.id as string)) {
console.warn(`This item exists already. Be sure the id %c${model.id}%c is unique.`, 'font-size: 20px; color: red;', ''); console.warn(`This item exists already. Be sure the id %c${model.id}%c is unique.`, 'font-size: 20px; color: red;', '');
@ -1164,8 +1103,8 @@ export default class Graph extends EventEmitter implements IGraph {
} }
/** /**
* *
* @param {ITEM_TYPE} type (node | edge | group) * @param {ITEM_TYPE} type (node | edge)
* @param {ModelConfig} model * @param {ModelConfig} model
* @param {boolean} stack true * @param {boolean} stack true
* @return {Item} * @return {Item}
@ -1377,19 +1316,6 @@ export default class Graph extends EventEmitter implements IGraph {
} }
} }
// 防止传入的数据不存在nodes
if (data.nodes) {
// 获取所有有groupID的node
const nodeInGroup = data.nodes.filter((node) => node.groupId);
// 所有node中存在groupID则说明需要群组
if (nodeInGroup.length > 0) {
// 渲染群组
const groupType = self.get('groupType');
this.renderCustomGroup(data, groupType);
}
}
if (this.get('enabledStack')) { if (this.get('enabledStack')) {
this.pushStack('render'); this.pushStack('render');
} }
@ -1888,58 +1814,6 @@ export default class Graph extends EventEmitter implements IGraph {
this.updateCombos(); this.updateCombos();
} }
/**
*
* @param {GraphData} data
* @param {string} groupType group类型
*/
public renderCustomGroup(data: GraphData, groupType: string) {
const { groups, nodes = [] } = data;
// 第一种情况不存在groups则不存在嵌套群组
let groupIndex = 10;
if (!groups) {
// 存在单个群组
// 获取所有有groupID的node
const nodeInGroup = nodes.filter((node) => node.groupId);
const groupsArr: GroupConfig[] = [];
// 根据groupID分组
const groupIds = groupBy(nodeInGroup, 'groupId');
// tslint:disable-next-line:forin
Object.keys(groupIds).forEach((groupId) => {
const nodeIds = groupIds[groupId].map((node) => node.id);
this.get('customGroupControll').create(groupId, nodeIds, groupType, groupIndex);
groupIndex--;
// 获取所有不重复的 groupId
if (!groupsArr.find((d) => d.id === groupId)) {
groupsArr.push({
id: groupId,
});
}
});
this.set({
groups: groupsArr,
});
} else {
// 将groups的数据存到groups中
this.set({ groups });
// 第二种情况存在嵌套的群组数据中有groups字段
const groupNodes = getAllNodeInGroups(data);
// tslint:disable-next-line:forin
Object.keys(groupNodes).forEach((groupId) => {
const tmpNodes = groupNodes[groupId];
this.get('customGroupControll').create(groupId, tmpNodes, groupType, groupIndex);
groupIndex--;
});
// 对所有Group排序
const customGroup = this.get('customGroup');
customGroup.sort();
}
}
/** /**
* *
* @return {object} data * @return {object} data
@ -1960,7 +1834,7 @@ export default class Graph extends EventEmitter implements IGraph {
combos.push(combo.getModel() as ComboConfig); combos.push(combo.getModel() as ComboConfig);
}); });
return { nodes, edges, combos, groups: this.get('groups') }; return { nodes, edges, combos };
} }
/** /**
@ -3004,24 +2878,6 @@ export default class Graph extends EventEmitter implements IGraph {
this.updateCombo(combo); this.updateCombo(combo);
} }
/**
*
* @param {string} groupId ID
*/
public collapseGroup(groupId: string): void {
const customGroupControll: CustomGroup = this.get('customGroupControll');
customGroupControll.collapseGroup(groupId);
}
/**
*
* @param {string} groupId ID
*/
public expandGroup(groupId: string): void {
const customGroupControll: CustomGroup = this.get('customGroupControll');
customGroupControll.expandGroup(groupId);
}
/** /**
* *
* @param {object} plugin * @param {object} plugin
@ -3279,7 +3135,6 @@ export default class Graph extends EventEmitter implements IGraph {
this.get('viewController').destroy(); this.get('viewController').destroy();
this.get('stateController').destroy(); this.get('stateController').destroy();
this.get('layoutController').destroy(); this.get('layoutController').destroy();
this.get('customGroupControll').destroy();
this.get('canvas').destroy(); this.get('canvas').destroy();
if (this.get('graphWaterMarker')) { if (this.get('graphWaterMarker')) {

View File

@ -1,4 +1,3 @@
import { version } from '../package.json';
import Behaviors from './behavior'; import Behaviors from './behavior';
import Graph from './graph/graph'; import Graph from './graph/graph';
import TreeGraph from './graph/tree-graph'; import TreeGraph from './graph/tree-graph';
@ -50,7 +49,7 @@ export {
}; };
export default { export default {
version, version: Global.version,
Graph, Graph,
TreeGraph, TreeGraph,
Util, Util,

View File

@ -195,8 +195,8 @@ export interface IGraph extends EventEmitter {
remove(item: Item | string, stack?: boolean): void; remove(item: Item | string, stack?: boolean): void;
/** /**
* *
* @param {string} type (node | edge | group) * @param {string} type (node | edge)
* @param {ModelConfig} model * @param {ModelConfig} model
* @param {boolean} stack true * @param {boolean} stack true
* @return {Item} * @return {Item}
@ -357,13 +357,6 @@ export interface IGraph extends EventEmitter {
*/ */
clear(): Graph; clear(): Graph;
/**
*
* @param {GraphData} data
* @param {string} groupType group类型
*/
renderCustomGroup(data: GraphData, groupType: string): void;
/** /**
* *
* @Param {GraphData} data * @Param {GraphData} data
@ -545,18 +538,6 @@ export interface IGraph extends EventEmitter {
*/ */
collapseExpandCombo(combo: string | ICombo): void; collapseExpandCombo(combo: string | ICombo): void;
/**
*
* @param {string} groupId ID
*/
collapseGroup(groupId: string): void;
/**
*
* @param {string} groupId ID
*/
expandGroup(groupId: string): void;
/** /**
* bbox combos combos * bbox combos combos
*/ */

View File

@ -106,7 +106,7 @@ export default class Edge extends Item implements IEdge {
const sourcePoint = this.getEndPoint('source'); const sourcePoint = this.getEndPoint('source');
const targetPoint = this.getEndPoint('target'); const targetPoint = this.getEndPoint('target');
const shapeFactory = this.get('shapeFactory'); const shapeFactory = this.get('shapeFactory');
const type = model.shape || model.type; const type = model.type;
return shapeFactory.getControlPoints(type, { return shapeFactory.getControlPoints(type, {
startPoint: sourcePoint, startPoint: sourcePoint,
endPoint: targetPoint, endPoint: targetPoint,

View File

@ -183,7 +183,7 @@ export default class ItemBase implements IItemBase {
} }
self.updatePosition(model); self.updatePosition(model);
const cfg = self.getShapeCfg(model); // 可能会附加额外信息 const cfg = self.getShapeCfg(model); // 可能会附加额外信息
const shapeType = (cfg.shape as string) || (cfg.type as string); const shapeType = (cfg.type as string);
const keyShape: IShapeBase = shapeFactory.draw(shapeType, cfg, group); const keyShape: IShapeBase = shapeFactory.draw(shapeType, cfg, group);
@ -420,7 +420,7 @@ export default class ItemBase implements IItemBase {
if (shapeFactory) { if (shapeFactory) {
const model: ModelConfig = this.get('model'); const model: ModelConfig = this.get('model');
const type = model.shape || model.type; const type = model.type;
// 调用 shape/shape.ts 中的 setState // 调用 shape/shape.ts 中的 setState
shapeFactory.setState(type, state, value, this); shapeFactory.setState(type, state, value, this);
@ -436,7 +436,7 @@ export default class ItemBase implements IItemBase {
const originStates = self.getStates(); const originStates = self.getStates();
const shapeFactory = self.get('shapeFactory'); const shapeFactory = self.get('shapeFactory');
const model: ModelConfig = self.get('model'); const model: ModelConfig = self.get('model');
const shape = model.shape || model.type; const shape = model.type;
if (!states) { if (!states) {
states = originStates; states = originStates;
} }
@ -587,7 +587,7 @@ export default class ItemBase implements IItemBase {
public updateShape() { public updateShape() {
const shapeFactory = this.get('shapeFactory'); const shapeFactory = this.get('shapeFactory');
const model = this.get('model'); const model = this.get('model');
const shape = model.shape || model.type; const shape = model.type;
// 判定是否允许更新 // 判定是否允许更新
// 1. 注册的节点允许更新 // 1. 注册的节点允许更新
// 2. 更新后的 shape 等于原先的 shape // 2. 更新后的 shape 等于原先的 shape

View File

@ -176,7 +176,7 @@ export default class Node extends Item implements INode {
const bbox = this.getBBox(); const bbox = this.getBBox();
const model: NodeConfig = this.get('model'); const model: NodeConfig = this.get('model');
const shapeCfg = this.getShapeCfg(model); const shapeCfg = this.getShapeCfg(model);
const type = model.shape || model.type; const type = model.type;
const points = shapeFactory.getAnchorPoints(type, shapeCfg) || []; const points = shapeFactory.getAnchorPoints(type, shapeCfg) || [];
each(points, (pointArr, index) => { each(points, (pointArr, index) => {

View File

@ -113,7 +113,7 @@ export default class DagreLayout extends BaseLayout {
g.edges().forEach((edge: any) => { g.edges().forEach((edge: any) => {
coord = g.edge(edge); coord = g.edge(edge);
const i = edges.findIndex((it) => it.source === edge.v && it.target === edge.w); const i = edges.findIndex((it) => it.source === edge.v && it.target === edge.w);
if (self.controlPoints && edges[i].type !== 'loop' && edges[i].shape !== 'loop') { if (self.controlPoints && edges[i].type !== 'loop') {
edges[i].controlPoints = coord.points.slice(1, coord.points.length - 1); edges[i].controlPoints = coord.points.slice(1, coord.points.length - 1);
} }
}); });

View File

@ -160,7 +160,6 @@ export default class Bundling extends Base {
// change the edges according to edgePoints // change the edges according to edgePoints
edges.forEach((e, i) => { edges.forEach((e, i) => {
if (e.source === e.target) return; if (e.source === e.target) return;
e.shape = 'polyline';
e.type = 'polyline'; e.type = 'polyline';
e.controlPoints = edgePoints[i].slice(1, edgePoints[i].length - 1); e.controlPoints = edgePoints[i].slice(1, edgePoints[i].length - 1);
}); });

View File

@ -6,7 +6,6 @@ import Graph from '../../graph/graph';
import { IG6GraphEvent, Item } from '../../types'; import { IG6GraphEvent, Item } from '../../types';
import Base, { IPluginBaseConfig } from '../base'; import Base, { IPluginBaseConfig } from '../base';
import { IGraph } from '../../interface/graph'; import { IGraph } from '../../interface/graph';
import edgeTooltip from '../../behavior/edge-tooltip';
insertCss(` insertCss(`
.g6-component-tooltip { .g6-component-tooltip {

View File

@ -366,8 +366,6 @@ export type Easeing =
| string; | string;
export interface ModelConfig extends ModelStyle { export interface ModelConfig extends ModelStyle {
// ⚠️ 节点或边的类型,后续会废弃
shape?: string;
// 节点或边的类型 // 节点或边的类型
type?: string; type?: string;
label?: string | LabelStyle; label?: string | LabelStyle;
@ -533,17 +531,9 @@ export interface NodeConfigMap {
[key: string]: NodeConfig; [key: string]: NodeConfig;
} }
export interface GroupConfig {
id: string;
parentId?: string;
[key: string]: string | ModelStyle | undefined;
}
export interface GraphData { export interface GraphData {
nodes?: NodeConfig[]; nodes?: NodeConfig[];
edges?: EdgeConfig[]; edges?: EdgeConfig[];
groups?: GroupConfig[];
combos?: ComboConfig[]; combos?: ComboConfig[];
} }
@ -554,8 +544,6 @@ export interface GraphAnimateConfig extends AnimateCfg {
onFrame?: (item: Item, ratio: number, data?: GraphData, originAttrs?: ShapeStyle) => unknown; onFrame?: (item: Item, ratio: number, data?: GraphData, originAttrs?: ShapeStyle) => unknown;
} }
// export type ModelConfig = NodeConfig | EdgeConfig | GroupConfig
export interface GroupNodeIds { export interface GroupNodeIds {
[key: string]: string[]; [key: string]: string[];
} }
@ -724,7 +712,7 @@ export interface BehaviorOption {
export type IEvent = Record<G6Event, string>; export type IEvent = Record<G6Event, string>;
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
export type ITEM_TYPE = 'node' | 'edge' | 'combo' | 'group' | 'vedge'; export type ITEM_TYPE = 'node' | 'edge' | 'combo' | 'vedge';
export type NodeIdxMap = { export type NodeIdxMap = {
[key: string]: number; [key: string]: number;

View File

@ -1,74 +0,0 @@
import groupBy, { ObjectType } from '@antv/util/lib/group-by';
import { GraphData, GroupConfig, GroupNodeIds } from '../types';
export const getAllNodeInGroups = (data: GraphData): GroupNodeIds => {
const groupById: ObjectType<GroupConfig> = groupBy(data.groups!, 'id');
const groupByParentId: ObjectType<GroupConfig> = groupBy(data.groups!, 'parentId');
const result: { [key: string]: GroupConfig[] } = {};
for (const parentId in groupByParentId) {
if (!parentId) {
continue;
}
// 获取当前parentId的所有子group ID
const subGroupIds = groupByParentId[parentId];
// 获取在parentid群组中的节点
const nodeInParentGroup = groupById[parentId];
if (nodeInParentGroup && subGroupIds) {
// 合并
const parentGroupNodes = [...subGroupIds, ...nodeInParentGroup];
result[parentId] = parentGroupNodes;
} else if (subGroupIds) {
result[parentId] = subGroupIds;
}
}
const allGroupsId = { ...groupById, ...result };
// 缓存所有group包括的groupID
const groupIds: { [key: string]: string[] } = {};
for (const groupId in allGroupsId) {
if (!groupId || groupId === 'undefined') {
continue;
}
const subGroupIds = allGroupsId[groupId].map((node) => node.id);
// const nodesInGroup = data.nodes.filter(node => node.groupId === groupId).map(node => node.id);
groupIds[groupId] = subGroupIds;
}
// 缓存所有groupID对应的Node
const groupNodes: GroupNodeIds = {} as GroupNodeIds;
for (const groupId in groupIds) {
if (!groupId || groupId === 'undefined') {
continue;
}
const subGroupIds = groupIds[groupId];
// const subGroupIds = allGroupsId[groupId].map(node => node.id);
// 解析所有子群组
const parentSubGroupIds: string[] = [];
for (const subId of subGroupIds) {
const tmpGroupId = allGroupsId[subId].map((node) => node.id);
// const tmpNodes = data.nodes.filter(node => node.groupId === subId).map(node => node.id);
parentSubGroupIds.push(...tmpGroupId);
}
const nodesInGroup = data.nodes
? data.nodes
.filter(
(node) =>
parentSubGroupIds.indexOf(node.groupId!) > -1 ||
parentSubGroupIds.indexOf(node.parentId as string) > -1,
)
.map((node) => node.id)
: [];
groupNodes[groupId] = nodesInGroup;
}
return groupNodes;
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -86,9 +86,8 @@ describe('graph', () => {
expect(inst.get('group').get('id').endsWith('-root')).toBe(true); expect(inst.get('group').get('id').endsWith('-root')).toBe(true);
const children = inst.get('group').get('children'); const children = inst.get('group').get('children');
expect(children.length).toBe(5); expect(children.length).toBe(4);
expect(children[2].get('className')).toEqual('edge-container'); expect(children[1].get('className')).toEqual('edge-container');
expect(children[1].get('className')).toEqual('custom-group-container');
const nodes = inst.getNodes(); const nodes = inst.getNodes();
expect(nodes).not.toBe(undefined); expect(nodes).not.toBe(undefined);
@ -1257,60 +1256,6 @@ describe('plugins & layout', () => {
graph.destroy(); graph.destroy();
expect(graph.destroyed).toBe(true); expect(graph.destroyed).toBe(true);
}); });
it('graph animate', () => {
const graph = new Graph({
container: div,
height: 500,
width: 500,
});
const data = {
nodes: [
{
id: 'node',
label: 'node',
groupId: 'g1',
},
{
id: 'node1',
groupId: 'g2',
},
],
groups: [
{
id: 'g1',
title: 'cokkdl',
},
{
id: 'g2',
},
],
};
graph.data(data);
graph.render();
graph.stopAnimate();
const isAnimating = graph.isAnimating();
expect(isAnimating).toBe(false);
graph.collapseGroup('g1');
let gnode = graph.findById('node');
expect(gnode.get('visible')).toBe(false);
const g2Node = graph.findById('node1');
expect(g2Node.get('visible')).toBe(true);
graph.expandGroup('g1');
timerOut(() => {
gnode = graph.findById('node');
expect(gnode.get('visible')).toBe(true);
}, 500);
});
}); });
describe('auto rotate label on edge', () => { describe('auto rotate label on edge', () => {

View File

@ -92,10 +92,9 @@ describe('graph', () => {
expect(inst.get('group').get('id').endsWith('-root')).toBe(true); expect(inst.get('group').get('id').endsWith('-root')).toBe(true);
const children = inst.get('group').get('children'); const children = inst.get('group').get('children');
expect(children.length).toBe(5); expect(children.length).toBe(4);
expect(children[2].get('className')).toEqual('edge-container'); expect(children[1].get('className')).toEqual('edge-container');
expect(children[1].get('className')).toEqual('custom-group-container');
const nodes = inst.getNodes(); const nodes = inst.getNodes();
expect(nodes).not.toBe(undefined); expect(nodes).not.toBe(undefined);
expect(nodes.length).toBe(0); expect(nodes.length).toBe(0);
@ -1007,66 +1006,6 @@ describe('plugins & layout', () => {
graph.destroy(); graph.destroy();
expect(graph.destroyed).toBe(true); expect(graph.destroyed).toBe(true);
}); });
it('graph animate', () => {
const graph = new Graph({
container: div,
height: 500,
width: 500,
renderer: 'svg',
});
const data = {
nodes: [
{
id: 'node',
label: 'node',
groupId: 'g1',
x: 100,
y: 100,
},
{
id: 'node1',
groupId: 'g2',
x: 50,
y: 150,
},
],
groups: [
{
id: 'g1',
title: 'cokkdl',
},
{
id: 'g2',
},
],
};
graph.data(data);
graph.render();
graph.stopAnimate();
const isAnimating = graph.isAnimating();
expect(isAnimating).toBe(false);
graph.collapseGroup('g1');
let gnode = graph.findById('node');
expect(gnode.get('visible')).toBe(false);
const g2Node = graph.findById('node1');
expect(g2Node.get('visible')).toBe(true);
graph.expandGroup('g1');
timerOut(() => {
gnode = graph.findById('node');
expect(gnode.get('visible')).toBe(true);
graph.destroy();
}, 500);
});
}); });
describe('auto rotate label on edge', () => { describe('auto rotate label on edge', () => {
@ -2027,11 +1966,10 @@ describe('plugins', () => {
graph.render(); graph.render();
setTimeout(() => { setTimeout(() => {
const minimapGroup = minimap.get('canvas').get('children')[0]; const minimapGroup = minimap.get('canvas').get('children')[0];
expect(minimapGroup.get('children').length).toBe(5); expect(minimapGroup.get('children').length).toBe(4);
graph.zoom(2, { x: 250, y: 250 }); graph.zoom(2, { x: 250, y: 250 });
expect(minimapGroup.get('children')[2].get('children').length).toBe(5); expect(minimapGroup.get('children')[2].get('children').length).toBe(5);
expect(minimapGroup.get('children')[3].get('children').length).toBe(5);
const viewport = minimap.get('viewport'); const viewport = minimap.get('viewport');
expect(viewport.style.width).toBe('37.2093px'); expect(viewport.style.width).toBe('37.2093px');
expect(viewport.style.height).toBe('6.1794px'); expect(viewport.style.height).toBe('6.1794px');
@ -2583,174 +2521,3 @@ describe('plugins', () => {
expect(parentDom).toBe(null); expect(parentDom).toBe(null);
}); });
}); });
describe('custom group', () => {
const data = {
nodes: [
{
id: 'node1',
label: 'node1',
groupId: 'group1',
x: 100,
y: 100,
},
{
id: 'node2',
label: 'node2',
groupId: 'group1',
x: 150,
y: 100,
},
{
id: 'node3',
label: 'node3',
groupId: 'group2',
x: 300,
y: 100,
},
{
id: 'node7',
groupId: 'p1',
x: 200,
y: 200,
},
{
id: 'node6',
groupId: 'bym',
label: 'rect',
x: 100,
y: 300,
type: 'rect',
},
{
id: 'node9',
label: 'noGroup',
x: 300,
y: 210,
},
],
};
const graph = new Graph({
container: div,
width: 500,
height: 500,
renderer: 'svg',
modes: {
default: [
{
type: 'collapse-expand-group',
trigger: 'click',
},
'drag-node-with-group',
'drag-group',
],
},
});
it('render', () => {
graph.data(data);
graph.render();
});
it('collapse-expand-group', () => {
const nodeGroup1 = graph.get('group').get('children')[1].get('children')[0].get('children')[0];
const hideNode1 = graph.getNodes()[0];
const hideNode2 = graph.getNodes()[1];
graph.emit('click', {
target: nodeGroup1,
});
graph.once('click', () => {
expect(hideNode1.isVisible()).toBe(false);
expect(hideNode2.isVisible()).toBe(false);
});
setTimeout(() => {
graph.emit('click', {
target: nodeGroup1,
});
}, 500);
});
it('drag-group', () => {
const nodeGroup1 = graph.get('group').get('children')[1].get('children')[0].get('children')[0];
const node1 = graph.getNodes()[0];
const node2 = graph.getNodes()[1];
const node1OriX = node1.getModel().x;
const node1OriY = node1.getModel().y;
const node2OriX = node2.getModel().x;
const node2OriY = node2.getModel().y;
graph.emit('dragstart', {
target: nodeGroup1,
x: 50,
y: 50,
});
graph.emit('drag', {
target: nodeGroup1,
x: 250,
y: 150,
});
graph.emit('drag', {
target: nodeGroup1,
x: 250,
y: 150,
});
const delegateGroup = graph.get('delegateGroup');
expect(delegateGroup.get('children').length).toBe(1);
expect(delegateGroup.get('children')[0].attr('x')).toBe(325);
expect(delegateGroup.get('children')[0].attr('y')).toBe(200);
expect(node1.getModel().x).toBe(node1OriX);
expect(node1.getModel().y).toBe(node1OriY);
expect(node2.getModel().x).toBe(node2OriX);
expect(node2.getModel().y).toBe(node2OriY);
graph.emit('dragend', {
target: nodeGroup1,
x: 150,
y: 150,
});
expect(delegateGroup.get('children').length).toBe(0);
expect(node1.getModel().x).not.toBe(node1OriX);
expect(node1.getModel().y).not.toBe(node1OriY);
expect(node2.getModel().x).not.toBe(node2OriX);
expect(node2.getModel().y).not.toBe(node2OriY);
});
it('drag-node-with-group', () => {
const node3 = graph.getNodes()[2];
const node3OriX = node3.getModel().x;
const node3OriY = node3.getModel().y;
graph.emit('node:dragstart', {
target: node3,
item: node3,
x: 50,
y: 50,
});
graph.emit('node:drag', {
target: node3,
item: node3,
x: 350,
y: 150,
});
graph.emit('node:drag', {
target: node3,
item: node3,
x: 350,
y: 150,
});
const delegateGroup = graph.get('delegateGroup');
expect(delegateGroup.get('children').length).toBe(1);
expect(node3.getModel().x).toBe(node3OriX);
expect(node3.getModel().y).toBe(node3OriY);
graph.emit('node:dragend', {
target: node3,
item: node3,
x: 350,
y: 150,
});
expect(delegateGroup.get('children').length).toBe(0);
expect(node3.getModel().x).not.toBe(node3OriX);
expect(node3.getModel().y).not.toBe(node3OriY);
graph.destroy();
});
});

View File

@ -114,7 +114,7 @@ describe('graph refactor states', () => {
], ],
}; };
it('compatible true/false states', () => { it.only('compatible true/false states', () => {
const graph = new G6.Graph({ const graph = new G6.Graph({
container: div, container: div,
width: 500, width: 500,
@ -145,9 +145,8 @@ describe('graph refactor states', () => {
graph.setItemState(item, 'hover', true); graph.setItemState(item, 'hover', true);
graph.setItemState(item, 'select', true); graph.setItemState(item, 'select', true);
}); });
graph.on('node:click', (e) => { graph.on('node:mouseleave', (e) => {
const item = e.item; const item = e.item;
graph.setItemState(item, 'hover', false); graph.setItemState(item, 'hover', false);
graph.setItemState(item, 'select', false); graph.setItemState(item, 'select', false);
}); });
@ -180,7 +179,7 @@ describe('graph refactor states', () => {
expect(item.hasState('hover')).toBe(false); expect(item.hasState('hover')).toBe(false);
expect(item.getStates()).toEqual([]); expect(item.getStates()).toEqual([]);
expect(keyShape.attr('opacity')).toEqual(0.8); expect(keyShape.attr('opacity')).toEqual(0.8);
graph.destroy(); // graph.destroy();
}); });
it('multivalued & muted', () => { it('multivalued & muted', () => {

View File

@ -1,67 +0,0 @@
import { getAllNodeInGroups } from '../../../src/util/group';
describe('group util test', () => {
it('getAllNodeInGroups', () => {
const nodes = [
{
id: 'node1',
label: 'node1',
groupId: 'group1',
},
{
id: 'node2',
label: 'node2',
groupId: 'group1',
},
{
id: 'node3',
label: 'node3',
groupId: 'group2',
},
{
id: 'node6',
groupId: 'p1',
},
{
id: 'node6',
groupId: 'bym',
},
];
const data = {
nodes,
groups: [
{
id: 'group1',
title: 'group1',
parentId: 'p1',
},
{
id: 'group2',
title: 'group2',
parentId: 'p1',
},
{
id: 'bym',
},
{
id: 'p1',
},
],
};
const nodesInGroup = getAllNodeInGroups(data);
expect(Object.keys(nodesInGroup).length).toBe(4);
const bym = nodesInGroup.bym;
expect(bym.length).toBe(1);
expect(bym[0]).toBe('node6');
const group1 = nodesInGroup.group1;
expect(Object.keys(group1).length).toBe(2);
expect(group1[0]).toBe('node1');
expect(group1[1]).toBe('node2');
const p1 = nodesInGroup.p1;
expect(Object.keys(p1).length).toBe(4);
});
});