@findlane/browser 0.16.1 → 0.17.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/dist/index.d.ts +21 -10
- package/dist/react/components.d.ts +4 -1
- package/dist/react/components.js +9 -4
- package/dist/react/highlight.js +1 -1
- package/dist/react/search-provider.d.ts +2 -0
- package/dist/react/search-provider.js +2 -0
- package/dist/react/store.d.ts +2 -1
- package/dist/react/store.js +5 -2
- package/dist/react/use-autocomplete.d.ts +1 -7
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,8 @@ export type SearchRequest = {
|
|
|
46
46
|
/** Only records inside a box, in degrees, such as a map's visible area. West greater than east crosses the
|
|
47
47
|
* antimeridian. */
|
|
48
48
|
within?: GeoBox;
|
|
49
|
+
/** With the index's related results on: false asks for none (0.17.0). */
|
|
50
|
+
related?: boolean;
|
|
49
51
|
};
|
|
50
52
|
/** A point in degrees: lat from -90 to 90, lng from -180 to 180. */
|
|
51
53
|
export type GeoPoint = {
|
|
@@ -59,16 +61,18 @@ export type GeoBox = {
|
|
|
59
61
|
east: number;
|
|
60
62
|
west: number;
|
|
61
63
|
};
|
|
64
|
+
/** A result. */
|
|
65
|
+
export type SearchHit<T> = {
|
|
66
|
+
id: string;
|
|
67
|
+
score: number | null;
|
|
68
|
+
record: T;
|
|
69
|
+
/** With `near`: meters from the point. */
|
|
70
|
+
distance?: number;
|
|
71
|
+
/** With `highlight`: matched searchable fields by path; list fields give one string per value. */
|
|
72
|
+
highlights?: Record<string, string | string[]>;
|
|
73
|
+
};
|
|
62
74
|
export type SearchResponse<T> = {
|
|
63
|
-
hits:
|
|
64
|
-
id: string;
|
|
65
|
-
score: number | null;
|
|
66
|
-
record: T;
|
|
67
|
-
/** With `near`: meters from the point. */
|
|
68
|
-
distance?: number;
|
|
69
|
-
/** With `highlight`: matched searchable fields by path; list fields give one string per value. */
|
|
70
|
-
highlights?: Record<string, string | string[]>;
|
|
71
|
-
}[];
|
|
75
|
+
hits: SearchHit<T>[];
|
|
72
76
|
total: number;
|
|
73
77
|
/** "gte" when `total` is a lower bound (page count mode). */
|
|
74
78
|
totalRelation?: "eq" | "gte";
|
|
@@ -95,6 +99,13 @@ export type SearchResponse<T> = {
|
|
|
95
99
|
appliedRules?: AppliedRule[];
|
|
96
100
|
/** When a rule replaced words: the query that was searched. */
|
|
97
101
|
rewrittenQuery?: string;
|
|
102
|
+
/** With the index's related results on (0.17.0): when the query's words found nothing (`hits` empty, `total`
|
|
103
|
+
* 0), the products closest to it in meaning, best first, with their similarity as `score`. Show them under
|
|
104
|
+
* a heading of their own, such as "No exact matches. Related products". */
|
|
105
|
+
related?: {
|
|
106
|
+
hits: SearchHit<T>[];
|
|
107
|
+
total: number;
|
|
108
|
+
};
|
|
98
109
|
};
|
|
99
110
|
/** A query rule a search fired, with its `userData`, if it has any. */
|
|
100
111
|
export type AppliedRule = {
|
|
@@ -227,7 +238,7 @@ export type EventItem = {
|
|
|
227
238
|
export type ClickDetails = {
|
|
228
239
|
position?: number;
|
|
229
240
|
query?: string;
|
|
230
|
-
list?: "goesWith" | "similar" | "together";
|
|
241
|
+
list?: "goesWith" | "similar" | "together" | "related";
|
|
231
242
|
from?: string;
|
|
232
243
|
};
|
|
233
244
|
/** Events from the page, sent in the background: a batch a second, and what's left when the page is
|
|
@@ -56,6 +56,9 @@ export type HitsProps<T> = {
|
|
|
56
56
|
render?: (hit: Hit<T>, index: number) => ReactNode;
|
|
57
57
|
/** Shown when nothing matches (default "No results for “…”"). */
|
|
58
58
|
empty?: (query: string) => ReactNode;
|
|
59
|
+
/** The heading over related results, shown when nothing matched but the index's related results found
|
|
60
|
+
* products close in meaning (default "No exact matches. Related products"). */
|
|
61
|
+
relatedTitle?: (query: string) => ReactNode;
|
|
59
62
|
/** Shown before the first results arrive. */
|
|
60
63
|
loading?: ReactNode;
|
|
61
64
|
className?: string;
|
|
@@ -63,7 +66,7 @@ export type HitsProps<T> = {
|
|
|
63
66
|
/** Whether a click was on a link inside the element that handled it, such as a hit's link. */
|
|
64
67
|
export declare function onLink(event: MouseEvent<HTMLElement>): boolean;
|
|
65
68
|
/** The results. Following a link in a hit sends a click event, with its place and the query. */
|
|
66
|
-
export declare function Hits<T = Record<string, unknown>>({ render, empty, loading, className }: HitsProps<T>): import("react").JSX.Element;
|
|
69
|
+
export declare function Hits<T = Record<string, unknown>>({ render, empty, relatedTitle, loading, className }: HitsProps<T>): import("react").JSX.Element;
|
|
67
70
|
export type RefinementListProps = {
|
|
68
71
|
field: string;
|
|
69
72
|
/** The heading (default the field). */
|
package/dist/react/components.js
CHANGED
|
@@ -74,22 +74,27 @@ export function onLink(event) {
|
|
|
74
74
|
return Boolean(link && event.currentTarget.contains(link));
|
|
75
75
|
}
|
|
76
76
|
/** The results. Following a link in a hit sends a click event, with its place and the query. */
|
|
77
|
-
export function Hits({ render, empty, loading = null, className }) {
|
|
78
|
-
const { hits, isLoaded, isPending, error, click } = useHits();
|
|
77
|
+
export function Hits({ render, empty, relatedTitle, loading = null, className }) {
|
|
78
|
+
const { hits, related, isLoaded, isPending, error, click } = useHits();
|
|
79
79
|
const { query } = useSearchBox();
|
|
80
80
|
if (error && !isLoaded)
|
|
81
81
|
return _jsx("p", { "data-findlane-note": "", children: messageFor(error) });
|
|
82
82
|
if (!isLoaded)
|
|
83
83
|
return _jsx(_Fragment, { children: loading });
|
|
84
|
-
if (!hits.length)
|
|
84
|
+
if (!hits.length && !related.length)
|
|
85
85
|
return _jsx("p", { "data-findlane-empty": "", children: empty ? empty(query) : query ? _jsxs(_Fragment, { children: ["No results for \u201C", query, "\u201D"] }) : "No results" });
|
|
86
|
-
|
|
86
|
+
// Nothing matched, but related results found products close in meaning: those, under their own heading.
|
|
87
|
+
const shown = hits.length ? hits : related;
|
|
88
|
+
const list = (_jsx("ol", { className: className, "data-findlane-hits": "", "data-findlane-related": !hits.length || undefined, "data-pending": isPending || undefined, children: shown.map((hit, index) => {
|
|
87
89
|
// A click, and a middle click that opens the link in a new tab.
|
|
88
90
|
const clicked = (event) => { if (!event.defaultPrevented && onLink(event))
|
|
89
91
|
click(hit); };
|
|
90
92
|
return (_jsx("li", { onClick: clicked, onAuxClick: (event) => { if (event.button === 1)
|
|
91
93
|
clicked(event); }, children: render ? render(hit, index) : _jsx(DefaultHit, { hit: hit }) }, hit.id));
|
|
92
94
|
}) }));
|
|
95
|
+
if (hits.length)
|
|
96
|
+
return list;
|
|
97
|
+
return (_jsxs(_Fragment, { children: [_jsx("p", { "data-findlane-related-title": "", children: relatedTitle ? relatedTitle(query) : "No exact matches. Related products" }), list] }));
|
|
93
98
|
}
|
|
94
99
|
/** Checkboxes for a facet's values, with counts. */
|
|
95
100
|
export function RefinementList({ field, label, limit = 8, format, className }) {
|
package/dist/react/highlight.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Fragment } from "react";
|
|
3
|
-
// The API escapes highlighted values (
|
|
3
|
+
// The API escapes highlighted values (the engine's, engine/src/present.rs) and wraps matched words in
|
|
4
4
|
// <mark>. Splitting on the tags and decoding the five escapes gives text React can render, so
|
|
5
5
|
// nothing is inserted as HTML.
|
|
6
6
|
const escapes = { "&": "&", "<": "<", ">": ">", """: '"', "'": "'" };
|
|
@@ -35,6 +35,8 @@ export declare function useSearchBox(): {
|
|
|
35
35
|
export declare function useHits<T = Record<string, unknown>>(): {
|
|
36
36
|
hits: import("./state.js").Hit<T>[];
|
|
37
37
|
total: number;
|
|
38
|
+
/** With the index's related results on, when nothing matched: the products closest in meaning. */
|
|
39
|
+
related: import("../index.js").SearchHit<T>[];
|
|
38
40
|
/** "gte" when `total` is a lower bound (with `countMode: "page"`). */
|
|
39
41
|
totalRelation: "eq" | "gte";
|
|
40
42
|
response: import("../index.js").SearchResponse<T> | null;
|
|
@@ -82,6 +82,8 @@ export function useHits() {
|
|
|
82
82
|
return {
|
|
83
83
|
hits: results?.hits ?? [],
|
|
84
84
|
total: results?.response.total ?? 0,
|
|
85
|
+
/** With the index's related results on, when nothing matched: the products closest in meaning. */
|
|
86
|
+
related: results?.response.related?.hits ?? [],
|
|
85
87
|
/** "gte" when `total` is a lower bound (with `countMode: "page"`). */
|
|
86
88
|
totalRelation: results?.response.totalRelation ?? "eq",
|
|
87
89
|
response: results?.response ?? null,
|
package/dist/react/store.d.ts
CHANGED
|
@@ -62,7 +62,8 @@ export declare class SearchStore<T = unknown> {
|
|
|
62
62
|
get settings(): SearchConfig;
|
|
63
63
|
/** Opens the connection to the API before the first search needs it (the client's warm). */
|
|
64
64
|
warm(): void;
|
|
65
|
-
/** Sends a click on a result: its place in the results, from 1, and the query they're for.
|
|
65
|
+
/** Sends a click on a result: its place in the results, from 1, and the query they're for. A related
|
|
66
|
+
* result's click says so (list "related"), with its place among them. */
|
|
66
67
|
click(objectID: string): void;
|
|
67
68
|
/** Searches unless the results already answer the current state; with a state, such as the
|
|
68
69
|
* URL's, adopts it first. */
|
package/dist/react/store.js
CHANGED
|
@@ -67,12 +67,15 @@ export class SearchStore {
|
|
|
67
67
|
get settings() { return this.config; }
|
|
68
68
|
/** Opens the connection to the API before the first search needs it (the client's warm). */
|
|
69
69
|
warm() { this.client.warm(); }
|
|
70
|
-
/** Sends a click on a result: its place in the results, from 1, and the query they're for.
|
|
70
|
+
/** Sends a click on a result: its place in the results, from 1, and the query they're for. A related
|
|
71
|
+
* result's click says so (list "related"), with its place among them. */
|
|
71
72
|
click(objectID) {
|
|
72
73
|
const results = this.snapshot.results;
|
|
73
74
|
const index = results ? results.hits.findIndex((hit) => hit.id === objectID) : -1;
|
|
75
|
+
const related = index < 0 && results ? (results.response.related?.hits ?? []).findIndex((hit) => hit.id === objectID) : -1;
|
|
74
76
|
this.client.events.click(objectID, { query: results?.request.q ?? "",
|
|
75
|
-
...(index >= 0 ? { position: (results.request.offset ?? 0) + index + 1 } : {})
|
|
77
|
+
...(index >= 0 ? { position: (results.request.offset ?? 0) + index + 1 } : {}),
|
|
78
|
+
...(related >= 0 ? { position: related + 1, list: "related" } : {}) });
|
|
76
79
|
}
|
|
77
80
|
/** Searches unless the results already answer the current state; with a state, such as the
|
|
78
81
|
* URL's, adopts it first. */
|
|
@@ -63,13 +63,7 @@ export declare function fillTemplate<T>(template: string, hit: Hit<T>): string |
|
|
|
63
63
|
export declare function useAutocomplete<T = Record<string, unknown>>(options?: AutocompleteOptions<T>): {
|
|
64
64
|
query: string;
|
|
65
65
|
setQuery: (value: string) => void;
|
|
66
|
-
hits:
|
|
67
|
-
id: string;
|
|
68
|
-
score: number | null;
|
|
69
|
-
record: T;
|
|
70
|
-
distance?: number;
|
|
71
|
-
highlights?: Record<string, string | string[]>;
|
|
72
|
-
}[];
|
|
66
|
+
hits: import("../index.js").SearchHit<T>[];
|
|
73
67
|
total: number;
|
|
74
68
|
response: import("../index.js").SearchResponse<T> | null;
|
|
75
69
|
error: unknown;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@findlane/browser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Findlane browser SDK: fast, typo-tolerant public search from the browser with an index's public ID, plus React hooks and components.",
|
|
5
5
|
"keywords": ["findlane", "search", "full-text search", "typo tolerance", "instant search", "ecommerce search", "browser", "react", "autocomplete"],
|
|
6
6
|
"license": "MIT",
|