@intechstudio/grid-uikit 1.20260622.1304 → 1.20260622.1332

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.
@@ -20,7 +20,7 @@
20
20
  </script>
21
21
 
22
22
  <script lang="ts">
23
- import { createEventDispatcher } from "svelte";
23
+ import { createEventDispatcher, tick } from "svelte";
24
24
  import { createPopover } from "@melt-ui/svelte";
25
25
  import { fade } from "svelte/transition";
26
26
  import { writable, type Writable } from "svelte/store";
@@ -53,6 +53,16 @@
53
53
  let selected: Writable<MeltComboSuggestion> = writable();
54
54
 
55
55
  let inputElement: any;
56
+ let highlightedIndex = -1;
57
+
58
+ $: if (!$open) {
59
+ highlightedIndex = -1;
60
+ } else if ($open && filteredSuggestions.length > 0) {
61
+ const match = filteredSuggestions.findIndex(
62
+ (s) => String(s.value).trim() === String(inputValue).trim(),
63
+ );
64
+ highlightedIndex = match >= 0 ? match : 0;
65
+ }
56
66
 
57
67
  const open = writable(false);
58
68
 
@@ -80,7 +90,7 @@
80
90
  $: handleSelectionChange($selected);
81
91
  $: handleInputChange(inputValue);
82
92
 
83
- $: filteredSuggestions = searchable
93
+ $: baseList = searchable
84
94
  ? suggestions.filter(
85
95
  (e) =>
86
96
  e.info.toLowerCase().includes(inputValue?.toLowerCase()) ||
@@ -88,6 +98,17 @@
88
98
  )
89
99
  : suggestions;
90
100
 
101
+ $: filteredSuggestions =
102
+ inputValue.trim() &&
103
+ !suggestions.some(
104
+ (s) => String(s.value).trim() === String(inputValue).trim(),
105
+ )
106
+ ? [
107
+ { info: "Manual entry: " + inputValue, value: inputValue },
108
+ ...baseList,
109
+ ]
110
+ : baseList;
111
+
91
112
  $: infoValue =
92
113
  suggestions.find((s) => String(s.value).trim() == String(inputValue).trim())
93
114
  ?.info || "";
@@ -148,7 +169,6 @@
148
169
  }
149
170
 
150
171
  function handleFocus() {
151
- filteredSuggestions = suggestions;
152
172
  if (searchable) {
153
173
  inputElement.select();
154
174
  }
@@ -159,6 +179,51 @@
159
179
  open.set(false);
160
180
  }
161
181
 
182
+ function handleKeydown(e: KeyboardEvent) {
183
+ if (e.key === "ArrowDown") {
184
+ e.preventDefault();
185
+ if (filteredSuggestions.length === 0) return;
186
+ if (!$open) open.set(true);
187
+ highlightedIndex =
188
+ highlightedIndex < filteredSuggestions.length - 1
189
+ ? highlightedIndex + 1
190
+ : 0;
191
+ scrollIntoView();
192
+ } else if (e.key === "ArrowUp") {
193
+ e.preventDefault();
194
+ if (filteredSuggestions.length === 0) return;
195
+ highlightedIndex =
196
+ highlightedIndex > 0
197
+ ? highlightedIndex - 1
198
+ : filteredSuggestions.length - 1;
199
+ scrollIntoView();
200
+ } else if (e.key === "Enter") {
201
+ if (
202
+ highlightedIndex >= 0 &&
203
+ highlightedIndex < filteredSuggestions.length
204
+ ) {
205
+ e.preventDefault();
206
+ selected.set(filteredSuggestions[highlightedIndex]);
207
+ highlightedIndex = -1;
208
+ } else if (!$open) {
209
+ e.preventDefault();
210
+ open.set(true);
211
+ }
212
+ } else if (e.key === "Escape") {
213
+ highlightedIndex = -1;
214
+ open.set(false);
215
+ }
216
+ }
217
+
218
+ function scrollIntoView() {
219
+ tick().then(() => {
220
+ const el = document.querySelector(
221
+ `[data-combo-index="${highlightedIndex}"]`,
222
+ );
223
+ el?.scrollIntoView({ block: "nearest" });
224
+ });
225
+ }
226
+
162
227
  let menuWidth;
163
228
  </script>
164
229
 
@@ -180,6 +245,7 @@
180
245
  on:m-keydown={(e) => {
181
246
  e.preventDefault();
182
247
  }}
248
+ on:keydown={handleKeydown}
183
249
  on:click={() => {
184
250
  open.set(true);
185
251
  }}
@@ -204,6 +270,7 @@
204
270
  on:m-keydown={(e) => {
205
271
  e.preventDefault();
206
272
  }}
273
+ on:keydown={handleKeydown}
207
274
  on:click={() => {
208
275
  open.set(true);
209
276
  }}
@@ -216,7 +283,7 @@
216
283
  {/if}
217
284
  </div>
218
285
 
219
- {#if $open && !disabled && suggestions.length > 0}
286
+ {#if $open && !disabled && filteredSuggestions.length > 0}
220
287
  <!-- svelte-ignore a11y-no-static-element-interactions -->
221
288
  <div
222
289
  {...$content}
@@ -227,12 +294,16 @@
227
294
  style="min-width: {menuWidth}px;"
228
295
  >
229
296
  <div>
230
- {#each filteredSuggestions as suggestion}
297
+ {#each filteredSuggestions as suggestion, i}
231
298
  <option
232
299
  {...$close}
233
300
  use:close
234
301
  class="suggestion"
235
- on:click={() => selected.set(suggestion)}>{suggestion.info}</option
302
+ data-highlighted={highlightedIndex === i ? "" : undefined}
303
+ data-combo-index={i}
304
+ on:click={() => selected.set(suggestion)}
305
+ on:mouseenter={() => (highlightedIndex = i)}
306
+ >{suggestion.info}</option
236
307
  >
237
308
  {/each}
238
309
  </div>
@@ -317,7 +388,8 @@
317
388
  padding-top: 0.25em;
318
389
  padding-bottom: 0.25em;
319
390
  }
320
- option.suggestion:hover {
391
+ option.suggestion:hover,
392
+ option.suggestion[data-highlighted] {
321
393
  background-color: var(--popover-selection);
322
394
  color: var(--foreground);
323
395
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intechstudio/grid-uikit",
3
- "version": "1.20260622.1304",
3
+ "version": "1.20260622.1332",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/intechstudio/grid-uikit.git"