From fbb5866b0176113f1cb1a65cd528820ddbe78f24 Mon Sep 17 00:00:00 2001 From: Antoni Czaplicki Date: Fri, 3 Jul 2026 00:06:52 +0200 Subject: [PATCH] fix(wrapped): prevent keyboard navigation when interactive elements or dialogs are active --- src/app/wrapped/use-wrapped-player.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/app/wrapped/use-wrapped-player.ts b/src/app/wrapped/use-wrapped-player.ts index 9a79633e..971420f8 100644 --- a/src/app/wrapped/use-wrapped-player.ts +++ b/src/app/wrapped/use-wrapped-player.ts @@ -14,6 +14,21 @@ function clampIndex(index: number, count: number): number { return Math.max(0, Math.min(max, index)); } +const INTERACTIVE_KEYBOARD_TARGET = + 'a[href], button, input, select, textarea, [contenteditable="true"], [role="button"], [role="checkbox"], [role="combobox"], [role="radio"], [role="searchbox"], [role="switch"], [role="textbox"]'; + +function hasInteractiveKeyboardTarget(event: KeyboardEvent): boolean { + return event.composedPath().some((target) => { + if (!(target instanceof HTMLElement)) { + return false; + } + + return ( + target.isContentEditable || target.matches(INTERACTIVE_KEYBOARD_TARGET) + ); + }); +} + interface UseWrappedPlayerOptions { count: number; getDuration: (index: number) => number; @@ -150,6 +165,13 @@ export function useWrappedPlayer({ // Keyboard control. useEffect(() => { const onKey = (event: KeyboardEvent) => { + if ( + hasInteractiveKeyboardTarget(event) || + document.querySelector('[role="dialog"]') !== null + ) { + return; + } + if ( event.key === "ArrowRight" || event.key === " " ||