multiselect chips for dataset/algo filters
This commit is contained in:
parent
b2be3d0835
commit
ca59516f26
@ -20,8 +20,13 @@ const PALETTE = [
|
||||
|
||||
const state = {
|
||||
raw: [],
|
||||
dataset: "all",
|
||||
algorithm: "all",
|
||||
// Multi-select: membership = "this value is currently enabled". Empty
|
||||
// set = nothing shows (matches "none" toggle). Both default to all
|
||||
// values on load so the initial view matches what a user expects.
|
||||
datasets: new Set(),
|
||||
algorithms: new Set(),
|
||||
datasetsAll: [],
|
||||
algorithmsAll: [],
|
||||
stat: "mean",
|
||||
// Runs the user has clicked in the legend to isolate. Empty = show all.
|
||||
selected: new Set(),
|
||||
@ -55,28 +60,67 @@ function populateFilters() {
|
||||
datasets.add(shortGen(r.meta?.generator_path));
|
||||
algos.add(shortEmb(r.meta?.embedder));
|
||||
}
|
||||
const dsSel = document.getElementById("flt-dataset");
|
||||
const algoSel = document.getElementById("flt-algo");
|
||||
for (const d of [...datasets].filter(Boolean).sort()) {
|
||||
const o = document.createElement("option");
|
||||
o.value = d; o.textContent = d;
|
||||
dsSel.appendChild(o);
|
||||
}
|
||||
for (const a of [...algos].filter(Boolean).sort()) {
|
||||
const o = document.createElement("option");
|
||||
o.value = a; o.textContent = a;
|
||||
algoSel.appendChild(o);
|
||||
}
|
||||
state.datasetsAll = [...datasets].filter(Boolean).sort();
|
||||
state.algorithmsAll = [...algos].filter(Boolean).sort();
|
||||
// Default to everything enabled.
|
||||
state.datasets = new Set(state.datasetsAll);
|
||||
state.algorithms = new Set(state.algorithmsAll);
|
||||
renderChips();
|
||||
document.getElementById("total-count").textContent = state.raw.length;
|
||||
}
|
||||
|
||||
function renderChips() {
|
||||
paintChips("flt-dataset", state.datasetsAll, state.datasets);
|
||||
paintChips("flt-algo", state.algorithmsAll, state.algorithms);
|
||||
}
|
||||
|
||||
function paintChips(containerId, values, selectedSet) {
|
||||
const el = document.getElementById(containerId);
|
||||
el.innerHTML = "";
|
||||
|
||||
for (const v of values) {
|
||||
const b = document.createElement("button");
|
||||
b.type = "button";
|
||||
b.className = "chip" + (selectedSet.has(v) ? " is-on" : "");
|
||||
b.setAttribute("aria-pressed", selectedSet.has(v) ? "true" : "false");
|
||||
b.dataset.value = v;
|
||||
b.dataset.role = "value";
|
||||
b.textContent = v;
|
||||
el.appendChild(b);
|
||||
}
|
||||
|
||||
for (const [role, label] of [["all", "all"], ["none", "none"]]) {
|
||||
const b = document.createElement("button");
|
||||
b.type = "button";
|
||||
b.className = "chip chip-meta";
|
||||
b.dataset.role = role;
|
||||
b.textContent = label;
|
||||
el.appendChild(b);
|
||||
}
|
||||
}
|
||||
|
||||
function wireEvents() {
|
||||
document.getElementById("flt-dataset").addEventListener("change", (e) => {
|
||||
state.dataset = e.target.value; render();
|
||||
});
|
||||
document.getElementById("flt-algo").addEventListener("change", (e) => {
|
||||
state.algorithm = e.target.value; render();
|
||||
});
|
||||
const bindChipRow = (containerId, selectedSet, allList) => {
|
||||
document.getElementById(containerId).addEventListener("click", (e) => {
|
||||
const btn = e.target.closest(".chip");
|
||||
if (!btn) return;
|
||||
const role = btn.dataset.role;
|
||||
if (role === "value") {
|
||||
const v = btn.dataset.value;
|
||||
if (selectedSet.has(v)) selectedSet.delete(v);
|
||||
else selectedSet.add(v);
|
||||
} else if (role === "all") {
|
||||
for (const v of allList) selectedSet.add(v);
|
||||
} else if (role === "none") {
|
||||
selectedSet.clear();
|
||||
}
|
||||
renderChips();
|
||||
render();
|
||||
});
|
||||
};
|
||||
bindChipRow("flt-dataset", state.datasets, state.datasetsAll);
|
||||
bindChipRow("flt-algo", state.algorithms, state.algorithmsAll);
|
||||
|
||||
document.querySelectorAll('input[name="stat"]').forEach((el) => {
|
||||
el.addEventListener("change", (e) => {
|
||||
if (e.target.checked) { state.stat = e.target.value; render(); }
|
||||
@ -86,8 +130,8 @@ function wireEvents() {
|
||||
|
||||
function matchesFilters(r) {
|
||||
const m = r.meta || {};
|
||||
if (state.dataset !== "all" && shortGen(m.generator_path) !== state.dataset) return false;
|
||||
if (state.algorithm !== "all" && shortEmb(m.embedder) !== state.algorithm) return false;
|
||||
if (!state.datasets.has(shortGen(m.generator_path))) return false;
|
||||
if (!state.algorithms.has(shortEmb(m.embedder))) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -896,15 +896,15 @@ button.submit:disabled { background: var(--faint); border-color: var(--faint); c
|
||||
}
|
||||
|
||||
.filter-bar {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto 1fr auto;
|
||||
align-items: end;
|
||||
gap: 1.4rem 1.8rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
gap: 1.1rem 2rem;
|
||||
padding: 0.9rem 0 1rem;
|
||||
border-bottom: 1px solid var(--rule);
|
||||
margin-bottom: 1.4rem;
|
||||
}
|
||||
.filter-group { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
|
||||
.filter-group { display: flex; flex-direction: column; gap: 0.35rem; min-width: 0; }
|
||||
.filter-group .ctl-label {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.68rem;
|
||||
@ -912,22 +912,49 @@ button.submit:disabled { background: var(--faint); border-color: var(--faint); c
|
||||
text-transform: uppercase;
|
||||
color: var(--mute);
|
||||
}
|
||||
.filter-group select {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.85rem;
|
||||
color: var(--ink);
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-bottom: 1px solid var(--rule-2);
|
||||
padding: 3px 18px 4px 2px;
|
||||
outline: none;
|
||||
min-width: 12ch;
|
||||
.filter-group.stat-group { min-width: 18rem; }
|
||||
|
||||
.filter-group .chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.3rem 0.35rem;
|
||||
align-items: center;
|
||||
}
|
||||
.filter-group select:focus {
|
||||
border-bottom-color: var(--accent);
|
||||
.chip {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.75rem;
|
||||
color: var(--mute);
|
||||
background: transparent;
|
||||
border: 1px solid var(--rule-2);
|
||||
border-radius: 2px;
|
||||
padding: 0.22rem 0.55rem;
|
||||
cursor: pointer;
|
||||
transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
|
||||
user-select: none;
|
||||
letter-spacing: 0.02em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.chip:hover { color: var(--ink); }
|
||||
.chip.is-on {
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
background: var(--accent-tint);
|
||||
}
|
||||
.filter-group.stat-group { min-width: 18rem; }
|
||||
.chip:focus-visible { outline: 1px solid var(--accent); outline-offset: 2px; }
|
||||
.chip-meta {
|
||||
color: var(--faint);
|
||||
font-family: var(--serif);
|
||||
font-style: italic;
|
||||
font-size: 0.76rem;
|
||||
letter-spacing: 0;
|
||||
border-style: dashed;
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
.chip-meta:hover {
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
background: transparent;
|
||||
}
|
||||
.filter-group .segmented.count-4 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
@ -965,8 +992,10 @@ button.submit:disabled { background: var(--faint); border-color: var(--faint); c
|
||||
font-family: var(--mono);
|
||||
font-size: 0.8rem;
|
||||
color: var(--ink);
|
||||
text-align: right;
|
||||
font-variant-numeric: tabular-nums;
|
||||
margin-left: auto;
|
||||
align-self: center;
|
||||
padding-top: 0.3rem;
|
||||
}
|
||||
.filter-count .muted { color: var(--mute); }
|
||||
|
||||
@ -1122,14 +1151,9 @@ button.submit:disabled { background: var(--faint); border-color: var(--faint); c
|
||||
|
||||
@media (max-width: 780px) {
|
||||
.metrics-page { padding: 1rem 1.1rem 1.4rem; }
|
||||
.filter-bar {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.9rem 1.1rem;
|
||||
}
|
||||
.filter-group.stat-group { grid-column: 1 / -1; min-width: 0; }
|
||||
.filter-count { grid-column: 1 / -1; text-align: left; }
|
||||
.legend .legend-row {
|
||||
grid-template-columns: 10px 1fr;
|
||||
}
|
||||
.filter-bar { gap: 0.9rem 1.2rem; }
|
||||
.filter-group.stat-group { width: 100%; min-width: 0; }
|
||||
.filter-count { margin-left: 0; text-align: left; padding-top: 0; }
|
||||
.legend .legend-row { grid-template-columns: 10px 1fr; }
|
||||
.legend .fn { display: none; }
|
||||
}
|
||||
|
||||
@ -183,7 +183,6 @@
|
||||
|
||||
<footer class="colophon">
|
||||
<span><span class="k">web</span> · scientific instrument · port 8001</span>
|
||||
<span>fastapi · htmx · no build step</span>
|
||||
</footer>
|
||||
|
||||
<script type="module" src="/static/dataset-picker.js"></script>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>metrics — embedding notebook</title>
|
||||
<link rel="stylesheet" href="/static/style.css?v=3" />
|
||||
<link rel="stylesheet" href="/static/style.css?v=4" />
|
||||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3' fill='%231f4e5f'/%3E%3C/svg%3E" />
|
||||
</head>
|
||||
<body>
|
||||
@ -24,16 +24,12 @@
|
||||
<div class="filter-bar">
|
||||
<div class="filter-group">
|
||||
<span class="ctl-label">dataset</span>
|
||||
<select id="flt-dataset" aria-label="filter by dataset">
|
||||
<option value="all">all</option>
|
||||
</select>
|
||||
<div class="chips" id="flt-dataset" aria-label="filter by dataset"></div>
|
||||
</div>
|
||||
|
||||
<div class="filter-group">
|
||||
<span class="ctl-label">algorithm</span>
|
||||
<select id="flt-algo" aria-label="filter by algorithm">
|
||||
<option value="all">all</option>
|
||||
</select>
|
||||
<div class="chips" id="flt-algo" aria-label="filter by algorithm"></div>
|
||||
</div>
|
||||
|
||||
<div class="filter-group stat-group">
|
||||
@ -89,7 +85,7 @@
|
||||
<span><span class="k">web</span> · metrics · port 8001</span>
|
||||
</footer>
|
||||
|
||||
<script type="module" src="/static/metrics.js?v=3"></script>
|
||||
<script type="module" src="/static/metrics.js?v=4"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user