@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.
- package/dist/calendar/IncompleteDate.d.ts +61 -0
- package/dist/calendar/createCalendarState.d.ts +20 -10
- package/dist/calendar/createDateFieldState.d.ts +65 -20
- package/dist/calendar/createRangeCalendarState.d.ts +20 -2
- package/dist/calendar/createTimeFieldState.d.ts +38 -67
- package/dist/calendar/index.d.ts +1 -1
- package/dist/calendar/intl/index.d.ts +10 -0
- package/dist/collections/ListCollection.d.ts +8 -0
- package/dist/collections/createListState.d.ts +16 -1
- package/dist/collections/createSelectionState.d.ts +17 -5
- package/dist/collections/getChildNodes.d.ts +19 -0
- package/dist/collections/index.d.ts +3 -2
- package/dist/collections/types.d.ts +9 -0
- package/dist/color/intl/index.d.ts +14 -0
- package/dist/combobox/createComboBoxState.d.ts +7 -0
- package/dist/dnd/createDroppableCollectionState.d.ts +17 -2
- package/dist/dnd/index.d.ts +1 -1
- package/dist/dnd/types.d.ts +3 -1
- package/dist/grid/types.d.ts +17 -3
- package/dist/index.d.ts +6 -6
- package/dist/index.js +4372 -1087
- package/dist/index.js.map +1 -1
- package/dist/select/createSelectState.d.ts +6 -0
- package/dist/selection/Selection.d.ts +16 -0
- package/dist/selection/SelectionManager.d.ts +119 -0
- package/dist/selection/createMultipleSelectionState.d.ts +59 -0
- package/dist/selection/index.d.ts +1 -0
- package/dist/table/TableCollection.d.ts +30 -0
- package/dist/table/createTreeGridState.d.ts +15 -0
- package/dist/table/index.d.ts +2 -1
- package/dist/table/types.d.ts +58 -0
- package/dist/toast/createToastState.d.ts +16 -0
- package/dist/toast/index.d.ts +1 -1
- package/dist/tree/types.d.ts +14 -6
- package/package.json +1 -1
- package/src/calendar/IncompleteDate.ts +430 -0
- package/src/calendar/createCalendarState.ts +129 -40
- package/src/calendar/createDateFieldState.ts +633 -477
- package/src/calendar/createRangeCalendarState.ts +343 -30
- package/src/calendar/createTimeFieldState.ts +179 -467
- package/src/calendar/index.ts +2 -0
- package/src/calendar/intl/index.ts +364 -0
- package/src/collections/ListCollection.ts +31 -0
- package/src/collections/createListState.ts +139 -17
- package/src/collections/createMenuState.ts +3 -0
- package/src/collections/createSelectionState.ts +38 -28
- package/src/collections/getChildNodes.ts +129 -0
- package/src/collections/index.ts +12 -0
- package/src/collections/types.ts +10 -0
- package/src/color/Color.ts +104 -133
- package/src/color/intl/index.ts +1409 -0
- package/src/combobox/createComboBoxState.ts +38 -13
- package/src/dnd/createDroppableCollectionState.ts +125 -6
- package/src/dnd/index.d.ts +1 -0
- package/src/dnd/index.ts +1 -0
- package/src/dnd/types.d.ts +3 -1
- package/src/dnd/types.ts +4 -1
- package/src/grid/createGridState.ts +36 -2
- package/src/grid/types.ts +17 -3
- package/src/index.d.ts +1 -0
- package/src/index.ts +14 -0
- package/src/select/createSelectState.ts +15 -2
- package/src/selection/Selection.ts +28 -0
- package/src/selection/SelectionManager.ts +541 -0
- package/src/selection/createMultipleSelectionState.ts +247 -0
- package/src/selection/index.ts +6 -0
- package/src/steplist/createStepListState.ts +27 -4
- package/src/table/TableCollection.ts +213 -43
- package/src/table/createTableState.ts +20 -0
- package/src/table/createTreeGridState.ts +146 -0
- package/src/table/index.ts +3 -0
- package/src/table/types.ts +66 -0
- package/src/tabs/createTabListState.ts +11 -9
- package/src/toast/createToastState.ts +43 -10
- package/src/toast/index.ts +1 -0
- package/src/tree/createTreeState.ts +37 -6
- 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
|
-
|
|
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
|
-
|
|
432
|
+
const willOpen = !overlayState.isOpen();
|
|
433
|
+
|
|
434
|
+
if (displayAll && willOpen) {
|
|
414
435
|
setShowAllItems(true);
|
|
415
436
|
}
|
|
416
437
|
|
|
417
|
-
if (
|
|
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
|
-
/**
|
|
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
|
-
|
|
291
|
-
|
|
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
|
-
|
|
302
|
-
|
|
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
|
|
424
|
+
return defaultGetDropOperation(dropTarget, types, allowedOperations, isInternal, draggingKeys);
|
|
306
425
|
};
|
|
307
426
|
|
|
308
427
|
const shouldAcceptItemDrop = (dropTarget: ItemDropTarget, types: DragTypes): boolean => {
|
package/src/dnd/index.d.ts
CHANGED
package/src/dnd/index.ts
CHANGED
package/src/dnd/types.d.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
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
|
|
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:
|
|
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?:
|
|
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
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
|
|
266
|
-
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
|
+
}
|