From d95cd5fac83ee8e6e7178322d2d0c8c23ad5bea1 Mon Sep 17 00:00:00 2001 From: George Cushen Date: Wed, 31 Dec 2025 01:14:33 +0000 Subject: [PATCH] fix(search): filters + metadata across collection layouts Fixes https://github.com/HugoBlox/hugo-blox-builder/discussions/3292 --- .../_partials/components/search-modal.html | 182 +++++++----------- .../layouts/_partials/docs_layout.html | 4 +- .../blox-tailwind/layouts/events/page.html | 10 +- modules/blox-tailwind/layouts/faq/single.html | 6 +- .../layouts/questions/single.html | 5 +- modules/blox-tailwind/layouts/single.html | 12 +- templates/academic-cv/pnpm-lock.yaml | 64 ++++++ 7 files changed, 160 insertions(+), 123 deletions(-) diff --git a/modules/blox-tailwind/layouts/_partials/components/search-modal.html b/modules/blox-tailwind/layouts/_partials/components/search-modal.html index fe97ebd9..03504ed9 100644 --- a/modules/blox-tailwind/layouts/_partials/components/search-modal.html +++ b/modules/blox-tailwind/layouts/_partials/components/search-modal.html @@ -267,6 +267,7 @@ selectedIndex: -1, activeFilter: null, availableFilters: [], + allFilters: [], trendingSearches: {{ site.Params.hugoblox.search.suggestions | default (slice "Search term 1" "Search term 2") | jsonify }}, pagefind: null, pagefindModulePath: window.hbb?.assetPaths?.pagefind ?? '/pagefind/pagefind.js', @@ -336,6 +337,62 @@ } }); }, + + buildFilters(filterIndex) { + const filters = []; + + if (filterIndex?.type) { + Object.entries(filterIndex.type).forEach(([value, count]) => { + filters.push({ + category: 'type', + value, + label: this.typeLabels[value] || value.charAt(0).toUpperCase() + value.slice(1), + count, + filterKey: `type:${value}` + }); + }); + } + + if (filterIndex?.category) { + Object.entries(filterIndex.category).forEach(([value, count]) => { + filters.push({ + category: 'category', + value, + label: value, + count, + filterKey: `category:${value}` + }); + }); + } + + if (filterIndex?.difficulty) { + Object.entries(filterIndex.difficulty).forEach(([value, count]) => { + filters.push({ + category: 'difficulty', + value, + label: value, + count, + filterKey: `difficulty:${value}` + }); + }); + } + + return filters; + }, + + mergeFilterCounts(filterCounts) { + if (!this.allFilters.length) { + return []; + } + + return this.allFilters.map((filter) => { + const count = filterCounts?.[filter.category]?.[filter.value]; + return { + ...filter, + count: typeof count === 'number' ? count : 0 + }; + }); + }, async search() { if (!this.query.trim()) { @@ -374,72 +431,14 @@ }; }) ); - - // Extract unique filters from results themselves - const filterCounts = { type: {}, category: {}, difficulty: {} }; - - this.results.forEach(result => { - if (result.filters) { - // Count type filters - if (result.filters.type) { - result.filters.type.forEach(val => { - filterCounts.type[val] = (filterCounts.type[val] || 0) + 1; - }); - } - // Count category filters - if (result.filters.category) { - result.filters.category.forEach(val => { - filterCounts.category[val] = (filterCounts.category[val] || 0) + 1; - }); - } - // Count difficulty filters - if (result.filters.difficulty) { - result.filters.difficulty.forEach(val => { - filterCounts.difficulty[val] = (filterCounts.difficulty[val] || 0) + 1; - }); - } - } - }); - - console.log('Extracted filter counts from results:', filterCounts); - - // Build filter list - const filters = []; - - // Add type filters with i18n labels - Object.entries(filterCounts.type).forEach(([value, count]) => { - filters.push({ - category: 'type', - value, - label: this.typeLabels[value] || value.charAt(0).toUpperCase() + value.slice(1), - count, - filterKey: `type:${value}` - }); - }); - - // Add category filters - Object.entries(filterCounts.category).forEach(([value, count]) => { - filters.push({ - category: 'category', - value, - label: value, - count, - filterKey: `category:${value}` - }); - }); - - // Add difficulty filters - Object.entries(filterCounts.difficulty).forEach(([value, count]) => { - filters.push({ - category: 'difficulty', - value, - label: value, - count, - filterKey: `difficulty:${value}` - }); - }); - - this.availableFilters = filters; + + const filterCounts = search.totalFilters || search.filters || {}; + + if (this.allFilters.length) { + this.availableFilters = this.mergeFilterCounts(filterCounts); + } else { + this.availableFilters = this.buildFilters(filterCounts); + } console.log('Final availableFilters array:', this.availableFilters); // Set first result as selected for keyboard navigation @@ -456,52 +455,13 @@ }, async loadFilters() { - // Pre-load available filters by doing an empty search + // Pre-load available filters from the full Pagefind index try { - const search = await this.pagefind.search(''); - console.log('Available Pagefind filters:', search.filters); - - const filters = []; - - // Load type filters (questions, faq, docs) - if (search.filters && search.filters.type) { - Object.entries(search.filters.type).forEach(([value, count]) => { - filters.push({ - category: 'type', - value, - label: value.charAt(0).toUpperCase() + value.slice(1), - count, - filterKey: `type:${value}` - }); - }); - } - - // Load category filters - if (search.filters && search.filters.category) { - Object.entries(search.filters.category).forEach(([value, count]) => { - filters.push({ - category: 'category', - value, - label: value, - count, - filterKey: `category:${value}` - }); - }); - } - - // Load difficulty filters (for questions) - if (search.filters && search.filters.difficulty) { - Object.entries(search.filters.difficulty).forEach(([value, count]) => { - filters.push({ - category: 'difficulty', - value, - label: value, - count, - filterKey: `difficulty:${value}` - }); - }); - } - + const filterIndex = await this.pagefind.filters(); + console.log('Available Pagefind filters:', filterIndex); + + const filters = this.buildFilters(filterIndex); + this.allFilters = filters; this.availableFilters = filters; console.log('Processed filters:', this.availableFilters); } catch (error) { diff --git a/modules/blox-tailwind/layouts/_partials/docs_layout.html b/modules/blox-tailwind/layouts/_partials/docs_layout.html index 3d4a4359..d0ead824 100644 --- a/modules/blox-tailwind/layouts/_partials/docs_layout.html +++ b/modules/blox-tailwind/layouts/_partials/docs_layout.html @@ -10,8 +10,8 @@ {{ end }} {{/* Pagefind metadata for search filtering */}} -
-

{{ .Title }}

+
+

{{ .Title }}

{{ .Content }} {{/* Use hidden spans for filter values - best practice per Pagefind docs */}} diff --git a/modules/blox-tailwind/layouts/events/page.html b/modules/blox-tailwind/layouts/events/page.html index 0db59cde..23205789 100644 --- a/modules/blox-tailwind/layouts/events/page.html +++ b/modules/blox-tailwind/layouts/events/page.html @@ -28,7 +28,7 @@ {{ end }} {{ end }} -

+

{{/* Inline icon before title if specified */}} {{ if $cover }} {{ $icon_config := .Params.cover.icon | default (dict) }} @@ -375,7 +375,13 @@

{{ end }} -
+
+ {{ with .Section }} + {{ . }} + {{ end }} + {{ with .Params.categories }} + {{ index . 0 }} + {{ end }} {{ .Content }}
diff --git a/modules/blox-tailwind/layouts/faq/single.html b/modules/blox-tailwind/layouts/faq/single.html index 16b17223..6439228b 100644 --- a/modules/blox-tailwind/layouts/faq/single.html +++ b/modules/blox-tailwind/layouts/faq/single.html @@ -4,7 +4,7 @@ {{ .Page.Store.Set "has_alpine" true }} {{/* Pagefind metadata for search filtering */}} -
+
{{/* Use hidden spans for filter values - best practice per Pagefind docs */}} faq {{ with .Params.categories }} @@ -23,7 +23,7 @@
{{ end }} -

{{ .Title }}

+

{{ .Title }}

{{ with .Content }}
@@ -138,4 +138,4 @@
{{/* End Pagefind metadata wrapper */}} -{{- end -}} \ No newline at end of file +{{- end -}} diff --git a/modules/blox-tailwind/layouts/questions/single.html b/modules/blox-tailwind/layouts/questions/single.html index 20649aef..911dfb43 100644 --- a/modules/blox-tailwind/layouts/questions/single.html +++ b/modules/blox-tailwind/layouts/questions/single.html @@ -1,8 +1,7 @@ {{- define "main" -}} {{/* Pagefind metadata for search filtering */}} -
+
{{/* Use hidden spans for filter values - best practice per Pagefind docs */}} questions {{ with .Params.categories }} @@ -26,7 +25,7 @@ {{/* Question as title */}}
-

+

{{ .Params.question | default .Title }}

diff --git a/modules/blox-tailwind/layouts/single.html b/modules/blox-tailwind/layouts/single.html index 5bf4cf9c..dc477f2f 100644 --- a/modules/blox-tailwind/layouts/single.html +++ b/modules/blox-tailwind/layouts/single.html @@ -37,7 +37,7 @@ {{ end }} {{ end }} -

+

{{/* Inline icon before title if specified */}} {{ if $cover }} {{ $icon_config := .Params.cover.icon | default (dict) }} @@ -201,6 +201,7 @@ {{ with $image_params.alt_text }}{{.}}{{ end }}
{{ with $image_params.alt_text }}{{.}}{{ end }} {{ end }} -
+
+ {{ with .Section }} + {{ . }} + {{ end }} + {{ with .Params.categories }} + {{ index . 0 }} + {{ end }} {{ .Content }}
diff --git a/templates/academic-cv/pnpm-lock.yaml b/templates/academic-cv/pnpm-lock.yaml index cd00ced1..d6f6c7c2 100644 --- a/templates/academic-cv/pnpm-lock.yaml +++ b/templates/academic-cv/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@tailwindcss/typography': specifier: ^0.5.10 version: 0.5.16(tailwindcss@4.1.12) + pagefind: + specifier: ^1.4.0 + version: 1.4.0 preact: specifier: ^10.27.2 version: 10.27.2 @@ -43,6 +46,36 @@ packages: '@jridgewell/trace-mapping@0.3.30': resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@pagefind/darwin-arm64@1.4.0': + resolution: {integrity: sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ==} + cpu: [arm64] + os: [darwin] + + '@pagefind/darwin-x64@1.4.0': + resolution: {integrity: sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A==} + cpu: [x64] + os: [darwin] + + '@pagefind/freebsd-x64@1.4.0': + resolution: {integrity: sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q==} + cpu: [x64] + os: [freebsd] + + '@pagefind/linux-arm64@1.4.0': + resolution: {integrity: sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw==} + cpu: [arm64] + os: [linux] + + '@pagefind/linux-x64@1.4.0': + resolution: {integrity: sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg==} + cpu: [x64] + os: [linux] + + '@pagefind/windows-x64@1.4.0': + resolution: {integrity: sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==} + cpu: [x64] + os: [win32] + '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} @@ -368,6 +401,10 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + pagefind@1.4.0: + resolution: {integrity: sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==} + hasBin: true + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -433,6 +470,24 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@pagefind/darwin-arm64@1.4.0': + optional: true + + '@pagefind/darwin-x64@1.4.0': + optional: true + + '@pagefind/freebsd-x64@1.4.0': + optional: true + + '@pagefind/linux-arm64@1.4.0': + optional: true + + '@pagefind/linux-x64@1.4.0': + optional: true + + '@pagefind/windows-x64@1.4.0': + optional: true + '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -680,6 +735,15 @@ snapshots: node-addon-api@7.1.1: {} + pagefind@1.4.0: + optionalDependencies: + '@pagefind/darwin-arm64': 1.4.0 + '@pagefind/darwin-x64': 1.4.0 + '@pagefind/freebsd-x64': 1.4.0 + '@pagefind/linux-arm64': 1.4.0 + '@pagefind/linux-x64': 1.4.0 + '@pagefind/windows-x64': 1.4.0 + picocolors@1.1.1: {} picomatch@2.3.1: {}