-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Expand file tree
/
Copy pathuseTree.ts
More file actions
115 lines (104 loc) · 4.46 KB
/
useTree.ts
File metadata and controls
115 lines (104 loc) · 4.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
'use client';
import * as React from 'react';
import { useEventCallback, useMergedRefs } from '@fluentui/react-utilities';
import type { TreeBaseProps, TreeBaseState, TreeProps, TreeState } from './Tree.types';
import { createNextOpenItems, useControllableOpenItems } from '../../hooks/useControllableOpenItems';
import { createNextNestedCheckedItems, useNestedCheckedItems } from './useNestedControllableCheckedItems';
import { SubtreeContext } from '../../contexts/subtreeContext';
import { useRootTree } from '../../hooks/useRootTree';
import { useSubtree } from '../../hooks/useSubtree';
import { useTreeNavigation } from '../../hooks/useTreeNavigation';
import { useTreeContext_unstable } from '../../contexts/treeContext';
import { ImmutableSet } from '../../utils/ImmutableSet';
import { ImmutableMap } from '../../utils/ImmutableMap';
/**
* Create the base state required to render Tree, without design-only props.
*
* @param props - props from this instance of Tree (without appearance and size)
* @param ref - reference to root HTMLElement of Tree
*/
export const useTreeBase_unstable = (props: TreeBaseProps, ref: React.Ref<HTMLElement>): TreeBaseState => {
'use no memo';
const isRoot = React.useContext(SubtreeContext) === undefined;
// as level is static, this doesn't break rule of hooks
// and if this becomes an issue later on, this can be easily converted
// eslint-disable-next-line react-hooks/rules-of-hooks
return isRoot ? useNestedRootTreeBase(props, ref) : (useNestedSubtree(props as TreeProps, ref) as TreeBaseState);
};
/**
* Create the state required to render Tree.
*
* The returned state can be modified with hooks such as useTreeStyles_unstable,
* before being passed to renderTree_unstable.
*
* @param props - props from this instance of Tree
* @param ref - reference to root HTMLElement of Tree
*/
export const useTree_unstable = (props: TreeProps, ref: React.Ref<HTMLElement>): TreeState => {
'use no memo';
const { appearance = 'subtle', size = 'medium' } = props;
const baseState = useTreeBase_unstable(props, ref);
if (baseState.contextType === 'root') {
return { ...baseState, appearance, size } as unknown as TreeState;
}
return baseState as unknown as TreeState;
};
function useNestedRootTreeBase(props: TreeBaseProps, ref: React.Ref<HTMLElement>): TreeBaseState {
'use no memo';
const [openItems, setOpenItems] = useControllableOpenItems(props);
const checkedItems = useNestedCheckedItems(props);
const navigation = useTreeNavigation(props.navigationMode);
const fullState = useRootTree(
{
...props,
openItems,
checkedItems,
onOpenChange: useEventCallback((event, data) => {
const nextOpenItems = createNextOpenItems(data, openItems);
props.onOpenChange?.(event, {
...data,
openItems: ImmutableSet.dangerouslyGetInternalSet(nextOpenItems),
});
setOpenItems(nextOpenItems);
}),
onNavigation: useEventCallback((event, data) => {
props.onNavigation?.(event, data);
if (!event.isDefaultPrevented()) {
navigation.navigate(data, {
preventScroll: data.isScrollPrevented(),
});
}
}),
onCheckedChange: useEventCallback((event, data) => {
const nextCheckedItems = createNextNestedCheckedItems(data, checkedItems);
props.onCheckedChange?.(event, {
...data,
checkedItems: ImmutableMap.dangerouslyGetInternalMap(nextCheckedItems),
});
}),
},
useMergedRefs(ref, navigation.treeRef),
);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { appearance: _appearance, size: _size, ...baseRootState } = fullState;
return Object.assign(baseRootState, {
treeType: 'nested',
forceUpdateRovingTabIndex: navigation.forceUpdateRovingTabIndex,
} as const) as unknown as TreeBaseState;
}
function useNestedSubtree(props: TreeProps, ref: React.Ref<HTMLElement>): TreeState {
'use no memo';
if (process.env.NODE_ENV === 'development') {
// this doesn't break rule of hooks, as environment is a static value
// eslint-disable-next-line react-hooks/rules-of-hooks
const treeType = useTreeContext_unstable(ctx => ctx.treeType);
if (treeType === 'flat') {
throw new Error(/* #__DE-INDENT__ */ `
@fluentui/react-tree [useTree]:
Subtrees are not allowed in a FlatTree!
You cannot use a <Tree> component inside of a <FlatTree> component!
`);
}
}
return useSubtree(props, ref);
}