1- import React , { useState } from 'react' ;
1+ import React , { useState , useRef } from 'react' ;
2+ import { useDispatch , useSelector } from 'react-redux' ;
23import { Group } from '@visx/group' ;
34import { hierarchy , Tree } from '@visx/hierarchy' ;
45import { LinearGradient } from '@visx/gradient' ;
56import { pointRadial } from 'd3-shape' ;
6- import { useTooltipInPortal } from '@visx/tooltip' ;
77import LinkControls from './axLinkControls' ;
88import 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?
1018const theme = {
1119 scheme : 'monokai' ,
1220 author : 'wimer hazenberg (http://www.monokai.nl)' ,
@@ -162,6 +170,50 @@ export default function AxTree(props) {
162170 let totalWidth = width ;
163171 let totalHeight = height ;
164172
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+
165217 const [ layout , setLayout ] = useState ( 'cartesian' ) ;
166218 const [ orientation , setOrientation ] = useState ( 'horizontal' ) ;
167219 const [ linkType , setLinkType ] = useState ( 'diagonal' ) ;
@@ -266,45 +318,49 @@ export default function AxTree(props) {
266318 populateNodeAxArr ( rootAxNode ) ;
267319 console . log ( 'nodeAxArr: ' , nodeAxArr ) ;
268320
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 ( ) ;
276324
277325 return totalWidth < 10 ? null : (
278326 < 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 >
289343
290344 { /* svg references purple background */ }
291- < svg ref = { containerRef } width = { totalWidth } height = { totalHeight + 0 } >
345+ < svg ref = { containerRef } width = { totalWidth + 0.2 * totalWidth } height = { totalHeight } >
292346 < LinearGradient id = 'root-gradient' from = '#488689' to = '#3c6e71' />
293347 < LinearGradient id = 'parent-gradient' from = '#488689' to = '#3c6e71' />
294348 < rect
295349 className = 'componentMapContainer'
296350 width = { sizeWidth / aspect }
297351 height = { sizeHeight / aspect + 0 }
298352 rx = { 14 }
299- />
353+ onClick = { ( ) => {
354+ hideTooltip ( ) ;
355+ } } />
300356 < Group transform = { `scale(${ aspect } )` } top = { margin . top } left = { margin . left } >
301357 < Tree
302358 root = { hierarchy ( nodeAxArr [ 0 ] , ( d ) => ( d . isExpanded ? null : d . children ) ) }
303359 size = { [ sizeWidth / aspect , sizeHeight / aspect ] }
304360 separation = { ( a , b ) => ( a . parent === b . parent ? 0.5 : 0.5 ) / a . depth }
305361 >
306362 { ( tree ) => (
307- < Group top = { origin . y + 35 } left = { origin . x + 50 / aspect } >
363+ < Group top = { origin . y + 35 } left = { origin . x + 110 } >
308364 { tree . links ( ) . map ( ( link , i ) => (
309365 < LinkComponent
310366 key = { i }
@@ -409,6 +465,19 @@ export default function AxTree(props) {
409465 } else {
410466 aspect = Math . max ( aspect , 0.2 ) ;
411467 }
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+ } ;
412481
413482 return (
414483 < Group top = { top } left = { left } key = { key } className = 'rect' >
@@ -425,6 +494,7 @@ export default function AxTree(props) {
425494 rx = { node . children ? 4 : 10 }
426495 onClick = { ( ) => {
427496 node . data . isExpanded = ! node . data . isExpanded ;
497+ hideTooltip ( ) ;
428498 } }
429499 />
430500 ) }
@@ -448,6 +518,40 @@ export default function AxTree(props) {
448518 rx = { node . children ? 4 : 10 }
449519 onClick = { ( ) => {
450520 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 ) ;
451555 } }
452556 />
453557 ) }
@@ -476,6 +580,45 @@ export default function AxTree(props) {
476580 </ Tree >
477581 </ Group >
478582 </ 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+
479622 </ div >
480623 ) ;
481624}
0 commit comments