Add hamburger menu to house Song List and Waveform Styles

This commit is contained in:
iamdoubz committed 2026-07-30 10:12:13 -05:00
1 parent 565585c95b
commit 5e98b9fca6
3 files changed
+118 -59

No files matched your search

+33 -24
View File
@@ -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; }
}
}
+44 -7
View File
@@ -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) {
+41 -28
View File
@@ -58,33 +58,6 @@ $showArtRing = $waveActive && in_array('circular', $waveTypes, true) && $rich;
<?php if ($waveActive): ?>
<div class="waveform-wrap">
<canvas id="waveform" class="waveform" style="height:<?= (int) cfg('waveform.height') ?>px" hidden aria-hidden="true"></canvas>
<?php if ($showWavePicker): ?>
<details class="waveform-picker" id="waveform-picker">
<summary>Style</summary>
<fieldset>
<legend>Below art</legend>
<?php
$stripLabels = ['none' => 'None', 'bars' => 'Bars', 'line' => 'Line', 'vu' => 'VU',
'spectrum' => 'Spectrum', 'spectrum-mirror' => 'Spectrum (mirror)', 'blob' => 'Blob'];
foreach ($stripLabels as $val => $label):
if ($val !== 'none' && !in_array($val, $waveTypes, true)) continue;
?>
<label><input type="radio" name="wave-strip" value="<?= e($val) ?>"/> <?= e($label) ?></label>
<?php endforeach; ?>
</fieldset>
<?php if ($showArtRing || in_array('glow', $waveTypes, true)): ?>
<fieldset>
<legend>On art</legend>
<?php if ($showArtRing): ?>
<label><input type="checkbox" id="wave-circular"/> Ring</label>
<?php endif; ?>
<?php if (in_array('glow', $waveTypes, true)): ?>
<label><input type="checkbox" id="wave-glow"/> Glow</label>
<?php endif; ?>
</fieldset>
<?php endif; ?>
</details>
<?php endif; ?>
</div>
<?php endif; ?>
@@ -112,8 +85,48 @@ $showArtRing = $waveActive && in_array('circular', $waveTypes, true) && $rich;
</section>
</main>
<?php if ($showHistory || $showWavePicker): ?>
<div class="menu-wrap" id="menu-wrap">
<button id="menu-toggle" class="menu-toggle" type="button" aria-haspopup="true" aria-expanded="false" aria-label="Menu">&#9776;</button>
<ul id="menu-list" class="menu-list" hidden>
<?php if ($showHistory): ?>
<li><button type="button" id="menu-history" class="menu-item">Song List</button></li>
<?php endif; ?>
<?php if ($showWavePicker): ?>
<li><button type="button" id="menu-waveform" class="menu-item">Waveform Styles</button></li>
<?php endif; ?>
</ul>
</div>
<?php endif; ?>
<?php if ($showWavePicker): ?>
<div class="waveform-picker" id="waveform-picker" hidden>
<fieldset>
<legend>Below art</legend>
<?php
$stripLabels = ['none' => 'None', 'bars' => 'Bars', 'line' => 'Line', 'vu' => 'VU',
'spectrum' => 'Spectrum', 'spectrum-mirror' => 'Spectrum (mirror)', 'blob' => 'Blob'];
foreach ($stripLabels as $val => $label):
if ($val !== 'none' && !in_array($val, $waveTypes, true)) continue;
?>
<label><input type="radio" name="wave-strip" value="<?= e($val) ?>"/> <?= e($label) ?></label>
<?php endforeach; ?>
</fieldset>
<?php if ($showArtRing || in_array('glow', $waveTypes, true)): ?>
<fieldset>
<legend>On art</legend>
<?php if ($showArtRing): ?>
<label><input type="checkbox" id="wave-circular"/> Ring</label>
<?php endif; ?>
<?php if (in_array('glow', $waveTypes, true)): ?>
<label><input type="checkbox" id="wave-glow"/> Glow</label>
<?php endif; ?>
</fieldset>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ($showHistory): ?>
<button id="history-toggle" class="history-toggle" type="button">Song list</button>
<aside id="history" class="history">
<table class="history-table">
<thead><tr><th>Time</th><th>Artist</th><th>Title</th></tr></thead>