@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
|
@@ -33,6 +33,7 @@ export function createTableState<
|
|
|
33
33
|
const gridState = createGridState<T, C>(() => ({
|
|
34
34
|
collection: getOptions().collection,
|
|
35
35
|
disabledKeys: getOptions().disabledKeys,
|
|
36
|
+
disabledBehavior: getOptions().disabledBehavior,
|
|
36
37
|
focusMode: getOptions().focusMode,
|
|
37
38
|
selectionMode: getOptions().selectionMode,
|
|
38
39
|
selectionBehavior: getOptions().selectionBehavior,
|
|
@@ -83,6 +84,9 @@ export function createTableState<
|
|
|
83
84
|
get disabledKeys() {
|
|
84
85
|
return gridState.disabledKeys;
|
|
85
86
|
},
|
|
87
|
+
get disabledBehavior() {
|
|
88
|
+
return gridState.disabledBehavior;
|
|
89
|
+
},
|
|
86
90
|
get isKeyboardNavigationDisabled() {
|
|
87
91
|
return gridState.isKeyboardNavigationDisabled;
|
|
88
92
|
},
|
|
@@ -101,6 +105,9 @@ export function createTableState<
|
|
|
101
105
|
get selectionBehavior() {
|
|
102
106
|
return gridState.selectionBehavior;
|
|
103
107
|
},
|
|
108
|
+
get disallowEmptySelection() {
|
|
109
|
+
return gridState.disallowEmptySelection;
|
|
110
|
+
},
|
|
104
111
|
get selectedKeys() {
|
|
105
112
|
return gridState.selectedKeys;
|
|
106
113
|
},
|
|
@@ -112,6 +119,8 @@ export function createTableState<
|
|
|
112
119
|
setFocused: gridState.setFocused,
|
|
113
120
|
toggleSelection: gridState.toggleSelection,
|
|
114
121
|
replaceSelection: gridState.replaceSelection,
|
|
122
|
+
setSelectionBehavior: gridState.setSelectionBehavior,
|
|
123
|
+
setSelectedKeys: gridState.setSelectedKeys,
|
|
115
124
|
extendSelection: gridState.extendSelection,
|
|
116
125
|
selectAll: gridState.selectAll,
|
|
117
126
|
clearSelection: gridState.clearSelection,
|
|
@@ -126,5 +135,16 @@ export function createTableState<
|
|
|
126
135
|
return sortDescriptor();
|
|
127
136
|
},
|
|
128
137
|
sort,
|
|
138
|
+
|
|
139
|
+
// A plain table has no expandable rows.
|
|
140
|
+
get expandedKeys(): "all" | Set<Key> {
|
|
141
|
+
return EMPTY_EXPANDED_KEYS;
|
|
142
|
+
},
|
|
143
|
+
get treeColumn(): Key | null {
|
|
144
|
+
return getOptions().collection.treeColumn ?? null;
|
|
145
|
+
},
|
|
146
|
+
toggleKey: () => {},
|
|
129
147
|
};
|
|
130
148
|
}
|
|
149
|
+
|
|
150
|
+
const EMPTY_EXPANDED_KEYS: Set<Key> = new Set();
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree grid state management for expandable Table rows.
|
|
3
|
+
* Based on @react-stately/table/useTreeGridState (the `UNSTABLE_` tree-grid feature).
|
|
4
|
+
*
|
|
5
|
+
* Owns the expanded-keys signal (controlled or uncontrolled), rebuilds the table
|
|
6
|
+
* collection from it, and layers the expansion API on top of {@link createTableState}.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { createMemo, createSignal, type Accessor } from "solid-js";
|
|
10
|
+
import { createTableState } from "./createTableState";
|
|
11
|
+
import { createTableCollection, TableCollection } from "./TableCollection";
|
|
12
|
+
import type { Key } from "../collections/types";
|
|
13
|
+
import type { TreeGridState, TreeGridStateOptions } from "./types";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates state management for a tree grid (a table with expandable rows). Handles the
|
|
17
|
+
* expanded rows in addition to the selection, sort and focus handled by the table state.
|
|
18
|
+
*/
|
|
19
|
+
export function createTreeGridState<
|
|
20
|
+
T extends object,
|
|
21
|
+
C extends TableCollection<T> = TableCollection<T>,
|
|
22
|
+
>(options: Accessor<TreeGridStateOptions<T, C>>): TreeGridState<T, C> {
|
|
23
|
+
const getOptions = () => options();
|
|
24
|
+
|
|
25
|
+
// Expanded keys: controlled via `UNSTABLE_expandedKeys`, otherwise the internal signal
|
|
26
|
+
// seeded from `UNSTABLE_defaultExpandedKeys`.
|
|
27
|
+
const [internalExpandedKeys, setInternalExpandedKeys] = createSignal<"all" | Set<Key>>(
|
|
28
|
+
getInitialExpanded(getOptions().UNSTABLE_defaultExpandedKeys),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const expandedKeys = createMemo<"all" | Set<Key>>(() => {
|
|
32
|
+
const controlled = getOptions().UNSTABLE_expandedKeys;
|
|
33
|
+
if (controlled !== undefined) {
|
|
34
|
+
return convertExpanded(controlled);
|
|
35
|
+
}
|
|
36
|
+
return internalExpandedKeys();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Rebuild the tree collection whenever the expanded keys (or the data) change.
|
|
40
|
+
const collection = createMemo(
|
|
41
|
+
() =>
|
|
42
|
+
createTableCollection<T>({
|
|
43
|
+
columns: getOptions().columns,
|
|
44
|
+
rows: getOptions().rows,
|
|
45
|
+
getKey: getOptions().getKey,
|
|
46
|
+
getTextValue: getOptions().getTextValue,
|
|
47
|
+
showSelectionCheckboxes: getOptions().showSelectionCheckboxes ?? false,
|
|
48
|
+
rowHeaderColumnKeys: getOptions().rowHeaderColumnKeys,
|
|
49
|
+
expandedKeys: expandedKeys(),
|
|
50
|
+
}) as unknown as C,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const tableState = createTableState<T, C>(() => ({
|
|
54
|
+
collection: collection(),
|
|
55
|
+
disabledKeys: getOptions().disabledKeys,
|
|
56
|
+
focusMode: getOptions().focusMode,
|
|
57
|
+
selectionMode: getOptions().selectionMode,
|
|
58
|
+
selectionBehavior: getOptions().selectionBehavior,
|
|
59
|
+
disallowEmptySelection: getOptions().disallowEmptySelection,
|
|
60
|
+
selectedKeys: getOptions().selectedKeys,
|
|
61
|
+
defaultSelectedKeys: getOptions().defaultSelectedKeys,
|
|
62
|
+
onSelectionChange: getOptions().onSelectionChange,
|
|
63
|
+
sortDescriptor: getOptions().sortDescriptor,
|
|
64
|
+
onSortChange: getOptions().onSortChange,
|
|
65
|
+
showSelectionCheckboxes: getOptions().showSelectionCheckboxes,
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
const toggleKey = (key: Key) => {
|
|
69
|
+
const updated = toggleExpandedKey(expandedKeys(), key, collection());
|
|
70
|
+
|
|
71
|
+
// Only own the value when uncontrolled.
|
|
72
|
+
if (getOptions().UNSTABLE_expandedKeys === undefined) {
|
|
73
|
+
setInternalExpandedKeys(updated);
|
|
74
|
+
}
|
|
75
|
+
getOptions().UNSTABLE_onExpandedChange?.(updated);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Layer the expansion API onto the table state object, preserving the reactive getters
|
|
79
|
+
// already defined on it (spreading would eagerly evaluate them and break reactivity).
|
|
80
|
+
const treeGridState = tableState as TreeGridState<T, C>;
|
|
81
|
+
Object.defineProperty(treeGridState, "expandedKeys", {
|
|
82
|
+
get: () => expandedKeys(),
|
|
83
|
+
configurable: true,
|
|
84
|
+
enumerable: true,
|
|
85
|
+
});
|
|
86
|
+
Object.defineProperty(treeGridState, "treeColumn", {
|
|
87
|
+
get: () => collection().treeColumn ?? null,
|
|
88
|
+
configurable: true,
|
|
89
|
+
enumerable: true,
|
|
90
|
+
});
|
|
91
|
+
Object.defineProperty(treeGridState, "keyMap", {
|
|
92
|
+
get: () => collection().keyMap,
|
|
93
|
+
configurable: true,
|
|
94
|
+
enumerable: true,
|
|
95
|
+
});
|
|
96
|
+
Object.defineProperty(treeGridState, "userColumnCount", {
|
|
97
|
+
get: () => collection().userColumnCount,
|
|
98
|
+
configurable: true,
|
|
99
|
+
enumerable: true,
|
|
100
|
+
});
|
|
101
|
+
treeGridState.toggleKey = toggleKey;
|
|
102
|
+
|
|
103
|
+
return treeGridState;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Computes the next expanded-key set when a row is toggled. Mirrors the upstream
|
|
108
|
+
* `toggleKey`: collapsing from `'all'` materialises every expandable row first.
|
|
109
|
+
*/
|
|
110
|
+
function toggleExpandedKey<T>(
|
|
111
|
+
current: "all" | Set<Key>,
|
|
112
|
+
key: Key,
|
|
113
|
+
collection: TableCollection<T>,
|
|
114
|
+
): Set<Key> {
|
|
115
|
+
let updated: Set<Key>;
|
|
116
|
+
if (current === "all") {
|
|
117
|
+
updated = new Set(
|
|
118
|
+
collection.rows
|
|
119
|
+
.filter((row) => row.type === "item" && row.isExpandable)
|
|
120
|
+
.map((row) => row.key),
|
|
121
|
+
);
|
|
122
|
+
updated.delete(key);
|
|
123
|
+
} else {
|
|
124
|
+
updated = new Set(current);
|
|
125
|
+
if (updated.has(key)) {
|
|
126
|
+
updated.delete(key);
|
|
127
|
+
} else {
|
|
128
|
+
updated.add(key);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return updated;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function getInitialExpanded(defaultExpanded?: "all" | Iterable<Key>): "all" | Set<Key> {
|
|
135
|
+
if (defaultExpanded === undefined) {
|
|
136
|
+
return new Set();
|
|
137
|
+
}
|
|
138
|
+
return convertExpanded(defaultExpanded);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function convertExpanded(expanded: "all" | Iterable<Key>): "all" | Set<Key> {
|
|
142
|
+
if (!expanded) {
|
|
143
|
+
return new Set<Key>();
|
|
144
|
+
}
|
|
145
|
+
return expanded === "all" ? "all" : new Set(expanded);
|
|
146
|
+
}
|
package/src/table/index.ts
CHANGED
|
@@ -4,12 +4,15 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export { createTableState } from "./createTableState";
|
|
7
|
+
export { createTreeGridState } from "./createTreeGridState";
|
|
7
8
|
export { createTableColumnResizeState } from "./createTableColumnResizeState";
|
|
8
9
|
export { TableCollection, createTableCollection } from "./TableCollection";
|
|
9
10
|
export type {
|
|
10
11
|
TableState,
|
|
11
12
|
TableStateOptions,
|
|
12
13
|
TableCollection as ITableCollection,
|
|
14
|
+
TreeGridState,
|
|
15
|
+
TreeGridStateOptions,
|
|
13
16
|
SortDescriptor,
|
|
14
17
|
SortDirection,
|
|
15
18
|
Sortable,
|
package/src/table/types.ts
CHANGED
|
@@ -46,6 +46,8 @@ export interface TableCollection<T = unknown> extends GridCollection<T> {
|
|
|
46
46
|
readonly head?: GridNode<T>;
|
|
47
47
|
/** The node that makes up the body of the table. */
|
|
48
48
|
readonly body: GridNode<T>;
|
|
49
|
+
/** The key of the column that renders the tree-grid expand/collapse affordance, if any. */
|
|
50
|
+
readonly treeColumn?: Key | null;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
/**
|
|
@@ -64,6 +66,59 @@ export interface TableState<T, C extends TableCollection<T> = TableCollection<T>
|
|
|
64
66
|
readonly sortDescriptor: SortDescriptor | null;
|
|
65
67
|
/** Sort by the given column and direction. */
|
|
66
68
|
sort(columnKey: Key, direction?: SortDirection): void;
|
|
69
|
+
/**
|
|
70
|
+
* The set of expanded row keys for tree grids. A plain table reports an empty set.
|
|
71
|
+
*/
|
|
72
|
+
readonly expandedKeys: "all" | Set<Key>;
|
|
73
|
+
/**
|
|
74
|
+
* The key of the column that renders the expand/collapse affordance, or `null` for a
|
|
75
|
+
* plain (non-tree) table.
|
|
76
|
+
*/
|
|
77
|
+
readonly treeColumn: Key | null;
|
|
78
|
+
/** Toggles the expanded state of a row by its key (no-op for a plain table). */
|
|
79
|
+
toggleKey(key: Key): void;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* State for a tree grid (expandable rows). Mirrors `@react-stately/table`'s
|
|
84
|
+
* `UNSTABLE_useTreeGridState` return value.
|
|
85
|
+
*/
|
|
86
|
+
export interface TreeGridState<
|
|
87
|
+
T,
|
|
88
|
+
C extends TableCollection<T> = TableCollection<T>,
|
|
89
|
+
> extends TableState<T, C> {
|
|
90
|
+
/** A set of keys for rows that are expanded. */
|
|
91
|
+
readonly expandedKeys: "all" | Set<Key>;
|
|
92
|
+
/** The key map containing nodes representing the collection's tree grid structure. */
|
|
93
|
+
readonly keyMap: Map<Key, GridNode<T>>;
|
|
94
|
+
/** The number of leaf columns provided by the user (excludes selection/drag columns). */
|
|
95
|
+
readonly userColumnCount: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Options for creating tree grid state. Mirrors `TreeGridStateProps` upstream; the
|
|
100
|
+
* expansion props keep the `UNSTABLE_` prefix while the feature is unstable.
|
|
101
|
+
*/
|
|
102
|
+
export interface TreeGridStateOptions<
|
|
103
|
+
T,
|
|
104
|
+
C extends TableCollection<T> = TableCollection<T>,
|
|
105
|
+
> extends Omit<TableStateOptions<T, C>, "collection"> {
|
|
106
|
+
/** Column definitions. */
|
|
107
|
+
columns: ColumnDefinition<T>[];
|
|
108
|
+
/** Row definitions (may contain `childRows` for nested rows). */
|
|
109
|
+
rows: RowDefinition<T>[];
|
|
110
|
+
/** Function to get the key from a data item. */
|
|
111
|
+
getKey?: (item: T) => Key;
|
|
112
|
+
/** Function to get the text value from a data item. */
|
|
113
|
+
getTextValue?: (item: T, column: ColumnDefinition<T>) => string;
|
|
114
|
+
/** Set of column keys that serve as row headers. */
|
|
115
|
+
rowHeaderColumnKeys?: Set<Key>;
|
|
116
|
+
/** The currently expanded keys in the collection (controlled). */
|
|
117
|
+
UNSTABLE_expandedKeys?: "all" | Iterable<Key>;
|
|
118
|
+
/** The initial expanded keys in the collection (uncontrolled). */
|
|
119
|
+
UNSTABLE_defaultExpandedKeys?: "all" | Iterable<Key>;
|
|
120
|
+
/** Handler that is called when rows are expanded or collapsed. */
|
|
121
|
+
UNSTABLE_onExpandedChange?: (keys: Set<Key>) => void;
|
|
67
122
|
}
|
|
68
123
|
|
|
69
124
|
/**
|
|
@@ -77,6 +132,8 @@ export interface TableStateOptions<
|
|
|
77
132
|
collection: C;
|
|
78
133
|
/** Keys of disabled rows. */
|
|
79
134
|
disabledKeys?: Iterable<Key>;
|
|
135
|
+
/** Whether `disabledKeys` applies to all interactions, or only selection. @default "all" */
|
|
136
|
+
disabledBehavior?: "selection" | "all";
|
|
80
137
|
/** Focus mode: 'row' or 'cell'. */
|
|
81
138
|
focusMode?: "row" | "cell";
|
|
82
139
|
/** Selection mode. */
|
|
@@ -151,4 +208,13 @@ export interface TableCollectionOptions<T = unknown> {
|
|
|
151
208
|
showSelectionCheckboxes?: boolean;
|
|
152
209
|
/** Set of column keys that serve as row headers. */
|
|
153
210
|
rowHeaderColumnKeys?: Set<Key>;
|
|
211
|
+
/**
|
|
212
|
+
* The set of expanded row keys. Passing this (even an empty set) builds the collection
|
|
213
|
+
* in tree-grid mode: `childRows` are walked recursively, only rows whose ancestors are
|
|
214
|
+
* all expanded are flattened into the navigable body, and {@link TableCollection.treeColumn}
|
|
215
|
+
* resolves to a real column. Omit it for a plain (non-tree) table.
|
|
216
|
+
*/
|
|
217
|
+
expandedKeys?: "all" | Set<Key>;
|
|
218
|
+
/** Explicit tree column key. Defaults to the first row-header column in tree-grid mode. */
|
|
219
|
+
treeColumn?: Key | null;
|
|
154
220
|
}
|
|
@@ -199,17 +199,19 @@ export function createTabListState<T = unknown>(
|
|
|
199
199
|
const setFocusedKey = (key: Key | null, childStrategy?: FocusStrategy) => {
|
|
200
200
|
setFocusedKeyInternal(key);
|
|
201
201
|
setChildFocusStrategy(childStrategy ?? null);
|
|
202
|
+
};
|
|
202
203
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
) {
|
|
210
|
-
|
|
204
|
+
// If the tab list doesn't have focus and the selected key changes, or if there
|
|
205
|
+
// isn't a focused key yet, move the focused key to the selected key so the
|
|
206
|
+
// roving tabIndex stays on the selected tab (mirrors useTabListState's effect).
|
|
207
|
+
let lastSelectedKey: Key | null = selectedKey();
|
|
208
|
+
createComputed(() => {
|
|
209
|
+
const sel = selectedKey();
|
|
210
|
+
if ((sel !== null && focusedKey() === null) || (!isFocused() && sel !== lastSelectedKey)) {
|
|
211
|
+
setFocusedKey(sel);
|
|
211
212
|
}
|
|
212
|
-
|
|
213
|
+
lastSelectedKey = sel;
|
|
214
|
+
});
|
|
213
215
|
|
|
214
216
|
// Keep uncontrolled selection valid as items/disabled keys change.
|
|
215
217
|
createComputed(() => {
|
|
@@ -31,11 +31,20 @@ export interface QueuedToast<T> {
|
|
|
31
31
|
timeout?: number;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/** The kind of queue mutation that triggered a notification, passed to `wrapUpdate`. */
|
|
35
|
+
export type ToastAction = "add" | "remove" | "clear";
|
|
36
|
+
|
|
34
37
|
export interface ToastQueueOptions {
|
|
35
38
|
/** The maximum number of toasts to display at once. */
|
|
36
39
|
maxVisibleToasts?: number;
|
|
37
40
|
/** Whether toasts should stack (true) or queue (false). */
|
|
38
41
|
hasExitAnimation?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Wraps each update to the visible toasts (e.g. in `document.startViewTransition()`),
|
|
44
|
+
* so the resulting re-render can be animated. Receives the action that triggered the
|
|
45
|
+
* update. Mirrors `@react-stately/toast`'s `wrapUpdate`.
|
|
46
|
+
*/
|
|
47
|
+
wrapUpdate?: (fn: () => void, action: ToastAction) => void;
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
export interface ToastStateProps<T> {
|
|
@@ -125,10 +134,29 @@ export class ToastQueue<T> {
|
|
|
125
134
|
private maxVisibleToasts: number;
|
|
126
135
|
private hasExitAnimation: boolean;
|
|
127
136
|
private keyCounter = 0;
|
|
137
|
+
private wrapUpdate?: (fn: () => void, action: ToastAction) => void;
|
|
128
138
|
|
|
129
139
|
constructor(options: ToastQueueOptions = {}) {
|
|
130
140
|
this.maxVisibleToasts = options.maxVisibleToasts ?? 5;
|
|
131
141
|
this.hasExitAnimation = options.hasExitAnimation ?? false;
|
|
142
|
+
this.wrapUpdate = options.wrapUpdate;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Installs (or clears) the function that wraps visible-toast updates, so the
|
|
147
|
+
* resulting re-render can be animated. Used to attach `document.startViewTransition`
|
|
148
|
+
* to a shared queue (e.g. the global queue) after construction.
|
|
149
|
+
*/
|
|
150
|
+
setWrapUpdate(wrapUpdate?: (fn: () => void, action: ToastAction) => void): void {
|
|
151
|
+
this.wrapUpdate = wrapUpdate;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private runWithWrapUpdate(fn: () => void, action: ToastAction): void {
|
|
155
|
+
if (this.wrapUpdate) {
|
|
156
|
+
this.wrapUpdate(fn, action);
|
|
157
|
+
} else {
|
|
158
|
+
fn();
|
|
159
|
+
}
|
|
132
160
|
}
|
|
133
161
|
|
|
134
162
|
/**
|
|
@@ -173,7 +201,7 @@ export class ToastQueue<T> {
|
|
|
173
201
|
this.queue = [...this.queue.slice(0, low), toast, ...this.queue.slice(low)];
|
|
174
202
|
|
|
175
203
|
// Start timer for visible toasts
|
|
176
|
-
this.updateVisibility();
|
|
204
|
+
this.updateVisibility("add");
|
|
177
205
|
|
|
178
206
|
return key;
|
|
179
207
|
}
|
|
@@ -193,7 +221,7 @@ export class ToastQueue<T> {
|
|
|
193
221
|
this.queue = this.queue.map((t) =>
|
|
194
222
|
t.key === key ? { ...t, timer: null, animation: "exiting" } : t,
|
|
195
223
|
);
|
|
196
|
-
this.notify();
|
|
224
|
+
this.notify("remove");
|
|
197
225
|
} else {
|
|
198
226
|
// Remove immediately
|
|
199
227
|
this.remove(key);
|
|
@@ -211,7 +239,7 @@ export class ToastQueue<T> {
|
|
|
211
239
|
}
|
|
212
240
|
|
|
213
241
|
this.queue = this.queue.filter((t) => t.key !== key);
|
|
214
|
-
this.updateVisibility();
|
|
242
|
+
this.updateVisibility("remove");
|
|
215
243
|
}
|
|
216
244
|
|
|
217
245
|
/**
|
|
@@ -223,7 +251,7 @@ export class ToastQueue<T> {
|
|
|
223
251
|
}
|
|
224
252
|
|
|
225
253
|
this.queue = [];
|
|
226
|
-
this.notify();
|
|
254
|
+
this.notify("clear");
|
|
227
255
|
}
|
|
228
256
|
|
|
229
257
|
/**
|
|
@@ -244,7 +272,7 @@ export class ToastQueue<T> {
|
|
|
244
272
|
}
|
|
245
273
|
}
|
|
246
274
|
|
|
247
|
-
private updateVisibility(): void {
|
|
275
|
+
private updateVisibility(action: ToastAction): void {
|
|
248
276
|
// Mark toasts as visible or queued based on maxVisibleToasts
|
|
249
277
|
const visibleCount = this.queue.filter(
|
|
250
278
|
(t) => t.animation !== "queued" && t.animation !== "exiting",
|
|
@@ -277,14 +305,19 @@ export class ToastQueue<T> {
|
|
|
277
305
|
return nextToast;
|
|
278
306
|
});
|
|
279
307
|
|
|
280
|
-
this.notify();
|
|
308
|
+
this.notify(action);
|
|
281
309
|
}
|
|
282
310
|
|
|
283
|
-
private notify(): void {
|
|
311
|
+
private notify(action: ToastAction = "add"): void {
|
|
284
312
|
const toasts = [...this.queue];
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
313
|
+
// The new visible toasts are computed synchronously above; only the subscriber
|
|
314
|
+
// fan-out (which drives the re-render) is wrapped, so it can run inside a view
|
|
315
|
+
// transition. Mirrors @react-stately/toast's updateVisibleToasts.
|
|
316
|
+
this.runWithWrapUpdate(() => {
|
|
317
|
+
for (const callback of this.subscriptions) {
|
|
318
|
+
callback(toasts);
|
|
319
|
+
}
|
|
320
|
+
}, action);
|
|
288
321
|
}
|
|
289
322
|
}
|
|
290
323
|
|
package/src/toast/index.ts
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { createSignal, createEffect, createMemo, on, type Accessor } from "solid-js";
|
|
9
|
-
import type { TreeState, TreeStateOptions, TreeCollection
|
|
10
|
-
import type { Key, FocusStrategy } from "../collections/types";
|
|
9
|
+
import type { TreeState, TreeStateOptions, TreeCollection } from "./types";
|
|
10
|
+
import type { Key, FocusStrategy, Selection, SelectionBehavior } from "../collections/types";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Creates state management for a tree component.
|
|
@@ -66,10 +66,27 @@ export function createTreeState<T extends object, C extends TreeCollection<T> =
|
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
const selectionMode = createMemo(() => getOptions().selectionMode ?? "none");
|
|
69
|
-
const
|
|
69
|
+
const selectionBehaviorProp = (): SelectionBehavior => getOptions().selectionBehavior ?? "toggle";
|
|
70
|
+
const [selectionBehaviorState, setSelectionBehaviorState] =
|
|
71
|
+
createSignal<SelectionBehavior>(selectionBehaviorProp());
|
|
72
|
+
const selectionBehavior = createMemo(() => selectionBehaviorState());
|
|
70
73
|
const disallowEmptySelection = createMemo(() => getOptions().disallowEmptySelection ?? false);
|
|
71
74
|
const disabledBehavior = createMemo(() => getOptions().disabledBehavior ?? "all");
|
|
72
75
|
|
|
76
|
+
createEffect(on(selectionBehaviorProp, (behavior) => setSelectionBehaviorState(behavior)));
|
|
77
|
+
|
|
78
|
+
createEffect(() => {
|
|
79
|
+
const keys = selectedKeys();
|
|
80
|
+
if (
|
|
81
|
+
selectionBehaviorProp() === "replace" &&
|
|
82
|
+
selectionBehaviorState() === "toggle" &&
|
|
83
|
+
keys !== "all" &&
|
|
84
|
+
keys.size === 0
|
|
85
|
+
) {
|
|
86
|
+
setSelectionBehaviorState("replace");
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
73
90
|
// Set focused key
|
|
74
91
|
const setFocusedKey = (key: Key | null, strategy: FocusStrategy = "first") => {
|
|
75
92
|
setFocusedKeyInternal(key);
|
|
@@ -169,7 +186,9 @@ export function createTreeState<T extends object, C extends TreeCollection<T> =
|
|
|
169
186
|
};
|
|
170
187
|
|
|
171
188
|
const toggleSelection = (key: Key) => {
|
|
172
|
-
|
|
189
|
+
// Disabled keys are never selectable, regardless of disabledBehavior
|
|
190
|
+
// (mirrors SelectionManager.canSelectItem, which ignores disabledBehavior).
|
|
191
|
+
if (isDisabled(key)) return;
|
|
173
192
|
if (selectionMode() === "none") return;
|
|
174
193
|
|
|
175
194
|
const current = selectedKeys();
|
|
@@ -203,15 +222,19 @@ export function createTreeState<T extends object, C extends TreeCollection<T> =
|
|
|
203
222
|
};
|
|
204
223
|
|
|
205
224
|
const replaceSelection = (key: Key) => {
|
|
206
|
-
if (isDisabled(key)
|
|
225
|
+
if (isDisabled(key)) return;
|
|
207
226
|
if (selectionMode() === "none") return;
|
|
208
227
|
|
|
209
228
|
updateSelection(new Set([key]));
|
|
210
229
|
setAnchorKey(key);
|
|
211
230
|
};
|
|
212
231
|
|
|
232
|
+
const setSelectedKeys = (keys: Selection | Iterable<Key>) => {
|
|
233
|
+
updateSelection(normalizeSelection(keys));
|
|
234
|
+
};
|
|
235
|
+
|
|
213
236
|
const extendSelection = (toKey: Key) => {
|
|
214
|
-
if (isDisabled(toKey)
|
|
237
|
+
if (isDisabled(toKey)) return;
|
|
215
238
|
if (selectionMode() !== "multiple") {
|
|
216
239
|
replaceSelection(toKey);
|
|
217
240
|
return;
|
|
@@ -330,6 +353,9 @@ export function createTreeState<T extends object, C extends TreeCollection<T> =
|
|
|
330
353
|
get disabledKeys() {
|
|
331
354
|
return disabledKeys();
|
|
332
355
|
},
|
|
356
|
+
get disabledBehavior() {
|
|
357
|
+
return disabledBehavior();
|
|
358
|
+
},
|
|
333
359
|
get expandedKeys() {
|
|
334
360
|
return expandedKeys();
|
|
335
361
|
},
|
|
@@ -351,6 +377,9 @@ export function createTreeState<T extends object, C extends TreeCollection<T> =
|
|
|
351
377
|
get selectionBehavior() {
|
|
352
378
|
return selectionBehavior();
|
|
353
379
|
},
|
|
380
|
+
get disallowEmptySelection() {
|
|
381
|
+
return disallowEmptySelection();
|
|
382
|
+
},
|
|
354
383
|
get selectedKeys() {
|
|
355
384
|
return selectedKeys();
|
|
356
385
|
},
|
|
@@ -361,6 +390,8 @@ export function createTreeState<T extends object, C extends TreeCollection<T> =
|
|
|
361
390
|
setFocused: setIsFocused,
|
|
362
391
|
toggleSelection,
|
|
363
392
|
replaceSelection,
|
|
393
|
+
setSelectedKeys,
|
|
394
|
+
setSelectionBehavior: setSelectionBehaviorState,
|
|
364
395
|
extendSelection,
|
|
365
396
|
selectAll,
|
|
366
397
|
clearSelection,
|
package/src/tree/types.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Based on @react-stately/tree.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { Key, FocusStrategy } from "../collections/types";
|
|
6
|
+
import type { Key, FocusStrategy, Selection, SelectionBehavior } from "../collections/types";
|
|
7
7
|
import type { GridNode, GridCollection } from "../grid/types";
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -81,6 +81,8 @@ export interface TreeState<T, C extends TreeCollection<T> = TreeCollection<T>> {
|
|
|
81
81
|
readonly collection: C;
|
|
82
82
|
/** Keys of disabled items. */
|
|
83
83
|
readonly disabledKeys: Set<Key>;
|
|
84
|
+
/** Whether `disabledKeys` applies to all interactions, or only selection. */
|
|
85
|
+
readonly disabledBehavior: "selection" | "all";
|
|
84
86
|
/** Keys of expanded items. */
|
|
85
87
|
readonly expandedKeys: Set<Key>;
|
|
86
88
|
/** Whether keyboard navigation is disabled. */
|
|
@@ -94,9 +96,11 @@ export interface TreeState<T, C extends TreeCollection<T> = TreeCollection<T>> {
|
|
|
94
96
|
/** The selection mode. */
|
|
95
97
|
readonly selectionMode: "none" | "single" | "multiple";
|
|
96
98
|
/** How selection behaves when pressing an item. */
|
|
97
|
-
readonly selectionBehavior:
|
|
99
|
+
readonly selectionBehavior: SelectionBehavior;
|
|
100
|
+
/** Whether empty selection is disallowed. */
|
|
101
|
+
readonly disallowEmptySelection: boolean;
|
|
98
102
|
/** The currently selected keys. */
|
|
99
|
-
readonly selectedKeys:
|
|
103
|
+
readonly selectedKeys: Selection;
|
|
100
104
|
|
|
101
105
|
/** Check if a key is selected. */
|
|
102
106
|
isSelected(key: Key): boolean;
|
|
@@ -112,6 +116,10 @@ export interface TreeState<T, C extends TreeCollection<T> = TreeCollection<T>> {
|
|
|
112
116
|
toggleSelection(key: Key): void;
|
|
113
117
|
/** Replace selection with a key. */
|
|
114
118
|
replaceSelection(key: Key): void;
|
|
119
|
+
/** Set the selected keys directly. */
|
|
120
|
+
setSelectedKeys(keys: Selection | Iterable<Key>): void;
|
|
121
|
+
/** Set how selection behaves when pressing an item. */
|
|
122
|
+
setSelectionBehavior(behavior: SelectionBehavior): void;
|
|
115
123
|
/** Extend selection to a key (shift-click). */
|
|
116
124
|
extendSelection(toKey: Key): void;
|
|
117
125
|
/** Select all visible items. */
|
|
@@ -145,13 +153,13 @@ export interface TreeStateOptions<T, C extends TreeCollection<T> = TreeCollectio
|
|
|
145
153
|
/** Selection mode. */
|
|
146
154
|
selectionMode?: "none" | "single" | "multiple";
|
|
147
155
|
/** Selection behavior. */
|
|
148
|
-
selectionBehavior?:
|
|
156
|
+
selectionBehavior?: SelectionBehavior;
|
|
149
157
|
/** Whether empty selection is disallowed. */
|
|
150
158
|
disallowEmptySelection?: boolean;
|
|
151
159
|
/** Currently selected keys (controlled). */
|
|
152
|
-
selectedKeys?:
|
|
160
|
+
selectedKeys?: Selection | Iterable<Key>;
|
|
153
161
|
/** Default selected keys (uncontrolled). */
|
|
154
|
-
defaultSelectedKeys?:
|
|
162
|
+
defaultSelectedKeys?: Selection | Iterable<Key>;
|
|
155
163
|
/** Handler for selection changes. */
|
|
156
164
|
onSelectionChange?: (keys: "all" | Set<Key>) => void;
|
|
157
165
|
/** Currently expanded keys (controlled). */
|