@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={() =>
|
|
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