Skip to content

Commit de087b3

Browse files
Reorder filters, group partner filters, add active-filter chips toolbar
Amp-Thread-ID: https://ampcode.com/threads/T-019d211c-730f-74d8-a8ee-a303cb7aad37 Co-authored-by: Amp <amp@ampcode.com>
1 parent 6d04707 commit de087b3

1 file changed

Lines changed: 74 additions & 13 deletions

File tree

table.html

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,21 @@
4141
.instructions .legend { margin-top: 8px; font-size: .82rem; color: var(--text-muted); border-top: 1px solid var(--border); padding-top: 8px; }
4242

4343
/* Toolbar */
44-
.toolbar { max-width: 1400px; margin: 0 auto; padding: 14px 32px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
44+
.toolbar { max-width: 1400px; margin: 0 auto; padding: 14px 32px 0; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
4545
.search-box { flex: 1; min-width: 260px; position: relative; }
4646
.search-box input { width: 100%; padding: 9px 14px 9px 36px; border: 1px solid var(--border); border-radius: var(--radius); font-size: .9rem; outline: none; transition: border .2s; }
4747
.search-box input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(0,118,168,.12); }
4848
.search-box svg { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--text-muted); }
4949
.result-count { font-size: .88rem; color: var(--text-muted); white-space: nowrap; }
50-
.btn-clear { padding: 8px 18px; background: var(--accent); color: #fff; border: none; border-radius: var(--radius); font-size: .85rem; font-weight: 600; cursor: pointer; white-space: nowrap; transition: background .2s; }
50+
51+
/* Active filters bar */
52+
.active-filters { max-width: 1400px; margin: 0 auto; padding: 6px 32px 10px; display: none; align-items: center; gap: 8px; flex-wrap: wrap; }
53+
.active-filters.visible { display: flex; }
54+
.active-filters-label { font-size: .8rem; font-weight: 600; color: var(--text-muted); white-space: nowrap; }
55+
.af-chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 10px; border-radius: 12px; font-size: .76rem; background: var(--primary); color: #fff; cursor: pointer; white-space: nowrap; transition: background .15s; }
56+
.af-chip:hover { background: var(--primary-dark); }
57+
.af-chip .af-x { font-weight: 700; font-size: .82rem; line-height: 1; }
58+
.btn-clear { padding: 6px 14px; background: var(--accent); color: #fff; border: none; border-radius: var(--radius); font-size: .8rem; font-weight: 600; cursor: pointer; white-space: nowrap; transition: background .2s; }
5159
.btn-clear:hover { background: #c55a24; }
5260

5361
/* Layout */
@@ -150,7 +158,11 @@ <h1>Browse Challenge Projects</h1>
150158
<input type="text" id="searchInput" placeholder="Search by title, technology, toolbox, application, partner...">
151159
</div>
152160
<span class="result-count" id="resultCount"></span>
153-
<button class="btn-clear" id="clearBtn">Clear Filters</button>
161+
</div>
162+
<div class="active-filters" id="activeFilters">
163+
<span class="active-filters-label">Filtered by:</span>
164+
<span id="activeChips"></span>
165+
<button class="btn-clear" id="clearBtn">Clear All</button>
154166
</div>
155167

156168
<div class="main">
@@ -166,14 +178,14 @@ <h1>Browse Challenge Projects</h1>
166178

167179
const FILTER_DEFS = [
168180
{ key: "technology_trends", label: "Technology Trends", tagClass: "tag-trend" },
169-
{ key: "mathworks_platforms", label: "MathWorks Platforms", tagClass: "tag-platform" },
170-
{ key: "mathworks_products", label: "MathWorks Products", tagClass: "tag-product" },
171181
{ key: "application_areas", label: "Application Areas", tagClass: "tag-area" },
172182
{ key: "difficulty", label: "Difficulty", tagClass: "", isScalar: true },
173183
{ key: "project_type", label: "Project Type", tagClass: "tag-type" },
174184
{ key: "hardware_required", label: "Hardware Required", tagClass: "", isScalar: true },
175185
{ key: "hardware_tags", label: "Hardware Tags", tagClass: "tag-hw" },
176186
{ key: "partners", label: "Partners", tagClass: "tag-partner" },
187+
{ key: "mathworks_platforms", label: "MathWorks Platforms", tagClass: "tag-platform" },
188+
{ key: "mathworks_products", label: "MathWorks Products", tagClass: "tag-product" },
177189
];
178190

179191
let allProjects = [];
@@ -186,6 +198,8 @@ <h1>Browse Challenge Projects</h1>
186198
const $count = document.getElementById("resultCount");
187199
const $clear = document.getElementById("clearBtn");
188200
const $status = document.getElementById("statusMsg");
201+
const $activeFilters = document.getElementById("activeFilters");
202+
const $activeChips = document.getElementById("activeChips");
189203

190204
// ── Data loading ──────────────────────────────────────────────
191205
fetch("./projects/projects.json")
@@ -205,15 +219,17 @@ <h1>Browse Challenge Projects</h1>
205219
function buildSidebar() {
206220
let html = "";
207221

208-
// Partner-only toggle
209-
html += `<div class="filter-section">
210-
<div class="filter-header" style="cursor:default">Partner Filter</div>
211-
<div class="filter-options">
212-
<label><input type="checkbox" id="partnerToggle"> Only partnered projects</label>
213-
</div>
214-
</div>`;
215-
216222
FILTER_DEFS.forEach(def => {
223+
// Insert partner toggle right before Partners section
224+
if (def.key === "partners") {
225+
html += `<div class="filter-section">
226+
<div class="filter-header" style="cursor:default">Partner Filter</div>
227+
<div class="filter-options">
228+
<label><input type="checkbox" id="partnerToggle"> Only partnered projects</label>
229+
</div>
230+
</div>`;
231+
}
232+
217233
const vals = collectValues(def.key, def.isScalar);
218234
if (!vals.length) return;
219235
filterState[def.key] = new Set();
@@ -283,6 +299,7 @@ <h1>Browse Challenge Projects</h1>
283299

284300
renderCards(filtered);
285301
updateCounts(filtered);
302+
renderActiveFilters();
286303
$count.textContent = `Showing ${filtered.length} of ${allProjects.length} projects`;
287304
}
288305

@@ -314,6 +331,50 @@ <h1>Browse Challenge Projects</h1>
314331
});
315332
}
316333

334+
// ── Active filter chips ────────────────────────────────────────
335+
function renderActiveFilters() {
336+
let chips = [];
337+
const q = $search.value.trim();
338+
if (q) {
339+
chips.push(`<span class="af-chip" data-action="search">Search: "${esc(q)}" <span class="af-x">✕</span></span>`);
340+
}
341+
if (partnerOnly) {
342+
chips.push(`<span class="af-chip" data-action="partner">Partnered only <span class="af-x">✕</span></span>`);
343+
}
344+
FILTER_DEFS.forEach(def => {
345+
const sel = filterState[def.key];
346+
if (!sel) return;
347+
sel.forEach(v => {
348+
chips.push(`<span class="af-chip" data-action="filter" data-key="${def.key}" data-val="${escAttr(v)}">${esc(v)} <span class="af-x">✕</span></span>`);
349+
});
350+
});
351+
352+
$activeChips.innerHTML = chips.join("");
353+
$activeFilters.classList.toggle("visible", chips.length > 0);
354+
355+
// click to remove individual chip
356+
$activeChips.querySelectorAll(".af-chip").forEach(chip => {
357+
chip.addEventListener("click", () => {
358+
const action = chip.dataset.action;
359+
if (action === "search") {
360+
$search.value = "";
361+
} else if (action === "partner") {
362+
partnerOnly = false;
363+
const pt = document.getElementById("partnerToggle");
364+
if (pt) pt.checked = false;
365+
} else if (action === "filter") {
366+
const key = chip.dataset.key;
367+
const val = chip.dataset.val;
368+
filterState[key].delete(val);
369+
// uncheck matching sidebar checkbox
370+
const cb = $sidebar.querySelector(`input[data-key="${key}"][value="${CSS.escape(val)}"]`);
371+
if (cb) cb.checked = false;
372+
}
373+
applyFilters();
374+
});
375+
});
376+
}
377+
317378
// ── Card rendering ────────────────────────────────────────────
318379
function renderCards(projects) {
319380
if (!projects.length) {

0 commit comments

Comments
 (0)