From 5e98b9fca66ba9f95b368cd439fda62b6fc732a8 Mon Sep 17 00:00:00 2001
From: iamdoubz <>
Date: Thu, 30 Jul 2026 10:12:13 -0500
Subject: [PATCH 1/4] Add hamburger menu to house Song List and Waveform Styles
---
assets/css/radio.css | 57 +++++++++++++++++++++---------------
assets/js/player.js | 51 +++++++++++++++++++++++++++-----
index.php | 69 ++++++++++++++++++++++++++------------------
3 files changed, 118 insertions(+), 59 deletions(-)
diff --git a/assets/css/radio.css b/assets/css/radio.css
index 7b7c9d5..3fdc1b1 100644
--- a/assets/css/radio.css
+++ b/assets/css/radio.css
@@ -160,22 +160,11 @@ audio[controls] { width: 100%; outline: 0; }
.waveform { width: 100%; display: block; border-radius: 8px; }
.waveform[hidden] { display: none; } /* [hidden] must beat display:block */
-.waveform-picker { position: absolute; top: 6px; right: 6px; z-index: 2; }
-.waveform-picker summary {
- list-style: none; padding: 4px 12px;
- background: rgba(0, 0, 0, 0.55); color: #fff;
- font-variant: small-caps; font-size: 0.8rem;
- border: 1px solid rgba(255, 255, 255, 0.35); border-radius: 999px;
- cursor: pointer; backdrop-filter: blur(4px);
- transition: background .15s ease;
-}
-.waveform-picker summary::-webkit-details-marker { display: none; }
-.waveform-picker summary:hover, .waveform-picker summary:focus { background: rgba(0, 0, 0, 0.8); outline: 0; }
-.waveform-picker[open] summary { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
-
-.waveform-picker[open] {
+.waveform-picker {
+ position: fixed; top: 64px; right: 16px; z-index: 30;
display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
}
+.waveform-picker[hidden] { display: none; }
.waveform-picker fieldset {
margin: 0; padding: 8px 10px;
background: rgba(0, 0, 0, 0.85); color: #fff;
@@ -266,15 +255,35 @@ audio[controls] { width: 100%; outline: 0; }
width: 0; height: 0; display: none;
}
-/* ----------------------------------------------------- history panel */
-.history-toggle {
- position: fixed; top: 16px; right: 16px; z-index: 30;
- padding: 8px 14px;
+/* ----------------------------------------------------------------- menu */
+.menu-wrap { position: fixed; top: 16px; right: 16px; z-index: 30; }
+.menu-toggle {
+ width: 40px; height: 40px;
+ display: flex; align-items: center; justify-content: center;
background: var(--accent); color: var(--accent-text);
- font-variant: small-caps; border: none; border-radius: 8px; cursor: pointer;
+ font-size: 1.1rem; line-height: 1;
+ border: none; border-radius: 8px; cursor: pointer;
}
-.history-toggle:hover, .history-toggle:focus { background: var(--accent-hover); outline: 0; }
+.menu-toggle:hover, .menu-toggle:focus { background: var(--accent-hover); outline: 0; }
+.menu-list {
+ position: absolute; top: calc(100% + 6px); right: 0; z-index: 30;
+ min-width: 170px; margin: 0; padding: 6px;
+ list-style: none;
+ background: rgba(0, 0, 0, 0.85); color: #fff;
+ border: 1px solid rgba(255, 255, 255, 0.35); border-radius: 10px;
+ backdrop-filter: blur(4px);
+}
+.menu-list[hidden] { display: none; }
+.menu-item {
+ display: block; width: 100%; text-align: left;
+ padding: 8px 10px; margin: 0;
+ background: none; border: none; border-radius: 6px;
+ color: inherit; font: inherit; font-size: 0.85rem; cursor: pointer;
+}
+.menu-item:hover, .menu-item:focus-visible { background: rgba(255, 255, 255, 0.15); outline: 0; }
+
+/* ----------------------------------------------------- history panel */
.history {
position: fixed; top: 0; right: 0; height: 100%;
width: min(90vw, 420px); padding: 64px 16px 16px;
@@ -308,11 +317,11 @@ audio[controls] { width: 100%; outline: 0; }
.history { width: min(96vw, 420px); }
/* -webkit-touch-callout is a no-op everywhere except iOS WebKit, so this
- @supports check targets iOS phones without any JS/UA sniffing. Picker is
- hidden there, so give the art a fake ambient glow (no audio analysis)
- in its place. */
+ @supports check targets iOS phones without any JS/UA sniffing. The menu
+ item is hidden there, so give the art a fake ambient glow (no audio
+ analysis) in its place. */
@supports (-webkit-touch-callout: none) {
- .waveform-picker { display: none; }
+ #menu-waveform { display: none; }
.album-art { animation: art-glow-pulse 3.2s ease-in-out infinite; }
}
}
diff --git a/assets/js/player.js b/assets/js/player.js
index 1dd68f0..f3b9061 100644
--- a/assets/js/player.js
+++ b/assets/js/player.js
@@ -39,14 +39,17 @@
lyricsLines: $('lyrics-lines'),
lyricsSync: $('lyrics-sync'),
lyricsOffset: $('lyrics-offset'),
- historyToggle:$('history-toggle'),
historyBody: $('history-body'),
history: $('history'),
listeners: $('listeners'),
canvas: $('tint-canvas'),
waveform: $('waveform'),
artRing: $('art-ring'),
- wavePicker: $('waveform-picker')
+ wavePicker: $('waveform-picker'),
+ menuToggle: $('menu-toggle'),
+ menuList: $('menu-list'),
+ menuHistory: $('menu-history'),
+ menuWaveform: $('menu-waveform')
};
var ICON = {
@@ -768,13 +771,47 @@
while (el.historyBody.children.length > 100) el.historyBody.removeChild(el.historyBody.lastChild);
}
- if (el.historyToggle && el.history) {
- el.historyToggle.addEventListener('click', function () {
- var open = el.history.classList.toggle('open');
- el.historyToggle.classList.toggle('open', open);
- el.historyToggle.textContent = open ? 'Close' : 'Song list';
+ /* --------------------------------------------------------- menu / popovers */
+ function closeMenu() {
+ if (!el.menuList) return;
+ el.menuList.hidden = true;
+ if (el.menuToggle) el.menuToggle.setAttribute('aria-expanded', 'false');
+ }
+
+ if (el.menuToggle && el.menuList) {
+ el.menuToggle.addEventListener('click', function (e) {
+ e.stopPropagation();
+ var open = el.menuList.hidden;
+ el.menuList.hidden = !open;
+ el.menuToggle.setAttribute('aria-expanded', open ? 'true' : 'false');
});
}
+ if (el.menuHistory && el.history) {
+ el.menuHistory.addEventListener('click', function (e) {
+ e.stopPropagation();
+ el.history.classList.toggle('open');
+ closeMenu();
+ });
+ }
+ if (el.menuWaveform && el.wavePicker) {
+ el.menuWaveform.addEventListener('click', function (e) {
+ e.stopPropagation();
+ el.wavePicker.hidden = !el.wavePicker.hidden;
+ closeMenu();
+ });
+ }
+ // Clicking anywhere outside the menu / history pane / waveform picker
+ // closes whichever of them is open (the triggers above stop propagation
+ // so this only fires for genuine outside clicks).
+ document.addEventListener('click', function (e) {
+ closeMenu();
+ if (el.history && el.history.classList.contains('open') && !el.history.contains(e.target)) {
+ el.history.classList.remove('open');
+ }
+ if (el.wavePicker && !el.wavePicker.hidden && !el.wavePicker.contains(e.target)) {
+ el.wavePicker.hidden = true;
+ }
+ });
/* ----------------------------------------------------------- listeners */
function updateListeners(data) {
diff --git a/index.php b/index.php
index ea78d4c..89253d4 100644
--- a/index.php
+++ b/index.php
@@ -58,33 +58,6 @@ $showArtRing = $waveActive && in_array('circular', $waveTypes, true) && $rich;
-
-
- Style
-
-
-
-
-
-
@@ -112,8 +85,48 @@ $showArtRing = $waveActive && in_array('circular', $waveTypes, true) && $rich;
+
+
+
+
+
+
+
+
+
+
+
+
+
-