@stapel/search-react 0.20.0 → 0.22.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.
- package/CHANGELOG.md +138 -0
- package/README.md +65 -2
- package/dist/api/generated/schema.d.ts +96 -4
- package/dist/api/generated/schema.d.ts.map +1 -1
- package/dist/api/types.d.ts +68 -6
- package/dist/api/types.d.ts.map +1 -1
- package/dist/api/types.js.map +1 -1
- package/dist/default/FacetGroupControl.d.ts +34 -4
- package/dist/default/FacetGroupControl.d.ts.map +1 -1
- package/dist/default/FacetGroupControl.js +112 -7
- package/dist/default/FacetGroupControl.js.map +1 -1
- package/dist/default/FacetPanelPane.d.ts.map +1 -1
- package/dist/default/FacetPanelPane.js +27 -1
- package/dist/default/FacetPanelPane.js.map +1 -1
- package/dist/default/LocationSummaryLine.d.ts +3 -0
- package/dist/default/LocationSummaryLine.d.ts.map +1 -1
- package/dist/default/LocationSummaryLine.js +55 -8
- package/dist/default/LocationSummaryLine.js.map +1 -1
- package/dist/default/PartitionChips.d.ts +23 -0
- package/dist/default/PartitionChips.d.ts.map +1 -0
- package/dist/default/PartitionChips.js +94 -0
- package/dist/default/PartitionChips.js.map +1 -0
- package/dist/default/PopularValues.d.ts +58 -0
- package/dist/default/PopularValues.d.ts.map +1 -0
- package/dist/default/PopularValues.js +46 -0
- package/dist/default/PopularValues.js.map +1 -0
- package/dist/default/SearchPage.d.ts.map +1 -1
- package/dist/default/SearchPage.js +5 -0
- package/dist/default/SearchPage.js.map +1 -1
- package/dist/default/index.d.ts +5 -1
- package/dist/default/index.d.ts.map +1 -1
- package/dist/default/index.js +6 -1
- package/dist/default/index.js.map +1 -1
- package/dist/headless/FacetPanel.d.ts +29 -1
- package/dist/headless/FacetPanel.d.ts.map +1 -1
- package/dist/headless/FacetPanel.js +14 -0
- package/dist/headless/FacetPanel.js.map +1 -1
- package/dist/headless/SearchStateProvider.d.ts +15 -0
- package/dist/headless/SearchStateProvider.d.ts.map +1 -1
- package/dist/headless/SearchStateProvider.js +38 -3
- package/dist/headless/SearchStateProvider.js.map +1 -1
- package/dist/headless/useFacetLabels.js +7 -7
- package/dist/headless/useFacetLabels.js.map +1 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +12 -0
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/keys.d.ts +72 -0
- package/dist/i18n/keys.d.ts.map +1 -1
- package/dist/i18n/keys.js +86 -0
- package/dist/i18n/keys.js.map +1 -1
- package/dist/i18n/ru.d.ts.map +1 -1
- package/dist/i18n/ru.js +14 -0
- package/dist/i18n/ru.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/state/degradations.d.ts +9 -0
- package/dist/state/degradations.d.ts.map +1 -1
- package/dist/state/degradations.js +13 -0
- package/dist/state/degradations.js.map +1 -1
- package/dist/state/facets.d.ts +29 -6
- package/dist/state/facets.d.ts.map +1 -1
- package/dist/state/facets.js +120 -16
- package/dist/state/facets.js.map +1 -1
- package/dist/state/translit.d.ts +58 -0
- package/dist/state/translit.d.ts.map +1 -0
- package/dist/state/translit.js +115 -0
- package/dist/state/translit.js.map +1 -0
- package/dist/state/urlState.d.ts +26 -1
- package/dist/state/urlState.d.ts.map +1 -1
- package/dist/state/urlState.js +43 -17
- package/dist/state/urlState.js.map +1 -1
- package/llms.txt +4 -2
- package/manifest.json +49 -2
- package/nav-manifest.json +1 -1
- package/package.json +8 -8
- package/src/analytics/generated/events.json +1 -1
- package/src/api/generated/schema.ts +96 -4
- package/src/api/types.ts +77 -5
- package/src/default/FacetGroupControl.tsx +213 -8
- package/src/default/FacetPanelPane.tsx +36 -1
- package/src/default/LocationSummaryLine.tsx +114 -53
- package/src/default/PartitionChips.tsx +161 -0
- package/src/default/PopularValues.tsx +151 -0
- package/src/default/SearchPage.tsx +5 -0
- package/src/default/index.ts +15 -0
- package/src/headless/FacetPanel.tsx +49 -1
- package/src/headless/SearchStateProvider.tsx +59 -3
- package/src/headless/useFacetLabels.ts +7 -7
- package/src/i18n/es.ts +16 -0
- package/src/i18n/keys.ts +91 -0
- package/src/i18n/ru.ts +20 -0
- package/src/index.ts +12 -0
- package/src/state/degradations.ts +14 -0
- package/src/state/facets.ts +154 -28
- package/src/state/translit.ts +113 -0
- package/src/state/urlState.ts +72 -13
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGxE,OAAO,EACL,YAAY,EACZ,2BAA2B,EAC3B,uBAAuB,EACvB,YAAY,GACb,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGxE,OAAO,EACL,YAAY,EACZ,2BAA2B,EAC3B,uBAAuB,EACvB,YAAY,GACb,MAAM,gBAAgB,CAAC;AA0BxB,gFAAgF;AAChF,OAAO,EACL,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAS7B,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,SAAS,EACT,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,GACb,MAAM,yBAAyB,CAAC;AAMjC,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAQ3B,OAAO,EACL,YAAY,EACZ,WAAW,EACX,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,aAAa,GACd,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,sBAAsB,EACtB,8BAA8B,EAC9B,wBAAwB,EACxB,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,gFAAgF;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAMzD,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGvD,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEtF,gFAAgF;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAMxF,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAErE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAKlE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/E,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAM/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAGpE,gFAAgF;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,gFAAgF;AAChF,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AAGxB,gFAAgF;AAChF,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,0BAA0B,EAC1B,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC"}
|
|
@@ -14,6 +14,15 @@
|
|
|
14
14
|
* with the literal beside it, not silence.
|
|
15
15
|
*/
|
|
16
16
|
import type { SearchDegradation, SearchDegradationKind } from "../api/types.js";
|
|
17
|
+
/**
|
|
18
|
+
* "This answer's facet plan could not be drawn from evidence."
|
|
19
|
+
*
|
|
20
|
+
* Named rather than inlined because the FACET PANEL reads the raw literal
|
|
21
|
+
* off the envelope: whether "this search offers no filters" is a true
|
|
22
|
+
* sentence is a question about THIS answer, not about the banner, and the
|
|
23
|
+
* panel must not have to parse the whole list to ask it.
|
|
24
|
+
*/
|
|
25
|
+
export declare const FACET_PLAN_EVIDENCE = "facet_plan_evidence";
|
|
17
26
|
/** i18n key per kind (`search.degraded.*`). */
|
|
18
27
|
export declare function degradationMessageKey(kind: SearchDegradationKind): string;
|
|
19
28
|
/** Parse and de-duplicate the envelope's `degraded[]`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"degradations.d.ts","sourceRoot":"","sources":["../../src/state/degradations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"degradations.d.ts","sourceRoot":"","sources":["../../src/state/degradations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAIhF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AAczD,+CAA+C;AAC/C,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,qBAAqB,GAAG,MAAM,CAEzE;AAED,0DAA0D;AAC1D,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GACtC,SAAS,iBAAiB,EAAE,CAwB9B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,OAAO,EACnB,YAAY,EAAE,SAAS,iBAAiB,EAAE,GACzC,OAAO,CAET;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;AAE/D;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,YAAY,EAAE,OAAO,GAAG,SAAS,EACjC,UAAU,EAAE,OAAO,EACnB,YAAY,EAAE,SAAS,iBAAiB,EAAE,GACzC,eAAe,CAIjB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,SAAS,iBAAiB,EAAE,GACzC,OAAO,CAKT;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,yBAAyB,GAAG,QAAQ,GAAG,UAAU,CAAC;AAa9D,sDAAsD;AACtD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,qBAAqB,GAC1B,yBAAyB,CAE3B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAC1B,YAAY,EAAE,SAAS,iBAAiB,EAAE,GACzC,SAAS,iBAAiB,EAAE,CAI9B"}
|
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
const SCORER_PREFIX = "scorer:";
|
|
2
|
+
/**
|
|
3
|
+
* "This answer's facet plan could not be drawn from evidence."
|
|
4
|
+
*
|
|
5
|
+
* Named rather than inlined because the FACET PANEL reads the raw literal
|
|
6
|
+
* off the envelope: whether "this search offers no filters" is a true
|
|
7
|
+
* sentence is a question about THIS answer, not about the banner, and the
|
|
8
|
+
* panel must not have to parse the whole list to ask it.
|
|
9
|
+
*/
|
|
10
|
+
export const FACET_PLAN_EVIDENCE = "facet_plan_evidence";
|
|
2
11
|
const KNOWN = {
|
|
3
12
|
typo_tolerance: "typo_tolerance",
|
|
4
13
|
phrase_synonyms: "phrase_synonyms",
|
|
5
14
|
exact_total: "exact_total",
|
|
6
15
|
exact_facet_counts: "exact_facet_counts",
|
|
7
16
|
category_rollup: "category_rollup",
|
|
17
|
+
// Reader-facing by omission from OPERATOR_KINDS below, and deliberately:
|
|
18
|
+
// it changes what an empty filter panel MEANS, which is the one thing on
|
|
19
|
+
// the page a buyer can act on (widen, or filter by category instead).
|
|
20
|
+
[FACET_PLAN_EVIDENCE]: "facet_plan_evidence",
|
|
8
21
|
};
|
|
9
22
|
/** i18n key per kind (`search.degraded.*`). */
|
|
10
23
|
export function degradationMessageKey(kind) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"degradations.js","sourceRoot":"","sources":["../../src/state/degradations.ts"],"names":[],"mappings":"AAiBA,MAAM,aAAa,GAAG,SAAS,CAAC;AAEhC,MAAM,KAAK,GAAoD;IAC7D,cAAc,EAAE,gBAAgB;IAChC,eAAe,EAAE,iBAAiB;IAClC,WAAW,EAAE,aAAa;IAC1B,kBAAkB,EAAE,oBAAoB;IACxC,eAAe,EAAE,iBAAiB;
|
|
1
|
+
{"version":3,"file":"degradations.js","sourceRoot":"","sources":["../../src/state/degradations.ts"],"names":[],"mappings":"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"}
|
package/dist/state/facets.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import type { FeatureDef } from "@stapel/attributes-react";
|
|
2
|
-
import type { FacetMeta, SearchQueryState } from "../api/types.js";
|
|
2
|
+
import type { FacetLabelsMap, FacetMeta, SearchQueryState } from "../api/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Where a caption came from — the group's heading and every option carry it,
|
|
5
|
+
* because "did anybody actually name this?" is a question two surfaces have
|
|
6
|
+
* to answer and neither can answer by looking at the string.
|
|
7
|
+
*
|
|
8
|
+
* `"none"` is the honest bottom: the raw slug for a heading, the raw index
|
|
9
|
+
* term for an option. It is not a label, it is what is printed when there is
|
|
10
|
+
* none, and it is the one value a storefront's own test asserts against.
|
|
11
|
+
*/
|
|
12
|
+
export type FacetLabelSource = "server" | "schema" | "host" | "none";
|
|
3
13
|
/**
|
|
4
14
|
* Value types whose values are a BOUNDED OPTION SET — the only kind of
|
|
5
15
|
* feature a person can be offered as a filter.
|
|
@@ -42,13 +52,21 @@ export interface FacetOption {
|
|
|
42
52
|
readonly count: number | null;
|
|
43
53
|
/** Resolved through the category schema; the raw value when there is none. */
|
|
44
54
|
readonly label: string;
|
|
55
|
+
/** Which of the three sources named it — `"none"` means {@link label} IS
|
|
56
|
+
* the raw index term. The host seam reads this rather than comparing the
|
|
57
|
+
* two strings. */
|
|
58
|
+
readonly labelSource: FacetLabelSource;
|
|
45
59
|
readonly selected: boolean;
|
|
46
60
|
}
|
|
47
61
|
/** One facet slug, with its options. */
|
|
48
62
|
export interface FacetGroup {
|
|
49
63
|
readonly slug: string;
|
|
50
|
-
/** The
|
|
64
|
+
/** The group's heading: the answer's own `label`, else the feature's
|
|
65
|
+
* display name, else — with a dev warning — the raw slug. */
|
|
51
66
|
readonly label: string;
|
|
67
|
+
/** Which source named the heading. `"none"` is the slug standing in for a
|
|
68
|
+
* name nobody has; a surface marks it so a storefront test can fail on it. */
|
|
69
|
+
readonly labelSource: FacetLabelSource;
|
|
52
70
|
/** The category-schema entry behind the slug, when the host supplied one. */
|
|
53
71
|
readonly feature: FeatureDef | undefined;
|
|
54
72
|
/** `false` when the server skipped this slug — counts are `null`. */
|
|
@@ -113,15 +131,17 @@ export interface BuildFacetGroupsInput {
|
|
|
113
131
|
* `apple` or `chernyy`. The server read that vocabulary against the
|
|
114
132
|
* snapshot the documents were written with.
|
|
115
133
|
*
|
|
134
|
+
* Each entry also carries the GROUP's own `label` — the heading, resolved
|
|
135
|
+
* from the feature definition server-side and localized like everything
|
|
136
|
+
* else. It leads for the same two reasons, and it is what a category whose
|
|
137
|
+
* host passed no feature list has instead of a rail full of index slugs.
|
|
138
|
+
*
|
|
116
139
|
* ABSENT on a server older than 0.4.0 — absent, not empty — which is why
|
|
117
140
|
* every read of it here is optional-chained and every option falls through
|
|
118
141
|
* to the schema and then to its raw self. No labels are invented at any
|
|
119
142
|
* step.
|
|
120
143
|
*/
|
|
121
|
-
readonly facetLabels?:
|
|
122
|
-
readonly translatable: boolean;
|
|
123
|
-
readonly values: Readonly<Record<string, string>>;
|
|
124
|
-
}>>;
|
|
144
|
+
readonly facetLabels?: FacetLabelsMap;
|
|
125
145
|
/** Translator for label keys. */
|
|
126
146
|
readonly t?: (key: string) => string;
|
|
127
147
|
/** BCP-47 tag, forwarded to `formatFeatureValue` for `date` options. */
|
|
@@ -159,6 +179,9 @@ export declare function facetOptionLabel(feature: FeatureDef | undefined, value:
|
|
|
159
179
|
* a chip in the row. The APPLIED-filter clause outranks the type rule: a
|
|
160
180
|
* constraint the URL carries always gets its control back, whatever the schema
|
|
161
181
|
* now says about it, or a person is left holding a filter they cannot clear.
|
|
182
|
+
*
|
|
183
|
+
* A COUNTED group with zero coverage goes the same way, and for the same
|
|
184
|
+
* reason — see {@link keepsAnAxisOpen}.
|
|
162
185
|
*/
|
|
163
186
|
export declare function buildFacetGroups(input: BuildFacetGroupsInput): readonly FacetGroup[];
|
|
164
187
|
//# sourceMappingURL=facets.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facets.d.ts","sourceRoot":"","sources":["../../src/state/facets.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"facets.d.ts","sourceRoot":"","sources":["../../src/state/facets.ts"],"names":[],"mappings":"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"}
|
package/dist/state/facets.js
CHANGED
|
@@ -8,9 +8,12 @@
|
|
|
8
8
|
* they would have if you swapped to them. A panel that greys the siblings
|
|
9
9
|
* out has silently converted a drill-down facet into a naive one, and the
|
|
10
10
|
* e2e leg in the spec (§7.2) exists to catch exactly that.
|
|
11
|
-
* 2. **
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* 2. **LABELS have one stated order: ANSWER, then schema, then the raw
|
|
12
|
+
* term** — for the group's heading and for every option alike, with a host
|
|
13
|
+
* resolver between the last two for options, applied by
|
|
14
|
+
* `useHostFacetLabels` on what this module could not name. The bottom of
|
|
15
|
+
* that order is not a label and is marked as such: `labelSource: "none"`,
|
|
16
|
+
* a warning in development, and a data attribute on the drawn group.
|
|
14
17
|
*
|
|
15
18
|
* The answer leads because it is the only source that always exists and
|
|
16
19
|
* the only one that has RESOLVED anything: `categoryFeatures` is an
|
|
@@ -47,7 +50,29 @@
|
|
|
47
50
|
* chipped is therefore decided from the category's own feature defs, by
|
|
48
51
|
* value TYPE (see {@link FACETABLE_FEATURE_TYPES}), not by what came back.
|
|
49
52
|
*/
|
|
50
|
-
import { VOCABULARY_BACKED_TYPES, featureConfig,
|
|
53
|
+
import { VOCABULARY_BACKED_TYPES, featureConfig, featureType, formatFeatureValue, } from "@stapel/attributes-react";
|
|
54
|
+
/** Slugs already complained about — one warning per slug per page load, not
|
|
55
|
+
* one per render. */
|
|
56
|
+
const warnedSlugs = new Set();
|
|
57
|
+
/**
|
|
58
|
+
* A heading nobody named, said once, in development only.
|
|
59
|
+
*
|
|
60
|
+
* A raw slug in a filter panel is a wiring fault — the category has no
|
|
61
|
+
* feature definition for the slug and the server sent no label — and it is
|
|
62
|
+
* invisible to everyone but the buyer who meets it. The group still renders,
|
|
63
|
+
* because a heading a person cannot read still beats options with no heading
|
|
64
|
+
* at all; `FacetGroup.labelSource` is how a surface marks it up for a test.
|
|
65
|
+
*/
|
|
66
|
+
function warnUnnamedGroup(slug) {
|
|
67
|
+
const env = typeof process === "undefined" ? undefined : process.env;
|
|
68
|
+
if (env?.NODE_ENV === "production")
|
|
69
|
+
return;
|
|
70
|
+
if (warnedSlugs.has(slug))
|
|
71
|
+
return;
|
|
72
|
+
warnedSlugs.add(slug);
|
|
73
|
+
console.warn(`[search-react] facet group "${slug}" has no label: the answer sent none ` +
|
|
74
|
+
`and no category feature defines it, so the heading is the raw slug.`);
|
|
75
|
+
}
|
|
51
76
|
/**
|
|
52
77
|
* Value types whose values are a BOUNDED OPTION SET — the only kind of
|
|
53
78
|
* feature a person can be offered as a filter.
|
|
@@ -200,8 +225,44 @@ function declaredOptionValues(feature) {
|
|
|
200
225
|
* value — never a blank, and never a guess.
|
|
201
226
|
*/
|
|
202
227
|
function resolveLabel(input, feature, slug, value, labelOptions) {
|
|
203
|
-
|
|
204
|
-
|
|
228
|
+
const answer = serverLabel(input.facetLabels, slug, value, input.t);
|
|
229
|
+
if (answer !== undefined)
|
|
230
|
+
return { label: answer, labelSource: "server" };
|
|
231
|
+
const schema = facetOptionLabel(feature, value, labelOptions);
|
|
232
|
+
// `facetOptionLabel` hands back the value unchanged for an option it
|
|
233
|
+
// cannot name, and that identity is the whole test — a formatter that
|
|
234
|
+
// returned the term is a formatter that named nothing.
|
|
235
|
+
return schema === value
|
|
236
|
+
? { label: value, labelSource: "none" }
|
|
237
|
+
: { label: schema, labelSource: "schema" };
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The group's HEADING, in the one order the fleet states: the answer, then
|
|
241
|
+
* the category schema, then the slug under a dev warning.
|
|
242
|
+
*
|
|
243
|
+
* The answer leads because it is the only source that always exists. The
|
|
244
|
+
* schema slot is optional, and at a live classified's cars branch the
|
|
245
|
+
* storefront passed an empty feature list, so every heading in the rail was
|
|
246
|
+
* a raw index slug — the make group was on screen, unlabelled, and the
|
|
247
|
+
* complaint that came back was "I cannot pick a make".
|
|
248
|
+
*
|
|
249
|
+
* The slug is not a fallback anyone may ship: it renders, because a heading
|
|
250
|
+
* beats no heading, and it renders MARKED — `labelSource: "none"`, a warning
|
|
251
|
+
* in development, and a data attribute on the drawn group.
|
|
252
|
+
*/
|
|
253
|
+
function resolveGroupLabel(input, feature, slug) {
|
|
254
|
+
const answer = input.facetLabels?.[slug]?.label;
|
|
255
|
+
if (typeof answer === "string" && answer.length > 0) {
|
|
256
|
+
return { label: translate(input.t, answer), labelSource: "server" };
|
|
257
|
+
}
|
|
258
|
+
// `featureName` falls back to the slug itself, so the def has to be asked
|
|
259
|
+
// for a NAME rather than for a name-or-slug: a def with none names nothing.
|
|
260
|
+
const declared = feature?.name;
|
|
261
|
+
if (typeof declared === "string" && declared.length > 0) {
|
|
262
|
+
return { label: translate(input.t, declared), labelSource: "schema" };
|
|
263
|
+
}
|
|
264
|
+
warnUnnamedGroup(slug);
|
|
265
|
+
return { label: slug, labelSource: "none" };
|
|
205
266
|
}
|
|
206
267
|
/**
|
|
207
268
|
* A facet value as a person reads it, through
|
|
@@ -262,6 +323,9 @@ export function facetOptionLabel(feature, value, options) {
|
|
|
262
323
|
* a chip in the row. The APPLIED-filter clause outranks the type rule: a
|
|
263
324
|
* constraint the URL carries always gets its control back, whatever the schema
|
|
264
325
|
* now says about it, or a person is left holding a filter they cannot clear.
|
|
326
|
+
*
|
|
327
|
+
* A COUNTED group with zero coverage goes the same way, and for the same
|
|
328
|
+
* reason — see {@link keepsAnAxisOpen}.
|
|
265
329
|
*/
|
|
266
330
|
export function buildFacetGroups(input) {
|
|
267
331
|
const bySlug = new Map();
|
|
@@ -347,24 +411,64 @@ export function buildFacetGroups(input) {
|
|
|
347
411
|
};
|
|
348
412
|
return {
|
|
349
413
|
slug,
|
|
350
|
-
|
|
351
|
-
? slug
|
|
352
|
-
: translate(input.t, featureName(feature)),
|
|
414
|
+
...resolveGroupLabel(input, feature, slug),
|
|
353
415
|
feature,
|
|
354
416
|
counted,
|
|
355
417
|
selected,
|
|
356
418
|
options: values.map((value) => ({
|
|
357
419
|
value,
|
|
358
420
|
count: counted ? (counts[value] ?? 0) : null,
|
|
359
|
-
// The
|
|
360
|
-
//
|
|
361
|
-
//
|
|
362
|
-
|
|
363
|
-
// makes "did it resolve?" answerable without a second lookup.
|
|
364
|
-
label: resolveLabel(input, feature, slug, value, labelOptions),
|
|
421
|
+
// The answer's caption first, then the schema's own option table,
|
|
422
|
+
// then the raw value — and the source is carried rather than
|
|
423
|
+
// re-derived, so "did it resolve?" needs no second lookup.
|
|
424
|
+
...resolveLabel(input, feature, slug, value, labelOptions),
|
|
365
425
|
selected: selected.includes(value),
|
|
366
426
|
})),
|
|
367
427
|
};
|
|
368
|
-
});
|
|
428
|
+
}).filter(keepsAnAxisOpen);
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Is this group an axis a person can actually move along?
|
|
432
|
+
*
|
|
433
|
+
* The coverage floor stops at the queried category's own schema, on the
|
|
434
|
+
* server, on purpose: `FACET_MIN_COVERAGE` governs only the slugs an
|
|
435
|
+
* evidence plan BORROWED from sibling leaves, because "a closed option set
|
|
436
|
+
* answering with its zeros is a shipped decision". That is right about an
|
|
437
|
+
* option and wrong about a GROUP. A size chart showing `XL — 0` beside
|
|
438
|
+
* `M — 12` is telling the truth about a shape the reader wants to see whole;
|
|
439
|
+
* a group whose every option is 0 is not a shape, it is three checkboxes that
|
|
440
|
+
* are each guaranteed to return nothing, and it costs a heading in the rail
|
|
441
|
+
* and a chip on a 390px row to say so.
|
|
442
|
+
*
|
|
443
|
+
* Measured on the deployed phones leaf: `sim_config`, `device_history` and
|
|
444
|
+
* `set` are authored `select` features that no listing in the leaf fills.
|
|
445
|
+
* The server's `fill_zero_options` creates the slug and zero-fills every
|
|
446
|
+
* authored option, so all three arrive counted, complete and dead, and the
|
|
447
|
+
* withholding loop never looks at them because an authored plan has no
|
|
448
|
+
* `evidence`. Nothing on the wire marks them: the client has to sum the
|
|
449
|
+
* buckets itself, which is exactly what {@link facetCoverage} already does
|
|
450
|
+
* for two other surfaces.
|
|
451
|
+
*
|
|
452
|
+
* Three things this must NOT drop, which is why the predicate is this narrow:
|
|
453
|
+
*
|
|
454
|
+
* - an UNCOUNTED group (`counted: false`). Its options carry `count: null`,
|
|
455
|
+
* so it sums to zero for the opposite reason — nobody looked. "We did not
|
|
456
|
+
* count this" and "there are none" are different sentences, and dropping
|
|
457
|
+
* on the first is the regression the `MAX_FACET_FIELDS` branch below
|
|
458
|
+
* exists to prevent (a live cars leaf: 26 facetable features declared, 12
|
|
459
|
+
* counted, and `/query` accepts `f.<slug>` for all 26).
|
|
460
|
+
* - a group the reader has ALREADY filtered on, whatever its counts say —
|
|
461
|
+
* the same clause that outranks the type rule. Withholding that group
|
|
462
|
+
* leaves a constraint applied with no control to undo it.
|
|
463
|
+
* - a group with any non-zero option. A zero option beside a live one is
|
|
464
|
+
* drill-down working as designed: it reports what swapping to that value
|
|
465
|
+
* would get you, and the answer being "nothing" is information.
|
|
466
|
+
*/
|
|
467
|
+
function keepsAnAxisOpen(group) {
|
|
468
|
+
if (!group.counted)
|
|
469
|
+
return true;
|
|
470
|
+
if (group.selected.length > 0)
|
|
471
|
+
return true;
|
|
472
|
+
return facetCoverage(group) > 0;
|
|
369
473
|
}
|
|
370
474
|
//# sourceMappingURL=facets.js.map
|
package/dist/state/facets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facets.js","sourceRoot":"","sources":["../../src/state/facets.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"facets.js","sourceRoot":"","sources":["../../src/state/facets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,WAAW,EACX,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAiBlC;qBACqB;AACrB,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;AAEtC;;;;;;;;GAQG;AACH,SAAS,gBAAgB,CAAC,IAAY;IACpC,MAAM,GAAG,GAAG,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;IACrE,IAAI,GAAG,EAAE,QAAQ,KAAK,YAAY;QAAE,OAAO;IAC3C,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO;IAClC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACtB,OAAO,CAAC,IAAI,CACV,+BAA+B,IAAI,uCAAuC;QACxE,qEAAqE,CACxE,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAsB;IACxD,MAAM;IACN,qBAAqB;IACrB,GAAG,uBAAuB;IAC1B,QAAQ;CACT,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA+B;IAChE,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,uBAAuB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC;AAqCD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO;QAAE,KAAK,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;IAC/D,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,uBAAuB,CAAC,CAAa,EAAE,CAAa;IAClE,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/E,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACpC,OAAO,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,gBAAgB,CAC9B,MAA6B;IAE7B,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;AACnD,CAAC;AA2CD,SAAS,SAAS,CAAC,CAAwC,EAAE,GAAW;IACtE,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IAChC,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACxB,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;AAC9C,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,WAAW,CAClB,MAA4C,EAC5C,IAAY,EACZ,KAAa,EACb,CAAwC;IAExC,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACpE,OAAO,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AACxE,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,oBAAoB,CAAC,OAA+B;IAC3D,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACrC,MAAM,GAAG,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,MAAM,IAAI,GAAG,EAAE,CAAC;QACzB,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAClD,MAAM,KAAK,GAAI,MAA8B,CAAC,KAAK,CAAC;YACpD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;gBAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACrE,CAAC;aAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACtC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAS,YAAY,CACnB,KAA4B,EAC5B,OAA+B,EAC/B,IAAY,EACZ,KAAa,EACb,YAA8D;IAE9D,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACpE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;IAC1E,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;IAC9D,qEAAqE;IACrE,sEAAsE;IACtE,uDAAuD;IACvD,OAAO,MAAM,KAAK,KAAK;QACrB,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE;QACvC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;AAC/C,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,iBAAiB,CACxB,KAA4B,EAC5B,OAA+B,EAC/B,IAAY;IAEZ,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC;IAChD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpD,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;IACtE,CAAC;IACD,0EAA0E;IAC1E,4EAA4E;IAC5E,MAAM,QAAQ,GAAG,OAAO,EAAE,IAAI,CAAC;IAC/B,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxD,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;IACxE,CAAC;IACD,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAA+B,EAC/B,KAAa,EACb,OAA4E;IAE5E,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAErC,IAAI,OAAO,GAAY,KAAK,CAAC;IAC7B,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ,CAAC;QACd,KAAK,qBAAqB;YACxB,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC;YAClB,MAAM;QACR,KAAK,KAAK,CAAC;QACX,KAAK,OAAO,CAAC;QACb,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC7B,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;YACnD,MAAM;QACR,CAAC;QACD,KAAK,MAAM;YACT,OAAO,GAAG,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,GAAG,CAAC;YAC5C,MAAM;QACR;YACE,OAAO,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,MAAM,SAAS,GAAG,kBAAkB,CAClC,OAAO,EACP,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EACxB;QACE,GAAG,CAAC,OAAO,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrE,CACF,CAAC;IACF,OAAO,SAAS,KAAK,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;AAC7E,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAA4B;IAC3D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC7C,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,gBAAgB,IAAI,EAAE;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAEtF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI;QACjB,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;QAC5B,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO;QACrB,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;KACpC,EAAE,CAAC;QACF,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAC7B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACf,oEAAoE;QACpE,6DAA6D;QAC7D,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAC7D,IAAI,CAAC,OAAO,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAAE,SAAS;QAChE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IAED,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACxB,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC;QAC3D,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAEjD,wEAAwE;QACxE,sEAAsE;QACtE,yEAAyE;QACzE,oBAAoB;QACpB,MAAM,UAAU,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACjD,MAAM,QAAQ,GACZ,UAAU,CAAC,MAAM,GAAG,CAAC;YACnB,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;QAC3D,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,CAAC,KAAa,EAAQ,EAAE;YACnC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClD,CAAC,CAAC;QACF,IAAI,OAAO,EAAE,CAAC;YACZ,qEAAqE;YACrE,mEAAmE;YACnE,KAAK,MAAM,KAAK,IAAI,QAAQ;gBAAE,IAAI,KAAK,IAAI,MAAM;oBAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/D,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;iBAClC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;iBAC5C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7E,KAAK,MAAM,KAAK,IAAI,SAAS;gBAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7C,CAAC;aAAM,CAAC;YACN,sCAAsC;YACtC,EAAE;YACF,qEAAqE;YACrE,wDAAwD;YACxD,sEAAsE;YACtE,uEAAuE;YACvE,qEAAqE;YACrE,wEAAwE;YACxE,wEAAwE;YACxE,sEAAsE;YACtE,EAAE;YACF,uEAAuE;YACvE,sEAAsE;YACtE,iEAAiE;YACjE,mEAAmE;YACnE,+DAA+D;YAC/D,EAAE;YACF,oEAAoE;YACpE,wDAAwD;YACxD,oDAAoD;YACpD,KAAK,MAAM,KAAK,IAAI,QAAQ;gBAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,QAAQ;YAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAE1C,MAAM,YAAY,GAAG;YACnB,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChD,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChE,CAAC;QAEF,OAAO;YACL,IAAI;YACJ,GAAG,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC;YAC1C,OAAO;YACP,OAAO;YACP,QAAQ;YACR,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC9B,KAAK;gBACL,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;gBAC5C,kEAAkE;gBAClE,6DAA6D;gBAC7D,2DAA2D;gBAC3D,GAAG,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC;gBAC1D,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;aACnC,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,SAAS,eAAe,CAAC,KAAiB;IACxC,IAAI,CAAC,KAAK,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAChC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,OAAO,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matching a typed prefix against a dictionary value ACROSS ALPHABETS.
|
|
3
|
+
*
|
|
4
|
+
* A vocabulary of car makes holds `Toyota`, `Land Rover`, `Timberland` — Latin
|
|
5
|
+
* words, in a catalogue read by people typing Cyrillic. "toyota" spelled in
|
|
6
|
+
* Cyrillic and `Toyota` are the same word said in two scripts, and a
|
|
7
|
+
* `String.includes` over the caption answers "no" to every one of them. The
|
|
8
|
+
* autocatalog has 418 makes: without this, the search box in a dictionary
|
|
9
|
+
* facet is a box that works only for the half of the buyers whose keyboard
|
|
10
|
+
* matches the catalogue's.
|
|
11
|
+
*
|
|
12
|
+
* ── Two keys per word, and why the second one exists ──────────────────────
|
|
13
|
+
*
|
|
14
|
+
* The first key is a plain transliteration: Cyrillic → Latin through the table
|
|
15
|
+
* below, lowercased, everything but letters and digits dropped. That alone
|
|
16
|
+
* gets the Cyrillic spelling of "toyota" to `toiota` and `Toyota` to `toyota`
|
|
17
|
+
* — still not equal, and this is the general case rather than a bad table.
|
|
18
|
+
* Vowels are exactly where two scripts disagree: `i` against `y`, `e` against
|
|
19
|
+
* `a` in a borrowed word, `timberlend` against `timberland`. A vowel-perfect
|
|
20
|
+
* table does not exist, because the disagreement is in the borrowing, not in
|
|
21
|
+
* the letters.
|
|
22
|
+
*
|
|
23
|
+
* So the second key is the CONSONANT SKELETON: the first key with its vowels
|
|
24
|
+
* removed. `timberlend` and `timberland` are both `tmbrlnd`; `toiota` and
|
|
25
|
+
* `toyota` are both `tt`. Digits stay — a skeleton is about letters people
|
|
26
|
+
* disagree on, and `5` is not one.
|
|
27
|
+
*
|
|
28
|
+
* A candidate matches when either key of any of its WORDS starts with the
|
|
29
|
+
* corresponding key of the needle: a typed "rover" in either script finds
|
|
30
|
+
* `Land Rover`, and typing into the box narrows as you go, which is the whole
|
|
31
|
+
* point of a prefix rule. The skeleton arm is skipped for a needle that has no
|
|
32
|
+
* consonants at all — an empty skeleton is a prefix of everything, and a box
|
|
33
|
+
* that matches all 418 makes after one vowel is a box that has stopped
|
|
34
|
+
* answering.
|
|
35
|
+
*
|
|
36
|
+
* Table-driven and dependency-free on purpose: this is 40 pairs of characters,
|
|
37
|
+
* and a transliteration library is a page-weight decision made for a lookup
|
|
38
|
+
* table.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* One word, transliterated and folded to `[a-z0-9]`.
|
|
42
|
+
*
|
|
43
|
+
* `NFKD` + combining-mark strip first, so `Škoda` and `Skoda` are one word
|
|
44
|
+
* before anything else looks at them.
|
|
45
|
+
*/
|
|
46
|
+
export declare function translitKey(text: string): string;
|
|
47
|
+
/** {@link translitKey} without its vowels — the key two spellings of one
|
|
48
|
+
* borrowed name agree on. */
|
|
49
|
+
export declare function consonantKey(text: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Does `candidate` answer the typed `needle`?
|
|
52
|
+
*
|
|
53
|
+
* Prefix, not substring: a dictionary of makes searched by substring puts
|
|
54
|
+
* `Great Wall` under `all`. Every word of the candidate is a starting point,
|
|
55
|
+
* and both keys are tried — see the module note.
|
|
56
|
+
*/
|
|
57
|
+
export declare function translitPrefixMatch(needle: string, candidate: string): boolean;
|
|
58
|
+
//# sourceMappingURL=translit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translit.d.ts","sourceRoot":"","sources":["../../src/state/translit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAuBH;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAYhD;AAED;6BAC6B;AAC7B,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIjD;AAQD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAU9E"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matching a typed prefix against a dictionary value ACROSS ALPHABETS.
|
|
3
|
+
*
|
|
4
|
+
* A vocabulary of car makes holds `Toyota`, `Land Rover`, `Timberland` — Latin
|
|
5
|
+
* words, in a catalogue read by people typing Cyrillic. "toyota" spelled in
|
|
6
|
+
* Cyrillic and `Toyota` are the same word said in two scripts, and a
|
|
7
|
+
* `String.includes` over the caption answers "no" to every one of them. The
|
|
8
|
+
* autocatalog has 418 makes: without this, the search box in a dictionary
|
|
9
|
+
* facet is a box that works only for the half of the buyers whose keyboard
|
|
10
|
+
* matches the catalogue's.
|
|
11
|
+
*
|
|
12
|
+
* ── Two keys per word, and why the second one exists ──────────────────────
|
|
13
|
+
*
|
|
14
|
+
* The first key is a plain transliteration: Cyrillic → Latin through the table
|
|
15
|
+
* below, lowercased, everything but letters and digits dropped. That alone
|
|
16
|
+
* gets the Cyrillic spelling of "toyota" to `toiota` and `Toyota` to `toyota`
|
|
17
|
+
* — still not equal, and this is the general case rather than a bad table.
|
|
18
|
+
* Vowels are exactly where two scripts disagree: `i` against `y`, `e` against
|
|
19
|
+
* `a` in a borrowed word, `timberlend` against `timberland`. A vowel-perfect
|
|
20
|
+
* table does not exist, because the disagreement is in the borrowing, not in
|
|
21
|
+
* the letters.
|
|
22
|
+
*
|
|
23
|
+
* So the second key is the CONSONANT SKELETON: the first key with its vowels
|
|
24
|
+
* removed. `timberlend` and `timberland` are both `tmbrlnd`; `toiota` and
|
|
25
|
+
* `toyota` are both `tt`. Digits stay — a skeleton is about letters people
|
|
26
|
+
* disagree on, and `5` is not one.
|
|
27
|
+
*
|
|
28
|
+
* A candidate matches when either key of any of its WORDS starts with the
|
|
29
|
+
* corresponding key of the needle: a typed "rover" in either script finds
|
|
30
|
+
* `Land Rover`, and typing into the box narrows as you go, which is the whole
|
|
31
|
+
* point of a prefix rule. The skeleton arm is skipped for a needle that has no
|
|
32
|
+
* consonants at all — an empty skeleton is a prefix of everything, and a box
|
|
33
|
+
* that matches all 418 makes after one vowel is a box that has stopped
|
|
34
|
+
* answering.
|
|
35
|
+
*
|
|
36
|
+
* Table-driven and dependency-free on purpose: this is 40 pairs of characters,
|
|
37
|
+
* and a transliteration library is a page-weight decision made for a lookup
|
|
38
|
+
* table.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* Cyrillic → Latin, chosen for how the two scripts spell the SAME borrowed
|
|
42
|
+
* name rather than for a standard's fidelity: `kha` is `h` (Honda, not
|
|
43
|
+
* Khonda), `tse` is `c`, the two signs are nothing. Any letter absent here is
|
|
44
|
+
* kept as it is, so a Latin caption passes through untouched. Keys are
|
|
45
|
+
* quoted — the fleet's source is English, and a Cyrillic identifier is not.
|
|
46
|
+
*/
|
|
47
|
+
const CYRILLIC_TO_LATIN = {
|
|
48
|
+
"а": "a", "б": "b", "в": "v", "г": "g", "д": "d", "е": "e", "ё": "e",
|
|
49
|
+
"ж": "zh", "з": "z", "и": "i", "й": "i", "к": "k", "л": "l", "м": "m",
|
|
50
|
+
"н": "n", "о": "o", "п": "p", "р": "r", "с": "s", "т": "t", "у": "u",
|
|
51
|
+
"ф": "f", "х": "h", "ц": "c", "ч": "ch", "ш": "sh", "щ": "sh", "ъ": "",
|
|
52
|
+
"ы": "y", "ь": "", "э": "e", "ю": "u", "я": "a",
|
|
53
|
+
// Ukrainian/Belarusian letters a shared vocabulary can carry.
|
|
54
|
+
"і": "i", "ї": "i", "є": "e", "ґ": "g", "ў": "u",
|
|
55
|
+
};
|
|
56
|
+
/** Vowels of the transliterated form — `y` included, because it stands for a
|
|
57
|
+
* Cyrillic semi-vowel as often as it stands for a consonant. */
|
|
58
|
+
const VOWELS = new Set(["a", "e", "i", "o", "u", "y"]);
|
|
59
|
+
/**
|
|
60
|
+
* One word, transliterated and folded to `[a-z0-9]`.
|
|
61
|
+
*
|
|
62
|
+
* `NFKD` + combining-mark strip first, so `Škoda` and `Skoda` are one word
|
|
63
|
+
* before anything else looks at them.
|
|
64
|
+
*/
|
|
65
|
+
export function translitKey(text) {
|
|
66
|
+
const folded = text
|
|
67
|
+
.normalize("NFKD")
|
|
68
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
69
|
+
.toLowerCase();
|
|
70
|
+
let out = "";
|
|
71
|
+
for (const char of folded) {
|
|
72
|
+
const mapped = CYRILLIC_TO_LATIN[char];
|
|
73
|
+
if (mapped !== undefined)
|
|
74
|
+
out += mapped;
|
|
75
|
+
else if (/[a-z0-9]/.test(char))
|
|
76
|
+
out += char;
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
/** {@link translitKey} without its vowels — the key two spellings of one
|
|
81
|
+
* borrowed name agree on. */
|
|
82
|
+
export function consonantKey(text) {
|
|
83
|
+
let out = "";
|
|
84
|
+
for (const char of translitKey(text))
|
|
85
|
+
if (!VOWELS.has(char))
|
|
86
|
+
out += char;
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
/** The words of a caption, split on everything that is not a letter or a
|
|
90
|
+
* digit — so `Land Rover` is reachable by "rover" in either script. */
|
|
91
|
+
function words(text) {
|
|
92
|
+
return text.split(/[^\p{L}\p{N}]+/u).filter((word) => word.length > 0);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Does `candidate` answer the typed `needle`?
|
|
96
|
+
*
|
|
97
|
+
* Prefix, not substring: a dictionary of makes searched by substring puts
|
|
98
|
+
* `Great Wall` under `all`. Every word of the candidate is a starting point,
|
|
99
|
+
* and both keys are tried — see the module note.
|
|
100
|
+
*/
|
|
101
|
+
export function translitPrefixMatch(needle, candidate) {
|
|
102
|
+
const key = translitKey(needle);
|
|
103
|
+
if (key.length === 0)
|
|
104
|
+
return true;
|
|
105
|
+
const skeleton = consonantKey(needle);
|
|
106
|
+
for (const word of [candidate, ...words(candidate)]) {
|
|
107
|
+
const wordKey = translitKey(word);
|
|
108
|
+
if (wordKey.startsWith(key))
|
|
109
|
+
return true;
|
|
110
|
+
if (skeleton.length > 0 && consonantKey(word).startsWith(skeleton))
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=translit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translit.js","sourceRoot":"","sources":["../../src/state/translit.ts"],"names":[],"mappings":"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"}
|
package/dist/state/urlState.d.ts
CHANGED
|
@@ -65,11 +65,36 @@ export type SearchStateIssueCode =
|
|
|
65
65
|
/** `bbox` was not four numbers. */
|
|
66
66
|
| "bbox_malformed"
|
|
67
67
|
/** `r.<slug>` was not `from..to`. */
|
|
68
|
-
| "range_malformed"
|
|
68
|
+
| "range_malformed"
|
|
69
|
+
/**
|
|
70
|
+
* `radius_km` with nothing to measure it FROM — no `lat`/`lon`, no `bbox`.
|
|
71
|
+
*
|
|
72
|
+
* Not malformed: `radius_km=300` is a perfectly good number, and the search
|
|
73
|
+
* that ran is the honest one (a radius with no centre narrows nothing, so
|
|
74
|
+
* the count is the full count). What was wrong was the SILENCE. The URL said
|
|
75
|
+
* 300km, the results were unnarrowed, and the location control went on
|
|
76
|
+
* advertising its own default — measured on a live leaf as "300 in the
|
|
77
|
+
* address bar, 25 on the screen" with no way for a person to tell which one
|
|
78
|
+
* the page had used. Neither number is dropped and neither is rewritten;
|
|
79
|
+
* the disagreement is simply stated.
|
|
80
|
+
*/
|
|
81
|
+
| "radius_without_place";
|
|
69
82
|
/** What {@link parseSearchState} answers: the state, plus what it could not read. */
|
|
70
83
|
export interface ParsedSearchState {
|
|
71
84
|
readonly state: SearchQueryState;
|
|
72
85
|
readonly issues: readonly SearchStateIssue[];
|
|
86
|
+
/**
|
|
87
|
+
* A `radius_km` the URL carries with no place to measure it from.
|
|
88
|
+
*
|
|
89
|
+
* NOT part of the state — a radius with no centre is not a filter and
|
|
90
|
+
* nothing is sent to the server — but it is a number the person typed, and
|
|
91
|
+
* dropping it on the floor is how the control and the address ended up
|
|
92
|
+
* disagreeing: `?radius_km=300`, a button offering "within 25 km", and a
|
|
93
|
+
* press that silently rewrote 300 to 25. Whoever draws the offer reads this
|
|
94
|
+
* so that what the button SAYS, what pressing it DOES, and what the link
|
|
95
|
+
* ASKED FOR are one number.
|
|
96
|
+
*/
|
|
97
|
+
readonly orphanRadiusKm?: number;
|
|
73
98
|
}
|
|
74
99
|
export interface ParseSearchStateOptions {
|
|
75
100
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"urlState.d.ts","sourceRoot":"","sources":["../../src/state/urlState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAGV,gBAAgB,EAChB,WAAW,EACZ,MAAM,iBAAiB,CAAC;AAEzB,kEAAkE;AAClE,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;CAef,CAAC;AAEX,4DAA4D;AAC5D,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,+DAA+D;AAC/D,eAAO,MAAM,YAAY,OAAO,CAAC;AAEjC;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,wDAAwD;IACxD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,oBAAoB;AAC9B,4CAA4C;AAC1C,cAAc;AAChB,mDAAmD;GACjD,gBAAgB;AAClB,mCAAmC;GACjC,gBAAgB;AAClB,qCAAqC;GACnC,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"urlState.d.ts","sourceRoot":"","sources":["../../src/state/urlState.ts"],"names":[],"mappings":"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"}
|