Files
main_frontend/src/shared/hooks/useSegmentResize.ts
T
2026-02-27 23:34:17 +03:00

91 lines
2.5 KiB
TypeScript

import { useCallback, useRef, useState } from "react"
export type ResizeEdge = "left" | "right"
interface UseSegmentResizeOptions {
pixelsPerSecond: number
onResize: (index: number, edge: ResizeEdge, deltaSec: number) => void
onResizeEnd: (index: number, edge: ResizeEdge) => void
}
export function useSegmentResize({
pixelsPerSecond,
onResize,
onResizeEnd,
}: UseSegmentResizeOptions) {
const [resizingIndex, setResizingIndex] = useState(-1)
const [resizingEdge, setResizingEdge] = useState<ResizeEdge | null>(null)
const startXRef = useRef(0)
const accDeltaRef = useRef(0)
const indexRef = useRef(-1)
const edgeRef = useRef<ResizeEdge>("left")
const ppsRef = useRef(pixelsPerSecond)
ppsRef.current = pixelsPerSecond
// Use refs for callbacks to avoid stale closures in event listeners
const onResizeRef = useRef(onResize)
onResizeRef.current = onResize
const onResizeEndRef = useRef(onResizeEnd)
onResizeEndRef.current = onResizeEnd
const justResizedRef = useRef(false)
// Stable references that never change — safe for addEventListener/removeEventListener
const moveHandlerRef = useRef<((e: PointerEvent) => void) | null>(null)
const upHandlerRef = useRef<((e: PointerEvent) => void) | null>(null)
if (!moveHandlerRef.current) {
moveHandlerRef.current = (e: PointerEvent) => {
const dx = e.clientX - startXRef.current
const deltaSec = dx / ppsRef.current
const prevDelta = accDeltaRef.current
const incrementalDelta = deltaSec - prevDelta
accDeltaRef.current = deltaSec
onResizeRef.current(indexRef.current, edgeRef.current, incrementalDelta)
}
}
if (!upHandlerRef.current) {
upHandlerRef.current = (e: PointerEvent) => {
document.removeEventListener("pointermove", moveHandlerRef.current!)
document.removeEventListener("pointerup", upHandlerRef.current!)
onResizeEndRef.current(indexRef.current, edgeRef.current)
setResizingIndex(-1)
setResizingEdge(null)
justResizedRef.current = true
setTimeout(() => {
justResizedRef.current = false
}, 200)
}
}
const handlePointerDown = useCallback(
(e: React.PointerEvent, index: number, edge: ResizeEdge) => {
e.stopPropagation()
e.preventDefault()
startXRef.current = e.clientX
accDeltaRef.current = 0
indexRef.current = index
edgeRef.current = edge
setResizingIndex(index)
setResizingEdge(edge)
document.addEventListener("pointermove", moveHandlerRef.current!)
document.addEventListener("pointerup", upHandlerRef.current!)
},
[],
)
return {
handlePointerDown,
resizingIndex,
resizingEdge,
justResizedRef,
}
}