From 1dc6048d9c4fdbe664849419ed90a0eeba183ecd Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 8 Feb 2026 17:14:20 +0000 Subject: [PATCH] (de-) select all --- frontend/app.js | 44 +++++++++++++++++++++++++++++++++-- frontend/style.css | 57 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 96 insertions(+), 5 deletions(-) diff --git a/frontend/app.js b/frontend/app.js index 0718874..00bd42a 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -636,12 +636,20 @@ function renderFilters(container, session) { const wrapper = document.createElement('div'); wrapper.className = 'setting-item'; + const labelRow = document.createElement('div'); + labelRow.className = 'setting-label-row'; + const label = document.createElement('label'); label.textContent = optionGroup.title || optionGroup.id; + labelRow.appendChild(label); const options = optionGroup.options || []; const currentSelection = session.options ? session.options[optionGroup.id] : null; if (optionGroup.multiSelect) { + const actionBtn = document.createElement('button'); + actionBtn.type = 'button'; + actionBtn.className = 'btn-link'; + const list = document.createElement('div'); list.className = 'multi-select'; @@ -651,6 +659,14 @@ function renderFilters(container, session) { : [] ); + const updateActionLabel = () => { + const allChecked = options.length > 0 && + Array.from(list.querySelectorAll('input[type="checkbox"]')) + .every((cb) => cb.checked); + actionBtn.textContent = allChecked ? 'Deselect all' : 'Select all'; + actionBtn.disabled = options.length === 0; + }; + options.forEach((opt) => { const item = document.createElement('label'); item.className = 'multi-select-item'; @@ -677,6 +693,7 @@ function renderFilters(container, session) { setSession(nextSession); savePreference(nextSession); resetAndReload(); + updateActionLabel(); }; item.appendChild(checkbox); @@ -684,7 +701,30 @@ function renderFilters(container, session) { list.appendChild(item); }); - wrapper.appendChild(label); + updateActionLabel(); + + actionBtn.onclick = () => { + const checkboxes = Array.from(list.querySelectorAll('input[type="checkbox"]')); + const allChecked = checkboxes.length > 0 && checkboxes.every((cb) => cb.checked); + checkboxes.forEach((cb) => { + cb.checked = !allChecked; + }); + + const nextSession = getSession(); + if (!nextSession || !nextSession.channel) return; + const selected = []; + if (!allChecked) { + options.forEach((opt) => selected.push(opt)); + } + nextSession.options[optionGroup.id] = selected; + setSession(nextSession); + savePreference(nextSession); + resetAndReload(); + updateActionLabel(); + }; + + labelRow.appendChild(actionBtn); + wrapper.appendChild(labelRow); wrapper.appendChild(list); container.appendChild(wrapper); return; @@ -715,7 +755,7 @@ function renderFilters(container, session) { resetAndReload(); }; - wrapper.appendChild(label); + wrapper.appendChild(labelRow); wrapper.appendChild(select); container.appendChild(wrapper); }); diff --git a/frontend/style.css b/frontend/style.css index cc52209..c8aaeee 100644 --- a/frontend/style.css +++ b/frontend/style.css @@ -293,6 +293,38 @@ body.theme-light .sidebar { letter-spacing: 0.5px; } +.setting-label-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 8px; +} + +.setting-label-row label { + margin-bottom: 0; +} + +.btn-link { + background: transparent; + border: none; + color: var(--text-secondary); + font-size: 12px; + cursor: pointer; + text-decoration: underline; + padding: 0; +} + +.btn-link:hover { + color: var(--text-primary); +} + +.btn-link:disabled { + color: var(--border); + cursor: not-allowed; + text-decoration: none; +} + .input-row { display: flex; gap: 8px; @@ -413,15 +445,34 @@ body.theme-light .input-row input:focus { .multi-select-item { display: flex; align-items: center; - gap: 8px; + gap: 12px; font-size: 13px; color: var(--text-primary); + padding: 6px 10px; + border-radius: 8px; + border: 1px solid transparent; + background: transparent; + transition: all 0.2s ease; } .multi-select-item input[type="checkbox"] { - width: 16px; - height: 16px; + width: 18px; + height: 18px; accent-color: var(--accent); + flex-shrink: 0; +} + +.multi-select-item span { + padding-left: 6px; +} + +.multi-select-item:hover { + border-color: var(--border); + background: var(--bg-tertiary); +} + +.multi-select-item input[type="checkbox"]:checked + span { + color: var(--accent); } .setting-item select:focus {