@proyecto-viviana/solid-stately 0.3.0 → 0.4.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.
Files changed (77) hide show
  1. package/dist/calendar/IncompleteDate.d.ts +61 -0
  2. package/dist/calendar/createCalendarState.d.ts +20 -10
  3. package/dist/calendar/createDateFieldState.d.ts +65 -20
  4. package/dist/calendar/createRangeCalendarState.d.ts +20 -2
  5. package/dist/calendar/createTimeFieldState.d.ts +38 -67
  6. package/dist/calendar/index.d.ts +1 -1
  7. package/dist/calendar/intl/index.d.ts +10 -0
  8. package/dist/collections/ListCollection.d.ts +8 -0
  9. package/dist/collections/createListState.d.ts +16 -1
  10. package/dist/collections/createSelectionState.d.ts +17 -5
  11. package/dist/collections/getChildNodes.d.ts +19 -0
  12. package/dist/collections/index.d.ts +3 -2
  13. package/dist/collections/types.d.ts +9 -0
  14. package/dist/color/intl/index.d.ts +14 -0
  15. package/dist/combobox/createComboBoxState.d.ts +7 -0
  16. package/dist/dnd/createDroppableCollectionState.d.ts +17 -2
  17. package/dist/dnd/index.d.ts +1 -1
  18. package/dist/dnd/types.d.ts +3 -1
  19. package/dist/grid/types.d.ts +17 -3
  20. package/dist/index.d.ts +6 -6
  21. package/dist/index.js +4372 -1087
  22. package/dist/index.js.map +1 -1
  23. package/dist/select/createSelectState.d.ts +6 -0
  24. package/dist/selection/Selection.d.ts +16 -0
  25. package/dist/selection/SelectionManager.d.ts +119 -0
  26. package/dist/selection/createMultipleSelectionState.d.ts +59 -0
  27. package/dist/selection/index.d.ts +1 -0
  28. package/dist/table/TableCollection.d.ts +30 -0
  29. package/dist/table/createTreeGridState.d.ts +15 -0
  30. package/dist/table/index.d.ts +2 -1
  31. package/dist/table/types.d.ts +58 -0
  32. package/dist/toast/createToastState.d.ts +16 -0
  33. package/dist/toast/index.d.ts +1 -1
  34. package/dist/tree/types.d.ts +14 -6
  35. package/package.json +1 -1
  36. package/src/calendar/IncompleteDate.ts +430 -0
  37. package/src/calendar/createCalendarState.ts +129 -40
  38. package/src/calendar/createDateFieldState.ts +633 -477
  39. package/src/calendar/createRangeCalendarState.ts +343 -30
  40. package/src/calendar/createTimeFieldState.ts +179 -467
  41. package/src/calendar/index.ts +2 -0
  42. package/src/calendar/intl/index.ts +364 -0
  43. package/src/collections/ListCollection.ts +31 -0
  44. package/src/collections/createListState.ts +139 -17
  45. package/src/collections/createMenuState.ts +3 -0
  46. package/src/collections/createSelectionState.ts +38 -28
  47. package/src/collections/getChildNodes.ts +129 -0
  48. package/src/collections/index.ts +12 -0
  49. package/src/collections/types.ts +10 -0
  50. package/src/color/Color.ts +104 -133
  51. package/src/color/intl/index.ts +1409 -0
  52. package/src/combobox/createComboBoxState.ts +38 -13
  53. package/src/dnd/createDroppableCollectionState.ts +125 -6
  54. package/src/dnd/index.d.ts +1 -0
  55. package/src/dnd/index.ts +1 -0
  56. package/src/dnd/types.d.ts +3 -1
  57. package/src/dnd/types.ts +4 -1
  58. package/src/grid/createGridState.ts +36 -2
  59. package/src/grid/types.ts +17 -3
  60. package/src/index.d.ts +1 -0
  61. package/src/index.ts +14 -0
  62. package/src/select/createSelectState.ts +15 -2
  63. package/src/selection/Selection.ts +28 -0
  64. package/src/selection/SelectionManager.ts +541 -0
  65. package/src/selection/createMultipleSelectionState.ts +247 -0
  66. package/src/selection/index.ts +6 -0
  67. package/src/steplist/createStepListState.ts +27 -4
  68. package/src/table/TableCollection.ts +213 -43
  69. package/src/table/createTableState.ts +20 -0
  70. package/src/table/createTreeGridState.ts +146 -0
  71. package/src/table/index.ts +3 -0
  72. package/src/table/types.ts +66 -0
  73. package/src/tabs/createTabListState.ts +11 -9
  74. package/src/toast/createToastState.ts +43 -10
  75. package/src/toast/index.ts +1 -0
  76. package/src/tree/createTreeState.ts +37 -6
  77. package/src/tree/types.ts +14 -6
@@ -11,6 +11,7 @@ import { access, type MaybeAccessor } from "../utils";
11
11
  import { createListState } from "../collections/createListState";
12
12
  import { createOverlayTriggerState } from "../overlays";
13
13
  import type { Key, CollectionNode, Collection, FocusStrategy } from "../collections/types";
14
+ import type { SelectionManager } from "../selection/SelectionManager";
14
15
 
15
16
  export type MenuTriggerAction = "focus" | "input" | "manual";
16
17
 
@@ -78,6 +79,12 @@ export interface ComboBoxStateProps<T = unknown> {
78
79
  export interface ComboBoxState<T = unknown> {
79
80
  /** The collection of items (may be filtered). */
80
81
  readonly collection: Accessor<Collection<T>>;
82
+ /**
83
+ * The collection-aware selection manager. Mirrors upstream `useComboBoxState`,
84
+ * which returns the `useListState` selection manager bound to the unfiltered
85
+ * collection (selection spans all items; filtering is display-only).
86
+ */
87
+ readonly selectionManager: SelectionManager<T>;
81
88
  /** Whether the combobox dropdown is open. */
82
89
  readonly isOpen: Accessor<boolean>;
83
90
  /** Open the combobox dropdown. */
@@ -366,6 +373,28 @@ export function createComboBoxState<T = unknown>(
366
373
  };
367
374
 
368
375
  // ---- Open/Toggle Logic ----
376
+ // Auto-focus the menu on open the way upstream's listbox does via its
377
+ // `autoFocus: state.focusStrategy || true` prop (useComboBox.ts:490 → useSelectableCollection):
378
+ // the first selectable selected key, else the first/last key for an explicit
379
+ // strategy, else nothing. The port drives this from the state (createListBox
380
+ // reimplements nav inline and does not honor a listbox `autoFocus` prop), so
381
+ // BOTH open() and toggle() must apply it — a button/mouse open routes through
382
+ // toggle(), and omitting it there left the menu opened with no active option
383
+ // (no aria-activedescendant / no highlighted row) unlike upstream.
384
+ const applyOpenFocus = (strategy: FocusStrategy | null) => {
385
+ const key = selectedKey();
386
+ const nextFocusedKey =
387
+ key != null && !listState.isDisabled(key)
388
+ ? key
389
+ : strategy === "first"
390
+ ? displayedCollection().getFirstKey()
391
+ : strategy === "last"
392
+ ? displayedCollection().getLastKey()
393
+ : null;
394
+ listState.setFocused(true);
395
+ listState.setFocusedKey(nextFocusedKey);
396
+ };
397
+
369
398
  const open = (strategy: FocusStrategy | null = null, trigger?: MenuTriggerAction) => {
370
399
  const displayAll = trigger === "manual" || (trigger === "focus" && menuTrigger() === "focus");
371
400
 
@@ -384,17 +413,7 @@ export function createComboBoxState<T = unknown>(
384
413
  menuOpenTrigger = trigger ?? "focus";
385
414
  setFocusStrategy(strategy);
386
415
  if (!overlayState.isOpen()) {
387
- const key = selectedKey();
388
- const nextFocusedKey =
389
- key != null && !listState.isDisabled(key)
390
- ? key
391
- : strategy === "first"
392
- ? displayedCollection().getFirstKey()
393
- : strategy === "last"
394
- ? displayedCollection().getLastKey()
395
- : null;
396
- listState.setFocused(true);
397
- listState.setFocusedKey(nextFocusedKey);
416
+ applyOpenFocus(strategy);
398
417
  }
399
418
  overlayState.open();
400
419
  };
@@ -410,15 +429,20 @@ export function createComboBoxState<T = unknown>(
410
429
 
411
430
  if (!canOpen && !overlayState.isOpen()) return;
412
431
 
413
- if (displayAll && !overlayState.isOpen()) {
432
+ const willOpen = !overlayState.isOpen();
433
+
434
+ if (displayAll && willOpen) {
414
435
  setShowAllItems(true);
415
436
  }
416
437
 
417
- if (!overlayState.isOpen()) {
438
+ if (willOpen) {
418
439
  menuOpenTrigger = trigger ?? "focus";
419
440
  }
420
441
 
421
442
  setFocusStrategy(strategy);
443
+ if (willOpen) {
444
+ applyOpenFocus(strategy);
445
+ }
422
446
  overlayState.toggle();
423
447
  };
424
448
 
@@ -624,6 +648,7 @@ export function createComboBoxState<T = unknown>(
624
648
  inputValue,
625
649
  defaultInputValue: getProps().defaultInputValue ?? "",
626
650
  setInputValue,
651
+ selectionManager: listState.selectionManager,
627
652
  focusedKey: listState.focusedKey,
628
653
  setFocusedKey: listState.setFocusedKey,
629
654
  isFocused,
@@ -21,6 +21,7 @@ import type {
21
21
  DroppableCollectionReorderEvent,
22
22
  ItemDropTarget,
23
23
  } from "./types";
24
+ import type { Collection } from "../collections/types";
24
25
 
25
26
  export interface DroppableCollectionStateOptions {
26
27
  /**
@@ -56,6 +57,14 @@ export interface DroppableCollectionStateOptions {
56
57
  onMove?: (e: DroppableCollectionReorderEvent) => void;
57
58
  /** A function returning whether a given target is a valid "on" drop target. */
58
59
  shouldAcceptItemDrop?: (target: ItemDropTarget, types: DragTypes) => boolean;
60
+ /**
61
+ * The host collection. Used by the default drop-operation feature detection to
62
+ * confirm a reorder stays within the same parent and to prevent dropping an
63
+ * item onto itself or a descendant. Optional: a flat collection (e.g. a plain
64
+ * ListBox) is always "within parent" and has no descendants, so omitting it is
65
+ * faithful for flat hosts.
66
+ */
67
+ collection?: Collection<unknown>;
59
68
  /** Whether the droppable collection is disabled. */
60
69
  isDisabled?: boolean;
61
70
  }
@@ -86,11 +95,19 @@ export interface DroppableCollectionState {
86
95
  isInternal: boolean,
87
96
  draggingKeys?: Set<string | number>,
88
97
  ): void;
89
- /** Get the drop operation for a target. */
98
+ /**
99
+ * Get the drop operation for a target. `isInternal` is true when the active
100
+ * drag originated from this same collection; `draggingKeys` are the keys being
101
+ * dragged. Both feed the handler-aware feature detection that decides whether a
102
+ * given drop position (before/after/on/root) is a valid target — e.g. a
103
+ * reorder-only collection rejects "on" targets, matching React Aria.
104
+ */
90
105
  getDropOperation(
91
106
  target: DropTarget,
92
107
  types: DragTypes,
93
108
  allowedOperations: DropOperation[],
109
+ isInternal?: boolean,
110
+ draggingKeys?: Set<string | number>,
94
111
  ): DropOperation;
95
112
  /** Check if an item drop should be accepted. */
96
113
  shouldAcceptItemDrop(target: ItemDropTarget, types: DragTypes): boolean;
@@ -287,22 +304,124 @@ export function createDroppableCollectionState(
287
304
  }
288
305
  };
289
306
 
290
- const getDropOperation = (
291
- dropTarget: DropTarget,
307
+ // Faithful of react-stately's `isDraggingWithinParent`: every dragged item must
308
+ // share the target's parent for a reorder to be valid. A flat collection (no
309
+ // `collection` provided) has a single implicit parent, so this is always true.
310
+ const isDraggingWithinParent = (
311
+ target: ItemDropTarget,
312
+ draggingKeys: Set<string | number>,
313
+ ): boolean => {
314
+ const collection = getProps().collection;
315
+ if (!collection) return true;
316
+ const targetNode = collection.getItem(target.key);
317
+ for (const key of draggingKeys) {
318
+ const node = collection.getItem(key);
319
+ if (node?.parentKey !== targetNode?.parentKey) {
320
+ return false;
321
+ }
322
+ }
323
+ return true;
324
+ };
325
+
326
+ // Faithful of react-stately's `defaultGetDropOperation`: decide whether a drop
327
+ // position is a valid target from the handlers that are actually wired. A
328
+ // reorder-only collection (only `onReorder`) accepts before/after but rejects
329
+ // "on"; an item-drop collection accepts "on"; and so on.
330
+ const defaultGetDropOperation = (
331
+ target: DropTarget,
292
332
  types: DragTypes,
293
333
  allowedOperations: DropOperation[],
334
+ isInternal: boolean,
335
+ draggingKeys: Set<string | number>,
294
336
  ): DropOperation => {
295
337
  const p = getProps();
296
338
 
339
+ if (p.isDisabled || !target) {
340
+ return "cancel";
341
+ }
342
+
297
343
  if (!isAccepted(types)) {
298
344
  return "cancel";
299
345
  }
300
346
 
301
- if (typeof p.getDropOperation === "function") {
302
- return p.getDropOperation(dropTarget, types, allowedOperations);
347
+ const isValidInsert =
348
+ p.onInsert &&
349
+ target.type === "item" &&
350
+ !isInternal &&
351
+ (target.dropPosition === "before" || target.dropPosition === "after");
352
+ const isValidReorder =
353
+ p.onReorder &&
354
+ target.type === "item" &&
355
+ isInternal &&
356
+ (target.dropPosition === "before" || target.dropPosition === "after") &&
357
+ isDraggingWithinParent(target, draggingKeys);
358
+
359
+ const isItemDropAllowed =
360
+ target.type !== "item" ||
361
+ target.dropPosition !== "on" ||
362
+ !p.shouldAcceptItemDrop ||
363
+ p.shouldAcceptItemDrop(target, types);
364
+
365
+ const isValidMove = p.onMove && target.type === "item" && isInternal && isItemDropAllowed;
366
+
367
+ // Feedback was that internal root drop was weird, so it is prevented.
368
+ const isValidRootDrop = p.onRootDrop && target.type === "root" && !isInternal;
369
+
370
+ // Automatically prevent items (e.g. folders) from being dropped on themselves.
371
+ const isValidOnItemDrop =
372
+ p.onItemDrop &&
373
+ target.type === "item" &&
374
+ target.dropPosition === "on" &&
375
+ !(isInternal && target.key != null && draggingKeys.has(target.key)) &&
376
+ isItemDropAllowed;
377
+
378
+ if (
379
+ p.onDrop ||
380
+ isValidInsert ||
381
+ isValidReorder ||
382
+ isValidMove ||
383
+ isValidRootDrop ||
384
+ isValidOnItemDrop
385
+ ) {
386
+ if (typeof p.getDropOperation === "function") {
387
+ return p.getDropOperation(target, types, allowedOperations);
388
+ }
389
+ return allowedOperations[0] ?? "cancel";
390
+ }
391
+
392
+ return "cancel";
393
+ };
394
+
395
+ const getDropOperation = (
396
+ dropTarget: DropTarget,
397
+ types: DragTypes,
398
+ allowedOperations: DropOperation[],
399
+ isInternal: boolean = false,
400
+ draggingKeys: Set<string | number> = new Set(),
401
+ ): DropOperation => {
402
+ // Prevent dropping items onto themselves or their descendants (faithful of
403
+ // react-stately's `getDropOperation` guard). Only meaningful for a nested
404
+ // collection; a flat host has no descendants.
405
+ if (isInternal && dropTarget.type === "item" && draggingKeys.size > 0) {
406
+ if (draggingKeys.has(dropTarget.key) && dropTarget.dropPosition === "on") {
407
+ return "cancel";
408
+ }
409
+
410
+ const collection = getProps().collection;
411
+ if (collection) {
412
+ let currentKey: string | number | null = dropTarget.key;
413
+ while (currentKey != null) {
414
+ const item = collection.getItem(currentKey);
415
+ const parentKey = item?.parentKey;
416
+ if (parentKey != null && draggingKeys.has(parentKey)) {
417
+ return "cancel";
418
+ }
419
+ currentKey = parentKey ?? null;
420
+ }
421
+ }
303
422
  }
304
423
 
305
- return allowedOperations[0] ?? "cancel";
424
+ return defaultGetDropOperation(dropTarget, types, allowedOperations, isInternal, draggingKeys);
306
425
  };
307
426
 
308
427
  const shouldAcceptItemDrop = (dropTarget: ItemDropTarget, types: DragTypes): boolean => {
@@ -32,6 +32,7 @@ export type {
32
32
  DroppableCollectionRootDropEvent,
33
33
  DroppableCollectionOnItemDropEvent,
34
34
  DroppableCollectionReorderEvent,
35
+ DragType,
35
36
  DragTypes,
36
37
  DropTargetDelegate,
37
38
  DraggableCollectionStartEvent,
package/src/dnd/index.ts CHANGED
@@ -33,6 +33,7 @@ export type {
33
33
  DroppableCollectionRootDropEvent,
34
34
  DroppableCollectionOnItemDropEvent,
35
35
  DroppableCollectionReorderEvent,
36
+ DragType,
36
37
  DragTypes,
37
38
  DropTargetDelegate,
38
39
  DraggableCollectionStartEvent,
@@ -174,10 +174,12 @@ export interface DroppableCollectionReorderEvent {
174
174
  /** The drop target. */
175
175
  target: ItemDropTarget;
176
176
  }
177
+ /** A drag type: either a MIME type string or a symbol such as the directory drag type. */
178
+ export type DragType = string | symbol;
177
179
  /** Interface for checking drag types. */
178
180
  export interface DragTypes {
179
181
  /** Returns whether the drag contains data of the given type. */
180
- has(type: string | symbol): boolean;
182
+ has(type: DragType | DragType[]): boolean;
181
183
  }
182
184
  /** Delegate for determining drop targets within a collection. */
183
185
  export interface DropTargetDelegate {
package/src/dnd/types.ts CHANGED
@@ -205,10 +205,13 @@ export interface DroppableCollectionReorderEvent {
205
205
  target: ItemDropTarget;
206
206
  }
207
207
 
208
+ /** A drag type: either a MIME type string or a symbol such as the directory drag type. */
209
+ export type DragType = string | symbol;
210
+
208
211
  /** Interface for checking drag types. */
209
212
  export interface DragTypes {
210
213
  /** Returns whether the drag contains data of the given type. */
211
- has(type: string | symbol): boolean;
214
+ has(type: DragType | DragType[]): boolean;
212
215
  }
213
216
 
214
217
  /** Delegate for determining drop targets within a collection. */
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { createSignal, createEffect, createMemo, on, type Accessor } from "solid-js";
7
7
  import type { GridState, GridStateOptions, GridCollection, GridNode } from "./types";
8
- import type { Key, FocusStrategy } from "../collections/types";
8
+ import type { Key, FocusStrategy, SelectionBehavior, Selection } from "../collections/types";
9
9
 
10
10
  /**
11
11
  * Creates state management for a grid component.
@@ -40,11 +40,33 @@ export function createGridState<T extends object, C extends GridCollection<T> =
40
40
  });
41
41
 
42
42
  const selectionMode = createMemo(() => getOptions().selectionMode ?? "none");
43
- const selectionBehavior = createMemo(() => getOptions().selectionBehavior ?? "toggle");
43
+ const selectionBehaviorProp = (): SelectionBehavior => getOptions().selectionBehavior ?? "toggle";
44
+ const [selectionBehaviorState, setSelectionBehaviorState] =
45
+ createSignal<SelectionBehavior>(selectionBehaviorProp());
46
+ const selectionBehavior = createMemo(() => selectionBehaviorState());
44
47
  const disallowEmptySelection = createMemo(() => getOptions().disallowEmptySelection ?? false);
48
+ const disabledBehavior = createMemo(() => getOptions().disabledBehavior ?? "all");
45
49
 
46
50
  const focusMode = createMemo(() => getOptions().focusMode ?? "row");
47
51
 
52
+ createEffect(
53
+ on(selectionBehaviorProp, (behavior) => {
54
+ setSelectionBehaviorState(behavior);
55
+ }),
56
+ );
57
+
58
+ createEffect(() => {
59
+ const keys = selectedKeys();
60
+ if (
61
+ selectionBehaviorProp() === "replace" &&
62
+ selectionBehaviorState() === "toggle" &&
63
+ keys !== "all" &&
64
+ keys.size === 0
65
+ ) {
66
+ setSelectionBehaviorState("replace");
67
+ }
68
+ });
69
+
48
70
  const setFocusedKey = (key: Key | null, strategy: FocusStrategy = "first") => {
49
71
  const opts = getOptions();
50
72
  const collection = opts.collection;
@@ -215,6 +237,10 @@ export function createGridState<T extends object, C extends GridCollection<T> =
215
237
  setAnchorKey(key);
216
238
  };
217
239
 
240
+ const setSelectedKeys = (keys: Selection | Iterable<Key>) => {
241
+ updateSelection(normalizeSelection(keys));
242
+ };
243
+
218
244
  const extendSelection = (toKey: Key) => {
219
245
  if (isDisabled(toKey)) return;
220
246
  if (selectionMode() !== "multiple") {
@@ -273,6 +299,9 @@ export function createGridState<T extends object, C extends GridCollection<T> =
273
299
  get disabledKeys() {
274
300
  return disabledKeys();
275
301
  },
302
+ get disabledBehavior() {
303
+ return disabledBehavior();
304
+ },
276
305
  get isKeyboardNavigationDisabled() {
277
306
  return isKeyboardNavigationDisabled();
278
307
  },
@@ -291,6 +320,9 @@ export function createGridState<T extends object, C extends GridCollection<T> =
291
320
  get selectionBehavior() {
292
321
  return selectionBehavior();
293
322
  },
323
+ get disallowEmptySelection() {
324
+ return disallowEmptySelection();
325
+ },
294
326
  get selectedKeys() {
295
327
  return selectedKeys();
296
328
  },
@@ -298,8 +330,10 @@ export function createGridState<T extends object, C extends GridCollection<T> =
298
330
  isDisabled,
299
331
  setFocusedKey,
300
332
  setFocused: setIsFocused,
333
+ setSelectionBehavior: setSelectionBehaviorState,
301
334
  toggleSelection,
302
335
  replaceSelection,
336
+ setSelectedKeys,
303
337
  extendSelection,
304
338
  selectAll,
305
339
  clearSelection,
package/src/grid/types.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Based on @react-types/grid.
4
4
  */
5
5
 
6
- import type { Key, FocusStrategy } from "../collections/types";
6
+ import type { Key, FocusStrategy, Selection, SelectionBehavior } from "../collections/types";
7
7
 
8
8
  /**
9
9
  * Represents the type of a grid node.
@@ -42,6 +42,10 @@ export interface GridNode<T = unknown> {
42
42
  prevKey?: Key | null;
43
43
  /** Next sibling key (visible order). */
44
44
  nextKey?: Key | null;
45
+ /** Key of the first child node, if any (used by tree grids). */
46
+ firstChildKey?: Key | null;
47
+ /** Key of the last child node, if any (used by tree grids). */
48
+ lastChildKey?: Key | null;
45
49
  /** Whether this node has child nodes. */
46
50
  hasChildNodes: boolean;
47
51
  /** Child nodes (cells for rows, rows for sections). */
@@ -123,6 +127,8 @@ export interface GridState<T, C extends GridCollection<T> = GridCollection<T>> {
123
127
  readonly collection: C;
124
128
  /** Keys of disabled rows. */
125
129
  readonly disabledKeys: Set<Key>;
130
+ /** Whether `disabledKeys` applies to all interactions, or only selection. */
131
+ readonly disabledBehavior: "selection" | "all";
126
132
  /** Whether keyboard navigation is disabled. */
127
133
  readonly isKeyboardNavigationDisabled: boolean;
128
134
  /** Currently focused key. */
@@ -134,7 +140,9 @@ export interface GridState<T, C extends GridCollection<T> = GridCollection<T>> {
134
140
  /** The selection mode. */
135
141
  readonly selectionMode: "none" | "single" | "multiple";
136
142
  /** The selection behavior. */
137
- readonly selectionBehavior: "toggle" | "replace";
143
+ readonly selectionBehavior: SelectionBehavior;
144
+ /** Whether empty selection is disallowed. */
145
+ readonly disallowEmptySelection: boolean;
138
146
  /** The currently selected keys. */
139
147
  readonly selectedKeys: "all" | Set<Key>;
140
148
  /** Check if a key is selected. */
@@ -145,10 +153,14 @@ export interface GridState<T, C extends GridCollection<T> = GridCollection<T>> {
145
153
  setFocusedKey(key: Key | null, childFocusStrategy?: FocusStrategy): void;
146
154
  /** Set focused state. */
147
155
  setFocused(isFocused: boolean): void;
156
+ /** Set the selection behavior. */
157
+ setSelectionBehavior(behavior: SelectionBehavior): void;
148
158
  /** Toggle selection for a key. */
149
159
  toggleSelection(key: Key): void;
150
160
  /** Replace selection with a key. */
151
161
  replaceSelection(key: Key): void;
162
+ /** Set multiple selected keys. */
163
+ setSelectedKeys(keys: Selection | Iterable<Key>): void;
152
164
  /** Extend selection to a key (shift-click). */
153
165
  extendSelection(toKey: Key): void;
154
166
  /** Select all rows. */
@@ -169,12 +181,14 @@ export interface GridStateOptions<T, C extends GridCollection<T> = GridCollectio
169
181
  collection: C;
170
182
  /** Keys of disabled rows. */
171
183
  disabledKeys?: Iterable<Key>;
184
+ /** Whether `disabledKeys` applies to all interactions, or only selection. @default "all" */
185
+ disabledBehavior?: "selection" | "all";
172
186
  /** Focus mode: 'row' or 'cell'. */
173
187
  focusMode?: "row" | "cell";
174
188
  /** Selection mode. */
175
189
  selectionMode?: "none" | "single" | "multiple";
176
190
  /** Selection behavior. */
177
- selectionBehavior?: "toggle" | "replace";
191
+ selectionBehavior?: SelectionBehavior;
178
192
  /** Whether empty selection is disallowed. */
179
193
  disallowEmptySelection?: boolean;
180
194
  /** Currently selected keys (controlled). */
package/src/index.d.ts CHANGED
@@ -266,6 +266,7 @@ export {
266
266
  type DroppableCollectionRootDropEvent,
267
267
  type DroppableCollectionOnItemDropEvent,
268
268
  type DroppableCollectionReorderEvent,
269
+ type DragType,
269
270
  type DragTypes,
270
271
  type DropTargetDelegate,
271
272
  type DraggableCollectionStartEvent,
package/src/index.ts CHANGED
@@ -50,13 +50,17 @@ export {
50
50
  type MultipleSelection,
51
51
  ListCollection,
52
52
  createListCollection,
53
+ getItemCount,
53
54
  createSelectionState,
54
55
  type SelectionStateProps,
55
56
  type SelectionState,
57
+ type SelectionPressEvent,
56
58
  createListState,
59
+ createFilteredListState,
57
60
  createSingleSelectListState,
58
61
  type ListStateProps,
59
62
  type ListState,
63
+ type ListFilterFn,
60
64
  type SingleSelectListStateProps,
61
65
  type SingleSelectListState,
62
66
  createMenuState,
@@ -72,6 +76,9 @@ export {
72
76
  useMultipleSelectionState,
73
77
  type MultipleSelectionStateProps,
74
78
  type MultipleSelectionState,
79
+ SelectionManager,
80
+ type SelectionManagerOptions,
81
+ type LayoutDelegate,
75
82
  } from "./selection";
76
83
 
77
84
  // Menu (module-compat aliases)
@@ -143,6 +150,7 @@ export {
143
150
  type ToastOptions,
144
151
  type QueuedToast,
145
152
  type ToastQueueOptions,
153
+ type ToastAction,
146
154
  type ToastStateProps,
147
155
  type ToastState,
148
156
  } from "./toast";
@@ -161,6 +169,8 @@ export {
161
169
  createCalendarState,
162
170
  type CalendarStateProps,
163
171
  type CalendarState,
172
+ type CalendarSelectionMode,
173
+ type CalendarValueType,
164
174
  type ValidationState,
165
175
  type CalendarPageBehavior,
166
176
  type CalendarSelectionAlignment,
@@ -240,12 +250,15 @@ export {
240
250
 
241
251
  export {
242
252
  createTableState,
253
+ createTreeGridState,
243
254
  createTableColumnResizeState,
244
255
  TableCollection,
245
256
  createTableCollection,
246
257
  type TableState,
247
258
  type TableStateOptions,
248
259
  type ITableCollection,
260
+ type TreeGridState,
261
+ type TreeGridStateOptions,
249
262
  type SortDescriptor,
250
263
  type SortDirection,
251
264
  type Sortable,
@@ -324,6 +337,7 @@ export {
324
337
  type DroppableCollectionRootDropEvent,
325
338
  type DroppableCollectionOnItemDropEvent,
326
339
  type DroppableCollectionReorderEvent,
340
+ type DragType,
327
341
  type DragTypes,
328
342
  type DropTargetDelegate,
329
343
  type DraggableCollectionStartEvent,
@@ -9,6 +9,7 @@ import { createListState } from "../collections/createListState";
9
9
  import { createOverlayTriggerState } from "../overlays";
10
10
  import type { Key, CollectionNode, Collection } from "../collections/types";
11
11
  import type { SelectionMode, Selection } from "../collections/types";
12
+ import type { SelectionManager } from "../selection/SelectionManager";
12
13
 
13
14
  export interface SelectStateProps<T = unknown> {
14
15
  /** The items to display in the select. */
@@ -50,6 +51,11 @@ export interface SelectStateProps<T = unknown> {
50
51
  export interface SelectState<T = unknown> {
51
52
  /** The collection of items. */
52
53
  readonly collection: Accessor<Collection<T>>;
54
+ /**
55
+ * The collection-aware selection manager. Mirrors upstream `useSelectState`,
56
+ * which spreads the underlying `useListState` (including its selection manager).
57
+ */
58
+ readonly selectionManager: SelectionManager<T>;
53
59
  /** Whether the select dropdown is open. */
54
60
  readonly isOpen: Accessor<boolean>;
55
61
  /** Open the select dropdown. */
@@ -234,6 +240,12 @@ export function createSelectState<T = unknown>(
234
240
  },
235
241
  });
236
242
 
243
+ // The select's own focus state (the trigger), separate from the collection's
244
+ // focus-within state on the selection manager — upstream useSelectState keeps
245
+ // these apart with a dedicated useState. Sharing the manager's signal makes
246
+ // trigger focus re-arm the item roving-focus effect and steal focus back.
247
+ const [isFocused, setFocused] = createSignal(false);
248
+
237
249
  // Get the selected item from the collection (memoized)
238
250
  const selectedItem: Accessor<CollectionNode<T> | null> = createMemo(() => {
239
251
  const key = selectedKey();
@@ -258,12 +270,13 @@ export function createSelectState<T = unknown>(
258
270
  return {
259
271
  // Collection
260
272
  collection: listState.collection,
273
+ selectionManager: listState.selectionManager,
261
274
 
262
275
  // Focus management
263
276
  focusedKey: listState.focusedKey,
264
277
  setFocusedKey: listState.setFocusedKey,
265
- isFocused: listState.isFocused,
266
- setFocused: listState.setFocused,
278
+ isFocused,
279
+ setFocused,
267
280
 
268
281
  // Overlay state
269
282
  isOpen: overlayState.isOpen,
@@ -0,0 +1,28 @@
1
+ /**
2
+ * A Selection is a special Set containing Keys, which also has an anchor
3
+ * and current selected key for use when range selecting.
4
+ *
5
+ * Based on @react-stately/selection.
6
+ */
7
+
8
+ import type { Key } from "../collections/types";
9
+
10
+ /**
11
+ * A Selection is a special Set containing Keys, which also has an anchor
12
+ * and current selected key for use when range selecting.
13
+ */
14
+ export class Selection extends Set<Key> {
15
+ anchorKey: Key | null;
16
+ currentKey: Key | null;
17
+
18
+ constructor(keys?: Iterable<Key> | Selection, anchorKey?: Key | null, currentKey?: Key | null) {
19
+ super(keys);
20
+ if (keys instanceof Selection) {
21
+ this.anchorKey = anchorKey ?? keys.anchorKey;
22
+ this.currentKey = currentKey ?? keys.currentKey;
23
+ } else {
24
+ this.anchorKey = anchorKey ?? null;
25
+ this.currentKey = currentKey ?? null;
26
+ }
27
+ }
28
+ }