feat(facets+ui): clickable facet values everywhere, genre prettify, active-filter chips
Frontend half of the faceted-navigation Phase 2. 'Everything filterable should be clickable': every displayed facet value links to the bookshelf pre-filtered by its raw stored value; display forms are prettified at render, never in the DB. - templates/facet_links.templ: FacetLink (inline links) + FacetBadge (badge links) to /bookshelf?<facet>_filter=<value>; the raw value rides the param while the shown text is prettified, so matching and display can never disagree. Series/tags keep their dedicated pages by design; the card media-type icon stays unlinked (a derived kind, not a column value — deep-linking it would send wrong values). - book_detail.templ: publisher, language, edition, genre, imprint cells + narrators line + age-rating/story-arc badges are now links. - prettifyGenre (templates/utils.go + unit tests): title-cases only entirely-lowercase values; small words stay down except first position; & preserved. TS port for chip labels documents the twin. - bookshelf.templ + bookshelf.ts: active-filter chips bar — Alpine renders chips from client-local form state (sanctioned island); one bubbled htmx:afterRequest hook keeps chips + shareable URL in sync; removeFilterChip clears the facet and re-applies. URL deep links now accept all 18 facet params (hidden inputs where the drawer has no field). Pins changelog: Batch B/C entry in docs/contract_pins.md. Rendering ownership guard (make lint-rendering) green.
This commit is contained in:
1 parent
7e586df48a
commit
bbedbfb13f
10 files changed
+990
-545
No files matched your search
@@ -286,6 +286,28 @@ templ BookShelf(
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- Active-filter chips: a pure function of the client-side
|
||||
form state (Alpine renders them, templ owns the look —
|
||||
sanctioned TS island). Removing a chip clears that facet
|
||||
and re-applies the form. -->
|
||||
<div
|
||||
id="active-filter-chips"
|
||||
class="flex flex-wrap items-center gap-2 mb-4"
|
||||
x-show="activeFilters.length > 0"
|
||||
x-cloak
|
||||
>
|
||||
<template x-for="chip in activeFilters" :key="chip.param">
|
||||
<button
|
||||
type="button"
|
||||
class="badge inline-flex items-center gap-1.5 hover:opacity-80 transition-opacity cursor-pointer"
|
||||
style="background-color: var(--accent-muted); color: var(--accent); border: 1px solid transparent;"
|
||||
@click="removeFilterChip(chip.param)"
|
||||
>
|
||||
<span x-text="chip.label"></span>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
<div id="books-grid" class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4">
|
||||
@BooksGrid(books, limit, offset, count, currentLibraryID)
|
||||
if errorMessage != "" {
|
||||
|
||||
Reference in new issue
Block a user