export type ButtonConfig = [x: number, y: number, w: number, h: number]; export const useButtonNavigation = >({ buttons, initialButton, onButtonClick, navigation, }: { buttons: T; initialButton: keyof T; onButtonClick?: (buttonName: keyof T) => void; navigation: Record< keyof T, { up?: keyof T; down?: keyof T | "last"; left?: keyof T; right?: keyof T; horizontalMode?: "navigate" | "preview"; } >; }) => { const selectedButton = ref(initialButton); const selectorPosition = computed(() => buttons[selectedButton.value]!); const nextButton = ref(); useScreenClick((x: number, y: number) => { for (const [buttonName, config] of Object.entries(buttons) as [ keyof T, ButtonConfig, ][]) { const [sx, sy, sw, sh] = config; if (x >= sx && x <= sx + sw && y >= sy && y <= sy + sh) { if (selectedButton.value === buttonName) { onButtonClick?.(buttonName); } else { if ( navigation[buttonName].down === "last" && navigation[selectedButton.value]!.up === buttonName ) { nextButton.value = selectedButton.value; } selectedButton.value = buttonName; } break; } } }); const handleKeyPress = (event: KeyboardEvent) => { const currentButton = selectedButton.value as keyof T; const currentNav = navigation[currentButton]; if (!currentNav) return; switch (event.key) { case "ArrowUp": if (!currentNav.up) return; if (currentNav.up === "last") { selectedButton.value = nextButton.value; } else { nextButton.value = selectedButton.value as keyof T; selectedButton.value = currentNav.up; } break; case "ArrowDown": if (!currentNav.down) return; if (currentNav.down === "last") { if (nextButton.value) { selectedButton.value = nextButton.value; } else { selectedButton.value = currentNav.left ?? currentNav.right; } } else { selectedButton.value = currentNav.down; } break; case "ArrowLeft": if (!currentNav.left) return; if (currentNav.horizontalMode === "preview") { nextButton.value = currentNav.left; } else { selectedButton.value = currentNav.left; } break; case "ArrowRight": if (!currentNav.right) return; if (currentNav.horizontalMode === "preview") { nextButton.value = currentNav.right; } else { selectedButton.value = currentNav.right; } break; case "Enter": case " ": onButtonClick?.(selectedButton.value); break; default: return; } event.preventDefault(); }; onMounted(() => { window.addEventListener("keydown", handleKeyPress); }); onUnmounted(() => { window.removeEventListener("keydown", handleKeyPress); }); return { selectedButton, selectorPosition, }; };