import { useEffect, useState } from 'react'; import { useMediaSettingsStore } from '@/stores/media-settings-store'; interface UseListKeyboardNavOptions { items: Array<{ id: string }>; enabled: boolean; onOpen: (id: string) => void; } /** * Shared keyboard grammar for browsable lists (network root, folders): * ↑/↓ move selection, Enter opens, 1–9 jump, V toggles video/audio. */ export function useListKeyboardNav({ items, enabled, onOpen, }: UseListKeyboardNavOptions) { const [selectedIndex, setSelectedIndex] = useState( items.length > 0 ? 0 : null, ); const [prevItemCount, setPrevItemCount] = useState(items.length); const recordingMode = useMediaSettingsStore((s) => s.recordingMode); const setRecordingMode = useMediaSettingsStore((s) => s.setRecordingMode); // Select the first item once they load and clear when empty — but not on // every Firestore update, which would scroll the list back to the top. if (items.length !== prevItemCount) { setPrevItemCount(items.length); if (items.length === 0) { setSelectedIndex(null); } else if (selectedIndex === null) { setSelectedIndex(0); } } useEffect(() => { if (!enabled || items.length === 0) return; const handleKeyDown = (e: KeyboardEvent) => { if (e.defaultPrevented) return; const target = e.target as HTMLElement; if ( target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable ) { return; } // Number keys 1-9: immediate navigation const digit = parseInt(e.key, 10); if (digit >= 1 && digit <= 9) { const index = digit - 1; if (index < items.length) { e.preventDefault(); onOpen(items[index].id); } return; } // Enter: open selected if (e.key === 'Enter') { setSelectedIndex((idx) => { if (idx !== null && idx < items.length) { e.preventDefault(); onOpen(items[idx].id); } return idx; }); return; } // V: toggle video / audio if (e.key === 'v' || e.key === 'V') { e.preventDefault(); setRecordingMode(recordingMode === 'video' ? 'audio' : 'video'); return; } // Arrow keys: move selection let delta: number | null = null; if (e.key === 'ArrowDown') delta = 1; else if (e.key === 'ArrowUp') delta = -1; if (delta !== null) { e.preventDefault(); setSelectedIndex((prev) => { if (prev === null) return 0; const next = prev + delta; return Math.max(0, Math.min(next, items.length - 1)); }); } }; // Use capture phase so arrow keys are intercepted before Radix UI // components (ToggleGroup, etc.) consume them for their own navigation. window.addEventListener('keydown', handleKeyDown, true); return () => window.removeEventListener('keydown', handleKeyDown, true); }, [enabled, items, onOpen, recordingMode, setRecordingMode]); return { selectedIndex }; }