type MutableCurrent = { current: T; }; type AttachDocumentMouseDrag = ( documentTarget: Document, onMove: (event: MouseEvent) => void, onUp: (event: MouseEvent) => void, ) => () => void; type MultiDragStateLike = { selectedAllocationIds: string[]; multiDragDaysDelta: number; isMultiDragging: boolean; multiDragMode: TMode; }; type BeginAllocationMultiDragSessionParams = { startMouseX: number; dragMode: TMode; documentTarget: Document; cleanupRef: MutableCurrent<(() => void) | null>; stateRef: MutableCurrent; setState: (state: TState) => void; startState: (state: TState, dragMode: TMode) => TState; updateState: (state: TState, daysDelta: number) => TState | null; finalizeState: (state: TState) => TState; toDaysDelta: (deltaX: number) => number; onComplete: (daysDelta: number, finalState: TState) => void; attachDrag: AttachDocumentMouseDrag; }; export function beginAllocationMultiDragSession({ startMouseX, dragMode, documentTarget, cleanupRef, stateRef, setState, startState, updateState, finalizeState, toDaysDelta, onComplete, attachDrag, }: BeginAllocationMultiDragSessionParams) { const initialState = startState(stateRef.current, dragMode); stateRef.current = initialState; setState(initialState); cleanupRef.current?.(); function handleMove(event: MouseEvent) { const updated = updateState(stateRef.current, toDaysDelta(event.clientX - startMouseX)); if (!updated) return; stateRef.current = updated; setState(updated); } function handleUp(event: MouseEvent) { cleanupRef.current?.(); cleanupRef.current = null; const finalState = finalizeState(stateRef.current); stateRef.current = finalState; setState(finalState); const finalDelta = toDaysDelta(event.clientX - startMouseX); if (finalDelta !== 0) { onComplete(finalDelta, finalState); } } cleanupRef.current = attachDrag(documentTarget, handleMove, handleUp); }