Skip to content

Commit f8445f5

Browse files
committed
testing visx visualization with example data
1 parent f90ad7f commit f8445f5

File tree

6 files changed

+377
-56
lines changed

6 files changed

+377
-56
lines changed

src/app/components/StateRoute/Ax.tsx

Lines changed: 0 additions & 55 deletions
This file was deleted.
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import React, { useState } from 'react';
2+
import { Group } from '@visx/group';
3+
import { hierarchy, Tree } from '@visx/hierarchy';
4+
import { LinearGradient } from '@visx/gradient';
5+
import { pointRadial } from 'd3-shape';
6+
import useForceUpdate from './useForceUpdate';
7+
import LinkControls from './axLinkControls';
8+
import getLinkComponent from './getAxLinkComponents';
9+
10+
const theme = {
11+
scheme: 'monokai',
12+
author: 'wimer hazenberg (http://www.monokai.nl)',
13+
base00: '#272822',
14+
base01: '#383830',
15+
base02: '#49483e',
16+
base03: '#75715e',
17+
base04: '#a59f85',
18+
base05: '#f8f8f2',
19+
base06: '#f5f4f1',
20+
base07: '#f9f8f5',
21+
base08: '#f92672',
22+
base09: '#fd971f',
23+
base0A: '#f4bf75',
24+
base0B: '#a6e22e',
25+
base0C: '#a1efe4',
26+
base0D: '#66d9ef',
27+
base0E: '#ae81ff',
28+
base0F: '#cc6633',
29+
};
30+
31+
interface TreeNode {
32+
name: string;
33+
isExpanded?: boolean;
34+
children?: TreeNode[];
35+
}
36+
37+
const data: TreeNode = {
38+
name: 'T',
39+
children: [
40+
{
41+
name: 'A',
42+
children: [
43+
{ name: 'A1' },
44+
{ name: 'A2' },
45+
{ name: 'A3' },
46+
{
47+
name: 'C',
48+
children: [
49+
{
50+
name: 'C1',
51+
},
52+
{
53+
name: 'D',
54+
children: [
55+
{
56+
name: 'D1',
57+
},
58+
{
59+
name: 'D2',
60+
},
61+
{
62+
name: 'D3',
63+
},
64+
],
65+
},
66+
],
67+
},
68+
],
69+
},
70+
{ name: 'Z' },
71+
{
72+
name: 'B',
73+
children: [{ name: 'B1' }, { name: 'B2' }, { name: 'B3' }],
74+
},
75+
],
76+
};
77+
78+
const defaultMargin = {
79+
top: 30,
80+
left: 30,
81+
right: 55,
82+
bottom: 70,
83+
};
84+
85+
export type LinkTypesProps = {
86+
width: number;
87+
height: number;
88+
margin?: { top: number; right: number; bottom: number; left: number };
89+
};
90+
91+
export default function AxTree({
92+
width: totalWidth,
93+
height: totalHeight,
94+
margin = defaultMargin,
95+
}: LinkTypesProps) {
96+
const [layout, setLayout] = useState('cartesian');
97+
const [orientation, setOrientation] = useState('horizontal');
98+
const [linkType, setLinkType] = useState('diagonal');
99+
const [stepPercent, setStepPercent] = useState(0.5);
100+
const forceUpdate = useForceUpdate();
101+
102+
const innerWidth: number = totalWidth - margin.left - margin.right;
103+
const innerHeight: number = totalHeight - margin.top - margin.bottom - 60;
104+
105+
let origin: { x: number; y: number };
106+
let sizeWidth: number;
107+
let sizeHeight: number;
108+
109+
if (layout === 'polar') {
110+
origin = {
111+
x: innerWidth / 2,
112+
y: innerHeight / 2,
113+
};
114+
sizeWidth = 2 * Math.PI;
115+
sizeHeight = Math.min(innerWidth, innerHeight) / 2;
116+
} else {
117+
origin = { x: 0, y: 0 };
118+
if (orientation === 'vertical') {
119+
sizeWidth = innerWidth;
120+
sizeHeight = innerHeight;
121+
} else {
122+
sizeWidth = innerHeight;
123+
sizeHeight = innerWidth;
124+
}
125+
}
126+
127+
const LinkComponent = getLinkComponent({ layout, linkType, orientation });
128+
129+
return totalWidth < 10 ? null : (
130+
<div>
131+
<LinkControls
132+
layout={layout}
133+
orientation={orientation}
134+
linkType={linkType}
135+
stepPercent={stepPercent}
136+
setLayout={setLayout}
137+
setOrientation={setOrientation}
138+
setLinkType={setLinkType}
139+
setStepPercent={setStepPercent}
140+
/>
141+
<svg width={totalWidth} height={totalHeight}>
142+
<LinearGradient id="links-gradient" from="#fd9b93" to="#fe6e9e" />
143+
<rect width={totalWidth} height={totalHeight} rx={14} fill="#272b4d" />
144+
<Group top={margin.top} left={margin.left}>
145+
<Tree
146+
root={hierarchy(data, (d) => (d.isExpanded ? null : d.children))}
147+
size={[sizeWidth, sizeHeight]}
148+
separation={(a, b) => (a.parent === b.parent ? 1 : 0.5) / a.depth}
149+
>
150+
{(tree) => (
151+
<Group top={origin.y} left={origin.x}>
152+
{tree.links().map((link, i) => (
153+
<LinkComponent
154+
key={i}
155+
data={link}
156+
percent={stepPercent}
157+
stroke="rgb(254,110,158,0.6)"
158+
strokeWidth="1"
159+
fill="none"
160+
/>
161+
))}
162+
163+
{tree.descendants().map((node, key) => {
164+
const width = 40;
165+
const height = 20;
166+
167+
let top: number;
168+
let left: number;
169+
if (layout === 'polar') {
170+
const [radialX, radialY] = pointRadial(node.x, node.y);
171+
top = radialY;
172+
left = radialX;
173+
} else if (orientation === 'vertical') {
174+
top = node.y;
175+
left = node.x;
176+
} else {
177+
top = node.x;
178+
left = node.y;
179+
}
180+
181+
return (
182+
<Group top={top} left={left} key={key}>
183+
{node.depth === 0 && (
184+
<circle
185+
r={12}
186+
fill="url('#links-gradient')"
187+
onClick={() => {
188+
node.data.isExpanded = !node.data.isExpanded;
189+
console.log(node);
190+
forceUpdate();
191+
}}
192+
/>
193+
)}
194+
{node.depth !== 0 && (
195+
<rect
196+
height={height}
197+
width={width}
198+
y={-height / 2}
199+
x={-width / 2}
200+
fill="#272b4d"
201+
stroke={node.data.children ? '#03c0dc' : '#26deb0'}
202+
strokeWidth={1}
203+
strokeDasharray={node.data.children ? '0' : '2,2'}
204+
strokeOpacity={node.data.children ? 1 : 0.6}
205+
rx={node.data.children ? 0 : 10}
206+
onClick={() => {
207+
node.data.isExpanded = !node.data.isExpanded;
208+
console.log(node);
209+
forceUpdate();
210+
}}
211+
/>
212+
)}
213+
<text
214+
dy=".33em"
215+
fontSize={9}
216+
fontFamily="Arial"
217+
textAnchor="middle"
218+
style={{ pointerEvents: 'none' }}
219+
fill={node.depth === 0 ? '#71248e' : node.children ? 'white' : '#26deb0'}
220+
>
221+
{node.data.name}
222+
</text>
223+
</Group>
224+
);
225+
})}
226+
</Group>
227+
)}
228+
</Tree>
229+
</Group>
230+
</svg>
231+
</div>
232+
);
233+
}
234+
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import React from 'react';
2+
3+
const controlStyles = { fontSize: 10 };
4+
5+
type Props = {
6+
layout: string;
7+
orientation: string;
8+
linkType: string;
9+
stepPercent: number;
10+
setLayout: (layout: string) => void;
11+
setOrientation: (orientation: string) => void;
12+
setLinkType: (linkType: string) => void;
13+
setStepPercent: (percent: number) => void;
14+
};
15+
16+
export default function LinkControls({
17+
layout,
18+
orientation,
19+
linkType,
20+
stepPercent,
21+
setLayout,
22+
setOrientation,
23+
setLinkType,
24+
setStepPercent,
25+
}: Props) {
26+
return (
27+
<div style={controlStyles}>
28+
<label>layout:</label>&nbsp;
29+
<select
30+
onClick={(e) => e.stopPropagation()}
31+
onChange={(e) => setLayout(e.target.value)}
32+
value={layout}
33+
>
34+
<option value="cartesian">cartesian</option>
35+
<option value="polar">polar</option>
36+
</select>
37+
&nbsp;&nbsp;
38+
<label>orientation:</label>&nbsp;
39+
<select
40+
onClick={(e) => e.stopPropagation()}
41+
onChange={(e) => setOrientation(e.target.value)}
42+
value={orientation}
43+
disabled={layout === 'polar'}
44+
>
45+
<option value="vertical">vertical</option>
46+
<option value="horizontal">horizontal</option>
47+
</select>
48+
&nbsp;&nbsp;
49+
<label>link:</label>&nbsp;
50+
<select
51+
onClick={(e) => e.stopPropagation()}
52+
onChange={(e) => setLinkType(e.target.value)}
53+
value={linkType}
54+
>
55+
<option value="diagonal">diagonal</option>
56+
<option value="step">step</option>
57+
<option value="curve">curve</option>
58+
<option value="line">line</option>
59+
</select>
60+
{linkType === 'step' && layout !== 'polar' && (
61+
<>
62+
&nbsp;&nbsp;
63+
<label>step:</label>&nbsp;
64+
<input
65+
onClick={(e) => e.stopPropagation()}
66+
type="range"
67+
min={0}
68+
max={1}
69+
step={0.1}
70+
onChange={(e) => setStepPercent(Number(e.target.value))}
71+
value={stepPercent}
72+
disabled={linkType !== 'step' || layout === 'polar'}
73+
/>
74+
</>
75+
)}
76+
</div>
77+
);
78+
}

0 commit comments

Comments
 (0)