@react-stately/selection 3.16.2 → 3.18.0
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/Selection.main.js +4 -4
- package/dist/Selection.main.js.map +1 -1
- package/dist/Selection.mjs +4 -4
- package/dist/Selection.module.js +4 -4
- package/dist/Selection.module.js.map +1 -1
- package/dist/SelectionManager.main.js +42 -31
- package/dist/SelectionManager.main.js.map +1 -1
- package/dist/SelectionManager.mjs +42 -31
- package/dist/SelectionManager.module.js +42 -31
- package/dist/SelectionManager.module.js.map +1 -1
- package/dist/types.d.ts +8 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/useMultipleSelectionState.main.js +1 -1
- package/dist/useMultipleSelectionState.main.js.map +1 -1
- package/dist/useMultipleSelectionState.mjs +1 -1
- package/dist/useMultipleSelectionState.module.js +1 -1
- package/dist/useMultipleSelectionState.module.js.map +1 -1
- package/package.json +6 -6
- package/src/Selection.ts +7 -7
- package/src/SelectionManager.ts +53 -39
- package/src/types.ts +3 -3
- package/src/useMultipleSelectionState.ts +7 -7
package/src/SelectionManager.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
FocusStrategy,
|
|
16
16
|
Selection as ISelection,
|
|
17
17
|
Key,
|
|
18
|
+
LayoutDelegate,
|
|
18
19
|
LongPressEvent,
|
|
19
20
|
Node,
|
|
20
21
|
PressEvent,
|
|
@@ -26,23 +27,26 @@ import {MultipleSelectionManager, MultipleSelectionState} from './types';
|
|
|
26
27
|
import {Selection} from './Selection';
|
|
27
28
|
|
|
28
29
|
interface SelectionManagerOptions {
|
|
29
|
-
allowsCellSelection?: boolean
|
|
30
|
+
allowsCellSelection?: boolean,
|
|
31
|
+
layoutDelegate?: LayoutDelegate
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
/**
|
|
33
35
|
* An interface for reading and updating multiple selection state.
|
|
34
36
|
*/
|
|
35
37
|
export class SelectionManager implements MultipleSelectionManager {
|
|
36
|
-
|
|
38
|
+
collection: Collection<Node<unknown>>;
|
|
37
39
|
private state: MultipleSelectionState;
|
|
38
40
|
private allowsCellSelection: boolean;
|
|
39
|
-
private _isSelectAll: boolean;
|
|
41
|
+
private _isSelectAll: boolean | null;
|
|
42
|
+
private layoutDelegate: LayoutDelegate | null;
|
|
40
43
|
|
|
41
44
|
constructor(collection: Collection<Node<unknown>>, state: MultipleSelectionState, options?: SelectionManagerOptions) {
|
|
42
45
|
this.collection = collection;
|
|
43
46
|
this.state = state;
|
|
44
47
|
this.allowsCellSelection = options?.allowsCellSelection ?? false;
|
|
45
48
|
this._isSelectAll = null;
|
|
49
|
+
this.layoutDelegate = options?.layoutDelegate || null;
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
/**
|
|
@@ -90,12 +94,12 @@ export class SelectionManager implements MultipleSelectionManager {
|
|
|
90
94
|
/**
|
|
91
95
|
* The current focused key in the collection.
|
|
92
96
|
*/
|
|
93
|
-
get focusedKey(): Key {
|
|
97
|
+
get focusedKey(): Key | null {
|
|
94
98
|
return this.state.focusedKey;
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
/** Whether the first or last child of the focused key should receive focus. */
|
|
98
|
-
get childFocusStrategy(): FocusStrategy {
|
|
102
|
+
get childFocusStrategy(): FocusStrategy | null {
|
|
99
103
|
return this.state.childFocusStrategy;
|
|
100
104
|
}
|
|
101
105
|
|
|
@@ -133,10 +137,13 @@ export class SelectionManager implements MultipleSelectionManager {
|
|
|
133
137
|
return false;
|
|
134
138
|
}
|
|
135
139
|
|
|
136
|
-
|
|
140
|
+
let mappedKey = this.getKey(key);
|
|
141
|
+
if (mappedKey == null) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
137
144
|
return this.state.selectedKeys === 'all'
|
|
138
|
-
? this.canSelectItem(
|
|
139
|
-
: this.state.selectedKeys.has(
|
|
145
|
+
? this.canSelectItem(mappedKey)
|
|
146
|
+
: this.state.selectedKeys.has(mappedKey);
|
|
140
147
|
}
|
|
141
148
|
|
|
142
149
|
/**
|
|
@@ -177,7 +184,7 @@ export class SelectionManager implements MultipleSelectionManager {
|
|
|
177
184
|
}
|
|
178
185
|
}
|
|
179
186
|
|
|
180
|
-
return first?.key;
|
|
187
|
+
return first?.key ?? null;
|
|
181
188
|
}
|
|
182
189
|
|
|
183
190
|
get lastSelectedKey(): Key | null {
|
|
@@ -189,7 +196,7 @@ export class SelectionManager implements MultipleSelectionManager {
|
|
|
189
196
|
}
|
|
190
197
|
}
|
|
191
198
|
|
|
192
|
-
return last?.key;
|
|
199
|
+
return last?.key ?? null;
|
|
193
200
|
}
|
|
194
201
|
|
|
195
202
|
get disabledKeys(): Set<Key> {
|
|
@@ -213,22 +220,25 @@ export class SelectionManager implements MultipleSelectionManager {
|
|
|
213
220
|
return;
|
|
214
221
|
}
|
|
215
222
|
|
|
216
|
-
|
|
223
|
+
let mappedToKey = this.getKey(toKey);
|
|
224
|
+
if (mappedToKey == null) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
217
227
|
|
|
218
228
|
let selection: Selection;
|
|
219
229
|
|
|
220
230
|
// Only select the one key if coming from a select all.
|
|
221
231
|
if (this.state.selectedKeys === 'all') {
|
|
222
|
-
selection = new Selection([
|
|
232
|
+
selection = new Selection([mappedToKey], mappedToKey, mappedToKey);
|
|
223
233
|
} else {
|
|
224
234
|
let selectedKeys = this.state.selectedKeys as Selection;
|
|
225
|
-
let anchorKey = selectedKeys.anchorKey
|
|
226
|
-
selection = new Selection(selectedKeys, anchorKey,
|
|
227
|
-
for (let key of this.getKeyRange(anchorKey, selectedKeys.currentKey
|
|
235
|
+
let anchorKey = selectedKeys.anchorKey ?? mappedToKey;
|
|
236
|
+
selection = new Selection(selectedKeys, anchorKey, mappedToKey);
|
|
237
|
+
for (let key of this.getKeyRange(anchorKey, selectedKeys.currentKey ?? mappedToKey)) {
|
|
228
238
|
selection.delete(key);
|
|
229
239
|
}
|
|
230
240
|
|
|
231
|
-
for (let key of this.getKeyRange(
|
|
241
|
+
for (let key of this.getKeyRange(mappedToKey, anchorKey)) {
|
|
232
242
|
if (this.canSelectItem(key)) {
|
|
233
243
|
selection.add(key);
|
|
234
244
|
}
|
|
@@ -253,11 +263,15 @@ export class SelectionManager implements MultipleSelectionManager {
|
|
|
253
263
|
}
|
|
254
264
|
|
|
255
265
|
private getKeyRangeInternal(from: Key, to: Key) {
|
|
266
|
+
if (this.layoutDelegate?.getKeyRange) {
|
|
267
|
+
return this.layoutDelegate.getKeyRange(from, to);
|
|
268
|
+
}
|
|
269
|
+
|
|
256
270
|
let keys: Key[] = [];
|
|
257
|
-
let key = from;
|
|
258
|
-
while (key) {
|
|
271
|
+
let key: Key | null = from;
|
|
272
|
+
while (key != null) {
|
|
259
273
|
let item = this.collection.getItem(key);
|
|
260
|
-
if (item && item.type === 'item' || (item.type === 'cell' && this.allowsCellSelection)) {
|
|
274
|
+
if (item && (item.type === 'item' || (item.type === 'cell' && this.allowsCellSelection))) {
|
|
261
275
|
keys.push(key);
|
|
262
276
|
}
|
|
263
277
|
|
|
@@ -284,7 +298,7 @@ export class SelectionManager implements MultipleSelectionManager {
|
|
|
284
298
|
}
|
|
285
299
|
|
|
286
300
|
// Find a parent item to select
|
|
287
|
-
while (item.type !== 'item' && item.parentKey != null) {
|
|
301
|
+
while (item && item.type !== 'item' && item.parentKey != null) {
|
|
288
302
|
item = this.collection.getItem(item.parentKey);
|
|
289
303
|
}
|
|
290
304
|
|
|
@@ -308,20 +322,20 @@ export class SelectionManager implements MultipleSelectionManager {
|
|
|
308
322
|
return;
|
|
309
323
|
}
|
|
310
324
|
|
|
311
|
-
|
|
312
|
-
if (
|
|
325
|
+
let mappedKey = this.getKey(key);
|
|
326
|
+
if (mappedKey == null) {
|
|
313
327
|
return;
|
|
314
328
|
}
|
|
315
329
|
|
|
316
330
|
let keys = new Selection(this.state.selectedKeys === 'all' ? this.getSelectAllKeys() : this.state.selectedKeys);
|
|
317
|
-
if (keys.has(
|
|
318
|
-
keys.delete(
|
|
331
|
+
if (keys.has(mappedKey)) {
|
|
332
|
+
keys.delete(mappedKey);
|
|
319
333
|
// TODO: move anchor to last selected key...
|
|
320
334
|
// Does `current` need to move here too?
|
|
321
|
-
} else if (this.canSelectItem(
|
|
322
|
-
keys.add(
|
|
323
|
-
keys.anchorKey =
|
|
324
|
-
keys.currentKey =
|
|
335
|
+
} else if (this.canSelectItem(mappedKey)) {
|
|
336
|
+
keys.add(mappedKey);
|
|
337
|
+
keys.anchorKey = mappedKey;
|
|
338
|
+
keys.currentKey = mappedKey;
|
|
325
339
|
}
|
|
326
340
|
|
|
327
341
|
if (this.disallowEmptySelection && keys.size === 0) {
|
|
@@ -339,13 +353,13 @@ export class SelectionManager implements MultipleSelectionManager {
|
|
|
339
353
|
return;
|
|
340
354
|
}
|
|
341
355
|
|
|
342
|
-
|
|
343
|
-
if (
|
|
356
|
+
let mappedKey = this.getKey(key);
|
|
357
|
+
if (mappedKey == null) {
|
|
344
358
|
return;
|
|
345
359
|
}
|
|
346
360
|
|
|
347
|
-
let selection = this.canSelectItem(
|
|
348
|
-
? new Selection([
|
|
361
|
+
let selection = this.canSelectItem(mappedKey)
|
|
362
|
+
? new Selection([mappedKey], mappedKey, mappedKey)
|
|
349
363
|
: new Selection();
|
|
350
364
|
|
|
351
365
|
this.state.setSelectedKeys(selection);
|
|
@@ -361,9 +375,9 @@ export class SelectionManager implements MultipleSelectionManager {
|
|
|
361
375
|
|
|
362
376
|
let selection = new Selection();
|
|
363
377
|
for (let key of keys) {
|
|
364
|
-
|
|
365
|
-
if (
|
|
366
|
-
selection.add(
|
|
378
|
+
let mappedKey = this.getKey(key);
|
|
379
|
+
if (mappedKey != null) {
|
|
380
|
+
selection.add(mappedKey);
|
|
367
381
|
if (this.selectionMode === 'single') {
|
|
368
382
|
break;
|
|
369
383
|
}
|
|
@@ -375,17 +389,17 @@ export class SelectionManager implements MultipleSelectionManager {
|
|
|
375
389
|
|
|
376
390
|
private getSelectAllKeys() {
|
|
377
391
|
let keys: Key[] = [];
|
|
378
|
-
let addKeys = (key: Key) => {
|
|
392
|
+
let addKeys = (key: Key | null) => {
|
|
379
393
|
while (key != null) {
|
|
380
394
|
if (this.canSelectItem(key)) {
|
|
381
395
|
let item = this.collection.getItem(key);
|
|
382
|
-
if (item
|
|
396
|
+
if (item?.type === 'item') {
|
|
383
397
|
keys.push(key);
|
|
384
398
|
}
|
|
385
399
|
|
|
386
400
|
// Add child keys. If cell selection is allowed, then include item children too.
|
|
387
|
-
if (item
|
|
388
|
-
addKeys(getFirstItem(getChildNodes(item, this.collection))
|
|
401
|
+
if (item?.hasChildNodes && (this.allowsCellSelection || item.type !== 'item')) {
|
|
402
|
+
addKeys(getFirstItem(getChildNodes(item, this.collection))?.key ?? null);
|
|
389
403
|
}
|
|
390
404
|
}
|
|
391
405
|
|
package/src/types.ts
CHANGED
|
@@ -19,11 +19,11 @@ export interface FocusState {
|
|
|
19
19
|
/** Sets whether the collection is focused. */
|
|
20
20
|
setFocused(isFocused: boolean): void,
|
|
21
21
|
/** The current focused key in the collection. */
|
|
22
|
-
readonly focusedKey: Key,
|
|
22
|
+
readonly focusedKey: Key | null,
|
|
23
23
|
/** Whether the first or last child of the focused key should receive focus. */
|
|
24
|
-
readonly childFocusStrategy: FocusStrategy,
|
|
24
|
+
readonly childFocusStrategy: FocusStrategy | null,
|
|
25
25
|
/** Sets the focused key, and optionally, whether the first or last child of that key should receive focus. */
|
|
26
|
-
setFocusedKey(key: Key, child?: FocusStrategy): void
|
|
26
|
+
setFocusedKey(key: Key | null, child?: FocusStrategy): void
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export interface SingleSelectionState extends FocusState {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {DisabledBehavior, Key, MultipleSelection, SelectionBehavior, SelectionMode} from '@react-types/shared';
|
|
13
|
+
import {DisabledBehavior, FocusStrategy, Key, MultipleSelection, SelectionBehavior, SelectionMode} from '@react-types/shared';
|
|
14
14
|
import {MultipleSelectionState} from './types';
|
|
15
15
|
import {Selection} from './Selection';
|
|
16
16
|
import {useControlledState} from '@react-stately/utils';
|
|
@@ -45,7 +45,7 @@ export interface MultipleSelectionStateProps extends MultipleSelection {
|
|
|
45
45
|
export function useMultipleSelectionState(props: MultipleSelectionStateProps): MultipleSelectionState {
|
|
46
46
|
let {
|
|
47
47
|
selectionMode = 'none' as SelectionMode,
|
|
48
|
-
disallowEmptySelection,
|
|
48
|
+
disallowEmptySelection = false,
|
|
49
49
|
allowDuplicateSelectionEvents,
|
|
50
50
|
selectionBehavior: selectionBehaviorProp = 'toggle',
|
|
51
51
|
disabledBehavior = 'all'
|
|
@@ -55,14 +55,14 @@ export function useMultipleSelectionState(props: MultipleSelectionStateProps): M
|
|
|
55
55
|
// But we also need to trigger a react re-render. So, we have both a ref (sync) and state (async).
|
|
56
56
|
let isFocusedRef = useRef(false);
|
|
57
57
|
let [, setFocused] = useState(false);
|
|
58
|
-
let focusedKeyRef = useRef(null);
|
|
59
|
-
let childFocusStrategyRef = useRef(null);
|
|
60
|
-
let [, setFocusedKey] = useState(null);
|
|
58
|
+
let focusedKeyRef = useRef<Key | null>(null);
|
|
59
|
+
let childFocusStrategyRef = useRef<FocusStrategy | null>(null);
|
|
60
|
+
let [, setFocusedKey] = useState<Key | null>(null);
|
|
61
61
|
let selectedKeysProp = useMemo(() => convertSelection(props.selectedKeys), [props.selectedKeys]);
|
|
62
62
|
let defaultSelectedKeys = useMemo(() => convertSelection(props.defaultSelectedKeys, new Selection()), [props.defaultSelectedKeys]);
|
|
63
63
|
let [selectedKeys, setSelectedKeys] = useControlledState(
|
|
64
64
|
selectedKeysProp,
|
|
65
|
-
defaultSelectedKeys
|
|
65
|
+
defaultSelectedKeys!,
|
|
66
66
|
props.onSelectionChange
|
|
67
67
|
);
|
|
68
68
|
let disabledKeysProp = useMemo(() =>
|
|
@@ -119,7 +119,7 @@ export function useMultipleSelectionState(props: MultipleSelectionStateProps): M
|
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
function convertSelection(selection: 'all' | Iterable<Key
|
|
122
|
+
function convertSelection(selection: 'all' | Iterable<Key> | null | undefined, defaultValue?: Selection): 'all' | Set<Key> | undefined {
|
|
123
123
|
if (!selection) {
|
|
124
124
|
return defaultValue;
|
|
125
125
|
}
|