@mui/x-tree-view 7.1.0 → 7.2.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/CHANGELOG.md +261 -4
- package/README.md +1 -1
- package/RichTreeView/RichTreeView.d.ts +2 -2
- package/RichTreeView/RichTreeView.js +7 -5
- package/RichTreeView/RichTreeView.types.d.ts +3 -3
- package/SimpleTreeView/SimpleTreeView.js +4 -2
- package/TreeItem/TreeItem.js +1 -1
- package/TreeItem2/TreeItem2.d.ts +5 -1
- package/TreeItem2/TreeItem2.js +0 -1
- package/TreeView/TreeView.js +2 -1
- package/index.js +1 -1
- package/internals/TreeViewProvider/TreeViewChildrenItemProvider.d.ts +16 -0
- package/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +57 -0
- package/internals/TreeViewProvider/TreeViewContext.d.ts +2 -0
- package/internals/TreeViewProvider/TreeViewProvider.js +2 -3
- package/internals/TreeViewProvider/TreeViewProvider.types.d.ts +3 -1
- package/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +7 -8
- package/internals/index.d.ts +18 -8
- package/internals/index.js +11 -0
- package/internals/models/plugin.d.ts +14 -6
- package/internals/models/treeView.d.ts +1 -2
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +33 -19
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +12 -2
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +25 -26
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +12 -4
- package/internals/plugins/useTreeViewId/useTreeViewId.js +5 -7
- package/internals/plugins/useTreeViewId/useTreeViewId.types.d.ts +1 -1
- package/internals/plugins/useTreeViewItems/useTreeViewItems.js +61 -51
- package/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +30 -21
- package/internals/plugins/useTreeViewItems/useTreeViewItems.utils.d.ts +4 -0
- package/internals/plugins/useTreeViewItems/useTreeViewItems.utils.js +8 -0
- package/internals/plugins/useTreeViewJSXItems/index.d.ts +1 -1
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +67 -42
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.types.d.ts +5 -4
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +20 -18
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +11 -22
- package/internals/useTreeView/useTreeView.js +21 -3
- package/internals/useTreeView/useTreeViewModels.js +2 -2
- package/internals/utils/tree.d.ts +8 -0
- package/internals/utils/tree.js +137 -0
- package/modern/RichTreeView/RichTreeView.js +7 -5
- package/modern/SimpleTreeView/SimpleTreeView.js +4 -2
- package/modern/TreeItem/TreeItem.js +1 -1
- package/modern/TreeItem2/TreeItem2.js +0 -1
- package/modern/TreeView/TreeView.js +2 -1
- package/modern/index.js +1 -1
- package/modern/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +57 -0
- package/modern/internals/TreeViewProvider/TreeViewProvider.js +2 -3
- package/modern/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +7 -8
- package/modern/internals/index.js +11 -0
- package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +33 -19
- package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +25 -26
- package/modern/internals/plugins/useTreeViewId/useTreeViewId.js +5 -7
- package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.js +61 -51
- package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.utils.js +8 -0
- package/modern/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +67 -42
- package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +20 -18
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +11 -22
- package/modern/internals/useTreeView/useTreeView.js +21 -3
- package/modern/internals/useTreeView/useTreeViewModels.js +2 -2
- package/modern/internals/utils/tree.js +137 -0
- package/modern/useTreeItem2/useTreeItem2.js +1 -1
- package/node/RichTreeView/RichTreeView.js +7 -5
- package/node/SimpleTreeView/SimpleTreeView.js +4 -2
- package/node/TreeItem/TreeItem.js +1 -1
- package/node/TreeItem2/TreeItem2.js +0 -1
- package/node/TreeView/TreeView.js +2 -1
- package/node/index.js +1 -1
- package/node/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +67 -0
- package/node/internals/TreeViewProvider/TreeViewProvider.js +2 -3
- package/node/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +7 -8
- package/node/internals/index.js +70 -0
- package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +33 -19
- package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +25 -26
- package/node/internals/plugins/useTreeViewId/useTreeViewId.js +5 -7
- package/node/internals/plugins/useTreeViewItems/useTreeViewItems.js +61 -51
- package/node/internals/plugins/useTreeViewItems/useTreeViewItems.utils.js +15 -0
- package/node/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +67 -42
- package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +20 -18
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +11 -22
- package/node/internals/useTreeView/useTreeView.js +21 -3
- package/node/internals/useTreeView/useTreeViewModels.js +2 -2
- package/node/internals/utils/tree.js +148 -0
- package/node/useTreeItem2/useTreeItem2.js +1 -1
- package/package.json +2 -2
- package/useTreeItem2/useTreeItem2.js +1 -1
- package/internals/TreeViewProvider/DescendantProvider.d.ts +0 -38
- package/internals/TreeViewProvider/DescendantProvider.js +0 -176
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.d.ts +0 -17
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +0 -55
- package/internals/useTreeView/useTreeView.utils.d.ts +0 -9
- package/internals/useTreeView/useTreeView.utils.js +0 -46
- package/modern/internals/TreeViewProvider/DescendantProvider.js +0 -176
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +0 -55
- package/modern/internals/useTreeView/useTreeView.utils.js +0 -46
- package/node/internals/TreeViewProvider/DescendantProvider.js +0 -185
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +0 -62
- package/node/internals/useTreeView/useTreeView.utils.js +0 -58
package/internals/index.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
export { useTreeView } from './useTreeView';
|
|
2
2
|
export { TreeViewProvider } from './TreeViewProvider';
|
|
3
|
+
export { unstable_resetCleanupTracking } from './hooks/useInstanceEventHandler';
|
|
4
|
+
// Plugins
|
|
3
5
|
export { DEFAULT_TREE_VIEW_PLUGINS } from './plugins/defaultPlugins';
|
|
6
|
+
export { useTreeViewExpansion } from './plugins/useTreeViewExpansion';
|
|
7
|
+
export { useTreeViewSelection } from './plugins/useTreeViewSelection';
|
|
8
|
+
export { useTreeViewFocus } from './plugins/useTreeViewFocus';
|
|
9
|
+
export { useTreeViewKeyboardNavigation } from './plugins/useTreeViewKeyboardNavigation';
|
|
10
|
+
export { useTreeViewId } from './plugins/useTreeViewId';
|
|
11
|
+
export { useTreeViewIcons } from './plugins/useTreeViewIcons';
|
|
12
|
+
export { useTreeViewItems } from './plugins/useTreeViewItems';
|
|
13
|
+
export { useTreeViewJSXItems } from './plugins/useTreeViewJSXItems';
|
|
14
|
+
export { buildWarning } from './utils/warning';
|
|
4
15
|
export { extractPluginParamsFromProps } from './utils/extractPluginParamsFromProps';
|
|
@@ -7,7 +7,6 @@ import type { TreeViewCorePluginsSignature } from '../corePlugins';
|
|
|
7
7
|
import { TreeViewItemId } from '../../models';
|
|
8
8
|
export interface TreeViewPluginOptions<TSignature extends TreeViewAnyPluginSignature> {
|
|
9
9
|
instance: TreeViewUsedInstance<TSignature>;
|
|
10
|
-
publicAPI: TreeViewUsedPublicAPI<TSignature>;
|
|
11
10
|
params: TreeViewUsedDefaultizedParams<TSignature>;
|
|
12
11
|
state: TreeViewUsedState<TSignature>;
|
|
13
12
|
slots: TSignature['slots'];
|
|
@@ -23,7 +22,7 @@ type TreeViewModelsInitializer<TSignature extends TreeViewAnyPluginSignature> =
|
|
|
23
22
|
};
|
|
24
23
|
type TreeViewResponse<TSignature extends TreeViewAnyPluginSignature> = {
|
|
25
24
|
getRootProps?: <TOther extends EventHandlers = {}>(otherHandlers: TOther) => React.HTMLAttributes<HTMLUListElement>;
|
|
26
|
-
} & OptionalIfEmpty<'contextValue', TSignature['contextValue']>;
|
|
25
|
+
} & OptionalIfEmpty<'publicAPI', TSignature['publicAPI']> & OptionalIfEmpty<'instance', TSignature['instance']> & OptionalIfEmpty<'contextValue', TSignature['contextValue']>;
|
|
27
26
|
export type TreeViewPluginSignature<T extends {
|
|
28
27
|
params?: {};
|
|
29
28
|
defaultizedParams?: {};
|
|
@@ -97,7 +96,7 @@ type TreeViewUsedPlugins<TSignature extends TreeViewAnyPluginSignature> = [
|
|
|
97
96
|
TreeViewCorePluginsSignature,
|
|
98
97
|
...TSignature['dependantPlugins']
|
|
99
98
|
];
|
|
100
|
-
type TreeViewUsedParams<TSignature extends TreeViewAnyPluginSignature> = TSignature['params'] & MergePluginsProperty<TreeViewUsedPlugins<TSignature>, 'params'>;
|
|
99
|
+
export type TreeViewUsedParams<TSignature extends TreeViewAnyPluginSignature> = TSignature['params'] & MergePluginsProperty<TreeViewUsedPlugins<TSignature>, 'params'>;
|
|
101
100
|
type TreeViewUsedDefaultizedParams<TSignature extends TreeViewAnyPluginSignature> = TSignature['defaultizedParams'] & MergePluginsProperty<TreeViewUsedPlugins<TSignature>, 'defaultizedParams'>;
|
|
102
101
|
export type TreeViewUsedInstance<TSignature extends TreeViewAnyPluginSignature> = TSignature['instance'] & MergePluginsProperty<TreeViewUsedPlugins<TSignature>, 'instance'> & {
|
|
103
102
|
/**
|
|
@@ -135,8 +134,11 @@ export type TreeItemWrapper = (params: {
|
|
|
135
134
|
itemId: TreeViewItemId;
|
|
136
135
|
children: React.ReactNode;
|
|
137
136
|
}) => React.ReactNode;
|
|
137
|
+
export type TreeRootWrapper = (params: {
|
|
138
|
+
children: React.ReactNode;
|
|
139
|
+
}) => React.ReactNode;
|
|
138
140
|
export type TreeViewPlugin<TSignature extends TreeViewAnyPluginSignature> = {
|
|
139
|
-
(options: TreeViewPluginOptions<TSignature>):
|
|
141
|
+
(options: TreeViewPluginOptions<TSignature>): TreeViewResponse<TSignature>;
|
|
140
142
|
getDefaultizedParams?: (params: TreeViewUsedParams<TSignature>) => TSignature['defaultizedParams'];
|
|
141
143
|
getInitialState?: (params: TreeViewUsedDefaultizedParams<TSignature>) => TSignature['state'];
|
|
142
144
|
models?: TreeViewModelsInitializer<TSignature>;
|
|
@@ -144,9 +146,15 @@ export type TreeViewPlugin<TSignature extends TreeViewAnyPluginSignature> = {
|
|
|
144
146
|
itemPlugin?: TreeViewItemPlugin<any>;
|
|
145
147
|
/**
|
|
146
148
|
* Render function used to add React wrappers around the TreeItem.
|
|
147
|
-
* @param {
|
|
148
|
-
* @returns {React.ReactNode} The wrapped
|
|
149
|
+
* @param {{ nodeId: TreeViewItemId; children: React.ReactNode; }} params The params of the item.
|
|
150
|
+
* @returns {React.ReactNode} The wrapped item.
|
|
149
151
|
*/
|
|
150
152
|
wrapItem?: TreeItemWrapper;
|
|
153
|
+
/**
|
|
154
|
+
* Render function used to add React wrappers around the TreeView.
|
|
155
|
+
* @param {{ children: React.ReactNode; }} params The params of the root.
|
|
156
|
+
* @returns {React.ReactNode} The wrapped root.
|
|
157
|
+
*/
|
|
158
|
+
wrapRoot?: TreeRootWrapper;
|
|
151
159
|
};
|
|
152
160
|
export {};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { TreeViewAnyPluginSignature } from './plugin';
|
|
2
2
|
import type { MergePluginsProperty } from './helpers';
|
|
3
|
-
export interface
|
|
3
|
+
export interface TreeViewItemMeta {
|
|
4
4
|
id: string;
|
|
5
5
|
idAttribute: string | undefined;
|
|
6
|
-
index: number;
|
|
7
6
|
parentId: string | null;
|
|
8
7
|
expandable: boolean;
|
|
9
8
|
disabled: boolean;
|
|
@@ -1,39 +1,47 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import useEventCallback from '@mui/utils/useEventCallback';
|
|
4
|
-
import { populateInstance } from '../../useTreeView/useTreeView.utils';
|
|
5
4
|
export const useTreeViewExpansion = ({
|
|
6
5
|
instance,
|
|
7
6
|
params,
|
|
8
7
|
models
|
|
9
8
|
}) => {
|
|
9
|
+
const expandedItemsMap = React.useMemo(() => {
|
|
10
|
+
const temp = new Map();
|
|
11
|
+
models.expandedItems.value.forEach(id => {
|
|
12
|
+
temp.set(id, true);
|
|
13
|
+
});
|
|
14
|
+
return temp;
|
|
15
|
+
}, [models.expandedItems.value]);
|
|
10
16
|
const setExpandedItems = (event, value) => {
|
|
11
17
|
params.onExpandedItemsChange?.(event, value);
|
|
12
18
|
models.expandedItems.setControlledValue(value);
|
|
13
19
|
};
|
|
14
|
-
const isItemExpanded = React.useCallback(itemId =>
|
|
15
|
-
|
|
16
|
-
}, [models.expandedItems.value]);
|
|
17
|
-
const isItemExpandable = React.useCallback(itemId => !!instance.getNode(itemId)?.expandable, [instance]);
|
|
20
|
+
const isItemExpanded = React.useCallback(itemId => expandedItemsMap.has(itemId), [expandedItemsMap]);
|
|
21
|
+
const isItemExpandable = React.useCallback(itemId => !!instance.getItemMeta(itemId)?.expandable, [instance]);
|
|
18
22
|
const toggleItemExpansion = useEventCallback((event, itemId) => {
|
|
19
|
-
|
|
23
|
+
const isExpandedBefore = instance.isItemExpanded(itemId);
|
|
24
|
+
instance.setItemExpansion(event, itemId, !isExpandedBefore);
|
|
25
|
+
});
|
|
26
|
+
const setItemExpansion = useEventCallback((event, itemId, isExpanded) => {
|
|
27
|
+
const isExpandedBefore = instance.isItemExpanded(itemId);
|
|
28
|
+
if (isExpandedBefore === isExpanded) {
|
|
20
29
|
return;
|
|
21
30
|
}
|
|
22
|
-
const isExpandedBefore = models.expandedItems.value.indexOf(itemId) !== -1;
|
|
23
31
|
let newExpanded;
|
|
24
|
-
if (
|
|
25
|
-
newExpanded = models.expandedItems.value.filter(id => id !== itemId);
|
|
26
|
-
} else {
|
|
32
|
+
if (isExpanded) {
|
|
27
33
|
newExpanded = [itemId].concat(models.expandedItems.value);
|
|
34
|
+
} else {
|
|
35
|
+
newExpanded = models.expandedItems.value.filter(id => id !== itemId);
|
|
28
36
|
}
|
|
29
37
|
if (params.onItemExpansionToggle) {
|
|
30
|
-
params.onItemExpansionToggle(event, itemId,
|
|
38
|
+
params.onItemExpansionToggle(event, itemId, isExpanded);
|
|
31
39
|
}
|
|
32
40
|
setExpandedItems(event, newExpanded);
|
|
33
41
|
});
|
|
34
42
|
const expandAllSiblings = (event, itemId) => {
|
|
35
|
-
const
|
|
36
|
-
const siblings = instance.
|
|
43
|
+
const itemMeta = instance.getItemMeta(itemId);
|
|
44
|
+
const siblings = instance.getItemOrderedChildrenIds(itemMeta.parentId);
|
|
37
45
|
const diff = siblings.filter(child => instance.isItemExpandable(child) && !instance.isItemExpanded(child));
|
|
38
46
|
const newExpanded = models.expandedItems.value.concat(diff);
|
|
39
47
|
if (diff.length > 0) {
|
|
@@ -45,12 +53,18 @@ export const useTreeViewExpansion = ({
|
|
|
45
53
|
setExpandedItems(event, newExpanded);
|
|
46
54
|
}
|
|
47
55
|
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
return {
|
|
57
|
+
publicAPI: {
|
|
58
|
+
setItemExpansion
|
|
59
|
+
},
|
|
60
|
+
instance: {
|
|
61
|
+
isItemExpanded,
|
|
62
|
+
isItemExpandable,
|
|
63
|
+
setItemExpansion,
|
|
64
|
+
toggleItemExpansion,
|
|
65
|
+
expandAllSiblings
|
|
66
|
+
}
|
|
67
|
+
};
|
|
54
68
|
};
|
|
55
69
|
useTreeViewExpansion.models = {
|
|
56
70
|
expandedItems: {
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { DefaultizedProps, TreeViewPluginSignature } from '../../models';
|
|
3
3
|
import { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
4
|
-
export interface
|
|
4
|
+
export interface UseTreeViewExpansionPublicAPI {
|
|
5
|
+
/**
|
|
6
|
+
* Change the expansion status of a given item.
|
|
7
|
+
* @param {React.SyntheticEvent} event The UI event that triggered the change.
|
|
8
|
+
* @param {string} itemId The id of the item to modify.
|
|
9
|
+
* @param {boolean} isExpanded The new expansion status of the given item.
|
|
10
|
+
*/
|
|
11
|
+
setItemExpansion: (event: React.SyntheticEvent, itemId: string, isExpanded: boolean) => void;
|
|
12
|
+
}
|
|
13
|
+
export interface UseTreeViewExpansionInstance extends UseTreeViewExpansionPublicAPI {
|
|
5
14
|
isItemExpanded: (itemId: string) => boolean;
|
|
6
15
|
isItemExpandable: (itemId: string) => boolean;
|
|
7
|
-
toggleItemExpansion: (event: React.SyntheticEvent,
|
|
16
|
+
toggleItemExpansion: (event: React.SyntheticEvent, itemId: string) => void;
|
|
8
17
|
expandAllSiblings: (event: React.KeyboardEvent, itemId: string) => void;
|
|
9
18
|
}
|
|
10
19
|
export interface UseTreeViewExpansionParameters {
|
|
@@ -38,6 +47,7 @@ export type UseTreeViewExpansionSignature = TreeViewPluginSignature<{
|
|
|
38
47
|
params: UseTreeViewExpansionParameters;
|
|
39
48
|
defaultizedParams: UseTreeViewExpansionDefaultizedParameters;
|
|
40
49
|
instance: UseTreeViewExpansionInstance;
|
|
50
|
+
publicAPI: UseTreeViewExpansionPublicAPI;
|
|
41
51
|
modelNames: 'expandedItems';
|
|
42
52
|
dependantPlugins: [UseTreeViewItemsSignature];
|
|
43
53
|
}>;
|
|
@@ -2,13 +2,13 @@ import _extends from "@babel/runtime/helpers/esm/extends";
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import useEventCallback from '@mui/utils/useEventCallback';
|
|
4
4
|
import ownerDocument from '@mui/utils/ownerDocument';
|
|
5
|
-
import { populateInstance, populatePublicAPI } from '../../useTreeView/useTreeView.utils';
|
|
6
5
|
import { useInstanceEventHandler } from '../../hooks/useInstanceEventHandler';
|
|
7
6
|
import { getActiveElement } from '../../utils/utils';
|
|
7
|
+
import { getFirstNavigableItem } from '../../utils/tree';
|
|
8
8
|
const useTabbableItemId = (instance, selectedItems) => {
|
|
9
9
|
const isItemVisible = itemId => {
|
|
10
|
-
const
|
|
11
|
-
return
|
|
10
|
+
const itemMeta = instance.getItemMeta(itemId);
|
|
11
|
+
return itemMeta && (itemMeta.parentId == null || instance.isItemExpanded(itemMeta.parentId));
|
|
12
12
|
};
|
|
13
13
|
let tabbableItemId;
|
|
14
14
|
if (Array.isArray(selectedItems)) {
|
|
@@ -17,13 +17,12 @@ const useTabbableItemId = (instance, selectedItems) => {
|
|
|
17
17
|
tabbableItemId = selectedItems;
|
|
18
18
|
}
|
|
19
19
|
if (tabbableItemId == null) {
|
|
20
|
-
tabbableItemId = instance
|
|
20
|
+
tabbableItemId = getFirstNavigableItem(instance);
|
|
21
21
|
}
|
|
22
22
|
return tabbableItemId;
|
|
23
23
|
};
|
|
24
24
|
export const useTreeViewFocus = ({
|
|
25
25
|
instance,
|
|
26
|
-
publicAPI,
|
|
27
26
|
params,
|
|
28
27
|
state,
|
|
29
28
|
setState,
|
|
@@ -42,12 +41,12 @@ export const useTreeViewFocus = ({
|
|
|
42
41
|
const isTreeViewFocused = React.useCallback(() => !!rootRef.current && rootRef.current.contains(getActiveElement(ownerDocument(rootRef.current))), [rootRef]);
|
|
43
42
|
const isItemFocused = React.useCallback(itemId => state.focusedItemId === itemId && isTreeViewFocused(), [state.focusedItemId, isTreeViewFocused]);
|
|
44
43
|
const isItemVisible = itemId => {
|
|
45
|
-
const
|
|
46
|
-
return
|
|
44
|
+
const itemMeta = instance.getItemMeta(itemId);
|
|
45
|
+
return itemMeta && (itemMeta.parentId == null || instance.isItemExpanded(itemMeta.parentId));
|
|
47
46
|
};
|
|
48
47
|
const innerFocusItem = (event, itemId) => {
|
|
49
|
-
const
|
|
50
|
-
const itemElement = document.getElementById(instance.
|
|
48
|
+
const itemMeta = instance.getItemMeta(itemId);
|
|
49
|
+
const itemElement = document.getElementById(instance.getTreeItemIdAttribute(itemId, itemMeta.idAttribute));
|
|
51
50
|
if (itemElement) {
|
|
52
51
|
itemElement.focus();
|
|
53
52
|
}
|
|
@@ -70,7 +69,7 @@ export const useTreeViewFocus = ({
|
|
|
70
69
|
itemToFocusId = models.selectedItems.value;
|
|
71
70
|
}
|
|
72
71
|
if (itemToFocusId == null) {
|
|
73
|
-
itemToFocusId = instance
|
|
72
|
+
itemToFocusId = getFirstNavigableItem(instance);
|
|
74
73
|
}
|
|
75
74
|
innerFocusItem(event, itemToFocusId);
|
|
76
75
|
});
|
|
@@ -78,9 +77,9 @@ export const useTreeViewFocus = ({
|
|
|
78
77
|
if (state.focusedItemId == null) {
|
|
79
78
|
return;
|
|
80
79
|
}
|
|
81
|
-
const
|
|
82
|
-
if (
|
|
83
|
-
const itemElement = document.getElementById(instance.
|
|
80
|
+
const itemMeta = instance.getItemMeta(state.focusedItemId);
|
|
81
|
+
if (itemMeta) {
|
|
82
|
+
const itemElement = document.getElementById(instance.getTreeItemIdAttribute(state.focusedItemId, itemMeta.idAttribute));
|
|
84
83
|
if (itemElement) {
|
|
85
84
|
itemElement.blur();
|
|
86
85
|
}
|
|
@@ -88,16 +87,6 @@ export const useTreeViewFocus = ({
|
|
|
88
87
|
setFocusedItemId(null);
|
|
89
88
|
});
|
|
90
89
|
const canItemBeTabbed = itemId => itemId === tabbableItemId;
|
|
91
|
-
populateInstance(instance, {
|
|
92
|
-
isItemFocused,
|
|
93
|
-
canItemBeTabbed,
|
|
94
|
-
focusItem,
|
|
95
|
-
focusDefaultItem,
|
|
96
|
-
removeFocusedItem
|
|
97
|
-
});
|
|
98
|
-
populatePublicAPI(publicAPI, {
|
|
99
|
-
focusItem
|
|
100
|
-
});
|
|
101
90
|
useInstanceEventHandler(instance, 'removeItem', ({
|
|
102
91
|
id
|
|
103
92
|
}) => {
|
|
@@ -112,13 +101,23 @@ export const useTreeViewFocus = ({
|
|
|
112
101
|
instance.focusDefaultItem(event);
|
|
113
102
|
}
|
|
114
103
|
};
|
|
115
|
-
const focusedItem = instance.
|
|
116
|
-
const activeDescendant = focusedItem ? instance.
|
|
104
|
+
const focusedItem = instance.getItemMeta(state.focusedItemId);
|
|
105
|
+
const activeDescendant = focusedItem ? instance.getTreeItemIdAttribute(focusedItem.id, focusedItem.idAttribute) : null;
|
|
117
106
|
return {
|
|
118
107
|
getRootProps: otherHandlers => ({
|
|
119
108
|
onFocus: createHandleFocus(otherHandlers),
|
|
120
109
|
'aria-activedescendant': activeDescendant ?? undefined
|
|
121
|
-
})
|
|
110
|
+
}),
|
|
111
|
+
publicAPI: {
|
|
112
|
+
focusItem
|
|
113
|
+
},
|
|
114
|
+
instance: {
|
|
115
|
+
isItemFocused,
|
|
116
|
+
canItemBeTabbed,
|
|
117
|
+
focusItem,
|
|
118
|
+
focusDefaultItem,
|
|
119
|
+
removeFocusedItem
|
|
120
|
+
}
|
|
122
121
|
};
|
|
123
122
|
};
|
|
124
123
|
useTreeViewFocus.getInitialState = () => ({
|
|
@@ -4,15 +4,23 @@ import { UseTreeViewIdSignature } from '../useTreeViewId/useTreeViewId.types';
|
|
|
4
4
|
import type { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
5
5
|
import type { UseTreeViewSelectionSignature } from '../useTreeViewSelection';
|
|
6
6
|
import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion';
|
|
7
|
-
export interface
|
|
7
|
+
export interface UseTreeViewFocusPublicAPI {
|
|
8
|
+
/**
|
|
9
|
+
* Focuses the item with the given id.
|
|
10
|
+
*
|
|
11
|
+
* If the item is the child of a collapsed item, then this method will do nothing.
|
|
12
|
+
* Make sure to expand the ancestors of the item before calling this method if needed.
|
|
13
|
+
* @param {React.SyntheticEvent} event The event source of the action.
|
|
14
|
+
* @param {string} itemId The id of the item to focus.
|
|
15
|
+
*/
|
|
16
|
+
focusItem: (event: React.SyntheticEvent, itemId: string) => void;
|
|
17
|
+
}
|
|
18
|
+
export interface UseTreeViewFocusInstance extends UseTreeViewFocusPublicAPI {
|
|
8
19
|
isItemFocused: (itemId: string) => boolean;
|
|
9
20
|
canItemBeTabbed: (itemId: string) => boolean;
|
|
10
|
-
focusItem: (event: React.SyntheticEvent, itemId: string) => void;
|
|
11
21
|
focusDefaultItem: (event: React.SyntheticEvent | null) => void;
|
|
12
22
|
removeFocusedItem: () => void;
|
|
13
23
|
}
|
|
14
|
-
export interface UseTreeViewFocusPublicAPI extends Pick<UseTreeViewFocusInstance, 'focusItem'> {
|
|
15
|
-
}
|
|
16
24
|
export interface UseTreeViewFocusParameters {
|
|
17
25
|
/**
|
|
18
26
|
* Callback fired when tree items are focused.
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import useId from '@mui/utils/useId';
|
|
3
|
-
import { populateInstance } from '../../useTreeView/useTreeView.utils';
|
|
4
3
|
export const useTreeViewId = ({
|
|
5
|
-
instance,
|
|
6
4
|
params
|
|
7
5
|
}) => {
|
|
8
6
|
const treeId = useId(params.id);
|
|
9
|
-
const
|
|
10
|
-
populateInstance(instance, {
|
|
11
|
-
getTreeItemId
|
|
12
|
-
});
|
|
7
|
+
const getTreeItemIdAttribute = React.useCallback((itemId, idAttribute) => idAttribute ?? `${treeId}-${itemId}`, [treeId]);
|
|
13
8
|
return {
|
|
14
9
|
getRootProps: () => ({
|
|
15
10
|
id: treeId
|
|
16
|
-
})
|
|
11
|
+
}),
|
|
12
|
+
instance: {
|
|
13
|
+
getTreeItemIdAttribute
|
|
14
|
+
}
|
|
17
15
|
};
|
|
18
16
|
};
|
|
19
17
|
useTreeViewId.params = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TreeViewPluginSignature } from '../../models';
|
|
2
2
|
export interface UseTreeViewIdInstance {
|
|
3
|
-
|
|
3
|
+
getTreeItemIdAttribute: (itemId: string, idAttribute: string | undefined) => string;
|
|
4
4
|
}
|
|
5
5
|
export interface UseTreeViewIdParameters {
|
|
6
6
|
/**
|
|
@@ -1,86 +1,98 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { populateInstance, populatePublicAPI } from '../../useTreeView/useTreeView.utils';
|
|
4
3
|
import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
|
|
4
|
+
import { buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from './useTreeViewItems.utils';
|
|
5
5
|
const updateItemsState = ({
|
|
6
6
|
items,
|
|
7
7
|
isItemDisabled,
|
|
8
8
|
getItemLabel,
|
|
9
9
|
getItemId
|
|
10
10
|
}) => {
|
|
11
|
-
const
|
|
11
|
+
const itemMetaMap = {};
|
|
12
12
|
const itemMap = {};
|
|
13
|
-
const
|
|
13
|
+
const itemOrderedChildrenIds = {
|
|
14
|
+
[TREE_VIEW_ROOT_PARENT_ID]: []
|
|
15
|
+
};
|
|
16
|
+
const processItem = (item, parentId) => {
|
|
14
17
|
const id = getItemId ? getItemId(item) : item.id;
|
|
15
18
|
if (id == null) {
|
|
16
19
|
throw new Error(['MUI X: The Tree View component requires all items to have a unique `id` property.', 'Alternatively, you can use the `getItemId` prop to specify a custom id for each item.', 'An item was provided without id in the `items` prop:', JSON.stringify(item)].join('\n'));
|
|
17
20
|
}
|
|
18
|
-
if (
|
|
19
|
-
throw new Error(['MUI X: The Tree View component requires all items to have a unique `id` property.', 'Alternatively, you can use the `getItemId` prop to specify a custom id for each item.', `
|
|
21
|
+
if (itemMetaMap[id] != null) {
|
|
22
|
+
throw new Error(['MUI X: The Tree View component requires all items to have a unique `id` property.', 'Alternatively, you can use the `getItemId` prop to specify a custom id for each item.', `Two items were provided with the same id in the \`items\` prop: "${id}"`].join('\n'));
|
|
20
23
|
}
|
|
21
24
|
const label = getItemLabel ? getItemLabel(item) : item.label;
|
|
22
25
|
if (label == null) {
|
|
23
26
|
throw new Error(['MUI X: The Tree View component requires all items to have a `label` property.', 'Alternatively, you can use the `getItemLabel` prop to specify a custom label for each item.', 'An item was provided without label in the `items` prop:', JSON.stringify(item)].join('\n'));
|
|
24
27
|
}
|
|
25
|
-
|
|
28
|
+
itemMetaMap[id] = {
|
|
26
29
|
id,
|
|
27
30
|
label,
|
|
28
|
-
index,
|
|
29
31
|
parentId,
|
|
30
32
|
idAttribute: undefined,
|
|
31
33
|
expandable: !!item.children?.length,
|
|
32
34
|
disabled: isItemDisabled ? isItemDisabled(item) : false
|
|
33
35
|
};
|
|
34
36
|
itemMap[id] = item;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
itemOrderedChildrenIds[id] = [];
|
|
38
|
+
const parentIdWithDefault = parentId ?? TREE_VIEW_ROOT_PARENT_ID;
|
|
39
|
+
if (!itemOrderedChildrenIds[parentIdWithDefault]) {
|
|
40
|
+
itemOrderedChildrenIds[parentIdWithDefault] = [];
|
|
41
|
+
}
|
|
42
|
+
itemOrderedChildrenIds[parentIdWithDefault].push(id);
|
|
43
|
+
item.children?.forEach(child => processItem(child, id));
|
|
39
44
|
};
|
|
40
|
-
|
|
45
|
+
items.forEach(item => processItem(item, null));
|
|
46
|
+
const itemChildrenIndexes = {};
|
|
47
|
+
Object.keys(itemOrderedChildrenIds).forEach(parentId => {
|
|
48
|
+
itemChildrenIndexes[parentId] = buildSiblingIndexes(itemOrderedChildrenIds[parentId]);
|
|
49
|
+
});
|
|
41
50
|
return {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
itemMetaMap,
|
|
52
|
+
itemMap,
|
|
53
|
+
itemOrderedChildrenIds,
|
|
54
|
+
itemChildrenIndexes
|
|
45
55
|
};
|
|
46
56
|
};
|
|
47
57
|
export const useTreeViewItems = ({
|
|
48
58
|
instance,
|
|
49
|
-
publicAPI,
|
|
50
59
|
params,
|
|
51
60
|
state,
|
|
52
61
|
setState
|
|
53
62
|
}) => {
|
|
54
|
-
const
|
|
63
|
+
const getItemMeta = React.useCallback(itemId => state.items.itemMetaMap[itemId], [state.items.itemMetaMap]);
|
|
55
64
|
const getItem = React.useCallback(itemId => state.items.itemMap[itemId], [state.items.itemMap]);
|
|
56
65
|
const isItemDisabled = React.useCallback(itemId => {
|
|
57
66
|
if (itemId == null) {
|
|
58
67
|
return false;
|
|
59
68
|
}
|
|
60
|
-
let
|
|
69
|
+
let itemMeta = instance.getItemMeta(itemId);
|
|
61
70
|
|
|
62
71
|
// This can be called before the item has been added to the item map.
|
|
63
|
-
if (!
|
|
72
|
+
if (!itemMeta) {
|
|
64
73
|
return false;
|
|
65
74
|
}
|
|
66
|
-
if (
|
|
75
|
+
if (itemMeta.disabled) {
|
|
67
76
|
return true;
|
|
68
77
|
}
|
|
69
|
-
while (
|
|
70
|
-
|
|
71
|
-
if (
|
|
78
|
+
while (itemMeta.parentId != null) {
|
|
79
|
+
itemMeta = instance.getItemMeta(itemMeta.parentId);
|
|
80
|
+
if (itemMeta.disabled) {
|
|
72
81
|
return true;
|
|
73
82
|
}
|
|
74
83
|
}
|
|
75
84
|
return false;
|
|
76
85
|
}, [instance]);
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
const getItemIndex = React.useCallback(itemId => {
|
|
87
|
+
const parentId = instance.getItemMeta(itemId).parentId ?? TREE_VIEW_ROOT_PARENT_ID;
|
|
88
|
+
return state.items.itemChildrenIndexes[parentId][itemId];
|
|
89
|
+
}, [instance, state.items.itemChildrenIndexes]);
|
|
90
|
+
const getItemOrderedChildrenIds = React.useCallback(itemId => state.items.itemOrderedChildrenIds[itemId ?? TREE_VIEW_ROOT_PARENT_ID] ?? [], [state.items.itemOrderedChildrenIds]);
|
|
91
|
+
const isItemNavigable = itemId => {
|
|
92
|
+
if (params.disabledItemsFocusable) {
|
|
93
|
+
return true;
|
|
82
94
|
}
|
|
83
|
-
return
|
|
95
|
+
return !instance.isItemDisabled(itemId);
|
|
84
96
|
};
|
|
85
97
|
const areItemUpdatesPreventedRef = React.useRef(false);
|
|
86
98
|
const preventItemUpdates = React.useCallback(() => {
|
|
@@ -98,8 +110,8 @@ export const useTreeViewItems = ({
|
|
|
98
110
|
getItemId: params.getItemId,
|
|
99
111
|
getItemLabel: params.getItemLabel
|
|
100
112
|
});
|
|
101
|
-
Object.values(prevState.items.
|
|
102
|
-
if (!newState.
|
|
113
|
+
Object.values(prevState.items.itemMetaMap).forEach(item => {
|
|
114
|
+
if (!newState.itemMetaMap[item.id]) {
|
|
103
115
|
publishTreeViewEvent(instance, 'removeItem', {
|
|
104
116
|
id: item.id
|
|
105
117
|
});
|
|
@@ -111,34 +123,32 @@ export const useTreeViewItems = ({
|
|
|
111
123
|
});
|
|
112
124
|
}, [instance, setState, params.items, params.isItemDisabled, params.getItemId, params.getItemLabel]);
|
|
113
125
|
const getItemsToRender = () => {
|
|
114
|
-
const getPropsFromItemId =
|
|
115
|
-
id
|
|
116
|
-
children
|
|
117
|
-
}) => {
|
|
118
|
-
const item = state.items.nodeMap[id];
|
|
126
|
+
const getPropsFromItemId = id => {
|
|
127
|
+
const item = state.items.itemMetaMap[id];
|
|
119
128
|
return {
|
|
120
129
|
label: item.label,
|
|
121
130
|
itemId: item.id,
|
|
122
131
|
id: item.idAttribute,
|
|
123
|
-
children:
|
|
132
|
+
children: state.items.itemOrderedChildrenIds[id].map(getPropsFromItemId)
|
|
124
133
|
};
|
|
125
134
|
};
|
|
126
|
-
return state.items.
|
|
135
|
+
return state.items.itemOrderedChildrenIds[TREE_VIEW_ROOT_PARENT_ID].map(getPropsFromItemId);
|
|
127
136
|
};
|
|
128
|
-
populateInstance(instance, {
|
|
129
|
-
getNode,
|
|
130
|
-
getItem,
|
|
131
|
-
getItemsToRender,
|
|
132
|
-
getChildrenIds,
|
|
133
|
-
getNavigableChildrenIds,
|
|
134
|
-
isItemDisabled,
|
|
135
|
-
preventItemUpdates,
|
|
136
|
-
areItemUpdatesPrevented
|
|
137
|
-
});
|
|
138
|
-
populatePublicAPI(publicAPI, {
|
|
139
|
-
getItem
|
|
140
|
-
});
|
|
141
137
|
return {
|
|
138
|
+
publicAPI: {
|
|
139
|
+
getItem
|
|
140
|
+
},
|
|
141
|
+
instance: {
|
|
142
|
+
getItemMeta,
|
|
143
|
+
getItem,
|
|
144
|
+
getItemsToRender,
|
|
145
|
+
getItemIndex,
|
|
146
|
+
getItemOrderedChildrenIds,
|
|
147
|
+
isItemDisabled,
|
|
148
|
+
isItemNavigable,
|
|
149
|
+
preventItemUpdates,
|
|
150
|
+
areItemUpdatesPrevented
|
|
151
|
+
},
|
|
142
152
|
contextValue: {
|
|
143
153
|
disabledItemsFocusable: params.disabledItemsFocusable
|
|
144
154
|
}
|