1
- import React , { useRef } from 'react' ;
1
+ import React , { useLayoutEffect , useRef , useState } from 'react' ;
2
2
3
3
import cx from 'classnames' ;
4
4
@@ -8,6 +8,8 @@ import '@osrd-project/ui-core/dist/theme.css';
8
8
import { positionMmToKm } from '../utils' ;
9
9
10
10
const MANCHETTE_ACTIONS_HEIGHT = '-40px' ;
11
+ // Waypoint height minus its position offset
12
+ const MENU_OFFSET = 2 ;
11
13
12
14
type WaypointProps = {
13
15
waypoint : InteractiveWaypoint ;
@@ -23,37 +25,62 @@ const Waypoint = ({
23
25
scrollableParentRef,
24
26
} : WaypointProps ) => {
25
27
const waypointRef = useRef < HTMLDivElement > ( null ) ;
28
+ const menuRef = useRef < HTMLDivElement > ( null ) ;
29
+ const [ menuPositionTop , setMenuPositionTop ] = useState ( 0 ) ;
26
30
27
- const isWaypointInView = useElementInView ( waypointRef , scrollableParentRef , {
28
- threshold : 1 ,
29
- rootMargin : `0px 0px ${ MANCHETTE_ACTIONS_HEIGHT } 0px` ,
31
+ const isWaypointInView = useElementInView (
32
+ isActive ? waypointRef : undefined ,
33
+ scrollableParentRef ,
34
+ {
35
+ threshold : 1 ,
36
+ rootMargin : `0px 0px ${ MANCHETTE_ACTIONS_HEIGHT } 0px` ,
37
+ }
38
+ ) ;
39
+
40
+ // We have to omit the dependency array here otherwise the menu won't move on scroll
41
+ // eslint-disable-next-line react-hooks/exhaustive-deps
42
+ useLayoutEffect ( ( ) => {
43
+ // Having the waypoint div in position relative has the side effect of making the menu
44
+ // not overflowing the waypoint list. So we need to position it with this logic.
45
+ if ( waypointRef . current ) {
46
+ const { bottom } = waypointRef . current ?. getBoundingClientRect ( ) ;
47
+ setMenuPositionTop ( bottom - MENU_OFFSET ) ;
48
+ }
30
49
} ) ;
31
50
32
51
if ( ! display ) return null ;
33
52
34
53
return (
35
- < div
36
- className = { cx ( 'flex waypoint items-baseline' , {
37
- 'menu-active' : isActive ,
38
- } ) }
39
- id = { id }
40
- onClick = { ( ) => {
41
- if ( onClick ) onClick ( id ) ;
42
- } }
43
- >
44
- < div ref = { waypointRef } className = "waypoint-position justify-self-start text-end" >
45
- { positionMmToKm ( position ) }
46
- </ div >
54
+ < >
55
+ < div
56
+ ref = { waypointRef }
57
+ className = { cx ( 'flex waypoint items-baseline' , {
58
+ 'menu-active' : isActive ,
59
+ } ) }
60
+ id = { id }
61
+ onClick = { ( ) => {
62
+ if ( onClick ) onClick ( id ) ;
63
+ } }
64
+ >
65
+ < div className = "waypoint-position justify-self-start text-end" >
66
+ { positionMmToKm ( position ) }
67
+ </ div >
47
68
48
- < div className = "waypoint-name mx-2 justify-self-start" > { name } </ div >
49
- < div className = "waypoint-separator" > </ div >
50
- < div className = "waypoint-ch font-mono justify-self-end" > { secondaryCode } </ div >
51
- < div className = "waypoint-separator" > </ div >
69
+ < div className = "waypoint-name mx-2 justify-self-start" > { name } </ div >
70
+ < div className = "waypoint-separator" > </ div >
71
+ < div className = "waypoint-ch font-mono justify-self-end" > { secondaryCode } </ div >
72
+ < div className = "waypoint-separator" > </ div >
52
73
53
- < div className = "waypoint-type" > </ div >
54
- < div className = "waypoint-separator" > </ div >
55
- { waypointMenu && isActive && isWaypointInView && waypointMenu }
56
- </ div >
74
+ < div className = "waypoint-type" > </ div >
75
+ < div className = "waypoint-separator" > </ div >
76
+ { waypointMenu && isActive && isWaypointInView && (
77
+ < div className = "menu-wrapper" ref = { menuRef } style = { { top : menuPositionTop } } >
78
+ { waypointMenu }
79
+ </ div >
80
+ ) }
81
+ </ div >
82
+ < div className = "last-separator" > </ div >
83
+ </ >
57
84
) ;
58
85
} ;
59
86
0 commit comments