Split 1175-LOC workflow page into _components, _hooks and _types modules. page.tsx now 256 LOC (wire-up only). Behavior preserved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
56 lines
1.8 KiB
TypeScript
56 lines
1.8 KiB
TypeScript
'use client'
|
|
|
|
import { useCallback, useEffect, useRef, RefObject } from 'react'
|
|
|
|
export function useSyncScroll(
|
|
leftPanelRef: RefObject<HTMLDivElement | null>,
|
|
rightPanelRef: RefObject<HTMLDivElement | null>,
|
|
deps: unknown[],
|
|
) {
|
|
const isScrolling = useRef(false)
|
|
|
|
const setupSyncScroll = useCallback(() => {
|
|
const leftPanel = leftPanelRef.current
|
|
const rightPanel = rightPanelRef.current
|
|
|
|
if (!leftPanel || !rightPanel) return
|
|
|
|
const handleLeftScroll = () => {
|
|
if (isScrolling.current) return
|
|
isScrolling.current = true
|
|
|
|
const leftScrollPercent = leftPanel.scrollTop / (leftPanel.scrollHeight - leftPanel.clientHeight || 1)
|
|
const rightMaxScroll = rightPanel.scrollHeight - rightPanel.clientHeight
|
|
rightPanel.scrollTop = leftScrollPercent * rightMaxScroll
|
|
|
|
setTimeout(() => { isScrolling.current = false }, 10)
|
|
}
|
|
|
|
const handleRightScroll = () => {
|
|
if (isScrolling.current) return
|
|
isScrolling.current = true
|
|
|
|
const rightScrollPercent = rightPanel.scrollTop / (rightPanel.scrollHeight - rightPanel.clientHeight || 1)
|
|
const leftMaxScroll = leftPanel.scrollHeight - leftPanel.clientHeight
|
|
leftPanel.scrollTop = rightScrollPercent * leftMaxScroll
|
|
|
|
setTimeout(() => { isScrolling.current = false }, 10)
|
|
}
|
|
|
|
leftPanel.addEventListener('scroll', handleLeftScroll)
|
|
rightPanel.addEventListener('scroll', handleRightScroll)
|
|
|
|
return () => {
|
|
leftPanel.removeEventListener('scroll', handleLeftScroll)
|
|
rightPanel.removeEventListener('scroll', handleRightScroll)
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
const cleanup = setupSyncScroll()
|
|
return cleanup
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [setupSyncScroll, ...deps])
|
|
}
|