feat(buttonNavigation): block any interactions while the selector is being animated

This commit is contained in:
2026-01-14 15:09:09 +01:00
parent d757945bcd
commit 03bbc25f4b

View File

@@ -1,5 +1,7 @@
import gsap from "gsap"; import gsap from "gsap";
const SELECTOR_SPEED = 500;
export const useButtonNavigation = <T extends Record<string, Rect>>({ export const useButtonNavigation = <T extends Record<string, Rect>>({
buttons, buttons,
initialButton, initialButton,
@@ -29,6 +31,11 @@ export const useButtonNavigation = <T extends Record<string, Rect>>({
const selectedButton = ref(initialButton); const selectedButton = ref(initialButton);
const selectorPosition = ref<Rect>(buttons[initialButton]!); const selectorPosition = ref<Rect>(buttons[initialButton]!);
const nextButton = ref<Entry | undefined>(); const nextButton = ref<Entry | undefined>();
const isAnimating = ref(false);
const blockInteractions = computed(
() => confirmationModal.isOpen || disabled?.value || isAnimating.value,
);
const getNavigationTarget = ( const getNavigationTarget = (
value: Entry | [buttonName: Entry, blocked: boolean] | undefined, value: Entry | [buttonName: Entry, blocked: boolean] | undefined,
@@ -149,10 +156,14 @@ export const useButtonNavigation = <T extends Record<string, Rect>>({
}; };
const animateToButton = (targetButton: Entry, path?: Array<Entry> | null) => { const animateToButton = (targetButton: Entry, path?: Array<Entry> | null) => {
const SPEED = 400;
const pathButtons = path && path.length > 0 ? path : [targetButton]; const pathButtons = path && path.length > 0 ? path : [targetButton];
const timeline = gsap.timeline(); isAnimating.value = true;
const timeline = gsap.timeline({
onComplete: () => {
isAnimating.value = false;
},
});
let prevRect = selectorPosition.value; let prevRect = selectorPosition.value;
selectorPosition.value = [...selectorPosition.value]; selectorPosition.value = [...selectorPosition.value];
@@ -160,7 +171,7 @@ export const useButtonNavigation = <T extends Record<string, Rect>>({
for (const button of pathButtons) { for (const button of pathButtons) {
const buttonRect = buttons[button]!; const buttonRect = buttons[button]!;
const distance = calculateDistance(prevRect, buttonRect); const distance = calculateDistance(prevRect, buttonRect);
const duration = distance / SPEED; const duration = distance / SELECTOR_SPEED;
timeline.to( timeline.to(
selectorPosition.value, selectorPosition.value,
@@ -184,7 +195,7 @@ export const useButtonNavigation = <T extends Record<string, Rect>>({
const { onClick } = useScreen(); const { onClick } = useScreen();
onClick((x: number, y: number) => { onClick((x: number, y: number) => {
if (confirmationModal.isOpen || disabled?.value) return; if (blockInteractions.value) return;
for (const [buttonName, buttonRect] of Object.entries(buttons) as [ for (const [buttonName, buttonRect] of Object.entries(buttons) as [
Entry, Entry,
@@ -214,7 +225,7 @@ export const useButtonNavigation = <T extends Record<string, Rect>>({
}); });
useKeyDown((key) => { useKeyDown((key) => {
if (confirmationModal.isOpen || disabled?.value) return; if (blockInteractions.value) return;
const currentButton = selectedButton.value as Entry; const currentButton = selectedButton.value as Entry;
const currentNav = navigation[currentButton]; const currentNav = navigation[currentButton];