new features
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user