feat: show the active queue filter count

Add a fixed filter summary that counts sidebar, filename, host, and status constraints, highlighting nonzero counts without changing the toolbar geometry.
This commit is contained in:
Sucukdeluxe
2026-08-21 01:06:16 +02:00
parent a1ff19cc30
commit fb544efe2f
7 changed files with 62 additions and 4 deletions
+13 -2
View File
@@ -3933,13 +3933,24 @@ function applyQueueDetailFilters() {
} }
function _hasActiveQueueFilters() { function _hasActiveQueueFilters() {
return uploadSidebarFilter !== 'all' || Boolean(queueSearchQuery || queueHosterFilter || queueStatusFilter); return _activeQueueFilterCount() > 0;
}
function _activeQueueFilterCount() {
return Number(uploadSidebarFilter !== 'all') + Number(Boolean(queueSearchQuery)) + Number(Boolean(queueHosterFilter)) + Number(Boolean(queueStatusFilter));
} }
function syncQueueFilterResetAction() { function syncQueueFilterResetAction() {
const button = document.getElementById('queueFilterResetBtn'); const button = document.getElementById('queueFilterResetBtn');
const count = document.getElementById('queueActiveFilterCount');
const activeCount = _activeQueueFilterCount();
const active = activeCount > 0;
if (count) {
count.textContent = String(activeCount);
count.setAttribute('aria-label', String(activeCount));
count.classList.toggle('is-active', active);
}
if (!button) return; if (!button) return;
const active = _hasActiveQueueFilters();
button.disabled = !active; button.disabled = !active;
} }
+1
View File
@@ -108,6 +108,7 @@
['Bereit zum Download', 'Ready to download'], ['Bereit zum Download', 'Ready to download'],
['Abbrechen', 'Cancel'], ['Abbrechen', 'Cancel'],
['Filter zurücksetzen', 'Reset filters'], ['Filter zurücksetzen', 'Reset filters'],
['Filter', 'Filters'],
['Jetzt installieren', 'Install now'], ['Jetzt installieren', 'Install now'],
['Jetzt updaten', 'Install now'], ['Jetzt updaten', 'Install now'],
['Alle Dateien', 'All files'], ['Alle Dateien', 'All files'],
+5
View File
@@ -290,6 +290,11 @@
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M8 7l-4-5h8z" fill="#4caf50"/><path d="M8 11l-4-5h8z" fill="#4caf50"/><rect x="4" y="13" width="8" height="2" rx="0.5" fill="#4caf50"/></svg> <svg width="16" height="16" viewBox="0 0 16 16"><path d="M8 7l-4-5h8z" fill="#4caf50"/><path d="M8 11l-4-5h8z" fill="#4caf50"/><rect x="4" y="13" width="8" height="2" rx="0.5" fill="#4caf50"/></svg>
</button> </button>
<div class="queue-filter-bar" aria-label="Warteschlange filtern"> <div class="queue-filter-bar" aria-label="Warteschlange filtern">
<div class="queue-filter-summary">
<span>Filter</span>
<strong id="queueActiveFilterCount" aria-label="0">0</strong>
</div>
<span class="queue-filter-divider" aria-hidden="true"></span>
<input class="key-input" id="queueSearchInput" type="search" placeholder="Dateiname suchen" aria-label="Dateiname suchen" style="max-width:260px"> <input class="key-input" id="queueSearchInput" type="search" placeholder="Dateiname suchen" aria-label="Dateiname suchen" style="max-width:260px">
<select class="hs-input" id="queueHosterFilter" aria-label="Nach Hoster filtern" style="width:auto;min-width:150px"><option value="">Alle Hoster</option></select> <select class="hs-input" id="queueHosterFilter" aria-label="Nach Hoster filtern" style="width:auto;min-width:150px"><option value="">Alle Hoster</option></select>
<select class="hs-input" id="queueStatusFilter" aria-label="Nach Status filtern" style="width:auto;min-width:150px"> <select class="hs-input" id="queueStatusFilter" aria-label="Nach Status filtern" style="width:auto;min-width:150px">
+36
View File
@@ -425,6 +425,42 @@ body {
.queue-filter-bar .hs-input { .queue-filter-bar .hs-input {
height: 30px; height: 30px;
} }
.queue-filter-summary {
min-width: 72px;
height: 30px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 7px;
padding: 0 6px;
color: var(--text-dim);
font-size: 10px;
font-weight: 700;
letter-spacing: .05em;
text-transform: uppercase;
white-space: nowrap;
}
#queueActiveFilterCount {
width: 20px;
height: 20px;
display: grid;
place-items: center;
border-radius: 999px;
background: var(--bg-raised);
color: var(--text-muted);
font-size: 11px;
font-variant-numeric: tabular-nums;
transition: background-color .16s ease, color .16s ease;
}
#queueActiveFilterCount.is-active {
background: var(--success);
color: #000;
}
.queue-filter-divider {
width: 1px;
height: 22px;
background: var(--border);
}
.queue-container { .queue-container {
flex: 1 1 0; flex: 1 1 0;
min-height: 0; min-height: 0;
+1
View File
@@ -30,6 +30,7 @@ test('translations cover static labels and interpolated status text in both lang
assert.equal(translateText('Update v2.1.0 available', 'de'), 'Update v2.1.0 verfügbar'); assert.equal(translateText('Update v2.1.0 available', 'de'), 'Update v2.1.0 verfügbar');
assert.equal(translateText('Alle Status', 'en'), 'Any status'); assert.equal(translateText('Alle Status', 'en'), 'Any status');
assert.equal(translateText('Any status', 'de'), 'Alle Status'); assert.equal(translateText('Any status', 'de'), 'Alle Status');
assert.equal(translateText('Filter', 'en'), 'Filters');
}); });
test('sidebar hierarchy uses distinct English and German kicker labels', () => { test('sidebar hierarchy uses distinct English and German kicker labels', () => {
+1
View File
@@ -178,6 +178,7 @@ test('header occupies its final geometry before asynchronous initialization', ()
assert.match(css, /\.update-release-notes\s*\{[^}]*height:\s*min\(264px,\s*48vh\);/su); assert.match(css, /\.update-release-notes\s*\{[^}]*height:\s*min\(264px,\s*48vh\);/su);
assert.match(html, /class="update-progress-footer"[\s\S]*id="updateProgressDetails"[\s\S]*id="updateProgressSize"[\s\S]*id="updateProgressSpeed"[\s\S]*id="updateProgressEta"[\s\S]*id="updateProgressText"/u); assert.match(html, /class="update-progress-footer"[\s\S]*id="updateProgressDetails"[\s\S]*id="updateProgressSize"[\s\S]*id="updateProgressSpeed"[\s\S]*id="updateProgressEta"[\s\S]*id="updateProgressText"/u);
assert.match(html, /id="queueFilterResetBtn"[^>]*disabled[^>]*>Filter zurücksetzen</u); assert.match(html, /id="queueFilterResetBtn"[^>]*disabled[^>]*>Filter zurücksetzen</u);
assert.match(html, /class="queue-filter-summary"[\s\S]*id="queueActiveFilterCount"[^>]*>0</u);
assert.doesNotMatch(html, /<\/div>\s*<div class="queue-filter-bar"/u); assert.doesNotMatch(html, /<\/div>\s*<div class="queue-filter-bar"/u);
assert.match(css, /\.queue-filter-bar\s*\{[^}]*display:\s*flex;[^}]*margin-left:\s*auto;[^}]*border:\s*1px solid var\(--border\);[^}]*border-radius:\s*7px;/su); assert.match(css, /\.queue-filter-bar\s*\{[^}]*display:\s*flex;[^}]*margin-left:\s*auto;[^}]*border:\s*1px solid var\(--border\);[^}]*border-radius:\s*7px;/su);
assert.match(css, /#updateProgressDetails\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*19ch 2ch 11ch 2ch 10ch;[^}]*column-gap:\s*0;[^}]*font-variant-numeric:\s*tabular-nums;/su); assert.match(css, /#updateProgressDetails\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*19ch 2ch 11ch 2ch 10ch;[^}]*column-gap:\s*0;[^}]*font-variant-numeric:\s*tabular-nums;/su);
+5 -2
View File
@@ -737,11 +737,14 @@ setTimeout(async () => {
document.getElementById('queueStatusFilter').value = 'active'; document.getElementById('queueStatusFilter').value = 'active';
applyQueueDetailFilters(); applyQueueDetailFilters();
const button = document.getElementById('queueFilterResetBtn'); const button = document.getElementById('queueFilterResetBtn');
const before = { hidden: button.hidden, disabled: button.disabled }; const count = document.getElementById('queueActiveFilterCount');
const before = { hidden: button.hidden, disabled: button.disabled, count: count.textContent, active: count.classList.contains('is-active') };
button.click(); button.click();
const after = { const after = {
hidden: button.hidden, hidden: button.hidden,
disabled: button.disabled, disabled: button.disabled,
count: count.textContent,
active: count.classList.contains('is-active'),
sidebar: uploadSidebarFilter, sidebar: uploadSidebarFilter,
search: document.getElementById('queueSearchInput').value, search: document.getElementById('queueSearchInput').value,
hoster: document.getElementById('queueHosterFilter').value, hoster: document.getElementById('queueHosterFilter').value,
@@ -754,7 +757,7 @@ setTimeout(async () => {
renderQueueTable(); renderQueueTable();
return { before, after }; return { before, after };
})()\`); })()\`);
check('Queue filter reset clears every filter without shifting its stable action', queueFilterReset.before.hidden === false && queueFilterReset.before.disabled === false && queueFilterReset.after.hidden === false && queueFilterReset.after.disabled === true && queueFilterReset.after.sidebar === 'all' && queueFilterReset.after.search === '' && queueFilterReset.after.hoster === '' && queueFilterReset.after.status === '' && queueFilterReset.after.allPressed === 'true' && queueFilterReset.after.visible === 'filter-a|filter-b'); check('Queue filter reset clears every filter and updates the stable active count', queueFilterReset.before.hidden === false && queueFilterReset.before.disabled === false && queueFilterReset.before.count === '4' && queueFilterReset.before.active === true && queueFilterReset.after.hidden === false && queueFilterReset.after.disabled === true && queueFilterReset.after.count === '0' && queueFilterReset.after.active === false && queueFilterReset.after.sidebar === 'all' && queueFilterReset.after.search === '' && queueFilterReset.after.hoster === '' && queueFilterReset.after.status === '' && queueFilterReset.after.allPressed === 'true' && queueFilterReset.after.visible === 'filter-a|filter-b');
const queueSelectionAnchor = await wc.executeJavaScript(\`(() => { const queueSelectionAnchor = await wc.executeJavaScript(\`(() => {
queueJobs = ['a', 'b', 'c', 'd'].map(id => ({ id: 'anchor-' + id, file: 'C:/ui/anchor-' + id + '.bin', fileName: 'anchor-' + id + '.bin', hoster: 'byse.sx', status: 'queued', bytesUploaded: 0, bytesTotal: 100, progress: 0 })); queueJobs = ['a', 'b', 'c', 'd'].map(id => ({ id: 'anchor-' + id, file: 'C:/ui/anchor-' + id + '.bin', fileName: 'anchor-' + id + '.bin', hoster: 'byse.sx', status: 'queued', bytesUploaded: 0, bytesTotal: 100, progress: 0 }));