fix(website): scope subcategory filter values to parent category

Subcategories with the same name (e.g. 'Frameworks') across different
top-level categories were sharing a filter value, so clicking one
subcategory tag would match entries from unrelated categories.

Each subcategory now stores both a display name and a scoped value
('Category > Subcategory') used for data-cats matching. The template
renders the display name on tags and mobile-cat span, but uses the
scoped value for filtering. Subcategory tags are also moved before
category tags so the most-specific label appears first.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Vinta Chen
2026-03-23 01:11:35 +08:00
parent 964f246d86
commit 0c26d352f0
2 changed files with 14 additions and 12 deletions

View File

@@ -103,8 +103,10 @@ def extract_entries(
if group_name not in existing["groups"]: if group_name not in existing["groups"]:
existing["groups"].append(group_name) existing["groups"].append(group_name)
subcat = entry["subcategory"] subcat = entry["subcategory"]
if subcat and subcat not in existing["subcategories"]: if subcat:
existing["subcategories"].append(subcat) scoped = f"{cat['name']} > {subcat}"
if not any(s["value"] == scoped for s in existing["subcategories"]):
existing["subcategories"].append({"name": subcat, "value": scoped})
else: else:
merged = { merged = {
"name": entry["name"], "name": entry["name"],
@@ -112,7 +114,7 @@ def extract_entries(
"description": entry["description"], "description": entry["description"],
"categories": [cat["name"]], "categories": [cat["name"]],
"groups": [group_name], "groups": [group_name],
"subcategories": [entry["subcategory"]] if entry["subcategory"] else [], "subcategories": [{"name": entry["subcategory"], "value": f"{cat['name']} > {entry['subcategory']}"}] if entry["subcategory"] else [],
"stars": None, "stars": None,
"owner": None, "owner": None,
"last_commit_at": None, "last_commit_at": None,

View File

@@ -121,7 +121,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 | join('||') }}{% endif %}{% if entry.source_type == 'Built-in' %}||Built-in{% endif %}" 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-groups="{{ entry.groups | join('||') }}" data-groups="{{ entry.groups | join('||') }}"
tabindex="0" tabindex="0"
aria-expanded="false" aria-expanded="false"
@@ -132,7 +132,7 @@
<a href="{{ entry.url }}" target="_blank" rel="noopener" <a href="{{ entry.url }}" target="_blank" rel="noopener"
>{{ entry.name }}</a >{{ entry.name }}</a
> >
<span class="mobile-cat">{{ entry.categories[0] }}</span> <span class="mobile-cat">{% if entry.subcategories %}{{ entry.subcategories[0].name }}{% else %}{{ entry.categories[0] }}{% endif %}</span>
</td> </td>
<td class="col-stars"> <td class="col-stars">
{% if entry.stars is not none %}{{ "{:,}".format(entry.stars) }}{% {% if entry.stars is not none %}{{ "{:,}".format(entry.stars) }}{%
@@ -156,17 +156,17 @@
>{% else %}&mdash;{% endif %} >{% else %}&mdash;{% endif %}
</td> </td>
<td class="col-cat"> <td class="col-cat">
{% for cat in entry.categories %} {% for subcat in entry.subcategories %}
<button class="tag" data-type="cat" data-value="{{ cat }}">
{{ cat }}
</button>
{% endfor %} {% for subcat in entry.subcategories %}
<button <button
class="tag tag-subcat" class="tag tag-subcat"
data-type="cat" data-type="cat"
data-value="{{ subcat }}" data-value="{{ subcat.value }}"
> >
{{ subcat }} {{ subcat.name }}
</button>
{% endfor %} {% for cat in entry.categories %}
<button class="tag" data-type="cat" data-value="{{ cat }}">
{{ cat }}
</button> </button>
{% endfor %} {% endfor %}
<button <button