@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
|
@@ -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
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const TREE_VIEW_ROOT_PARENT_ID = '__TREE_VIEW_ROOT_PARENT_ID__';
|
|
2
|
+
export const buildSiblingIndexes = siblings => {
|
|
3
|
+
const siblingsIndexLookup = {};
|
|
4
|
+
siblings.forEach((childId, index) => {
|
|
5
|
+
siblingsIndexLookup[childId] = index;
|
|
6
|
+
});
|
|
7
|
+
return siblingsIndexLookup;
|
|
8
|
+
};
|
|
@@ -2,10 +2,11 @@ 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 useForkRef from '@mui/utils/useForkRef';
|
|
5
|
-
import
|
|
5
|
+
import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
|
|
6
6
|
import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
|
|
7
7
|
import { useTreeViewContext } from '../../TreeViewProvider/useTreeViewContext';
|
|
8
|
-
import {
|
|
8
|
+
import { TreeViewChildrenItemContext, TreeViewChildrenItemProvider } from '../../TreeViewProvider/TreeViewChildrenItemProvider';
|
|
9
|
+
import { buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from '../useTreeViewItems/useTreeViewItems.utils';
|
|
9
10
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
11
|
export const useTreeViewJSXItems = ({
|
|
11
12
|
instance,
|
|
@@ -14,12 +15,12 @@ export const useTreeViewJSXItems = ({
|
|
|
14
15
|
instance.preventItemUpdates();
|
|
15
16
|
const insertJSXItem = useEventCallback(item => {
|
|
16
17
|
setState(prevState => {
|
|
17
|
-
if (prevState.items.
|
|
18
|
-
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.', `
|
|
18
|
+
if (prevState.items.itemMetaMap[item.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.', `Two items were provided with the same id in the \`items\` prop: "${item.id}"`].join('\n'));
|
|
19
20
|
}
|
|
20
21
|
return _extends({}, prevState, {
|
|
21
22
|
items: _extends({}, prevState.items, {
|
|
22
|
-
|
|
23
|
+
itemMetaMap: _extends({}, prevState.items.itemMetaMap, {
|
|
23
24
|
[item.id]: item
|
|
24
25
|
}),
|
|
25
26
|
// For `SimpleTreeView`, we don't have a proper `item` object, so we create a very basic one.
|
|
@@ -33,15 +34,28 @@ export const useTreeViewJSXItems = ({
|
|
|
33
34
|
});
|
|
34
35
|
});
|
|
35
36
|
});
|
|
37
|
+
const setJSXItemsOrderedChildrenIds = (parentId, orderedChildrenIds) => {
|
|
38
|
+
const parentIdWithDefault = parentId ?? TREE_VIEW_ROOT_PARENT_ID;
|
|
39
|
+
setState(prevState => _extends({}, prevState, {
|
|
40
|
+
items: _extends({}, prevState.items, {
|
|
41
|
+
itemOrderedChildrenIds: _extends({}, prevState.items.itemOrderedChildrenIds, {
|
|
42
|
+
[parentIdWithDefault]: orderedChildrenIds
|
|
43
|
+
}),
|
|
44
|
+
itemChildrenIndexes: _extends({}, prevState.items.itemChildrenIndexes, {
|
|
45
|
+
[parentIdWithDefault]: buildSiblingIndexes(orderedChildrenIds)
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
}));
|
|
49
|
+
};
|
|
36
50
|
const removeJSXItem = useEventCallback(itemId => {
|
|
37
51
|
setState(prevState => {
|
|
38
|
-
const
|
|
52
|
+
const newItemMetaMap = _extends({}, prevState.items.itemMetaMap);
|
|
39
53
|
const newItemMap = _extends({}, prevState.items.itemMap);
|
|
40
|
-
delete
|
|
54
|
+
delete newItemMetaMap[itemId];
|
|
41
55
|
delete newItemMap[itemId];
|
|
42
56
|
return _extends({}, prevState, {
|
|
43
57
|
items: _extends({}, prevState.items, {
|
|
44
|
-
|
|
58
|
+
itemMetaMap: newItemMetaMap,
|
|
45
59
|
itemMap: newItemMap
|
|
46
60
|
})
|
|
47
61
|
});
|
|
@@ -63,17 +77,23 @@ export const useTreeViewJSXItems = ({
|
|
|
63
77
|
});
|
|
64
78
|
};
|
|
65
79
|
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
80
|
+
return {
|
|
81
|
+
instance: {
|
|
82
|
+
insertJSXItem,
|
|
83
|
+
removeJSXItem,
|
|
84
|
+
setJSXItemsOrderedChildrenIds,
|
|
85
|
+
mapFirstCharFromJSX
|
|
86
|
+
}
|
|
87
|
+
};
|
|
71
88
|
};
|
|
72
89
|
const useTreeViewJSXItemsItemPlugin = ({
|
|
73
90
|
props,
|
|
74
91
|
rootRef,
|
|
75
92
|
contentRef
|
|
76
93
|
}) => {
|
|
94
|
+
const {
|
|
95
|
+
instance
|
|
96
|
+
} = useTreeViewContext();
|
|
77
97
|
const {
|
|
78
98
|
children,
|
|
79
99
|
disabled = false,
|
|
@@ -81,9 +101,15 @@ const useTreeViewJSXItemsItemPlugin = ({
|
|
|
81
101
|
itemId,
|
|
82
102
|
id
|
|
83
103
|
} = props;
|
|
104
|
+
const parentContext = React.useContext(TreeViewChildrenItemContext);
|
|
105
|
+
if (parentContext == null) {
|
|
106
|
+
throw new Error(['MUI X: Could not find the Tree View Children Item context.', 'It looks like you rendered your component outside of a SimpleTreeView parent component.', 'This can also happen if you are bundling multiple versions of the Tree View.'].join('\n'));
|
|
107
|
+
}
|
|
84
108
|
const {
|
|
85
|
-
|
|
86
|
-
|
|
109
|
+
registerChild,
|
|
110
|
+
unregisterChild,
|
|
111
|
+
parentId
|
|
112
|
+
} = parentContext;
|
|
87
113
|
const isExpandable = reactChildren => {
|
|
88
114
|
if (Array.isArray(reactChildren)) {
|
|
89
115
|
return reactChildren.length > 0 && reactChildren.some(isExpandable);
|
|
@@ -91,33 +117,27 @@ const useTreeViewJSXItemsItemPlugin = ({
|
|
|
91
117
|
return Boolean(reactChildren);
|
|
92
118
|
};
|
|
93
119
|
const expandable = isExpandable(children);
|
|
94
|
-
const [treeItemElement, setTreeItemElement] = React.useState(null);
|
|
95
120
|
const pluginContentRef = React.useRef(null);
|
|
96
|
-
const handleRootRef = useForkRef(setTreeItemElement, rootRef);
|
|
97
121
|
const handleContentRef = useForkRef(pluginContentRef, contentRef);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
122
|
+
|
|
123
|
+
// Prevent any flashing
|
|
124
|
+
useEnhancedEffect(() => {
|
|
125
|
+
const idAttributeWithDefault = instance.getTreeItemIdAttribute(itemId, id);
|
|
126
|
+
registerChild(idAttributeWithDefault, itemId);
|
|
127
|
+
return () => {
|
|
128
|
+
unregisterChild(idAttributeWithDefault);
|
|
129
|
+
};
|
|
130
|
+
}, [instance, registerChild, unregisterChild, itemId, id]);
|
|
106
131
|
React.useEffect(() => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
});
|
|
117
|
-
return () => instance.removeJSXItem(itemId);
|
|
118
|
-
}
|
|
119
|
-
return undefined;
|
|
120
|
-
}, [instance, parentId, index, itemId, expandable, disabled, id]);
|
|
132
|
+
instance.insertJSXItem({
|
|
133
|
+
id: itemId,
|
|
134
|
+
idAttribute: id,
|
|
135
|
+
parentId,
|
|
136
|
+
expandable,
|
|
137
|
+
disabled
|
|
138
|
+
});
|
|
139
|
+
return () => instance.removeJSXItem(itemId);
|
|
140
|
+
}, [instance, parentId, itemId, expandable, disabled, id]);
|
|
121
141
|
React.useEffect(() => {
|
|
122
142
|
if (label) {
|
|
123
143
|
return instance.mapFirstCharFromJSX(itemId, (pluginContentRef.current?.textContent ?? '').substring(0, 1).toLowerCase());
|
|
@@ -126,15 +146,20 @@ const useTreeViewJSXItemsItemPlugin = ({
|
|
|
126
146
|
}, [instance, itemId, label]);
|
|
127
147
|
return {
|
|
128
148
|
contentRef: handleContentRef,
|
|
129
|
-
rootRef
|
|
149
|
+
rootRef
|
|
130
150
|
};
|
|
131
151
|
};
|
|
132
152
|
useTreeViewJSXItems.itemPlugin = useTreeViewJSXItemsItemPlugin;
|
|
133
153
|
useTreeViewJSXItems.wrapItem = ({
|
|
134
154
|
children,
|
|
135
155
|
itemId
|
|
136
|
-
}) => /*#__PURE__*/_jsx(
|
|
137
|
-
|
|
156
|
+
}) => /*#__PURE__*/_jsx(TreeViewChildrenItemProvider, {
|
|
157
|
+
itemId: itemId,
|
|
158
|
+
children: children
|
|
159
|
+
});
|
|
160
|
+
useTreeViewJSXItems.wrapRoot = ({
|
|
161
|
+
children
|
|
162
|
+
}) => /*#__PURE__*/_jsx(TreeViewChildrenItemProvider, {
|
|
138
163
|
children: children
|
|
139
164
|
});
|
|
140
165
|
useTreeViewJSXItems.params = {};
|
package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useTheme } from '@mui/material/styles';
|
|
3
3
|
import useEventCallback from '@mui/utils/useEventCallback';
|
|
4
|
-
import {
|
|
4
|
+
import { getFirstNavigableItem, getLastNavigableItem, getNextNavigableItem, getPreviousNavigableItem } from '../../utils/tree';
|
|
5
5
|
function isPrintableCharacter(string) {
|
|
6
6
|
return !!string && string.length === 1 && !!string.match(/\S/);
|
|
7
7
|
}
|
|
@@ -29,12 +29,12 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
const newFirstCharMap = {};
|
|
32
|
-
const processItem =
|
|
33
|
-
newFirstCharMap[
|
|
32
|
+
const processItem = item => {
|
|
33
|
+
newFirstCharMap[item.id] = item.label.substring(0, 1).toLowerCase();
|
|
34
34
|
};
|
|
35
|
-
Object.values(state.items.
|
|
35
|
+
Object.values(state.items.itemMetaMap).forEach(processItem);
|
|
36
36
|
firstCharMap.current = newFirstCharMap;
|
|
37
|
-
}, [state.items.
|
|
37
|
+
}, [state.items.itemMetaMap, params.getItemId, instance]);
|
|
38
38
|
const getFirstMatchingItem = (itemId, firstChar) => {
|
|
39
39
|
let start;
|
|
40
40
|
let index;
|
|
@@ -43,7 +43,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
43
43
|
const firstChars = [];
|
|
44
44
|
// This really only works since the ids are strings
|
|
45
45
|
Object.keys(firstCharMap.current).forEach(mapItemId => {
|
|
46
|
-
const map = instance.
|
|
46
|
+
const map = instance.getItemMeta(mapItemId);
|
|
47
47
|
const visible = map.parentId ? instance.isItemExpanded(map.parentId) : true;
|
|
48
48
|
const shouldBeSkipped = params.disabledItemsFocusable ? false : instance.isItemDisabled(mapItemId);
|
|
49
49
|
if (visible && !shouldBeSkipped) {
|
|
@@ -128,7 +128,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
128
128
|
// Focus the next focusable item
|
|
129
129
|
case key === 'ArrowDown':
|
|
130
130
|
{
|
|
131
|
-
const nextItem =
|
|
131
|
+
const nextItem = getNextNavigableItem(instance, itemId);
|
|
132
132
|
if (nextItem) {
|
|
133
133
|
event.preventDefault();
|
|
134
134
|
instance.focusItem(event, nextItem);
|
|
@@ -148,7 +148,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
148
148
|
// Focuses the previous focusable item
|
|
149
149
|
case key === 'ArrowUp':
|
|
150
150
|
{
|
|
151
|
-
const previousItem =
|
|
151
|
+
const previousItem = getPreviousNavigableItem(instance, itemId);
|
|
152
152
|
if (previousItem) {
|
|
153
153
|
event.preventDefault();
|
|
154
154
|
instance.focusItem(event, previousItem);
|
|
@@ -170,7 +170,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
170
170
|
case key === 'ArrowRight' && !isRTL || key === 'ArrowLeft' && isRTL:
|
|
171
171
|
{
|
|
172
172
|
if (instance.isItemExpanded(itemId)) {
|
|
173
|
-
const nextItemId =
|
|
173
|
+
const nextItemId = getNextNavigableItem(instance, itemId);
|
|
174
174
|
if (nextItemId) {
|
|
175
175
|
instance.focusItem(event, nextItemId);
|
|
176
176
|
event.preventDefault();
|
|
@@ -190,7 +190,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
190
190
|
instance.toggleItemExpansion(event, itemId);
|
|
191
191
|
event.preventDefault();
|
|
192
192
|
} else {
|
|
193
|
-
const parent = instance.
|
|
193
|
+
const parent = instance.getItemMeta(itemId).parentId;
|
|
194
194
|
if (parent) {
|
|
195
195
|
instance.focusItem(event, parent);
|
|
196
196
|
event.preventDefault();
|
|
@@ -202,7 +202,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
202
202
|
// Focuses the first item in the tree
|
|
203
203
|
case key === 'Home':
|
|
204
204
|
{
|
|
205
|
-
instance.focusItem(event,
|
|
205
|
+
instance.focusItem(event, getFirstNavigableItem(instance));
|
|
206
206
|
|
|
207
207
|
// Multi select behavior when pressing Ctrl + Shift + Home
|
|
208
208
|
// Selects the focused item and all items up to the first item.
|
|
@@ -216,7 +216,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
216
216
|
// Focuses the last item in the tree
|
|
217
217
|
case key === 'End':
|
|
218
218
|
{
|
|
219
|
-
instance.focusItem(event,
|
|
219
|
+
instance.focusItem(event, getLastNavigableItem(instance));
|
|
220
220
|
|
|
221
221
|
// Multi select behavior when pressing Ctrl + Shirt + End
|
|
222
222
|
// Selects the focused item and all the items down to the last item.
|
|
@@ -240,8 +240,8 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
240
240
|
case key === 'a' && ctrlPressed && params.multiSelect && !params.disableSelection:
|
|
241
241
|
{
|
|
242
242
|
instance.selectRange(event, {
|
|
243
|
-
start:
|
|
244
|
-
end:
|
|
243
|
+
start: getFirstNavigableItem(instance),
|
|
244
|
+
end: getLastNavigableItem(instance)
|
|
245
245
|
});
|
|
246
246
|
event.preventDefault();
|
|
247
247
|
break;
|
|
@@ -260,9 +260,11 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
260
260
|
}
|
|
261
261
|
}
|
|
262
262
|
};
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
263
|
+
return {
|
|
264
|
+
instance: {
|
|
265
|
+
updateFirstCharMap,
|
|
266
|
+
handleItemKeyDown
|
|
267
|
+
}
|
|
268
|
+
};
|
|
267
269
|
};
|
|
268
270
|
useTreeViewKeyboardNavigation.params = {};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import { findOrderInTremauxTree } from './useTreeViewSelection.utils';
|
|
3
|
+
import { getFirstNavigableItem, getLastNavigableItem, getNavigableItemsInRange } from '../../utils/tree';
|
|
5
4
|
export const useTreeViewSelection = ({
|
|
6
5
|
instance,
|
|
7
6
|
params,
|
|
@@ -58,16 +57,6 @@ export const useTreeViewSelection = ({
|
|
|
58
57
|
lastSelectionWasRange.current = false;
|
|
59
58
|
currentRangeSelection.current = [];
|
|
60
59
|
};
|
|
61
|
-
const getItemsInRange = (itemAId, itemBId) => {
|
|
62
|
-
const [first, last] = findOrderInTremauxTree(instance, itemAId, itemBId);
|
|
63
|
-
const items = [first];
|
|
64
|
-
let current = first;
|
|
65
|
-
while (current !== last) {
|
|
66
|
-
current = getNextItem(instance, current);
|
|
67
|
-
items.push(current);
|
|
68
|
-
}
|
|
69
|
-
return items;
|
|
70
|
-
};
|
|
71
60
|
const handleRangeArrowSelect = (event, items) => {
|
|
72
61
|
let base = models.selectedItems.value.slice();
|
|
73
62
|
const {
|
|
@@ -105,7 +94,7 @@ export const useTreeViewSelection = ({
|
|
|
105
94
|
if (lastSelectionWasRange.current) {
|
|
106
95
|
base = base.filter(id => currentRangeSelection.current.indexOf(id) === -1);
|
|
107
96
|
}
|
|
108
|
-
let range =
|
|
97
|
+
let range = getNavigableItemsInRange(instance, start, end);
|
|
109
98
|
range = range.filter(item => !instance.isItemDisabled(item));
|
|
110
99
|
currentRangeSelection.current = range;
|
|
111
100
|
let newSelected = base.concat(range);
|
|
@@ -142,7 +131,7 @@ export const useTreeViewSelection = ({
|
|
|
142
131
|
const start = lastSelectionWasRange.current ? lastSelectedItem.current : itemId;
|
|
143
132
|
instance.selectRange(event, {
|
|
144
133
|
start,
|
|
145
|
-
end:
|
|
134
|
+
end: getFirstNavigableItem(instance)
|
|
146
135
|
});
|
|
147
136
|
};
|
|
148
137
|
const rangeSelectToLast = (event, itemId) => {
|
|
@@ -152,20 +141,20 @@ export const useTreeViewSelection = ({
|
|
|
152
141
|
const start = lastSelectionWasRange.current ? lastSelectedItem.current : itemId;
|
|
153
142
|
instance.selectRange(event, {
|
|
154
143
|
start,
|
|
155
|
-
end:
|
|
144
|
+
end: getLastNavigableItem(instance)
|
|
156
145
|
});
|
|
157
146
|
};
|
|
158
|
-
populateInstance(instance, {
|
|
159
|
-
isItemSelected,
|
|
160
|
-
selectItem,
|
|
161
|
-
selectRange,
|
|
162
|
-
rangeSelectToLast,
|
|
163
|
-
rangeSelectToFirst
|
|
164
|
-
});
|
|
165
147
|
return {
|
|
166
148
|
getRootProps: () => ({
|
|
167
149
|
'aria-multiselectable': params.multiSelect
|
|
168
150
|
}),
|
|
151
|
+
instance: {
|
|
152
|
+
isItemSelected,
|
|
153
|
+
selectItem,
|
|
154
|
+
selectRange,
|
|
155
|
+
rangeSelectToLast,
|
|
156
|
+
rangeSelectToFirst
|
|
157
|
+
},
|
|
169
158
|
contextValue: {
|
|
170
159
|
selection: {
|
|
171
160
|
multiSelect: params.multiSelect
|
|
@@ -39,12 +39,12 @@ export const useTreeView = inParams => {
|
|
|
39
39
|
const rootPropsGetters = [];
|
|
40
40
|
const contextValue = {
|
|
41
41
|
publicAPI,
|
|
42
|
-
instance: instance
|
|
42
|
+
instance: instance,
|
|
43
|
+
rootRef: innerRootRef
|
|
43
44
|
};
|
|
44
45
|
const runPlugin = plugin => {
|
|
45
46
|
const pluginResponse = plugin({
|
|
46
47
|
instance,
|
|
47
|
-
publicAPI,
|
|
48
48
|
params,
|
|
49
49
|
slots: params.slots,
|
|
50
50
|
slotProps: params.slotProps,
|
|
@@ -52,10 +52,16 @@ export const useTreeView = inParams => {
|
|
|
52
52
|
setState,
|
|
53
53
|
rootRef: innerRootRef,
|
|
54
54
|
models
|
|
55
|
-
})
|
|
55
|
+
});
|
|
56
56
|
if (pluginResponse.getRootProps) {
|
|
57
57
|
rootPropsGetters.push(pluginResponse.getRootProps);
|
|
58
58
|
}
|
|
59
|
+
if (pluginResponse.publicAPI) {
|
|
60
|
+
Object.assign(publicAPI, pluginResponse.publicAPI);
|
|
61
|
+
}
|
|
62
|
+
if (pluginResponse.instance) {
|
|
63
|
+
Object.assign(instance, pluginResponse.instance);
|
|
64
|
+
}
|
|
59
65
|
if (pluginResponse.contextValue) {
|
|
60
66
|
Object.assign(contextValue, pluginResponse.contextValue);
|
|
61
67
|
}
|
|
@@ -99,6 +105,18 @@ export const useTreeView = inParams => {
|
|
|
99
105
|
});
|
|
100
106
|
return finalChildren;
|
|
101
107
|
};
|
|
108
|
+
const rootWrappers = plugins.map(plugin => plugin.wrapRoot).filter(wrapRoot => !!wrapRoot);
|
|
109
|
+
contextValue.wrapRoot = ({
|
|
110
|
+
children
|
|
111
|
+
}) => {
|
|
112
|
+
let finalChildren = children;
|
|
113
|
+
rootWrappers.forEach(rootWrapper => {
|
|
114
|
+
finalChildren = rootWrapper({
|
|
115
|
+
children: finalChildren
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
return finalChildren;
|
|
119
|
+
};
|
|
102
120
|
const getRootProps = (otherHandlers = {}) => {
|
|
103
121
|
const rootProps = _extends({
|
|
104
122
|
role: 'tree'
|
|
@@ -22,7 +22,7 @@ export const useTreeViewModels = (plugins, props) => {
|
|
|
22
22
|
return initialState;
|
|
23
23
|
});
|
|
24
24
|
const models = Object.fromEntries(Object.entries(modelsRef.current).map(([modelName, model]) => {
|
|
25
|
-
const value =
|
|
25
|
+
const value = props[modelName] ?? modelsState[modelName];
|
|
26
26
|
return [modelName, {
|
|
27
27
|
value,
|
|
28
28
|
setControlledValue: newValue => {
|
|
@@ -53,7 +53,7 @@ export const useTreeViewModels = (plugins, props) => {
|
|
|
53
53
|
if (!model.isControlled && defaultValue !== newDefaultValue) {
|
|
54
54
|
console.error([`MUI X: A component is changing the default ${modelName} state of an uncontrolled TreeView after being initialized. ` + `To suppress this warning opt to use a controlled TreeView.`].join('\n'));
|
|
55
55
|
}
|
|
56
|
-
}, [JSON.stringify(
|
|
56
|
+
}, [JSON.stringify(newDefaultValue)]);
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
/* eslint-enable react-hooks/rules-of-hooks, react-hooks/exhaustive-deps */
|