@mui/x-tree-view 7.5.1 → 7.6.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 +87 -1
- package/RichTreeView/RichTreeView.js +14 -0
- package/RichTreeView/RichTreeView.types.d.ts +7 -1
- package/SimpleTreeView/SimpleTreeView.js +14 -0
- package/SimpleTreeView/SimpleTreeView.types.d.ts +7 -1
- package/TreeItem/TreeItem.js +35 -7
- package/TreeItem/TreeItem.types.d.ts +1 -0
- package/TreeItem2/TreeItem2.d.ts +12 -6
- package/TreeItem2/TreeItem2.js +21 -10
- package/TreeItem2Provider/TreeItem2Provider.js +4 -2
- package/TreeView/TreeView.js +14 -0
- package/index.js +1 -1
- package/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.d.ts +3 -0
- package/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.js +5 -0
- package/internals/TreeViewItemDepthContext/index.d.ts +1 -0
- package/internals/TreeViewItemDepthContext/index.js +1 -0
- package/internals/TreeViewProvider/TreeViewContext.d.ts +2 -2
- package/internals/TreeViewProvider/TreeViewProvider.types.d.ts +2 -2
- package/internals/index.d.ts +1 -1
- package/internals/models/helpers.d.ts +1 -0
- package/internals/models/plugin.d.ts +12 -5
- package/internals/models/treeView.d.ts +8 -1
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +28 -4
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +11 -26
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +20 -5
- package/internals/plugins/useTreeViewId/useTreeViewId.types.d.ts +10 -1
- package/internals/plugins/useTreeViewItems/useTreeViewItems.js +29 -8
- package/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +50 -7
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +34 -25
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.types.d.ts +22 -4
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +14 -1
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +5 -0
- package/internals/useTreeView/useTreeView.js +8 -3
- package/internals/useTreeView/useTreeView.types.d.ts +2 -1
- package/internals/utils/extractPluginParamsFromProps.d.ts +3 -2
- package/internals/utils/extractPluginParamsFromProps.js +4 -2
- package/internals/utils/tree.js +18 -1
- package/modern/RichTreeView/RichTreeView.js +14 -0
- package/modern/SimpleTreeView/SimpleTreeView.js +14 -0
- package/modern/TreeItem/TreeItem.js +35 -7
- package/modern/TreeItem2/TreeItem2.js +21 -10
- package/modern/TreeItem2Provider/TreeItem2Provider.js +4 -2
- package/modern/TreeView/TreeView.js +14 -0
- package/modern/index.js +1 -1
- package/modern/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.js +5 -0
- package/modern/internals/TreeViewItemDepthContext/index.js +1 -0
- package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +11 -26
- package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.js +29 -8
- package/modern/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +34 -25
- package/modern/internals/useTreeView/useTreeView.js +8 -3
- package/modern/internals/utils/extractPluginParamsFromProps.js +4 -2
- package/modern/internals/utils/tree.js +18 -1
- package/modern/useTreeItem2/useTreeItem2.js +20 -3
- package/node/RichTreeView/RichTreeView.js +14 -0
- package/node/SimpleTreeView/SimpleTreeView.js +14 -0
- package/node/TreeItem/TreeItem.js +35 -7
- package/node/TreeItem2/TreeItem2.js +20 -9
- package/node/TreeItem2Provider/TreeItem2Provider.js +4 -2
- package/node/TreeView/TreeView.js +14 -0
- package/node/index.js +1 -1
- package/node/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.js +13 -0
- package/node/internals/TreeViewItemDepthContext/index.js +12 -0
- package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +11 -26
- package/node/internals/plugins/useTreeViewItems/useTreeViewItems.js +29 -8
- package/node/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +34 -25
- package/node/internals/useTreeView/useTreeView.js +8 -3
- package/node/internals/utils/extractPluginParamsFromProps.js +4 -2
- package/node/internals/utils/tree.js +18 -1
- package/node/useTreeItem2/useTreeItem2.js +20 -3
- package/package.json +3 -3
- package/useTreeItem2/useTreeItem2.js +20 -3
- package/useTreeItem2/useTreeItem2.types.d.ts +12 -0
|
@@ -7,7 +7,11 @@ export interface TreeViewItemMeta {
|
|
|
7
7
|
expandable: boolean;
|
|
8
8
|
disabled: boolean;
|
|
9
9
|
/**
|
|
10
|
-
* Only defined for `RichTreeView`.
|
|
10
|
+
* Only defined for `RichTreeView` and `RichTreeViewPro`.
|
|
11
|
+
*/
|
|
12
|
+
depth?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Only defined for `RichTreeView` and `RichTreeViewPro`.
|
|
11
15
|
*/
|
|
12
16
|
label?: string;
|
|
13
17
|
}
|
|
@@ -18,3 +22,6 @@ export interface TreeViewModel<TValue> {
|
|
|
18
22
|
}
|
|
19
23
|
export type TreeViewInstance<TSignatures extends readonly TreeViewAnyPluginSignature[]> = MergePluginsProperty<TSignatures, 'instance'>;
|
|
20
24
|
export type TreeViewPublicAPI<TSignatures extends readonly TreeViewAnyPluginSignature[]> = MergePluginsProperty<TSignatures, 'publicAPI'>;
|
|
25
|
+
export type TreeViewExperimentalFeatures<TSignatures extends readonly TreeViewAnyPluginSignature[]> = {
|
|
26
|
+
[key in MergePluginsProperty<TSignatures, 'experimentalFeatures'>]?: boolean;
|
|
27
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { DefaultizedProps, TreeViewPluginSignature } from '../../models';
|
|
3
3
|
import { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
4
|
+
import { TreeViewItemId } from '../../../models';
|
|
4
5
|
export interface UseTreeViewExpansionPublicAPI {
|
|
5
6
|
/**
|
|
6
7
|
* Change the expansion status of a given item.
|
|
@@ -11,10 +12,33 @@ export interface UseTreeViewExpansionPublicAPI {
|
|
|
11
12
|
setItemExpansion: (event: React.SyntheticEvent, itemId: string, isExpanded: boolean) => void;
|
|
12
13
|
}
|
|
13
14
|
export interface UseTreeViewExpansionInstance extends UseTreeViewExpansionPublicAPI {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Check if an item is expanded.
|
|
17
|
+
* @param {TreeViewItemId} itemId The id of the item to check.
|
|
18
|
+
* @returns {boolean} `true` if the item is expanded, `false` otherwise.
|
|
19
|
+
*/
|
|
20
|
+
isItemExpanded: (itemId: TreeViewItemId) => boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Check if an item is expandable.
|
|
23
|
+
* Currently, an item is expandable if it has children.
|
|
24
|
+
* In the future, the user should be able to flag an item as expandable even if it has no loaded children to support children lazy loading.
|
|
25
|
+
* @param {TreeViewItemId} itemId The id of the item to check.
|
|
26
|
+
* @returns {boolean} `true` if the item can be expanded, `false` otherwise.
|
|
27
|
+
*/
|
|
28
|
+
isItemExpandable: (itemId: TreeViewItemId) => boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Toggle the current expansion of an item.
|
|
31
|
+
* If it is expanded, it will be collapsed, and vice versa.
|
|
32
|
+
* @param {React.SyntheticEvent} event The UI event that triggered the change.
|
|
33
|
+
* @param {TreeViewItemId} itemId The id of the item to toggle.
|
|
34
|
+
*/
|
|
35
|
+
toggleItemExpansion: (event: React.SyntheticEvent, itemId: TreeViewItemId) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Expand all the siblings (i.e.: the items that have the same parent) of a given item.
|
|
38
|
+
* @param {React.SyntheticEvent} event The UI event that triggered the change.
|
|
39
|
+
* @param {TreeViewItemId} itemId The id of the item whose siblings will be expanded.
|
|
40
|
+
*/
|
|
41
|
+
expandAllSiblings: (event: React.KeyboardEvent, itemId: TreeViewItemId) => void;
|
|
18
42
|
}
|
|
19
43
|
export interface UseTreeViewExpansionParameters {
|
|
20
44
|
/**
|
|
@@ -5,17 +5,15 @@ import ownerDocument from '@mui/utils/ownerDocument';
|
|
|
5
5
|
import { useInstanceEventHandler } from '../../hooks/useInstanceEventHandler';
|
|
6
6
|
import { getActiveElement } from '../../utils/utils';
|
|
7
7
|
import { getFirstNavigableItem } from '../../utils/tree';
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
import { convertSelectedItemsToArray } from '../useTreeViewSelection/useTreeViewSelection.utils';
|
|
9
|
+
const useDefaultFocusableItemId = (instance, selectedItems) => {
|
|
10
|
+
let tabbableItemId = convertSelectedItemsToArray(selectedItems).find(itemId => {
|
|
11
|
+
if (!instance.isItemNavigable(itemId)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
10
14
|
const itemMeta = instance.getItemMeta(itemId);
|
|
11
15
|
return itemMeta && (itemMeta.parentId == null || instance.isItemExpanded(itemMeta.parentId));
|
|
12
|
-
};
|
|
13
|
-
let tabbableItemId;
|
|
14
|
-
if (Array.isArray(selectedItems)) {
|
|
15
|
-
tabbableItemId = selectedItems.find(isItemVisible);
|
|
16
|
-
} else if (selectedItems != null && isItemVisible(selectedItems)) {
|
|
17
|
-
tabbableItemId = selectedItems;
|
|
18
|
-
}
|
|
16
|
+
});
|
|
19
17
|
if (tabbableItemId == null) {
|
|
20
18
|
tabbableItemId = getFirstNavigableItem(instance);
|
|
21
19
|
}
|
|
@@ -29,7 +27,7 @@ export const useTreeViewFocus = ({
|
|
|
29
27
|
models,
|
|
30
28
|
rootRef
|
|
31
29
|
}) => {
|
|
32
|
-
const
|
|
30
|
+
const defaultFocusableItemId = useDefaultFocusableItemId(instance, models.selectedItems.value);
|
|
33
31
|
const setFocusedItemId = useEventCallback(itemId => {
|
|
34
32
|
const cleanItemId = typeof itemId === 'function' ? itemId(state.focusedItemId) : itemId;
|
|
35
33
|
if (state.focusedItemId !== cleanItemId) {
|
|
@@ -61,18 +59,6 @@ export const useTreeViewFocus = ({
|
|
|
61
59
|
innerFocusItem(event, itemId);
|
|
62
60
|
}
|
|
63
61
|
});
|
|
64
|
-
const focusDefaultItem = useEventCallback(event => {
|
|
65
|
-
let itemToFocusId;
|
|
66
|
-
if (Array.isArray(models.selectedItems.value)) {
|
|
67
|
-
itemToFocusId = models.selectedItems.value.find(isItemVisible);
|
|
68
|
-
} else if (models.selectedItems.value != null && isItemVisible(models.selectedItems.value)) {
|
|
69
|
-
itemToFocusId = models.selectedItems.value;
|
|
70
|
-
}
|
|
71
|
-
if (itemToFocusId == null) {
|
|
72
|
-
itemToFocusId = getFirstNavigableItem(instance);
|
|
73
|
-
}
|
|
74
|
-
innerFocusItem(event, itemToFocusId);
|
|
75
|
-
});
|
|
76
62
|
const removeFocusedItem = useEventCallback(() => {
|
|
77
63
|
if (state.focusedItemId == null) {
|
|
78
64
|
return;
|
|
@@ -86,12 +72,12 @@ export const useTreeViewFocus = ({
|
|
|
86
72
|
}
|
|
87
73
|
setFocusedItemId(null);
|
|
88
74
|
});
|
|
89
|
-
const canItemBeTabbed = itemId => itemId ===
|
|
75
|
+
const canItemBeTabbed = itemId => itemId === defaultFocusableItemId;
|
|
90
76
|
useInstanceEventHandler(instance, 'removeItem', ({
|
|
91
77
|
id
|
|
92
78
|
}) => {
|
|
93
79
|
if (state.focusedItemId === id) {
|
|
94
|
-
|
|
80
|
+
innerFocusItem(null, defaultFocusableItemId);
|
|
95
81
|
}
|
|
96
82
|
});
|
|
97
83
|
const createRootHandleFocus = otherHandlers => event => {
|
|
@@ -102,7 +88,7 @@ export const useTreeViewFocus = ({
|
|
|
102
88
|
|
|
103
89
|
// if the event bubbled (which is React specific) we don't want to steal focus
|
|
104
90
|
if (event.target === event.currentTarget) {
|
|
105
|
-
|
|
91
|
+
innerFocusItem(event, defaultFocusableItemId);
|
|
106
92
|
}
|
|
107
93
|
};
|
|
108
94
|
return {
|
|
@@ -116,7 +102,6 @@ export const useTreeViewFocus = ({
|
|
|
116
102
|
isItemFocused,
|
|
117
103
|
canItemBeTabbed,
|
|
118
104
|
focusItem,
|
|
119
|
-
focusDefaultItem,
|
|
120
105
|
removeFocusedItem
|
|
121
106
|
}
|
|
122
107
|
};
|
|
@@ -4,21 +4,36 @@ 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
|
+
import { TreeViewItemId } from '../../../models';
|
|
7
8
|
export interface UseTreeViewFocusPublicAPI {
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
+
* Focus the item with the given id.
|
|
10
11
|
*
|
|
11
12
|
* If the item is the child of a collapsed item, then this method will do nothing.
|
|
12
13
|
* Make sure to expand the ancestors of the item before calling this method if needed.
|
|
13
14
|
* @param {React.SyntheticEvent} event The event source of the action.
|
|
14
|
-
* @param {
|
|
15
|
+
* @param {TreeViewItemId} itemId The id of the item to focus.
|
|
15
16
|
*/
|
|
16
17
|
focusItem: (event: React.SyntheticEvent, itemId: string) => void;
|
|
17
18
|
}
|
|
18
19
|
export interface UseTreeViewFocusInstance extends UseTreeViewFocusPublicAPI {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Check if an item is the currently focused item.
|
|
22
|
+
* @param {TreeViewItemId} itemId The id of the item to check.
|
|
23
|
+
* @returns {boolean} `true` if the item is focused, `false` otherwise.
|
|
24
|
+
*/
|
|
25
|
+
isItemFocused: (itemId: TreeViewItemId) => boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Check if an item should be sequentially focusable (usually with the Tab key).
|
|
28
|
+
* At any point in time, there is a single item that can be sequentially focused in the Tree View.
|
|
29
|
+
* This item is the first selected item (that is both visible and navigable), if any, or the first navigable item if no item is selected.
|
|
30
|
+
* @param {TreeViewItemId} itemId The id of the item to check.
|
|
31
|
+
* @returns {boolean} `true` if the item can be sequentially focusable, `false` otherwise.
|
|
32
|
+
*/
|
|
33
|
+
canItemBeTabbed: (itemId: TreeViewItemId) => boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Remove the focus from the currently focused item (both from the internal state and the DOM).
|
|
36
|
+
*/
|
|
22
37
|
removeFocusedItem: () => void;
|
|
23
38
|
}
|
|
24
39
|
export interface UseTreeViewFocusParameters {
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { TreeViewPluginSignature } from '../../models';
|
|
2
|
+
import { TreeViewItemId } from '../../../models';
|
|
2
3
|
export interface UseTreeViewIdInstance {
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Get the id attribute (i.e.: the `id` attribute passed to the DOM element) of a tree item.
|
|
6
|
+
* If the user explicitly defined an id attribute, it will be returned.
|
|
7
|
+
* Otherwise, the method created a unique id for the item based on the Tree View id attribute and the item `itemId`
|
|
8
|
+
* @param {TreeViewItemId} itemId The id of the item to get the id attribute of.
|
|
9
|
+
* @param {string | undefined} idAttribute The id attribute of the item if explicitly defined by the user.
|
|
10
|
+
* @returns {string} The id attribute of the item.
|
|
11
|
+
*/
|
|
12
|
+
getTreeItemIdAttribute: (itemId: TreeViewItemId, idAttribute: string | undefined) => string;
|
|
4
13
|
}
|
|
5
14
|
export interface UseTreeViewIdParameters {
|
|
6
15
|
/**
|
|
@@ -2,6 +2,8 @@ import _extends from "@babel/runtime/helpers/esm/extends";
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
|
|
4
4
|
import { buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from './useTreeViewItems.utils';
|
|
5
|
+
import { TreeViewItemDepthContext } from '../../TreeViewItemDepthContext';
|
|
6
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
7
|
const updateItemsState = ({
|
|
6
8
|
items,
|
|
7
9
|
isItemDisabled,
|
|
@@ -13,7 +15,7 @@ const updateItemsState = ({
|
|
|
13
15
|
const itemOrderedChildrenIds = {
|
|
14
16
|
[TREE_VIEW_ROOT_PARENT_ID]: []
|
|
15
17
|
};
|
|
16
|
-
const processItem = (item, parentId) => {
|
|
18
|
+
const processItem = (item, depth, parentId) => {
|
|
17
19
|
const id = getItemId ? getItemId(item) : item.id;
|
|
18
20
|
if (id == null) {
|
|
19
21
|
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'));
|
|
@@ -31,7 +33,8 @@ const updateItemsState = ({
|
|
|
31
33
|
parentId,
|
|
32
34
|
idAttribute: undefined,
|
|
33
35
|
expandable: !!item.children?.length,
|
|
34
|
-
disabled: isItemDisabled ? isItemDisabled(item) : false
|
|
36
|
+
disabled: isItemDisabled ? isItemDisabled(item) : false,
|
|
37
|
+
depth
|
|
35
38
|
};
|
|
36
39
|
itemMap[id] = item;
|
|
37
40
|
itemOrderedChildrenIds[id] = [];
|
|
@@ -40,9 +43,9 @@ const updateItemsState = ({
|
|
|
40
43
|
itemOrderedChildrenIds[parentIdWithDefault] = [];
|
|
41
44
|
}
|
|
42
45
|
itemOrderedChildrenIds[parentIdWithDefault].push(id);
|
|
43
|
-
item.children?.forEach(child => processItem(child, id));
|
|
46
|
+
item.children?.forEach(child => processItem(child, depth + 1, id));
|
|
44
47
|
};
|
|
45
|
-
items.forEach(item => processItem(item, null));
|
|
48
|
+
items.forEach(item => processItem(item, 0, null));
|
|
46
49
|
const itemChildrenIndexes = {};
|
|
47
50
|
Object.keys(itemOrderedChildrenIds).forEach(parentId => {
|
|
48
51
|
itemChildrenIndexes[parentId] = buildSiblingIndexes(itemOrderedChildrenIds[parentId]);
|
|
@@ -58,7 +61,8 @@ export const useTreeViewItems = ({
|
|
|
58
61
|
instance,
|
|
59
62
|
params,
|
|
60
63
|
state,
|
|
61
|
-
setState
|
|
64
|
+
setState,
|
|
65
|
+
experimentalFeatures
|
|
62
66
|
}) => {
|
|
63
67
|
const getItemMeta = React.useCallback(itemId => state.items.itemMetaMap[itemId], [state.items.itemMetaMap]);
|
|
64
68
|
const getItem = React.useCallback(itemId => state.items.itemMap[itemId], [state.items.itemMap]);
|
|
@@ -135,6 +139,11 @@ export const useTreeViewItems = ({
|
|
|
135
139
|
return state.items.itemOrderedChildrenIds[TREE_VIEW_ROOT_PARENT_ID].map(getPropsFromItemId);
|
|
136
140
|
};
|
|
137
141
|
return {
|
|
142
|
+
getRootProps: () => ({
|
|
143
|
+
style: {
|
|
144
|
+
'--TreeView-itemChildrenIndentation': typeof params.itemChildrenIndentation === 'number' ? `${params.itemChildrenIndentation}px` : params.itemChildrenIndentation
|
|
145
|
+
}
|
|
146
|
+
}),
|
|
138
147
|
publicAPI: {
|
|
139
148
|
getItem
|
|
140
149
|
},
|
|
@@ -150,7 +159,8 @@ export const useTreeViewItems = ({
|
|
|
150
159
|
areItemUpdatesPrevented
|
|
151
160
|
},
|
|
152
161
|
contextValue: {
|
|
153
|
-
disabledItemsFocusable: params.disabledItemsFocusable
|
|
162
|
+
disabledItemsFocusable: params.disabledItemsFocusable,
|
|
163
|
+
indentationAtItemLevel: experimentalFeatures.indentationAtItemLevel ?? false
|
|
154
164
|
}
|
|
155
165
|
};
|
|
156
166
|
};
|
|
@@ -163,12 +173,23 @@ useTreeViewItems.getInitialState = params => ({
|
|
|
163
173
|
})
|
|
164
174
|
});
|
|
165
175
|
useTreeViewItems.getDefaultizedParams = params => _extends({}, params, {
|
|
166
|
-
disabledItemsFocusable: params.disabledItemsFocusable ?? false
|
|
176
|
+
disabledItemsFocusable: params.disabledItemsFocusable ?? false,
|
|
177
|
+
itemChildrenIndentation: params.itemChildrenIndentation ?? '12px'
|
|
167
178
|
});
|
|
179
|
+
useTreeViewItems.wrapRoot = ({
|
|
180
|
+
children,
|
|
181
|
+
instance
|
|
182
|
+
}) => {
|
|
183
|
+
return /*#__PURE__*/_jsx(TreeViewItemDepthContext.Provider, {
|
|
184
|
+
value: itemId => instance.getItemMeta(itemId)?.depth ?? 0,
|
|
185
|
+
children: children
|
|
186
|
+
});
|
|
187
|
+
};
|
|
168
188
|
useTreeViewItems.params = {
|
|
169
189
|
disabledItemsFocusable: true,
|
|
170
190
|
items: true,
|
|
171
191
|
isItemDisabled: true,
|
|
172
192
|
getItemLabel: true,
|
|
173
|
-
getItemId: true
|
|
193
|
+
getItemId: true,
|
|
194
|
+
itemChildrenIndentation: true
|
|
174
195
|
};
|
|
@@ -13,15 +13,50 @@ export interface UseTreeViewItemsPublicAPI<R extends {}> {
|
|
|
13
13
|
* @param {string} itemId The id of the item to return.
|
|
14
14
|
* @returns {R} The item with the given id.
|
|
15
15
|
*/
|
|
16
|
-
getItem: (itemId:
|
|
16
|
+
getItem: (itemId: TreeViewItemId) => R;
|
|
17
17
|
}
|
|
18
18
|
export interface UseTreeViewItemsInstance<R extends {}> extends UseTreeViewItemsPublicAPI<R> {
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Get the meta-information of an item.
|
|
21
|
+
* Check the `TreeViewItemMeta` type for more information.
|
|
22
|
+
* @param {TreeViewItemId} itemId The id of the item to get the meta-information of.
|
|
23
|
+
* @returns {TreeViewItemMeta} The meta-information of the item.
|
|
24
|
+
*/
|
|
25
|
+
getItemMeta: (itemId: TreeViewItemId) => TreeViewItemMeta;
|
|
26
|
+
/**
|
|
27
|
+
* Get the item that should be rendered.
|
|
28
|
+
* This method is only used on Rich Tree View components.
|
|
29
|
+
* Check the `TreeViewItemProps` type for more information.
|
|
30
|
+
* @returns {TreeViewItemProps[]} The items to render.
|
|
31
|
+
*/
|
|
20
32
|
getItemsToRender: () => TreeViewItemProps[];
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Get the ids of a given item's children.
|
|
35
|
+
* Those ids are returned in the order they should be rendered.
|
|
36
|
+
* @param {TreeViewItemId | null} itemId The id of the item to get the children of.
|
|
37
|
+
* @returns {TreeViewItemId[]} The ids of the item's children.
|
|
38
|
+
*/
|
|
39
|
+
getItemOrderedChildrenIds: (itemId: TreeViewItemId | null) => TreeViewItemId[];
|
|
40
|
+
/**
|
|
41
|
+
* Check if a given item is disabled.
|
|
42
|
+
* An item is disabled if it was marked as disabled or if one of its ancestors is disabled.
|
|
43
|
+
* @param {TreeViewItemId} itemId The id of the item to check.
|
|
44
|
+
* @returns {boolean} `true` if the item is disabled, `false` otherwise.
|
|
45
|
+
*/
|
|
46
|
+
isItemDisabled: (itemId: TreeViewItemId) => boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Check if a given item is navigable (i.e.: if it can be accessed through keyboard navigation).
|
|
49
|
+
* An item is navigable if it is not disabled or if the `disabledItemsFocusable` prop is `true`.
|
|
50
|
+
* @param {TreeViewItemId} itemId The id of the item to check.
|
|
51
|
+
* @returns {boolean} `true` if the item is navigable, `false` otherwise.
|
|
52
|
+
*/
|
|
53
|
+
isItemNavigable: (itemId: TreeViewItemId) => boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Get the index of a given item in its parent's children list.
|
|
56
|
+
* @param {TreeViewItemId} itemId The id of the item to get the index of.
|
|
57
|
+
* @returns {number} The index of the item in its parent's children list.
|
|
58
|
+
*/
|
|
59
|
+
getItemIndex: (itemId: TreeViewItemId) => number;
|
|
25
60
|
/**
|
|
26
61
|
* Freeze any future update to the state based on the `items` prop.
|
|
27
62
|
* This is useful when `useTreeViewJSXItems` is used to avoid having conflicting sources of truth.
|
|
@@ -66,8 +101,14 @@ export interface UseTreeViewItemsParameters<R extends {}> {
|
|
|
66
101
|
* @default (item) => item.id
|
|
67
102
|
*/
|
|
68
103
|
getItemId?: (item: R) => TreeViewItemId;
|
|
104
|
+
/**
|
|
105
|
+
* Horizontal indentation between an item and its children.
|
|
106
|
+
* Examples: 24, "24px", "2rem", "2em".
|
|
107
|
+
* @default 12px
|
|
108
|
+
*/
|
|
109
|
+
itemChildrenIndentation?: string | number;
|
|
69
110
|
}
|
|
70
|
-
export type UseTreeViewItemsDefaultizedParameters<R extends {}> = DefaultizedProps<UseTreeViewItemsParameters<R>, 'disabledItemsFocusable'>;
|
|
111
|
+
export type UseTreeViewItemsDefaultizedParameters<R extends {}> = DefaultizedProps<UseTreeViewItemsParameters<R>, 'disabledItemsFocusable' | 'itemChildrenIndentation'>;
|
|
71
112
|
interface UseTreeViewItemsEventLookup {
|
|
72
113
|
removeItem: {
|
|
73
114
|
params: {
|
|
@@ -90,6 +131,7 @@ export interface UseTreeViewItemsState<R extends {}> {
|
|
|
90
131
|
};
|
|
91
132
|
}
|
|
92
133
|
interface UseTreeViewItemsContextValue extends Pick<UseTreeViewItemsDefaultizedParameters<any>, 'disabledItemsFocusable'> {
|
|
134
|
+
indentationAtItemLevel: boolean;
|
|
93
135
|
}
|
|
94
136
|
export type UseTreeViewItemsSignature = TreeViewPluginSignature<{
|
|
95
137
|
params: UseTreeViewItemsParameters<any>;
|
|
@@ -99,6 +141,7 @@ export type UseTreeViewItemsSignature = TreeViewPluginSignature<{
|
|
|
99
141
|
events: UseTreeViewItemsEventLookup;
|
|
100
142
|
state: UseTreeViewItemsState<any>;
|
|
101
143
|
contextValue: UseTreeViewItemsContextValue;
|
|
144
|
+
experimentalFeatures: 'indentationAtItemLevel';
|
|
102
145
|
}>;
|
|
103
146
|
export type TreeViewItemMetaMap = {
|
|
104
147
|
[itemId: string]: TreeViewItemMeta;
|
|
@@ -7,6 +7,7 @@ import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
|
|
|
7
7
|
import { useTreeViewContext } from '../../TreeViewProvider/useTreeViewContext';
|
|
8
8
|
import { TreeViewChildrenItemContext, TreeViewChildrenItemProvider } from '../../TreeViewProvider/TreeViewChildrenItemProvider';
|
|
9
9
|
import { buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from '../useTreeViewItems/useTreeViewItems.utils';
|
|
10
|
+
import { TreeViewItemDepthContext } from '../../TreeViewItemDepthContext';
|
|
10
11
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
12
|
export const useTreeViewJSXItems = ({
|
|
12
13
|
instance,
|
|
@@ -33,6 +34,23 @@ export const useTreeViewJSXItems = ({
|
|
|
33
34
|
})
|
|
34
35
|
});
|
|
35
36
|
});
|
|
37
|
+
return () => {
|
|
38
|
+
setState(prevState => {
|
|
39
|
+
const newItemMetaMap = _extends({}, prevState.items.itemMetaMap);
|
|
40
|
+
const newItemMap = _extends({}, prevState.items.itemMap);
|
|
41
|
+
delete newItemMetaMap[item.id];
|
|
42
|
+
delete newItemMap[item.id];
|
|
43
|
+
return _extends({}, prevState, {
|
|
44
|
+
items: _extends({}, prevState.items, {
|
|
45
|
+
itemMetaMap: newItemMetaMap,
|
|
46
|
+
itemMap: newItemMap
|
|
47
|
+
})
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
publishTreeViewEvent(instance, 'removeItem', {
|
|
51
|
+
id: item.id
|
|
52
|
+
});
|
|
53
|
+
};
|
|
36
54
|
});
|
|
37
55
|
const setJSXItemsOrderedChildrenIds = (parentId, orderedChildrenIds) => {
|
|
38
56
|
const parentIdWithDefault = parentId ?? TREE_VIEW_ROOT_PARENT_ID;
|
|
@@ -47,23 +65,6 @@ export const useTreeViewJSXItems = ({
|
|
|
47
65
|
})
|
|
48
66
|
}));
|
|
49
67
|
};
|
|
50
|
-
const removeJSXItem = useEventCallback(itemId => {
|
|
51
|
-
setState(prevState => {
|
|
52
|
-
const newItemMetaMap = _extends({}, prevState.items.itemMetaMap);
|
|
53
|
-
const newItemMap = _extends({}, prevState.items.itemMap);
|
|
54
|
-
delete newItemMetaMap[itemId];
|
|
55
|
-
delete newItemMap[itemId];
|
|
56
|
-
return _extends({}, prevState, {
|
|
57
|
-
items: _extends({}, prevState.items, {
|
|
58
|
-
itemMetaMap: newItemMetaMap,
|
|
59
|
-
itemMap: newItemMap
|
|
60
|
-
})
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
publishTreeViewEvent(instance, 'removeItem', {
|
|
64
|
-
id: itemId
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
68
|
const mapFirstCharFromJSX = useEventCallback((itemId, firstChar) => {
|
|
68
69
|
instance.updateFirstCharMap(firstCharMap => {
|
|
69
70
|
firstCharMap[itemId] = firstChar;
|
|
@@ -80,7 +81,6 @@ export const useTreeViewJSXItems = ({
|
|
|
80
81
|
return {
|
|
81
82
|
instance: {
|
|
82
83
|
insertJSXItem,
|
|
83
|
-
removeJSXItem,
|
|
84
84
|
setJSXItemsOrderedChildrenIds,
|
|
85
85
|
mapFirstCharFromJSX
|
|
86
86
|
}
|
|
@@ -129,14 +129,13 @@ const useTreeViewJSXItemsItemPlugin = ({
|
|
|
129
129
|
};
|
|
130
130
|
}, [instance, registerChild, unregisterChild, itemId, id]);
|
|
131
131
|
React.useEffect(() => {
|
|
132
|
-
instance.insertJSXItem({
|
|
132
|
+
return instance.insertJSXItem({
|
|
133
133
|
id: itemId,
|
|
134
134
|
idAttribute: id,
|
|
135
135
|
parentId,
|
|
136
136
|
expandable,
|
|
137
137
|
disabled
|
|
138
138
|
});
|
|
139
|
-
return () => instance.removeJSXItem(itemId);
|
|
140
139
|
}, [instance, parentId, itemId, expandable, disabled, id]);
|
|
141
140
|
React.useEffect(() => {
|
|
142
141
|
if (label) {
|
|
@@ -153,13 +152,23 @@ useTreeViewJSXItems.itemPlugin = useTreeViewJSXItemsItemPlugin;
|
|
|
153
152
|
useTreeViewJSXItems.wrapItem = ({
|
|
154
153
|
children,
|
|
155
154
|
itemId
|
|
156
|
-
}) =>
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
155
|
+
}) => {
|
|
156
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
157
|
+
const depthContext = React.useContext(TreeViewItemDepthContext);
|
|
158
|
+
return /*#__PURE__*/_jsx(TreeViewChildrenItemProvider, {
|
|
159
|
+
itemId: itemId,
|
|
160
|
+
children: /*#__PURE__*/_jsx(TreeViewItemDepthContext.Provider, {
|
|
161
|
+
value: depthContext + 1,
|
|
162
|
+
children: children
|
|
163
|
+
})
|
|
164
|
+
});
|
|
165
|
+
};
|
|
160
166
|
useTreeViewJSXItems.wrapRoot = ({
|
|
161
167
|
children
|
|
162
168
|
}) => /*#__PURE__*/_jsx(TreeViewChildrenItemProvider, {
|
|
163
|
-
children:
|
|
169
|
+
children: /*#__PURE__*/_jsx(TreeViewItemDepthContext.Provider, {
|
|
170
|
+
value: 0,
|
|
171
|
+
children: children
|
|
172
|
+
})
|
|
164
173
|
});
|
|
165
174
|
useTreeViewJSXItems.params = {};
|
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
import { TreeViewItemMeta, TreeViewPluginSignature } from '../../models';
|
|
2
2
|
import { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
3
3
|
import { UseTreeViewKeyboardNavigationSignature } from '../useTreeViewKeyboardNavigation';
|
|
4
|
+
import { TreeViewItemId } from '../../../models';
|
|
4
5
|
export interface UseTreeViewItemsInstance {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Insert a new item in the state from a Tree Item component.
|
|
8
|
+
* @param {TreeViewItemMeta} item The meta-information of the item to insert.
|
|
9
|
+
* @returns {() => void} A function to remove the item from the state.
|
|
10
|
+
*/
|
|
11
|
+
insertJSXItem: (item: TreeViewItemMeta) => () => void;
|
|
12
|
+
/**
|
|
13
|
+
* Updates the `firstCharMap` to register the first character of the given item's label.
|
|
14
|
+
* This map is used to navigate the tree using type-ahead search.
|
|
15
|
+
* @param {TreeViewItemId} itemId The id of the item to map the first character of.
|
|
16
|
+
* @param {string} firstChar The first character of the item's label.
|
|
17
|
+
* @returns {() => void} A function to remove the item from the `firstCharMap`.
|
|
18
|
+
*/
|
|
19
|
+
mapFirstCharFromJSX: (itemId: TreeViewItemId, firstChar: string) => () => void;
|
|
20
|
+
/**
|
|
21
|
+
* Store the ids of a given item's children in the state.
|
|
22
|
+
* Those ids must be passed in the order they should be rendered.
|
|
23
|
+
* @param {TreeViewItemId | null} parentId The id of the item to store the children of.
|
|
24
|
+
* @param {TreeViewItemId[]} orderedChildrenIds The ids of the item's children.
|
|
25
|
+
*/
|
|
26
|
+
setJSXItemsOrderedChildrenIds: (parentId: TreeViewItemId | null, orderedChildrenIds: TreeViewItemId[]) => void;
|
|
9
27
|
}
|
|
10
28
|
export interface UseTreeViewJSXItemsParameters {
|
|
11
29
|
}
|
package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts
CHANGED
|
@@ -5,9 +5,22 @@ import { UseTreeViewSelectionSignature } from '../useTreeViewSelection';
|
|
|
5
5
|
import { UseTreeViewFocusSignature } from '../useTreeViewFocus';
|
|
6
6
|
import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion';
|
|
7
7
|
import { MuiCancellableEvent } from '../../models/MuiCancellableEvent';
|
|
8
|
+
import { TreeViewItemId } from '../../../models';
|
|
8
9
|
export interface UseTreeViewKeyboardNavigationInstance {
|
|
10
|
+
/**
|
|
11
|
+
* Updates the `firstCharMap` to add/remove the first character of some item's labels.
|
|
12
|
+
* This map is used to navigate the tree using type-ahead search.
|
|
13
|
+
* This method is only used by the `useTreeViewJSXItems` plugin, otherwise the updates are handled internally.
|
|
14
|
+
* @param {(map: TreeViewFirstCharMap) => TreeViewFirstCharMap} updater The function to update the map.
|
|
15
|
+
*/
|
|
9
16
|
updateFirstCharMap: (updater: (map: TreeViewFirstCharMap) => TreeViewFirstCharMap) => void;
|
|
10
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Callback fired when a key is pressed on an item.
|
|
19
|
+
* Handles all the keyboard navigation logic.
|
|
20
|
+
* @param {React.KeyboardEvent<HTMLElement> & MuiCancellableEvent} event The keyboard event that triggered the callback.
|
|
21
|
+
* @param {TreeViewItemId} itemId The id of the item that the event was triggered on.
|
|
22
|
+
*/
|
|
23
|
+
handleItemKeyDown: (event: React.KeyboardEvent<HTMLElement> & MuiCancellableEvent, itemId: TreeViewItemId) => void;
|
|
11
24
|
}
|
|
12
25
|
export type UseTreeViewKeyboardNavigationSignature = TreeViewPluginSignature<{
|
|
13
26
|
instance: UseTreeViewKeyboardNavigationInstance;
|
|
@@ -3,6 +3,11 @@ import type { DefaultizedProps, TreeViewPluginSignature } from '../../models';
|
|
|
3
3
|
import { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
4
4
|
import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion';
|
|
5
5
|
export interface UseTreeViewSelectionInstance {
|
|
6
|
+
/**
|
|
7
|
+
* Check if an item is selected.
|
|
8
|
+
* @param {TreeViewItemId} itemId The id of the item to check.
|
|
9
|
+
* @returns {boolean} `true` if the item is selected, `false` otherwise.
|
|
10
|
+
*/
|
|
6
11
|
isItemSelected: (itemId: string) => boolean;
|
|
7
12
|
/**
|
|
8
13
|
* Select or deselect an item.
|
|
@@ -48,6 +48,7 @@ export const useTreeView = inParams => {
|
|
|
48
48
|
params,
|
|
49
49
|
slots: params.slots,
|
|
50
50
|
slotProps: params.slotProps,
|
|
51
|
+
experimentalFeatures: params.experimentalFeatures,
|
|
51
52
|
state,
|
|
52
53
|
setState,
|
|
53
54
|
rootRef: innerRootRef,
|
|
@@ -100,19 +101,23 @@ export const useTreeView = inParams => {
|
|
|
100
101
|
itemWrappers.forEach(itemWrapper => {
|
|
101
102
|
finalChildren = itemWrapper({
|
|
102
103
|
itemId,
|
|
103
|
-
children: finalChildren
|
|
104
|
+
children: finalChildren,
|
|
105
|
+
instance
|
|
104
106
|
});
|
|
105
107
|
});
|
|
106
108
|
return finalChildren;
|
|
107
109
|
};
|
|
108
|
-
const rootWrappers = plugins.map(plugin => plugin.wrapRoot).filter(wrapRoot => !!wrapRoot)
|
|
110
|
+
const rootWrappers = plugins.map(plugin => plugin.wrapRoot).filter(wrapRoot => !!wrapRoot)
|
|
111
|
+
// The wrappers are reversed to ensure that the first wrapper is the outermost one.
|
|
112
|
+
.reverse();
|
|
109
113
|
contextValue.wrapRoot = ({
|
|
110
114
|
children
|
|
111
115
|
}) => {
|
|
112
116
|
let finalChildren = children;
|
|
113
117
|
rootWrappers.forEach(rootWrapper => {
|
|
114
118
|
finalChildren = rootWrapper({
|
|
115
|
-
children: finalChildren
|
|
119
|
+
children: finalChildren,
|
|
120
|
+
instance
|
|
116
121
|
});
|
|
117
122
|
});
|
|
118
123
|
return finalChildren;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { EventHandlers } from '@mui/base/utils';
|
|
3
3
|
import type { TreeViewContextValue } from '../TreeViewProvider';
|
|
4
|
-
import { TreeViewAnyPluginSignature, TreeViewPlugin, ConvertPluginsIntoSignatures, MergePluginsProperty, TreeViewInstance, TreeViewPublicAPI } from '../models';
|
|
4
|
+
import { TreeViewAnyPluginSignature, TreeViewPlugin, ConvertPluginsIntoSignatures, MergePluginsProperty, TreeViewInstance, TreeViewPublicAPI, TreeViewExperimentalFeatures } from '../models';
|
|
5
5
|
export type UseTreeViewParameters<TPlugins extends readonly TreeViewPlugin<TreeViewAnyPluginSignature>[]> = UseTreeViewBaseParameters<TPlugins> & MergePluginsProperty<ConvertPluginsIntoSignatures<TPlugins>, 'params'>;
|
|
6
6
|
export interface UseTreeViewBaseParameters<TPlugins extends readonly TreeViewPlugin<TreeViewAnyPluginSignature>[]> {
|
|
7
7
|
apiRef: React.MutableRefObject<TreeViewPublicAPI<ConvertPluginsIntoSignatures<TPlugins>>> | undefined;
|
|
@@ -9,6 +9,7 @@ export interface UseTreeViewBaseParameters<TPlugins extends readonly TreeViewPlu
|
|
|
9
9
|
plugins: TPlugins;
|
|
10
10
|
slots: MergePluginsProperty<ConvertPluginsIntoSignatures<TPlugins>, 'slots'>;
|
|
11
11
|
slotProps: MergePluginsProperty<ConvertPluginsIntoSignatures<TPlugins>, 'slotProps'>;
|
|
12
|
+
experimentalFeatures: TreeViewExperimentalFeatures<ConvertPluginsIntoSignatures<TPlugins>>;
|
|
12
13
|
}
|
|
13
14
|
export type UseTreeViewDefaultizedParameters<TPlugins extends readonly TreeViewPlugin<TreeViewAnyPluginSignature>[]> = UseTreeViewBaseParameters<TPlugins> & MergePluginsProperty<ConvertPluginsIntoSignatures<TPlugins>, 'defaultizedParams'>;
|
|
14
15
|
export interface UseTreeViewRootSlotProps extends Pick<React.HTMLAttributes<HTMLUListElement>, 'onFocus' | 'onBlur' | 'onKeyDown' | 'id' | 'aria-multiselectable' | 'role' | 'tabIndex'> {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { ConvertPluginsIntoSignatures, MergePluginsProperty, TreeViewPlugin, TreeViewPublicAPI } from '../models';
|
|
2
|
+
import { ConvertPluginsIntoSignatures, MergePluginsProperty, TreeViewExperimentalFeatures, TreeViewPlugin, TreeViewPublicAPI } from '../models';
|
|
3
3
|
import { UseTreeViewBaseParameters } from '../useTreeView/useTreeView.types';
|
|
4
4
|
export declare const extractPluginParamsFromProps: <TPlugins extends readonly TreeViewPlugin<any>[], TSlots extends MergePluginsProperty<TPlugins, "slots">, TSlotProps extends MergePluginsProperty<TPlugins, "slotProps">, TProps extends {
|
|
5
5
|
slots?: TSlots | undefined;
|
|
6
6
|
slotProps?: TSlotProps | undefined;
|
|
7
7
|
apiRef?: React.MutableRefObject<TreeViewPublicAPI<ConvertPluginsIntoSignatures<TPlugins>> | undefined> | undefined;
|
|
8
|
-
|
|
8
|
+
experimentalFeatures?: TreeViewExperimentalFeatures<ConvertPluginsIntoSignatures<TPlugins>> | undefined;
|
|
9
|
+
}>({ props: { slots, slotProps, apiRef, experimentalFeatures, ...props }, plugins, rootRef, }: {
|
|
9
10
|
props: TProps;
|
|
10
11
|
plugins: TPlugins;
|
|
11
12
|
rootRef?: React.Ref<HTMLUListElement>;
|