1
- import { useMemo } from 'react' ;
1
+ import { useLayoutEffect , useMemo , useState } from 'react' ;
2
2
3
3
import { Location } from '@osrd-project/ui-icons' ;
4
4
import { useTranslation } from 'react-i18next' ;
@@ -27,6 +27,8 @@ const StdcmVias = ({ disabled = false }: StdcmConfigCardProps) => {
27
27
useOsrdConfActions ( ) as StdcmConfSliceActions ;
28
28
const pathSteps = useSelector ( getStdcmPathSteps ) ;
29
29
30
+ const [ newIntermediateOpIndex , setNewIntermediateOpIndex ] = useState < number > ( ) ;
31
+
30
32
const intermediatePoints = useMemo ( ( ) => pathSteps . slice ( 1 , - 1 ) , [ pathSteps ] ) ;
31
33
32
34
const updateStopType = ( newStopType : StdcmStopTypes , pathStep : StdcmPathStep ) => {
@@ -44,6 +46,44 @@ const StdcmVias = ({ disabled = false }: StdcmConfigCardProps) => {
44
46
) ;
45
47
} ;
46
48
49
+ /**
50
+ * As the new intermediateOp block animates, we want to scroll to keep the box in the viewport.
51
+ * To do so, we install an animation frame listener (requestAnimationFrame) which updates the scroll position
52
+ * each time an animation frame is triggered.
53
+ * An animation end listener is also installed to cancel the animation frame listener.
54
+ * To properly clean up when the component is unmounted, we return a cleanup function that removes both listeners.
55
+ */
56
+ useLayoutEffect ( ( ) => {
57
+ if ( ! newIntermediateOpIndex ) return undefined ;
58
+
59
+ const newElement = document . querySelector (
60
+ `.stdcm-vias-bundle:nth-child(${ newIntermediateOpIndex } ) > :last-child`
61
+ ) ;
62
+
63
+ if ( ! newElement ) return undefined ;
64
+
65
+ let requestId : number ;
66
+
67
+ const scrollWithAnimation = ( ) => {
68
+ newElement . scrollIntoView ( {
69
+ block : 'nearest' ,
70
+ behavior : 'auto' ,
71
+ } ) ;
72
+
73
+ requestId = requestAnimationFrame ( scrollWithAnimation ) ;
74
+ } ;
75
+
76
+ requestId = requestAnimationFrame ( scrollWithAnimation ) ;
77
+
78
+ const cancelListener = ( ) => cancelAnimationFrame ( requestId ) ;
79
+
80
+ newElement . addEventListener ( 'animationend' , cancelListener ) ;
81
+ return ( ) => {
82
+ newElement . removeEventListener ( 'animationend' , cancelListener ) ;
83
+ cancelListener ( ) ;
84
+ } ;
85
+ } , [ newIntermediateOpIndex ] ) ;
86
+
47
87
const updateStopDuration = ( stopTime : string , pathStep : StdcmPathStep ) => {
48
88
const stopFor = stopTime ? Number ( stopTime ) : undefined ;
49
89
dispatch (
@@ -60,6 +100,7 @@ const StdcmVias = ({ disabled = false }: StdcmConfigCardProps) => {
60
100
61
101
const addViaOnClick = ( pathStepIndex : number ) => {
62
102
dispatch ( addStdcmVia ( pathStepIndex ) ) ;
103
+ setNewIntermediateOpIndex ( pathStepIndex ) ;
63
104
} ;
64
105
65
106
return (
0 commit comments