@fluentui/react-tree 9.7.7 → 9.7.8
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/CHANGELOG.md +11 -2
- package/dist/index.d.ts +54 -84
- package/lib/components/FlatTree/useFlatControllableCheckedItems.js +15 -13
- package/lib/components/FlatTree/useFlatControllableCheckedItems.js.map +1 -1
- package/lib/components/FlatTree/useHeadlessFlatTree.js +4 -2
- package/lib/components/FlatTree/useHeadlessFlatTree.js.map +1 -1
- package/lib/components/Tree/useNestedControllableCheckedItems.js +1 -1
- package/lib/components/Tree/useNestedControllableCheckedItems.js.map +1 -1
- package/lib/components/Tree/useTree.js +4 -2
- package/lib/components/Tree/useTree.js.map +1 -1
- package/lib/hooks/useControllableOpenItems.js +3 -12
- package/lib/hooks/useControllableOpenItems.js.map +1 -1
- package/lib/hooks/useRootTree.js +5 -4
- package/lib/hooks/useRootTree.js.map +1 -1
- package/lib/utils/ImmutableMap.js +71 -40
- package/lib/utils/ImmutableMap.js.map +1 -1
- package/lib/utils/ImmutableSet.js +65 -44
- package/lib/utils/ImmutableSet.js.map +1 -1
- package/lib/utils/createCheckedItems.js +5 -17
- package/lib/utils/createCheckedItems.js.map +1 -1
- package/lib-commonjs/components/FlatTree/useFlatControllableCheckedItems.js +15 -13
- package/lib-commonjs/components/FlatTree/useFlatControllableCheckedItems.js.map +1 -1
- package/lib-commonjs/components/FlatTree/useHeadlessFlatTree.js +4 -2
- package/lib-commonjs/components/FlatTree/useHeadlessFlatTree.js.map +1 -1
- package/lib-commonjs/components/Tree/useNestedControllableCheckedItems.js +1 -1
- package/lib-commonjs/components/Tree/useNestedControllableCheckedItems.js.map +1 -1
- package/lib-commonjs/components/Tree/useTree.js +4 -2
- package/lib-commonjs/components/Tree/useTree.js.map +1 -1
- package/lib-commonjs/hooks/useControllableOpenItems.js +3 -12
- package/lib-commonjs/hooks/useControllableOpenItems.js.map +1 -1
- package/lib-commonjs/hooks/useRootTree.js +5 -4
- package/lib-commonjs/hooks/useRootTree.js.map +1 -1
- package/lib-commonjs/utils/ImmutableMap.js +71 -40
- package/lib-commonjs/utils/ImmutableMap.js.map +1 -1
- package/lib-commonjs/utils/ImmutableSet.js +61 -45
- package/lib-commonjs/utils/ImmutableSet.js.map +1 -1
- package/lib-commonjs/utils/createCheckedItems.js +5 -17
- package/lib-commonjs/utils/createCheckedItems.js.map +1 -1
- package/package.json +1 -1
- package/lib/utils/createOpenItems.js +0 -10
- package/lib/utils/createOpenItems.js.map +0 -1
- package/lib-commonjs/utils/createOpenItems.js +0 -20
- package/lib-commonjs/utils/createOpenItems.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
# Change Log - @fluentui/react-tree
|
|
2
2
|
|
|
3
|
-
This log was last generated on
|
|
3
|
+
This log was last generated on Tue, 06 Aug 2024 21:39:53 GMT and should not be manually modified.
|
|
4
4
|
|
|
5
5
|
<!-- Start content -->
|
|
6
6
|
|
|
7
|
+
## [9.7.8](https://github.com/microsoft/fluentui/tree/@fluentui/react-tree_v9.7.8)
|
|
8
|
+
|
|
9
|
+
Tue, 06 Aug 2024 21:39:53 GMT
|
|
10
|
+
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-tree_v9.7.7..@fluentui/react-tree_v9.7.8)
|
|
11
|
+
|
|
12
|
+
### Patches
|
|
13
|
+
|
|
14
|
+
- chore: improve ImmutableSet and ImmutableMap internal implementation ([PR #32167](https://github.com/microsoft/fluentui/pull/32167) by bernardo.sunderhus@gmail.com)
|
|
15
|
+
|
|
7
16
|
## [9.7.7](https://github.com/microsoft/fluentui/tree/@fluentui/react-tree_v9.7.7)
|
|
8
17
|
|
|
9
|
-
Mon, 05 Aug 2024 22:
|
|
18
|
+
Mon, 05 Aug 2024 22:33:04 GMT
|
|
10
19
|
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-tree_v9.7.6..@fluentui/react-tree_v9.7.7)
|
|
11
20
|
|
|
12
21
|
### Patches
|
package/dist/index.d.ts
CHANGED
|
@@ -28,28 +28,6 @@ import type { SelectionMode as SelectionMode_2 } from '@fluentui/react-utilities
|
|
|
28
28
|
import type { Slot } from '@fluentui/react-utilities';
|
|
29
29
|
import type { SlotClassNames } from '@fluentui/react-utilities';
|
|
30
30
|
|
|
31
|
-
/**
|
|
32
|
-
* properly creates an ImmutableMap instance from an iterable
|
|
33
|
-
*/
|
|
34
|
-
declare function createImmutableMap<Key, Value>(iterable?: Iterable<[Key, Value]>): ImmutableMap<Key, Value>;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* properly creates an ImmutableSet instance from an iterable
|
|
38
|
-
*/
|
|
39
|
-
declare function createImmutableSet<Value>(iterable?: Iterable<Value>): ImmutableSet<Value>;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Avoid using *dangerouslyCreateImmutableMap*, since this method will expose internally used set, use createImmutableMap instead,
|
|
43
|
-
* @param internalMap - a set that is used internally to store values.
|
|
44
|
-
*/
|
|
45
|
-
declare function dangerouslyCreateImmutableMap<Key, Value>(internalMap: Map<Key, Value>): ImmutableMap<Key, Value>;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Avoid using *dangerouslyCreateImmutableSet*, since this method will expose internally used set, use createImmutableSet instead,
|
|
49
|
-
* @param internalSet - a set that is used internally to store values.
|
|
50
|
-
*/
|
|
51
|
-
declare function dangerouslyCreateImmutableSet<Value>(internalSet: Set<Value>): ImmutableSet<Value>;
|
|
52
|
-
|
|
53
31
|
declare type FlattenedTreeItem<Props extends TreeItemProps> = HeadlessFlatTreeItemProps & Props;
|
|
54
32
|
|
|
55
33
|
/**
|
|
@@ -297,85 +275,77 @@ declare type HeadlessTreeItemProps = Omit<TreeItemProps, 'itemType' | 'value'> &
|
|
|
297
275
|
parentValue?: TreeItemValue;
|
|
298
276
|
};
|
|
299
277
|
|
|
300
|
-
declare
|
|
301
|
-
|
|
302
|
-
|
|
278
|
+
declare class ImmutableMap<Key, Value> implements Iterable<[Key, Value]> {
|
|
279
|
+
static empty: ImmutableMap<never, never>;
|
|
280
|
+
readonly size: number;
|
|
281
|
+
private [internalMapSymbol];
|
|
282
|
+
static dangerouslyGetInternalMap<Key, Value>(immutableMap: ImmutableMap<Key, Value>): Map<Key, Value>;
|
|
283
|
+
static copy<Key, Value>(immutableMap: ImmutableMap<Key, Value>): ImmutableMap<Key, Value>;
|
|
303
284
|
/**
|
|
304
|
-
*
|
|
305
|
-
*
|
|
285
|
+
* Creates a new {@link ImmutableMap} from an iterable.
|
|
286
|
+
* If the iterable is undefined, {@link ImmutableMap.empty} will be returned.
|
|
287
|
+
* If the iterable is already an {@link ImmutableMap}, it will be returned as is no copy will be made.
|
|
306
288
|
*/
|
|
307
|
-
|
|
289
|
+
static from<T extends [unknown, unknown]>(iterable?: Iterable<T>): ImmutableMap<T[0], T[1]>;
|
|
308
290
|
/**
|
|
309
|
-
* @
|
|
291
|
+
* Creates a new {@link ImmutableMap} from an iterable with an auxiliary map function to modify the iterable.
|
|
292
|
+
* If the iterable is undefined, {@link ImmutableMap.empty} will be returned.
|
|
293
|
+
* If the iterable is already an {@link ImmutableMap}, it will be returned as is no copy will be made.
|
|
294
|
+
* The map function will be called for each element in the iterable.
|
|
310
295
|
*/
|
|
311
|
-
|
|
296
|
+
static from<T, U extends [unknown, unknown]>(iterable: Iterable<T> | undefined, mapFn: (value: T) => U): ImmutableMap<U[0], U[1]>;
|
|
297
|
+
static [Symbol.hasInstance](instance: unknown): boolean;
|
|
312
298
|
/**
|
|
313
|
-
*
|
|
299
|
+
* Do not use this constructor directly, use {@link ImmutableMap.from} instead.
|
|
300
|
+
* {@link ImmutableMap.from} handles instance verification (which might be problematic on {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms | multiple realms}),
|
|
301
|
+
* avoid unnecessary copies, supports iterables and ensures that the internal map is never exposed.
|
|
302
|
+
*
|
|
303
|
+
*⚠️⚠️ _By using this constructor directly, you might end up with a mutable map, as it is not guaranteed that the internal map is not exposed._ ⚠️⚠️
|
|
314
304
|
*/
|
|
305
|
+
constructor(internalMap: Map<Key, Value>);
|
|
306
|
+
delete(key: Key): ImmutableMap<Key, Value>;
|
|
307
|
+
get(key: Key): Value | undefined;
|
|
308
|
+
has(key: Key): boolean;
|
|
315
309
|
set(key: Key, value: Value): ImmutableMap<Key, Value>;
|
|
316
|
-
|
|
317
|
-
* @returns the number of elements in the Map.
|
|
318
|
-
*/
|
|
319
|
-
readonly size: number;
|
|
320
|
-
/** Iterates over entries in the Map. */
|
|
321
|
-
[Symbol.iterator](): IterableIterator<[Key, Value]>;
|
|
322
|
-
/**
|
|
323
|
-
* @internal
|
|
324
|
-
* Exposes the internal map used to store values.
|
|
325
|
-
* This is an internal API and should not be used directly.
|
|
326
|
-
*/
|
|
327
|
-
dangerouslyGetInternalMap_unstable(): Map<Key, Value>;
|
|
310
|
+
[Symbol.iterator](): Iterator<[Key, Value]>;
|
|
328
311
|
}
|
|
329
312
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* The number of (unique) elements in a ImmutableSet.
|
|
340
|
-
*/
|
|
313
|
+
/**
|
|
314
|
+
* @public
|
|
315
|
+
*
|
|
316
|
+
* Small immutable wrapper around the native Set implementation.
|
|
317
|
+
* Every operation that would modify the set returns a new copy instance.
|
|
318
|
+
*/
|
|
319
|
+
declare class ImmutableSet<T> implements Iterable<T> {
|
|
320
|
+
static empty: ImmutableSet<never>;
|
|
341
321
|
readonly size: number;
|
|
322
|
+
private [internalSetSymbol];
|
|
323
|
+
static dangerouslyGetInternalSet<Value>(set: ImmutableSet<Value>): Set<Value>;
|
|
324
|
+
static copy<T>(immutableSet: ImmutableSet<T>): ImmutableSet<T>;
|
|
342
325
|
/**
|
|
343
|
-
* Creates a new ImmutableSet
|
|
344
|
-
*
|
|
345
|
-
|
|
346
|
-
add(value: Value): ImmutableSet<Value>;
|
|
347
|
-
/**
|
|
348
|
-
* Returns a reference to ImmutableSet.emptySet
|
|
349
|
-
*/
|
|
350
|
-
clear(): ImmutableSet<Value>;
|
|
351
|
-
/**
|
|
352
|
-
* Creates a new ImmutableSet with the original items and removes a specified value from the new ImmutableSet.
|
|
326
|
+
* Creates a new {@link ImmutableSet} from an iterable.
|
|
327
|
+
* If the iterable is undefined, {@link ImmutableSet.empty} will be returned.
|
|
328
|
+
* If the iterable is already an {@link ImmutableSet}, it will be returned as is no copy will be made.
|
|
353
329
|
*/
|
|
354
|
-
|
|
330
|
+
static from<Value>(iterable?: Iterable<Value>): ImmutableSet<Value>;
|
|
331
|
+
static [Symbol.hasInstance](instance: unknown): boolean;
|
|
355
332
|
/**
|
|
356
|
-
*
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* @internal
|
|
363
|
-
* Exposes the internal set used to store values.
|
|
364
|
-
* This is an internal API and should not be used directly.
|
|
333
|
+
* Do not use this constructor directly, use {@link ImmutableSet.from} instead.
|
|
334
|
+
* {@link ImmutableSet.from} handles instance verification (which might be problematic on {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms | multiple realms}),
|
|
335
|
+
* avoid unnecessary copies, supports iterables and ensures that the internal set is never exposed.
|
|
336
|
+
*
|
|
337
|
+
*⚠️⚠️ _By using this constructor directly, you might end up with a mutable set, as it is not guaranteed that the internal set is not exposed._ ⚠️⚠️
|
|
365
338
|
*/
|
|
366
|
-
|
|
339
|
+
constructor(internalSet: Set<T>);
|
|
340
|
+
add(value: T): ImmutableSet<T>;
|
|
341
|
+
delete(value: T): ImmutableSet<T>;
|
|
342
|
+
has(value: T): boolean;
|
|
343
|
+
[Symbol.iterator](): Iterator<T>;
|
|
367
344
|
}
|
|
368
345
|
|
|
369
|
-
declare const
|
|
370
|
-
empty: ImmutableSet<never>;
|
|
371
|
-
create: typeof createImmutableSet;
|
|
372
|
-
isImmutableSet: typeof isImmutableSet;
|
|
373
|
-
dangerouslyCreate_unstable: typeof dangerouslyCreateImmutableSet;
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
declare function isImmutableMap<Key, Value>(value: unknown): value is ImmutableMap<Key, Value>;
|
|
346
|
+
declare const internalMapSymbol: unique symbol;
|
|
377
347
|
|
|
378
|
-
declare
|
|
348
|
+
declare const internalSetSymbol: unique symbol;
|
|
379
349
|
|
|
380
350
|
declare type MultiSelectValue = NonNullable<CheckboxProps['checked']>;
|
|
381
351
|
|
|
@@ -14,7 +14,7 @@ export function useFlatControllableCheckedItems(props, headlessTree) {
|
|
|
14
14
|
}
|
|
15
15
|
export function createNextFlatCheckedItems(data, previousCheckedItems, headlessTree) {
|
|
16
16
|
if (data.selectionMode === 'single') {
|
|
17
|
-
return ImmutableMap.
|
|
17
|
+
return ImmutableMap.from([
|
|
18
18
|
[
|
|
19
19
|
data.value,
|
|
20
20
|
data.checked
|
|
@@ -30,40 +30,42 @@ Tree item ${data.value} not found.`);
|
|
|
30
30
|
}
|
|
31
31
|
return previousCheckedItems;
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
let nextCheckedItems = previousCheckedItems;
|
|
34
34
|
for (const children of headlessTree.subtree(data.value)){
|
|
35
|
-
nextCheckedItems.set(children.value, data.checked);
|
|
35
|
+
nextCheckedItems = nextCheckedItems.set(children.value, data.checked);
|
|
36
36
|
}
|
|
37
|
-
nextCheckedItems.set(data.value, data.checked);
|
|
37
|
+
nextCheckedItems = nextCheckedItems.set(data.value, data.checked);
|
|
38
38
|
let isAncestorsMixed = false;
|
|
39
39
|
for (const parent of headlessTree.ancestors(treeItem.value)){
|
|
40
40
|
// if one parent is mixed, all ancestors are mixed
|
|
41
41
|
if (isAncestorsMixed) {
|
|
42
|
-
nextCheckedItems.set(parent.value, 'mixed');
|
|
42
|
+
nextCheckedItems = nextCheckedItems.set(parent.value, 'mixed');
|
|
43
43
|
continue;
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
let checkedChildrenAmount = 0;
|
|
46
46
|
for (const child of headlessTree.children(parent.value)){
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
checkedChildren.push(child);
|
|
47
|
+
if ((nextCheckedItems.get(child.value) || false) === data.checked) {
|
|
48
|
+
checkedChildrenAmount++;
|
|
50
49
|
}
|
|
51
50
|
}
|
|
52
|
-
if
|
|
53
|
-
|
|
51
|
+
// if all children are checked, parent is checked
|
|
52
|
+
if (checkedChildrenAmount === parent.childrenValues.length) {
|
|
53
|
+
nextCheckedItems = nextCheckedItems.set(parent.value, data.checked);
|
|
54
54
|
} else {
|
|
55
55
|
// if one parent is mixed, all ancestors are mixed
|
|
56
56
|
isAncestorsMixed = true;
|
|
57
|
-
nextCheckedItems.set(parent.value, 'mixed');
|
|
57
|
+
nextCheckedItems = nextCheckedItems.set(parent.value, 'mixed');
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
return
|
|
60
|
+
return nextCheckedItems;
|
|
61
61
|
}
|
|
62
62
|
function initializeCheckedItems(props, headlessTree) {
|
|
63
63
|
if (!props.selectionMode) {
|
|
64
64
|
return ImmutableMap.empty;
|
|
65
65
|
}
|
|
66
66
|
let state = createCheckedItems(props.defaultCheckedItems);
|
|
67
|
+
// if selectionMode is multiselect, we need to calculate the checked state of all children
|
|
68
|
+
// and ancestors of the defaultCheckedItems
|
|
67
69
|
if (props.selectionMode === 'multiselect') {
|
|
68
70
|
for (const [value, checked] of state){
|
|
69
71
|
state = createNextFlatCheckedItems({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["useFlatControllableCheckedItems.ts"],"sourcesContent":["import { useControllableState } from '@fluentui/react-utilities';\nimport { TreeItemValue } from '../../TreeItem';\nimport { ImmutableMap } from '../../utils/ImmutableMap';\nimport * as React from 'react';\nimport type { HeadlessTree, HeadlessTreeItemProps } from '../../utils/createHeadlessTree';\nimport { createCheckedItems } from '../../utils/createCheckedItems';\nimport type { TreeCheckedChangeData } from '../Tree/Tree.types';\nimport { HeadlessFlatTreeOptions } from './useHeadlessFlatTree';\n\nexport function useFlatControllableCheckedItems<Props extends HeadlessTreeItemProps>(\n props: Pick<HeadlessFlatTreeOptions, 'checkedItems' | 'defaultCheckedItems' | 'selectionMode'>,\n headlessTree: HeadlessTree<Props>,\n) {\n return useControllableState({\n initialState: ImmutableMap.empty,\n state: React.useMemo(\n () => (props.selectionMode ? props.checkedItems && createCheckedItems(props.checkedItems) : undefined),\n [props.checkedItems, props.selectionMode],\n ),\n defaultState: props.defaultCheckedItems ? () => initializeCheckedItems(props, headlessTree) : undefined,\n });\n}\n\nexport function createNextFlatCheckedItems(\n data: Pick<TreeCheckedChangeData, 'value' | 'checked' | 'selectionMode'>,\n previousCheckedItems: ImmutableMap<TreeItemValue, 'mixed' | boolean>,\n headlessTree: HeadlessTree<HeadlessTreeItemProps>,\n): ImmutableMap<TreeItemValue, 'mixed' | boolean> {\n if (data.selectionMode === 'single') {\n return ImmutableMap.
|
|
1
|
+
{"version":3,"sources":["useFlatControllableCheckedItems.ts"],"sourcesContent":["import { useControllableState } from '@fluentui/react-utilities';\nimport { TreeItemValue } from '../../TreeItem';\nimport { ImmutableMap } from '../../utils/ImmutableMap';\nimport * as React from 'react';\nimport type { HeadlessTree, HeadlessTreeItemProps } from '../../utils/createHeadlessTree';\nimport { createCheckedItems } from '../../utils/createCheckedItems';\nimport type { TreeCheckedChangeData } from '../Tree/Tree.types';\nimport { HeadlessFlatTreeOptions } from './useHeadlessFlatTree';\n\nexport function useFlatControllableCheckedItems<Props extends HeadlessTreeItemProps>(\n props: Pick<HeadlessFlatTreeOptions, 'checkedItems' | 'defaultCheckedItems' | 'selectionMode'>,\n headlessTree: HeadlessTree<Props>,\n) {\n return useControllableState({\n initialState: ImmutableMap.empty,\n state: React.useMemo(\n () => (props.selectionMode ? props.checkedItems && createCheckedItems(props.checkedItems) : undefined),\n [props.checkedItems, props.selectionMode],\n ),\n defaultState: props.defaultCheckedItems ? () => initializeCheckedItems(props, headlessTree) : undefined,\n });\n}\n\nexport function createNextFlatCheckedItems(\n data: Pick<TreeCheckedChangeData, 'value' | 'checked' | 'selectionMode'>,\n previousCheckedItems: ImmutableMap<TreeItemValue, 'mixed' | boolean>,\n headlessTree: HeadlessTree<HeadlessTreeItemProps>,\n): ImmutableMap<TreeItemValue, 'mixed' | boolean> {\n if (data.selectionMode === 'single') {\n return ImmutableMap.from([[data.value, data.checked]]);\n }\n const treeItem = headlessTree.get(data.value);\n if (!treeItem) {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.error(/* #__DE-INDENT__ */ `\n @fluentui/react-tree [useHeadlessFlatTree]:\n Tree item ${data.value} not found.\n `);\n }\n return previousCheckedItems;\n }\n let nextCheckedItems = previousCheckedItems;\n for (const children of headlessTree.subtree(data.value)) {\n nextCheckedItems = nextCheckedItems.set(children.value, data.checked);\n }\n nextCheckedItems = nextCheckedItems.set(data.value, data.checked);\n\n let isAncestorsMixed = false;\n for (const parent of headlessTree.ancestors(treeItem.value)) {\n // if one parent is mixed, all ancestors are mixed\n if (isAncestorsMixed) {\n nextCheckedItems = nextCheckedItems.set(parent.value, 'mixed');\n continue;\n }\n let checkedChildrenAmount = 0;\n for (const child of headlessTree.children(parent.value)) {\n if ((nextCheckedItems.get(child.value) || false) === data.checked) {\n checkedChildrenAmount++;\n }\n }\n // if all children are checked, parent is checked\n if (checkedChildrenAmount === parent.childrenValues.length) {\n nextCheckedItems = nextCheckedItems.set(parent.value, data.checked);\n } else {\n // if one parent is mixed, all ancestors are mixed\n isAncestorsMixed = true;\n nextCheckedItems = nextCheckedItems.set(parent.value, 'mixed');\n }\n }\n return nextCheckedItems;\n}\n\nfunction initializeCheckedItems(\n props: Pick<HeadlessFlatTreeOptions, 'selectionMode' | 'defaultCheckedItems'>,\n headlessTree: HeadlessTree<HeadlessTreeItemProps>,\n) {\n if (!props.selectionMode) {\n return ImmutableMap.empty;\n }\n let state = createCheckedItems(props.defaultCheckedItems);\n // if selectionMode is multiselect, we need to calculate the checked state of all children\n // and ancestors of the defaultCheckedItems\n if (props.selectionMode === 'multiselect') {\n for (const [value, checked] of state) {\n state = createNextFlatCheckedItems({ value, checked, selectionMode: props.selectionMode }, state, headlessTree);\n }\n }\n return state;\n}\n"],"names":["useControllableState","ImmutableMap","React","createCheckedItems","useFlatControllableCheckedItems","props","headlessTree","initialState","empty","state","useMemo","selectionMode","checkedItems","undefined","defaultState","defaultCheckedItems","initializeCheckedItems","createNextFlatCheckedItems","data","previousCheckedItems","from","value","checked","treeItem","get","process","env","NODE_ENV","console","error","nextCheckedItems","children","subtree","set","isAncestorsMixed","parent","ancestors","checkedChildrenAmount","child","childrenValues","length"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAAA,SAASA,oBAAoB,QAAQ,4BAA4B;AAEjE,SAASC,YAAY,QAAQ,2BAA2B;AACxD,YAAYC,WAAW,QAAQ;AAE/B,SAASC,kBAAkB,QAAQ,iCAAiC;AAIpE,OAAO,SAASC,gCACdC,KAA8F,EAC9FC,YAAiC;IAEjC,OAAON,qBAAqB;QAC1BO,cAAcN,aAAaO,KAAK;QAChCC,OAAOP,MAAMQ,OAAO,CAClB,IAAOL,MAAMM,aAAa,GAAGN,MAAMO,YAAY,IAAIT,mBAAmBE,MAAMO,YAAY,IAAIC,WAC5F;YAACR,MAAMO,YAAY;YAAEP,MAAMM,aAAa;SAAC;QAE3CG,cAAcT,MAAMU,mBAAmB,GAAG,IAAMC,uBAAuBX,OAAOC,gBAAgBO;IAChG;AACF;AAEA,OAAO,SAASI,2BACdC,IAAwE,EACxEC,oBAAoE,EACpEb,YAAiD;IAEjD,IAAIY,KAAKP,aAAa,KAAK,UAAU;QACnC,OAAOV,aAAamB,IAAI,CAAC;YAAC;gBAACF,KAAKG,KAAK;gBAAEH,KAAKI,OAAO;aAAC;SAAC;IACvD;IACA,MAAMC,WAAWjB,aAAakB,GAAG,CAACN,KAAKG,KAAK;IAC5C,IAAI,CAACE,UAAU;QACb,IAAIE,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;YACzC,sCAAsC;YACtCC,QAAQC,KAAK,CAAsB,CAAC;UAExB,EAAEX,KAAKG,KAAK,CAAC,WACzB,CAAC;QACH;QACA,OAAOF;IACT;IACA,IAAIW,mBAAmBX;IACvB,KAAK,MAAMY,YAAYzB,aAAa0B,OAAO,CAACd,KAAKG,KAAK,EAAG;QACvDS,mBAAmBA,iBAAiBG,GAAG,CAACF,SAASV,KAAK,EAAEH,KAAKI,OAAO;IACtE;IACAQ,mBAAmBA,iBAAiBG,GAAG,CAACf,KAAKG,KAAK,EAAEH,KAAKI,OAAO;IAEhE,IAAIY,mBAAmB;IACvB,KAAK,MAAMC,UAAU7B,aAAa8B,SAAS,CAACb,SAASF,KAAK,EAAG;QAC3D,kDAAkD;QAClD,IAAIa,kBAAkB;YACpBJ,mBAAmBA,iBAAiBG,GAAG,CAACE,OAAOd,KAAK,EAAE;YACtD;QACF;QACA,IAAIgB,wBAAwB;QAC5B,KAAK,MAAMC,SAAShC,aAAayB,QAAQ,CAACI,OAAOd,KAAK,EAAG;YACvD,IAAI,AAACS,CAAAA,iBAAiBN,GAAG,CAACc,MAAMjB,KAAK,KAAK,KAAI,MAAOH,KAAKI,OAAO,EAAE;gBACjEe;YACF;QACF;QACA,iDAAiD;QACjD,IAAIA,0BAA0BF,OAAOI,cAAc,CAACC,MAAM,EAAE;YAC1DV,mBAAmBA,iBAAiBG,GAAG,CAACE,OAAOd,KAAK,EAAEH,KAAKI,OAAO;QACpE,OAAO;YACL,kDAAkD;YAClDY,mBAAmB;YACnBJ,mBAAmBA,iBAAiBG,GAAG,CAACE,OAAOd,KAAK,EAAE;QACxD;IACF;IACA,OAAOS;AACT;AAEA,SAASd,uBACPX,KAA6E,EAC7EC,YAAiD;IAEjD,IAAI,CAACD,MAAMM,aAAa,EAAE;QACxB,OAAOV,aAAaO,KAAK;IAC3B;IACA,IAAIC,QAAQN,mBAAmBE,MAAMU,mBAAmB;IACxD,0FAA0F;IAC1F,2CAA2C;IAC3C,IAAIV,MAAMM,aAAa,KAAK,eAAe;QACzC,KAAK,MAAM,CAACU,OAAOC,QAAQ,IAAIb,MAAO;YACpCA,QAAQQ,2BAA2B;gBAAEI;gBAAOC;gBAASX,eAAeN,MAAMM,aAAa;YAAC,GAAGF,OAAOH;QACpG;IACF;IACA,OAAOG;AACT"}
|
|
@@ -5,7 +5,9 @@ import { treeDataTypes } from '../../utils/tokens';
|
|
|
5
5
|
import { useFlatTreeNavigation } from '../../hooks/useFlatTreeNavigation';
|
|
6
6
|
import { createNextOpenItems, useControllableOpenItems } from '../../hooks/useControllableOpenItems';
|
|
7
7
|
import { dataTreeItemValueAttrName } from '../../utils/getTreeItemValueFromElement';
|
|
8
|
+
import { ImmutableSet } from '../../utils/ImmutableSet';
|
|
8
9
|
import { createNextFlatCheckedItems, useFlatControllableCheckedItems } from './useFlatControllableCheckedItems';
|
|
10
|
+
import { ImmutableMap } from '../../utils/ImmutableMap';
|
|
9
11
|
/**
|
|
10
12
|
* this hook provides FlatTree API to manage all required mechanisms to convert a list of items into renderable TreeItems
|
|
11
13
|
* in multiple scenarios including virtualization.
|
|
@@ -31,7 +33,7 @@ import { createNextFlatCheckedItems, useFlatControllableCheckedItems } from './u
|
|
|
31
33
|
const nextOpenItems = createNextOpenItems(data, openItems);
|
|
32
34
|
(_options_onOpenChange = options.onOpenChange) === null || _options_onOpenChange === void 0 ? void 0 : _options_onOpenChange.call(options, event, {
|
|
33
35
|
...data,
|
|
34
|
-
openItems:
|
|
36
|
+
openItems: ImmutableSet.dangerouslyGetInternalSet(nextOpenItems)
|
|
35
37
|
});
|
|
36
38
|
setOpenItems(nextOpenItems);
|
|
37
39
|
});
|
|
@@ -40,7 +42,7 @@ import { createNextFlatCheckedItems, useFlatControllableCheckedItems } from './u
|
|
|
40
42
|
const nextCheckedItems = createNextFlatCheckedItems(data, checkedItems, headlessTree);
|
|
41
43
|
(_options_onCheckedChange = options.onCheckedChange) === null || _options_onCheckedChange === void 0 ? void 0 : _options_onCheckedChange.call(options, event, {
|
|
42
44
|
...data,
|
|
43
|
-
checkedItems:
|
|
45
|
+
checkedItems: ImmutableMap.dangerouslyGetInternalMap(nextCheckedItems)
|
|
44
46
|
});
|
|
45
47
|
setCheckedItems(nextCheckedItems);
|
|
46
48
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["useHeadlessFlatTree.ts"],"sourcesContent":["import { useEventCallback, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\nimport { HeadlessTreeItem, HeadlessTreeItemProps, createHeadlessTree } from '../../utils/createHeadlessTree';\nimport { treeDataTypes } from '../../utils/tokens';\nimport { useFlatTreeNavigation } from '../../hooks/useFlatTreeNavigation';\nimport { createNextOpenItems, useControllableOpenItems } from '../../hooks/useControllableOpenItems';\nimport type { TreeItemValue } from '../../TreeItem';\nimport { dataTreeItemValueAttrName } from '../../utils/getTreeItemValueFromElement';\nimport { ImmutableSet } from '../../utils/ImmutableSet';\nimport { createNextFlatCheckedItems, useFlatControllableCheckedItems } from './useFlatControllableCheckedItems';\nimport { FlatTreeProps } from './FlatTree.types';\nimport {\n TreeCheckedChangeData,\n TreeCheckedChangeEvent,\n TreeNavigationData_unstable,\n TreeOpenChangeData,\n TreeOpenChangeEvent,\n TreeProps,\n} from '../Tree/Tree.types';\n\nexport type HeadlessFlatTreeItemProps = HeadlessTreeItemProps;\nexport type HeadlessFlatTreeItem<Props extends HeadlessFlatTreeItemProps> = HeadlessTreeItem<Props>;\n\n/**\n * FlatTree API to manage all required mechanisms to convert a list of items into renderable TreeItems\n * in multiple scenarios including virtualization.\n *\n * !!A flat tree is an unofficial spec for tree!!\n *\n * It should be used on cases where more complex interactions with a Tree is required.\n *\n * On simple scenarios it is advised to simply use a nested structure instead.\n */\nexport type HeadlessFlatTree<Props extends HeadlessFlatTreeItemProps> = {\n /**\n * returns the properties required for the Tree component to work properly.\n * That includes:\n * `openItems`, `onOpenChange`, `onNavigation_unstable` and `ref`\n */\n getTreeProps(): Required<\n Pick<FlatTreeProps, 'openItems' | 'onOpenChange' | 'onNavigation' | 'checkedItems' | 'onCheckedChange'>\n > & {\n ref: React.Ref<HTMLDivElement>;\n openItems: ImmutableSet<TreeItemValue>;\n };\n /**\n * internal method used to react to an `onNavigation` event.\n * This method ensures proper navigation on keyboard and mouse interaction.\n * In case of virtualization it might be required to cancel default provided `onNavigation`\n * event and then call this method manually.\n *\n * @example\n * ```ts\n * // react-window\n * const handleNavigation = (event, data) => {\n * event.preventDefault();\n * const nextItem = tree.getNextNavigableItem(data);\n * // scroll to item using virtualization scroll mechanism\n * if (nextItem && tree.getElementFromItem(nextItem)) {\n * listRef.current.scrollToItem(nextItem.index);\n * }\n * // wait for scrolling to happen and then invoke navigate method\n * requestAnimationFrame(() => {\n * tree.navigate(data);\n * });\n * };\n *```\n */\n navigate(data: TreeNavigationData_unstable): void;\n /**\n * returns next item to be focused on a navigation.\n * This method is provided to decouple the element that needs to be focused from\n * the action of focusing it itself.\n *\n * On the case of TypeAhead navigation this method returns the current item.\n */\n getNextNavigableItem(\n visibleItems: HeadlessTreeItem<Props>[],\n data: TreeNavigationData_unstable,\n ): HeadlessTreeItem<Props> | undefined;\n /**\n * similar to getElementById but for FlatTreeItems\n */\n getElementFromItem(item: HeadlessTreeItem<Props>): HTMLElement | null;\n /**\n * an iterable containing all visually available flat tree items\n */\n items(): IterableIterator<HeadlessTreeItem<Props>>;\n};\n\nexport type HeadlessFlatTreeOptions = Pick<\n FlatTreeProps,\n 'onOpenChange' | 'onNavigation' | 'selectionMode' | 'onCheckedChange'\n> &\n Pick<TreeProps, 'defaultOpenItems' | 'openItems' | 'checkedItems'> & {\n defaultCheckedItems?: TreeProps['checkedItems'];\n };\n\n/**\n * @internal\n */\ntype HeadlessFlatTreeReturn<Props extends HeadlessFlatTreeItemProps> = HeadlessFlatTree<Props> & {\n getItem(value: TreeItemValue): HeadlessTreeItem<Props> | undefined;\n};\n\n/**\n * this hook provides FlatTree API to manage all required mechanisms to convert a list of items into renderable TreeItems\n * in multiple scenarios including virtualization.\n *\n * !!A flat tree is an unofficial spec for tree!!\n *\n * It should be used on cases where more complex interactions with a Tree is required.\n * On simple scenarios it is advised to simply use a nested structure instead.\n *\n * @param props - a list of tree items\n * @param options - in case control over the internal openItems is required\n */\nexport function useHeadlessFlatTree_unstable<Props extends HeadlessTreeItemProps>(\n props: Props[],\n options: HeadlessFlatTreeOptions = {},\n): HeadlessFlatTreeReturn<Props> {\n 'use no memo';\n\n const headlessTree = React.useMemo(() => createHeadlessTree(props), [props]);\n const [openItems, setOpenItems] = useControllableOpenItems(options);\n const [checkedItems, setCheckedItems] = useFlatControllableCheckedItems(options, headlessTree);\n const navigation = useFlatTreeNavigation();\n\n const treeRef = React.useRef<HTMLDivElement>(null);\n const handleOpenChange = useEventCallback((event: TreeOpenChangeEvent, data: TreeOpenChangeData) => {\n const nextOpenItems = createNextOpenItems(data, openItems);\n options.onOpenChange?.(event, {\n ...data,\n openItems: nextOpenItems.dangerouslyGetInternalSet_unstable(),\n });\n setOpenItems(nextOpenItems);\n });\n\n const handleCheckedChange = useEventCallback((event: TreeCheckedChangeEvent, data: TreeCheckedChangeData) => {\n const nextCheckedItems = createNextFlatCheckedItems(data, checkedItems, headlessTree);\n options.onCheckedChange?.(event, {\n ...data,\n checkedItems: nextCheckedItems.dangerouslyGetInternalMap_unstable(),\n });\n setCheckedItems(nextCheckedItems);\n });\n\n const getNextNavigableItem = useEventCallback(\n (visibleItems: HeadlessTreeItem<Props>[], data: TreeNavigationData_unstable) => {\n const item = headlessTree.get(data.value);\n if (item) {\n switch (data.type) {\n case treeDataTypes.TypeAhead:\n return item;\n case treeDataTypes.ArrowLeft:\n return headlessTree.get(item.parentValue!);\n case treeDataTypes.ArrowRight:\n return visibleItems[item.index + 1];\n case treeDataTypes.End:\n return visibleItems[visibleItems.length - 1];\n case treeDataTypes.Home:\n return visibleItems[0];\n case treeDataTypes.ArrowDown:\n return visibleItems[item.index + 1];\n case treeDataTypes.ArrowUp:\n return visibleItems[item.index - 1];\n }\n }\n },\n );\n\n const getElementFromItem = React.useCallback((item: HeadlessTreeItem<Props>) => {\n return treeRef.current?.querySelector(`[${dataTreeItemValueAttrName}=\"${item.value}\"]`) as HTMLElement | null;\n }, []);\n\n const ref = useMergedRefs<HTMLDivElement>(treeRef, navigation.rootRef);\n\n const getTreeProps = React.useCallback(\n () => ({\n ref,\n openItems,\n selectionMode: options.selectionMode,\n checkedItems,\n onOpenChange: handleOpenChange,\n onCheckedChange: handleCheckedChange,\n onNavigation: options.onNavigation ?? noop,\n }),\n // ref, handleOpenChange - useEventCallback, handleCheckedChange - useEventCallback\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [openItems, checkedItems, options.selectionMode, options.onNavigation],\n );\n\n const items = React.useCallback(() => headlessTree.visibleItems(openItems), [openItems, headlessTree]);\n\n const getItem = React.useCallback((value: TreeItemValue) => headlessTree.get(value), [headlessTree]);\n\n return React.useMemo<HeadlessFlatTreeReturn<Props>>(\n () => ({\n navigate: navigation.navigate,\n getTreeProps,\n getNextNavigableItem,\n getElementFromItem,\n items,\n getItem,\n }),\n [navigation.navigate, getTreeProps, getNextNavigableItem, getElementFromItem, items, getItem],\n );\n}\n\n/** @internal */\nfunction noop() {\n /* noop */\n}\n"],"names":["useEventCallback","useMergedRefs","React","createHeadlessTree","treeDataTypes","useFlatTreeNavigation","createNextOpenItems","useControllableOpenItems","dataTreeItemValueAttrName","createNextFlatCheckedItems","useFlatControllableCheckedItems","useHeadlessFlatTree_unstable","props","options","headlessTree","useMemo","openItems","setOpenItems","checkedItems","setCheckedItems","navigation","treeRef","useRef","handleOpenChange","event","data","nextOpenItems","onOpenChange","dangerouslyGetInternalSet_unstable","handleCheckedChange","nextCheckedItems","onCheckedChange","dangerouslyGetInternalMap_unstable","getNextNavigableItem","visibleItems","item","get","value","type","TypeAhead","ArrowLeft","parentValue","ArrowRight","index","End","length","Home","ArrowDown","ArrowUp","getElementFromItem","useCallback","current","querySelector","ref","rootRef","getTreeProps","selectionMode","onNavigation","noop","items","getItem","navigate"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["useHeadlessFlatTree.ts"],"sourcesContent":["import { useEventCallback, useMergedRefs } from '@fluentui/react-utilities';\nimport * as React from 'react';\nimport { HeadlessTreeItem, HeadlessTreeItemProps, createHeadlessTree } from '../../utils/createHeadlessTree';\nimport { treeDataTypes } from '../../utils/tokens';\nimport { useFlatTreeNavigation } from '../../hooks/useFlatTreeNavigation';\nimport { createNextOpenItems, useControllableOpenItems } from '../../hooks/useControllableOpenItems';\nimport type { TreeItemValue } from '../../TreeItem';\nimport { dataTreeItemValueAttrName } from '../../utils/getTreeItemValueFromElement';\nimport { ImmutableSet } from '../../utils/ImmutableSet';\nimport { createNextFlatCheckedItems, useFlatControllableCheckedItems } from './useFlatControllableCheckedItems';\nimport { FlatTreeProps } from './FlatTree.types';\nimport {\n TreeCheckedChangeData,\n TreeCheckedChangeEvent,\n TreeNavigationData_unstable,\n TreeOpenChangeData,\n TreeOpenChangeEvent,\n TreeProps,\n} from '../Tree/Tree.types';\nimport { ImmutableMap } from '../../utils/ImmutableMap';\n\nexport type HeadlessFlatTreeItemProps = HeadlessTreeItemProps;\nexport type HeadlessFlatTreeItem<Props extends HeadlessFlatTreeItemProps> = HeadlessTreeItem<Props>;\n\n/**\n * FlatTree API to manage all required mechanisms to convert a list of items into renderable TreeItems\n * in multiple scenarios including virtualization.\n *\n * !!A flat tree is an unofficial spec for tree!!\n *\n * It should be used on cases where more complex interactions with a Tree is required.\n *\n * On simple scenarios it is advised to simply use a nested structure instead.\n */\nexport type HeadlessFlatTree<Props extends HeadlessFlatTreeItemProps> = {\n /**\n * returns the properties required for the Tree component to work properly.\n * That includes:\n * `openItems`, `onOpenChange`, `onNavigation_unstable` and `ref`\n */\n getTreeProps(): Required<\n Pick<FlatTreeProps, 'openItems' | 'onOpenChange' | 'onNavigation' | 'checkedItems' | 'onCheckedChange'>\n > & {\n ref: React.Ref<HTMLDivElement>;\n openItems: ImmutableSet<TreeItemValue>;\n };\n /**\n * internal method used to react to an `onNavigation` event.\n * This method ensures proper navigation on keyboard and mouse interaction.\n * In case of virtualization it might be required to cancel default provided `onNavigation`\n * event and then call this method manually.\n *\n * @example\n * ```ts\n * // react-window\n * const handleNavigation = (event, data) => {\n * event.preventDefault();\n * const nextItem = tree.getNextNavigableItem(data);\n * // scroll to item using virtualization scroll mechanism\n * if (nextItem && tree.getElementFromItem(nextItem)) {\n * listRef.current.scrollToItem(nextItem.index);\n * }\n * // wait for scrolling to happen and then invoke navigate method\n * requestAnimationFrame(() => {\n * tree.navigate(data);\n * });\n * };\n *```\n */\n navigate(data: TreeNavigationData_unstable): void;\n /**\n * returns next item to be focused on a navigation.\n * This method is provided to decouple the element that needs to be focused from\n * the action of focusing it itself.\n *\n * On the case of TypeAhead navigation this method returns the current item.\n */\n getNextNavigableItem(\n visibleItems: HeadlessTreeItem<Props>[],\n data: TreeNavigationData_unstable,\n ): HeadlessTreeItem<Props> | undefined;\n /**\n * similar to getElementById but for FlatTreeItems\n */\n getElementFromItem(item: HeadlessTreeItem<Props>): HTMLElement | null;\n /**\n * an iterable containing all visually available flat tree items\n */\n items(): IterableIterator<HeadlessTreeItem<Props>>;\n};\n\nexport type HeadlessFlatTreeOptions = Pick<\n FlatTreeProps,\n 'onOpenChange' | 'onNavigation' | 'selectionMode' | 'onCheckedChange'\n> &\n Pick<TreeProps, 'defaultOpenItems' | 'openItems' | 'checkedItems'> & {\n defaultCheckedItems?: TreeProps['checkedItems'];\n };\n\n/**\n * @internal\n */\ntype HeadlessFlatTreeReturn<Props extends HeadlessFlatTreeItemProps> = HeadlessFlatTree<Props> & {\n getItem(value: TreeItemValue): HeadlessTreeItem<Props> | undefined;\n};\n\n/**\n * this hook provides FlatTree API to manage all required mechanisms to convert a list of items into renderable TreeItems\n * in multiple scenarios including virtualization.\n *\n * !!A flat tree is an unofficial spec for tree!!\n *\n * It should be used on cases where more complex interactions with a Tree is required.\n * On simple scenarios it is advised to simply use a nested structure instead.\n *\n * @param props - a list of tree items\n * @param options - in case control over the internal openItems is required\n */\nexport function useHeadlessFlatTree_unstable<Props extends HeadlessTreeItemProps>(\n props: Props[],\n options: HeadlessFlatTreeOptions = {},\n): HeadlessFlatTreeReturn<Props> {\n 'use no memo';\n\n const headlessTree = React.useMemo(() => createHeadlessTree(props), [props]);\n const [openItems, setOpenItems] = useControllableOpenItems(options);\n const [checkedItems, setCheckedItems] = useFlatControllableCheckedItems(options, headlessTree);\n const navigation = useFlatTreeNavigation();\n\n const treeRef = React.useRef<HTMLDivElement>(null);\n const handleOpenChange = useEventCallback((event: TreeOpenChangeEvent, data: TreeOpenChangeData) => {\n const nextOpenItems = createNextOpenItems(data, openItems);\n options.onOpenChange?.(event, {\n ...data,\n openItems: ImmutableSet.dangerouslyGetInternalSet(nextOpenItems),\n });\n setOpenItems(nextOpenItems);\n });\n\n const handleCheckedChange = useEventCallback((event: TreeCheckedChangeEvent, data: TreeCheckedChangeData) => {\n const nextCheckedItems = createNextFlatCheckedItems(data, checkedItems, headlessTree);\n options.onCheckedChange?.(event, {\n ...data,\n checkedItems: ImmutableMap.dangerouslyGetInternalMap(nextCheckedItems),\n });\n setCheckedItems(nextCheckedItems);\n });\n\n const getNextNavigableItem = useEventCallback(\n (visibleItems: HeadlessTreeItem<Props>[], data: TreeNavigationData_unstable) => {\n const item = headlessTree.get(data.value);\n if (item) {\n switch (data.type) {\n case treeDataTypes.TypeAhead:\n return item;\n case treeDataTypes.ArrowLeft:\n return headlessTree.get(item.parentValue!);\n case treeDataTypes.ArrowRight:\n return visibleItems[item.index + 1];\n case treeDataTypes.End:\n return visibleItems[visibleItems.length - 1];\n case treeDataTypes.Home:\n return visibleItems[0];\n case treeDataTypes.ArrowDown:\n return visibleItems[item.index + 1];\n case treeDataTypes.ArrowUp:\n return visibleItems[item.index - 1];\n }\n }\n },\n );\n\n const getElementFromItem = React.useCallback((item: HeadlessTreeItem<Props>) => {\n return treeRef.current?.querySelector(`[${dataTreeItemValueAttrName}=\"${item.value}\"]`) as HTMLElement | null;\n }, []);\n\n const ref = useMergedRefs<HTMLDivElement>(treeRef, navigation.rootRef);\n\n const getTreeProps = React.useCallback(\n () => ({\n ref,\n openItems,\n selectionMode: options.selectionMode,\n checkedItems,\n onOpenChange: handleOpenChange,\n onCheckedChange: handleCheckedChange,\n onNavigation: options.onNavigation ?? noop,\n }),\n // ref, handleOpenChange - useEventCallback, handleCheckedChange - useEventCallback\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [openItems, checkedItems, options.selectionMode, options.onNavigation],\n );\n\n const items = React.useCallback(() => headlessTree.visibleItems(openItems), [openItems, headlessTree]);\n\n const getItem = React.useCallback((value: TreeItemValue) => headlessTree.get(value), [headlessTree]);\n\n return React.useMemo<HeadlessFlatTreeReturn<Props>>(\n () => ({\n navigate: navigation.navigate,\n getTreeProps,\n getNextNavigableItem,\n getElementFromItem,\n items,\n getItem,\n }),\n [navigation.navigate, getTreeProps, getNextNavigableItem, getElementFromItem, items, getItem],\n );\n}\n\n/** @internal */\nfunction noop() {\n /* noop */\n}\n"],"names":["useEventCallback","useMergedRefs","React","createHeadlessTree","treeDataTypes","useFlatTreeNavigation","createNextOpenItems","useControllableOpenItems","dataTreeItemValueAttrName","ImmutableSet","createNextFlatCheckedItems","useFlatControllableCheckedItems","ImmutableMap","useHeadlessFlatTree_unstable","props","options","headlessTree","useMemo","openItems","setOpenItems","checkedItems","setCheckedItems","navigation","treeRef","useRef","handleOpenChange","event","data","nextOpenItems","onOpenChange","dangerouslyGetInternalSet","handleCheckedChange","nextCheckedItems","onCheckedChange","dangerouslyGetInternalMap","getNextNavigableItem","visibleItems","item","get","value","type","TypeAhead","ArrowLeft","parentValue","ArrowRight","index","End","length","Home","ArrowDown","ArrowUp","getElementFromItem","useCallback","current","querySelector","ref","rootRef","getTreeProps","selectionMode","onNavigation","noop","items","getItem","navigate"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
|
|
@@ -8,7 +8,7 @@ export function useNestedCheckedItems(props) {
|
|
|
8
8
|
}
|
|
9
9
|
export function createNextNestedCheckedItems(data, previousCheckedItems) {
|
|
10
10
|
if (data.selectionMode === 'single') {
|
|
11
|
-
return ImmutableMap.
|
|
11
|
+
return ImmutableMap.from([
|
|
12
12
|
[
|
|
13
13
|
data.value,
|
|
14
14
|
data.checked
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["useNestedControllableCheckedItems.ts"],"sourcesContent":["import * as React from 'react';\nimport type { TreeCheckedChangeData, TreeProps } from './Tree.types';\nimport { ImmutableMap } from '../../utils/ImmutableMap';\nimport { createCheckedItems } from '../../utils/createCheckedItems';\nimport { TreeItemValue } from '../TreeItem/TreeItem.types';\n\nexport function useNestedCheckedItems(props: Pick<TreeProps, 'checkedItems'>) {\n return React.useMemo(() => createCheckedItems(props.checkedItems), [props.checkedItems]);\n}\n\nexport function createNextNestedCheckedItems(\n data: Pick<TreeCheckedChangeData, 'selectionMode' | 'value' | 'checked'>,\n previousCheckedItems: ImmutableMap<TreeItemValue, 'mixed' | boolean>,\n): ImmutableMap<TreeItemValue, 'mixed' | boolean> {\n if (data.selectionMode === 'single') {\n return ImmutableMap.
|
|
1
|
+
{"version":3,"sources":["useNestedControllableCheckedItems.ts"],"sourcesContent":["import * as React from 'react';\nimport type { TreeCheckedChangeData, TreeProps } from './Tree.types';\nimport { ImmutableMap } from '../../utils/ImmutableMap';\nimport { createCheckedItems } from '../../utils/createCheckedItems';\nimport { TreeItemValue } from '../TreeItem/TreeItem.types';\n\nexport function useNestedCheckedItems(props: Pick<TreeProps, 'checkedItems'>) {\n return React.useMemo(() => createCheckedItems(props.checkedItems), [props.checkedItems]);\n}\n\nexport function createNextNestedCheckedItems(\n data: Pick<TreeCheckedChangeData, 'selectionMode' | 'value' | 'checked'>,\n previousCheckedItems: ImmutableMap<TreeItemValue, 'mixed' | boolean>,\n): ImmutableMap<TreeItemValue, 'mixed' | boolean> {\n if (data.selectionMode === 'single') {\n return ImmutableMap.from([[data.value, data.checked]]);\n }\n if (data.selectionMode === 'multiselect') {\n return previousCheckedItems.set(data.value, data.checked);\n }\n return previousCheckedItems;\n}\n"],"names":["React","ImmutableMap","createCheckedItems","useNestedCheckedItems","props","useMemo","checkedItems","createNextNestedCheckedItems","data","previousCheckedItems","selectionMode","from","value","checked","set"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;","mappings":"AAAA,YAAYA,WAAW,QAAQ;AAE/B,SAASC,YAAY,QAAQ,2BAA2B;AACxD,SAASC,kBAAkB,QAAQ,iCAAiC;AAGpE,OAAO,SAASC,sBAAsBC,KAAsC;IAC1E,OAAOJ,MAAMK,OAAO,CAAC,IAAMH,mBAAmBE,MAAME,YAAY,GAAG;QAACF,MAAME,YAAY;KAAC;AACzF;AAEA,OAAO,SAASC,6BACdC,IAAwE,EACxEC,oBAAoE;IAEpE,IAAID,KAAKE,aAAa,KAAK,UAAU;QACnC,OAAOT,aAAaU,IAAI,CAAC;YAAC;gBAACH,KAAKI,KAAK;gBAAEJ,KAAKK,OAAO;aAAC;SAAC;IACvD;IACA,IAAIL,KAAKE,aAAa,KAAK,eAAe;QACxC,OAAOD,qBAAqBK,GAAG,CAACN,KAAKI,KAAK,EAAEJ,KAAKK,OAAO;IAC1D;IACA,OAAOJ;AACT"}
|
|
@@ -7,6 +7,8 @@ import { useRootTree } from '../../hooks/useRootTree';
|
|
|
7
7
|
import { useSubtree } from '../../hooks/useSubtree';
|
|
8
8
|
import { useTreeNavigation } from '../../hooks/useTreeNavigation';
|
|
9
9
|
import { useTreeContext_unstable } from '../../contexts/treeContext';
|
|
10
|
+
import { ImmutableSet } from '../../utils/ImmutableSet';
|
|
11
|
+
import { ImmutableMap } from '../../utils/ImmutableMap';
|
|
10
12
|
export const useTree_unstable = (props, ref)=>{
|
|
11
13
|
'use no memo';
|
|
12
14
|
const isRoot = React.useContext(SubtreeContext) === undefined;
|
|
@@ -29,7 +31,7 @@ function useNestedRootTree(props, ref) {
|
|
|
29
31
|
const nextOpenItems = createNextOpenItems(data, openItems);
|
|
30
32
|
(_props_onOpenChange = props.onOpenChange) === null || _props_onOpenChange === void 0 ? void 0 : _props_onOpenChange.call(props, event, {
|
|
31
33
|
...data,
|
|
32
|
-
openItems:
|
|
34
|
+
openItems: ImmutableSet.dangerouslyGetInternalSet(nextOpenItems)
|
|
33
35
|
});
|
|
34
36
|
setOpenItems(nextOpenItems);
|
|
35
37
|
}),
|
|
@@ -47,7 +49,7 @@ function useNestedRootTree(props, ref) {
|
|
|
47
49
|
const nextCheckedItems = createNextNestedCheckedItems(data, checkedItems);
|
|
48
50
|
(_props_onCheckedChange = props.onCheckedChange) === null || _props_onCheckedChange === void 0 ? void 0 : _props_onCheckedChange.call(props, event, {
|
|
49
51
|
...data,
|
|
50
|
-
checkedItems:
|
|
52
|
+
checkedItems: ImmutableMap.dangerouslyGetInternalMap(nextCheckedItems)
|
|
51
53
|
});
|
|
52
54
|
})
|
|
53
55
|
}, useMergedRefs(ref, navigation.treeRef)), {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["useTree.ts"],"sourcesContent":["import * as React from 'react';\nimport { useEventCallback, useMergedRefs } from '@fluentui/react-utilities';\nimport type { TreeProps, TreeState } from './Tree.types';\nimport { createNextOpenItems, useControllableOpenItems } from '../../hooks/useControllableOpenItems';\nimport { createNextNestedCheckedItems, useNestedCheckedItems } from './useNestedControllableCheckedItems';\nimport { SubtreeContext } from '../../contexts/subtreeContext';\nimport { useRootTree } from '../../hooks/useRootTree';\nimport { useSubtree } from '../../hooks/useSubtree';\nimport { useTreeNavigation } from '../../hooks/useTreeNavigation';\nimport { useTreeContext_unstable } from '../../contexts/treeContext';\n\nexport const useTree_unstable = (props: TreeProps, ref: React.Ref<HTMLElement>): TreeState => {\n 'use no memo';\n\n const isRoot = React.useContext(SubtreeContext) === undefined;\n // as level is static, this doesn't break rule of hooks\n // and if this becomes an issue later on, this can be easily converted\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return isRoot ? useNestedRootTree(props, ref) : useNestedSubtree(props, ref);\n};\n\nfunction useNestedRootTree(props: TreeProps, ref: React.Ref<HTMLElement>): TreeState {\n 'use no memo';\n\n const [openItems, setOpenItems] = useControllableOpenItems(props);\n const checkedItems = useNestedCheckedItems(props);\n const navigation = useTreeNavigation();\n\n return Object.assign(\n useRootTree(\n {\n ...props,\n openItems,\n checkedItems,\n onOpenChange: useEventCallback((event, data) => {\n const nextOpenItems = createNextOpenItems(data, openItems);\n props.onOpenChange?.(event, {\n ...data,\n openItems:
|
|
1
|
+
{"version":3,"sources":["useTree.ts"],"sourcesContent":["import * as React from 'react';\nimport { useEventCallback, useMergedRefs } from '@fluentui/react-utilities';\nimport type { TreeProps, TreeState } from './Tree.types';\nimport { createNextOpenItems, useControllableOpenItems } from '../../hooks/useControllableOpenItems';\nimport { createNextNestedCheckedItems, useNestedCheckedItems } from './useNestedControllableCheckedItems';\nimport { SubtreeContext } from '../../contexts/subtreeContext';\nimport { useRootTree } from '../../hooks/useRootTree';\nimport { useSubtree } from '../../hooks/useSubtree';\nimport { useTreeNavigation } from '../../hooks/useTreeNavigation';\nimport { useTreeContext_unstable } from '../../contexts/treeContext';\nimport { ImmutableSet } from '../../utils/ImmutableSet';\nimport { ImmutableMap } from '../../utils/ImmutableMap';\n\nexport const useTree_unstable = (props: TreeProps, ref: React.Ref<HTMLElement>): TreeState => {\n 'use no memo';\n\n const isRoot = React.useContext(SubtreeContext) === undefined;\n // as level is static, this doesn't break rule of hooks\n // and if this becomes an issue later on, this can be easily converted\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return isRoot ? useNestedRootTree(props, ref) : useNestedSubtree(props, ref);\n};\n\nfunction useNestedRootTree(props: TreeProps, ref: React.Ref<HTMLElement>): TreeState {\n 'use no memo';\n\n const [openItems, setOpenItems] = useControllableOpenItems(props);\n const checkedItems = useNestedCheckedItems(props);\n const navigation = useTreeNavigation();\n\n return Object.assign(\n useRootTree(\n {\n ...props,\n openItems,\n checkedItems,\n onOpenChange: useEventCallback((event, data) => {\n const nextOpenItems = createNextOpenItems(data, openItems);\n props.onOpenChange?.(event, {\n ...data,\n openItems: ImmutableSet.dangerouslyGetInternalSet(nextOpenItems),\n });\n setOpenItems(nextOpenItems);\n }),\n onNavigation: useEventCallback((event, data) => {\n props.onNavigation?.(event, data);\n if (!event.isDefaultPrevented()) {\n navigation.navigate(data, {\n preventScroll: data.isScrollPrevented(),\n });\n }\n }),\n onCheckedChange: useEventCallback((event, data) => {\n const nextCheckedItems = createNextNestedCheckedItems(data, checkedItems);\n props.onCheckedChange?.(event, {\n ...data,\n checkedItems: ImmutableMap.dangerouslyGetInternalMap(nextCheckedItems),\n });\n }),\n },\n useMergedRefs(ref, navigation.treeRef),\n ),\n { treeType: 'nested' } as const,\n );\n}\n\nfunction useNestedSubtree(props: TreeProps, ref: React.Ref<HTMLElement>): TreeState {\n 'use no memo';\n\n if (process.env.NODE_ENV === 'development') {\n // this doesn't break rule of hooks, as environment is a static value\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const treeType = useTreeContext_unstable(ctx => ctx.treeType);\n if (treeType === 'flat') {\n throw new Error(/* #__DE-INDENT__ */ `\n @fluentui/react-tree [useTree]:\n Subtrees are not allowed in a FlatTree!\n You cannot use a <Tree> component inside of a <FlatTree> component!\n `);\n }\n }\n return useSubtree(props, ref);\n}\n"],"names":["React","useEventCallback","useMergedRefs","createNextOpenItems","useControllableOpenItems","createNextNestedCheckedItems","useNestedCheckedItems","SubtreeContext","useRootTree","useSubtree","useTreeNavigation","useTreeContext_unstable","ImmutableSet","ImmutableMap","useTree_unstable","props","ref","isRoot","useContext","undefined","useNestedRootTree","useNestedSubtree","openItems","setOpenItems","checkedItems","navigation","Object","assign","onOpenChange","event","data","nextOpenItems","dangerouslyGetInternalSet","onNavigation","isDefaultPrevented","navigate","preventScroll","isScrollPrevented","onCheckedChange","nextCheckedItems","dangerouslyGetInternalMap","treeRef","treeType","process","env","NODE_ENV","ctx","Error"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAAA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,gBAAgB,EAAEC,aAAa,QAAQ,4BAA4B;AAE5E,SAASC,mBAAmB,EAAEC,wBAAwB,QAAQ,uCAAuC;AACrG,SAASC,4BAA4B,EAAEC,qBAAqB,QAAQ,sCAAsC;AAC1G,SAASC,cAAc,QAAQ,gCAAgC;AAC/D,SAASC,WAAW,QAAQ,0BAA0B;AACtD,SAASC,UAAU,QAAQ,yBAAyB;AACpD,SAASC,iBAAiB,QAAQ,gCAAgC;AAClE,SAASC,uBAAuB,QAAQ,6BAA6B;AACrE,SAASC,YAAY,QAAQ,2BAA2B;AACxD,SAASC,YAAY,QAAQ,2BAA2B;AAExD,OAAO,MAAMC,mBAAmB,CAACC,OAAkBC;IACjD;IAEA,MAAMC,SAASjB,MAAMkB,UAAU,CAACX,oBAAoBY;IACpD,uDAAuD;IACvD,sEAAsE;IACtE,sDAAsD;IACtD,OAAOF,SAASG,kBAAkBL,OAAOC,OAAOK,iBAAiBN,OAAOC;AAC1E,EAAE;AAEF,SAASI,kBAAkBL,KAAgB,EAAEC,GAA2B;IACtE;IAEA,MAAM,CAACM,WAAWC,aAAa,GAAGnB,yBAAyBW;IAC3D,MAAMS,eAAelB,sBAAsBS;IAC3C,MAAMU,aAAaf;IAEnB,OAAOgB,OAAOC,MAAM,CAClBnB,YACE;QACE,GAAGO,KAAK;QACRO;QACAE;QACAI,cAAc3B,iBAAiB,CAAC4B,OAAOC;gBAErCf;YADA,MAAMgB,gBAAgB5B,oBAAoB2B,MAAMR;aAChDP,sBAAAA,MAAMa,YAAY,cAAlBb,0CAAAA,yBAAAA,OAAqBc,OAAO;gBAC1B,GAAGC,IAAI;gBACPR,WAAWV,aAAaoB,yBAAyB,CAACD;YACpD;YACAR,aAAaQ;QACf;QACAE,cAAchC,iBAAiB,CAAC4B,OAAOC;gBACrCf;aAAAA,sBAAAA,MAAMkB,YAAY,cAAlBlB,0CAAAA,yBAAAA,OAAqBc,OAAOC;YAC5B,IAAI,CAACD,MAAMK,kBAAkB,IAAI;gBAC/BT,WAAWU,QAAQ,CAACL,MAAM;oBACxBM,eAAeN,KAAKO,iBAAiB;gBACvC;YACF;QACF;QACAC,iBAAiBrC,iBAAiB,CAAC4B,OAAOC;gBAExCf;YADA,MAAMwB,mBAAmBlC,6BAA6ByB,MAAMN;aAC5DT,yBAAAA,MAAMuB,eAAe,cAArBvB,6CAAAA,4BAAAA,OAAwBc,OAAO;gBAC7B,GAAGC,IAAI;gBACPN,cAAcX,aAAa2B,yBAAyB,CAACD;YACvD;QACF;IACF,GACArC,cAAcc,KAAKS,WAAWgB,OAAO,IAEvC;QAAEC,UAAU;IAAS;AAEzB;AAEA,SAASrB,iBAAiBN,KAAgB,EAAEC,GAA2B;IACrE;IAEA,IAAI2B,QAAQC,GAAG,CAACC,QAAQ,KAAK,eAAe;QAC1C,qEAAqE;QACrE,sDAAsD;QACtD,MAAMH,WAAW/B,wBAAwBmC,CAAAA,MAAOA,IAAIJ,QAAQ;QAC5D,IAAIA,aAAa,QAAQ;YACvB,MAAM,IAAIK,MAA2B,CAAC;;mEAItC,CAAC;QACH;IACF;IACA,OAAOtC,WAAWM,OAAOC;AAC3B"}
|
|
@@ -1,28 +1,19 @@
|
|
|
1
1
|
import { useControllableState } from '@fluentui/react-utilities';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { ImmutableSet } from '../utils/ImmutableSet';
|
|
4
|
-
import { createOpenItems } from '../utils/createOpenItems';
|
|
5
4
|
/**
|
|
6
5
|
* @internal
|
|
7
6
|
*/ export function useControllableOpenItems(props) {
|
|
8
7
|
return useControllableState({
|
|
9
|
-
state: React.useMemo(()=>props.openItems &&
|
|
8
|
+
state: React.useMemo(()=>props.openItems && ImmutableSet.from(props.openItems), [
|
|
10
9
|
props.openItems
|
|
11
10
|
]),
|
|
12
|
-
defaultState: props.defaultOpenItems && (()=>
|
|
11
|
+
defaultState: props.defaultOpenItems && (()=>ImmutableSet.from(props.defaultOpenItems)),
|
|
13
12
|
initialState: ImmutableSet.empty
|
|
14
13
|
});
|
|
15
14
|
}
|
|
16
15
|
/**
|
|
17
16
|
* @internal
|
|
18
17
|
*/ export function createNextOpenItems(data, previousOpenItems) {
|
|
19
|
-
|
|
20
|
-
return previousOpenItems;
|
|
21
|
-
}
|
|
22
|
-
const previousOpenItemsHasId = previousOpenItems.has(data.value);
|
|
23
|
-
if (data.open ? previousOpenItemsHasId : !previousOpenItemsHasId) {
|
|
24
|
-
return previousOpenItems;
|
|
25
|
-
}
|
|
26
|
-
const nextOpenItems = ImmutableSet.create(previousOpenItems);
|
|
27
|
-
return data.open ? nextOpenItems.add(data.value) : nextOpenItems.delete(data.value);
|
|
18
|
+
return data.open ? previousOpenItems.add(data.value) : previousOpenItems.delete(data.value);
|
|
28
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["useControllableOpenItems.ts"],"sourcesContent":["import { useControllableState } from '@fluentui/react-utilities';\nimport * as React from 'react';\nimport { ImmutableSet } from '../utils/ImmutableSet';\nimport type { TreeItemValue } from '../components/TreeItem/TreeItem.types';\nimport {
|
|
1
|
+
{"version":3,"sources":["useControllableOpenItems.ts"],"sourcesContent":["import { useControllableState } from '@fluentui/react-utilities';\nimport * as React from 'react';\nimport { ImmutableSet } from '../utils/ImmutableSet';\nimport type { TreeItemValue } from '../components/TreeItem/TreeItem.types';\nimport { TreeOpenChangeData, TreeProps } from '../Tree';\n\n/**\n * @internal\n */\nexport function useControllableOpenItems(props: Pick<TreeProps, 'openItems' | 'defaultOpenItems'>) {\n return useControllableState({\n state: React.useMemo(() => props.openItems && ImmutableSet.from(props.openItems), [props.openItems]),\n defaultState: props.defaultOpenItems && (() => ImmutableSet.from(props.defaultOpenItems)),\n initialState: ImmutableSet.empty,\n });\n}\n\n/**\n * @internal\n */\nexport function createNextOpenItems(\n data: Pick<TreeOpenChangeData, 'value' | 'open'>,\n previousOpenItems: ImmutableSet<TreeItemValue>,\n): ImmutableSet<TreeItemValue> {\n return data.open ? previousOpenItems.add(data.value) : previousOpenItems.delete(data.value);\n}\n"],"names":["useControllableState","React","ImmutableSet","useControllableOpenItems","props","state","useMemo","openItems","from","defaultState","defaultOpenItems","initialState","empty","createNextOpenItems","data","previousOpenItems","open","add","value","delete"],"rangeMappings":";;;;;;;;;;;;;;;;;;","mappings":"AAAA,SAASA,oBAAoB,QAAQ,4BAA4B;AACjE,YAAYC,WAAW,QAAQ;AAC/B,SAASC,YAAY,QAAQ,wBAAwB;AAIrD;;CAEC,GACD,OAAO,SAASC,yBAAyBC,KAAwD;IAC/F,OAAOJ,qBAAqB;QAC1BK,OAAOJ,MAAMK,OAAO,CAAC,IAAMF,MAAMG,SAAS,IAAIL,aAAaM,IAAI,CAACJ,MAAMG,SAAS,GAAG;YAACH,MAAMG,SAAS;SAAC;QACnGE,cAAcL,MAAMM,gBAAgB,IAAK,CAAA,IAAMR,aAAaM,IAAI,CAACJ,MAAMM,gBAAgB,CAAA;QACvFC,cAAcT,aAAaU,KAAK;IAClC;AACF;AAEA;;CAEC,GACD,OAAO,SAASC,oBACdC,IAAgD,EAChDC,iBAA8C;IAE9C,OAAOD,KAAKE,IAAI,GAAGD,kBAAkBE,GAAG,CAACH,KAAKI,KAAK,IAAIH,kBAAkBI,MAAM,CAACL,KAAKI,KAAK;AAC5F"}
|
package/lib/hooks/useRootTree.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { getIntrinsicElementProps, useEventCallback, slot } from '@fluentui/react-utilities';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { Collapse } from '@fluentui/react-motion-components-preview';
|
|
4
|
-
import { createOpenItems } from '../utils/createOpenItems';
|
|
5
4
|
import { createCheckedItems } from '../utils/createCheckedItems';
|
|
6
5
|
import { treeDataTypes } from '../utils/tokens';
|
|
7
6
|
import { createNextOpenItems } from './useControllableOpenItems';
|
|
7
|
+
import { ImmutableSet } from '../utils/ImmutableSet';
|
|
8
|
+
import { ImmutableMap } from '../utils/ImmutableMap';
|
|
8
9
|
/**
|
|
9
10
|
* Create the state required to render the root level tree.
|
|
10
11
|
*
|
|
@@ -13,7 +14,7 @@ import { createNextOpenItems } from './useControllableOpenItems';
|
|
|
13
14
|
*/ export function useRootTree(props, ref) {
|
|
14
15
|
warnIfNoProperPropsRootTree(props);
|
|
15
16
|
const { appearance = 'subtle', size = 'medium', selectionMode = 'none' } = props;
|
|
16
|
-
const openItems = React.useMemo(()=>
|
|
17
|
+
const openItems = React.useMemo(()=>ImmutableSet.from(props.openItems), [
|
|
17
18
|
props.openItems
|
|
18
19
|
]);
|
|
19
20
|
const checkedItems = React.useMemo(()=>createCheckedItems(props.checkedItems), [
|
|
@@ -23,7 +24,7 @@ import { createNextOpenItems } from './useControllableOpenItems';
|
|
|
23
24
|
var _props_onOpenChange;
|
|
24
25
|
(_props_onOpenChange = props.onOpenChange) === null || _props_onOpenChange === void 0 ? void 0 : _props_onOpenChange.call(props, request.event, {
|
|
25
26
|
...request,
|
|
26
|
-
openItems: createNextOpenItems(request, openItems)
|
|
27
|
+
openItems: ImmutableSet.dangerouslyGetInternalSet(createNextOpenItems(request, openItems))
|
|
27
28
|
});
|
|
28
29
|
};
|
|
29
30
|
const requestCheckedChange = (request)=>{
|
|
@@ -34,7 +35,7 @@ import { createNextOpenItems } from './useControllableOpenItems';
|
|
|
34
35
|
(_props_onCheckedChange = props.onCheckedChange) === null || _props_onCheckedChange === void 0 ? void 0 : _props_onCheckedChange.call(props, request.event, {
|
|
35
36
|
...request,
|
|
36
37
|
selectionMode,
|
|
37
|
-
checkedItems:
|
|
38
|
+
checkedItems: ImmutableMap.dangerouslyGetInternalMap(checkedItems)
|
|
38
39
|
});
|
|
39
40
|
};
|
|
40
41
|
const requestNavigation = (request)=>{
|