@mui/x-tree-view 7.1.1 → 7.3.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 +211 -0
- package/RichTreeView/RichTreeView.types.d.ts +3 -3
- package/TreeItem/TreeItem.js +1 -1
- package/TreeItem2/TreeItem2.d.ts +5 -1
- package/TreeItem2/TreeItem2.js +0 -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/models/plugin.d.ts +13 -5
- package/internals/models/treeView.d.ts +1 -2
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +15 -15
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +31 -28
- package/internals/plugins/useTreeViewId/useTreeViewId.js +5 -7
- package/internals/plugins/useTreeViewId/useTreeViewId.types.d.ts +1 -1
- package/internals/plugins/useTreeViewItems/useTreeViewItems.js +60 -50
- package/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +19 -15
- package/internals/plugins/useTreeViewItems/useTreeViewItems.utils.d.ts +4 -0
- package/internals/plugins/useTreeViewItems/useTreeViewItems.utils.js +8 -0
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +73 -48
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.types.d.ts +3 -2
- 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/utils/tree.d.ts +8 -0
- package/internals/utils/tree.js +137 -0
- package/modern/TreeItem/TreeItem.js +1 -1
- package/modern/TreeItem2/TreeItem2.js +0 -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/plugins/useTreeViewExpansion/useTreeViewExpansion.js +15 -15
- package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +31 -28
- package/modern/internals/plugins/useTreeViewId/useTreeViewId.js +5 -7
- package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.js +60 -50
- package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.utils.js +8 -0
- package/modern/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +73 -48
- 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/utils/tree.js +137 -0
- package/modern/useTreeItem2/useTreeItem2.js +1 -1
- package/node/TreeItem/TreeItem.js +1 -1
- package/node/TreeItem2/TreeItem2.js +0 -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/plugins/useTreeViewExpansion/useTreeViewExpansion.js +15 -15
- package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +31 -28
- package/node/internals/plugins/useTreeViewId/useTreeViewId.js +5 -7
- package/node/internals/plugins/useTreeViewItems/useTreeViewItems.js +60 -50
- package/node/internals/plugins/useTreeViewItems/useTreeViewItems.utils.js +15 -0
- package/node/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +73 -48
- 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/utils/tree.js +148 -0
- package/node/useTreeItem2/useTreeItem2.js +1 -1
- package/package.json +1 -1
- 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
|
@@ -2,13 +2,13 @@ import _extends from "@babel/runtime/helpers/esm/extends";
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import useEventCallback from '@mui/utils/useEventCallback';
|
|
4
4
|
import ownerDocument from '@mui/utils/ownerDocument';
|
|
5
|
-
import { populateInstance, populatePublicAPI } from '../../useTreeView/useTreeView.utils';
|
|
6
5
|
import { useInstanceEventHandler } from '../../hooks/useInstanceEventHandler';
|
|
7
6
|
import { getActiveElement } from '../../utils/utils';
|
|
7
|
+
import { getFirstNavigableItem } from '../../utils/tree';
|
|
8
8
|
const useTabbableItemId = (instance, selectedItems) => {
|
|
9
9
|
const isItemVisible = itemId => {
|
|
10
|
-
const
|
|
11
|
-
return
|
|
10
|
+
const itemMeta = instance.getItemMeta(itemId);
|
|
11
|
+
return itemMeta && (itemMeta.parentId == null || instance.isItemExpanded(itemMeta.parentId));
|
|
12
12
|
};
|
|
13
13
|
let tabbableItemId;
|
|
14
14
|
if (Array.isArray(selectedItems)) {
|
|
@@ -17,13 +17,12 @@ const useTabbableItemId = (instance, selectedItems) => {
|
|
|
17
17
|
tabbableItemId = selectedItems;
|
|
18
18
|
}
|
|
19
19
|
if (tabbableItemId == null) {
|
|
20
|
-
tabbableItemId = instance
|
|
20
|
+
tabbableItemId = getFirstNavigableItem(instance);
|
|
21
21
|
}
|
|
22
22
|
return tabbableItemId;
|
|
23
23
|
};
|
|
24
24
|
export const useTreeViewFocus = ({
|
|
25
25
|
instance,
|
|
26
|
-
publicAPI,
|
|
27
26
|
params,
|
|
28
27
|
state,
|
|
29
28
|
setState,
|
|
@@ -42,12 +41,12 @@ export const useTreeViewFocus = ({
|
|
|
42
41
|
const isTreeViewFocused = React.useCallback(() => !!rootRef.current && rootRef.current.contains(getActiveElement(ownerDocument(rootRef.current))), [rootRef]);
|
|
43
42
|
const isItemFocused = React.useCallback(itemId => state.focusedItemId === itemId && isTreeViewFocused(), [state.focusedItemId, isTreeViewFocused]);
|
|
44
43
|
const isItemVisible = itemId => {
|
|
45
|
-
const
|
|
46
|
-
return
|
|
44
|
+
const itemMeta = instance.getItemMeta(itemId);
|
|
45
|
+
return itemMeta && (itemMeta.parentId == null || instance.isItemExpanded(itemMeta.parentId));
|
|
47
46
|
};
|
|
48
47
|
const innerFocusItem = (event, itemId) => {
|
|
49
|
-
const
|
|
50
|
-
const itemElement = document.getElementById(instance.
|
|
48
|
+
const itemMeta = instance.getItemMeta(itemId);
|
|
49
|
+
const itemElement = document.getElementById(instance.getTreeItemIdAttribute(itemId, itemMeta.idAttribute));
|
|
51
50
|
if (itemElement) {
|
|
52
51
|
itemElement.focus();
|
|
53
52
|
}
|
|
@@ -70,7 +69,7 @@ export const useTreeViewFocus = ({
|
|
|
70
69
|
itemToFocusId = models.selectedItems.value;
|
|
71
70
|
}
|
|
72
71
|
if (itemToFocusId == null) {
|
|
73
|
-
itemToFocusId = instance
|
|
72
|
+
itemToFocusId = getFirstNavigableItem(instance);
|
|
74
73
|
}
|
|
75
74
|
innerFocusItem(event, itemToFocusId);
|
|
76
75
|
});
|
|
@@ -78,9 +77,9 @@ export const useTreeViewFocus = ({
|
|
|
78
77
|
if (state.focusedItemId == null) {
|
|
79
78
|
return;
|
|
80
79
|
}
|
|
81
|
-
const
|
|
82
|
-
if (
|
|
83
|
-
const itemElement = document.getElementById(instance.
|
|
80
|
+
const itemMeta = instance.getItemMeta(state.focusedItemId);
|
|
81
|
+
if (itemMeta) {
|
|
82
|
+
const itemElement = document.getElementById(instance.getTreeItemIdAttribute(state.focusedItemId, itemMeta.idAttribute));
|
|
84
83
|
if (itemElement) {
|
|
85
84
|
itemElement.blur();
|
|
86
85
|
}
|
|
@@ -88,16 +87,6 @@ export const useTreeViewFocus = ({
|
|
|
88
87
|
setFocusedItemId(null);
|
|
89
88
|
});
|
|
90
89
|
const canItemBeTabbed = itemId => itemId === tabbableItemId;
|
|
91
|
-
populateInstance(instance, {
|
|
92
|
-
isItemFocused,
|
|
93
|
-
canItemBeTabbed,
|
|
94
|
-
focusItem,
|
|
95
|
-
focusDefaultItem,
|
|
96
|
-
removeFocusedItem
|
|
97
|
-
});
|
|
98
|
-
populatePublicAPI(publicAPI, {
|
|
99
|
-
focusItem
|
|
100
|
-
});
|
|
101
90
|
useInstanceEventHandler(instance, 'removeItem', ({
|
|
102
91
|
id
|
|
103
92
|
}) => {
|
|
@@ -105,20 +94,34 @@ export const useTreeViewFocus = ({
|
|
|
105
94
|
instance.focusDefaultItem(null);
|
|
106
95
|
}
|
|
107
96
|
});
|
|
108
|
-
const
|
|
97
|
+
const createRootHandleFocus = otherHandlers => event => {
|
|
109
98
|
otherHandlers.onFocus?.(event);
|
|
99
|
+
if (event.defaultMuiPrevented) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
110
103
|
// if the event bubbled (which is React specific) we don't want to steal focus
|
|
111
104
|
if (event.target === event.currentTarget) {
|
|
112
105
|
instance.focusDefaultItem(event);
|
|
113
106
|
}
|
|
114
107
|
};
|
|
115
|
-
const focusedItem = instance.
|
|
116
|
-
const activeDescendant = focusedItem ? instance.
|
|
108
|
+
const focusedItem = instance.getItemMeta(state.focusedItemId);
|
|
109
|
+
const activeDescendant = focusedItem ? instance.getTreeItemIdAttribute(focusedItem.id, focusedItem.idAttribute) : null;
|
|
117
110
|
return {
|
|
118
111
|
getRootProps: otherHandlers => ({
|
|
119
|
-
onFocus:
|
|
112
|
+
onFocus: createRootHandleFocus(otherHandlers),
|
|
120
113
|
'aria-activedescendant': activeDescendant ?? undefined
|
|
121
|
-
})
|
|
114
|
+
}),
|
|
115
|
+
publicAPI: {
|
|
116
|
+
focusItem
|
|
117
|
+
},
|
|
118
|
+
instance: {
|
|
119
|
+
isItemFocused,
|
|
120
|
+
canItemBeTabbed,
|
|
121
|
+
focusItem,
|
|
122
|
+
focusDefaultItem,
|
|
123
|
+
removeFocusedItem
|
|
124
|
+
}
|
|
122
125
|
};
|
|
123
126
|
};
|
|
124
127
|
useTreeViewFocus.getInitialState = () => ({
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import useId from '@mui/utils/useId';
|
|
3
|
-
import { populateInstance } from '../../useTreeView/useTreeView.utils';
|
|
4
3
|
export const useTreeViewId = ({
|
|
5
|
-
instance,
|
|
6
4
|
params
|
|
7
5
|
}) => {
|
|
8
6
|
const treeId = useId(params.id);
|
|
9
|
-
const
|
|
10
|
-
populateInstance(instance, {
|
|
11
|
-
getTreeItemId
|
|
12
|
-
});
|
|
7
|
+
const getTreeItemIdAttribute = React.useCallback((itemId, idAttribute) => idAttribute ?? `${treeId}-${itemId}`, [treeId]);
|
|
13
8
|
return {
|
|
14
9
|
getRootProps: () => ({
|
|
15
10
|
id: treeId
|
|
16
|
-
})
|
|
11
|
+
}),
|
|
12
|
+
instance: {
|
|
13
|
+
getTreeItemIdAttribute
|
|
14
|
+
}
|
|
17
15
|
};
|
|
18
16
|
};
|
|
19
17
|
useTreeViewId.params = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TreeViewPluginSignature } from '../../models';
|
|
2
2
|
export interface UseTreeViewIdInstance {
|
|
3
|
-
|
|
3
|
+
getTreeItemIdAttribute: (itemId: string, idAttribute: string | undefined) => string;
|
|
4
4
|
}
|
|
5
5
|
export interface UseTreeViewIdParameters {
|
|
6
6
|
/**
|
|
@@ -1,86 +1,98 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { populateInstance, populatePublicAPI } from '../../useTreeView/useTreeView.utils';
|
|
4
3
|
import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
|
|
4
|
+
import { buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from './useTreeViewItems.utils';
|
|
5
5
|
const updateItemsState = ({
|
|
6
6
|
items,
|
|
7
7
|
isItemDisabled,
|
|
8
8
|
getItemLabel,
|
|
9
9
|
getItemId
|
|
10
10
|
}) => {
|
|
11
|
-
const
|
|
11
|
+
const itemMetaMap = {};
|
|
12
12
|
const itemMap = {};
|
|
13
|
-
const
|
|
13
|
+
const itemOrderedChildrenIds = {
|
|
14
|
+
[TREE_VIEW_ROOT_PARENT_ID]: []
|
|
15
|
+
};
|
|
16
|
+
const processItem = (item, parentId) => {
|
|
14
17
|
const id = getItemId ? getItemId(item) : item.id;
|
|
15
18
|
if (id == null) {
|
|
16
19
|
throw new Error(['MUI X: The Tree View component requires all items to have a unique `id` property.', 'Alternatively, you can use the `getItemId` prop to specify a custom id for each item.', 'An item was provided without id in the `items` prop:', JSON.stringify(item)].join('\n'));
|
|
17
20
|
}
|
|
18
|
-
if (
|
|
21
|
+
if (itemMetaMap[id] != null) {
|
|
19
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
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TreeViewItemMeta, DefaultizedProps, TreeViewPluginSignature } from '../../models';
|
|
2
2
|
import { TreeViewItemId } from '../../../models';
|
|
3
3
|
interface TreeViewItemProps {
|
|
4
4
|
label: string;
|
|
@@ -15,19 +15,20 @@ export interface UseTreeViewItemsPublicAPI<R extends {}> {
|
|
|
15
15
|
getItem: (itemId: string) => R;
|
|
16
16
|
}
|
|
17
17
|
export interface UseTreeViewItemsInstance<R extends {}> extends UseTreeViewItemsPublicAPI<R> {
|
|
18
|
-
|
|
18
|
+
getItemMeta: (itemId: string) => TreeViewItemMeta;
|
|
19
19
|
getItemsToRender: () => TreeViewItemProps[];
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
getItemOrderedChildrenIds: (parentId: string | null) => string[];
|
|
21
|
+
isItemDisabled: (itemId: string) => itemId is string;
|
|
22
|
+
isItemNavigable: (itemId: string) => boolean;
|
|
23
|
+
getItemIndex: (itemId: string) => number;
|
|
23
24
|
/**
|
|
24
25
|
* Freeze any future update to the state based on the `items` prop.
|
|
25
|
-
* This is useful when `
|
|
26
|
+
* This is useful when `useTreeViewJSXItems` is used to avoid having conflicting sources of truth.
|
|
26
27
|
*/
|
|
27
28
|
preventItemUpdates: () => void;
|
|
28
29
|
/**
|
|
29
30
|
* Check if the updates to the state based on the `items` prop are prevented.
|
|
30
|
-
* This is useful when `
|
|
31
|
+
* This is useful when `useTreeViewJSXItems` is used to avoid having conflicting sources of truth.
|
|
31
32
|
* @returns {boolean} `true` if the updates to the state based on the `items` prop are prevented.
|
|
32
33
|
*/
|
|
33
34
|
areItemUpdatesPrevented: () => boolean;
|
|
@@ -73,15 +74,18 @@ interface UseTreeViewItemsEventLookup {
|
|
|
73
74
|
};
|
|
74
75
|
};
|
|
75
76
|
}
|
|
76
|
-
export interface TreeViewItemIdAndChildren {
|
|
77
|
-
id: TreeViewItemId;
|
|
78
|
-
children?: TreeViewItemIdAndChildren[];
|
|
79
|
-
}
|
|
80
77
|
export interface UseTreeViewItemsState<R extends {}> {
|
|
81
78
|
items: {
|
|
82
|
-
|
|
83
|
-
nodeMap: TreeViewNodeMap;
|
|
79
|
+
itemMetaMap: TreeViewItemMetaMap;
|
|
84
80
|
itemMap: TreeViewItemMap<R>;
|
|
81
|
+
itemOrderedChildrenIds: {
|
|
82
|
+
[parentItemId: string]: string[];
|
|
83
|
+
};
|
|
84
|
+
itemChildrenIndexes: {
|
|
85
|
+
[parentItemId: string]: {
|
|
86
|
+
[itemId: string]: number;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
85
89
|
};
|
|
86
90
|
}
|
|
87
91
|
interface UseTreeViewItemsContextValue extends Pick<UseTreeViewItemsDefaultizedParameters<any>, 'disabledItemsFocusable'> {
|
|
@@ -95,8 +99,8 @@ export type UseTreeViewItemsSignature = TreeViewPluginSignature<{
|
|
|
95
99
|
state: UseTreeViewItemsState<any>;
|
|
96
100
|
contextValue: UseTreeViewItemsContextValue;
|
|
97
101
|
}>;
|
|
98
|
-
export type
|
|
99
|
-
[itemId: string]:
|
|
102
|
+
export type TreeViewItemMetaMap = {
|
|
103
|
+
[itemId: string]: TreeViewItemMeta;
|
|
100
104
|
};
|
|
101
105
|
export type TreeViewItemMap<R extends {}> = {
|
|
102
106
|
[itemId: string]: R;
|
|
@@ -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
|
+
if (prevState.items.itemMetaMap[item.id] != null) {
|
|
18
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,29 @@ 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
|
+
};
|
|
88
|
+
};
|
|
89
|
+
const isItemExpandable = reactChildren => {
|
|
90
|
+
if (Array.isArray(reactChildren)) {
|
|
91
|
+
return reactChildren.length > 0 && reactChildren.some(isItemExpandable);
|
|
92
|
+
}
|
|
93
|
+
return Boolean(reactChildren);
|
|
71
94
|
};
|
|
72
95
|
const useTreeViewJSXItemsItemPlugin = ({
|
|
73
96
|
props,
|
|
74
97
|
rootRef,
|
|
75
98
|
contentRef
|
|
76
99
|
}) => {
|
|
100
|
+
const {
|
|
101
|
+
instance
|
|
102
|
+
} = useTreeViewContext();
|
|
77
103
|
const {
|
|
78
104
|
children,
|
|
79
105
|
disabled = false,
|
|
@@ -81,43 +107,37 @@ const useTreeViewJSXItemsItemPlugin = ({
|
|
|
81
107
|
itemId,
|
|
82
108
|
id
|
|
83
109
|
} = props;
|
|
110
|
+
const parentContext = React.useContext(TreeViewChildrenItemContext);
|
|
111
|
+
if (parentContext == null) {
|
|
112
|
+
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'));
|
|
113
|
+
}
|
|
84
114
|
const {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
return Boolean(reactChildren);
|
|
92
|
-
};
|
|
93
|
-
const expandable = isExpandable(children);
|
|
94
|
-
const [treeItemElement, setTreeItemElement] = React.useState(null);
|
|
115
|
+
registerChild,
|
|
116
|
+
unregisterChild,
|
|
117
|
+
parentId
|
|
118
|
+
} = parentContext;
|
|
119
|
+
const expandable = isItemExpandable(children);
|
|
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 = {};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TreeViewItemMeta, TreeViewPluginSignature } from '../../models';
|
|
2
2
|
import { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
3
3
|
import { UseTreeViewKeyboardNavigationSignature } from '../useTreeViewKeyboardNavigation';
|
|
4
4
|
export interface UseTreeViewItemsInstance {
|
|
5
|
-
insertJSXItem: (item:
|
|
5
|
+
insertJSXItem: (item: TreeViewItemMeta) => void;
|
|
6
6
|
removeJSXItem: (itemId: string) => void;
|
|
7
7
|
mapFirstCharFromJSX: (itemId: string, firstChar: string) => () => void;
|
|
8
|
+
setJSXItemsOrderedChildrenIds: (parentId: string | null, orderedChildrenIds: string[]) => void;
|
|
8
9
|
}
|
|
9
10
|
export interface UseTreeViewJSXItemsParameters {
|
|
10
11
|
}
|