import { useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; interface Props { /** drag 中に呼ばれる。upperPct (0..100) を渡す。 */ onResize: (upperPct: number) => void; /** drag 終了時に 1 度だけ呼ばれる。localStorage 保存用。 */ onResizeEnd: (upperPct: number) => void; /** double-click でリセット。 */ onReset?: () => void; /** 上下のパネルを含む親要素を識別する data-* selector。 */ parentSelector: string; minUpperPct?: number; minLowerPct?: number; } export function VerticalResizeHandle({ onResize, onResizeEnd, onReset, parentSelector, minUpperPct = 20, minLowerPct = 15, }: Props) { const { t } = useTranslation('layout'); const onResizeRef = useRef(onResize); const onResizeEndRef = useRef(onResizeEnd); onResizeRef.current = onResize; onResizeEndRef.current = onResizeEnd; const draggingRef = useRef(false); const lastPctRef = useRef(null); useEffect(() => { const handleMove = (e: PointerEvent) => { if (!draggingRef.current) return; const parent = document.querySelector(parentSelector); if (!parent) return; const rect = parent.getBoundingClientRect(); const raw = ((e.clientY - rect.top) / rect.height) * 100; const clamped = Math.max(minUpperPct, Math.min(100 - minLowerPct, raw)); lastPctRef.current = clamped; onResizeRef.current(clamped); }; const handleUp = () => { if (!draggingRef.current) return; draggingRef.current = false; document.body.style.cursor = ''; document.body.style.userSelect = ''; if (lastPctRef.current !== null) onResizeEndRef.current(lastPctRef.current); }; window.addEventListener('pointermove', handleMove); window.addEventListener('pointerup', handleUp); window.addEventListener('pointercancel', handleUp); return () => { window.removeEventListener('pointermove', handleMove); window.removeEventListener('pointerup', handleUp); window.removeEventListener('pointercancel', handleUp); }; }, [parentSelector, minUpperPct, minLowerPct]); return (
{ e.preventDefault(); draggingRef.current = true; document.body.style.cursor = 'row-resize'; document.body.style.userSelect = 'none'; }} onDoubleClick={onReset} className="cursor-row-resize bg-transparent hover:bg-slate-300/60 transition-colors flex justify-center group" style={{ height: 6, touchAction: 'none' }} >
); }