11/* eslint-disable jsx-a11y/no-static-element-interactions */
22/* eslint-disable jsx-a11y/click-events-have-key-events */
3- import React , { useContext , useMemo , Fragment , useEffect } from 'react'
3+ import React , { useContext , useMemo , Fragment , useEffect , useRef } from 'react'
44import { WindowSizeContext } from 'src/contexts'
55import { GameNode , AnalyzedGame , Termination , BaseGame } from 'src/types'
66import { TuringGame } from 'src/types/turing'
@@ -68,6 +68,8 @@ export const MovesContainer: React.FC<Props> = (props) => {
6868 showVariations = true ,
6969 } = props
7070 const { isMobile } = useContext ( WindowSizeContext )
71+ const containerRef = useRef < HTMLDivElement > ( null )
72+ const currentMoveRef = useRef < HTMLDivElement > ( null )
7173
7274 // Helper function to determine if move indicators should be shown
7375 const shouldShowIndicators = ( node : GameNode | null ) => {
@@ -114,6 +116,17 @@ export const MovesContainer: React.FC<Props> = (props) => {
114116 return ( ) => window . removeEventListener ( 'keydown' , handleKeyDown )
115117 } , [ baseController . currentNode , baseController . goToNode ] )
116118
119+ // Auto-scroll to current move
120+ useEffect ( ( ) => {
121+ if ( currentMoveRef . current && containerRef . current ) {
122+ currentMoveRef . current . scrollIntoView ( {
123+ behavior : 'smooth' ,
124+ block : 'nearest' ,
125+ inline : 'nearest' ,
126+ } )
127+ }
128+ } , [ baseController . currentNode ] )
129+
117130 const moves = useMemo ( ( ) => {
118131 const nodes = mainLineNodes . slice ( 1 )
119132 const rows : ( GameNode | null ) [ ] [ ] = [ ]
@@ -187,7 +200,7 @@ export const MovesContainer: React.FC<Props> = (props) => {
187200
188201 if ( isMobile ) {
189202 return (
190- < div className = "w-full overflow-x-auto px-2" >
203+ < div ref = { containerRef } className = "w-full overflow-x-auto px-2" >
191204 < div className = "flex flex-row items-center gap-1" >
192205 { mobileMovePairs . map ( ( pair , pairIndex ) => (
193206 < React . Fragment key = { pairIndex } >
@@ -196,6 +209,11 @@ export const MovesContainer: React.FC<Props> = (props) => {
196209 </ div >
197210 { pair . whiteMove && (
198211 < div
212+ ref = {
213+ baseController . currentNode === pair . whiteMove
214+ ? currentMoveRef
215+ : null
216+ }
199217 onClick = { ( ) =>
200218 baseController . goToNode ( pair . whiteMove as GameNode )
201219 }
@@ -224,6 +242,11 @@ export const MovesContainer: React.FC<Props> = (props) => {
224242 ) }
225243 { pair . blackMove && (
226244 < div
245+ ref = {
246+ baseController . currentNode === pair . blackMove
247+ ? currentMoveRef
248+ : null
249+ }
227250 onClick = { ( ) =>
228251 baseController . goToNode ( pair . blackMove as GameNode )
229252 }
@@ -272,14 +295,20 @@ export const MovesContainer: React.FC<Props> = (props) => {
272295 }
273296
274297 return (
275- < div className = "red-scrollbar grid h-48 auto-rows-min grid-cols-5 overflow-y-auto overflow-x-hidden whitespace-nowrap rounded-sm bg-background-1/60 md:h-full md:w-full" >
298+ < div
299+ ref = { containerRef }
300+ className = "red-scrollbar grid h-48 auto-rows-min grid-cols-5 overflow-y-auto overflow-x-hidden whitespace-nowrap rounded-sm bg-background-1/60 md:h-full md:w-full"
301+ >
276302 { moves . map ( ( [ whiteNode , blackNode ] , index ) => {
277303 return (
278304 < >
279305 < span className = "flex h-7 items-center justify-center bg-background-2 text-sm text-secondary" >
280306 { ( whiteNode || blackNode ) ?. moveNumber }
281307 </ span >
282308 < div
309+ ref = {
310+ baseController . currentNode === whiteNode ? currentMoveRef : null
311+ }
283312 onClick = { ( ) => {
284313 if ( whiteNode ) baseController . goToNode ( whiteNode )
285314 } }
@@ -312,6 +341,9 @@ export const MovesContainer: React.FC<Props> = (props) => {
312341 />
313342 ) : null }
314343 < div
344+ ref = {
345+ baseController . currentNode === blackNode ? currentMoveRef : null
346+ }
315347 onClick = { ( ) => {
316348 if ( blackNode ) baseController . goToNode ( blackNode )
317349 } }
0 commit comments