|
144 | 144 | // Reactively call anytime `selected`, `value`, or `options` change
|
145 | 145 | $: updateSelected(selected, value, options);
|
146 | 146 |
|
147 |
| - export let search = async (text: string) => { |
| 147 | + // Filter by search text |
| 148 | + export let search = async (text: string, options: MenuOption<TValue>[]) => { |
148 | 149 | logger.debug('search', { text, open });
|
149 | 150 |
|
150 |
| - if (text === '') { |
| 151 | + if (text === '' || options.length === 0) { |
151 | 152 | // Reset options
|
152 |
| - filteredOptions = options; |
| 153 | + return options; |
153 | 154 | } else {
|
154 | 155 | const words = text?.toLowerCase().split(' ') ?? [];
|
155 |
| - filteredOptions = options.filter((option) => { |
156 |
| - return words.every((word) => option.label.toLowerCase().includes(word)); |
| 156 | + return options.filter((option) => { |
| 157 | + const label = option.label.toLowerCase(); |
| 158 | + return words.every((word) => label.includes(word)); |
157 | 159 | });
|
158 | 160 | }
|
159 | 161 | };
|
|
198 | 200 | }
|
199 | 201 | }
|
200 | 202 |
|
| 203 | + let previousSearchText = ''; |
| 204 | + // Do not search if menu is not open / closing on selection |
201 | 205 | $: if (open) {
|
202 | 206 | // Capture current highlighted item (attempt to restore after searching)
|
203 | 207 | const prevHighlightedOption = filteredOptions[highlightIndex];
|
204 | 208 |
|
205 | 209 | // Do not search if menu is not open / closing on selection
|
206 |
| - search(searchText).then(() => { |
207 |
| - // TODO: Find a way for scrollIntoView to still highlight after the menu height transition finishes |
208 |
| - const selectedIndex = filteredOptions.findIndex((o) => o.value === value); |
209 |
| - if (highlightIndex === -1) { |
210 |
| - // Highlight selected if none currently |
211 |
| - highlightIndex = selectedIndex === -1 ? nextOptionIndex(-1) : selectedIndex; |
212 |
| - } else { |
213 |
| - // Attempt to re-highlight previously highlighted option after search |
214 |
| - const prevHighlightedOptionIndex = filteredOptions.findIndex( |
215 |
| - (o) => o === prevHighlightedOption |
216 |
| - ); |
217 |
| -
|
218 |
| - if (prevHighlightedOptionIndex !== -1) { |
219 |
| - // Maintain previously highlight index after filter update (option still available) |
220 |
| - highlightIndex = prevHighlightedOptionIndex; |
| 210 | + if (searchText.trim() && previousSearchText !== searchText) { |
| 211 | + previousSearchText = searchText; |
| 212 | + search(searchText, options ?? []).then((options) => { |
| 213 | + // Update filtered options with new results |
| 214 | + filteredOptions = options; |
| 215 | + // TODO: Find a way for scrollIntoView to still highlight after the menu height transition finishes |
| 216 | + const selectedIndex = options.findIndex((o) => o.value === value); |
| 217 | + if (highlightIndex === -1) { |
| 218 | + // Highlight selected if none currently |
| 219 | + highlightIndex = selectedIndex === -1 ? nextOptionIndex(-1) : selectedIndex; |
221 | 220 | } else {
|
222 |
| - // Highlight first option |
223 |
| - highlightIndex = nextOptionIndex(-1); |
| 221 | + // Attempt to re-highlight previously highlighted option after search |
| 222 | + const prevHighlightedOptionIndex = options.findIndex((o) => o === prevHighlightedOption); |
| 223 | +
|
| 224 | + if (prevHighlightedOptionIndex !== -1) { |
| 225 | + // Maintain previously highlight index after filter update (option still available) |
| 226 | + highlightIndex = prevHighlightedOptionIndex; |
| 227 | + } else { |
| 228 | + // Highlight first option |
| 229 | + highlightIndex = nextOptionIndex(-1); |
| 230 | + } |
224 | 231 | }
|
225 |
| - } |
226 |
| - }); |
| 232 | + }); |
| 233 | + } else if (searchText.trim() === '') { |
| 234 | + // Restore options if cleared (show all options) |
| 235 | + filteredOptions = options; |
| 236 | + } |
227 | 237 | }
|
228 | 238 |
|
229 | 239 | function onChange(e: ComponentEvents<TextField>['change']) {
|
|
0 commit comments