bits-ui 1.0.0-next.94 → 1.0.0-next.96
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/dist/bits/accordion/types.d.ts +2 -0
- package/dist/bits/checkbox/checkbox.svelte.d.ts +2 -1
- package/dist/bits/checkbox/checkbox.svelte.js +2 -0
- package/dist/bits/checkbox/components/checkbox-group.svelte +5 -1
- package/dist/bits/command/command.svelte.d.ts +87 -1
- package/dist/bits/command/command.svelte.js +149 -19
- package/dist/bits/command/components/command.svelte +64 -0
- package/dist/bits/command/components/command.svelte.d.ts +56 -1
- package/package.json +1 -1
|
@@ -66,6 +66,8 @@ export type AccordionRootMultiplePropsWithoutHTML = BaseAccordionRootPropsWithou
|
|
|
66
66
|
};
|
|
67
67
|
export type AccordionRootPropsWithoutHTML = WithChild<AccordionRootSinglePropsWithoutHTML> | WithChild<AccordionRootMultiplePropsWithoutHTML>;
|
|
68
68
|
export type AccordionRootProps = AccordionRootPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, AccordionRootPropsWithoutHTML>;
|
|
69
|
+
export type AccordionRootSingleProps = AccordionRootSinglePropsWithoutHTML & Without<BitsPrimitiveDivAttributes, AccordionRootSinglePropsWithoutHTML>;
|
|
70
|
+
export type AccordionMultipleProps = AccordionRootMultiplePropsWithoutHTML & Without<BitsPrimitiveDivAttributes, AccordionRootMultiplePropsWithoutHTML>;
|
|
69
71
|
export type AccordionTriggerPropsWithoutHTML = WithChild;
|
|
70
72
|
export type AccordionTriggerProps = AccordionTriggerPropsWithoutHTML & Without<BitsPrimitiveButtonAttributes, AccordionTriggerPropsWithoutHTML>;
|
|
71
73
|
export type AccordionContentSnippetProps = {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
2
2
|
import type { ReadableBoxedValues, WritableBoxedValues } from "../../internal/box.svelte.js";
|
|
3
|
-
import type { BitsKeyboardEvent, BitsMouseEvent, WithRefProps } from "../../internal/types.js";
|
|
3
|
+
import type { BitsKeyboardEvent, BitsMouseEvent, OnChangeFn, WithRefProps } from "../../internal/types.js";
|
|
4
4
|
type CheckboxGroupStateProps = WithRefProps<ReadableBoxedValues<{
|
|
5
5
|
name: string | undefined;
|
|
6
6
|
disabled: boolean;
|
|
7
7
|
required: boolean;
|
|
8
|
+
onValueChange: OnChangeFn<string[]>;
|
|
8
9
|
}> & WritableBoxedValues<{
|
|
9
10
|
value: string[];
|
|
10
11
|
}>>;
|
|
@@ -17,6 +17,7 @@ class CheckboxGroupState {
|
|
|
17
17
|
return;
|
|
18
18
|
if (!this.opts.value.current.includes(checkboxValue)) {
|
|
19
19
|
this.opts.value.current.push(checkboxValue);
|
|
20
|
+
this.opts.onValueChange.current(this.opts.value.current);
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
removeValue(checkboxValue) {
|
|
@@ -26,6 +27,7 @@ class CheckboxGroupState {
|
|
|
26
27
|
if (index === -1)
|
|
27
28
|
return;
|
|
28
29
|
this.opts.value.current.splice(index, 1);
|
|
30
|
+
this.opts.onValueChange.current(this.opts.value.current);
|
|
29
31
|
}
|
|
30
32
|
props = $derived.by(() => ({
|
|
31
33
|
id: this.opts.id.current,
|
|
@@ -29,8 +29,12 @@
|
|
|
29
29
|
name: box.with(() => name),
|
|
30
30
|
value: box.with(
|
|
31
31
|
() => value,
|
|
32
|
-
(v) =>
|
|
32
|
+
(v) => {
|
|
33
|
+
value = v;
|
|
34
|
+
onValueChange(v);
|
|
35
|
+
}
|
|
33
36
|
),
|
|
37
|
+
onValueChange: box.with(() => onValueChange),
|
|
34
38
|
});
|
|
35
39
|
|
|
36
40
|
const mergedProps = $derived(mergeProps(restProps, groupState.props));
|
|
@@ -28,9 +28,95 @@ declare class CommandRootState {
|
|
|
28
28
|
_commandState: CommandState;
|
|
29
29
|
setState<K extends keyof CommandState>(key: K, value: CommandState[K], opts?: boolean): void;
|
|
30
30
|
constructor(opts: CommandRootStateProps);
|
|
31
|
+
/**
|
|
32
|
+
* Sets current value and triggers re-render if cleared.
|
|
33
|
+
*
|
|
34
|
+
* @param value - New value to set
|
|
35
|
+
*/
|
|
31
36
|
setValue(value: string, opts?: boolean): void;
|
|
32
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Gets all non-disabled, visible command items.
|
|
39
|
+
*
|
|
40
|
+
* @returns Array of valid item elements
|
|
41
|
+
* @remarks Exposed for direct item access and bound checking
|
|
42
|
+
*/
|
|
43
|
+
getValidItems(): HTMLElement[];
|
|
44
|
+
/**
|
|
45
|
+
* Sets selection to item at specified index in valid items array.
|
|
46
|
+
* If index is out of bounds, does nothing.
|
|
47
|
+
*
|
|
48
|
+
* @param index - Zero-based index of item to select
|
|
49
|
+
* @remarks
|
|
50
|
+
* Uses `getValidItems()` to get selectable items, filtering out disabled/hidden ones.
|
|
51
|
+
* Access valid items directly via `getValidItems()` to check bounds before calling.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // get valid items length for bounds check
|
|
55
|
+
* const items = getValidItems()
|
|
56
|
+
* if (index < items.length) {
|
|
57
|
+
* updateSelectedToIndex(index)
|
|
58
|
+
* }
|
|
59
|
+
*/
|
|
60
|
+
updateSelectedToIndex(index: number): void;
|
|
61
|
+
/**
|
|
62
|
+
* Updates selected item by moving up/down relative to current selection.
|
|
63
|
+
* Handles wrapping when loop option is enabled.
|
|
64
|
+
*
|
|
65
|
+
* @param change - Direction to move: 1 for next item, -1 for previous item
|
|
66
|
+
* @remarks
|
|
67
|
+
* The loop behavior wraps:
|
|
68
|
+
* - From last item to first when moving next
|
|
69
|
+
* - From first item to last when moving previous
|
|
70
|
+
*
|
|
71
|
+
* Uses `getValidItems()` to get all selectable items, which filters out disabled/hidden items.
|
|
72
|
+
* You can call `getValidItems()` directly to get the current valid items array.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* // select next item
|
|
76
|
+
* updateSelectedByItem(1)
|
|
77
|
+
*
|
|
78
|
+
* // get all valid items
|
|
79
|
+
* const items = getValidItems()
|
|
80
|
+
*/
|
|
81
|
+
updateSelectedByItem(change: 1 | -1): void;
|
|
82
|
+
/**
|
|
83
|
+
* Moves selection to the first valid item in the next/previous group.
|
|
84
|
+
* If no group is found, falls back to selecting the next/previous item globally.
|
|
85
|
+
*
|
|
86
|
+
* @param change - Direction to move: 1 for next group, -1 for previous group
|
|
87
|
+
* @example
|
|
88
|
+
* // move to first item in next group
|
|
89
|
+
* updateSelectedByGroup(1)
|
|
90
|
+
*
|
|
91
|
+
* // move to first item in previous group
|
|
92
|
+
* updateSelectedByGroup(-1)
|
|
93
|
+
*/
|
|
94
|
+
updateSelectedByGroup(change: 1 | -1): void;
|
|
95
|
+
/**
|
|
96
|
+
* Maps item id to display value and search keywords.
|
|
97
|
+
* Returns cleanup function to remove mapping.
|
|
98
|
+
*
|
|
99
|
+
* @param id - Unique item identifier
|
|
100
|
+
* @param value - Display text
|
|
101
|
+
* @param keywords - Optional search boost terms
|
|
102
|
+
* @returns Cleanup function
|
|
103
|
+
*/
|
|
104
|
+
registerValue(id: string, value: string, keywords?: string[]): () => void;
|
|
105
|
+
/**
|
|
106
|
+
* Registers item in command list and its group.
|
|
107
|
+
* Handles filtering, sorting and selection updates.
|
|
108
|
+
*
|
|
109
|
+
* @param id - Item identifier
|
|
110
|
+
* @param groupId - Optional group to add item to
|
|
111
|
+
* @returns Cleanup function that handles selection
|
|
112
|
+
*/
|
|
33
113
|
registerItem(id: string, groupId: string | undefined): () => void;
|
|
114
|
+
/**
|
|
115
|
+
* Creates empty group if not exists.
|
|
116
|
+
*
|
|
117
|
+
* @param id - Group identifier
|
|
118
|
+
* @returns Cleanup function
|
|
119
|
+
*/
|
|
34
120
|
registerGroup(id: string): () => void;
|
|
35
121
|
onkeydown(e: BitsKeyboardEvent): void;
|
|
36
122
|
props: {
|
|
@@ -5,6 +5,7 @@ import { kbd } from "../../internal/kbd.js";
|
|
|
5
5
|
import { getAriaDisabled, getAriaExpanded, getAriaSelected, getDataDisabled, getDataSelected, } from "../../internal/attrs.js";
|
|
6
6
|
import { getFirstNonCommentChild } from "../../internal/dom.js";
|
|
7
7
|
import { computeCommandScore } from "./index.js";
|
|
8
|
+
import { noop } from "../../internal/noop.js";
|
|
8
9
|
// attributes
|
|
9
10
|
const COMMAND_ROOT_ATTR = "data-command-root";
|
|
10
11
|
const COMMAND_LIST_ATTR = "data-command-list";
|
|
@@ -100,11 +101,24 @@ class CommandRootState {
|
|
|
100
101
|
useRefById(opts);
|
|
101
102
|
this.onkeydown = this.onkeydown.bind(this);
|
|
102
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Calculates score for an item based on search text and keywords.
|
|
106
|
+
* Higher score = better match.
|
|
107
|
+
*
|
|
108
|
+
* @param value - Item's display text
|
|
109
|
+
* @param keywords - Optional keywords to boost scoring
|
|
110
|
+
* @returns Score from 0-1, where 0 = no match
|
|
111
|
+
*/
|
|
103
112
|
#score(value, keywords) {
|
|
104
113
|
const filter = this.opts.filter.current ?? computeCommandScore;
|
|
105
114
|
const score = value ? filter(value, this._commandState.search, keywords) : 0;
|
|
106
115
|
return score;
|
|
107
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Sorts items and groups based on search scores.
|
|
119
|
+
* Groups are sorted by their highest scoring item.
|
|
120
|
+
* When no search active, selects first item.
|
|
121
|
+
*/
|
|
108
122
|
#sort() {
|
|
109
123
|
if (!this._commandState.search || this.opts.shouldFilter.current === false) {
|
|
110
124
|
// If no search and no selection yet, select first item
|
|
@@ -133,7 +147,7 @@ class CommandRootState {
|
|
|
133
147
|
// Sort items outside of groups
|
|
134
148
|
// Sort groups to bottom (pushes all non-grouped items to the top)
|
|
135
149
|
const listInsertionElement = this.viewportNode;
|
|
136
|
-
const sorted = this
|
|
150
|
+
const sorted = this.getValidItems().sort((a, b) => {
|
|
137
151
|
const valueA = a.getAttribute("id");
|
|
138
152
|
const valueB = b.getAttribute("id");
|
|
139
153
|
const scoresA = scores.get(valueA) ?? 0;
|
|
@@ -165,6 +179,11 @@ class CommandRootState {
|
|
|
165
179
|
element?.parentElement?.appendChild(element);
|
|
166
180
|
}
|
|
167
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Sets current value and triggers re-render if cleared.
|
|
184
|
+
*
|
|
185
|
+
* @param value - New value to set
|
|
186
|
+
*/
|
|
168
187
|
setValue(value, opts) {
|
|
169
188
|
if (value !== this.opts.value.current && value === "") {
|
|
170
189
|
afterTick(() => {
|
|
@@ -174,13 +193,20 @@ class CommandRootState {
|
|
|
174
193
|
this.setState("value", value, opts);
|
|
175
194
|
this.opts.value.current = value;
|
|
176
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Selects first non-disabled item on next tick.
|
|
198
|
+
*/
|
|
177
199
|
#selectFirstItem() {
|
|
178
200
|
afterTick(() => {
|
|
179
|
-
const item = this
|
|
201
|
+
const item = this.getValidItems().find((item) => item.getAttribute("aria-disabled") !== "true");
|
|
180
202
|
const value = item?.getAttribute(COMMAND_VALUE_ATTR);
|
|
181
203
|
this.setValue(value || "");
|
|
182
204
|
});
|
|
183
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Updates filtered items/groups based on search.
|
|
208
|
+
* Recalculates scores and filtered count.
|
|
209
|
+
*/
|
|
184
210
|
#filterItems() {
|
|
185
211
|
if (!this._commandState.search || this.opts.shouldFilter.current === false) {
|
|
186
212
|
this._commandState.filtered.count = this.allItems.size;
|
|
@@ -210,13 +236,24 @@ class CommandRootState {
|
|
|
210
236
|
}
|
|
211
237
|
this._commandState.filtered.count = itemCount;
|
|
212
238
|
}
|
|
213
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Gets all non-disabled, visible command items.
|
|
241
|
+
*
|
|
242
|
+
* @returns Array of valid item elements
|
|
243
|
+
* @remarks Exposed for direct item access and bound checking
|
|
244
|
+
*/
|
|
245
|
+
getValidItems() {
|
|
214
246
|
const node = this.opts.ref.current;
|
|
215
247
|
if (!node)
|
|
216
248
|
return [];
|
|
217
249
|
const validItems = Array.from(node.querySelectorAll(COMMAND_VALID_ITEM_SELECTOR)).filter((el) => !!el);
|
|
218
250
|
return validItems;
|
|
219
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Gets currently selected command item.
|
|
254
|
+
*
|
|
255
|
+
* @returns Selected element or undefined
|
|
256
|
+
*/
|
|
220
257
|
#getSelectedItem() {
|
|
221
258
|
const node = this.opts.ref.current;
|
|
222
259
|
if (!node)
|
|
@@ -226,6 +263,10 @@ class CommandRootState {
|
|
|
226
263
|
return;
|
|
227
264
|
return selectedNode;
|
|
228
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Scrolls selected item into view.
|
|
268
|
+
* Special handling for first items in groups.
|
|
269
|
+
*/
|
|
229
270
|
#scrollSelectedIntoView() {
|
|
230
271
|
afterSleep(1, () => {
|
|
231
272
|
const item = this.#getSelectedItem();
|
|
@@ -245,16 +286,52 @@ class CommandRootState {
|
|
|
245
286
|
item.scrollIntoView({ block: "nearest" });
|
|
246
287
|
});
|
|
247
288
|
}
|
|
248
|
-
|
|
249
|
-
|
|
289
|
+
/**
|
|
290
|
+
* Sets selection to item at specified index in valid items array.
|
|
291
|
+
* If index is out of bounds, does nothing.
|
|
292
|
+
*
|
|
293
|
+
* @param index - Zero-based index of item to select
|
|
294
|
+
* @remarks
|
|
295
|
+
* Uses `getValidItems()` to get selectable items, filtering out disabled/hidden ones.
|
|
296
|
+
* Access valid items directly via `getValidItems()` to check bounds before calling.
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* // get valid items length for bounds check
|
|
300
|
+
* const items = getValidItems()
|
|
301
|
+
* if (index < items.length) {
|
|
302
|
+
* updateSelectedToIndex(index)
|
|
303
|
+
* }
|
|
304
|
+
*/
|
|
305
|
+
updateSelectedToIndex(index) {
|
|
306
|
+
const items = this.getValidItems();
|
|
250
307
|
const item = items[index];
|
|
251
308
|
if (item) {
|
|
252
309
|
this.setValue(item.getAttribute(COMMAND_VALUE_ATTR) ?? "");
|
|
253
310
|
}
|
|
254
311
|
}
|
|
255
|
-
|
|
312
|
+
/**
|
|
313
|
+
* Updates selected item by moving up/down relative to current selection.
|
|
314
|
+
* Handles wrapping when loop option is enabled.
|
|
315
|
+
*
|
|
316
|
+
* @param change - Direction to move: 1 for next item, -1 for previous item
|
|
317
|
+
* @remarks
|
|
318
|
+
* The loop behavior wraps:
|
|
319
|
+
* - From last item to first when moving next
|
|
320
|
+
* - From first item to last when moving previous
|
|
321
|
+
*
|
|
322
|
+
* Uses `getValidItems()` to get all selectable items, which filters out disabled/hidden items.
|
|
323
|
+
* You can call `getValidItems()` directly to get the current valid items array.
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* // select next item
|
|
327
|
+
* updateSelectedByItem(1)
|
|
328
|
+
*
|
|
329
|
+
* // get all valid items
|
|
330
|
+
* const items = getValidItems()
|
|
331
|
+
*/
|
|
332
|
+
updateSelectedByItem(change) {
|
|
256
333
|
const selected = this.#getSelectedItem();
|
|
257
|
-
const items = this
|
|
334
|
+
const items = this.getValidItems();
|
|
258
335
|
const index = items.findIndex((item) => item === selected);
|
|
259
336
|
// Get item at this index
|
|
260
337
|
let newSelected = items[index + change];
|
|
@@ -270,7 +347,19 @@ class CommandRootState {
|
|
|
270
347
|
this.setValue(newSelected.getAttribute(COMMAND_VALUE_ATTR) ?? "");
|
|
271
348
|
}
|
|
272
349
|
}
|
|
273
|
-
|
|
350
|
+
/**
|
|
351
|
+
* Moves selection to the first valid item in the next/previous group.
|
|
352
|
+
* If no group is found, falls back to selecting the next/previous item globally.
|
|
353
|
+
*
|
|
354
|
+
* @param change - Direction to move: 1 for next group, -1 for previous group
|
|
355
|
+
* @example
|
|
356
|
+
* // move to first item in next group
|
|
357
|
+
* updateSelectedByGroup(1)
|
|
358
|
+
*
|
|
359
|
+
* // move to first item in previous group
|
|
360
|
+
* updateSelectedByGroup(-1)
|
|
361
|
+
*/
|
|
362
|
+
updateSelectedByGroup(change) {
|
|
274
363
|
const selected = this.#getSelectedItem();
|
|
275
364
|
let group = selected?.closest(COMMAND_GROUP_SELECTOR);
|
|
276
365
|
let item;
|
|
@@ -285,13 +374,21 @@ class CommandRootState {
|
|
|
285
374
|
this.setValue(item.getAttribute(COMMAND_VALUE_ATTR) ?? "");
|
|
286
375
|
}
|
|
287
376
|
else {
|
|
288
|
-
this
|
|
377
|
+
this.updateSelectedByItem(change);
|
|
289
378
|
}
|
|
290
379
|
}
|
|
291
|
-
|
|
380
|
+
/**
|
|
381
|
+
* Maps item id to display value and search keywords.
|
|
382
|
+
* Returns cleanup function to remove mapping.
|
|
383
|
+
*
|
|
384
|
+
* @param id - Unique item identifier
|
|
385
|
+
* @param value - Display text
|
|
386
|
+
* @param keywords - Optional search boost terms
|
|
387
|
+
* @returns Cleanup function
|
|
388
|
+
*/
|
|
292
389
|
registerValue(id, value, keywords) {
|
|
293
390
|
if (value === this.allIds.get(id)?.value)
|
|
294
|
-
return;
|
|
391
|
+
return noop;
|
|
295
392
|
this.allIds.set(id, { value, keywords });
|
|
296
393
|
this._commandState.filtered.items.set(id, this.#score(value, keywords));
|
|
297
394
|
this.#sort();
|
|
@@ -299,6 +396,14 @@ class CommandRootState {
|
|
|
299
396
|
this.allIds.delete(id);
|
|
300
397
|
};
|
|
301
398
|
}
|
|
399
|
+
/**
|
|
400
|
+
* Registers item in command list and its group.
|
|
401
|
+
* Handles filtering, sorting and selection updates.
|
|
402
|
+
*
|
|
403
|
+
* @param id - Item identifier
|
|
404
|
+
* @param groupId - Optional group to add item to
|
|
405
|
+
* @returns Cleanup function that handles selection
|
|
406
|
+
*/
|
|
302
407
|
registerItem(id, groupId) {
|
|
303
408
|
this.allItems.add(id);
|
|
304
409
|
// Track this item within the group
|
|
@@ -326,6 +431,12 @@ class CommandRootState {
|
|
|
326
431
|
this.#scheduleUpdate();
|
|
327
432
|
};
|
|
328
433
|
}
|
|
434
|
+
/**
|
|
435
|
+
* Creates empty group if not exists.
|
|
436
|
+
*
|
|
437
|
+
* @param id - Group identifier
|
|
438
|
+
* @returns Cleanup function
|
|
439
|
+
*/
|
|
329
440
|
registerGroup(id) {
|
|
330
441
|
if (!this.allGroups.has(id)) {
|
|
331
442
|
this.allGroups.set(id, new Set());
|
|
@@ -335,34 +446,53 @@ class CommandRootState {
|
|
|
335
446
|
this.allGroups.delete(id);
|
|
336
447
|
};
|
|
337
448
|
}
|
|
449
|
+
/**
|
|
450
|
+
* Selects last valid item.
|
|
451
|
+
*/
|
|
338
452
|
#last() {
|
|
339
|
-
return this
|
|
340
|
-
}
|
|
453
|
+
return this.updateSelectedToIndex(this.getValidItems().length - 1);
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Handles next item selection:
|
|
457
|
+
* - Meta: Jump to last
|
|
458
|
+
* - Alt: Next group
|
|
459
|
+
* - Default: Next item
|
|
460
|
+
*
|
|
461
|
+
* @param e - Keyboard event
|
|
462
|
+
*/
|
|
341
463
|
#next(e) {
|
|
342
464
|
e.preventDefault();
|
|
343
465
|
if (e.metaKey) {
|
|
344
466
|
this.#last();
|
|
345
467
|
}
|
|
346
468
|
else if (e.altKey) {
|
|
347
|
-
this
|
|
469
|
+
this.updateSelectedByGroup(1);
|
|
348
470
|
}
|
|
349
471
|
else {
|
|
350
|
-
this
|
|
472
|
+
this.updateSelectedByItem(1);
|
|
351
473
|
}
|
|
352
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* Handles previous item selection:
|
|
477
|
+
* - Meta: Jump to first
|
|
478
|
+
* - Alt: Previous group
|
|
479
|
+
* - Default: Previous item
|
|
480
|
+
*
|
|
481
|
+
* @param e - Keyboard event
|
|
482
|
+
*/
|
|
353
483
|
#prev(e) {
|
|
354
484
|
e.preventDefault();
|
|
355
485
|
if (e.metaKey) {
|
|
356
486
|
// First item
|
|
357
|
-
this
|
|
487
|
+
this.updateSelectedToIndex(0);
|
|
358
488
|
}
|
|
359
489
|
else if (e.altKey) {
|
|
360
490
|
// Previous group
|
|
361
|
-
this
|
|
491
|
+
this.updateSelectedByGroup(-1);
|
|
362
492
|
}
|
|
363
493
|
else {
|
|
364
494
|
// Previous item
|
|
365
|
-
this
|
|
495
|
+
this.updateSelectedByItem(-1);
|
|
366
496
|
}
|
|
367
497
|
}
|
|
368
498
|
onkeydown(e) {
|
|
@@ -392,7 +522,7 @@ class CommandRootState {
|
|
|
392
522
|
case kbd.HOME:
|
|
393
523
|
// first item
|
|
394
524
|
e.preventDefault();
|
|
395
|
-
this
|
|
525
|
+
this.updateSelectedToIndex(0);
|
|
396
526
|
break;
|
|
397
527
|
case kbd.END:
|
|
398
528
|
// last item
|
|
@@ -45,6 +45,70 @@
|
|
|
45
45
|
onStateChange: box.with(() => onStateChange),
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
// Imperative APIs - DO NOT REMOVE OR RENAME
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Sets selection to item at specified index in valid items array.
|
|
52
|
+
* If index is out of bounds, does nothing.
|
|
53
|
+
*
|
|
54
|
+
* @param index - Zero-based index of item to select
|
|
55
|
+
* @remarks
|
|
56
|
+
* Uses `getValidItems()` to get selectable items, filtering out disabled/hidden ones.
|
|
57
|
+
* Access valid items directly via `getValidItems()` to check bounds before calling.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* // get valid items length for bounds check
|
|
61
|
+
* const items = getValidItems()
|
|
62
|
+
* if (index < items.length) {
|
|
63
|
+
* updateSelectedToIndex(index)
|
|
64
|
+
* }
|
|
65
|
+
*/
|
|
66
|
+
export const updateSelectedToIndex: (typeof rootState)["updateSelectedToIndex"] = (i) =>
|
|
67
|
+
rootState.updateSelectedToIndex(i);
|
|
68
|
+
/**
|
|
69
|
+
* Moves selection to the first valid item in the next/previous group.
|
|
70
|
+
* If no group is found, falls back to selecting the next/previous item globally.
|
|
71
|
+
*
|
|
72
|
+
* @param change - Direction to move: 1 for next group, -1 for previous group
|
|
73
|
+
* @example
|
|
74
|
+
* // move to first item in next group
|
|
75
|
+
* updateSelectedByGroup(1)
|
|
76
|
+
*
|
|
77
|
+
* // move to first item in previous group
|
|
78
|
+
* updateSelectedByGroup(-1)
|
|
79
|
+
*/
|
|
80
|
+
export const updateSelectedByGroup: (typeof rootState)["updateSelectedByGroup"] = (c) =>
|
|
81
|
+
rootState.updateSelectedByGroup(c);
|
|
82
|
+
/**
|
|
83
|
+
* Updates selected item by moving up/down relative to current selection.
|
|
84
|
+
* Handles wrapping when loop option is enabled.
|
|
85
|
+
*
|
|
86
|
+
* @param change - Direction to move: 1 for next item, -1 for previous item
|
|
87
|
+
* @remarks
|
|
88
|
+
* The loop behavior wraps:
|
|
89
|
+
* - From last item to first when moving next
|
|
90
|
+
* - From first item to last when moving previous
|
|
91
|
+
*
|
|
92
|
+
* Uses `getValidItems()` to get all selectable items, which filters out disabled/hidden items.
|
|
93
|
+
* You can call `getValidItems()` directly to get the current valid items array.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* // select next item
|
|
97
|
+
* updateSelectedByItem(1)
|
|
98
|
+
*
|
|
99
|
+
* // get all valid items
|
|
100
|
+
* const items = getValidItems()
|
|
101
|
+
*/
|
|
102
|
+
export const updateSelectedByItem: (typeof rootState)["updateSelectedByItem"] = (c) =>
|
|
103
|
+
rootState.updateSelectedByItem(c);
|
|
104
|
+
/**
|
|
105
|
+
* Gets all non-disabled, visible command items.
|
|
106
|
+
*
|
|
107
|
+
* @returns Array of valid item elements
|
|
108
|
+
* @remarks Exposed for direct item access and bound checking
|
|
109
|
+
*/
|
|
110
|
+
export const getValidItems = () => rootState.getValidItems();
|
|
111
|
+
|
|
48
112
|
const mergedProps = $derived(mergeProps(restProps, rootState.props));
|
|
49
113
|
</script>
|
|
50
114
|
|
|
@@ -1,4 +1,59 @@
|
|
|
1
1
|
import type { CommandRootProps } from "../types.js";
|
|
2
|
-
declare const Command: import("svelte").Component<CommandRootProps, {
|
|
2
|
+
declare const Command: import("svelte").Component<CommandRootProps, {
|
|
3
|
+
/**
|
|
4
|
+
* Sets selection to item at specified index in valid items array.
|
|
5
|
+
* If index is out of bounds, does nothing.
|
|
6
|
+
*
|
|
7
|
+
* @param index - Zero-based index of item to select
|
|
8
|
+
* @remarks
|
|
9
|
+
* Uses `getValidItems()` to get selectable items, filtering out disabled/hidden ones.
|
|
10
|
+
* Access valid items directly via `getValidItems()` to check bounds before calling.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // get valid items length for bounds check
|
|
14
|
+
* const items = getValidItems()
|
|
15
|
+
* if (index < items.length) {
|
|
16
|
+
* updateSelectedToIndex(index)
|
|
17
|
+
* }
|
|
18
|
+
*/ updateSelectedToIndex: (index: number) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Moves selection to the first valid item in the next/previous group.
|
|
21
|
+
* If no group is found, falls back to selecting the next/previous item globally.
|
|
22
|
+
*
|
|
23
|
+
* @param change - Direction to move: 1 for next group, -1 for previous group
|
|
24
|
+
* @example
|
|
25
|
+
* // move to first item in next group
|
|
26
|
+
* updateSelectedByGroup(1)
|
|
27
|
+
*
|
|
28
|
+
* // move to first item in previous group
|
|
29
|
+
* updateSelectedByGroup(-1)
|
|
30
|
+
*/ updateSelectedByGroup: (change: 1 | -1) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Updates selected item by moving up/down relative to current selection.
|
|
33
|
+
* Handles wrapping when loop option is enabled.
|
|
34
|
+
*
|
|
35
|
+
* @param change - Direction to move: 1 for next item, -1 for previous item
|
|
36
|
+
* @remarks
|
|
37
|
+
* The loop behavior wraps:
|
|
38
|
+
* - From last item to first when moving next
|
|
39
|
+
* - From first item to last when moving previous
|
|
40
|
+
*
|
|
41
|
+
* Uses `getValidItems()` to get all selectable items, which filters out disabled/hidden items.
|
|
42
|
+
* You can call `getValidItems()` directly to get the current valid items array.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // select next item
|
|
46
|
+
* updateSelectedByItem(1)
|
|
47
|
+
*
|
|
48
|
+
* // get all valid items
|
|
49
|
+
* const items = getValidItems()
|
|
50
|
+
*/ updateSelectedByItem: (change: 1 | -1) => void;
|
|
51
|
+
/**
|
|
52
|
+
* Gets all non-disabled, visible command items.
|
|
53
|
+
*
|
|
54
|
+
* @returns Array of valid item elements
|
|
55
|
+
* @remarks Exposed for direct item access and bound checking
|
|
56
|
+
*/ getValidItems: () => HTMLElement[];
|
|
57
|
+
}, "value" | "ref">;
|
|
3
58
|
type Command = ReturnType<typeof Command>;
|
|
4
59
|
export default Command;
|