refactor(js): simplify filter state to a plain string

Replace the { type, value } filter object with a plain string value.
Merge data-cats and data-groups row attributes into a single data-tags
attribute. Drop data-type from tag buttons. Consolidate category/group
URL params into a single filter param, keeping backward-compat fallback.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Vinta Chen
2026-03-24 13:18:26 +08:00
parent 3b69697504
commit d7a7e68475
2 changed files with 14 additions and 39 deletions
+11 -33
View File
@@ -5,7 +5,7 @@ function getScrollBehavior() {
} }
// State // State
let activeFilter = null; // { type: "cat"|"group", value: "..." } let activeFilter = null; // string value or null
let activeSort = { col: "stars", order: "desc" }; let activeSort = { col: "stars", order: "desc" };
const searchInput = document.querySelector(".search"); const searchInput = document.querySelector(".search");
const filterBar = document.querySelector(".filter-bar"); const filterBar = document.querySelector(".filter-bar");
@@ -125,11 +125,9 @@ function applyFilters() {
rows.forEach(function (row) { rows.forEach(function (row) {
let show = true; let show = true;
// Category/group filter
if (activeFilter) { if (activeFilter) {
const attr = const tags = row.dataset.tags;
activeFilter.type === "cat" ? row.dataset.cats : row.dataset.groups; show = tags ? tags.split("||").indexOf(activeFilter) !== -1 : false;
show = attr ? attr.split("||").indexOf(activeFilter.value) !== -1 : false;
} }
// Text search // Text search
@@ -160,18 +158,14 @@ function applyFilters() {
// Update tag highlights // Update tag highlights
tags.forEach(function (tag) { tags.forEach(function (tag) {
const isActive = tag.classList.toggle("active", activeFilter === tag.dataset.value);
activeFilter &&
tag.dataset.type === activeFilter.type &&
tag.dataset.value === activeFilter.value;
tag.classList.toggle("active", isActive);
}); });
// Filter bar // Filter bar
if (filterBar) { if (filterBar) {
if (activeFilter) { if (activeFilter) {
filterBar.classList.add("visible"); filterBar.classList.add("visible");
if (filterValue) filterValue.textContent = activeFilter.value; if (filterValue) filterValue.textContent = activeFilter;
} else { } else {
filterBar.classList.remove("visible"); filterBar.classList.remove("visible");
} }
@@ -185,10 +179,7 @@ function updateURL() {
const query = searchInput ? searchInput.value.trim() : ""; const query = searchInput ? searchInput.value.trim() : "";
if (query) params.set("q", query); if (query) params.set("q", query);
if (activeFilter) { if (activeFilter) {
params.set( params.set("filter", activeFilter);
activeFilter.type === "cat" ? "category" : "group",
activeFilter.value,
);
} }
if (activeSort.col !== "stars" || activeSort.order !== "desc") { if (activeSort.col !== "stars" || activeSort.order !== "desc") {
params.set("sort", activeSort.col); params.set("sort", activeSort.col);
@@ -296,23 +287,12 @@ if (tbody) {
}); });
} }
// Tag click: filter by category or group // Tag click: filter
tags.forEach(function (tag) { tags.forEach(function (tag) {
tag.addEventListener("click", function (e) { tag.addEventListener("click", function (e) {
e.preventDefault(); e.preventDefault();
const type = tag.dataset.type;
const value = tag.dataset.value; const value = tag.dataset.value;
activeFilter = activeFilter === value ? null : value;
// Toggle: click same filter again to clear
if (
activeFilter &&
activeFilter.type === type &&
activeFilter.value === value
) {
activeFilter = null;
} else {
activeFilter = { type: type, value: value };
}
applyFilters(); applyFilters();
}); });
}); });
@@ -427,20 +407,18 @@ if (backToTop) {
(function () { (function () {
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
const q = params.get("q"); const q = params.get("q");
const cat = params.get("category"); const filter = params.get("filter") || params.get("category") || params.get("group");
const group = params.get("group");
const sort = params.get("sort"); const sort = params.get("sort");
const order = params.get("order"); const order = params.get("order");
if (q && searchInput) searchInput.value = q; if (q && searchInput) searchInput.value = q;
if (cat) activeFilter = { type: "cat", value: cat }; if (filter) activeFilter = filter;
else if (group) activeFilter = { type: "group", value: group };
if ( if (
(sort === "name" || sort === "stars" || sort === "commit-time") && (sort === "name" || sort === "stars" || sort === "commit-time") &&
(order === "desc" || order === "asc") (order === "desc" || order === "asc")
) { ) {
activeSort = { col: sort, order: order }; activeSort = { col: sort, order: order };
} }
if (q || cat || group || sort) { if (q || filter || sort) {
sortRows(); sortRows();
} }
updateSortIndicators(); updateSortIndicators();
+3 -6
View File
@@ -138,8 +138,7 @@
{% for entry in entries %} {% for entry in entries %}
<tr <tr
class="row" class="row"
data-cats="{{ entry.categories | join('||') }}{% if entry.subcategories %}||{{ entry.subcategories | map(attribute='value') | join('||') }}{% endif %}{% if entry.source_type == 'Built-in' %}||Built-in{% endif %}" data-tags="{{ entry.categories | join('||') }}{% if entry.subcategories %}||{{ entry.subcategories | map(attribute='value') | join('||') }}{% endif %}||{{ entry.groups | join('||') }}{% if entry.source_type == 'Built-in' %}||Built-in{% endif %}"
data-groups="{{ entry.groups | join('||') }}"
tabindex="0" tabindex="0"
aria-expanded="false" aria-expanded="false"
aria-controls="expand-{{ loop.index }}" aria-controls="expand-{{ loop.index }}"
@@ -177,17 +176,16 @@
</td> </td>
<td class="col-cat"> <td class="col-cat">
{% for subcat in entry.subcategories %} {% for subcat in entry.subcategories %}
<button class="tag" data-type="cat" data-value="{{ subcat.value }}"> <button class="tag" data-value="{{ subcat.value }}">
{{ subcat.name }} {{ subcat.name }}
</button> </button>
{% endfor %} {% for cat in entry.categories %} {% endfor %} {% for cat in entry.categories %}
<button class="tag" data-type="cat" data-value="{{ cat }}"> <button class="tag" data-value="{{ cat }}">
{{ cat }} {{ cat }}
</button> </button>
{% endfor %} {% endfor %}
<button <button
class="tag tag-group" class="tag tag-group"
data-type="group"
data-value="{{ entry.groups[0] }}" data-value="{{ entry.groups[0] }}"
> >
{{ entry.groups[0] }} {{ entry.groups[0] }}
@@ -195,7 +193,6 @@
{% if entry.source_type == 'Built-in' %} {% if entry.source_type == 'Built-in' %}
<button <button
class="tag tag-source" class="tag tag-source"
data-type="cat"
data-value="Built-in" data-value="Built-in"
> >
Built-in Built-in