@dorsk/tsumikit 0.11.1 → 0.11.2

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.
@@ -57,9 +57,11 @@
57
57
  } = $props();
58
58
 
59
59
  let el = $state<HTMLInputElement | null>(null);
60
+ let menuEl = $state<HTMLDivElement | null>(null);
60
61
  let menu = $state<SuggestState | null>(null);
61
62
  let active = $state(0);
62
63
  let open = $state(false);
64
+ let keyboardNavigation = $state(false);
63
65
  let reqId = 0;
64
66
 
65
67
  // Parsed view (drives the removable chips + the onchange AST).
@@ -72,6 +74,16 @@
72
74
  onchange?.(ast, value);
73
75
  });
74
76
 
77
+ // Keep keyboard navigation visible without moving the menu under the pointer
78
+ // when the active option changes because of hover.
79
+ $effect(() => {
80
+ active;
81
+ if (!keyboardNavigation || !open || !menuEl) return;
82
+ menuEl
83
+ .querySelector<HTMLElement>('.fsb__opt.is-active')
84
+ ?.scrollIntoView({ block: 'nearest' });
85
+ });
86
+
75
87
  function labelFor(fieldName: string): string {
76
88
  return findField(schema, fieldName)?.label ?? fieldName;
77
89
  }
@@ -106,6 +118,7 @@
106
118
  if (id !== reqId) return; // a newer keystroke won
107
119
  menu = next;
108
120
  active = 0;
121
+ keyboardNavigation = false;
109
122
  open = !!next && next.items.length > 0;
110
123
  }
111
124
 
@@ -167,10 +180,12 @@
167
180
  switch (e.key) {
168
181
  case 'ArrowDown':
169
182
  e.preventDefault();
183
+ keyboardNavigation = true;
170
184
  active = (active + 1) % menu.items.length;
171
185
  break;
172
186
  case 'ArrowUp':
173
187
  e.preventDefault();
188
+ keyboardNavigation = true;
174
189
  active = (active - 1 + menu.items.length) % menu.items.length;
175
190
  break;
176
191
  case 'Enter':
@@ -243,7 +258,7 @@
243
258
  </div>
244
259
 
245
260
  {#if open && menu}
246
- <div class="fsb__menu" role="listbox" aria-label={kindLabel[menu.kind]}>
261
+ <div bind:this={menuEl} class="fsb__menu" role="listbox" aria-label={kindLabel[menu.kind]}>
247
262
  <div class="fsb__menu-head">{kindLabel[menu.kind]}</div>
248
263
  {#each menu.items as item, i (item.insert + i)}
249
264
  <button
@@ -253,7 +268,10 @@
253
268
  role="option"
254
269
  aria-selected={i === active}
255
270
  onmousedown={(e) => e.preventDefault()}
256
- onmouseenter={() => (active = i)}
271
+ onmouseenter={() => {
272
+ keyboardNavigation = false;
273
+ active = i;
274
+ }}
257
275
  onclick={() => accept(i)}
258
276
  >
259
277
  <span class="fsb__opt-label">{item.label}</span>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dorsk/tsumikit",
3
- "version": "0.11.1",
3
+ "version": "0.11.2",
4
4
  "description": "Minimal, dependency-free Svelte 5 + pure-CSS UI kit. Token-driven atoms, molecules & layouts with theming out of the box.",
5
5
  "type": "module",
6
6
  "license": "MIT",