@dogsbay/docs-layout 0.2.0-beta.71 → 0.2.0-beta.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/SearchDialog.astro +11 -2
- package/src/search-facets.ts +61 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dogsbay/docs-layout",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.72",
|
|
4
4
|
"description": "Standard documentation layout components for Dogsbay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"./json-ld": "./src/json-ld.ts"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@dogsbay/ui": "0.2.0-beta.
|
|
33
|
-
"@dogsbay/primitives": "0.2.0-beta.
|
|
32
|
+
"@dogsbay/ui": "0.2.0-beta.72",
|
|
33
|
+
"@dogsbay/primitives": "0.2.0-beta.72"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"vitest": "^3.0.0"
|
package/src/SearchDialog.astro
CHANGED
|
@@ -161,6 +161,7 @@ const {
|
|
|
161
161
|
shapeFacets,
|
|
162
162
|
resolveFacetLabel,
|
|
163
163
|
resolveFacetTitle,
|
|
164
|
+
sortFacetNames,
|
|
164
165
|
filterStateToUrlParams,
|
|
165
166
|
parseFiltersFromUrl,
|
|
166
167
|
filterStateToPagefindFilters,
|
|
@@ -182,10 +183,15 @@ const {
|
|
|
182
183
|
meta: { title?: string };
|
|
183
184
|
sub_results?: Array<{ title: string; url: string; excerpt: string }>;
|
|
184
185
|
};
|
|
186
|
+
// Filter values match what filterStateToPagefindFilters emits:
|
|
187
|
+
// each facet wrapped in `{any: [...]}` for OR-within-facet semantics.
|
|
188
|
+
// Pagefind also accepts other operator shapes (`all`/`none`/`not`,
|
|
189
|
+
// bare strings, bare arrays) but we only emit the `any` form.
|
|
190
|
+
type PagefindFilterValue = { any: string[] };
|
|
185
191
|
type PagefindModule = {
|
|
186
192
|
search(
|
|
187
193
|
query: string,
|
|
188
|
-
options?: { filters?: Record<string,
|
|
194
|
+
options?: { filters?: Record<string, PagefindFilterValue> },
|
|
189
195
|
): Promise<{ results: PagefindResult[] }>;
|
|
190
196
|
filters(): Promise<Record<string, Record<string, number>>>;
|
|
191
197
|
};
|
|
@@ -340,7 +346,10 @@ const {
|
|
|
340
346
|
* filters, the sidebar stays hidden — single-column layout.
|
|
341
347
|
*/
|
|
342
348
|
function renderFacets() {
|
|
343
|
-
const facetNames =
|
|
349
|
+
const facetNames = sortFacetNames(
|
|
350
|
+
Object.keys(availableFacets),
|
|
351
|
+
taxonomyDisplay,
|
|
352
|
+
);
|
|
344
353
|
if (facetNames.length === 0) {
|
|
345
354
|
facetsBox!.classList.add("hidden");
|
|
346
355
|
return;
|
package/src/search-facets.ts
CHANGED
|
@@ -16,6 +16,13 @@ import type { PrefixDisplay } from "./tag-list-data.js";
|
|
|
16
16
|
export interface TaxonomyDisplay {
|
|
17
17
|
prefixes?: Record<string, PrefixDisplay>;
|
|
18
18
|
labels?: Record<string, string>;
|
|
19
|
+
/**
|
|
20
|
+
* Sort weight for the facet column in the search dialog. Lower
|
|
21
|
+
* numbers appear first. Facets without an `order` sort
|
|
22
|
+
* alphabetically *after* any pinned ones — so `{ type: { order: 1 } }`
|
|
23
|
+
* promotes "Type" to the top while everything else stays alpha.
|
|
24
|
+
*/
|
|
25
|
+
order?: number;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
/** Map of taxonomy name → display config. */
|
|
@@ -120,6 +127,40 @@ export function resolveFacetTitle(facetName: string): string {
|
|
|
120
127
|
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
121
128
|
}
|
|
122
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Return facet names in sidebar render order.
|
|
132
|
+
*
|
|
133
|
+
* Pagefind's `filters()` map iterates in index-discovery order
|
|
134
|
+
* (effectively the first page Pagefind happened to read), so without
|
|
135
|
+
* sorting the column order is arbitrary and shifts between builds.
|
|
136
|
+
* Order rule:
|
|
137
|
+
* 1. Facets with a numeric `order` in their `TaxonomyDisplay`
|
|
138
|
+
* come first, ascending — use this to pin "Type" or "Audience"
|
|
139
|
+
* to the top regardless of name.
|
|
140
|
+
* 2. Everything else sorts alphabetically by facet name.
|
|
141
|
+
* Stable within each tier so ties don't shuffle.
|
|
142
|
+
*/
|
|
143
|
+
export function sortFacetNames(
|
|
144
|
+
names: string[],
|
|
145
|
+
display?: TaxonomyDisplayMap,
|
|
146
|
+
): string[] {
|
|
147
|
+
const withOrder = (name: string): number | undefined => {
|
|
148
|
+
const o = display?.[name]?.order;
|
|
149
|
+
return typeof o === "number" ? o : undefined;
|
|
150
|
+
};
|
|
151
|
+
return [...names].sort((a, b) => {
|
|
152
|
+
const oa = withOrder(a);
|
|
153
|
+
const ob = withOrder(b);
|
|
154
|
+
if (oa !== undefined && ob !== undefined) {
|
|
155
|
+
if (oa !== ob) return oa - ob;
|
|
156
|
+
return a.localeCompare(b);
|
|
157
|
+
}
|
|
158
|
+
if (oa !== undefined) return -1;
|
|
159
|
+
if (ob !== undefined) return 1;
|
|
160
|
+
return a.localeCompare(b);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
123
164
|
// ── URL persistence ──────────────────────────────────────────────
|
|
124
165
|
|
|
125
166
|
/**
|
|
@@ -175,21 +216,32 @@ export function parseFiltersFromUrl(
|
|
|
175
216
|
}
|
|
176
217
|
|
|
177
218
|
/**
|
|
178
|
-
* Pagefind's `
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
219
|
+
* Build Pagefind's `filters` argument from internal facet state.
|
|
220
|
+
*
|
|
221
|
+
* Pagefind's array shape (`{ tag: ["a", "b"] }`) is AND by default —
|
|
222
|
+
* "page must have BOTH tags" — per
|
|
223
|
+
* https://pagefind.app/docs/js-api-filtering/ ("All filtering
|
|
224
|
+
* defaults to AND filtering"). That's the wrong UX for faceted
|
|
225
|
+
* search: clicking two checkboxes in the same group should widen
|
|
226
|
+
* the result set, not collapse it to zero. We wrap each facet in
|
|
227
|
+
* `{ any: [...] }` so multi-select within a facet is OR. Across
|
|
228
|
+
* different facets Pagefind already ANDs the keys, which matches
|
|
229
|
+
* the standard "narrow with each additional dimension" UX.
|
|
230
|
+
*
|
|
231
|
+
* Single-value selections also go through `{ any: ["x"] }` —
|
|
232
|
+
* Pagefind treats a one-element `any` identically to a bare string,
|
|
233
|
+
* so there's no behavioural delta and the code stays branch-free.
|
|
182
234
|
*
|
|
183
235
|
* Empty filter state → `{}` (Pagefind returns the unfiltered
|
|
184
|
-
* result set). A facet with an empty array
|
|
185
|
-
*
|
|
236
|
+
* result set). A facet with an empty array drops out so we don't
|
|
237
|
+
* accidentally narrow to "must equal nothing".
|
|
186
238
|
*/
|
|
187
239
|
export function filterStateToPagefindFilters(
|
|
188
240
|
filters: FilterState,
|
|
189
|
-
): Record<string, string[]> {
|
|
190
|
-
const out: Record<string, string[]> = {};
|
|
241
|
+
): Record<string, { any: string[] }> {
|
|
242
|
+
const out: Record<string, { any: string[] }> = {};
|
|
191
243
|
for (const [name, values] of Object.entries(filters)) {
|
|
192
|
-
if (values.length > 0) out[name] = [...values];
|
|
244
|
+
if (values.length > 0) out[name] = { any: [...values] };
|
|
193
245
|
}
|
|
194
246
|
return out;
|
|
195
247
|
}
|