From 22e3f67411023b4f719548e9b5bf504564240026 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sun, 3 May 2026 22:23:20 -0400 Subject: [PATCH] feat(web/m7-364): drawer focus management + grip Enter/Space handling --- web/src/lib/components/QueueDrawer.svelte | 16 ++++++++++++++++ web/src/lib/components/QueueTrackRow.svelte | 6 ++++++ web/src/lib/components/QueueTrackRow.test.ts | 14 ++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/web/src/lib/components/QueueDrawer.svelte b/web/src/lib/components/QueueDrawer.svelte index a69b41db..924b3f26 100644 --- a/web/src/lib/components/QueueDrawer.svelte +++ b/web/src/lib/components/QueueDrawer.svelte @@ -3,12 +3,27 @@ import { player, closeQueueDrawer } from '$lib/player/store.svelte'; import QueueTrackRow from './QueueTrackRow.svelte'; + let previouslyFocused: HTMLElement | null = null; + let closeButton: HTMLButtonElement | undefined = $state(); + function totalDurationLabel(tracks: { duration_sec: number }[]): string { const totalSec = tracks.reduce((s, tr) => s + (tr.duration_sec ?? 0), 0); const m = Math.floor(totalSec / 60); const h = Math.floor(m / 60); return h > 0 ? `${h}h ${m % 60}m` : `${m} min`; } + + $effect(() => { + if (player.queueDrawerOpen) { + previouslyFocused = document.activeElement as HTMLElement | null; + // Defer to next microtask so the close button is rendered + visible + // before we focus (the drawer may still be transitioning in). + Promise.resolve().then(() => closeButton?.focus()); + } else if (previouslyFocused) { + previouslyFocused.focus(); + previouslyFocused = null; + } + }); {#if player.queueDrawerOpen} @@ -38,6 +53,7 @@