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:
+13
-2
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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'],
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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', () => {
|
||||||
|
|||||||
@@ -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
@@ -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 }));
|
||||||
|
|||||||
Reference in New Issue
Block a user