Skip to content

Commit 6312da1

Browse files
Hover-over info boxes persist when hovered over
Co-authored-by: yuanjackie1 <[email protected]>
1 parent 690ad51 commit 6312da1

File tree

2 files changed

+81
-24
lines changed

2 files changed

+81
-24
lines changed

src/app/components/StateRoute/ComponentMap/ComponentMap.tsx

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
/* eslint-disable guard-for-in */
99
// @ts-nocheck
1010

11-
import React, { useState, useEffect } from 'react';
11+
import React, { useState, useEffect, useRef } from 'react';
1212
import { Group } from '@visx/group';
1313
import { hierarchy, Tree } from '@visx/hierarchy';
1414
import { LinearGradient } from '@visx/gradient';
@@ -47,9 +47,9 @@ export default function ComponentMap({
4747
const [orientation, setOrientation] = useState('vertical');
4848
const [linkType, setLinkType] = useState('diagonal');
4949
const [stepPercent, setStepPercent] = useState(10);
50-
const [Tooltip, setTooltip] = useState(false);
5150
const [selectedNode, setSelectedNode] = useState('root');
5251
const [, dispatch] = useStoreContext();
52+
const toolTipTimeoutID = useRef(null);
5353

5454
useEffect(() => {
5555
dispatch(setCurrentTabInApp('map'));
@@ -191,7 +191,6 @@ export default function ComponentMap({
191191
<LinearGradient id='links-gradient' from='#fd9b93' to='#fe6e9e' />
192192
<rect
193193
onClick={() => {
194-
setTooltip(false);
195194
hideTooltip();
196195
}}
197196
width={totalWidth}
@@ -266,7 +265,6 @@ export default function ComponentMap({
266265
onClick={() => {
267266
dispatch(toggleExpanded(node.data));
268267
hideTooltip();
269-
setTooltip(false);
270268
}}
271269
/>
272270
)}
@@ -290,16 +288,48 @@ export default function ComponentMap({
290288
onClick={() => {
291289
dispatch(toggleExpanded(node.data));
292290
hideTooltip();
293-
setTooltip(false);
294291
}}
295-
onMouseOver={(event) => {
296-
setTooltip(true);
292+
// Mouse Enter Rect (Component Node) -----------------------------------------------------------------------
293+
/** This onMouseEnter event fires when the mouse first moves/hovers over a component node.
294+
* The supplied event listener callback produces a Tooltip element for the current node. */
295+
296+
onMouseEnter={(event) => {
297+
/** This 'if' statement block checks to see if you've just left another component node
298+
* by seeing if there's a current setTimeout waiting to close that component node's
299+
* tooltip (see onMouseLeave immediately below).
300+
* This setTimeout gives the mouse time to enter the tooltip element so the tooltip
301+
* can persist. If instead of entering said tooltip element you've left the previous
302+
* component node to enter this component node, this logic will clear the timeout event,
303+
* and close the tooltip. */
304+
if (toolTipTimeoutID.current !== null) {
305+
clearTimeout(toolTipTimeoutID.current);
306+
hideTooltip();
307+
}
308+
/** The following line resets the toolTipTimeoutID.current to null, showing that there
309+
* are no current setTimeouts running. I placed this outside of the above if statement
310+
* to make sure there are no edge cases that would allow for the toolTipTimeoutID.current
311+
* to hold onto an old reference. */
312+
toolTipTimeoutID.current = null;
313+
//This generates a tooltip for the component node the mouse has entered.
297314
handleMouseAndClickOver(event);
298315
}}
299-
// with onmouseOver, this produces a hover over effect for the Tooltip
300-
onMouseOut={() => {
301-
hideTooltip();
302-
setTooltip(false);
316+
317+
// Mouse Leave Rect (Component Node) --------------------------------------------------------------------------
318+
/** This onMouseLeave event fires when the mouse leaves a component node.
319+
* The supplied event listener callback generates a setTimeout call which gives the
320+
* mouse a certain amount of time between leaving the current component node and
321+
* closing the tooltip for that node.
322+
* If the mouse enters the tooltip before the timeout delay has passed, the
323+
* setTimeout event will be canceled. */
324+
onMouseLeave={() => {
325+
// This line invokes setTimeout and saves its ID to the useRef var toolTipTimeoutID
326+
toolTipTimeoutID.current = setTimeout(() => {
327+
// hideTooltip unmounts the tooltip
328+
hideTooltip();
329+
// As the timeout has been executed, the timeoutID can be reset to null
330+
toolTipTimeoutID.current = null;
331+
//There is a delay of 300 ms
332+
}, 300);
303333
}}
304334
/>
305335
)}
@@ -329,33 +359,48 @@ export default function ComponentMap({
329359
top={tooltipTop}
330360
left={tooltipLeft}
331361
style={tooltipStyles}
332-
onClick={hideTooltip}
362+
363+
//------------- Mouse Over TooltipInPortal--------------------------------------------------------------------
364+
/** This onMouseEnter fires when the mouse first moves/hovers over the tooltip
365+
* The supplied event listener callback stops the setTimeout that was going to
366+
* close the tooltip from firing */
367+
368+
onMouseEnter={() => {
369+
// The setTimeoutID stored in toolTipTimeoutID.current is from the setTimeout initiated by leaving the
370+
// component node that generated the tooltip. If you've triggered an onMouseEnter event in that tooltip,
371+
clearTimeout(toolTipTimeoutID.current);
372+
// This line resets the timeoutID to null
373+
toolTipTimeoutID.current = null;
374+
}}
375+
376+
//------------- Mouse Leave TooltipInPortal -----------------------------------------------------------------
377+
/** This onMouseLeave event fires when the mouse leaves the tooltip
378+
* The supplied event listener callback unmounts the tooltip */
379+
onMouseLeave={() => {
380+
// hideTooltip unmounts the tooltip
381+
hideTooltip();
382+
}}
333383
>
334-
<div
335-
onClick={() => {
336-
setTooltip(false);
337-
hideTooltip();
338-
}}
339-
>
384+
<div>
340385
<div style={{}}>
341386
{' '}
342387
<strong>{tooltipData.name}</strong>{' '}
343388
</div>
344389
<div> Render time: {formatRenderTime(tooltipData.componentData.actualDuration)} </div>
345390
<div className='stateTip'>
346-
State:
347-
{formatState(tooltipData.state)}
391+
State: {formatState(tooltipData.state)}
348392
</div>
349393
<div style={React.scrollStyle}>
350394
<div className='tooltipWrapper'>
351395
<h2>Props:</h2>
352396
{formatData(tooltipData.componentData.props, 'props')}
353397
</div>
354-
398+
399+
{/* Currently no use for this field
355400
<div className='tooltipWrapper'>
356401
<h2>Initial Context:</h2>
357402
{formatData(tooltipData.componentData.context, 'context')}
358-
</div>
403+
</div> */}
359404

360405
<div className='tooltipWrapper'>
361406
<h2>State:</h2>

src/app/index.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
/* eslint-disable react/jsx-filename-extension */
22
import React from 'react';
3-
import ReactDOM from 'react-dom';
3+
// import ReactDOM from 'react-dom';
4+
import { createRoot } from 'react-dom/client';
45
import App from './components/App';
56
import './styles/main.scss';
67

7-
ReactDOM.render(<App />, document.getElementById('root'));
8+
// Old way of rendering that was locking Reactime into React 17
9+
// Left for testing purposes
10+
// ReactDOM.render(<App />, document.getElementById('root'));
11+
12+
//New way of rendering that allows us to use React 18
13+
const root = createRoot(document.getElementById("root"));
14+
root.render(
15+
//Strict mode is for developers, it throws extra errors
16+
// <StrictMode>
17+
<App/>
18+
// </StrictMode>
19+
);

0 commit comments

Comments
 (0)