위젯영역 리사이즈

This commit is contained in:
Macbook
2026-06-14 01:18:10 +09:00
parent 57677ef39c
commit a3dcfe5cb2
5 changed files with 336 additions and 22 deletions
@@ -0,0 +1,112 @@
import { useCallback, useEffect, useRef } from 'react';
import {
adjustAdjacentGridFr,
FLOATING_WIDGET_CELL_MIN_H,
FLOATING_WIDGET_CELL_MIN_W,
FLOATING_WIDGET_GRID_GAP,
} from '../types/floatingWidget';
import { bindWindowDrag, isPrimaryPointerButton } from '../utils/pointerDrag';
type SplitAxis = 'col' | 'row';
interface Options {
onColFrChange: (next: number[]) => void;
onRowFrChange: (next: number[]) => void;
getColFr: () => number[];
getRowFr: () => number[];
getBodySize: () => { width: number; height: number };
}
export function useFloatingWidgetGridSplit({
onColFrChange,
onRowFrChange,
getColFr,
getRowFr,
getBodySize,
}: Options) {
const unbindDragRef = useRef<(() => void) | null>(null);
const endDrag = useCallback(() => {
unbindDragRef.current?.();
unbindDragRef.current = null;
document.body.style.cursor = '';
document.body.style.userSelect = '';
}, []);
useEffect(() => () => {
unbindDragRef.current?.();
document.body.style.cursor = '';
document.body.style.userSelect = '';
}, []);
const startSplit = useCallback((
e: React.PointerEvent,
axis: SplitAxis,
index: number,
) => {
if (!isPrimaryPointerButton(e)) return;
e.preventDefault();
e.stopPropagation();
const handle = e.currentTarget as HTMLElement;
try {
handle.setPointerCapture(e.pointerId);
} catch {
/* ignore */
}
handle.classList.add('fw-grid-splitter--active');
const startX = e.clientX;
const startY = e.clientY;
const startColFr = [...getColFr()];
const startRowFr = [...getRowFr()];
const startSize = getBodySize();
document.body.style.cursor = axis === 'col' ? 'col-resize' : 'row-resize';
document.body.style.userSelect = 'none';
unbindDragRef.current?.();
unbindDragRef.current = bindWindowDrag(
(xy) => {
if (axis === 'col') {
const delta = xy.x - startX;
onColFrChange(adjustAdjacentGridFr(
startColFr,
index,
delta,
startSize.width,
FLOATING_WIDGET_GRID_GAP,
FLOATING_WIDGET_CELL_MIN_W,
));
return;
}
const delta = xy.y - startY;
onRowFrChange(adjustAdjacentGridFr(
startRowFr,
index,
delta,
startSize.height,
FLOATING_WIDGET_GRID_GAP,
FLOATING_WIDGET_CELL_MIN_H,
));
},
() => {
handle.classList.remove('fw-grid-splitter--active');
endDrag();
},
{ preventTouchScroll: true },
);
}, [endDrag, getBodySize, getColFr, getRowFr, onColFrChange, onRowFrChange]);
const onColSplitPointerDown = useCallback(
(index: number) => (e: React.PointerEvent) => startSplit(e, 'col', index),
[startSplit],
);
const onRowSplitPointerDown = useCallback(
(index: number) => (e: React.PointerEvent) => startSplit(e, 'row', index),
[startSplit],
);
return { onColSplitPointerDown, onRowSplitPointerDown };
}