runs: live counter + keep compare selections across filter swaps
- Counter shows 'N / cap' where cap flips 10→50 based on whether any chip is active. Updates immediately on chip click and after every htmx swap. - compare-select.js no longer prunes selections that aren't in the current DOM slice — server-side filtering replaces the whole list, so absence from DOM means 'off-current-filter', not 'run deleted'.
This commit is contained in:
parent
e94d28b8fc
commit
59a6bece2e
@ -19,14 +19,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyToDOM() {
|
function applyToDOM() {
|
||||||
const checkboxes = slot.querySelectorAll('.compare-cb');
|
// Selections persist across swaps — with server-side filtering, rows
|
||||||
// Drop any selected stems that are no longer in the DOM (run aged out of list)
|
// leave the DOM when they don't match the current filter, but the user
|
||||||
const present = new Set();
|
// still has them "in the cart".
|
||||||
checkboxes.forEach((cb) => present.add(cb.dataset.stem));
|
|
||||||
for (const s of [...selected]) if (!present.has(s)) selected.delete(s);
|
|
||||||
|
|
||||||
const atCap = selected.size >= MAX;
|
const atCap = selected.size >= MAX;
|
||||||
checkboxes.forEach((cb) => {
|
slot.querySelectorAll('.compare-cb').forEach((cb) => {
|
||||||
const stem = cb.dataset.stem;
|
const stem = cb.dataset.stem;
|
||||||
cb.checked = selected.has(stem);
|
cb.checked = selected.has(stem);
|
||||||
cb.disabled = atCap && !cb.checked;
|
cb.disabled = atCap && !cb.checked;
|
||||||
|
|||||||
@ -72,6 +72,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function anyFilterActive() {
|
||||||
|
return AXES.some((ax) => ax.selected != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCounter() {
|
||||||
|
const count = slot.querySelectorAll('li.run').length;
|
||||||
|
const cap = anyFilterActive() ? 50 : 10;
|
||||||
|
const countEl = document.getElementById('runs-count');
|
||||||
|
const capEl = document.getElementById('runs-cap');
|
||||||
|
if (countEl) countEl.textContent = String(count);
|
||||||
|
if (capEl) capEl.textContent = String(cap);
|
||||||
|
}
|
||||||
|
|
||||||
function triggerRunsRefresh() {
|
function triggerRunsRefresh() {
|
||||||
// Tell htmx to re-fetch /runs right now with the updated hx-vals.
|
// Tell htmx to re-fetch /runs right now with the updated hx-vals.
|
||||||
if (window.htmx && typeof window.htmx.trigger === 'function') {
|
if (window.htmx && typeof window.htmx.trigger === 'function') {
|
||||||
@ -88,6 +101,7 @@
|
|||||||
ax.selected = (ax.selected === v) ? null : v;
|
ax.selected = (ax.selected === v) ? null : v;
|
||||||
paint(ax);
|
paint(ax);
|
||||||
syncHtmxVals();
|
syncHtmxVals();
|
||||||
|
updateCounter();
|
||||||
triggerRunsRefresh();
|
triggerRunsRefresh();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -96,14 +110,14 @@
|
|||||||
// current even between explicit refreshes.
|
// current even between explicit refreshes.
|
||||||
document.body.addEventListener('htmx:afterSwap', (e) => {
|
document.body.addEventListener('htmx:afterSwap', (e) => {
|
||||||
if (e.target && e.target.id === 'runs-slot') {
|
if (e.target && e.target.id === 'runs-slot') {
|
||||||
// Nothing to re-apply in the DOM — the server already honored the
|
|
||||||
// filter. We just make sure selected chips stay marked.
|
|
||||||
repaintAll();
|
repaintAll();
|
||||||
|
updateCounter();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
syncHtmxVals();
|
syncHtmxVals();
|
||||||
refreshUniverse();
|
refreshUniverse();
|
||||||
|
updateCounter();
|
||||||
// Periodically refresh the universe so newly-introduced values appear.
|
// Periodically refresh the universe so newly-introduced values appear.
|
||||||
setInterval(refreshUniverse, 30_000);
|
setInterval(refreshUniverse, 30_000);
|
||||||
})();
|
})();
|
||||||
|
|||||||
@ -296,7 +296,7 @@
|
|||||||
<div class="section-label">
|
<div class="section-label">
|
||||||
<span>§ 4 recent runs</span>
|
<span>§ 4 recent runs</span>
|
||||||
<span class="run-count">
|
<span class="run-count">
|
||||||
<span id="runs-count">{{ runs|length }}</span> / 10 · refresh 3s
|
<span id="runs-count">{{ runs|length }}</span> / <span id="runs-cap">10</span> · refresh 3s
|
||||||
<span id="poll-ind" class="htmx-indicator" style="margin-left:6px">●</span>
|
<span id="poll-ind" class="htmx-indicator" style="margin-left:6px">●</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -500,8 +500,8 @@
|
|||||||
<script src="/static/theme.js?v=11"></script>
|
<script src="/static/theme.js?v=11"></script>
|
||||||
<script type="module" src="/static/dataset-picker.js?v=11"></script>
|
<script type="module" src="/static/dataset-picker.js?v=11"></script>
|
||||||
<script type="module" src="/static/metrics.js?v=11"></script>
|
<script type="module" src="/static/metrics.js?v=11"></script>
|
||||||
<script src="/static/compare-select.js?v=2"></script>
|
<script src="/static/compare-select.js?v=3"></script>
|
||||||
<script src="/static/runs-filter.js?v=5"></script>
|
<script src="/static/runs-filter.js?v=6"></script>
|
||||||
<script type="module" src="/static/run-modal.js?v=3"></script>
|
<script type="module" src="/static/run-modal.js?v=3"></script>
|
||||||
<script>
|
<script>
|
||||||
// Anchor-links alone don't expand <details>; force it.
|
// Anchor-links alone don't expand <details>; force it.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user