@@ -6,6 +6,8 @@ import { styled, useTheme } from "@mui/material"
6
6
import { ConvertedTreeNode } from "../convert"
7
7
import DetailsDrawer , { DetailsNode } from "./DetailsDrawer"
8
8
9
+ type NodeType = d3 . HierarchyRectangularNode < ConvertedTreeNode >
10
+
9
11
function scaleAdjust ( tree : ConvertedTreeNode , f : ( x : number ) => number ) : ConvertedTreeNode {
10
12
switch ( tree . type ) {
11
13
case "leaf" :
@@ -34,14 +36,96 @@ const AnimatedSVGGroup = styled("g")(({ theme }) => ({
34
36
} ,
35
37
} ) )
36
38
39
+ const TreemapNode = ( props : {
40
+ node : NodeType
41
+ i : number
42
+ xScale : d3 . ScaleContinuousNumeric < number , number , never >
43
+ yScale : d3 . ScaleContinuousNumeric < number , number , never >
44
+ setDetailsNode : ( x : DetailsNode | null ) => void
45
+ setDetailsOpen : ( x : boolean ) => void
46
+ } ) => {
47
+ function getNamePath ( d : NodeType ) {
48
+ return d
49
+ . ancestors ( )
50
+ . reverse ( )
51
+ . map ( ( d ) => d . data . name )
52
+ }
53
+
54
+ const [ x , y ] = [ props . xScale ( props . node . x0 ) , props . yScale ( props . node . y0 ) ]
55
+ const [ rectWidth , rectHeight ] = [
56
+ props . xScale ( props . node . x1 ) - props . xScale ( props . node . x0 ) ,
57
+ props . yScale ( props . node . y1 ) - props . yScale ( props . node . y0 ) ,
58
+ ]
59
+
60
+ const theme = useTheme ( )
61
+ const strokeColor =
62
+ theme . palette . mode === "dark" ? theme . palette . grey [ "400" ] : theme . palette . grey [ "900" ]
63
+
64
+ const leafId = useId ( )
65
+ const clipId = useId ( )
66
+
67
+ let area : number | null = null
68
+ switch ( props . node . data . type ) {
69
+ case "leaf" :
70
+ case "adj-leaf" :
71
+ area = props . node . data . size
72
+ break
73
+ case "internal" :
74
+ area = null
75
+ break
76
+ }
77
+
78
+ return (
79
+ < AnimatedSVGGroup
80
+ key = { props . i }
81
+ transform = { `translate(${ x } ,${ y } )` }
82
+ onClick = { ( ) => {
83
+ props . setDetailsNode ( { ...props . node . data , path : getNamePath ( props . node ) } )
84
+ props . setDetailsOpen ( true )
85
+ } }
86
+ >
87
+ < title >
88
+ { getNamePath ( props . node ) . join ( "/" ) }
89
+ { area ? ` (Area: ${ area } )` : null }
90
+ </ title >
91
+ < rect id = { leafId } stroke = { strokeColor } width = { rectWidth } height = { rectHeight } />
92
+ < clipPath id = { clipId } >
93
+ < use xlinkHref = { new URL ( `#${ leafId } ` , location . toString ( ) ) . toString ( ) } />
94
+ </ clipPath >
95
+ < text
96
+ clipPath = { clipId }
97
+ x = { 3 }
98
+ fill = { theme . palette . text . primary }
99
+ style = { {
100
+ userSelect : "none" ,
101
+ } }
102
+ >
103
+ { getNamePath ( props . node ) . map ( ( name , i ) => {
104
+ return (
105
+ < tspan key = { i } x = { 3 } dy = "1em" >
106
+ { name }
107
+ </ tspan >
108
+ )
109
+ } ) }
110
+ { area ? (
111
+ < >
112
+ < tspan x = { 3 } dy = "2em" >
113
+ Area:
114
+ </ tspan >
115
+ < tspan dx = { 3 } > { area } </ tspan >
116
+ </ >
117
+ ) : null }
118
+ </ text >
119
+ </ AnimatedSVGGroup >
120
+ )
121
+ }
122
+
37
123
const D3Treemap = ( props : {
38
124
data : ConvertedTreeNode
39
125
width : number
40
126
height : number
41
127
scaleFunc : ( x : number ) => number
42
128
} ) => {
43
- type NodeType = d3 . HierarchyRectangularNode < ConvertedTreeNode >
44
-
45
129
const theme = useTheme ( )
46
130
47
131
const data = scaleAdjust ( props . data , props . scaleFunc )
@@ -84,12 +168,6 @@ const D3Treemap = (props: {
84
168
[ root , height ]
85
169
)
86
170
87
- const getNamePath = ( d : NodeType ) =>
88
- d
89
- . ancestors ( )
90
- . reverse ( )
91
- . map ( ( d ) => d . data . name )
92
-
93
171
const [ detailsOpen , setDetailsOpen ] = useState ( false )
94
172
const [ detailsNode , setDetailsNode ] = useState < DetailsNode | null > ( null )
95
173
@@ -108,71 +186,17 @@ const D3Treemap = (props: {
108
186
borderColor : svgBorderColor ,
109
187
} }
110
188
>
111
- { ( root . leaves ( ) ?? [ ] ) . map ( ( node , i ) => {
112
- const [ x , y ] = [ xScale ( node . x0 ) , yScale ( node . y0 ) ]
113
- const [ rectWidth , rectHeight ] = [
114
- xScale ( node . x1 ) - xScale ( node . x0 ) ,
115
- yScale ( node . y1 ) - yScale ( node . y0 ) ,
116
- ]
117
-
118
- const leafId = useId ( )
119
- const clipId = useId ( )
120
-
121
- let area : number | null = null
122
- switch ( node . data . type ) {
123
- case "leaf" :
124
- case "adj-leaf" :
125
- area = node . data . size
126
- break
127
- case "internal" :
128
- area = null
129
- break
130
- }
131
-
132
- return (
133
- < AnimatedSVGGroup
134
- key = { i }
135
- transform = { `translate(${ x } ,${ y } )` }
136
- onClick = { ( ) => {
137
- setDetailsNode ( { ...node . data , path : getNamePath ( node ) } )
138
- setDetailsOpen ( true )
139
- } }
140
- >
141
- < title >
142
- { getNamePath ( node ) . join ( "/" ) }
143
- { area ? ` (Area: ${ area } )` : null }
144
- </ title >
145
- < rect id = { leafId } stroke = { svgBorderColor } width = { rectWidth } height = { rectHeight } />
146
- < clipPath id = { clipId } >
147
- < use xlinkHref = { new URL ( `#${ leafId } ` , location . toString ( ) ) . toString ( ) } />
148
- </ clipPath >
149
- < text
150
- clipPath = { clipId }
151
- x = { 3 }
152
- fill = { theme . palette . text . primary }
153
- style = { {
154
- userSelect : "none" ,
155
- } }
156
- >
157
- { getNamePath ( node ) . map ( ( name , i ) => {
158
- return (
159
- < tspan key = { i } x = { 3 } dy = "1em" >
160
- { name }
161
- </ tspan >
162
- )
163
- } ) }
164
- { area ? (
165
- < >
166
- < tspan x = { 3 } dy = "2em" >
167
- Area:
168
- </ tspan >
169
- < tspan dx = { 3 } > { area } </ tspan >
170
- </ >
171
- ) : null }
172
- </ text >
173
- </ AnimatedSVGGroup >
174
- )
175
- } ) }
189
+ { ( root . leaves ( ) ?? [ ] ) . map ( ( node , i ) => (
190
+ < TreemapNode
191
+ node = { node }
192
+ i = { i }
193
+ xScale = { xScale }
194
+ yScale = { yScale }
195
+ setDetailsNode = { setDetailsNode }
196
+ setDetailsOpen = { setDetailsOpen }
197
+ key = { i }
198
+ />
199
+ ) ) }
176
200
</ svg >
177
201
178
202
< DetailsDrawer
0 commit comments