svelte-typeahead 4.2.3 → 4.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-typeahead",
3
- "version": "4.2.3",
3
+ "version": "4.3.1",
4
4
  "license": "MIT",
5
5
  "description": "Accessible, fuzzy search typeahead component",
6
6
  "author": "Eric Liu (https://github.com/metonym)",
@@ -8,7 +8,6 @@
8
8
  "main": "./lib/index.js",
9
9
  "module": "./lib/index.mjs",
10
10
  "types": "./types/index.d.ts",
11
- "sideEffects": false,
12
11
  "scripts": {
13
12
  "dev": "rollup -cw",
14
13
  "predeploy": "rollup -c",
@@ -22,7 +21,7 @@
22
21
  },
23
22
  "devDependencies": {
24
23
  "@rollup/plugin-commonjs": "^21.0.2",
25
- "svelte": "^3.48.0",
24
+ "svelte": "^3.49.0",
26
25
  "svelte-check": "^2.8.0",
27
26
  "svelte-readme": "^3.6.3"
28
27
  },
@@ -33,6 +33,9 @@
33
33
  /** Set to `true` to re-focus the input after selecting a result */
34
34
  export let focusAfterSelect = false;
35
35
 
36
+ /** Set to `true` to only show results when the input is focused */
37
+ export let showDropdownOnFocus = false;
38
+
36
39
  /**
37
40
  * Specify the maximum number of results to return
38
41
  * @type {number}
@@ -50,10 +53,11 @@
50
53
  let hideDropdown = false;
51
54
  let selectedIndex = -1;
52
55
  let prevResults = "";
56
+ let isFocused = false;
53
57
 
54
58
  afterUpdate(() => {
55
59
  if (prevResults !== resultsId && autoselect) {
56
- selectedIndex = 0;
60
+ selectedIndex = getNextNonDisabledIndex();
57
61
  }
58
62
 
59
63
  if (prevResults !== resultsId && !$$slots["no-results"]) {
@@ -85,14 +89,33 @@
85
89
  await tick();
86
90
 
87
91
  if (focusAfterSelect) searchRef.focus();
92
+ close();
93
+ }
94
+
95
+ /** @type {() => number} */
96
+ function getNextNonDisabledIndex() {
97
+ let index = 0;
98
+ let disabled = results[index]?.disabled ?? false;
99
+
100
+ while (disabled) {
101
+ if (index === results.length) {
102
+ index = 0;
103
+ } else {
104
+ index += 1;
105
+ }
88
106
 
89
- hideDropdown = true;
107
+ disabled = results[index]?.disabled ?? false;
108
+ }
109
+
110
+ return index;
90
111
  }
91
112
 
92
113
  /** @type {(direction: -1 | 1) => void} */
93
114
  function change(direction) {
94
115
  let index =
95
- direction === 1 && selectedIndex === results.length - 1 ? 0 : selectedIndex + direction;
116
+ direction === 1 && selectedIndex === results.length - 1
117
+ ? 0
118
+ : selectedIndex + direction;
96
119
  if (index < 0) index = results.length - 1;
97
120
 
98
121
  let disabled = results[index].disabled;
@@ -110,6 +133,9 @@
110
133
  selectedIndex = index;
111
134
  }
112
135
 
136
+ const open = () => (hideDropdown = false);
137
+ const close = () => (hideDropdown = true);
138
+
113
139
  $: options = { pre: "<mark>", post: "</mark>", extract };
114
140
  $: results = fuzzy
115
141
  .filter(value, data, options)
@@ -118,12 +144,16 @@
118
144
  .filter((result) => !filter(result.original))
119
145
  .map((result) => ({ ...result, disabled: disable(result.original) }));
120
146
  $: resultsId = results.map((result) => extract(result.original)).join("");
147
+ $: showResults = !hideDropdown && results.length > 0;
148
+ $: if (showDropdownOnFocus) {
149
+ showResults = showResults && isFocused;
150
+ }
121
151
  </script>
122
152
 
123
153
  <svelte:window
124
154
  on:click={({ target }) => {
125
- if (!hideDropdown && comboboxRef && !comboboxRef.contains(target)) {
126
- hideDropdown = true;
155
+ if (!hideDropdown && !comboboxRef?.contains(target)) {
156
+ close();
127
157
  }
128
158
  }}
129
159
  />
@@ -135,7 +165,7 @@
135
165
  aria-haspopup="listbox"
136
166
  aria-owns="{id}-listbox"
137
167
  class:dropdown={results.length > 0}
138
- aria-expanded={!hideDropdown && results.length > 0}
168
+ aria-expanded={showResults}
139
169
  id="{id}-typeahead"
140
170
  >
141
171
  <Search
@@ -157,13 +187,15 @@
157
187
  on:change
158
188
  on:focus
159
189
  on:focus={() => {
160
- hideDropdown = false;
190
+ open();
191
+ isFocused = true;
161
192
  }}
162
193
  on:clear
163
- on:clear={() => {
164
- hideDropdown = false;
165
- }}
194
+ on:clear={open}
166
195
  on:blur
196
+ on:blur={() => {
197
+ isFocused = false;
198
+ }}
167
199
  on:keydown
168
200
  on:keydown={(e) => {
169
201
  if (results.length === 0) return;
@@ -183,8 +215,8 @@
183
215
  case "Escape":
184
216
  e.preventDefault();
185
217
  value = "";
186
- searchRef.focus();
187
- hideDropdown = true;
218
+ searchRef?.focus();
219
+ close();
188
220
  break;
189
221
  }
190
222
  }}
@@ -195,26 +227,25 @@
195
227
  aria-labelledby="{id}-label"
196
228
  id="{id}-listbox"
197
229
  >
198
- {#if !hideDropdown && results.length > 0}
199
- {#each results as result, i}
230
+ {#if showResults}
231
+ {#each results as result, index}
200
232
  <li
201
233
  role="option"
202
- id="{id}-result-{i}"
203
- class:selected={selectedIndex === i}
234
+ id="{id}-result-{index}"
235
+ class:selected={selectedIndex === index}
204
236
  class:disabled={result.disabled}
205
- aria-selected={selectedIndex === i}
237
+ aria-selected={selectedIndex === index}
206
238
  on:click={() => {
207
- if (!result.disabled) {
208
- selectedIndex = i;
209
- select();
210
- }
239
+ if (result.disabled) return;
240
+ selectedIndex = index;
241
+ select();
211
242
  }}
212
243
  on:mouseenter={() => {
213
244
  if (result.disabled) return;
214
- selectedIndex = i;
245
+ selectedIndex = index;
215
246
  }}
216
247
  >
217
- <slot {result} index={i} {value}>
248
+ <slot {result} {index} {value}>
218
249
  {@html result.string}
219
250
  </slot>
220
251
  </li>
@@ -245,6 +276,10 @@
245
276
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
246
277
  }
247
278
 
279
+ [aria-expanded="true"] ul {
280
+ z-index: 1;
281
+ }
282
+
248
283
  li,
249
284
  .no-results {
250
285
  padding: 0.25rem 1rem;
@@ -62,6 +62,12 @@ export interface TypeaheadProps<TItem> extends SearchProps {
62
62
  */
63
63
  focusAfterSelect?: boolean;
64
64
 
65
+ /**
66
+ * Set to `true` to only show results when the input is focused
67
+ * @default false
68
+ */
69
+ showDropdownOnFocus?: boolean;
70
+
65
71
  /**
66
72
  * Specify the maximum number of results to return
67
73
  * @default Infinity