@mui/x-tree-view 7.0.0 → 7.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +246 -4
- package/README.md +1 -1
- package/RichTreeView/RichTreeView.d.ts +2 -2
- package/RichTreeView/RichTreeView.js +11 -9
- package/SimpleTreeView/SimpleTreeView.js +4 -2
- package/SimpleTreeView/SimpleTreeView.plugins.d.ts +1 -1
- package/SimpleTreeView/SimpleTreeView.plugins.js +2 -2
- package/TreeItem/TreeItem.js +4 -4
- package/TreeItem/treeItemClasses.d.ts +1 -1
- package/TreeItem/useTreeItemState.js +9 -9
- package/TreeItem2Icon/TreeItem2Icon.types.d.ts +4 -4
- package/TreeView/TreeView.js +2 -1
- package/hooks/useTreeItem2Utils/useTreeItem2Utils.js +8 -8
- package/hooks/useTreeViewApiRef.d.ts +1 -1
- package/index.js +1 -1
- package/internals/TreeViewProvider/DescendantProvider.d.ts +1 -1
- package/internals/TreeViewProvider/DescendantProvider.js +1 -1
- package/internals/index.d.ts +18 -8
- package/internals/index.js +11 -0
- package/internals/models/plugin.d.ts +1 -1
- package/internals/plugins/defaultPlugins.d.ts +3 -3
- package/internals/plugins/defaultPlugins.js +2 -2
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +32 -18
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +16 -6
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +35 -33
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +17 -9
- package/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.d.ts +6 -6
- package/internals/plugins/useTreeViewId/useTreeViewId.types.d.ts +1 -1
- package/internals/plugins/useTreeViewItems/index.d.ts +2 -0
- package/internals/plugins/useTreeViewItems/index.js +1 -0
- package/internals/plugins/useTreeViewItems/useTreeViewItems.d.ts +3 -0
- package/internals/plugins/{useTreeViewNodes/useTreeViewNodes.js → useTreeViewItems/useTreeViewItems.js} +43 -33
- package/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +104 -0
- package/internals/plugins/useTreeViewJSXItems/index.d.ts +2 -0
- package/internals/plugins/useTreeViewJSXItems/index.js +1 -0
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.d.ts +3 -0
- package/{modern/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js → internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js} +26 -25
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.types.d.ts +18 -0
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +40 -44
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +2 -2
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +34 -34
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +6 -6
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.d.ts +7 -7
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +5 -5
- package/internals/useTreeView/useTreeView.utils.d.ts +5 -5
- package/internals/useTreeView/useTreeView.utils.js +15 -15
- package/internals/useTreeView/useTreeViewModels.js +2 -2
- package/modern/RichTreeView/RichTreeView.js +11 -9
- package/modern/SimpleTreeView/SimpleTreeView.js +4 -2
- package/modern/SimpleTreeView/SimpleTreeView.plugins.js +2 -2
- package/modern/TreeItem/TreeItem.js +4 -4
- package/modern/TreeItem/useTreeItemState.js +9 -9
- package/modern/TreeView/TreeView.js +2 -1
- package/modern/hooks/useTreeItem2Utils/useTreeItem2Utils.js +8 -8
- package/modern/index.js +1 -1
- package/modern/internals/TreeViewProvider/DescendantProvider.js +1 -1
- package/modern/internals/index.js +11 -0
- package/modern/internals/plugins/defaultPlugins.js +2 -2
- package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +32 -18
- package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +35 -33
- package/modern/internals/plugins/useTreeViewItems/index.js +1 -0
- package/modern/internals/plugins/{useTreeViewNodes/useTreeViewNodes.js → useTreeViewItems/useTreeViewItems.js} +43 -33
- package/modern/internals/plugins/useTreeViewJSXItems/index.js +1 -0
- package/{internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js → modern/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js} +26 -25
- package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +40 -44
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +34 -34
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +5 -5
- package/modern/internals/useTreeView/useTreeView.utils.js +15 -15
- package/modern/internals/useTreeView/useTreeViewModels.js +2 -2
- package/node/RichTreeView/RichTreeView.js +11 -9
- package/node/SimpleTreeView/SimpleTreeView.js +4 -2
- package/node/SimpleTreeView/SimpleTreeView.plugins.js +2 -2
- package/node/TreeItem/TreeItem.js +4 -4
- package/node/TreeItem/useTreeItemState.js +9 -9
- package/node/TreeView/TreeView.js +2 -1
- package/node/hooks/useTreeItem2Utils/useTreeItem2Utils.js +8 -8
- package/node/index.js +1 -1
- package/node/internals/TreeViewProvider/DescendantProvider.js +1 -1
- package/node/internals/index.js +70 -0
- package/node/internals/plugins/defaultPlugins.js +2 -2
- package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +31 -17
- package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +35 -33
- package/node/internals/plugins/useTreeViewItems/index.js +12 -0
- package/node/internals/plugins/{useTreeViewNodes/useTreeViewNodes.js → useTreeViewItems/useTreeViewItems.js} +45 -35
- package/node/internals/plugins/useTreeViewJSXItems/index.js +12 -0
- package/node/internals/plugins/{useTreeViewJSXNodes/useTreeViewJSXNodes.js → useTreeViewJSXItems/useTreeViewJSXItems.js} +28 -27
- package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +39 -43
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +33 -33
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +5 -5
- package/node/internals/useTreeView/useTreeView.utils.js +20 -20
- package/node/internals/useTreeView/useTreeViewModels.js +2 -2
- package/package.json +2 -2
- package/useTreeItem2/useTreeItem2.d.ts +1 -1
- package/internals/plugins/useTreeViewJSXNodes/index.d.ts +0 -2
- package/internals/plugins/useTreeViewJSXNodes/index.js +0 -1
- package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.d.ts +0 -3
- package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.d.ts +0 -18
- package/internals/plugins/useTreeViewNodes/index.d.ts +0 -2
- package/internals/plugins/useTreeViewNodes/index.js +0 -1
- package/internals/plugins/useTreeViewNodes/useTreeViewNodes.d.ts +0 -3
- package/internals/plugins/useTreeViewNodes/useTreeViewNodes.types.d.ts +0 -88
- package/modern/internals/plugins/useTreeViewJSXNodes/index.js +0 -1
- package/modern/internals/plugins/useTreeViewNodes/index.js +0 -1
- package/node/internals/plugins/useTreeViewJSXNodes/index.js +0 -12
- package/node/internals/plugins/useTreeViewNodes/index.js +0 -12
- /package/internals/plugins/{useTreeViewJSXNodes/useTreeViewJSXNodes.types.js → useTreeViewItems/useTreeViewItems.types.js} +0 -0
- /package/internals/plugins/{useTreeViewNodes/useTreeViewNodes.types.js → useTreeViewJSXItems/useTreeViewJSXItems.types.js} +0 -0
- /package/modern/internals/plugins/{useTreeViewJSXNodes/useTreeViewJSXNodes.types.js → useTreeViewItems/useTreeViewItems.types.js} +0 -0
- /package/modern/internals/plugins/{useTreeViewNodes/useTreeViewNodes.types.js → useTreeViewJSXItems/useTreeViewJSXItems.types.js} +0 -0
- /package/node/internals/plugins/{useTreeViewJSXNodes/useTreeViewJSXNodes.types.js → useTreeViewItems/useTreeViewItems.types.js} +0 -0
- /package/node/internals/plugins/{useTreeViewNodes/useTreeViewNodes.types.js → useTreeViewJSXItems/useTreeViewJSXItems.types.js} +0 -0
|
@@ -97,7 +97,7 @@ type TreeViewUsedPlugins<TSignature extends TreeViewAnyPluginSignature> = [
|
|
|
97
97
|
TreeViewCorePluginsSignature,
|
|
98
98
|
...TSignature['dependantPlugins']
|
|
99
99
|
];
|
|
100
|
-
type TreeViewUsedParams<TSignature extends TreeViewAnyPluginSignature> = TSignature['params'] & MergePluginsProperty<TreeViewUsedPlugins<TSignature>, 'params'>;
|
|
100
|
+
export type TreeViewUsedParams<TSignature extends TreeViewAnyPluginSignature> = TSignature['params'] & MergePluginsProperty<TreeViewUsedPlugins<TSignature>, 'params'>;
|
|
101
101
|
type TreeViewUsedDefaultizedParams<TSignature extends TreeViewAnyPluginSignature> = TSignature['defaultizedParams'] & MergePluginsProperty<TreeViewUsedPlugins<TSignature>, 'defaultizedParams'>;
|
|
102
102
|
export type TreeViewUsedInstance<TSignature extends TreeViewAnyPluginSignature> = TSignature['instance'] & MergePluginsProperty<TreeViewUsedPlugins<TSignature>, 'instance'> & {
|
|
103
103
|
/**
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { UseTreeViewIdParameters } from './useTreeViewId';
|
|
2
|
-
import {
|
|
2
|
+
import { UseTreeViewItemsParameters } from './useTreeViewItems';
|
|
3
3
|
import { UseTreeViewExpansionParameters } from './useTreeViewExpansion';
|
|
4
4
|
import { UseTreeViewSelectionParameters } from './useTreeViewSelection';
|
|
5
5
|
import { UseTreeViewFocusParameters } from './useTreeViewFocus';
|
|
6
6
|
import { UseTreeViewIconsParameters } from './useTreeViewIcons';
|
|
7
7
|
import { ConvertPluginsIntoSignatures, MergePluginsProperty } from '../models';
|
|
8
|
-
export declare const DEFAULT_TREE_VIEW_PLUGINS: readonly [import("../models").TreeViewPlugin<import("./useTreeViewId").UseTreeViewIdSignature>, import("../models").TreeViewPlugin<import("./
|
|
8
|
+
export declare const DEFAULT_TREE_VIEW_PLUGINS: readonly [import("../models").TreeViewPlugin<import("./useTreeViewId").UseTreeViewIdSignature>, import("../models").TreeViewPlugin<import("./useTreeViewItems").UseTreeViewItemsSignature>, import("../models").TreeViewPlugin<import("./useTreeViewExpansion").UseTreeViewExpansionSignature>, import("../models").TreeViewPlugin<import("./useTreeViewSelection").UseTreeViewSelectionSignature>, import("../models").TreeViewPlugin<import("./useTreeViewFocus").UseTreeViewFocusSignature>, import("../models").TreeViewPlugin<import("./useTreeViewKeyboardNavigation").UseTreeViewKeyboardNavigationSignature>, import("../models").TreeViewPlugin<import("./useTreeViewIcons").UseTreeViewIconsSignature>];
|
|
9
9
|
export type DefaultTreeViewPlugins = ConvertPluginsIntoSignatures<typeof DEFAULT_TREE_VIEW_PLUGINS>;
|
|
10
10
|
export type DefaultTreeViewPluginSlots = MergePluginsProperty<DefaultTreeViewPlugins, 'slots'>;
|
|
11
11
|
export type DefaultTreeViewPluginSlotProps = MergePluginsProperty<DefaultTreeViewPlugins, 'slotProps'>;
|
|
12
|
-
export interface DefaultTreeViewPluginParameters<R extends {}, Multiple extends boolean | undefined> extends UseTreeViewIdParameters,
|
|
12
|
+
export interface DefaultTreeViewPluginParameters<R extends {}, Multiple extends boolean | undefined> extends UseTreeViewIdParameters, UseTreeViewItemsParameters<R>, UseTreeViewExpansionParameters, UseTreeViewFocusParameters, UseTreeViewSelectionParameters<Multiple>, UseTreeViewIconsParameters {
|
|
13
13
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useTreeViewId } from './useTreeViewId';
|
|
2
|
-
import {
|
|
2
|
+
import { useTreeViewItems } from './useTreeViewItems';
|
|
3
3
|
import { useTreeViewExpansion } from './useTreeViewExpansion';
|
|
4
4
|
import { useTreeViewSelection } from './useTreeViewSelection';
|
|
5
5
|
import { useTreeViewFocus } from './useTreeViewFocus';
|
|
6
6
|
import { useTreeViewKeyboardNavigation } from './useTreeViewKeyboardNavigation';
|
|
7
7
|
import { useTreeViewIcons } from './useTreeViewIcons';
|
|
8
|
-
export const DEFAULT_TREE_VIEW_PLUGINS = [useTreeViewId,
|
|
8
|
+
export const DEFAULT_TREE_VIEW_PLUGINS = [useTreeViewId, useTreeViewItems, useTreeViewExpansion, useTreeViewSelection, useTreeViewFocus, useTreeViewKeyboardNavigation, useTreeViewIcons];
|
|
9
9
|
|
|
10
10
|
// We can't infer this type from the plugin, otherwise we would lose the generics.
|
|
@@ -1,40 +1,50 @@
|
|
|
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';
|
|
4
|
+
import { populateInstance, populatePublicAPI } from '../../useTreeView/useTreeView.utils';
|
|
5
5
|
export const useTreeViewExpansion = ({
|
|
6
6
|
instance,
|
|
7
|
+
publicAPI,
|
|
7
8
|
params,
|
|
8
9
|
models
|
|
9
10
|
}) => {
|
|
11
|
+
const expandedItemsMap = React.useMemo(() => {
|
|
12
|
+
const temp = new Map();
|
|
13
|
+
models.expandedItems.value.forEach(id => {
|
|
14
|
+
temp.set(id, true);
|
|
15
|
+
});
|
|
16
|
+
return temp;
|
|
17
|
+
}, [models.expandedItems.value]);
|
|
10
18
|
const setExpandedItems = (event, value) => {
|
|
11
19
|
params.onExpandedItemsChange?.(event, value);
|
|
12
20
|
models.expandedItems.setControlledValue(value);
|
|
13
21
|
};
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
const isItemExpanded = React.useCallback(itemId => expandedItemsMap.has(itemId), [expandedItemsMap]);
|
|
23
|
+
const isItemExpandable = React.useCallback(itemId => !!instance.getNode(itemId)?.expandable, [instance]);
|
|
24
|
+
const toggleItemExpansion = useEventCallback((event, itemId) => {
|
|
25
|
+
const isExpandedBefore = instance.isItemExpanded(itemId);
|
|
26
|
+
instance.setItemExpansion(event, itemId, !isExpandedBefore);
|
|
27
|
+
});
|
|
28
|
+
const setItemExpansion = useEventCallback((event, itemId, isExpanded) => {
|
|
29
|
+
const isExpandedBefore = instance.isItemExpanded(itemId);
|
|
30
|
+
if (isExpandedBefore === isExpanded) {
|
|
20
31
|
return;
|
|
21
32
|
}
|
|
22
|
-
const isExpandedBefore = models.expandedItems.value.indexOf(itemId) !== -1;
|
|
23
33
|
let newExpanded;
|
|
24
|
-
if (
|
|
25
|
-
newExpanded = models.expandedItems.value.filter(id => id !== itemId);
|
|
26
|
-
} else {
|
|
34
|
+
if (isExpanded) {
|
|
27
35
|
newExpanded = [itemId].concat(models.expandedItems.value);
|
|
36
|
+
} else {
|
|
37
|
+
newExpanded = models.expandedItems.value.filter(id => id !== itemId);
|
|
28
38
|
}
|
|
29
39
|
if (params.onItemExpansionToggle) {
|
|
30
|
-
params.onItemExpansionToggle(event, itemId,
|
|
40
|
+
params.onItemExpansionToggle(event, itemId, isExpanded);
|
|
31
41
|
}
|
|
32
42
|
setExpandedItems(event, newExpanded);
|
|
33
43
|
});
|
|
34
44
|
const expandAllSiblings = (event, itemId) => {
|
|
35
45
|
const node = instance.getNode(itemId);
|
|
36
46
|
const siblings = instance.getChildrenIds(node.parentId);
|
|
37
|
-
const diff = siblings.filter(child => instance.
|
|
47
|
+
const diff = siblings.filter(child => instance.isItemExpandable(child) && !instance.isItemExpanded(child));
|
|
38
48
|
const newExpanded = models.expandedItems.value.concat(diff);
|
|
39
49
|
if (diff.length > 0) {
|
|
40
50
|
if (params.onItemExpansionToggle) {
|
|
@@ -46,20 +56,24 @@ export const useTreeViewExpansion = ({
|
|
|
46
56
|
}
|
|
47
57
|
};
|
|
48
58
|
populateInstance(instance, {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
isItemExpanded,
|
|
60
|
+
isItemExpandable,
|
|
61
|
+
setItemExpansion,
|
|
62
|
+
toggleItemExpansion,
|
|
52
63
|
expandAllSiblings
|
|
53
64
|
});
|
|
65
|
+
populatePublicAPI(publicAPI, {
|
|
66
|
+
setItemExpansion
|
|
67
|
+
});
|
|
54
68
|
};
|
|
55
69
|
useTreeViewExpansion.models = {
|
|
56
70
|
expandedItems: {
|
|
57
71
|
getDefaultValue: params => params.defaultExpandedItems
|
|
58
72
|
}
|
|
59
73
|
};
|
|
60
|
-
const
|
|
74
|
+
const DEFAULT_EXPANDED_ITEMS = [];
|
|
61
75
|
useTreeViewExpansion.getDefaultizedParams = params => _extends({}, params, {
|
|
62
|
-
defaultExpandedItems: params.defaultExpandedItems ??
|
|
76
|
+
defaultExpandedItems: params.defaultExpandedItems ?? DEFAULT_EXPANDED_ITEMS
|
|
63
77
|
});
|
|
64
78
|
useTreeViewExpansion.params = {
|
|
65
79
|
expandedItems: true,
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { DefaultizedProps, TreeViewPluginSignature } from '../../models';
|
|
3
|
-
import {
|
|
4
|
-
export interface
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
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 {
|
|
14
|
+
isItemExpanded: (itemId: string) => boolean;
|
|
15
|
+
isItemExpandable: (itemId: string) => boolean;
|
|
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
|
-
dependantPlugins: [
|
|
52
|
+
dependantPlugins: [UseTreeViewItemsSignature];
|
|
43
53
|
}>;
|
|
@@ -8,7 +8,7 @@ import { getActiveElement } from '../../utils/utils';
|
|
|
8
8
|
const useTabbableItemId = (instance, selectedItems) => {
|
|
9
9
|
const isItemVisible = itemId => {
|
|
10
10
|
const node = instance.getNode(itemId);
|
|
11
|
-
return node && (node.parentId == null || instance.
|
|
11
|
+
return node && (node.parentId == null || instance.isItemExpanded(node.parentId));
|
|
12
12
|
};
|
|
13
13
|
let tabbableItemId;
|
|
14
14
|
if (Array.isArray(selectedItems)) {
|
|
@@ -32,18 +32,18 @@ export const useTreeViewFocus = ({
|
|
|
32
32
|
}) => {
|
|
33
33
|
const tabbableItemId = useTabbableItemId(instance, models.selectedItems.value);
|
|
34
34
|
const setFocusedItemId = useEventCallback(itemId => {
|
|
35
|
-
const cleanItemId = typeof itemId === 'function' ? itemId(state.
|
|
36
|
-
if (state.
|
|
35
|
+
const cleanItemId = typeof itemId === 'function' ? itemId(state.focusedItemId) : itemId;
|
|
36
|
+
if (state.focusedItemId !== cleanItemId) {
|
|
37
37
|
setState(prevState => _extends({}, prevState, {
|
|
38
|
-
|
|
38
|
+
focusedItemId: cleanItemId
|
|
39
39
|
}));
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
42
|
const isTreeViewFocused = React.useCallback(() => !!rootRef.current && rootRef.current.contains(getActiveElement(ownerDocument(rootRef.current))), [rootRef]);
|
|
43
|
-
const
|
|
44
|
-
const
|
|
43
|
+
const isItemFocused = React.useCallback(itemId => state.focusedItemId === itemId && isTreeViewFocused(), [state.focusedItemId, isTreeViewFocused]);
|
|
44
|
+
const isItemVisible = itemId => {
|
|
45
45
|
const node = instance.getNode(itemId);
|
|
46
|
-
return node && (node.parentId == null || instance.
|
|
46
|
+
return node && (node.parentId == null || instance.isItemExpanded(node.parentId));
|
|
47
47
|
};
|
|
48
48
|
const innerFocusItem = (event, itemId) => {
|
|
49
49
|
const node = instance.getNode(itemId);
|
|
@@ -56,62 +56,64 @@ export const useTreeViewFocus = ({
|
|
|
56
56
|
params.onItemFocus(event, itemId);
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
|
-
const focusItem = useEventCallback((event,
|
|
60
|
-
// If we receive
|
|
61
|
-
if (
|
|
62
|
-
innerFocusItem(event,
|
|
59
|
+
const focusItem = useEventCallback((event, itemId) => {
|
|
60
|
+
// If we receive an itemId, and it is visible, the focus will be set to it
|
|
61
|
+
if (isItemVisible(itemId)) {
|
|
62
|
+
innerFocusItem(event, itemId);
|
|
63
63
|
}
|
|
64
64
|
});
|
|
65
|
-
const
|
|
66
|
-
let
|
|
65
|
+
const focusDefaultItem = useEventCallback(event => {
|
|
66
|
+
let itemToFocusId;
|
|
67
67
|
if (Array.isArray(models.selectedItems.value)) {
|
|
68
|
-
|
|
69
|
-
} else if (models.selectedItems.value != null &&
|
|
70
|
-
|
|
68
|
+
itemToFocusId = models.selectedItems.value.find(isItemVisible);
|
|
69
|
+
} else if (models.selectedItems.value != null && isItemVisible(models.selectedItems.value)) {
|
|
70
|
+
itemToFocusId = models.selectedItems.value;
|
|
71
71
|
}
|
|
72
|
-
if (
|
|
73
|
-
|
|
72
|
+
if (itemToFocusId == null) {
|
|
73
|
+
itemToFocusId = instance.getNavigableChildrenIds(null)[0];
|
|
74
74
|
}
|
|
75
|
-
innerFocusItem(event,
|
|
75
|
+
innerFocusItem(event, itemToFocusId);
|
|
76
76
|
});
|
|
77
77
|
const removeFocusedItem = useEventCallback(() => {
|
|
78
|
-
if (state.
|
|
78
|
+
if (state.focusedItemId == null) {
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
|
-
const node = instance.getNode(state.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
itemElement
|
|
81
|
+
const node = instance.getNode(state.focusedItemId);
|
|
82
|
+
if (node) {
|
|
83
|
+
const itemElement = document.getElementById(instance.getTreeItemId(state.focusedItemId, node.idAttribute));
|
|
84
|
+
if (itemElement) {
|
|
85
|
+
itemElement.blur();
|
|
86
|
+
}
|
|
85
87
|
}
|
|
86
88
|
setFocusedItemId(null);
|
|
87
89
|
});
|
|
88
90
|
const canItemBeTabbed = itemId => itemId === tabbableItemId;
|
|
89
91
|
populateInstance(instance, {
|
|
90
|
-
|
|
92
|
+
isItemFocused,
|
|
91
93
|
canItemBeTabbed,
|
|
92
94
|
focusItem,
|
|
93
|
-
|
|
95
|
+
focusDefaultItem,
|
|
94
96
|
removeFocusedItem
|
|
95
97
|
});
|
|
96
98
|
populatePublicAPI(publicAPI, {
|
|
97
99
|
focusItem
|
|
98
100
|
});
|
|
99
|
-
useInstanceEventHandler(instance, '
|
|
101
|
+
useInstanceEventHandler(instance, 'removeItem', ({
|
|
100
102
|
id
|
|
101
103
|
}) => {
|
|
102
|
-
if (state.
|
|
103
|
-
instance.
|
|
104
|
+
if (state.focusedItemId === id) {
|
|
105
|
+
instance.focusDefaultItem(null);
|
|
104
106
|
}
|
|
105
107
|
});
|
|
106
108
|
const createHandleFocus = otherHandlers => event => {
|
|
107
109
|
otherHandlers.onFocus?.(event);
|
|
108
110
|
// if the event bubbled (which is React specific) we don't want to steal focus
|
|
109
111
|
if (event.target === event.currentTarget) {
|
|
110
|
-
instance.
|
|
112
|
+
instance.focusDefaultItem(event);
|
|
111
113
|
}
|
|
112
114
|
};
|
|
113
|
-
const
|
|
114
|
-
const activeDescendant =
|
|
115
|
+
const focusedItem = instance.getNode(state.focusedItemId);
|
|
116
|
+
const activeDescendant = focusedItem ? instance.getTreeItemId(focusedItem.id, focusedItem.idAttribute) : null;
|
|
115
117
|
return {
|
|
116
118
|
getRootProps: otherHandlers => ({
|
|
117
119
|
onFocus: createHandleFocus(otherHandlers),
|
|
@@ -120,7 +122,7 @@ export const useTreeViewFocus = ({
|
|
|
120
122
|
};
|
|
121
123
|
};
|
|
122
124
|
useTreeViewFocus.getInitialState = () => ({
|
|
123
|
-
|
|
125
|
+
focusedItemId: null
|
|
124
126
|
});
|
|
125
127
|
useTreeViewFocus.params = {
|
|
126
128
|
onItemFocus: true
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { TreeViewPluginSignature } from '../../models';
|
|
3
3
|
import { UseTreeViewIdSignature } from '../useTreeViewId/useTreeViewId.types';
|
|
4
|
-
import type {
|
|
4
|
+
import type { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
5
5
|
import type { UseTreeViewSelectionSignature } from '../useTreeViewSelection';
|
|
6
6
|
import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion';
|
|
7
|
-
export interface
|
|
8
|
-
|
|
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 {
|
|
19
|
+
isItemFocused: (itemId: string) => boolean;
|
|
9
20
|
canItemBeTabbed: (itemId: string) => boolean;
|
|
10
|
-
|
|
11
|
-
focusDefaultNode: (event: React.SyntheticEvent | null) => void;
|
|
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.
|
|
@@ -24,7 +32,7 @@ export interface UseTreeViewFocusParameters {
|
|
|
24
32
|
}
|
|
25
33
|
export type UseTreeViewFocusDefaultizedParameters = UseTreeViewFocusParameters;
|
|
26
34
|
export interface UseTreeViewFocusState {
|
|
27
|
-
|
|
35
|
+
focusedItemId: string | null;
|
|
28
36
|
}
|
|
29
37
|
export type UseTreeViewFocusSignature = TreeViewPluginSignature<{
|
|
30
38
|
params: UseTreeViewFocusParameters;
|
|
@@ -34,7 +42,7 @@ export type UseTreeViewFocusSignature = TreeViewPluginSignature<{
|
|
|
34
42
|
state: UseTreeViewFocusState;
|
|
35
43
|
dependantPlugins: [
|
|
36
44
|
UseTreeViewIdSignature,
|
|
37
|
-
|
|
45
|
+
UseTreeViewItemsSignature,
|
|
38
46
|
UseTreeViewSelectionSignature,
|
|
39
47
|
UseTreeViewExpansionSignature
|
|
40
48
|
];
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { SlotComponentProps } from '@mui/base/utils';
|
|
3
3
|
import { TreeViewPluginSignature } from '../../models';
|
|
4
|
-
import {
|
|
4
|
+
import { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
5
5
|
import { UseTreeViewSelectionSignature } from '../useTreeViewSelection';
|
|
6
6
|
export interface UseTreeViewIconsParameters {
|
|
7
7
|
}
|
|
8
8
|
export type UseTreeViewIconsDefaultizedParameters = UseTreeViewIconsParameters;
|
|
9
9
|
interface UseTreeViewIconsSlots {
|
|
10
10
|
/**
|
|
11
|
-
* The default icon used to collapse the
|
|
11
|
+
* The default icon used to collapse the item.
|
|
12
12
|
*/
|
|
13
13
|
collapseIcon?: React.ElementType;
|
|
14
14
|
/**
|
|
15
|
-
* The default icon used to expand the
|
|
15
|
+
* The default icon used to expand the item.
|
|
16
16
|
*/
|
|
17
17
|
expandIcon?: React.ElementType;
|
|
18
18
|
/**
|
|
19
|
-
* The default icon displayed next to an end
|
|
20
|
-
* This is applied to all tree
|
|
19
|
+
* The default icon displayed next to an end item.
|
|
20
|
+
* This is applied to all tree items and can be overridden by the TreeItem `icon` slot prop.
|
|
21
21
|
*/
|
|
22
22
|
endIcon?: React.ElementType;
|
|
23
23
|
}
|
|
@@ -38,6 +38,6 @@ export type UseTreeViewIconsSignature = TreeViewPluginSignature<{
|
|
|
38
38
|
contextValue: UseTreeViewIconsContextValue;
|
|
39
39
|
slots: UseTreeViewIconsSlots;
|
|
40
40
|
slotProps: UseTreeViewIconsSlotProps;
|
|
41
|
-
dependantPlugins: [
|
|
41
|
+
dependantPlugins: [UseTreeViewItemsSignature, UseTreeViewSelectionSignature];
|
|
42
42
|
}>;
|
|
43
43
|
export {};
|
|
@@ -11,7 +11,7 @@ export interface UseTreeViewIdParameters {
|
|
|
11
11
|
}
|
|
12
12
|
export type UseTreeViewIdDefaultizedParameters = UseTreeViewIdParameters;
|
|
13
13
|
export interface UseTreeViewIdState {
|
|
14
|
-
|
|
14
|
+
focusedItemId: string | null;
|
|
15
15
|
}
|
|
16
16
|
export type UseTreeViewIdSignature = TreeViewPluginSignature<{
|
|
17
17
|
params: UseTreeViewIdParameters;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useTreeViewItems } from './useTreeViewItems';
|
|
@@ -2,7 +2,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { populateInstance, populatePublicAPI } from '../../useTreeView/useTreeView.utils';
|
|
4
4
|
import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
|
|
5
|
-
const
|
|
5
|
+
const updateItemsState = ({
|
|
6
6
|
items,
|
|
7
7
|
isItemDisabled,
|
|
8
8
|
getItemLabel,
|
|
@@ -16,7 +16,7 @@ const updateNodesState = ({
|
|
|
16
16
|
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
17
|
}
|
|
18
18
|
if (nodeMap[id] != null) {
|
|
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.', `
|
|
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.', `Two items were provided with the same id in the \`items\` prop: "${id}"`].join('\n'));
|
|
20
20
|
}
|
|
21
21
|
const label = getItemLabel ? getItemLabel(item) : item.label;
|
|
22
22
|
if (label == null) {
|
|
@@ -44,86 +44,96 @@ const updateNodesState = ({
|
|
|
44
44
|
itemMap
|
|
45
45
|
};
|
|
46
46
|
};
|
|
47
|
-
export const
|
|
47
|
+
export const useTreeViewItems = ({
|
|
48
48
|
instance,
|
|
49
49
|
publicAPI,
|
|
50
50
|
params,
|
|
51
51
|
state,
|
|
52
52
|
setState
|
|
53
53
|
}) => {
|
|
54
|
-
const getNode = React.useCallback(itemId => state.
|
|
55
|
-
const getItem = React.useCallback(itemId => state.
|
|
56
|
-
const
|
|
54
|
+
const getNode = React.useCallback(itemId => state.items.nodeMap[itemId], [state.items.nodeMap]);
|
|
55
|
+
const getItem = React.useCallback(itemId => state.items.itemMap[itemId], [state.items.itemMap]);
|
|
56
|
+
const isItemDisabled = React.useCallback(itemId => {
|
|
57
57
|
if (itemId == null) {
|
|
58
58
|
return false;
|
|
59
59
|
}
|
|
60
|
-
let
|
|
60
|
+
let node = instance.getNode(itemId);
|
|
61
61
|
|
|
62
|
-
// This can be called before the item has been added to the
|
|
63
|
-
if (!
|
|
62
|
+
// This can be called before the item has been added to the item map.
|
|
63
|
+
if (!node) {
|
|
64
64
|
return false;
|
|
65
65
|
}
|
|
66
|
-
if (
|
|
66
|
+
if (node.disabled) {
|
|
67
67
|
return true;
|
|
68
68
|
}
|
|
69
|
-
while (
|
|
70
|
-
|
|
71
|
-
if (
|
|
69
|
+
while (node.parentId != null) {
|
|
70
|
+
node = instance.getNode(node.parentId);
|
|
71
|
+
if (node.disabled) {
|
|
72
72
|
return true;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
return false;
|
|
76
76
|
}, [instance]);
|
|
77
|
-
const getChildrenIds = React.useCallback(itemId => Object.values(state.
|
|
77
|
+
const getChildrenIds = React.useCallback(itemId => Object.values(state.items.nodeMap).filter(item => item.parentId === itemId).sort((a, b) => a.index - b.index).map(child => child.id), [state.items.nodeMap]);
|
|
78
78
|
const getNavigableChildrenIds = itemId => {
|
|
79
79
|
let childrenIds = instance.getChildrenIds(itemId);
|
|
80
80
|
if (!params.disabledItemsFocusable) {
|
|
81
|
-
childrenIds = childrenIds.filter(item => !instance.
|
|
81
|
+
childrenIds = childrenIds.filter(item => !instance.isItemDisabled(item));
|
|
82
82
|
}
|
|
83
83
|
return childrenIds;
|
|
84
84
|
};
|
|
85
|
+
const areItemUpdatesPreventedRef = React.useRef(false);
|
|
86
|
+
const preventItemUpdates = React.useCallback(() => {
|
|
87
|
+
areItemUpdatesPreventedRef.current = true;
|
|
88
|
+
}, []);
|
|
89
|
+
const areItemUpdatesPrevented = React.useCallback(() => areItemUpdatesPreventedRef.current, []);
|
|
85
90
|
React.useEffect(() => {
|
|
91
|
+
if (instance.areItemUpdatesPrevented()) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
86
94
|
setState(prevState => {
|
|
87
|
-
const newState =
|
|
95
|
+
const newState = updateItemsState({
|
|
88
96
|
items: params.items,
|
|
89
97
|
isItemDisabled: params.isItemDisabled,
|
|
90
98
|
getItemId: params.getItemId,
|
|
91
99
|
getItemLabel: params.getItemLabel
|
|
92
100
|
});
|
|
93
|
-
Object.values(prevState.
|
|
94
|
-
if (!newState.nodeMap[
|
|
95
|
-
publishTreeViewEvent(instance, '
|
|
96
|
-
id:
|
|
101
|
+
Object.values(prevState.items.nodeMap).forEach(item => {
|
|
102
|
+
if (!newState.nodeMap[item.id]) {
|
|
103
|
+
publishTreeViewEvent(instance, 'removeItem', {
|
|
104
|
+
id: item.id
|
|
97
105
|
});
|
|
98
106
|
}
|
|
99
107
|
});
|
|
100
108
|
return _extends({}, prevState, {
|
|
101
|
-
|
|
109
|
+
items: newState
|
|
102
110
|
});
|
|
103
111
|
});
|
|
104
112
|
}, [instance, setState, params.items, params.isItemDisabled, params.getItemId, params.getItemLabel]);
|
|
105
|
-
const
|
|
113
|
+
const getItemsToRender = () => {
|
|
106
114
|
const getPropsFromItemId = ({
|
|
107
115
|
id,
|
|
108
116
|
children
|
|
109
117
|
}) => {
|
|
110
|
-
const
|
|
118
|
+
const item = state.items.nodeMap[id];
|
|
111
119
|
return {
|
|
112
|
-
label:
|
|
113
|
-
itemId:
|
|
114
|
-
id:
|
|
120
|
+
label: item.label,
|
|
121
|
+
itemId: item.id,
|
|
122
|
+
id: item.idAttribute,
|
|
115
123
|
children: children?.map(getPropsFromItemId)
|
|
116
124
|
};
|
|
117
125
|
};
|
|
118
|
-
return state.
|
|
126
|
+
return state.items.nodeTree.map(getPropsFromItemId);
|
|
119
127
|
};
|
|
120
128
|
populateInstance(instance, {
|
|
121
129
|
getNode,
|
|
122
130
|
getItem,
|
|
123
|
-
|
|
131
|
+
getItemsToRender,
|
|
124
132
|
getChildrenIds,
|
|
125
133
|
getNavigableChildrenIds,
|
|
126
|
-
|
|
134
|
+
isItemDisabled,
|
|
135
|
+
preventItemUpdates,
|
|
136
|
+
areItemUpdatesPrevented
|
|
127
137
|
});
|
|
128
138
|
populatePublicAPI(publicAPI, {
|
|
129
139
|
getItem
|
|
@@ -134,18 +144,18 @@ export const useTreeViewNodes = ({
|
|
|
134
144
|
}
|
|
135
145
|
};
|
|
136
146
|
};
|
|
137
|
-
|
|
138
|
-
|
|
147
|
+
useTreeViewItems.getInitialState = params => ({
|
|
148
|
+
items: updateItemsState({
|
|
139
149
|
items: params.items,
|
|
140
150
|
isItemDisabled: params.isItemDisabled,
|
|
141
151
|
getItemId: params.getItemId,
|
|
142
152
|
getItemLabel: params.getItemLabel
|
|
143
153
|
})
|
|
144
154
|
});
|
|
145
|
-
|
|
155
|
+
useTreeViewItems.getDefaultizedParams = params => _extends({}, params, {
|
|
146
156
|
disabledItemsFocusable: params.disabledItemsFocusable ?? false
|
|
147
157
|
});
|
|
148
|
-
|
|
158
|
+
useTreeViewItems.params = {
|
|
149
159
|
disabledItemsFocusable: true,
|
|
150
160
|
items: true,
|
|
151
161
|
isItemDisabled: true,
|