@stapel/search-react 0.21.0 → 0.23.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 (86) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/MODULE.md +12 -0
  3. package/README.md +143 -2
  4. package/dist/api/types.d.ts +37 -3
  5. package/dist/api/types.d.ts.map +1 -1
  6. package/dist/api/types.js.map +1 -1
  7. package/dist/default/FacetGroupControl.d.ts +53 -5
  8. package/dist/default/FacetGroupControl.d.ts.map +1 -1
  9. package/dist/default/FacetGroupControl.js +189 -8
  10. package/dist/default/FacetGroupControl.js.map +1 -1
  11. package/dist/default/FacetPanelPane.d.ts +41 -0
  12. package/dist/default/FacetPanelPane.d.ts.map +1 -1
  13. package/dist/default/FacetPanelPane.js +72 -18
  14. package/dist/default/FacetPanelPane.js.map +1 -1
  15. package/dist/default/FilterChips.d.ts.map +1 -1
  16. package/dist/default/FilterChips.js +2 -2
  17. package/dist/default/FilterChips.js.map +1 -1
  18. package/dist/default/PartitionChips.d.ts +37 -0
  19. package/dist/default/PartitionChips.d.ts.map +1 -0
  20. package/dist/default/PartitionChips.js +120 -0
  21. package/dist/default/PartitionChips.js.map +1 -0
  22. package/dist/default/PopularValues.d.ts +58 -0
  23. package/dist/default/PopularValues.d.ts.map +1 -0
  24. package/dist/default/PopularValues.js +46 -0
  25. package/dist/default/PopularValues.js.map +1 -0
  26. package/dist/default/RangeFilterRow.d.ts.map +1 -1
  27. package/dist/default/RangeFilterRow.js +65 -11
  28. package/dist/default/RangeFilterRow.js.map +1 -1
  29. package/dist/default/SearchPage.d.ts +49 -0
  30. package/dist/default/SearchPage.d.ts.map +1 -1
  31. package/dist/default/SearchPage.js +43 -2
  32. package/dist/default/SearchPage.js.map +1 -1
  33. package/dist/default/index.d.ts +7 -3
  34. package/dist/default/index.d.ts.map +1 -1
  35. package/dist/default/index.js +8 -3
  36. package/dist/default/index.js.map +1 -1
  37. package/dist/headless/useFacetLabels.js +7 -7
  38. package/dist/headless/useFacetLabels.js.map +1 -1
  39. package/dist/i18n/es.d.ts.map +1 -1
  40. package/dist/i18n/es.js +9 -0
  41. package/dist/i18n/es.js.map +1 -1
  42. package/dist/i18n/keys.d.ts +52 -0
  43. package/dist/i18n/keys.d.ts.map +1 -1
  44. package/dist/i18n/keys.js +62 -0
  45. package/dist/i18n/keys.js.map +1 -1
  46. package/dist/i18n/ru.d.ts.map +1 -1
  47. package/dist/i18n/ru.js +9 -0
  48. package/dist/i18n/ru.js.map +1 -1
  49. package/dist/index.d.ts +5 -4
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +3 -2
  52. package/dist/index.js.map +1 -1
  53. package/dist/state/facets.d.ts +90 -6
  54. package/dist/state/facets.d.ts.map +1 -1
  55. package/dist/state/facets.js +216 -15
  56. package/dist/state/facets.js.map +1 -1
  57. package/dist/state/ranges.d.ts +22 -0
  58. package/dist/state/ranges.d.ts.map +1 -1
  59. package/dist/state/ranges.js +30 -0
  60. package/dist/state/ranges.js.map +1 -1
  61. package/dist/state/translit.d.ts +58 -0
  62. package/dist/state/translit.d.ts.map +1 -0
  63. package/dist/state/translit.js +115 -0
  64. package/dist/state/translit.js.map +1 -0
  65. package/llms.txt +3 -1
  66. package/manifest.json +45 -1
  67. package/nav-manifest.json +1 -1
  68. package/package.json +8 -8
  69. package/src/analytics/generated/events.json +1 -1
  70. package/src/api/types.ts +42 -3
  71. package/src/default/FacetGroupControl.tsx +339 -9
  72. package/src/default/FacetPanelPane.tsx +131 -19
  73. package/src/default/FilterChips.tsx +5 -2
  74. package/src/default/PartitionChips.tsx +204 -0
  75. package/src/default/PopularValues.tsx +151 -0
  76. package/src/default/RangeFilterRow.tsx +116 -1
  77. package/src/default/SearchPage.tsx +61 -2
  78. package/src/default/index.ts +27 -2
  79. package/src/headless/useFacetLabels.ts +7 -7
  80. package/src/i18n/es.ts +9 -0
  81. package/src/i18n/keys.ts +63 -0
  82. package/src/i18n/ru.ts +9 -0
  83. package/src/index.ts +13 -0
  84. package/src/state/facets.ts +259 -27
  85. package/src/state/ranges.ts +44 -0
  86. package/src/state/translit.ts +113 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,146 @@
1
1
  # @stapel/search-react
2
2
 
3
+ ## 0.23.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e8d7744: search: the rail is the category's own form, and the axes a seller had to fill are the ones a buyer sees first
8
+
9
+ A walk of a live classified's desktop cars page reported three things in one
10
+ breath, and they turned out to be four faults with one shape: the panel was
11
+ answering questions the _answer_ had asked, not the ones the _category_ asks.
12
+
13
+ **The rail is in SCHEMA order, required first.** It ranked by evidence — the
14
+ sum of an axis's counts — which is the right question for a phone chip row
15
+ with room for four and the wrong one for the column a person narrows a
16
+ catalogue in. On three listings the busiest axis is whichever three values
17
+ happen to be counted, so the rail opened on condition and colour while the
18
+ make, the model and the year — the three fields the category marks `mandatory`,
19
+ i.e. the three every seller had to fill — sat below them. `orderFacetGroupsBySchema`
20
+ puts pinned slugs first, then the required features in the schema's own order,
21
+ then the rest in schema order, then what the schema does not name at all in
22
+ evidence order, because with no schema there is no other order to have. Stable
23
+ under a click, which a rail that reshuffles as you tick is not. Past
24
+ `visibleGroups` (default 8) the tail folds under one **All filters (K)**
25
+ control — never a group you have already chosen a value in, because the control
26
+ that removes a filter is the one you came back for. `<SearchPage>` gains
27
+ `partition` (drawn above the price: which half of one template a page is about
28
+ is not a filter among filters) and `pinnedFacets`.
29
+
30
+ **Why the make could vanish.** `facetGroupIsDrawable` is now the one predicate
31
+ the rail and the chip row share, and it says what the old duplicated
32
+ `options.length > 0` said without saying WHY: a group with nothing under it is
33
+ a heading over nothing. The catalogue's `make`, `model`, `generation` and
34
+ `body_type` are `ref_select` features whose config is a bare `optionsRef`
35
+ pointer — no option table in the schema and there never will be — so the moment
36
+ the server's plan does not count one, there is nothing on either side to
37
+ enumerate and the group leaves the page, while every `select`-typed comfort
38
+ option (steering side, power steering, heating) draws its own schema table and
39
+ stays. That is the exact set the walker saw and did not see. The drop is still
40
+ right — a dead heading helps nobody — and it is no longer SILENT: outside
41
+ production one `console.warn` names the axis, says which side is missing
42
+ (uncounted, or a schema that does not define it), and says when the schema
43
+ calls the axis **required**. Both owners of that wiring fault can now see it
44
+ from the page. The regression is pinned against the live answer itself, saved
45
+ as a fixture (`test/liveCars.ts`, captured 2026-09-04): an axis with evidence
46
+ buckets survives the parent node's EMPTY feature list and a def that names it
47
+ without typing it.
48
+
49
+ **A dictionary outranks the pills, and on desktop it is a FIELD.** The live
50
+ make axis is `maxSelected: 1` over a 418-value vocabulary, so "pick one" won
51
+ the shape contest and the control it produced was four hundred pills in a 280px
52
+ rail — a wall with a different border radius. `facetGroupShape` now asks
53
+ "dictionary?" before "single-choice?". And on the rail
54
+ (`dictionaryMode="field"`, which `<SearchPage>` sets for the column layout) a
55
+ dictionary closes into a select-style field reading its chosen values or _Any_,
56
+ which opens the searchable list you already had: a real `role="combobox"`
57
+ button, ArrowDown to open, Escape to close. The phone sheet keeps the list
58
+ inline — the sheet is already the disclosure.
59
+
60
+ **A bounded integer is a picker, not a bare number.** The year was two empty
61
+ number fields; it is `int` with `min: 1900, max: 2027`, which is 128 values and
62
+ therefore a list. `RangeGroup.picker` carries it (newest first — a year picker
63
+ that opens on 1900 is a picker nobody uses) for any `int` feature whose schema
64
+ declares both bounds and spans at most `RANGE_PICKER_MAX_VALUES` (300), and
65
+ `<RangeFilterRow>` draws two from/to selects. Typing still works and carries
66
+ the bounds: a valid in-range number narrows the list, anything else brings the
67
+ whole list back with the bounds said in words, because a year below the
68
+ catalogue's floor otherwise does nothing at all, silently. A mileage
69
+ (`1..1000000`) and the core price stay two typed fields.
70
+
71
+ **The rail's scrollbar is in the gutter, not on the filters.**
72
+ `scrollbar-width: thin` and `scrollbar-gutter: stable` are the standard half
73
+ and they are not enough: on every overlay-scrollbar platform — a Mac by
74
+ default, every iOS browser — the bar is painted OVER the content and the gutter
75
+ reserves nothing, which is why it lay across the checkbox labels. The rail now
76
+ also declares a classic bar through the WebKit pseudo-elements, with a real
77
+ width so it displaces rather than overlaps, and every colour a `--stapel-*`
78
+ custom property so it is the panel's own hairline in both themes rather than a
79
+ grey that glows in the dark one. `railScrollbarCss()` and `RAIL_CLASS` are
80
+ exported for a host that lays out its own column.
81
+
82
+ **`<PartitionChips variant="segmented">`** is the desktop rail's shape of the
83
+ same choice: one joined control under its own label instead of a wrapping pill
84
+ row, which in a 280px column is two ragged lines. The SEMANTICS do not vary
85
+ with the variant — the same `radiogroup`, the same roving tabindex, the same
86
+ arrow keys — because a segmented look is a border-radius decision, and swapping
87
+ in a component that draws joined cells by giving up "exactly one of these is
88
+ true" would trade the accessible half of the control for the visible half.
89
+
90
+ Also: `FacetLabels.label_translatable` is typed. The live answer sends it
91
+ beside every group label and the pinned `schema.json` does not describe it, so
92
+ a fixture captured from the wire was a type error.
93
+
94
+ ## 0.22.0
95
+
96
+ ### Minor Changes
97
+
98
+ - 65e0c9f: search: headings come from the server, a vocabulary becomes a dictionary, and two browse surfaces for a category page
99
+
100
+ **A heading is named, or it is marked.** Group headings and option captions now
101
+ resolve in one stated order — `facet_labels` from the answer, then the category
102
+ feature definition, then the raw slug. The slug arm is not a fallback anyone may
103
+ ship: it renders, because a heading a person cannot read still beats options
104
+ with no heading at all, and it renders MARKED — `labelSource: "none"` on the
105
+ group and the option, `data-label-source` on the drawn control, and one
106
+ `console.warn` per slug outside production, so a storefront's own test fails on
107
+ it. Measured on a live classified's cars branch the page passed an empty feature
108
+ list and the whole rail was raw index slugs: the make group was on screen,
109
+ unlabelled, and the complaint that came back was "I cannot pick a make".
110
+ `FacetGroup` and `FacetOption` both carry `labelSource`
111
+ (`"server" | "schema" | "host" | "none"`) — "did anybody actually name this?" is
112
+ a question two surfaces have to answer and neither can answer by reading the
113
+ string.
114
+
115
+ **A vocabulary level is a DICTIONARY.** Past eight counted buckets a
116
+ `ref_select` group — or an untyped group that long, which is the live case where
117
+ no schema was threaded through — stops being eight checkboxes plus a
118
+ `Show all (418)` and becomes the busiest values, a search box over the rest, and
119
+ the chosen values pinned above it where a filter cannot go invisible. The box
120
+ matches ACROSS ALPHABETS: `тойота` finds `Toyota`, `тимберленд` finds
121
+ `Timberland`, `ровер` finds `Land Rover`. That is a prefix rule over two keys
122
+ per word — a transliteration, and its consonant skeleton, because the two
123
+ scripts disagree exactly on the vowels of a borrowed name (`timberlend` vs
124
+ `timberland`, both `tmbrlnd`). Table-driven and dependency-free, and local: the
125
+ buckets are already in the answer, so nothing is requested per keystroke.
126
+ `facetGroupShape` gains a `"dictionary"` arm beside `segmented`/`nested`/
127
+ `checkbox`, and the threshold counts EVIDENCE buckets — a zero-filled option
128
+ table or a schema-only tail is still a list a person can read.
129
+
130
+ **`<PopularValues>`** prints the busiest values of one group as a multi-column
131
+ `Toyota 802` block that applies the filter on click — a table of contents for a
132
+ category, drawn from the same drill-down counts the panel shows, so a value
133
+ cannot read `802` in one place and `93` in the other. `hidden` is a PROP rather
134
+ than a media query inside: whether a 390px screen has room for forty links is a
135
+ fact about the page, and the page is the storefront's.
136
+
137
+ **`<PartitionChips>`** is the single-select row a `chips` category draws instead
138
+ of a tile grid: `Все | child…` from `{id, path, name}`, controlled — the choice
139
+ is a `category` in the URL, not state in a chip row. It is a real `radiogroup`
140
+ with roving tabindex and arrow keys, because exactly one of them is true at a
141
+ time and a row of `aria-pressed` toggles announces the opposite: independent
142
+ switches, with no reason why pressing one released another.
143
+
3
144
  ## 0.21.0
4
145
 
5
146
  ### Minor Changes
package/MODULE.md CHANGED
@@ -207,6 +207,18 @@ Set on `<SearchPage>`, it reaches both filter surfaces: the desktop panel and
207
207
  the phone chip row read one `useFacetPanel`, so they cannot print two words for
208
208
  one value.
209
209
 
210
+ **The gap the seam does not close: EXISTENCE.** `resolveFacetLabels` names
211
+ values; it does not enumerate them. A `ref_select` group the server did not
212
+ count therefore has no options from either side — the answer counted nothing,
213
+ the schema holds a pointer — and `facetGroupIsDrawable` refuses to draw a
214
+ heading over nothing. Measured on a live cars leaf, that is exactly the set a
215
+ buyer misses (make, model, generation, body type) while every `select`-typed
216
+ option keeps its schema table. The client cannot invent the values, so the fix
217
+ is on the other side — the facet plan must count a required vocabulary axis —
218
+ and until it does, the disappearance is reported: one `console.warn` per slug
219
+ outside production, naming the axis, the missing side, and whether the schema
220
+ marks it required.
221
+
210
222
  ## The chip row's order is the row's product
211
223
 
212
224
  At 390px about four chips fit before the fold. On a live phone category the
package/README.md CHANGED
@@ -141,6 +141,147 @@ the raw value; a value nobody names keeps printing itself. `renderCategoryFilter
141
141
  also becomes the leading chip of the phone filter row, which is where the
142
142
  catalogue's deeper levels are chosen on a result list.
143
143
 
144
+ ## A heading is named by the server, or it is marked
145
+
146
+ Group headings and option captions resolve in one order and one only:
147
+ `facet_labels` from the answer → the category feature definition → the raw
148
+ slug. The last arm is not a fallback anyone ships: it renders (a heading a
149
+ person cannot read still beats options with no heading), and it renders
150
+ **marked** — `labelSource: "none"` on the group, `data-label-source="none"` on
151
+ the drawn control, and one `console.warn` per slug outside production. A
152
+ storefront's own test asserts on that attribute; measured on a live cars branch
153
+ the whole rail was raw slugs and the complaint that came back was "I cannot
154
+ pick a make".
155
+
156
+ Every `FacetGroup` and every `FacetOption` carries `labelSource:
157
+ "server" | "schema" | "host" | "none"`, so "did anybody actually name this?" is
158
+ answerable without looking at the string.
159
+
160
+ ## The rail is in schema order, required first
161
+
162
+ The panel used to rank its groups by **evidence** — the sum of an axis's
163
+ counts. That is the right question for a phone chip row with room for four and
164
+ the wrong one for the column a person narrows a catalogue in: on a live cars
165
+ page it opened on *condition* and *colour*, because with three listings the
166
+ busiest axis is whichever three values happen to be counted, while *make*,
167
+ *model* and *year* — the three fields the category marks `mandatory`, i.e. the
168
+ three every seller had to fill — sat below them.
169
+
170
+ The rail now follows the **category's own order**, in four bands: pinned slugs,
171
+ then `mandatory` features in schema order, then the rest in schema order, then
172
+ whatever the schema does not name at all (evidence order among themselves —
173
+ with no schema there is no other order to have). Stable under a click, which a
174
+ rail that reshuffles as you tick is not.
175
+
176
+ ```tsx
177
+ <SearchPage
178
+ partition={<PartitionChips variant="segmented" items={children} … />}
179
+ pinnedFacets={["body_type_ref_select"]}
180
+ />
181
+ orderFacetGroupsBySchema({ groups, categoryFeatures, pinned })
182
+ ```
183
+
184
+ Past `visibleGroups` (default `FACET_VISIBLE_GROUPS`, 8) the tail folds under
185
+ one **All filters (K)** control — except a group you have already chosen a
186
+ value in, which is never folded away: the control that removes a filter is the
187
+ one you came back for. The phone sheet passes `visibleGroups={null}`; a surface
188
+ devoted to filtering has the room.
189
+
190
+ `facetGroupIsDrawable(group)` is the one rule the rail and the chip row share
191
+ for "is there anything to draw here". A group with no options is a heading over
192
+ nothing — after `buildFacetGroups` learned to read the schema, what is left in
193
+ that state is a `ref_select` whose config is a bare `optionsRef` pointer and
194
+ which the server did not count, so there is nothing to enumerate from either
195
+ side. It is not drawn, and outside production it is **named**: a
196
+ `console.warn` says which axis went, why, and whether the schema calls it
197
+ required. That is the fault that took *make* off a live cars rail while every
198
+ `select`-typed comfort option drew its own table and stayed.
199
+
200
+ ## A vocabulary is a dictionary, not a checkbox list
201
+
202
+ Past eight **evidence buckets** a `ref_select` group (or an untyped group that
203
+ long — the live case, where no schema was threaded through) stops being a list
204
+ and becomes a dictionary: the busiest values, a search box that filters them
205
+ locally, and the chosen values pinned above the box so a filter is never
206
+ invisible. The box matches **across alphabets** — `тойота` finds `Toyota`,
207
+ `тимберленд` finds `Timberland`, `ровер` finds `Land Rover` — by a prefix rule
208
+ over two keys per word, a transliteration and its consonant skeleton
209
+ (`translitPrefixMatch`, `translitKey`, `consonantKey`; table-driven, no
210
+ dependency). No request per keystroke: the whole bucket list is already in the
211
+ answer — stapel-search caps a vocabulary-backed group at
212
+ `MAX_FACET_VALUES_VOCABULARY` (1000, raised from the shared 200 for exactly
213
+ this), so a 418-term make dictionary arrives whole and the box has everything
214
+ it filters.
215
+
216
+ A dictionary outranks the pills, too: the live `make` axis is `maxSelected: 1`
217
+ over a 418-value vocabulary, so "pick one" used to win and the control it drew
218
+ was four hundred pills in a 280px rail.
219
+
220
+ On the **desktop rail** a dictionary is a select-style FIELD
221
+ (`dictionaryMode="field"`, which `<SearchPage>` sets for the column layout):
222
+ closed it reads the chosen values or *Any*, and it opens the searchable list —
223
+ a real `role="combobox"` button, Enter/Space to open, ArrowDown to open,
224
+ Escape to close. The phone sheet keeps the list inline, because the sheet is
225
+ already the disclosure.
226
+
227
+ ```tsx
228
+ facetGroupShape(group) // "segmented" | "nested" | "checkbox" | "dictionary"
229
+ isDictionaryFacet(group) // > FACET_DICTIONARY_THRESHOLD counted buckets
230
+ ```
231
+
232
+ ## A bounded integer is a picker, not a bare number
233
+
234
+ A year is not a quantity a person computes, it is one of a hundred-odd values,
235
+ and on a live cars page it was two empty number fields. An `int` feature whose
236
+ schema declares both bounds and spans at most `RANGE_PICKER_MAX_VALUES` (300)
237
+ gets `RangeGroup.picker` — the value list, newest first — and
238
+ `<RangeFilterRow>` draws two from/to selects over it. Typing still works and
239
+ carries the bounds: a valid in-range number narrows the list, anything else
240
+ brings the whole list back **with the bounds said in words** ("from 1900 to
241
+ 2027"), because a year below the catalogue's floor otherwise does nothing at
242
+ all, silently. A mileage (`1..1000000`) and the core price stay two typed
243
+ fields.
244
+
245
+ ## The rail's scrollbar is in the gutter, not on the filters
246
+
247
+ The rail scrolls on its own when it outgrows the window. `scrollbar-width:
248
+ thin` plus `scrollbar-gutter: stable` is the standard half and it is not
249
+ enough: on every overlay-scrollbar platform (a Mac by default, every iOS
250
+ browser) the bar is drawn OVER the content and the gutter reserves nothing. So
251
+ the rail also declares a classic bar through the WebKit pseudo-elements, with a
252
+ real width and every colour a `--stapel-*` custom property, so it is the
253
+ panel's own hairline in both themes. `railScrollbarCss()` and `RAIL_CLASS` are
254
+ exported for a host that lays out its own column.
255
+
256
+ ## Two browse surfaces the page places
257
+
258
+ Both are exported from `./default` and neither lays itself out — where they
259
+ belong on a category page is the storefront's decision.
260
+
261
+ `<PopularValues>` prints the busiest values of one group as a multi-column
262
+ `Toyota 802` list that applies the filter on click — a table of contents for a
263
+ category, from the same drill-down counts the panel shows. `hidden` is a prop,
264
+ not a media query: whether a 390px screen has room is a fact about the page.
265
+
266
+ ```tsx
267
+ <PopularValues group={firstRefSelect} onApply={toggle} hidden={isPhone}
268
+ onShowAll={openPanel} />
269
+ ```
270
+
271
+ `<PartitionChips>` is the single-select row a `chips` category draws instead of
272
+ a tile grid: an "all" cell plus one per child, from `{id, path, name}`,
273
+ controlled (the choice is a `category` in the URL), a real `radiogroup` with
274
+ roving tabindex and arrow keys — because exactly one of them is true at a time,
275
+ and `aria-pressed` toggles say the opposite. `variant="segmented"` is the
276
+ desktop rail's shape (one joined control under its own label); the semantics do
277
+ not vary with it.
278
+
279
+ ```tsx
280
+ <PartitionChips items={children} value={state.category ?? null}
281
+ variant={isPhone ? "chips" : "segmented"}
282
+ onChange={(path) => patch({ category: path })} />
283
+ ```
284
+
144
285
  ## What the server admits, the screen repeats
145
286
 
146
287
  | The envelope says | The page says |
@@ -191,10 +332,10 @@ the configured engine cannot evaluate.
191
332
  | Layer | Exports |
192
333
  |---|---|
193
334
  | api | `createSearchApi`, `searchQueryParams`, `SEARCH_SORTS`, wire types |
194
- | state (pure) | `parseSearchState`, `writeSearchState`, `patchSearchState`, `toggleFilterValue`, `setFilterValues`, `setRangeValue`, `clearFilters`, `activeFilterCount`, `parseDegradations`, `countIsEstimate`, `buildFacetGroups`, `facetOptionLabel` |
335
+ | state (pure) | `parseSearchState`, `writeSearchState`, `patchSearchState`, `toggleFilterValue`, `setFilterValues`, `setRangeValue`, `clearFilters`, `activeFilterCount`, `parseDegradations`, `countIsEstimate`, `buildFacetGroups`, `orderFacetGroupsBySchema`, `facetGroupIsDrawable`, `facetGroupHasEvidence`, `facetOptionLabel`, `translitPrefixMatch`, `translitKey`, `consonantKey` |
195
336
  | model | `createSearchRuntime`, `searchQueryKeys`, `useSearchQuery`, `useRankingDisclosure` |
196
337
  | headless | `SearchProvider`, `SearchStateProvider`/`useSearchState`, `SearchResults`, `FacetPanel`, `RankingDisclosure` |
197
- | `./default` | `SearchPage`, `SearchResultsPane`, `FacetPanelPane`, `RankingDisclosurePane`, `SearchBox`, `SortSelect`, `PageSizeSelect`, `LanguageSelect`, `SearchResultCard`, `RangeFilterRow`, `DegradationNotice`, `UrlIssueNotice` (the skin themes itself through `SkinTheme` from `@stapel/tokens-antd/skin`; the pair's own `SearchSkinTheme` is gone as of 0.6.0) |
338
+ | `./default` | `SearchPage`, `SearchResultsPane`, `FacetPanelPane`, `RankingDisclosurePane`, `SearchBox`, `SortSelect`, `PageSizeSelect`, `LanguageSelect`, `SearchResultCard`, `RangeFilterRow`, `DegradationNotice`, `UrlIssueNotice`, `PopularValues`, `PartitionChips`, `facetGroupShape`, `isDictionaryFacet`, `railScrollbarCss`, `RAIL_CLASS`, `FACET_VISIBLE_GROUPS` (the skin themes itself through `SkinTheme` from `@stapel/tokens-antd/skin`; the pair's own `SearchSkinTheme` is gone as of 0.6.0) |
198
339
  | `./router` | `useRouterSearchParams` |
199
340
  | i18n | `registerSearchI18n` (+ `./i18n/ru`, `./i18n/es`) |
200
341
  | errors | `SEARCH_ERRORS`, `explainSearchError`, `SEARCH_WINDOW_EXCEEDED`, `SEARCH_BACKEND_UNAVAILABLE` |
@@ -11,10 +11,44 @@
11
11
  import type { components } from "./generated/schema.js";
12
12
  /** The generated schema table — the one source of truth for wire shapes. */
13
13
  export type Schemas = components["schemas"];
14
- /** `GET /query` 200 — the whole envelope. Corrected in one place: see
15
- * {@link FacetMeta}. */
16
- export type SearchResponse = Omit<Schemas["SearchResponse"], "facet_meta"> & {
14
+ /**
15
+ * One slug's captions: the GROUP's own name, and the words for its values.
16
+ *
17
+ * `label` is the group heading the server resolved from the category's
18
+ * feature definition, in the request's own language. It is the only source
19
+ * that always exists — `categoryFeatures` is an optional slot a live
20
+ * classified board never filled, and without it every heading in the panel
21
+ * was the raw index slug. `null` is the server saying it has no name for the
22
+ * slug either; ABSENT is a server too old to send one, and both read the same
23
+ * way here (fall through to the schema).
24
+ *
25
+ * WHAT THE GENERATOR LOST: the field is newer than this pair's pinned
26
+ * `schema.json`, so it is declared here as optional-and-nullable rather than
27
+ * regenerated into a shape a deployed older server does not send.
28
+ */
29
+ export type FacetLabels = Schemas["FacetLabels"] & {
30
+ readonly label?: string | null;
31
+ /**
32
+ * Whether {@link label} is a translation KEY rather than a caption, the way
33
+ * `translatable` says it for the values.
34
+ *
35
+ * Measured on a live answer (`category=141/151`, 2026-09-04): the server
36
+ * sends it beside every label — a make caption with
37
+ * `label_translatable: false`, a condition caption with `true` — and the
38
+ * describe it. Declared, not consumed: this pair passes the group heading
39
+ * through its translator either way, and a caption that is not a key comes
40
+ * back unchanged (`translate()` keeps a key it cannot resolve). Typed so a
41
+ * fixture captured from the wire is not a type error.
42
+ */
43
+ readonly label_translatable?: boolean;
44
+ };
45
+ /** `facet_labels` as a whole: `{slug: {label, translatable, values}}`. */
46
+ export type FacetLabelsMap = Readonly<Record<string, FacetLabels>>;
47
+ /** `GET /query` 200 — the whole envelope. Corrected in two places: see
48
+ * {@link FacetMeta} and {@link FacetLabels}. */
49
+ export type SearchResponse = Omit<Schemas["SearchResponse"], "facet_meta" | "facet_labels"> & {
17
50
  readonly facet_meta: FacetMeta;
51
+ readonly facet_labels: FacetLabelsMap;
18
52
  };
19
53
  /** One result row. `promoted` is present on EVERY item under EVERY sort — a
20
54
  * mandatory marking (DSA Art. 26), not an optional field, which is why the
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"AA0JA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,sBAAsB,CAAC;AAElE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,iBAAiB,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,MAAiC;IAC5D,OAAO,MAAM,EAAE,KAAK,IAAI,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;AAC9C,CAAC;AAQD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,YAAY,GAAsB;IAC7C,WAAW;IACX,QAAQ;IACR,WAAW;IACX,YAAY;IACZ,UAAU;CACX,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"AAiMA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,sBAAsB,CAAC;AAElE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,iBAAiB,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,MAAiC;IAC5D,OAAO,MAAM,EAAE,KAAK,IAAI,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC;AAC9C,CAAC;AAQD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,YAAY,GAAsB;IAC7C,WAAW;IACX,QAAQ;IACR,WAAW;IACX,YAAY;IACZ,UAAU;CACX,CAAC"}
@@ -9,8 +9,14 @@ import type { FacetGroup, FacetOption } from "../state/facets.js";
9
9
  * leaves a group of seven whole rather than truncated by one row.
10
10
  */
11
11
  export declare const FACET_VISIBLE_OPTIONS = 8;
12
- /** The three shapes a facet group takes. */
13
- export type FacetGroupShape = "segmented" | "nested" | "checkbox";
12
+ /**
13
+ * From how many values a group stops being a list and becomes a DICTIONARY —
14
+ * the same eight, because it is the same fold: past it the group is drawn as
15
+ * its busiest values plus a box that searches the rest.
16
+ */
17
+ export declare const FACET_DICTIONARY_THRESHOLD: number;
18
+ /** The four shapes a facet group takes. */
19
+ export type FacetGroupShape = "segmented" | "nested" | "checkbox" | "dictionary";
14
20
  /** The option rows of a group, already nested where the schema nests them. */
15
21
  export interface FacetOptionNode {
16
22
  readonly option: FacetOption;
@@ -18,10 +24,44 @@ export interface FacetOptionNode {
18
24
  readonly depth: number;
19
25
  }
20
26
  /**
21
- * Which of the three shapes a group takes.
27
+ * Is this group a DICTIONARY a vocabulary's level, too long to scroll?
28
+ *
29
+ * Two ways to be one, because the schema is an optional slot and the live
30
+ * case is the one where it is empty:
31
+ *
32
+ * - the def types the slug `ref_select`/`ref_hierarchical_select`, i.e. its
33
+ * config is a POINTER into a vocabulary and there was never an option
34
+ * table to draw;
35
+ * - there is NO def at all and the answer came back with more values than a
36
+ * fold. At a live classified's cars branch the storefront passed an empty
37
+ * feature list, so the 418 makes arrived as an unnamed, untyped group of
38
+ * 418 checkboxes behind "Show all (418)". A box is the only control that
39
+ * answers that, and refusing to draw one because the schema is missing
40
+ * punishes the buyer for the wiring.
41
+ *
42
+ * Either way it takes more than {@link FACET_DICTIONARY_THRESHOLD} EVIDENCE
43
+ * buckets — values the answer actually counted. A zero-filled option table or
44
+ * a schema-only tail is a list a person can already read, and a box over it
45
+ * would search for values no document carries.
46
+ */
47
+ export declare function isDictionaryFacet(group: FacetGroup): boolean;
48
+ /**
49
+ * Which of the four shapes a group takes.
50
+ *
51
+ * Order matters, and it changed in one place after a live measurement:
22
52
  *
23
- * Order matters: a hierarchical facet is nested even when it is single-choice,
24
- * because losing the tree costs more than losing the pills.
53
+ * - a hierarchical facet is nested even when it is single-choice, because
54
+ * losing the tree costs more than losing the pills;
55
+ * - a DICTIONARY outranks the pills. The make axis on the live cars leaf
56
+ * `maxSelected: 1` over a 418-value vocabulary, so "pick one" won and the
57
+ * control it produced was four hundred pills in a 280px rail — a wall
58
+ * with a different border radius. Above the fold the shape a person needs
59
+ * is a search box, whether or not they may tick two; below it,
60
+ * single-choice still means pills, because `isDictionaryFacet` requires
61
+ * more than {@link FACET_DICTIONARY_THRESHOLD} counted buckets;
62
+ * - and a dictionary is a dictionary before it is a checkbox list, because
63
+ * the checkbox list is the shape it was drawn as when nobody could pick a
64
+ * make.
25
65
  */
26
66
  export declare function facetGroupShape(group: FacetGroup): FacetGroupShape;
27
67
  /** The group's options in render order, carrying the depth the schema gives. */
@@ -45,6 +85,14 @@ export interface FacetGroupControlProps {
45
85
  /** Whether a `collapsible` group STARTS open. Default `true`. The initial
46
86
  * value only — the person owns the state after the first click. */
47
87
  readonly defaultOpen?: boolean;
88
+ /**
89
+ * How a `"dictionary"` group is drawn. `"field"` is the desktop shape — a
90
+ * select-style field reading its chosen values or "Any", which opens the
91
+ * searchable list; `"inline"` (the default) keeps the list open, the shape
92
+ * a phone sheet wants because the sheet is already the disclosure.
93
+ * Meaningless for the other three shapes.
94
+ */
95
+ readonly dictionaryMode?: "field" | "inline";
48
96
  }
49
97
  export declare function FacetGroupControl(props: FacetGroupControlProps): ReactElement;
50
98
  //# sourceMappingURL=FacetGroupControl.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FacetGroupControl.d.ts","sourceRoot":"","sources":["../../src/default/FacetGroupControl.tsx"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAC;AAMzD,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGlE;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,4CAA4C;AAC5C,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAElE,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAwCD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,eAAe,CAMlE;AAWD,gFAAgF;AAChF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,eAAe,EAAE,CAkC9E;AAgJD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD;2EACuE;IACvE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B;gFAC4E;IAC5E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;uEACmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,YAAY,CAsH7E"}
1
+ {"version":3,"file":"FacetGroupControl.d.ts","sourceRoot":"","sources":["../../src/default/FacetGroupControl.tsx"],"names":[],"mappings":"AAiEA,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAC;AAUzD,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAIlE;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,EAAE,MAA8B,CAAC;AAExE,2CAA2C;AAC3C,MAAM,MAAM,eAAe,GACvB,WAAW,GACX,QAAQ,GACR,UAAU,GACV,YAAY,CAAC;AAEjB,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAwCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAS5D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,eAAe,CAOlE;AAWD,gFAAgF;AAChF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,eAAe,EAAE,CAkC9E;AA2XD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD;2EACuE;IACvE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B;gFAC4E;IAC5E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;uEACmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC9C;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,YAAY,CA2I7E"}