import React, { useEffect, useRef, useState } from "react"; import type { RefObject } from "react"; interface UseTimelineKeyboardOptions { scrollContainerRef: RefObject; cellWidth: number; selectedAllocationIds: string[]; onDeleteSelected: () => void; } export interface UseTimelineKeyboardResult { showShortcuts: boolean; setShowShortcuts: React.Dispatch>; } function isTypingTarget(el: Element | null): boolean { if (!el) return false; const tag = el.tagName.toLowerCase(); if (tag === "input" || tag === "textarea" || tag === "select") return true; if ((el as HTMLElement).isContentEditable) return true; return false; } export function useTimelineKeyboard({ scrollContainerRef, cellWidth, selectedAllocationIds, onDeleteSelected, }: UseTimelineKeyboardOptions): UseTimelineKeyboardResult { const [showShortcuts, setShowShortcuts] = useState(false); // Keep stable refs so the handler closure doesn't need to be re-registered on every render const onDeleteRef = useRef(onDeleteSelected); onDeleteRef.current = onDeleteSelected; const selectedCountRef = useRef(selectedAllocationIds.length); selectedCountRef.current = selectedAllocationIds.length; const cellWidthRef = useRef(cellWidth); cellWidthRef.current = cellWidth; useEffect(() => { const handler = (e: KeyboardEvent) => { if (isTypingTarget(document.activeElement)) return; const isMac = navigator.platform.toUpperCase().includes("MAC"); const modKey = isMac ? e.metaKey : e.ctrlKey; const dayPx = cellWidthRef.current; const el = scrollContainerRef.current; switch (e.key) { case "?": e.preventDefault(); setShowShortcuts((prev) => !prev); break; case "ArrowLeft": if (el) { e.preventDefault(); el.scrollLeft -= (e.shiftKey ? 7 : 1) * dayPx; } break; case "ArrowRight": if (el) { e.preventDefault(); el.scrollLeft += (e.shiftKey ? 7 : 1) * dayPx; } break; case "Delete": case "Backspace": if (modKey) break; // let browser handle Cmd+Backspace etc. if (selectedCountRef.current > 0) { e.preventDefault(); onDeleteRef.current(); } break; default: break; } }; window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, [scrollContainerRef]); // stable — uses refs for everything that changes return { showShortcuts, setShowShortcuts }; }