Skip to content

Commit 5c15852

Browse files
authored
🚧 Prepare v2 (#19)
* 🚧 Prepare v2 * 🎨 Fix tslint * 📦 2.0.0 version
1 parent 3400d69 commit 5c15852

25 files changed

+2547
-188
lines changed

README.md

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Treeviz
22

3+
[![Known Vulnerabilities](https://snyk.io/test/github/dwyl/hapi-auth-jwt2/badge.svg?targetFile=package.json)](https://snyk.io/test/github/dwyl/hapi-auth-jwt2?targetFile=package.json)
4+
![David](https://img.shields.io/david/PierreCapo/treeviz)
5+
[![license](https://badgen.now.sh/badge/license/BSD)](./LICENSE)
6+
37
This javascript module aims at providing an easy interface in order to represent tree diagrams on screen with the ability to handle dynamic data flows. The data format must be JSON.
48

59
![](https://i.imgur.com/vyB2Erg.gif)
@@ -71,16 +75,15 @@ The table below lists all the avalaible key that the config object can have
7175
| `htmlID` | string (Required) | | The HTML id tag on the page where the tree should be drawn. It must have a width and an height specified |
7276
| `nodeField` | string | "id" | The unique identifier field in the dataset representing the node |
7377
| `relationnalField` | string | "father" | In case of flat dataset, usually the relationnal field between each node is the field representing the father of the node, linking it to the id of the field. (See example below). |
74-
| `flatData` | boolean | true | Specify whether the data passed to the tree is flat or already hierarchical |
75-
| `zoomBehavior` | boolean | true | Toggle the ability to pan and zoom the tree |
78+
| `hasFlatData` | boolean | true | Specify whether the data passed to the tree is flat or already hierarchical |
79+
| `hasPanAndZoom` | boolean | true | Toggle the ability to pan and zoom the tree |
7680
| `nodeWidth` | number | 160 | Width of a node in px |
7781
| `nodeHeight` | number | 100 | Height of a node in px |
78-
| `nodeColor` | function | (nodeData) => "#2196f3" | Color of the node |
7982
| `linkColor` | function | (nodeData) => "#ffcc80" | Color of the link |
8083
| `linkWidth` | function | (nodeData) => 10 | Width of the link |
8184
| `linkShape` | "quadraticBeziers" \| "orthogonal" | "quadraticBeziers" | Shape of the link |
82-
| `nodeTemplate` | function | (nodeData) => null | HTML template for every node |
83-
| `horizontalLayout` | boolean | true | Direction of the tree. If true, the tree expands from left to right. If false, it goes from top to bottom |
85+
| `renderNode` | function | (nodeData) => null | HTML template for every node |
86+
| `isHorizontal` | boolean | true | Direction of the tree. If true, the tree expands from left to right. If false, it goes from top to bottom |
8487
| `onNodeClick` | function | (nodeData) => null | Function handling the event when someone click on it |
8588
| `onNodeMouseEnter` | function | (nodeData) => null | Function handling the event when someone hover a node |
8689
| `onNodeMouseLeave` | function | (nodeData) => null | Function handling the event when the mouse pointer leaves a node |
@@ -93,7 +96,7 @@ The table below lists all the avalaible key that the config object can have
9396

9497
## Example
9598

96-
Assuming that you already have an HTML element on the pagh with the tag `id="a_tree"`
99+
Assuming that you already have an HTML element on the page with the tag `id="a_tree"`
97100

98101
#### Flat data case :
99102

demo/demo.html

+21-13
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
{ id: 1, text_1: "Chaos", text_2: "Void", father: null, color:"#FF5722" },
1919
{ id: 2, text_1: "Tartarus", text_2: "Abyss", father: 1, color:"#FFC107" },
2020
{ id: 3, text_1: "Gaia", text_2: "Earth", father: 1, color:"#8BC34A" },
21-
{ id: 4, text_1: "Eros", text_2: "Desire", father: 1, color:"#00BCD4" }
22-
];
21+
{ id: 4, text_1: "Eros", text_2: "Desire", father: 1, color:"#00BCD4" }];
2322

2423
var data_2 = [
2524
{ id: 1, text_1: "Chaos", text_2: " Void", father: null, color:"#2196F3" },
@@ -37,21 +36,30 @@
3736
{ id: 5, text_1: "Uranus", text_2: "Sky", father: 3,color:"#4CAF50" },
3837
{ id: 6, text_1: "Ourea", text_2: "Mountains", father: 3,color:"#FF9800" },
3938
{ id: 7, text_1: "Hermes", text_2: " Sky", father: 4,color:"#2196F3" },
40-
{ id: 8, text_1: "Aphrodite", text_2: "Love", father: 4,color:"#8BC34A" }
39+
{ id: 8, text_1: "Aphrodite", text_2: "Love", father: 4,color:"#8BC34A" },
40+
{ id: 3.3, text_1: "Love", text_2: "Peace", father: 8,color:"#c72e99" },
41+
{ id: 4.1, text_1: "Hope", text_2: "Life", father: 8,color:"#2eecc7" }
4142
];
4243

4344
var myTree = Treeviz.create({
44-
htmlID: "tree",
45-
nodeField: "id",
46-
flatData: true,
45+
htmlId: "tree",
46+
idKey: "id",
47+
hasFlatData: true,
4748
relationnalField: "father",
48-
zoomBehavior: true,
49-
nodeWidth:160,
50-
nodeHeight:100,
51-
nodeColor: (nodeData) => nodeData.color ,
52-
depthDistance:300,
53-
horizontalLayout:true,
54-
nodeTemplate: (nodeData) => "<div style='height:100px; width:160px;display:flex;flex-direction:column;justify-content:center;align-items:center;'><div><strong>"+nodeData.text_1+"</strong></div><div>is</div><div><i>"+nodeData.text_2+"</i></div></div>",
49+
hasPanAndZoom: true,
50+
nodeWidth:120,
51+
nodeHeight:80,
52+
mainAxisNodeSpacing:2,
53+
isHorizontal:true,
54+
renderNode: function(node) {
55+
return result = "<div class='box' style='cursor:pointer;height:"+node.settings.nodeHeight+"px; width:"+node.settings.nodeWidth+"px;display:flex;flex-direction:column;justify-content:center;align-items:center;background-color:"
56+
+node.data.color+
57+
";border-radius:5px;'><div><strong>"
58+
+node.data.text_1+
59+
"</strong></div><div>is</div><div><i>"
60+
+node.data.text_2+
61+
"</i></div></div>";
62+
},
5563
linkWidth : (nodeData)=> nodeData.id*5,
5664
linkColor : (nodeData) => "#B0BEC5" ,
5765
onNodeClick : (nodeData) => console.log(nodeData)

dist/index.js

+2,272-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/typescript/index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ITreeConfig } from "./typings";
22
export declare function create(userSettings: Partial<ITreeConfig>): {
3-
refresh: (data: any) => void;
3+
refresh: (data: any, newSettings?: Partial<ITreeConfig> | undefined) => void;
44
clean: (keepConfig: boolean) => void;
55
};

dist/typescript/links/link-enter.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import { HierarchyPointNode } from "d3-hierarchy";
22
import { BaseType, Selection } from "d3-selection";
3-
import { ITreeConfig } from "../typings";
4-
export declare const drawLinkEnter: (link: Selection<BaseType, HierarchyPointNode<{}>, SVGGElement, {}>, computedTree: HierarchyPointNode<{}>, settings: ITreeConfig) => Selection<SVGPathElement, HierarchyPointNode<{}>, SVGGElement, {}>;
3+
import { ITreeConfig, ExtendedHierarchyPointNode } from "../typings";
4+
export declare const drawLinkEnter: (link: Selection<BaseType, HierarchyPointNode<{}>, SVGGElement, {}>, settings: ITreeConfig, nodes: ExtendedHierarchyPointNode[], oldNodes: ExtendedHierarchyPointNode[]) => Selection<SVGPathElement, HierarchyPointNode<{}>, SVGGElement, {}>;

dist/typescript/links/link-exit.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import { HierarchyPointNode } from "d3-hierarchy";
22
import { BaseType, Selection } from "d3-selection";
3-
import { ITreeConfig } from "../typings";
4-
export declare const drawLinkExit: (link: Selection<BaseType, HierarchyPointNode<{}>, SVGGElement, {}>, settings: ITreeConfig) => void;
3+
import { ITreeConfig, ExtendedHierarchyPointNode } from "../typings";
4+
export declare const drawLinkExit: (link: Selection<BaseType, HierarchyPointNode<{}>, SVGGElement, {}>, settings: ITreeConfig, nodes: ExtendedHierarchyPointNode[], oldNodes: ExtendedHierarchyPointNode[]) => void;

dist/typescript/nodes/node-enter.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import { BaseType, Selection } from "d3-selection";
22
import { ExtendedHierarchyPointNode, ITreeConfig } from "../typings";
3-
export declare const drawNodeEnter: (node: Selection<BaseType, ExtendedHierarchyPointNode, SVGGElement, {}>, settings: ITreeConfig) => Selection<SVGGElement, ExtendedHierarchyPointNode, SVGGElement, {}>;
3+
export declare const drawNodeEnter: (node: Selection<BaseType, ExtendedHierarchyPointNode, SVGGElement, {}>, settings: ITreeConfig, nodes: ExtendedHierarchyPointNode[], oldNodes: ExtendedHierarchyPointNode[]) => Selection<SVGGElement, ExtendedHierarchyPointNode, SVGGElement, {}>;

dist/typescript/nodes/node-exit.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import { BaseType, Selection } from "d3-selection";
22
import { ExtendedHierarchyPointNode, ITreeConfig } from "../typings";
3-
export declare const drawNodeExit: (node: Selection<BaseType, ExtendedHierarchyPointNode, SVGGElement, {}>, settings: ITreeConfig) => void;
3+
export declare const drawNodeExit: (node: Selection<BaseType, ExtendedHierarchyPointNode, SVGGElement, {}>, settings: ITreeConfig, nodes: ExtendedHierarchyPointNode[], oldNodes: ExtendedHierarchyPointNode[]) => void;

dist/typescript/typings.d.ts

+8-9
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,25 @@ export interface ITreeConfig {
33
htmlID: string;
44
nodeField: string;
55
relationnalField: string;
6-
flatData: boolean;
6+
hasFlatData: boolean;
77
nodeWidth: number;
88
nodeHeight: number;
9-
nodeDepthDistance: number | "auto";
10-
nodeTemplate: (node: any) => string | null;
11-
nodeColor: (node: any) => string | null | number | boolean;
9+
mainAxisNodeSpacing: number | "auto";
10+
renderNode: (node: any) => string | null;
1211
linkShape: string;
13-
linkColor: (node: any) => string | null | boolean;
14-
linkWidth: (node: any) => string | number | null | boolean;
12+
linkColor: (node: any) => string;
13+
linkWidth: (node: any) => number;
1514
onNodeClick: (node: any) => void;
1615
onNodeMouseEnter: (node: any) => void;
1716
onNodeMouseLeave: (node: any) => void;
18-
horizontalLayout: boolean;
19-
zoomBehavior: boolean;
17+
isHorizontal: boolean;
18+
hasPanAndZoom: boolean;
2019
duration: number;
2120
marginTop: number;
2221
marginBottom: number;
2322
marginLeft: number;
2423
marginRight: number;
25-
nodeSpacerPercentage: number;
24+
secondaryAxisNodeSpacing: number;
2625
}
2726
export interface ExtendedHierarchyPointNode extends HierarchyPointNode<{}> {
2827
x0?: number;

dist/typescript/utils.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1+
import { ExtendedHierarchyPointNode, ITreeConfig } from "./typings";
12
export declare const getAreaSize: (htmlID: string) => {
23
areaWidth: number;
34
areaHeight: number;
45
};
6+
declare type Result = ExtendedHierarchyPointNode & {
7+
x0: number;
8+
y0: number;
9+
};
10+
export declare const getFirstDisplayedAncestor: (ghostNodes: ExtendedHierarchyPointNode[], viewableNodes: ExtendedHierarchyPointNode[], id: string) => Result;
11+
export declare const setNodeLocation: (xPosition: number, yPosition: number, settings: ITreeConfig) => string;
12+
export {};

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "treeviz",
3-
"version": "1.3.3",
3+
"version": "2.0.0",
44
"description": "Library which aims to represent trees for data visualization",
55
"keywords": [
66
"d3",
@@ -20,6 +20,7 @@
2020
"types": "./dist/typescript/index.d.ts",
2121
"scripts": {
2222
"build": "webpack --mode=production",
23+
"watch": "webpack --mode=development --watch",
2324
"dev": "webpack-dev-server --mode=development",
2425
"test": "jest",
2526
"lint": "tslint src/*.ts"
@@ -29,7 +30,7 @@
2930
"@babel/preset-env": "^7.5.5",
3031
"@babel/preset-typescript": "^7.3.3",
3132
"@types/node": "^12.6.8",
32-
"@types/webpack": "^4.32.0",
33+
"@types/webpack": "^4.32.1",
3334
"@types/webpack-dev-server": "^3.1.7",
3435
"babel-loader": "^8.0.6",
3536
"html-webpack-plugin": "^3.2.0",
@@ -38,7 +39,7 @@
3839
"tslint": "^5.18.0",
3940
"tslint-config-prettier": "^1.18.0",
4041
"typescript": "^3.5.3",
41-
"webpack": "^4.36.1",
42+
"webpack": "^4.38.0",
4243
"webpack-cli": "^3.3.6",
4344
"webpack-dev-server": "^3.7.2"
4445
},

src/index.ts

+42-42
Original file line numberDiff line numberDiff line change
@@ -12,107 +12,107 @@ import { ExtendedHierarchyPointNode, ITreeConfig } from "./typings";
1212

1313
export function create(userSettings: Partial<ITreeConfig>) {
1414
const defaultSettings: ITreeConfig = {
15-
htmlID: "",
16-
nodeField: "",
17-
relationnalField: "",
18-
flatData: true,
15+
htmlId: "",
16+
idKey: "",
17+
relationnalField: "father",
18+
hasFlatData: true,
1919
nodeWidth: 160,
2020
nodeHeight: 100,
21-
nodeDepthDistance: 300,
22-
nodeColor: () => "#2196f3",
23-
nodeTemplate: () => "Node",
21+
mainAxisNodeSpacing: 300,
22+
renderNode: () => "Node",
2423
linkColor: () => "#ffcc80",
2524
linkWidth: () => 10,
2625
linkShape: "quadraticBeziers",
27-
horizontalLayout: true,
28-
zoomBehavior: false,
29-
duration: 400,
26+
isHorizontal: true,
27+
hasPanAndZoom: false,
28+
duration: 600,
3029
onNodeClick: () => undefined,
3130
onNodeMouseEnter: () => undefined,
3231
onNodeMouseLeave: () => undefined,
3332
marginBottom: 0,
3433
marginLeft: 0,
3534
marginRight: 0,
3635
marginTop: 0,
37-
nodeSpacerPercentage: 1.25,
36+
secondaryAxisNodeSpacing: 1.25,
3837
};
39-
const settings: ITreeConfig = { ...defaultSettings, ...userSettings };
40-
let oldPosition: Array<{ x0?: number; y0?: number; id?: string }> = [];
38+
let settings: ITreeConfig = {
39+
...defaultSettings,
40+
...userSettings,
41+
};
42+
let oldNodes: ExtendedHierarchyPointNode[] = [];
4143

4244
function draw(
4345
svg: Selection<SVGGElement, {}, HTMLElement, any>,
4446
computedTree: HierarchyPointNode<{}>
4547
) {
4648
const nodes = computedTree.descendants() as ExtendedHierarchyPointNode[];
49+
4750
const links = computedTree.descendants().slice(1);
4851

49-
const { nodeDepthDistance } = settings;
50-
if (nodeDepthDistance !== "auto") {
52+
const { mainAxisNodeSpacing: mainAxisNodeSpacing } = settings;
53+
if (mainAxisNodeSpacing !== "auto") {
5154
// Normalize for fixed-depth.
5255
nodes.forEach((d: any) => {
53-
d.y = d.depth * nodeDepthDistance;
56+
d.y = d.depth * settings.nodeWidth * mainAxisNodeSpacing;
5457
});
5558
}
5659

5760
nodes.forEach((currentNode: ExtendedHierarchyPointNode) => {
58-
currentNode.x0 = {
59-
...currentNode,
60-
x0: undefined,
61-
y0: undefined,
62-
// @ts-ignore
63-
...oldPosition.filter(oldNode => oldNode.id === currentNode.id)[0],
64-
}.x0;
65-
currentNode.y0 = {
66-
...currentNode,
67-
x0: undefined,
68-
y0: undefined,
69-
// @ts-ignore
70-
...oldPosition.filter(oldNode => oldNode.id === currentNode.id)[0],
71-
}.y0;
61+
const currentNodeOldPosition = oldNodes.find(
62+
node => node.id === currentNode.id
63+
);
64+
currentNode.x0 = currentNodeOldPosition
65+
? currentNodeOldPosition.x0
66+
: currentNode.x;
67+
currentNode.y0 = currentNodeOldPosition
68+
? currentNodeOldPosition.y0
69+
: currentNode.y;
7270
});
7371

7472
// ****************** Nodes section ***************************
7573
const node = svg.selectAll("g.node").data(nodes, (d: any) => {
76-
return d[settings.nodeField];
74+
return d[settings.idKey];
7775
});
7876

79-
const nodeEnter = drawNodeEnter(node, settings);
77+
const nodeEnter = drawNodeEnter(node, settings, nodes, oldNodes);
8078
drawNodeUpdate(nodeEnter, node, settings);
81-
drawNodeExit(node, settings);
79+
drawNodeExit(node, settings, nodes, oldNodes);
8280

8381
// ****************** links section ***************************
8482

8583
const link = svg.selectAll("path.link").data(links, (d: any) => {
8684
return d.id;
8785
});
8886

89-
const linkEnter = drawLinkEnter(link, computedTree, settings);
87+
const linkEnter = drawLinkEnter(link, settings, nodes, oldNodes);
9088
drawLinkUpdate(linkEnter, link, settings);
91-
drawLinkExit(link, settings);
89+
drawLinkExit(link, settings, nodes, oldNodes);
9290

93-
nodes.forEach((d: any, index: any) => {
94-
oldPosition[index] = { x0: d.x, y0: d.y, id: d.id };
95-
});
91+
oldNodes = [...nodes];
9692
}
9793

98-
function refresh(data: any) {
94+
function refresh(data: any, newSettings?: Partial<ITreeConfig>) {
95+
if (newSettings) {
96+
settings = { ...settings, ...newSettings };
97+
}
9998
const nestedData = generateNestedData(data, settings);
10099
const treemap = generateBasicTreemap(settings);
101100
const computedTree = treemap(nestedData); // mutation
101+
102102
// @ts-ignore
103103
draw(svg, computedTree);
104104
}
105105

106106
function clean(keepConfig: boolean) {
107107
const myNode = keepConfig
108-
? document.querySelector(`#${settings.htmlID} svg g`)
109-
: document.querySelector(`#${settings.htmlID}`);
108+
? document.querySelector(`#${settings.htmlId} svg g`)
109+
: document.querySelector(`#${settings.htmlId}`);
110110
if (myNode) {
111111
while (myNode.firstChild) {
112112
myNode.removeChild(myNode.firstChild);
113113
}
114114
}
115-
oldPosition = [];
115+
oldNodes = [];
116116
}
117117

118118
const treeObject = { refresh, clean };

0 commit comments

Comments
 (0)