Skip to content

Commit 708b470

Browse files
feat(docsearch): introduce favorite searches
1 parent 3cc88fa commit 708b470

37 files changed

Lines changed: 507 additions & 406 deletions

src/DocSearch.tsx

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import { getAlgoliaHits } from '@francoischalifour/autocomplete-preset-algolia';
88
import {
99
DocSearchHit,
1010
InternalDocSearchHit,
11-
RecentDocSearchHit,
11+
StoredDocSearchHit,
1212
} from './types';
1313
import { createSearchClient, groupBy, noop } from './utils';
1414
import { SearchBox } from './SearchBox';
15-
import { Dropdown } from './Dropdown';
15+
import { ScreenState } from './ScreenState';
1616
import { Footer } from './Footer';
1717

18-
import { createRecentSearches } from './recent-searches';
18+
import { createStoredSearches } from './stored-searches';
1919

2020
interface DocSearchProps {
2121
appId?: string;
@@ -48,7 +48,18 @@ export function DocSearch({
4848
appId,
4949
apiKey,
5050
]);
51-
const recentSearches = useRef(createRecentSearches<RecentDocSearchHit>());
51+
const recentSearches = useRef(
52+
createStoredSearches<StoredDocSearchHit>({
53+
key: '__DOCSEARCH_RECENT_SEARCHES__',
54+
limit: 5,
55+
})
56+
).current;
57+
const favoriteSearches = useRef(
58+
createStoredSearches<StoredDocSearchHit>({
59+
key: '__DOCSEARCH_FAVORITE_SEARCHES__',
60+
limit: 10,
61+
})
62+
).current;
5263

5364
const autocomplete = React.useMemo(
5465
() =>
@@ -128,14 +139,26 @@ export function DocSearch({
128139
return [
129140
{
130141
onSelect({ suggestion }) {
131-
recentSearches.current.saveSearch(suggestion);
142+
recentSearches.add(suggestion);
132143
onClose();
133144
},
134145
getSuggestionUrl({ suggestion }) {
135146
return suggestion.url;
136147
},
137148
getSuggestions() {
138-
return recentSearches.current.getSearches();
149+
return recentSearches.getAll();
150+
},
151+
},
152+
{
153+
onSelect({ suggestion }) {
154+
recentSearches.add(suggestion);
155+
onClose();
156+
},
157+
getSuggestionUrl({ suggestion }) {
158+
return suggestion.url;
159+
},
160+
getSuggestions() {
161+
return favoriteSearches.getAll();
139162
},
140163
},
141164
];
@@ -144,7 +167,7 @@ export function DocSearch({
144167
return Object.values<DocSearchHit[]>(sources).map(items => {
145168
return {
146169
onSelect({ suggestion }) {
147-
recentSearches.current.saveSearch(suggestion);
170+
recentSearches.add(suggestion);
148171
onClose();
149172
},
150173
getSuggestionUrl({ suggestion }) {
@@ -177,7 +200,14 @@ export function DocSearch({
177200
});
178201
},
179202
}),
180-
[indexName, searchParameters, searchClient, onClose]
203+
[
204+
indexName,
205+
searchParameters,
206+
searchClient,
207+
onClose,
208+
recentSearches,
209+
favoriteSearches,
210+
]
181211
);
182212

183213
const { getEnvironmentProps, getRootProps } = autocomplete;
@@ -244,18 +274,16 @@ export function DocSearch({
244274
</header>
245275

246276
<div className="DocSearch-Dropdown" ref={dropdownRef}>
247-
<Dropdown
277+
<ScreenState
248278
{...autocomplete}
249279
state={state}
250-
inputRef={inputRef}
280+
recentSearches={recentSearches}
281+
favoriteSearches={favoriteSearches}
251282
onItemClick={item => {
252-
recentSearches.current.saveSearch(item);
283+
recentSearches.add(item);
253284
onClose();
254285
}}
255-
onAction={item => {
256-
recentSearches.current.deleteSearch(item);
257-
autocomplete.refresh();
258-
}}
286+
inputRef={inputRef}
259287
/>
260288
</div>
261289

src/Dropdown/Dropdown.tsx

Lines changed: 0 additions & 49 deletions
This file was deleted.

src/Dropdown/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/EmptyScreen.tsx

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import React from 'react';
2+
import {
3+
AutocompleteApi,
4+
AutocompleteState,
5+
} from '@francoischalifour/autocomplete-core';
6+
7+
import { StoredDocSearchHit } from './types';
8+
import { StoredSearchPlugin } from './stored-searches';
9+
import { Results } from './Results';
10+
import { ResetIcon, RecentIcon, StarIcon } from './icons';
11+
12+
interface EmptyScreenProps
13+
extends AutocompleteApi<
14+
StoredDocSearchHit,
15+
React.FormEvent,
16+
React.MouseEvent,
17+
React.KeyboardEvent
18+
> {
19+
state: AutocompleteState<StoredDocSearchHit>;
20+
hasSuggestions: boolean;
21+
onItemClick(item: StoredDocSearchHit): void;
22+
recentSearches: StoredSearchPlugin<StoredDocSearchHit>;
23+
favoriteSearches: StoredSearchPlugin<StoredDocSearchHit>;
24+
}
25+
26+
export function EmptyScreen(props: EmptyScreenProps) {
27+
if (props.state.status === 'idle' && props.hasSuggestions === false) {
28+
return (
29+
<div className="DocSearch-EmptyScreen">
30+
<p className="DocSearch-Help">Your search history will appear here.</p>
31+
</div>
32+
);
33+
}
34+
35+
if (props.hasSuggestions === false) {
36+
return null;
37+
}
38+
39+
return (
40+
<div className="DocSearch-Dropdown-Container">
41+
<Results
42+
{...props}
43+
title="Recent"
44+
suggestion={props.state.suggestions[0]}
45+
renderIcon={() => (
46+
<div className="DocSearch-Hit-icon">
47+
<RecentIcon />
48+
</div>
49+
)}
50+
renderAction={({ item }) => (
51+
<>
52+
<div className="DocSearch-Hit-action">
53+
<button
54+
className="DocSearch-Hit-action-button"
55+
title="Save this search"
56+
onClick={event => {
57+
event.preventDefault();
58+
event.stopPropagation();
59+
props.favoriteSearches.add(item);
60+
props.refresh();
61+
}}
62+
>
63+
<StarIcon />
64+
</button>
65+
</div>
66+
<div className="DocSearch-Hit-action">
67+
<button
68+
className="DocSearch-Hit-action-button"
69+
title="Remove this search from history"
70+
onClick={event => {
71+
event.preventDefault();
72+
event.stopPropagation();
73+
props.recentSearches.remove(item);
74+
props.refresh();
75+
}}
76+
>
77+
<ResetIcon />
78+
</button>
79+
</div>
80+
</>
81+
)}
82+
/>
83+
84+
<Results
85+
{...props}
86+
title="Favorites"
87+
suggestion={props.state.suggestions[1]}
88+
renderIcon={() => (
89+
<div className="DocSearch-Hit-icon">
90+
<StarIcon />
91+
</div>
92+
)}
93+
renderAction={({ item }) => (
94+
<button
95+
className="DocSearch-Hit-action-button"
96+
title="Remove this search from favorite"
97+
onClick={event => {
98+
event.preventDefault();
99+
event.stopPropagation();
100+
props.favoriteSearches.remove(item);
101+
props.refresh();
102+
}}
103+
>
104+
<ResetIcon />
105+
</button>
106+
)}
107+
/>
108+
</div>
109+
);
110+
}

0 commit comments

Comments
 (0)