@stapel/search-react 0.11.0 → 0.13.0

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.
Files changed (125) hide show
  1. package/CHANGELOG.md +178 -0
  2. package/MODULE.md +118 -3
  3. package/README.md +42 -0
  4. package/dist/api/generated/schema.d.ts +60 -6
  5. package/dist/api/generated/schema.d.ts.map +1 -1
  6. package/dist/api/searchApi.d.ts +13 -6
  7. package/dist/api/searchApi.d.ts.map +1 -1
  8. package/dist/api/searchApi.js.map +1 -1
  9. package/dist/api/types.d.ts +107 -1
  10. package/dist/api/types.d.ts.map +1 -1
  11. package/dist/api/types.js +29 -0
  12. package/dist/api/types.js.map +1 -1
  13. package/dist/default/FacetPanelPane.d.ts +13 -2
  14. package/dist/default/FacetPanelPane.d.ts.map +1 -1
  15. package/dist/default/FacetPanelPane.js +3 -1
  16. package/dist/default/FacetPanelPane.js.map +1 -1
  17. package/dist/default/FilterChips.d.ts +88 -3
  18. package/dist/default/FilterChips.d.ts.map +1 -1
  19. package/dist/default/FilterChips.js +161 -13
  20. package/dist/default/FilterChips.js.map +1 -1
  21. package/dist/default/LocationSummaryLine.d.ts.map +1 -1
  22. package/dist/default/LocationSummaryLine.js +1 -1
  23. package/dist/default/LocationSummaryLine.js.map +1 -1
  24. package/dist/default/SearchBox.d.ts.map +1 -1
  25. package/dist/default/SearchBox.js +74 -4
  26. package/dist/default/SearchBox.js.map +1 -1
  27. package/dist/default/SearchPage.d.ts +29 -1
  28. package/dist/default/SearchPage.d.ts.map +1 -1
  29. package/dist/default/SearchPage.js +25 -10
  30. package/dist/default/SearchPage.js.map +1 -1
  31. package/dist/default/SearchResultCard.d.ts.map +1 -1
  32. package/dist/default/SearchResultCard.js +83 -37
  33. package/dist/default/SearchResultCard.js.map +1 -1
  34. package/dist/default/SearchResultsPane.d.ts +22 -0
  35. package/dist/default/SearchResultsPane.d.ts.map +1 -1
  36. package/dist/default/SearchResultsPane.js +2 -2
  37. package/dist/default/SearchResultsPane.js.map +1 -1
  38. package/dist/default/SortSelect.d.ts +23 -0
  39. package/dist/default/SortSelect.d.ts.map +1 -1
  40. package/dist/default/SortSelect.js +29 -9
  41. package/dist/default/SortSelect.js.map +1 -1
  42. package/dist/default/cardPhotos.d.ts +65 -0
  43. package/dist/default/cardPhotos.d.ts.map +1 -0
  44. package/dist/default/cardPhotos.js +127 -0
  45. package/dist/default/cardPhotos.js.map +1 -0
  46. package/dist/default/index.d.ts +2 -2
  47. package/dist/default/index.d.ts.map +1 -1
  48. package/dist/default/index.js +1 -1
  49. package/dist/default/index.js.map +1 -1
  50. package/dist/headless/FacetPanel.d.ts +10 -0
  51. package/dist/headless/FacetPanel.d.ts.map +1 -1
  52. package/dist/headless/FacetPanel.js +9 -1
  53. package/dist/headless/FacetPanel.js.map +1 -1
  54. package/dist/headless/useFacetLabels.d.ts +39 -0
  55. package/dist/headless/useFacetLabels.d.ts.map +1 -0
  56. package/dist/headless/useFacetLabels.js +129 -0
  57. package/dist/headless/useFacetLabels.js.map +1 -0
  58. package/dist/headless/useSearchBox.d.ts +71 -0
  59. package/dist/headless/useSearchBox.d.ts.map +1 -1
  60. package/dist/headless/useSearchBox.js +67 -2
  61. package/dist/headless/useSearchBox.js.map +1 -1
  62. package/dist/i18n/es.d.ts.map +1 -1
  63. package/dist/i18n/es.js +10 -0
  64. package/dist/i18n/es.js.map +1 -1
  65. package/dist/i18n/keys.d.ts +55 -0
  66. package/dist/i18n/keys.d.ts.map +1 -1
  67. package/dist/i18n/keys.js +67 -0
  68. package/dist/i18n/keys.js.map +1 -1
  69. package/dist/i18n/ru.d.ts.map +1 -1
  70. package/dist/i18n/ru.js +12 -0
  71. package/dist/i18n/ru.js.map +1 -1
  72. package/dist/index.d.ts +7 -5
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +4 -3
  75. package/dist/index.js.map +1 -1
  76. package/dist/model/queries.d.ts +6 -5
  77. package/dist/model/queries.d.ts.map +1 -1
  78. package/dist/model/queries.js +4 -3
  79. package/dist/model/queries.js.map +1 -1
  80. package/dist/model/queryKeys.d.ts +12 -0
  81. package/dist/model/queryKeys.d.ts.map +1 -1
  82. package/dist/model/queryKeys.js +7 -0
  83. package/dist/model/queryKeys.js.map +1 -1
  84. package/dist/model/runtime.d.ts +40 -3
  85. package/dist/model/runtime.d.ts.map +1 -1
  86. package/dist/model/runtime.js +2 -1
  87. package/dist/model/runtime.js.map +1 -1
  88. package/dist/nav/manifest.d.ts +21 -0
  89. package/dist/nav/manifest.d.ts.map +1 -1
  90. package/dist/nav/manifest.js +3 -2
  91. package/dist/nav/manifest.js.map +1 -1
  92. package/dist/state/facets.d.ts +56 -15
  93. package/dist/state/facets.d.ts.map +1 -1
  94. package/dist/state/facets.js +101 -27
  95. package/dist/state/facets.js.map +1 -1
  96. package/llms.txt +3 -3
  97. package/manifest.json +33 -4
  98. package/nav-manifest.json +4 -3
  99. package/package.json +11 -11
  100. package/src/analytics/generated/events.json +1 -1
  101. package/src/api/generated/schema.ts +60 -6
  102. package/src/api/searchApi.ts +13 -6
  103. package/src/api/types.ts +115 -1
  104. package/src/default/FacetPanelPane.tsx +16 -2
  105. package/src/default/FilterChips.tsx +285 -38
  106. package/src/default/LocationSummaryLine.tsx +6 -1
  107. package/src/default/SearchBox.tsx +134 -6
  108. package/src/default/SearchPage.tsx +79 -5
  109. package/src/default/SearchResultCard.tsx +157 -71
  110. package/src/default/SearchResultsPane.tsx +48 -11
  111. package/src/default/SortSelect.tsx +70 -9
  112. package/src/default/cardPhotos.ts +195 -0
  113. package/src/default/index.ts +10 -2
  114. package/src/headless/FacetPanel.tsx +20 -1
  115. package/src/headless/useFacetLabels.ts +172 -0
  116. package/src/headless/useSearchBox.ts +132 -5
  117. package/src/i18n/es.ts +11 -0
  118. package/src/i18n/keys.ts +69 -0
  119. package/src/i18n/ru.ts +13 -0
  120. package/src/index.ts +22 -3
  121. package/src/model/queries.ts +6 -5
  122. package/src/model/queryKeys.ts +23 -0
  123. package/src/model/runtime.ts +43 -4
  124. package/src/nav/manifest.ts +24 -2
  125. package/src/state/facets.ts +123 -39
@@ -0,0 +1,129 @@
1
+ /**
2
+ * The HOST LABEL SEAM — the third source of a facet option's caption, and the
3
+ * only one that can reach a vocabulary.
4
+ *
5
+ * ── The defect ────────────────────────────────────────────────────────────
6
+ *
7
+ * A live classified deployment's category schema types `vendor`, `model`,
8
+ * `memory_size` and `color_ref_select` as `ref_select`: their config carries
9
+ * no `options` table at all, only a POINTER —
10
+ * `{"optionsRef": {"level": "Vendor", "vocabulary": "avito-phone-catalog"}}`.
11
+ * The words live in the vocabulary the pointer names, which is a different
12
+ * service with a different client. So the two label sources this pair already
13
+ * has both come up empty: the answer's `facet_labels` because the deployed
14
+ * server predates stapel-search 0.4.0 and sends no such key, and the schema
15
+ * because a pointer is not a table. The chips printed `apple`, `128-gb`,
16
+ * `chernyy` — storage slugs, at buyers.
17
+ *
18
+ * This pair must not grow a vocabulary client to fix that: it would tie every
19
+ * storefront's search to a service it may not run, and the host ALREADY has
20
+ * that client (`@stapel/attributes-react`'s `VocabularyClientProvider` is
21
+ * mounted for the composer). So the fetch is the host's and the discipline is
22
+ * this module's — the same division as `renderGeoFilter` and
23
+ * `renderCategoryFilter`, one seam lower because what crosses it is data
24
+ * rather than a control.
25
+ *
26
+ * ── Precedence, stated once ───────────────────────────────────────────────
27
+ *
28
+ * 1. the answer's `facet_labels` — the server saw the write-time snapshot;
29
+ * 2. the feature def's inline `options` table — for the plain `select`
30
+ * family, which carries its words with it;
31
+ * 3. this resolver;
32
+ * 4. the raw value.
33
+ *
34
+ * `buildFacetGroups` has already applied 1 and 2 by the time this hook runs,
35
+ * and it leaves an option it could not name with `label === value` — which is
36
+ * what makes "did anyone name this?" answerable without a second lookup. Only
37
+ * those values are asked about, so the host is never called for a value the
38
+ * server or the schema already captioned, and a resolver that returns nothing
39
+ * for a value leaves the raw value on screen. A chip that silently dropped an
40
+ * option would be worse than one showing a slug: the option is real, it has a
41
+ * count, and it is the only way to reach those documents.
42
+ *
43
+ * ── The fetch discipline ──────────────────────────────────────────────────
44
+ *
45
+ * TanStack Query, exactly like the pair's other three reads, and for the four
46
+ * reasons that made it right there:
47
+ *
48
+ * - **batched per group.** One call per facet slug carrying every unresolved
49
+ * value of it, not one call per chip.
50
+ * - **cached.** `staleTime: Infinity` — a vocabulary term's caption changes
51
+ * when somebody edits the catalogue, not between two clicks on a filter.
52
+ * - **deduplicated.** The chip row, the filter panel and the page's layout
53
+ * probe each call `useFacetPanel`, so three components ask for the same
54
+ * group's captions in one render pass; one query key means one request.
55
+ * - **aborted on supersession.** The `signal` is the query's own, so a
56
+ * resolver that honours it stops a request whose answer nobody will read.
57
+ */
58
+ import { useQueries } from "@tanstack/react-query";
59
+ import { mapLoad } from "@stapel/core";
60
+ import { searchQueryKeys } from "../model/queryKeys.js";
61
+ /** One frozen empty list, so a render with no unresolved values is stable. */
62
+ const NO_REQUESTS = [];
63
+ /** The values of one group nobody has named — see the precedence note above. */
64
+ function unresolvedValues(group) {
65
+ return group.options
66
+ .filter((option) => option.label === option.value)
67
+ .map((option) => option.value)
68
+ .sort((a, b) => a.localeCompare(b));
69
+ }
70
+ function isCaptionMap(value) {
71
+ return typeof value === "object" && value !== null && !Array.isArray(value);
72
+ }
73
+ /**
74
+ * Fill in what the server and the schema could not name, through the host.
75
+ *
76
+ * Returns the groups UNCHANGED — same object identity — when there is no
77
+ * resolver or nothing came back, so a page with the seam unwired pays neither
78
+ * a request nor a re-render.
79
+ */
80
+ export function useHostFacetLabels(groups, resolve, locale) {
81
+ const requests = resolve === undefined || groups.status !== "ready"
82
+ ? NO_REQUESTS
83
+ : groups.data
84
+ .map((group) => ({
85
+ slug: group.slug,
86
+ feature: group.feature,
87
+ values: unresolvedValues(group),
88
+ }))
89
+ .filter((request) => request.values.length > 0);
90
+ const answers = useQueries({
91
+ queries: requests.map((request) => ({
92
+ queryKey: searchQueryKeys.facetLabels(request.slug, request.values, locale),
93
+ queryFn: async ({ signal }) => resolve === undefined ? {} : await resolve(request, { signal }),
94
+ staleTime: Number.POSITIVE_INFINITY,
95
+ // A vocabulary that answered 500 answers 500 again a millisecond later,
96
+ // and three retries only delay the chip's raw value by three round
97
+ // trips — the same discipline the pair's other reads keep.
98
+ retry: false,
99
+ })),
100
+ });
101
+ const captions = new Map();
102
+ requests.forEach((request, index) => {
103
+ const data = answers[index]?.data;
104
+ if (isCaptionMap(data))
105
+ captions.set(request.slug, data);
106
+ });
107
+ if (captions.size === 0)
108
+ return groups;
109
+ return mapLoad(groups, (list) => list.map((group) => {
110
+ const named = captions.get(group.slug);
111
+ if (named === undefined)
112
+ return group;
113
+ return {
114
+ ...group,
115
+ options: group.options.map((option) => {
116
+ // Precedence again, enforced rather than assumed: an option whose
117
+ // label already differs from its value was named by the server or
118
+ // the schema, and the host does not get to overwrite either.
119
+ if (option.label !== option.value)
120
+ return option;
121
+ const caption = named[option.value];
122
+ return caption === undefined || caption.length === 0
123
+ ? option
124
+ : { ...option, label: caption };
125
+ }),
126
+ };
127
+ }));
128
+ }
129
+ //# sourceMappingURL=useFacetLabels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFacetLabels.js","sourceRoot":"","sources":["../../src/headless/useFacetLabels.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { LoadState } from "@stapel/core";
2
+ import type { SuggestAnswer, SuggestCategory } from "../api/types.js";
2
3
  /** What a query box needs to read and move the search text. */
3
4
  export interface SearchBoxBag {
4
5
  /** What is in the input right now. */
@@ -20,6 +21,42 @@ export interface SearchBoxBag {
20
21
  readonly suggestState: LoadState<readonly string[]>;
21
22
  /** A suggestion request is in flight for a prefix nothing is shown for yet. */
22
23
  readonly suggestLoading: boolean;
24
+ /**
25
+ * CATEGORY destinations for the typed prefix, server-ranked by live listing
26
+ * count — the primary half of the answer (stapel-search 0.7.0).
27
+ *
28
+ * Empty on a server that sends no `categories` key, and empty when the
29
+ * server could not reach a category provider: see
30
+ * {@link SearchBoxBag.categoriesUnavailable} for why a surface must tell
31
+ * those two apart from "nothing matched".
32
+ *
33
+ * A category with a count of `0` is already filtered out — see
34
+ * {@link SearchBoxBag.categoryCountsUnknown}.
35
+ */
36
+ readonly categories: readonly SuggestCategory[];
37
+ /**
38
+ * The server HAS no category provider for this answer
39
+ * (`degraded: ["category_suggestions"]`), so `categories` being empty says
40
+ * nothing about the catalogue.
41
+ *
42
+ * A surface must render no group at all rather than an empty one: a heading
43
+ * over nothing is the box telling a person the catalogue has no section by
44
+ * that name, which is a claim this answer did not make. It is deliberately
45
+ * NOT a banner either — the reader is mid-word, the terms half still
46
+ * answers, and a provider being down is the operator's business, the same
47
+ * ruling `degradationAudience` already applies to an engine shortfall.
48
+ */
49
+ readonly categoriesUnavailable: boolean;
50
+ /**
51
+ * The counts on {@link SearchBoxBag.categories} are not answers
52
+ * (`degraded: ["category_rollup"]`): with no ancestry every stored path is
53
+ * one segment long, so every count would read `0` for a mechanical reason.
54
+ *
55
+ * The rows are still real destinations and are still offered; a surface
56
+ * prints the path and omits the number, because a catalogue of zeros is
57
+ * worse than a list with no counts.
58
+ */
59
+ readonly categoryCountsUnknown: boolean;
23
60
  /** Characters a prefix needs before the index is asked. */
24
61
  readonly minSuggestChars: number;
25
62
  readonly maxLength: number;
@@ -29,6 +66,25 @@ export interface SearchBoxBag {
29
66
  submit(value?: string): void;
30
67
  /** Empty the box AND the search (an empty `q` is a valid browse). */
31
68
  clear(): void;
69
+ /**
70
+ * Follow a category suggestion: narrow the SERP to that section.
71
+ *
72
+ * Two decisions, both load-bearing.
73
+ *
74
+ * The `category` parameter is set to the server's own
75
+ * {@link SuggestCategory.category} string, VERBATIM. The server joins the
76
+ * ancestry itself precisely so a client cannot invent a different join and
77
+ * silently miss, so nothing here rebuilds it from the path or the slug.
78
+ *
79
+ * The typed text is CLEARED in the same write. The row promised a count —
80
+ * "Menswear / Shorts, 1 240 listings" — and that number is the
81
+ * section's, not the section's intersected with a title search for the word
82
+ * that found it. Keeping `q` would land a person on strictly fewer results
83
+ * than the row they tapped had just quoted, which is the row lying about
84
+ * where it goes. One `patch` call, so it is also one history entry: Back
85
+ * returns to the search the person typed.
86
+ */
87
+ chooseCategory(category: SuggestCategory): void;
32
88
  }
33
89
  export interface UseSearchBoxOptions {
34
90
  readonly debounceMs?: number;
@@ -40,5 +96,20 @@ export interface UseSearchBoxOptions {
40
96
  /** How many suggestions to ask for (backend clamps to 25). Default 8. */
41
97
  readonly suggestLimit?: number;
42
98
  }
99
+ /**
100
+ * The destinations worth offering out of one answer.
101
+ *
102
+ * A category with a count of `0` is a section with nothing in it: following it
103
+ * lands the buyer on an empty SERP, which is a dead end dressed as a
104
+ * destination — and the server does return those (it reads the count with a
105
+ * `0` default and ranks them last), so somebody has to drop them.
106
+ *
107
+ * The one exception is `category_rollup`, where EVERY count is `0` because the
108
+ * ancestry never arrived rather than because the sections are empty. Filtering
109
+ * on the count there would delete the whole group for a reason that has
110
+ * nothing to do with what is in the catalogue, so the rows are kept and the
111
+ * numbers are what the surface omits.
112
+ */
113
+ export declare function offerableCategories(answer: SuggestAnswer | undefined): readonly SuggestCategory[];
43
114
  export declare function useSearchBox(options?: UseSearchBoxOptions): SearchBoxBag;
44
115
  //# sourceMappingURL=useSearchBox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSearchBox.d.ts","sourceRoot":"","sources":["../../src/headless/useSearchBox.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAU9C,+DAA+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,gDAAgD;IAChD,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IACpD,+EAA+E;IAC/E,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,2DAA2D;IAC3D,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,sEAAsE;IACtE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qEAAqE;IACrE,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B;kDAC8C;IAC9C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,yEAAyE;IACzE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAKD,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,YAAY,CAyG5E"}
1
+ {"version":3,"file":"useSearchBox.d.ts","sourceRoot":"","sources":["../../src/headless/useSearchBox.ts"],"names":[],"mappings":"AA8CA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAM9C,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAUtE,+DAA+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,gDAAgD;IAChD,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IACpD,+EAA+E;IAC/E,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC;IAChD;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAC;IACxC;;;;;;;;OAQG;IACH,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAC;IACxC,2DAA2D;IAC3D,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,sEAAsE;IACtE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qEAAqE;IACrE,KAAK,IAAI,IAAI,CAAC;IACd;;;;;;;;;;;;;;;;;OAiBG;IACH,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B;kDAC8C;IAC9C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,yEAAyE;IACzE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAaD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,GAAG,SAAS,GAChC,SAAS,eAAe,EAAE,CAK5B;AAED,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,YAAY,CAwH5E"}
@@ -21,16 +21,65 @@
21
21
  *
22
22
  * The suggestion list is asked for the DEBOUNCED prefix, not the draft — one
23
23
  * request per pause, not per keystroke, on an endpoint the backend throttles.
24
+ *
25
+ * ── The box reaches the CATALOGUE, not only the titles ────────────────────
26
+ *
27
+ * stapel-search 0.7.0 answers `/suggest` with two halves. Until it did, typing
28
+ * a word that names a section of the catalogue answered listing titles and
29
+ * nothing else: on a live classified deployment the search field could not
30
+ * reach a category at all, and the owner's own navigation canon rules out both
31
+ * of the usual workarounds — a picker, and a client-side typeahead over the
32
+ * whole tree.
33
+ *
34
+ * The server's answer is neither, and it carries the one thing a client-side
35
+ * matcher can never have: the number of LIVE listings behind each destination,
36
+ * computed as one aggregate over the index and equal to what `/query` reports
37
+ * for the same category. That number is what tells "Menswear / Shorts" from
38
+ * "Childrenswear / Shorts".
39
+ *
40
+ * Everything here is absent-safe. A server that predates 0.7.0 sends no
41
+ * `categories` key, `suggestCategories` is empty, and the box behaves exactly
42
+ * as it did — which is not a hypothetical: the key appeared mid-session on a
43
+ * stand that was redeployed underneath a running client.
24
44
  */
25
45
  import { useCallback, useEffect, useRef, useState } from "react";
26
46
  import { loadStateFromQuery, mapLoad } from "@stapel/core";
47
+ import { SUGGEST_DEGRADED_CATEGORIES, SUGGEST_DEGRADED_ROLLUP, suggestTerms, } from "../api/types.js";
27
48
  import { useSearchState } from "./SearchStateProvider.js";
28
49
  import { useSuggest } from "../model/queries.js";
29
50
  import { SEARCH_BOX_DEBOUNCE_MS, SEARCH_BOX_SUGGEST_DEBOUNCE_MS, SEARCH_QUERY_MAX_CHARS, SUGGEST_MIN_CHARS, } from "../state/limits.js";
30
51
  /** One frozen empty list, so a render with no suggestions is a stable value. */
31
52
  const NO_SUGGESTIONS = [];
53
+ /** The same, for the categories half. */
54
+ const NO_CATEGORIES = [];
55
+ /** Does this answer name a shortfall? Absent-safe on every older server. */
56
+ function degradedWith(answer, literal) {
57
+ return answer?.degraded?.includes(literal) === true;
58
+ }
59
+ /**
60
+ * The destinations worth offering out of one answer.
61
+ *
62
+ * A category with a count of `0` is a section with nothing in it: following it
63
+ * lands the buyer on an empty SERP, which is a dead end dressed as a
64
+ * destination — and the server does return those (it reads the count with a
65
+ * `0` default and ranks them last), so somebody has to drop them.
66
+ *
67
+ * The one exception is `category_rollup`, where EVERY count is `0` because the
68
+ * ancestry never arrived rather than because the sections are empty. Filtering
69
+ * on the count there would delete the whole group for a reason that has
70
+ * nothing to do with what is in the catalogue, so the rows are kept and the
71
+ * numbers are what the surface omits.
72
+ */
73
+ export function offerableCategories(answer) {
74
+ const categories = answer?.categories;
75
+ if (categories === undefined || categories.length === 0)
76
+ return NO_CATEGORIES;
77
+ if (degradedWith(answer, SUGGEST_DEGRADED_ROLLUP))
78
+ return categories;
79
+ return categories.filter((category) => category.count > 0);
80
+ }
32
81
  export function useSearchBox(options = {}) {
33
- const { state, setText } = useSearchState();
82
+ const { state, setText, patch } = useSearchState();
34
83
  const committed = state.q;
35
84
  const debounceMs = options.debounceMs ?? SEARCH_BOX_DEBOUNCE_MS;
36
85
  const [draft, setDraftState] = useState(committed);
@@ -98,7 +147,19 @@ export function useSearchBox(options = {}) {
98
147
  ...(options.suggestLimit !== undefined ? { limit: options.suggestLimit } : {}),
99
148
  enabled: options.suggest !== false,
100
149
  });
101
- const suggestState = mapLoad(loadStateFromQuery(suggest), (data) => data.items);
150
+ const suggestState = mapLoad(loadStateFromQuery(suggest), suggestTerms);
151
+ // The READY answer only. A category row is a navigation control, and one
152
+ // rendered out of a stale answer would send a person somewhere the current
153
+ // prefix never named.
154
+ const answer = suggest.data;
155
+ const categories = offerableCategories(answer);
156
+ const chooseCategory = (category) => {
157
+ setDraftState("");
158
+ cancel();
159
+ // One write, so one history entry — see `SearchBoxBag.chooseCategory` for
160
+ // why the text goes with it.
161
+ patch({ q: "", category: category.category });
162
+ };
102
163
  return {
103
164
  draft,
104
165
  committed,
@@ -106,11 +167,15 @@ export function useSearchBox(options = {}) {
106
167
  suggestions: suggestState.status === "ready" ? suggestState.data : NO_SUGGESTIONS,
107
168
  suggestState,
108
169
  suggestLoading: suggest.isLoading && suggest.fetchStatus === "fetching",
170
+ categories,
171
+ categoriesUnavailable: degradedWith(answer, SUGGEST_DEGRADED_CATEGORIES),
172
+ categoryCountsUnknown: degradedWith(answer, SUGGEST_DEGRADED_ROLLUP),
109
173
  minSuggestChars: SUGGEST_MIN_CHARS,
110
174
  maxLength: SEARCH_QUERY_MAX_CHARS,
111
175
  setDraft,
112
176
  submit,
113
177
  clear,
178
+ chooseCategory,
114
179
  };
115
180
  }
116
181
  //# sourceMappingURL=useSearchBox.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSearchBox.js","sourceRoot":"","sources":["../../src/headless/useSearchBox.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSearchBox.js","sourceRoot":"","sources":["../../src/headless/useSearchBox.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"es.d.ts","sourceRoot":"","sources":["../../src/i18n/es.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAEnE;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,cA4IhC,CAAC;AAEF,+DAA+D;AAC/D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAE7D"}
1
+ {"version":3,"file":"es.d.ts","sourceRoot":"","sources":["../../src/i18n/es.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAEnE;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAuJhC,CAAC;AAEF,+DAA+D;AAC/D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAE7D"}
package/dist/i18n/es.js CHANGED
@@ -33,11 +33,17 @@ export const searchI18nBundleEs = {
33
33
  "search.results.untitled": "Sin título",
34
34
  "search.results.open": "Abrir",
35
35
  "search.results.image_alt": "Foto de {title}",
36
+ "search.results.photo_alt": "Foto {index} de {total}: {title}",
37
+ "search.results.photos": "Fotos",
38
+ "search.results.photo_unavailable": "Foto no disponible",
36
39
  "search.box.label": "Buscar",
37
40
  "search.box.placeholder": "¿Qué estás buscando?",
38
41
  "search.box.submit": "Buscar",
39
42
  "search.box.clear": "Borrar la búsqueda",
40
43
  "search.box.suggestions": "Sugerencias",
44
+ "search.box.categories": "Secciones",
45
+ "search.box.category_count.one": "{count} anuncio",
46
+ "search.box.category_count.other": "{count} anuncios",
41
47
  "search.sort.label": "Orden",
42
48
  "search.sort.relevance": "Más relevantes",
43
49
  "search.sort.newest": "Más recientes",
@@ -77,6 +83,7 @@ export const searchI18nBundleEs = {
77
83
  "search.filters.dismiss": "Cerrar los filtros",
78
84
  "search.filters.chips_label": "Filtros",
79
85
  "search.filters.all": "Todos los filtros",
86
+ "search.filters.short": "Filtros",
80
87
  "search.filters.chip_more": ", +{count}",
81
88
  "search.category.title": "Categoría",
82
89
  "search.category.clear": "Buscar en todo el catálogo",
@@ -117,6 +124,9 @@ export const searchI18nBundleEs = {
117
124
  "search.ranking.inactive": "No aplicado: {reason}",
118
125
  "search.ranking.notes": "Notas",
119
126
  "search.ranking.link": "Cómo se ordenan estos resultados",
127
+ "search.nav.results": "Buscar",
128
+ "search.nav.ranking": "Orden de resultados",
129
+ "search.nav.ranking.short": "Orden",
120
130
  };
121
131
  /** Register the es bundle. Call AFTER `registerSearchI18n`. */
122
132
  export function registerSearchI18nEs(engine) {
@@ -1 +1 @@
1
- {"version":3,"file":"es.js","sourceRoot":"","sources":["../../src/i18n/es.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"es.js","sourceRoot":"","sources":["../../src/i18n/es.ts"],"names":[],"mappings":"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"}
@@ -52,12 +52,38 @@ export declare const SEARCH_I18N_KEYS: {
52
52
  readonly resultsOpen: "search.results.open";
53
53
  /** Alt text for the card photo — the card's own title, in a sentence. */
54
54
  readonly resultsImageAlt: "search.results.image_alt";
55
+ /** Alt text for one photo of a GALLERY: its place in the strip, and the
56
+ * card's title. A row of ten photos all called "Photo of X" is ten
57
+ * identical announcements. */
58
+ readonly resultsPhotoAlt: "search.results.photo_alt";
59
+ /** The photo strip's accessible name. `SkinCarousel` takes its label from
60
+ * the caller for exactly this reason: the token bridge owns no i18n. */
61
+ readonly resultsPhotos: "search.results.photos";
62
+ /** The card stores a reference and nothing resolved it — a sentence gets
63
+ * the wiring fixed; an empty grey box teaches nobody anything. */
64
+ readonly resultsPhotoUnavailable: "search.results.photo_unavailable";
55
65
  readonly boxLabel: "search.box.label";
56
66
  readonly boxPlaceholder: "search.box.placeholder";
57
67
  readonly boxSubmit: "search.box.submit";
58
68
  readonly boxClear: "search.box.clear";
59
69
  /** Accessible name of the suggestion list under the box. */
60
70
  readonly boxSuggestions: "search.box.suggestions";
71
+ /**
72
+ * The heading over the CATEGORY half of the type-ahead (stapel-search
73
+ * 0.7.0). The rows under it are DESTINATIONS — a section of the catalogue —
74
+ * and not, like everything else in the menu, another set of words to search
75
+ * for; a group with no heading would read as more of the same.
76
+ */
77
+ readonly boxCategories: "search.box.categories";
78
+ /**
79
+ * "1 240 listings" beside a category row — how many LIVE listings a buyer
80
+ * would see there, the same number the SERP reports for it.
81
+ *
82
+ * A PLURAL FAMILY: render with `tPlural`, never `t`. It counts a noun in
83
+ * words, so Russian needs its four endings and a single string would be
84
+ * right only for 5-20 — the exact defect the results count already fixed.
85
+ */
86
+ readonly boxCategoryCount: "search.box.category_count";
61
87
  readonly sortLabel: "search.sort.label";
62
88
  readonly sortRelevance: "search.sort.relevance";
63
89
  readonly sortNewest: "search.sort.newest";
@@ -115,6 +141,9 @@ export declare const SEARCH_I18N_KEYS: {
115
141
  /** The leading, icon-only chip: the whole panel. Icon-only means the name
116
142
  * exists ONLY here, so this key is the control's entire accessibility. */
117
143
  readonly filtersAll: "search.filters.all";
144
+ /** The same door, named for a 390px row: the location line's trailing link
145
+ * sits beside a place name and cannot spend six characters on "All". */
146
+ readonly filtersShort: "search.filters.short";
118
147
  /** A chip filtering on more than one value: "Bosch, +2". Not a plural
119
148
  * family — nothing is being counted in words. */
120
149
  readonly filtersChipMore: "search.filters.chip_more";
@@ -179,6 +208,32 @@ export declare const SEARCH_I18N_KEYS: {
179
208
  readonly rankingInactive: "search.ranking.inactive";
180
209
  readonly rankingNotes: "search.ranking.notes";
181
210
  readonly rankingLink: "search.ranking.link";
211
+ /**
212
+ * What the MENU calls the results screen.
213
+ *
214
+ * Separate from `search.results.title` on purpose. That key captions the
215
+ * list of matches — a heading over rows, which is why it reads "Results" —
216
+ * and a menu entry pointing at `/s` labelled "Results" tells a visitor
217
+ * nothing about where they would be going: results of what? The destination
218
+ * is the search itself. One key, one job; a translator asked to make
219
+ * "Results" work as both a page heading and a menu label has to pick which
220
+ * one to get wrong.
221
+ */
222
+ readonly navResults: "search.nav.results";
223
+ /** The menu name of the P2B disclosure PAGE — see {@link navResults} for
224
+ * why it is not `search.ranking.title`, which is the page's own heading and
225
+ * a whole sentence. */
226
+ readonly navRanking: "search.nav.ranking";
227
+ /**
228
+ * The compact form for a phone dock — see `NavEntry.shortLabelKey`. A
229
+ * five-cell dock at 390px gives a destination roughly ten characters, and
230
+ * "Ranking disclosure" ellipsizes to a fragment there.
231
+ *
232
+ * `search.nav.results` needs no short form: it is already one word in every
233
+ * locale this pair ships, and declaring a short key identical to the long
234
+ * one only gives a translator two strings to keep in sync.
235
+ */
236
+ readonly navRankingShort: "search.nav.ranking.short";
182
237
  };
183
238
  export type SearchI18nKey = (typeof SEARCH_I18N_KEYS)[keyof typeof SEARCH_I18N_KEYS];
184
239
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,gBAAgB;;;;;;;IAS3B;;;;;;;OAOG;;IAEH;kEAC8D;;IAE9D,sEAAsE;;;;;;;IAOtE,yDAAyD;;;;;;;IAOzD,yEAAyE;;;;;;IAQzE,4DAA4D;;;;;;;;;IAa5D;2CACuC;;;;;;;;;;;;IAcvC,oEAAoE;;;;;;;IAOpE;kEAC8D;;;IAG9D,4DAA4D;;IAE5D;wEACoE;;IAEpE;;;sCAGkC;;;;IAMlC,2DAA2D;;IAE3D;0BACsB;;IAEtB,uEAAuE;;;IAGvE;mEAC+D;;IAE/D;8EAC0E;;IAE1E;qDACiD;;;;;;;;;IAejD;iFAC6E;;;;;;IAQ7E;;;;;;;;OAQG;;;IAGH;;;;;;;;;;OAUG;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCK,CAAC;AAEX,MAAM,MAAM,aAAa,GACvB,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC;AAE3D;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,aAAa,EAM3D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA2IrD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAEzD"}
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,gBAAgB;;;;;;;IAS3B;;;;;;;OAOG;;IAEH;kEAC8D;;IAE9D,sEAAsE;;;;;;;IAOtE,yDAAyD;;;;;;;IAOzD,yEAAyE;;IAEzE;;kCAE8B;;IAE9B;4EACwE;;IAExE;sEACkE;;;;;;IAQlE,4DAA4D;;IAE5D;;;;;OAKG;;IAEH;;;;;;;OAOG;;;;;;;;;IAaH;2CACuC;;;;;;;;;;;;IAcvC,oEAAoE;;;;;;;IAOpE;kEAC8D;;;IAG9D,4DAA4D;;IAE5D;wEACoE;;IAEpE;;;sCAGkC;;;;IAMlC,2DAA2D;;IAE3D;0BACsB;;IAEtB,uEAAuE;;;IAGvE;mEAC+D;;IAE/D;8EAC0E;;IAE1E;4EACwE;;IAExE;qDACiD;;;;;;;;;IAejD;iFAC6E;;;;;;IAQ7E;;;;;;;;OAQG;;;IAGH;;;;;;;;;;OAUG;;;;;;;;;;;;;;;;;;;;;;;;;;IAkCH;;;;;;;;;;OAUG;;IAEH;;2BAEuB;;IAEvB;;;;;;;;OAQG;;CAEK,CAAC;AAEX,MAAM,MAAM,aAAa,GACvB,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC;AAE3D;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,aAAa,EAO3D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAsJrD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAEzD"}
package/dist/i18n/keys.js CHANGED
@@ -53,6 +53,16 @@ export const SEARCH_I18N_KEYS = {
53
53
  resultsOpen: "search.results.open",
54
54
  /** Alt text for the card photo — the card's own title, in a sentence. */
55
55
  resultsImageAlt: "search.results.image_alt",
56
+ /** Alt text for one photo of a GALLERY: its place in the strip, and the
57
+ * card's title. A row of ten photos all called "Photo of X" is ten
58
+ * identical announcements. */
59
+ resultsPhotoAlt: "search.results.photo_alt",
60
+ /** The photo strip's accessible name. `SkinCarousel` takes its label from
61
+ * the caller for exactly this reason: the token bridge owns no i18n. */
62
+ resultsPhotos: "search.results.photos",
63
+ /** The card stores a reference and nothing resolved it — a sentence gets
64
+ * the wiring fixed; an empty grey box teaches nobody anything. */
65
+ resultsPhotoUnavailable: "search.results.photo_unavailable",
56
66
  // ── the query box ────────────────────────────────────────────────────────
57
67
  boxLabel: "search.box.label",
58
68
  boxPlaceholder: "search.box.placeholder",
@@ -60,6 +70,22 @@ export const SEARCH_I18N_KEYS = {
60
70
  boxClear: "search.box.clear",
61
71
  /** Accessible name of the suggestion list under the box. */
62
72
  boxSuggestions: "search.box.suggestions",
73
+ /**
74
+ * The heading over the CATEGORY half of the type-ahead (stapel-search
75
+ * 0.7.0). The rows under it are DESTINATIONS — a section of the catalogue —
76
+ * and not, like everything else in the menu, another set of words to search
77
+ * for; a group with no heading would read as more of the same.
78
+ */
79
+ boxCategories: "search.box.categories",
80
+ /**
81
+ * "1 240 listings" beside a category row — how many LIVE listings a buyer
82
+ * would see there, the same number the SERP reports for it.
83
+ *
84
+ * A PLURAL FAMILY: render with `tPlural`, never `t`. It counts a noun in
85
+ * words, so Russian needs its four endings and a single string would be
86
+ * right only for 5-20 — the exact defect the results count already fixed.
87
+ */
88
+ boxCategoryCount: "search.box.category_count",
63
89
  // ── sort ─────────────────────────────────────────────────────────────────
64
90
  sortLabel: "search.sort.label",
65
91
  sortRelevance: "search.sort.relevance",
@@ -121,6 +147,9 @@ export const SEARCH_I18N_KEYS = {
121
147
  /** The leading, icon-only chip: the whole panel. Icon-only means the name
122
148
  * exists ONLY here, so this key is the control's entire accessibility. */
123
149
  filtersAll: "search.filters.all",
150
+ /** The same door, named for a 390px row: the location line's trailing link
151
+ * sits beside a place name and cannot spend six characters on "All". */
152
+ filtersShort: "search.filters.short",
124
153
  /** A chip filtering on more than one value: "Bosch, +2". Not a plural
125
154
  * family — nothing is being counted in words. */
126
155
  filtersChipMore: "search.filters.chip_more",
@@ -192,6 +221,33 @@ export const SEARCH_I18N_KEYS = {
192
221
  rankingInactive: "search.ranking.inactive",
193
222
  rankingNotes: "search.ranking.notes",
194
223
  rankingLink: "search.ranking.link",
224
+ // ── nav (the destination's own name, not the page's heading) ─────────────
225
+ /**
226
+ * What the MENU calls the results screen.
227
+ *
228
+ * Separate from `search.results.title` on purpose. That key captions the
229
+ * list of matches — a heading over rows, which is why it reads "Results" —
230
+ * and a menu entry pointing at `/s` labelled "Results" tells a visitor
231
+ * nothing about where they would be going: results of what? The destination
232
+ * is the search itself. One key, one job; a translator asked to make
233
+ * "Results" work as both a page heading and a menu label has to pick which
234
+ * one to get wrong.
235
+ */
236
+ navResults: "search.nav.results",
237
+ /** The menu name of the P2B disclosure PAGE — see {@link navResults} for
238
+ * why it is not `search.ranking.title`, which is the page's own heading and
239
+ * a whole sentence. */
240
+ navRanking: "search.nav.ranking",
241
+ /**
242
+ * The compact form for a phone dock — see `NavEntry.shortLabelKey`. A
243
+ * five-cell dock at 390px gives a destination roughly ten characters, and
244
+ * "Ranking disclosure" ellipsizes to a fragment there.
245
+ *
246
+ * `search.nav.results` needs no short form: it is already one word in every
247
+ * locale this pair ships, and declaring a short key identical to the long
248
+ * one only gives a translator two strings to keep in sync.
249
+ */
250
+ navRankingShort: "search.nav.ranking.short",
195
251
  };
196
252
  /**
197
253
  * The keys above that are PLURAL FAMILIES rather than single messages.
@@ -214,6 +270,7 @@ export const SEARCH_I18N_PLURAL_KEYS = [
214
270
  SEARCH_I18N_KEYS.resultsCountExact,
215
271
  SEARCH_I18N_KEYS.filtersShowCount,
216
272
  SEARCH_I18N_KEYS.filtersShowCountAtLeast,
273
+ SEARCH_I18N_KEYS.boxCategoryCount,
217
274
  ];
218
275
  /**
219
276
  * English fallback bundle for search-react UI keys + backend error codes.
@@ -246,11 +303,17 @@ export const searchI18nBundleEn = {
246
303
  "search.results.untitled": "Untitled",
247
304
  "search.results.open": "Open",
248
305
  "search.results.image_alt": "Photo of {title}",
306
+ "search.results.photo_alt": "Photo {index} of {total}: {title}",
307
+ "search.results.photos": "Photos",
308
+ "search.results.photo_unavailable": "Photo unavailable",
249
309
  "search.box.label": "Search",
250
310
  "search.box.placeholder": "What are you looking for?",
251
311
  "search.box.submit": "Search",
252
312
  "search.box.clear": "Clear the search",
253
313
  "search.box.suggestions": "Suggestions",
314
+ "search.box.categories": "Sections",
315
+ "search.box.category_count.one": "{count} listing",
316
+ "search.box.category_count.other": "{count} listings",
254
317
  "search.sort.label": "Sort",
255
318
  "search.sort.relevance": "Most relevant",
256
319
  "search.sort.newest": "Newest first",
@@ -290,6 +353,7 @@ export const searchI18nBundleEn = {
290
353
  "search.filters.dismiss": "Close the filters",
291
354
  "search.filters.chips_label": "Filters",
292
355
  "search.filters.all": "All filters",
356
+ "search.filters.short": "Filters",
293
357
  "search.filters.chip_more": ", +{count}",
294
358
  "search.category.title": "Category",
295
359
  "search.category.clear": "Search the whole catalogue",
@@ -330,6 +394,9 @@ export const searchI18nBundleEn = {
330
394
  "search.ranking.inactive": "Not applied: {reason}",
331
395
  "search.ranking.notes": "Notes",
332
396
  "search.ranking.link": "How these results are ordered",
397
+ "search.nav.results": "Search",
398
+ "search.nav.ranking": "Ranking disclosure",
399
+ "search.nav.ranking.short": "Ranking",
333
400
  };
334
401
  /**
335
402
  * Register the English bundle into a core i18n engine. Locale bundles ship as