import { ref, onMounted, onUnmounted } from "vue"; import type { Ref } from "vue"; export function useListKeyboardNavigation( items: Ref, navigateTo: (item: T) => void, rowSelector = ".kb-active-item", enabled?: Ref, ) { const activeIndex = ref(-1); function onKeydown(e: KeyboardEvent) { if (enabled && !enabled.value) return; const el = document.activeElement; const tag = el ? (el as HTMLElement).tagName : ""; const isInput = tag === "INPUT" || tag === "TEXTAREA" || (el as HTMLElement)?.isContentEditable; if (isInput || e.ctrlKey || e.metaKey || e.altKey) return; const count = items.value.length; if (e.key === "j") { e.preventDefault(); activeIndex.value = Math.min(activeIndex.value + 1, count - 1); document.querySelector(rowSelector)?.scrollIntoView({ block: "nearest" }); } else if (e.key === "k") { e.preventDefault(); activeIndex.value = Math.max(activeIndex.value - 1, 0); document.querySelector(rowSelector)?.scrollIntoView({ block: "nearest" }); } else if (e.key === "Enter" && activeIndex.value >= 0) { const item = items.value[activeIndex.value]; if (item) navigateTo(item); } } onMounted(() => document.addEventListener("keydown", onKeydown)); onUnmounted(() => document.removeEventListener("keydown", onKeydown)); return { activeIndex }; }