perfect-postcode/frontend/src/hooks/usePaneResize.ts
Andras Schmelczer b94cf17d75
Some checks failed
CI / Python (lint + test) (push) Failing after 1m39s
CI / Frontend (lint + typecheck) (push) Failing after 1m49s
CI / Rust (lint + test) (push) Failing after 1m50s
Build and publish Docker image / build-and-push (push) Failing after 3m9s
Lots of improvements
2026-04-04 10:45:48 +01:00

93 lines
2.8 KiB
TypeScript

import { useState, useCallback, useRef } from 'react';
interface PaneResizeHandlers {
onPointerDown: (e: React.PointerEvent) => void;
onPointerMove: (e: React.PointerEvent) => void;
onPointerUp: () => void;
}
export function usePaneResize(
initialSize: number,
minSize: number,
maxSize: number,
side: 'left' | 'right' | 'top' | 'bottom'
): [number, PaneResizeHandlers, React.RefCallback<HTMLElement>] {
const [size, setSize] = useState(initialSize);
const draggingRef = useRef(false);
const liveSizeRef = useRef(initialSize);
const targetRef = useRef<HTMLElement | null>(null);
const containerOffsetRef = useRef(0);
const containerSizeRef = useRef(0);
const isVertical = side === 'top' || side === 'bottom';
const styleProp = isVertical ? 'height' : 'width';
const targetCallbackRef = useCallback((el: HTMLElement | null) => {
targetRef.current = el;
}, []);
const computeSize = useCallback(
(e: React.PointerEvent): number => {
if (isVertical) {
const total = containerSizeRef.current || window.innerHeight;
const resolvedMax = maxSize <= 1 ? total * maxSize : maxSize;
const pos = e.clientY - containerOffsetRef.current;
return side === 'top'
? Math.min(resolvedMax, Math.max(minSize, pos))
: Math.min(resolvedMax, Math.max(minSize, total - pos));
} else {
const resolvedMax = maxSize <= 1 ? window.innerWidth * maxSize : maxSize;
return side === 'left'
? Math.min(resolvedMax, Math.max(minSize, e.clientX))
: Math.min(resolvedMax, Math.max(minSize, window.innerWidth - e.clientX));
}
},
[side, isVertical, minSize, maxSize]
);
const handlePointerDown = useCallback(
(e: React.PointerEvent) => {
e.preventDefault();
(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
draggingRef.current = true;
if (isVertical) {
const container = (e.currentTarget as HTMLElement).parentElement;
if (container) {
const rect = container.getBoundingClientRect();
containerOffsetRef.current = rect.top;
containerSizeRef.current = rect.height;
}
}
},
[isVertical]
);
const handlePointerMove = useCallback(
(e: React.PointerEvent) => {
if (!draggingRef.current) return;
const newSize = computeSize(e);
liveSizeRef.current = newSize;
if (targetRef.current) {
targetRef.current.style[styleProp] = `${newSize}px`;
} else {
setSize(newSize);
}
},
[computeSize, styleProp]
);
const handlePointerUp = useCallback(() => {
draggingRef.current = false;
setSize(liveSizeRef.current);
}, []);
return [
size,
{
onPointerDown: handlePointerDown,
onPointerMove: handlePointerMove,
onPointerUp: handlePointerUp,
},
targetCallbackRef,
];
}