From f4f26d6d34b6f164cf183034f8592ae1db54d35c Mon Sep 17 00:00:00 2001
From: KeelrLeek <156356974+KeelrLeek@users.noreply.github.com>
Date: Mon, 17 Aug 2026 18:05:36 -0500
Subject: [PATCH] player bar: the overflow menu out from under the bar's own
clip, and Lyrics wired for real
---
src/components/PlayerBar.svelte | 115 ++++++++++++++++++++++----------
1 file changed, 79 insertions(+), 36 deletions(-)
diff --git a/src/components/PlayerBar.svelte b/src/components/PlayerBar.svelte
index 7920cf1..69e3d6c 100644
--- a/src/components/PlayerBar.svelte
+++ b/src/components/PlayerBar.svelte
@@ -215,25 +215,58 @@
}
// ---- the overflow menu -------------------------------------------------
+ // The popup renders OUTSIDE the footer and position:fixed (the SaveMenu
+ // idiom): the bar is overflow:hidden, so an absolute menu inside it got
+ // amputated at the bar's top edge — and what poked out still lost to the
+ // Now Playing panel's z-indexed layers. Fixed + measured coordinates
+ // escape both the clip and the stacking war. It must NOT live inside
+ // .extras: that element's centering transform would become a fixed
+ // element's containing block and hand it right back to the clip.
let menuOpen = $state(false);
let menuWrap = $state(null);
+ let menuEl = $state(null);
+ let menuPos = $state({ right: 0, bottom: 0 });
+ function toggleMenu() {
+ if (!menuOpen && menuWrap) {
+ const r = menuWrap.getBoundingClientRect();
+ // Right-aligned over the button, opening upward — the same seat
+ // the old absolute placement (bottom + right of the wrap) gave it.
+ menuPos = {
+ right: Math.max(8, window.innerWidth - r.right - 6),
+ bottom: window.innerHeight - r.top + 6,
+ };
+ }
+ menuOpen = !menuOpen;
+ }
$effect(() => {
if (!menuOpen) return;
const away = (e) => {
- if (menuWrap && !menuWrap.contains(e.target)) menuOpen = false;
+ const inside =
+ (menuWrap && menuWrap.contains(e.target)) ||
+ (menuEl && menuEl.contains(e.target));
+ if (!inside) menuOpen = false;
};
const esc = (e) => e.key === "Escape" && (menuOpen = false);
+ // The menu is anchored to coordinates measured at open; a resize
+ // moves the anchor out from under them, so it closes instead.
+ const close = () => (menuOpen = false);
window.addEventListener("pointerdown", away);
window.addEventListener("keydown", esc);
+ window.addEventListener("resize", close);
return () => {
window.removeEventListener("pointerdown", away);
window.removeEventListener("keydown", esc);
+ window.removeEventListener("resize", close);
};
});
function openQueue() {
menuOpen = false;
showNowPlaying(); // the queue lives in the panel; always reveal it
}
+ function openLyrics() {
+ menuOpen = false;
+ ui.lyricsFull = true; // the fullscreen overlay (LyricsOverlay.svelte)
+ }
// ---- Save to library, from the ⋯ menu ---------------------------------
// Only a stream can be saved; a single queued stream has one destination
@@ -1224,40 +1257,10 @@
title="More"
aria-haspopup="menu"
aria-expanded={menuOpen}
- onclick={() => (menuOpen = !menuOpen)}
+ onclick={toggleMenu}
>