1
- import React , { useState } from 'react' ;
1
+ import React , { useState , useRef } from 'react' ;
2
+ import { useDispatch , useSelector } from 'react-redux' ;
2
3
import { Group } from '@visx/group' ;
3
4
import { hierarchy , Tree } from '@visx/hierarchy' ;
4
5
import { LinearGradient } from '@visx/gradient' ;
5
6
import { pointRadial } from 'd3-shape' ;
6
- import { useTooltipInPortal } from '@visx/tooltip' ;
7
7
import LinkControls from './axLinkControls' ;
8
8
import getLinkComponent from './getAxLinkComponents' ;
9
-
9
+ import { useTooltip , useTooltipInPortal , defaultStyles } from '@visx/tooltip' ;
10
+ import ToolTipDataDisplay from './ToolTipDataDisplay' ;
11
+ import { ToolTipStyles } from '../../../FrontendTypes' ;
12
+ import { localPoint } from '@visx/event' ;
13
+ import AxLegend from './axLegend' ;
14
+ import { renderAxLegend } from '../../../slices/AxSlices/axLegendSlice' ;
15
+ import type { RootState } from '../../../store' ;
16
+
17
+ //still using below themes?
10
18
const theme = {
11
19
scheme : 'monokai' ,
12
20
author : 'wimer hazenberg (http://www.monokai.nl)' ,
@@ -162,6 +170,50 @@ export default function AxTree(props) {
162
170
let totalWidth = width ;
163
171
let totalHeight = height ;
164
172
173
+
174
+
175
+ const toolTipTimeoutID = useRef ( null ) ; //useRef stores stateful data that’s not needed for rendering.
176
+ const {
177
+ tooltipData, // value/data that tooltip may need to render
178
+ tooltipLeft, // number used for tooltip positioning
179
+ tooltipTop, // number used for tooltip positioning
180
+ tooltipOpen, // boolean whether the tooltip state is open or closed
181
+ showTooltip, // function to set tooltip state
182
+ hideTooltip, // function to close a tooltip
183
+ } = useTooltip ( ) ; // returns an object with several properties that you can use to manage the tooltip state of your component
184
+ console . log ( 'tool tip data: ' , tooltipData ) ;
185
+ // let nameVal = JSON.stringify(tooltipData)
186
+ // console.log('nameVal', nameVal);
187
+ const {
188
+ containerRef, // Access to the container's bounding box. This will be empty on first render.
189
+ TooltipInPortal, // TooltipWithBounds in a Portal, outside of your component DOM tree
190
+ } = useTooltipInPortal ( {
191
+ // Visx hook
192
+ detectBounds : true , // use TooltipWithBounds
193
+ scroll : true , // when tooltip containers are scrolled, this will correctly update the Tooltip position
194
+ } ) ;
195
+
196
+ const tooltipStyles : ToolTipStyles = {
197
+ ...defaultStyles ,
198
+ minWidth : 60 ,
199
+ maxWidth : 300 ,
200
+ backgroundColor : 'rgb(15,15,15)' ,
201
+ color : 'white' ,
202
+ fontSize : '16px' ,
203
+ lineHeight : '18px' ,
204
+ fontFamily : 'Roboto' ,
205
+ zIndex : 100 ,
206
+ pointerEvents : 'all !important' ,
207
+ } ;
208
+
209
+ // const formatRenderTime = (time: number): string => {
210
+ // if (!time) return 'No time information';
211
+ // const renderTime = time.toFixed(3);
212
+ // return `${renderTime} ms `;
213
+ // };
214
+
215
+
216
+
165
217
const [ layout , setLayout ] = useState ( 'cartesian' ) ;
166
218
const [ orientation , setOrientation ] = useState ( 'horizontal' ) ;
167
219
const [ linkType , setLinkType ] = useState ( 'diagonal' ) ;
@@ -266,45 +318,49 @@ export default function AxTree(props) {
266
318
populateNodeAxArr ( rootAxNode ) ;
267
319
console . log ( 'nodeAxArr: ' , nodeAxArr ) ;
268
320
269
- const {
270
- containerRef, // Access to the container's bounding box. This will be empty on first render.
271
- } = useTooltipInPortal ( {
272
- // Visx hook
273
- detectBounds : true , // use TooltipWithBounds
274
- scroll : true , // when tooltip containers are scrolled, this will correctly update the Tooltip position
275
- } ) ;
321
+ // ax Legend
322
+ const { axLegendButtonClicked } = useSelector ( ( state : RootState ) => state . axLegend ) ;
323
+ const dispatch = useDispatch ( ) ;
276
324
277
325
return totalWidth < 10 ? null : (
278
326
< div >
279
- < LinkControls
280
- layout = { layout }
281
- orientation = { orientation }
282
- linkType = { linkType }
283
- stepPercent = { stepPercent }
284
- setLayout = { setLayout }
285
- setOrientation = { setOrientation }
286
- setLinkType = { setLinkType }
287
- setStepPercent = { setStepPercent }
288
- />
327
+ < div id = 'axControls' >
328
+ < LinkControls
329
+ layout = { layout }
330
+ orientation = { orientation }
331
+ linkType = { linkType }
332
+ stepPercent = { stepPercent }
333
+ setLayout = { setLayout }
334
+ setOrientation = { setOrientation }
335
+ setLinkType = { setLinkType }
336
+ setStepPercent = { setStepPercent }
337
+ />
338
+
339
+ < button id = 'axLegendButton' onClick = { ( ) => dispatch ( renderAxLegend ( ) ) } >
340
+ Generate Ax Tree Legend
341
+ </ button >
342
+ </ div >
289
343
290
344
{ /* svg references purple background */ }
291
- < svg ref = { containerRef } width = { totalWidth } height = { totalHeight + 0 } >
345
+ < svg ref = { containerRef } width = { totalWidth + 0.2 * totalWidth } height = { totalHeight } >
292
346
< LinearGradient id = 'root-gradient' from = '#488689' to = '#3c6e71' />
293
347
< LinearGradient id = 'parent-gradient' from = '#488689' to = '#3c6e71' />
294
348
< rect
295
349
className = 'componentMapContainer'
296
350
width = { sizeWidth / aspect }
297
351
height = { sizeHeight / aspect + 0 }
298
352
rx = { 14 }
299
- />
353
+ onClick = { ( ) => {
354
+ hideTooltip ( ) ;
355
+ } } />
300
356
< Group transform = { `scale(${ aspect } )` } top = { margin . top } left = { margin . left } >
301
357
< Tree
302
358
root = { hierarchy ( nodeAxArr [ 0 ] , ( d ) => ( d . isExpanded ? null : d . children ) ) }
303
359
size = { [ sizeWidth / aspect , sizeHeight / aspect ] }
304
360
separation = { ( a , b ) => ( a . parent === b . parent ? 0.5 : 0.5 ) / a . depth }
305
361
>
306
362
{ ( tree ) => (
307
- < Group top = { origin . y + 35 } left = { origin . x + 50 / aspect } >
363
+ < Group top = { origin . y + 35 } left = { origin . x + 110 } >
308
364
{ tree . links ( ) . map ( ( link , i ) => (
309
365
< LinkComponent
310
366
key = { i }
@@ -409,6 +465,19 @@ export default function AxTree(props) {
409
465
} else {
410
466
aspect = Math . max ( aspect , 0.2 ) ;
411
467
}
468
+ const handleMouseAndClickOver = ( event ) : void => {
469
+ const coords = localPoint ( event . target . ownerSVGElement , event ) ;
470
+ const tooltipObj = { ...node . data } ;
471
+ console . log ( "tooltipobj: " , tooltipObj ) ;
472
+
473
+ showTooltip ( {
474
+ tooltipLeft : coords . x ,
475
+ tooltipTop : coords . y ,
476
+ tooltipData : tooltipObj ,
477
+ // this is where the data for state and render time is displayed
478
+ // but does not show props functions and etc
479
+ } ) ;
480
+ } ;
412
481
413
482
return (
414
483
< Group top = { top } left = { left } key = { key } className = 'rect' >
@@ -425,6 +494,7 @@ export default function AxTree(props) {
425
494
rx = { node . children ? 4 : 10 }
426
495
onClick = { ( ) => {
427
496
node . data . isExpanded = ! node . data . isExpanded ;
497
+ hideTooltip ( ) ;
428
498
} }
429
499
/>
430
500
) }
@@ -448,6 +518,40 @@ export default function AxTree(props) {
448
518
rx = { node . children ? 4 : 10 }
449
519
onClick = { ( ) => {
450
520
node . data . isExpanded = ! node . data . isExpanded ;
521
+ hideTooltip ( ) ;
522
+ } }
523
+ // Mouse Enter Rect (Component Node) -----------------------------------------------------------------------
524
+ /** This onMouseEnter event fires when the mouse first moves/hovers over a component node.
525
+ * The supplied event listener callback produces a Tooltip element for the current node. */
526
+
527
+ onMouseEnter = { ( event ) => {
528
+ /** This 'if' statement block checks to see if you've just left another component node
529
+ * by seeing if there's a current setTimeout waiting to close that component node's
530
+ * tooltip (see onMouseLeave immediately below). If so it clears the tooltip generated
531
+ * from that component node so a new tooltip for the node you've just entered can render. */
532
+ if ( toolTipTimeoutID . current !== null ) {
533
+ clearTimeout ( toolTipTimeoutID . current ) ;
534
+ hideTooltip ( ) ;
535
+ }
536
+ // Removes the previous timeoutID to avoid errors
537
+ toolTipTimeoutID . current = null ;
538
+ //This generates a tooltip for the component node the mouse has entered.
539
+ handleMouseAndClickOver ( event ) ;
540
+ } }
541
+ // Mouse Leave Rect (Component Node) --------------------------------------------------------------------------
542
+ /** This onMouseLeave event fires when the mouse leaves a component node.
543
+ * The supplied event listener callback generates a setTimeout call which gives the
544
+ * mouse a certain amount of time between leaving the current component node and
545
+ * closing the tooltip for that node.
546
+ * If the mouse enters the tooltip before the timeout delay has passed, the
547
+ * setTimeout event will be canceled. */
548
+ onMouseLeave = { ( ) => {
549
+ // Store setTimeout ID so timeout can be cleared if necessary
550
+ toolTipTimeoutID . current = setTimeout ( ( ) => {
551
+ // hideTooltip unmounts the tooltip
552
+ hideTooltip ( ) ;
553
+ toolTipTimeoutID . current = null ;
554
+ } , 300 ) ;
451
555
} }
452
556
/>
453
557
) }
@@ -476,6 +580,45 @@ export default function AxTree(props) {
476
580
</ Tree >
477
581
</ Group >
478
582
</ svg >
583
+ { tooltipOpen && tooltipData && (
584
+ < TooltipInPortal
585
+ // set this to random so it correctly updates with parent bounds
586
+ key = { Math . random ( ) }
587
+ top = { tooltipTop }
588
+ left = { tooltipLeft }
589
+ style = { tooltipStyles }
590
+ //------------- Mouse Over TooltipInPortal--------------------------------------------------------------------
591
+ /** After the mouse enters the tooltip, it's able to persist by clearing the setTimeout
592
+ * that would've unmounted it */
593
+ onMouseEnter = { ( ) => {
594
+ clearTimeout ( toolTipTimeoutID . current ) ;
595
+ toolTipTimeoutID . current = null ;
596
+ } }
597
+ //------------- Mouse Leave TooltipInPortal -----------------------------------------------------------------
598
+ /** When the mouse leaves the tooltip, the tooltip unmounts */
599
+ onMouseLeave = { ( ) => {
600
+ hideTooltip ( ) ;
601
+ } }
602
+ >
603
+ < div >
604
+ < div >
605
+ < strong > { JSON . stringify ( tooltipData [ 'name' ] . value ) } </ strong >
606
+ </ div >
607
+ < div >
608
+ < ToolTipDataDisplay containerName = 'Ax Node Info' dataObj = { tooltipData } />
609
+ { /* <ToolTipDataDisplay containerName='State'dataObj={tooltipData}/> */ }
610
+ </ div >
611
+ </ div >
612
+ </ TooltipInPortal >
613
+ ) }
614
+
615
+ { /* ax Legend */ }
616
+ < div >
617
+ { axLegendButtonClicked ?
618
+ < AxLegend /> : ''
619
+ }
620
+ </ div >
621
+
479
622
</ div >
480
623
) ;
481
624
}
0 commit comments