import { useRef } from 'react'; import { useTranslation } from 'react-i18next'; /** Sidebar width bounds and default, in rem (issue #99). */ export const SIDEBAR_MIN_WIDTH_REM = 12; export const SIDEBAR_MAX_WIDTH_REM = 32; export const SIDEBAR_DEFAULT_WIDTH_REM = 16; /** Step for keyboard adjustment, in rem. */ const KEYBOARD_STEP_REM = 1; export function clampSidebarWidth(rem: number): number { if (!Number.isFinite(rem)) return SIDEBAR_DEFAULT_WIDTH_REM; return Math.min(SIDEBAR_MAX_WIDTH_REM, Math.max(SIDEBAR_MIN_WIDTH_REM, rem)); } interface SidebarResizerProps { widthRem: number; onResize: (widthRem: number) => void; } /** * Drag handle on the sidebar's right edge. Pointer-draggable, keyboard- * adjustable (arrow keys, Home/End), double-click resets to the default. */ export function SidebarResizer({ widthRem, onResize }: SidebarResizerProps): React.JSX.Element { const { t } = useTranslation(); const dragStart = useRef<{ pointerX: number; widthRem: number } | null>(null); function remToPx(): number { return parseFloat(getComputedStyle(document.documentElement).fontSize) || 16; } function handlePointerDown(event: React.PointerEvent): void { // preventDefault (drag/selection guard) also swallows dblclick, so the // double-click reset is detected via the click count here. event.preventDefault(); if (event.detail >= 2) { dragStart.current = null; onResize(SIDEBAR_DEFAULT_WIDTH_REM); return; } dragStart.current = { pointerX: event.clientX, widthRem }; event.currentTarget.setPointerCapture(event.pointerId); } function handlePointerMove(event: React.PointerEvent): void { if (!dragStart.current) return; const deltaRem = (event.clientX - dragStart.current.pointerX) / remToPx(); onResize(clampSidebarWidth(dragStart.current.widthRem + deltaRem)); } function handlePointerUp(event: React.PointerEvent): void { dragStart.current = null; event.currentTarget.releasePointerCapture(event.pointerId); } function handleKeyDown(event: React.KeyboardEvent): void { let next: number | null = null; if (event.key === 'ArrowLeft') next = widthRem - KEYBOARD_STEP_REM; else if (event.key === 'ArrowRight') next = widthRem + KEYBOARD_STEP_REM; else if (event.key === 'Home') next = SIDEBAR_MIN_WIDTH_REM; else if (event.key === 'End') next = SIDEBAR_MAX_WIDTH_REM; if (next === null) return; event.preventDefault(); onResize(clampSidebarWidth(next)); } return (
onResize(SIDEBAR_DEFAULT_WIDTH_REM)} onKeyDown={handleKeyDown} /> ); }