Skip to content

Commit

Permalink
🚧 Prepare v2 (#19)
Browse files Browse the repository at this point in the history
* 🚧 Prepare v2

* 🎨 Fix tslint

* 📦 2.0.0 version
  • Loading branch information
PierreCapo authored Jul 28, 2019
1 parent 3400d69 commit 5c15852
Show file tree
Hide file tree
Showing 25 changed files with 2,547 additions and 188 deletions.
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Treeviz

[![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)
![David](https://img.shields.io/david/PierreCapo/treeviz)
[![license](https://badgen.now.sh/badge/license/BSD)](./LICENSE)

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.

![](https://i.imgur.com/vyB2Erg.gif)
Expand Down Expand Up @@ -71,16 +75,15 @@ The table below lists all the avalaible key that the config object can have
| `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 |
| `nodeField` | string | "id" | The unique identifier field in the dataset representing the node |
| `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). |
| `flatData` | boolean | true | Specify whether the data passed to the tree is flat or already hierarchical |
| `zoomBehavior` | boolean | true | Toggle the ability to pan and zoom the tree |
| `hasFlatData` | boolean | true | Specify whether the data passed to the tree is flat or already hierarchical |
| `hasPanAndZoom` | boolean | true | Toggle the ability to pan and zoom the tree |
| `nodeWidth` | number | 160 | Width of a node in px |
| `nodeHeight` | number | 100 | Height of a node in px |
| `nodeColor` | function | (nodeData) => "#2196f3" | Color of the node |
| `linkColor` | function | (nodeData) => "#ffcc80" | Color of the link |
| `linkWidth` | function | (nodeData) => 10 | Width of the link |
| `linkShape` | "quadraticBeziers" \| "orthogonal" | "quadraticBeziers" | Shape of the link |
| `nodeTemplate` | function | (nodeData) => null | HTML template for every node |
| `horizontalLayout` | boolean | true | Direction of the tree. If true, the tree expands from left to right. If false, it goes from top to bottom |
| `renderNode` | function | (nodeData) => null | HTML template for every node |
| `isHorizontal` | boolean | true | Direction of the tree. If true, the tree expands from left to right. If false, it goes from top to bottom |
| `onNodeClick` | function | (nodeData) => null | Function handling the event when someone click on it |
| `onNodeMouseEnter` | function | (nodeData) => null | Function handling the event when someone hover a node |
| `onNodeMouseLeave` | function | (nodeData) => null | Function handling the event when the mouse pointer leaves a node |
Expand All @@ -93,7 +96,7 @@ The table below lists all the avalaible key that the config object can have

## Example

Assuming that you already have an HTML element on the pagh with the tag `id="a_tree"`
Assuming that you already have an HTML element on the page with the tag `id="a_tree"`

#### Flat data case :

Expand Down
34 changes: 21 additions & 13 deletions demo/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
{ id: 1, text_1: "Chaos", text_2: "Void", father: null, color:"#FF5722" },
{ id: 2, text_1: "Tartarus", text_2: "Abyss", father: 1, color:"#FFC107" },
{ id: 3, text_1: "Gaia", text_2: "Earth", father: 1, color:"#8BC34A" },
{ id: 4, text_1: "Eros", text_2: "Desire", father: 1, color:"#00BCD4" }
];
{ id: 4, text_1: "Eros", text_2: "Desire", father: 1, color:"#00BCD4" }];

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

var myTree = Treeviz.create({
htmlID: "tree",
nodeField: "id",
flatData: true,
htmlId: "tree",
idKey: "id",
hasFlatData: true,
relationnalField: "father",
zoomBehavior: true,
nodeWidth:160,
nodeHeight:100,
nodeColor: (nodeData) => nodeData.color ,
depthDistance:300,
horizontalLayout:true,
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>",
hasPanAndZoom: true,
nodeWidth:120,
nodeHeight:80,
mainAxisNodeSpacing:2,
isHorizontal:true,
renderNode: function(node) {
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:"
+node.data.color+
";border-radius:5px;'><div><strong>"
+node.data.text_1+
"</strong></div><div>is</div><div><i>"
+node.data.text_2+
"</i></div></div>";
},
linkWidth : (nodeData)=> nodeData.id*5,
linkColor : (nodeData) => "#B0BEC5" ,
onNodeClick : (nodeData) => console.log(nodeData)
Expand Down
2,273 changes: 2,272 additions & 1 deletion dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/typescript/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ITreeConfig } from "./typings";
export declare function create(userSettings: Partial<ITreeConfig>): {
refresh: (data: any) => void;
refresh: (data: any, newSettings?: Partial<ITreeConfig> | undefined) => void;
clean: (keepConfig: boolean) => void;
};
4 changes: 2 additions & 2 deletions dist/typescript/links/link-enter.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HierarchyPointNode } from "d3-hierarchy";
import { BaseType, Selection } from "d3-selection";
import { ITreeConfig } from "../typings";
export declare const drawLinkEnter: (link: Selection<BaseType, HierarchyPointNode<{}>, SVGGElement, {}>, computedTree: HierarchyPointNode<{}>, settings: ITreeConfig) => Selection<SVGPathElement, HierarchyPointNode<{}>, SVGGElement, {}>;
import { ITreeConfig, ExtendedHierarchyPointNode } from "../typings";
export declare const drawLinkEnter: (link: Selection<BaseType, HierarchyPointNode<{}>, SVGGElement, {}>, settings: ITreeConfig, nodes: ExtendedHierarchyPointNode[], oldNodes: ExtendedHierarchyPointNode[]) => Selection<SVGPathElement, HierarchyPointNode<{}>, SVGGElement, {}>;
4 changes: 2 additions & 2 deletions dist/typescript/links/link-exit.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HierarchyPointNode } from "d3-hierarchy";
import { BaseType, Selection } from "d3-selection";
import { ITreeConfig } from "../typings";
export declare const drawLinkExit: (link: Selection<BaseType, HierarchyPointNode<{}>, SVGGElement, {}>, settings: ITreeConfig) => void;
import { ITreeConfig, ExtendedHierarchyPointNode } from "../typings";
export declare const drawLinkExit: (link: Selection<BaseType, HierarchyPointNode<{}>, SVGGElement, {}>, settings: ITreeConfig, nodes: ExtendedHierarchyPointNode[], oldNodes: ExtendedHierarchyPointNode[]) => void;
2 changes: 1 addition & 1 deletion dist/typescript/nodes/node-enter.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { BaseType, Selection } from "d3-selection";
import { ExtendedHierarchyPointNode, ITreeConfig } from "../typings";
export declare const drawNodeEnter: (node: Selection<BaseType, ExtendedHierarchyPointNode, SVGGElement, {}>, settings: ITreeConfig) => Selection<SVGGElement, ExtendedHierarchyPointNode, SVGGElement, {}>;
export declare const drawNodeEnter: (node: Selection<BaseType, ExtendedHierarchyPointNode, SVGGElement, {}>, settings: ITreeConfig, nodes: ExtendedHierarchyPointNode[], oldNodes: ExtendedHierarchyPointNode[]) => Selection<SVGGElement, ExtendedHierarchyPointNode, SVGGElement, {}>;
2 changes: 1 addition & 1 deletion dist/typescript/nodes/node-exit.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { BaseType, Selection } from "d3-selection";
import { ExtendedHierarchyPointNode, ITreeConfig } from "../typings";
export declare const drawNodeExit: (node: Selection<BaseType, ExtendedHierarchyPointNode, SVGGElement, {}>, settings: ITreeConfig) => void;
export declare const drawNodeExit: (node: Selection<BaseType, ExtendedHierarchyPointNode, SVGGElement, {}>, settings: ITreeConfig, nodes: ExtendedHierarchyPointNode[], oldNodes: ExtendedHierarchyPointNode[]) => void;
17 changes: 8 additions & 9 deletions dist/typescript/typings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,25 @@ export interface ITreeConfig {
htmlID: string;
nodeField: string;
relationnalField: string;
flatData: boolean;
hasFlatData: boolean;
nodeWidth: number;
nodeHeight: number;
nodeDepthDistance: number | "auto";
nodeTemplate: (node: any) => string | null;
nodeColor: (node: any) => string | null | number | boolean;
mainAxisNodeSpacing: number | "auto";
renderNode: (node: any) => string | null;
linkShape: string;
linkColor: (node: any) => string | null | boolean;
linkWidth: (node: any) => string | number | null | boolean;
linkColor: (node: any) => string;
linkWidth: (node: any) => number;
onNodeClick: (node: any) => void;
onNodeMouseEnter: (node: any) => void;
onNodeMouseLeave: (node: any) => void;
horizontalLayout: boolean;
zoomBehavior: boolean;
isHorizontal: boolean;
hasPanAndZoom: boolean;
duration: number;
marginTop: number;
marginBottom: number;
marginLeft: number;
marginRight: number;
nodeSpacerPercentage: number;
secondaryAxisNodeSpacing: number;
}
export interface ExtendedHierarchyPointNode extends HierarchyPointNode<{}> {
x0?: number;
Expand Down
8 changes: 8 additions & 0 deletions dist/typescript/utils.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { ExtendedHierarchyPointNode, ITreeConfig } from "./typings";
export declare const getAreaSize: (htmlID: string) => {
areaWidth: number;
areaHeight: number;
};
declare type Result = ExtendedHierarchyPointNode & {
x0: number;
y0: number;
};
export declare const getFirstDisplayedAncestor: (ghostNodes: ExtendedHierarchyPointNode[], viewableNodes: ExtendedHierarchyPointNode[], id: string) => Result;
export declare const setNodeLocation: (xPosition: number, yPosition: number, settings: ITreeConfig) => string;
export {};
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "treeviz",
"version": "1.3.3",
"version": "2.0.0",
"description": "Library which aims to represent trees for data visualization",
"keywords": [
"d3",
Expand All @@ -20,6 +20,7 @@
"types": "./dist/typescript/index.d.ts",
"scripts": {
"build": "webpack --mode=production",
"watch": "webpack --mode=development --watch",
"dev": "webpack-dev-server --mode=development",
"test": "jest",
"lint": "tslint src/*.ts"
Expand All @@ -29,7 +30,7 @@
"@babel/preset-env": "^7.5.5",
"@babel/preset-typescript": "^7.3.3",
"@types/node": "^12.6.8",
"@types/webpack": "^4.32.0",
"@types/webpack": "^4.32.1",
"@types/webpack-dev-server": "^3.1.7",
"babel-loader": "^8.0.6",
"html-webpack-plugin": "^3.2.0",
Expand All @@ -38,7 +39,7 @@
"tslint": "^5.18.0",
"tslint-config-prettier": "^1.18.0",
"typescript": "^3.5.3",
"webpack": "^4.36.1",
"webpack": "^4.38.0",
"webpack-cli": "^3.3.6",
"webpack-dev-server": "^3.7.2"
},
Expand Down
84 changes: 42 additions & 42 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,107 +12,107 @@ import { ExtendedHierarchyPointNode, ITreeConfig } from "./typings";

export function create(userSettings: Partial<ITreeConfig>) {
const defaultSettings: ITreeConfig = {
htmlID: "",
nodeField: "",
relationnalField: "",
flatData: true,
htmlId: "",
idKey: "",
relationnalField: "father",
hasFlatData: true,
nodeWidth: 160,
nodeHeight: 100,
nodeDepthDistance: 300,
nodeColor: () => "#2196f3",
nodeTemplate: () => "Node",
mainAxisNodeSpacing: 300,
renderNode: () => "Node",
linkColor: () => "#ffcc80",
linkWidth: () => 10,
linkShape: "quadraticBeziers",
horizontalLayout: true,
zoomBehavior: false,
duration: 400,
isHorizontal: true,
hasPanAndZoom: false,
duration: 600,
onNodeClick: () => undefined,
onNodeMouseEnter: () => undefined,
onNodeMouseLeave: () => undefined,
marginBottom: 0,
marginLeft: 0,
marginRight: 0,
marginTop: 0,
nodeSpacerPercentage: 1.25,
secondaryAxisNodeSpacing: 1.25,
};
const settings: ITreeConfig = { ...defaultSettings, ...userSettings };
let oldPosition: Array<{ x0?: number; y0?: number; id?: string }> = [];
let settings: ITreeConfig = {
...defaultSettings,
...userSettings,
};
let oldNodes: ExtendedHierarchyPointNode[] = [];

function draw(
svg: Selection<SVGGElement, {}, HTMLElement, any>,
computedTree: HierarchyPointNode<{}>
) {
const nodes = computedTree.descendants() as ExtendedHierarchyPointNode[];

const links = computedTree.descendants().slice(1);

const { nodeDepthDistance } = settings;
if (nodeDepthDistance !== "auto") {
const { mainAxisNodeSpacing: mainAxisNodeSpacing } = settings;
if (mainAxisNodeSpacing !== "auto") {
// Normalize for fixed-depth.
nodes.forEach((d: any) => {
d.y = d.depth * nodeDepthDistance;
d.y = d.depth * settings.nodeWidth * mainAxisNodeSpacing;
});
}

nodes.forEach((currentNode: ExtendedHierarchyPointNode) => {
currentNode.x0 = {
...currentNode,
x0: undefined,
y0: undefined,
// @ts-ignore
...oldPosition.filter(oldNode => oldNode.id === currentNode.id)[0],
}.x0;
currentNode.y0 = {
...currentNode,
x0: undefined,
y0: undefined,
// @ts-ignore
...oldPosition.filter(oldNode => oldNode.id === currentNode.id)[0],
}.y0;
const currentNodeOldPosition = oldNodes.find(
node => node.id === currentNode.id
);
currentNode.x0 = currentNodeOldPosition
? currentNodeOldPosition.x0
: currentNode.x;
currentNode.y0 = currentNodeOldPosition
? currentNodeOldPosition.y0
: currentNode.y;
});

// ****************** Nodes section ***************************
const node = svg.selectAll("g.node").data(nodes, (d: any) => {
return d[settings.nodeField];
return d[settings.idKey];
});

const nodeEnter = drawNodeEnter(node, settings);
const nodeEnter = drawNodeEnter(node, settings, nodes, oldNodes);
drawNodeUpdate(nodeEnter, node, settings);
drawNodeExit(node, settings);
drawNodeExit(node, settings, nodes, oldNodes);

// ****************** links section ***************************

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

const linkEnter = drawLinkEnter(link, computedTree, settings);
const linkEnter = drawLinkEnter(link, settings, nodes, oldNodes);
drawLinkUpdate(linkEnter, link, settings);
drawLinkExit(link, settings);
drawLinkExit(link, settings, nodes, oldNodes);

nodes.forEach((d: any, index: any) => {
oldPosition[index] = { x0: d.x, y0: d.y, id: d.id };
});
oldNodes = [...nodes];
}

function refresh(data: any) {
function refresh(data: any, newSettings?: Partial<ITreeConfig>) {
if (newSettings) {
settings = { ...settings, ...newSettings };
}
const nestedData = generateNestedData(data, settings);
const treemap = generateBasicTreemap(settings);
const computedTree = treemap(nestedData); // mutation

// @ts-ignore
draw(svg, computedTree);
}

function clean(keepConfig: boolean) {
const myNode = keepConfig
? document.querySelector(`#${settings.htmlID} svg g`)
: document.querySelector(`#${settings.htmlID}`);
? document.querySelector(`#${settings.htmlId} svg g`)
: document.querySelector(`#${settings.htmlId}`);
if (myNode) {
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
}
oldPosition = [];
oldNodes = [];
}

const treeObject = { refresh, clean };
Expand Down
Loading

0 comments on commit 5c15852

Please sign in to comment.