@mui/x-tree-view 8.0.0-alpha.0 → 8.0.0-alpha.2

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.
Files changed (159) hide show
  1. package/CHANGELOG.md +651 -6
  2. package/README.md +2 -2
  3. package/RichTreeView/RichTreeView.js +2 -4
  4. package/RichTreeView/RichTreeView.types.d.ts +5 -18
  5. package/SimpleTreeView/SimpleTreeView.types.d.ts +2 -2
  6. package/TreeItem/TreeItem.js +4 -4
  7. package/TreeItem/TreeItem.types.d.ts +4 -2
  8. package/TreeItemDragAndDropOverlay/TreeItemDragAndDropOverlay.js +1 -1
  9. package/TreeItemIcon/TreeItemIcon.types.d.ts +1 -1
  10. package/TreeItemProvider/TreeItemProvider.d.ts +2 -4
  11. package/TreeItemProvider/TreeItemProvider.js +26 -11
  12. package/TreeItemProvider/TreeItemProvider.types.d.ts +1 -0
  13. package/hooks/index.d.ts +1 -0
  14. package/hooks/index.js +2 -1
  15. package/hooks/useTreeItemModel.d.ts +2 -0
  16. package/hooks/useTreeItemModel.js +11 -0
  17. package/hooks/useTreeItemUtils/useTreeItemUtils.d.ts +2 -1
  18. package/hooks/useTreeItemUtils/useTreeItemUtils.js +31 -15
  19. package/hooks/useTreeViewApiRef.d.ts +1 -0
  20. package/index.js +1 -1
  21. package/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.d.ts +3 -1
  22. package/internals/TreeViewProvider/TreeViewChildrenItemProvider.d.ts +2 -1
  23. package/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +6 -22
  24. package/internals/TreeViewProvider/TreeViewProvider.js +1 -2
  25. package/internals/TreeViewProvider/TreeViewProvider.types.d.ts +4 -2
  26. package/internals/components/RichTreeViewItems.d.ts +3 -5
  27. package/internals/components/RichTreeViewItems.js +42 -30
  28. package/internals/corePlugins/useTreeViewId/useTreeViewId.js +10 -11
  29. package/internals/corePlugins/useTreeViewId/useTreeViewId.selectors.d.ts +36 -0
  30. package/internals/corePlugins/useTreeViewId/useTreeViewId.selectors.js +9 -0
  31. package/internals/corePlugins/useTreeViewId/useTreeViewId.types.d.ts +1 -5
  32. package/internals/hooks/useSelector.d.ts +4 -0
  33. package/internals/hooks/useSelector.js +6 -0
  34. package/internals/index.d.ts +6 -1
  35. package/internals/index.js +5 -1
  36. package/internals/models/itemPlugin.d.ts +5 -5
  37. package/internals/models/plugin.d.ts +20 -8
  38. package/internals/models/treeView.d.ts +6 -0
  39. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +36 -24
  40. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.d.ts +124 -0
  41. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.js +17 -0
  42. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +6 -14
  43. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.utils.d.ts +1 -0
  44. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.utils.js +7 -0
  45. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +62 -40
  46. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.d.ts +182 -0
  47. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.js +34 -0
  48. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +4 -16
  49. package/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +15 -13
  50. package/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.d.ts +1 -1
  51. package/internals/plugins/useTreeViewItems/index.d.ts +1 -1
  52. package/internals/plugins/useTreeViewItems/useTreeViewItems.js +58 -98
  53. package/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.d.ts +718 -0
  54. package/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +103 -0
  55. package/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +15 -52
  56. package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +29 -26
  57. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +27 -18
  58. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +13 -5
  59. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +19 -30
  60. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.d.ts +74 -0
  61. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.js +26 -0
  62. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.d.ts +7 -24
  63. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.itemPlugin.js +8 -6
  64. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +45 -34
  65. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.d.ts +32 -0
  66. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.js +9 -0
  67. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +6 -6
  68. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.d.ts +6 -6
  69. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +23 -13
  70. package/internals/useTreeView/useTreeView.js +30 -17
  71. package/internals/useTreeView/useTreeView.types.d.ts +2 -3
  72. package/internals/useTreeView/useTreeViewBuildContext.d.ts +3 -1
  73. package/internals/useTreeView/useTreeViewBuildContext.js +24 -18
  74. package/internals/utils/TreeViewStore.d.ts +12 -0
  75. package/internals/utils/TreeViewStore.js +24 -0
  76. package/internals/utils/selectors.d.ts +9 -0
  77. package/internals/utils/selectors.js +37 -0
  78. package/internals/utils/tree.d.ts +8 -8
  79. package/internals/utils/tree.js +51 -43
  80. package/models/items.d.ts +3 -2
  81. package/modern/RichTreeView/RichTreeView.js +2 -4
  82. package/modern/TreeItem/TreeItem.js +4 -4
  83. package/modern/TreeItemDragAndDropOverlay/TreeItemDragAndDropOverlay.js +1 -1
  84. package/modern/TreeItemProvider/TreeItemProvider.js +26 -11
  85. package/modern/hooks/index.js +2 -1
  86. package/modern/hooks/useTreeItemModel.js +11 -0
  87. package/modern/hooks/useTreeItemUtils/useTreeItemUtils.js +31 -15
  88. package/modern/index.js +1 -1
  89. package/modern/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +6 -22
  90. package/modern/internals/TreeViewProvider/TreeViewProvider.js +1 -2
  91. package/modern/internals/components/RichTreeViewItems.js +42 -30
  92. package/modern/internals/corePlugins/useTreeViewId/useTreeViewId.js +10 -11
  93. package/modern/internals/corePlugins/useTreeViewId/useTreeViewId.selectors.js +9 -0
  94. package/modern/internals/hooks/useSelector.js +6 -0
  95. package/modern/internals/index.js +5 -1
  96. package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +36 -24
  97. package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.js +17 -0
  98. package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.utils.js +7 -0
  99. package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +62 -40
  100. package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.js +34 -0
  101. package/modern/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +15 -13
  102. package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.js +58 -98
  103. package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +103 -0
  104. package/modern/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +29 -26
  105. package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +27 -18
  106. package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +13 -5
  107. package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +19 -30
  108. package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.js +26 -0
  109. package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.itemPlugin.js +8 -6
  110. package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +45 -34
  111. package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.js +9 -0
  112. package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +23 -13
  113. package/modern/internals/useTreeView/useTreeView.js +30 -17
  114. package/modern/internals/useTreeView/useTreeViewBuildContext.js +24 -18
  115. package/modern/internals/utils/TreeViewStore.js +24 -0
  116. package/modern/internals/utils/selectors.js +37 -0
  117. package/modern/internals/utils/tree.js +51 -43
  118. package/modern/useTreeItem/useTreeItem.js +26 -11
  119. package/node/RichTreeView/RichTreeView.js +2 -4
  120. package/node/TreeItem/TreeItem.js +4 -4
  121. package/node/TreeItemDragAndDropOverlay/TreeItemDragAndDropOverlay.js +2 -2
  122. package/node/TreeItemProvider/TreeItemProvider.js +26 -10
  123. package/node/hooks/index.js +8 -1
  124. package/node/hooks/useTreeItemModel.js +17 -0
  125. package/node/hooks/useTreeItemUtils/useTreeItemUtils.js +32 -15
  126. package/node/index.js +1 -1
  127. package/node/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +6 -22
  128. package/node/internals/TreeViewProvider/TreeViewProvider.js +1 -2
  129. package/node/internals/components/RichTreeViewItems.js +42 -30
  130. package/node/internals/corePlugins/useTreeViewId/useTreeViewId.js +12 -13
  131. package/node/internals/corePlugins/useTreeViewId/useTreeViewId.selectors.js +15 -0
  132. package/node/internals/hooks/useSelector.js +13 -0
  133. package/node/internals/index.js +47 -1
  134. package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +36 -24
  135. package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.js +23 -0
  136. package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.utils.js +14 -0
  137. package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +62 -40
  138. package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.js +40 -0
  139. package/node/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +16 -13
  140. package/node/internals/plugins/useTreeViewItems/useTreeViewItems.js +60 -100
  141. package/node/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +109 -0
  142. package/node/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +30 -27
  143. package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +27 -18
  144. package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +13 -5
  145. package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +19 -30
  146. package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.js +32 -0
  147. package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.itemPlugin.js +8 -6
  148. package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +46 -35
  149. package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.js +15 -0
  150. package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +24 -14
  151. package/node/internals/useTreeView/useTreeView.js +30 -17
  152. package/node/internals/useTreeView/useTreeViewBuildContext.js +25 -18
  153. package/node/internals/utils/TreeViewStore.js +31 -0
  154. package/node/internals/utils/selectors.js +44 -0
  155. package/node/internals/utils/tree.js +51 -43
  156. package/node/useTreeItem/useTreeItem.js +26 -11
  157. package/package.json +6 -4
  158. package/useTreeItem/useTreeItem.js +26 -11
  159. package/useTreeItem/useTreeItem.types.d.ts +9 -0
@@ -0,0 +1,74 @@
1
+ import { UseTreeViewLabelSignature } from './useTreeViewLabel.types';
2
+ import { TreeViewRootSelector } from '../../utils/selectors';
3
+ /**
4
+ * Check if an item is editable.
5
+ * @param {TreeViewState<[UseTreeViewItemsSignature]>} state The state of the tree view.
6
+ * @param {object} params The parameters.
7
+ * @param {TreeViewItemId} params.itemId The id of the item to check.
8
+ * @param {((item: any) => boolean) | boolean} params.isItemEditable The function to determine if an item is editable.
9
+ * @returns {boolean} `true` if the item is editable, `false` otherwise.
10
+ */
11
+ export declare const selectorIsItemEditable: ((state: any, args: any) => boolean) & {
12
+ clearCache: () => void;
13
+ resultsCount: () => number;
14
+ resetResultsCount: () => void;
15
+ } & {
16
+ resultFunc: (resultFuncArgs_0: {
17
+ itemId: string;
18
+ isItemEditable: ((item: any) => boolean) | boolean;
19
+ }, resultFuncArgs_1: import("../../..").TreeViewBaseItem<import("../../..").TreeViewDefaultItemModelProperties>) => boolean;
20
+ memoizedResultFunc: ((resultFuncArgs_0: {
21
+ itemId: string;
22
+ isItemEditable: ((item: any) => boolean) | boolean;
23
+ }, resultFuncArgs_1: import("../../..").TreeViewBaseItem<import("../../..").TreeViewDefaultItemModelProperties>) => boolean) & {
24
+ clearCache: () => void;
25
+ resultsCount: () => number;
26
+ resetResultsCount: () => void;
27
+ };
28
+ lastResult: () => boolean;
29
+ dependencies: [(_: any, args: {
30
+ itemId: string;
31
+ isItemEditable: ((item: any) => boolean) | boolean;
32
+ }) => {
33
+ itemId: string;
34
+ isItemEditable: ((item: any) => boolean) | boolean;
35
+ }, (state: any, args: any) => import("../../..").TreeViewBaseItem<import("../../..").TreeViewDefaultItemModelProperties>];
36
+ recomputations: () => number;
37
+ resetRecomputations: () => void;
38
+ dependencyRecomputations: () => number;
39
+ resetDependencyRecomputations: () => void;
40
+ } & {
41
+ argsMemoize: typeof import("reselect").weakMapMemoize;
42
+ memoize: typeof import("reselect").weakMapMemoize;
43
+ };
44
+ /**
45
+ * Check if an item is being edited.
46
+ * @param {TreeViewState<[UseTreeViewLabelSignature]>} state The state of the tree view.
47
+ * @param {TreeViewItemId} itemId The id of the item to check.
48
+ * @returns {boolean} `true` if the item is being edited, `false` otherwise.
49
+ */
50
+ export declare const selectorIsItemBeingEdited: ((state: any, itemId: string) => boolean) & {
51
+ clearCache: () => void;
52
+ resultsCount: () => number;
53
+ resetResultsCount: () => void;
54
+ } & {
55
+ resultFunc: (resultFuncArgs_0: {
56
+ editedItemId: string | null;
57
+ }, resultFuncArgs_1: string) => boolean;
58
+ memoizedResultFunc: ((resultFuncArgs_0: {
59
+ editedItemId: string | null;
60
+ }, resultFuncArgs_1: string) => boolean) & {
61
+ clearCache: () => void;
62
+ resultsCount: () => number;
63
+ resetResultsCount: () => void;
64
+ };
65
+ lastResult: () => boolean;
66
+ dependencies: [TreeViewRootSelector<UseTreeViewLabelSignature>, (_: any, itemId: string) => string];
67
+ recomputations: () => number;
68
+ resetRecomputations: () => void;
69
+ dependencyRecomputations: () => number;
70
+ resetDependencyRecomputations: () => void;
71
+ } & {
72
+ argsMemoize: typeof import("reselect").weakMapMemoize;
73
+ memoize: typeof import("reselect").weakMapMemoize;
74
+ };
@@ -0,0 +1,26 @@
1
+ import { createSelector } from "../../utils/selectors.js";
2
+ import { selectorItemModel } from "../useTreeViewItems/useTreeViewItems.selectors.js";
3
+ const selectorTreeViewLabelState = state => state.label;
4
+
5
+ /**
6
+ * Check if an item is editable.
7
+ * @param {TreeViewState<[UseTreeViewItemsSignature]>} state The state of the tree view.
8
+ * @param {object} params The parameters.
9
+ * @param {TreeViewItemId} params.itemId The id of the item to check.
10
+ * @param {((item: any) => boolean) | boolean} params.isItemEditable The function to determine if an item is editable.
11
+ * @returns {boolean} `true` if the item is editable, `false` otherwise.
12
+ */
13
+ export const selectorIsItemEditable = createSelector([(_, args) => args, (state, args) => selectorItemModel(state, args.itemId)], (args, itemModel) => {
14
+ if (!itemModel || !args.isItemEditable) {
15
+ return false;
16
+ }
17
+ return typeof args.isItemEditable === 'function' ? args.isItemEditable(itemModel) : true;
18
+ });
19
+
20
+ /**
21
+ * Check if an item is being edited.
22
+ * @param {TreeViewState<[UseTreeViewLabelSignature]>} state The state of the tree view.
23
+ * @param {TreeViewItemId} itemId The id of the item to check.
24
+ * @returns {boolean} `true` if the item is being edited, `false` otherwise.
25
+ */
26
+ export const selectorIsItemBeingEdited = createSelector([selectorTreeViewLabelState, (_, itemId) => itemId], (labelState, itemId) => labelState.editedItemId === itemId);
@@ -18,29 +18,6 @@ export interface UseTreeViewLabelInstance extends UseTreeViewLabelPublicAPI {
18
18
  * @returns {void}.
19
19
  */
20
20
  setEditedItemId: (itemId: TreeViewItemId | null) => void;
21
- /**
22
- * Checks if an item is being edited or not.
23
- * @param {TreeViewItemId} itemId The id of the item to check.
24
- * @returns {boolean}.
25
- */
26
- isItemBeingEdited: (itemId: TreeViewItemId) => boolean;
27
- /**
28
- * Checks if an item is being edited or not.
29
- * Purely internal use, used to avoid unnecessarily calling `updateItemLabel` twice.
30
- * @param {TreeViewItemId} itemId The id of the item to check.
31
- * @returns {boolean}.
32
- */
33
- isItemBeingEditedRef: (itemId: TreeViewItemId) => boolean;
34
- /**
35
- * Determines if a given item is editable.
36
- * @param {TreeViewItemId} itemId The id of the item to check.
37
- * @returns {boolean} `true` if the item is editable.
38
- */
39
- isItemEditable: (itemId: TreeViewItemId) => boolean;
40
- /**
41
- * Set to `true` if the Tree View is editable.
42
- */
43
- isTreeViewEditable: boolean;
44
21
  }
45
22
  export interface UseTreeViewLabelParameters<R extends {}> {
46
23
  /**
@@ -62,7 +39,12 @@ export interface UseTreeViewLabelParameters<R extends {}> {
62
39
  }
63
40
  export type UseTreeViewLabelDefaultizedParameters<R extends {}> = DefaultizedProps<UseTreeViewLabelParameters<R>, 'isItemEditable'>;
64
41
  export interface UseTreeViewLabelState {
65
- editedItemId: string | null;
42
+ label: {
43
+ editedItemId: string | null;
44
+ };
45
+ }
46
+ export interface UseTreeViewLabelContextValue {
47
+ label: Pick<UseTreeViewLabelDefaultizedParameters<any>, 'isItemEditable'>;
66
48
  }
67
49
  export type UseTreeViewLabelSignature = TreeViewPluginSignature<{
68
50
  params: UseTreeViewLabelParameters<any>;
@@ -70,6 +52,7 @@ export type UseTreeViewLabelSignature = TreeViewPluginSignature<{
70
52
  publicAPI: UseTreeViewLabelPublicAPI;
71
53
  instance: UseTreeViewLabelInstance;
72
54
  state: UseTreeViewLabelState;
55
+ contextValue: UseTreeViewLabelContextValue;
73
56
  experimentalFeatures: 'labelEditing';
74
57
  dependencies: [UseTreeViewItemsSignature];
75
58
  }>;
@@ -1,8 +1,10 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import { useTreeViewContext } from "../../TreeViewProvider/index.js";
3
+ import { selectorItemOrderedChildrenIds } from "../useTreeViewItems/useTreeViewItems.selectors.js";
4
+ import { selectorIsItemSelected } from "./useTreeViewSelection.selectors.js";
3
5
  function getCheckboxStatus({
4
6
  itemId,
5
- instance,
7
+ store,
6
8
  selectionPropagation,
7
9
  selected
8
10
  }) {
@@ -12,7 +14,7 @@ function getCheckboxStatus({
12
14
  checked: true
13
15
  };
14
16
  }
15
- const children = instance.getItemOrderedChildrenIds(itemId);
17
+ const children = selectorItemOrderedChildrenIds(store.value, itemId);
16
18
  if (children.length === 0) {
17
19
  return {
18
20
  indeterminate: false,
@@ -23,13 +25,13 @@ function getCheckboxStatus({
23
25
  let hasUnSelectedDescendant = false;
24
26
  const traverseDescendants = itemToTraverseId => {
25
27
  if (itemToTraverseId !== itemId) {
26
- if (instance.isItemSelected(itemToTraverseId)) {
28
+ if (selectorIsItemSelected(store.value, itemToTraverseId)) {
27
29
  hasSelectedDescendant = true;
28
30
  } else {
29
31
  hasUnSelectedDescendant = true;
30
32
  }
31
33
  }
32
- instance.getItemOrderedChildrenIds(itemToTraverseId).forEach(traverseDescendants);
34
+ selectorItemOrderedChildrenIds(store.value, itemToTraverseId).forEach(traverseDescendants);
33
35
  };
34
36
  traverseDescendants(itemId);
35
37
  return {
@@ -44,7 +46,7 @@ export const useTreeViewSelectionItemPlugin = ({
44
46
  itemId
45
47
  } = props;
46
48
  const {
47
- instance,
49
+ store,
48
50
  selection: {
49
51
  disableSelection,
50
52
  checkboxSelection,
@@ -69,7 +71,7 @@ export const useTreeViewSelectionItemPlugin = ({
69
71
  interactions.handleCheckboxSelection(event);
70
72
  };
71
73
  const checkboxStatus = getCheckboxStatus({
72
- instance,
74
+ store,
73
75
  itemId,
74
76
  selectionPropagation,
75
77
  selected: status.selected
@@ -1,31 +1,29 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
+ import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
3
4
  import { findOrderInTremauxTree, getAllNavigableItems, getFirstNavigableItem, getLastNavigableItem, getNonDisabledItemsInRange } from "../../utils/tree.js";
4
- import { convertSelectedItemsToArray, propagateSelection, getAddedAndRemovedItems, getLookupFromArray } from "./useTreeViewSelection.utils.js";
5
+ import { convertSelectedItemsToArray, propagateSelection, getAddedAndRemovedItems, getLookupFromArray, createSelectedItemsMap } from "./useTreeViewSelection.utils.js";
6
+ import { selectorIsItemSelected } from "./useTreeViewSelection.selectors.js";
5
7
  import { useTreeViewSelectionItemPlugin } from "./useTreeViewSelection.itemPlugin.js";
6
8
  export const useTreeViewSelection = ({
7
- instance,
9
+ store,
8
10
  params,
9
11
  models
10
12
  }) => {
11
13
  const lastSelectedItem = React.useRef(null);
12
14
  const lastSelectedRange = React.useRef({});
13
- const selectedItemsMap = React.useMemo(() => {
14
- const temp = new Map();
15
- if (Array.isArray(models.selectedItems.value)) {
16
- models.selectedItems.value.forEach(id => {
17
- temp.set(id, true);
18
- });
19
- } else if (models.selectedItems.value != null) {
20
- temp.set(models.selectedItems.value, true);
21
- }
22
- return temp;
23
- }, [models.selectedItems.value]);
15
+ useEnhancedEffect(() => {
16
+ store.update(prevState => _extends({}, prevState, {
17
+ selection: {
18
+ selectedItemsMap: createSelectedItemsMap(models.selectedItems.value)
19
+ }
20
+ }));
21
+ }, [store, models.selectedItems.value]);
24
22
  const setSelectedItems = (event, newModel, additionalItemsToPropagate) => {
25
23
  let cleanModel;
26
24
  if (params.multiSelect && (params.selectionPropagation.descendants || params.selectionPropagation.parents)) {
27
25
  cleanModel = propagateSelection({
28
- instance,
26
+ store,
29
27
  selectionPropagation: params.selectionPropagation,
30
28
  newModel: newModel,
31
29
  oldModel: models.selectedItems.value,
@@ -37,7 +35,7 @@ export const useTreeViewSelection = ({
37
35
  if (params.onItemSelectionToggle) {
38
36
  if (params.multiSelect) {
39
37
  const changes = getAddedAndRemovedItems({
40
- instance,
38
+ store,
41
39
  newModel: cleanModel,
42
40
  oldModel: models.selectedItems.value
43
41
  });
@@ -63,7 +61,6 @@ export const useTreeViewSelection = ({
63
61
  }
64
62
  models.selectedItems.setControlledValue(cleanModel);
65
63
  };
66
- const isItemSelected = itemId => selectedItemsMap.has(itemId);
67
64
  const selectItem = ({
68
65
  event,
69
66
  itemId,
@@ -76,7 +73,7 @@ export const useTreeViewSelection = ({
76
73
  let newSelected;
77
74
  if (keepExistingSelection) {
78
75
  const cleanSelectedItems = convertSelectedItemsToArray(models.selectedItems.value);
79
- const isSelectedBefore = instance.isItemSelected(itemId);
76
+ const isSelectedBefore = selectorIsItemSelected(store.value, itemId);
80
77
  if (isSelectedBefore && (shouldBeSelected === false || shouldBeSelected == null)) {
81
78
  newSelected = cleanSelectedItems.filter(id => id !== itemId);
82
79
  } else if (!isSelectedBefore && (shouldBeSelected === true || shouldBeSelected == null)) {
@@ -86,14 +83,14 @@ export const useTreeViewSelection = ({
86
83
  }
87
84
  } else {
88
85
  // eslint-disable-next-line no-lonely-if
89
- if (shouldBeSelected === false || shouldBeSelected == null && instance.isItemSelected(itemId)) {
86
+ if (shouldBeSelected === false || shouldBeSelected == null && selectorIsItemSelected(store.value, itemId)) {
90
87
  newSelected = params.multiSelect ? [] : null;
91
88
  } else {
92
89
  newSelected = params.multiSelect ? [itemId] : itemId;
93
90
  }
94
91
  }
95
92
  setSelectedItems(event, newSelected,
96
- // If shouldBeSelected === instance.isItemSelect(itemId), we still want to propagate the select.
93
+ // If shouldBeSelected === selectorIsItemSelected(store, itemId), we still want to propagate the select.
97
94
  // This is useful when the element is in an indeterminate state.
98
95
  [itemId]);
99
96
  lastSelectedItem.current = itemId;
@@ -113,7 +110,7 @@ export const useTreeViewSelection = ({
113
110
 
114
111
  // Add to the model the items that are part of the new range and not already part of the model.
115
112
  const selectedItemsLookup = getLookupFromArray(newSelectedItems);
116
- const range = getNonDisabledItemsInRange(instance, start, end);
113
+ const range = getNonDisabledItemsInRange(store.value, start, end);
117
114
  const itemsToAddToModel = range.filter(id => !selectedItemsLookup[id]);
118
115
  newSelectedItems = newSelectedItems.concat(itemsToAddToModel);
119
116
  setSelectedItems(event, newSelectedItems);
@@ -121,21 +118,21 @@ export const useTreeViewSelection = ({
121
118
  };
122
119
  const expandSelectionRange = (event, itemId) => {
123
120
  if (lastSelectedItem.current != null) {
124
- const [start, end] = findOrderInTremauxTree(instance, itemId, lastSelectedItem.current);
121
+ const [start, end] = findOrderInTremauxTree(store.value, itemId, lastSelectedItem.current);
125
122
  selectRange(event, [start, end]);
126
123
  }
127
124
  };
128
125
  const selectRangeFromStartToItem = (event, itemId) => {
129
- selectRange(event, [getFirstNavigableItem(instance), itemId]);
126
+ selectRange(event, [getFirstNavigableItem(store.value), itemId]);
130
127
  };
131
128
  const selectRangeFromItemToEnd = (event, itemId) => {
132
- selectRange(event, [itemId, getLastNavigableItem(instance)]);
129
+ selectRange(event, [itemId, getLastNavigableItem(store.value)]);
133
130
  };
134
131
  const selectAllNavigableItems = event => {
135
132
  if (params.disableSelection || !params.multiSelect) {
136
133
  return;
137
134
  }
138
- const navigableItems = getAllNavigableItems(instance);
135
+ const navigableItems = getAllNavigableItems(store.value);
139
136
  setSelectedItems(event, navigableItems);
140
137
  lastSelectedRange.current = getLookupFromArray(navigableItems);
141
138
  };
@@ -164,6 +161,17 @@ export const useTreeViewSelection = ({
164
161
  }
165
162
  setSelectedItems(event, newSelectedItems);
166
163
  };
164
+ const pluginContextValue = React.useMemo(() => ({
165
+ selection: {
166
+ multiSelect: params.multiSelect,
167
+ checkboxSelection: params.checkboxSelection,
168
+ disableSelection: params.disableSelection,
169
+ selectionPropagation: {
170
+ descendants: params.selectionPropagation.descendants,
171
+ parents: params.selectionPropagation.parents
172
+ }
173
+ }
174
+ }), [params.multiSelect, params.checkboxSelection, params.disableSelection, params.selectionPropagation.descendants, params.selectionPropagation.parents]);
167
175
  return {
168
176
  getRootProps: () => ({
169
177
  'aria-multiselectable': params.multiSelect
@@ -172,7 +180,6 @@ export const useTreeViewSelection = ({
172
180
  selectItem
173
181
  },
174
182
  instance: {
175
- isItemSelected,
176
183
  selectItem,
177
184
  selectAllNavigableItems,
178
185
  expandSelectionRange,
@@ -180,14 +187,7 @@ export const useTreeViewSelection = ({
180
187
  selectRangeFromItemToEnd,
181
188
  selectItemFromArrowNavigation
182
189
  },
183
- contextValue: {
184
- selection: {
185
- multiSelect: params.multiSelect,
186
- checkboxSelection: params.checkboxSelection,
187
- disableSelection: params.disableSelection,
188
- selectionPropagation: params.selectionPropagation
189
- }
190
- }
190
+ contextValue: pluginContextValue
191
191
  };
192
192
  };
193
193
  useTreeViewSelection.itemPlugin = useTreeViewSelectionItemPlugin;
@@ -197,6 +197,7 @@ useTreeViewSelection.models = {
197
197
  }
198
198
  };
199
199
  const DEFAULT_SELECTED_ITEMS = [];
200
+ const EMPTY_SELECTION_PROPAGATION = {};
200
201
  useTreeViewSelection.getDefaultizedParams = ({
201
202
  params
202
203
  }) => _extends({}, params, {
@@ -204,7 +205,17 @@ useTreeViewSelection.getDefaultizedParams = ({
204
205
  multiSelect: params.multiSelect ?? false,
205
206
  checkboxSelection: params.checkboxSelection ?? false,
206
207
  defaultSelectedItems: params.defaultSelectedItems ?? (params.multiSelect ? DEFAULT_SELECTED_ITEMS : null),
207
- selectionPropagation: params.selectionPropagation ?? {}
208
+ selectionPropagation: params.selectionPropagation ?? EMPTY_SELECTION_PROPAGATION
209
+ });
210
+ useTreeViewSelection.getInitialState = params => ({
211
+ selection: {
212
+ selectedItemsMap: createSelectedItemsMap(params.selectedItems === undefined ? params.defaultSelectedItems : params.selectedItems)
213
+ }
214
+ });
215
+ useTreeViewSelection.getInitialState = params => ({
216
+ selection: {
217
+ selectedItemsMap: createSelectedItemsMap(params.selectedItems === undefined ? params.defaultSelectedItems : params.selectedItems)
218
+ }
208
219
  });
209
220
  useTreeViewSelection.params = {
210
221
  disableSelection: true,
@@ -0,0 +1,32 @@
1
+ import { TreeViewRootSelector } from '../../utils/selectors';
2
+ import { UseTreeViewSelectionSignature } from './useTreeViewSelection.types';
3
+ /**
4
+ * Check if an item is selected.
5
+ * @param {TreeViewState<[UseTreeViewSelectionSignature]>} state The state of the tree view.
6
+ * @returns {boolean} `true` if the item is selected, `false` otherwise.
7
+ */
8
+ export declare const selectorIsItemSelected: ((state: any, itemId: string) => boolean) & {
9
+ clearCache: () => void;
10
+ resultsCount: () => number;
11
+ resetResultsCount: () => void;
12
+ } & {
13
+ resultFunc: (resultFuncArgs_0: {
14
+ selectedItemsMap: Map<string, true>;
15
+ }, resultFuncArgs_1: string) => boolean;
16
+ memoizedResultFunc: ((resultFuncArgs_0: {
17
+ selectedItemsMap: Map<string, true>;
18
+ }, resultFuncArgs_1: string) => boolean) & {
19
+ clearCache: () => void;
20
+ resultsCount: () => number;
21
+ resetResultsCount: () => void;
22
+ };
23
+ lastResult: () => boolean;
24
+ dependencies: [TreeViewRootSelector<UseTreeViewSelectionSignature>, (_: any, itemId: string) => string];
25
+ recomputations: () => number;
26
+ resetRecomputations: () => void;
27
+ dependencyRecomputations: () => number;
28
+ resetDependencyRecomputations: () => void;
29
+ } & {
30
+ argsMemoize: typeof import("reselect").weakMapMemoize;
31
+ memoize: typeof import("reselect").weakMapMemoize;
32
+ };
@@ -0,0 +1,9 @@
1
+ import { createSelector } from "../../utils/selectors.js";
2
+ const selectorTreeViewSelectionState = state => state.selection;
3
+
4
+ /**
5
+ * Check if an item is selected.
6
+ * @param {TreeViewState<[UseTreeViewSelectionSignature]>} state The state of the tree view.
7
+ * @returns {boolean} `true` if the item is selected, `false` otherwise.
8
+ */
9
+ export const selectorIsItemSelected = createSelector([selectorTreeViewSelectionState, (_, itemId) => itemId], (selectionState, itemId) => selectionState.selectedItemsMap.has(itemId));
@@ -21,12 +21,6 @@ export interface UseTreeViewSelectionPublicAPI {
21
21
  }) => void;
22
22
  }
23
23
  export interface UseTreeViewSelectionInstance extends UseTreeViewSelectionPublicAPI {
24
- /**
25
- * Check if an item is selected.
26
- * @param {TreeViewItemId} itemId The id of the item to check.
27
- * @returns {boolean} `true` if the item is selected, `false` otherwise.
28
- */
29
- isItemSelected: (itemId: string) => boolean;
30
24
  /**
31
25
  * Select all the navigable items in the tree.
32
26
  * @param {React.SyntheticEvent} event The DOM event that triggered the change.
@@ -119,6 +113,11 @@ export interface UseTreeViewSelectionParameters<Multiple extends boolean | undef
119
113
  onItemSelectionToggle?: (event: React.SyntheticEvent, itemId: string, isSelected: boolean) => void;
120
114
  }
121
115
  export type UseTreeViewSelectionDefaultizedParameters<Multiple extends boolean> = DefaultizedProps<UseTreeViewSelectionParameters<Multiple>, 'disableSelection' | 'defaultSelectedItems' | 'multiSelect' | 'checkboxSelection' | 'selectionPropagation'>;
116
+ export interface UseTreeViewSelectionState {
117
+ selection: {
118
+ selectedItemsMap: Map<string, true>;
119
+ };
120
+ }
122
121
  interface UseTreeViewSelectionContextValue {
123
122
  selection: Pick<UseTreeViewSelectionDefaultizedParameters<boolean>, 'multiSelect' | 'checkboxSelection' | 'disableSelection' | 'selectionPropagation'>;
124
123
  }
@@ -129,6 +128,7 @@ export type UseTreeViewSelectionSignature = TreeViewPluginSignature<{
129
128
  publicAPI: UseTreeViewSelectionPublicAPI;
130
129
  contextValue: UseTreeViewSelectionContextValue;
131
130
  modelNames: 'selectedItems';
131
+ state: UseTreeViewSelectionState;
132
132
  dependencies: [
133
133
  UseTreeViewItemsSignature,
134
134
  UseTreeViewExpansionSignature,
@@ -1,6 +1,5 @@
1
1
  import { TreeViewItemId, TreeViewSelectionPropagation } from '../../../models';
2
- import { TreeViewInstance } from '../../models';
3
- import { UseTreeViewItemsSignature } from '../useTreeViewItems';
2
+ import { TreeViewUsedStore } from '../../models';
4
3
  import { UseTreeViewSelectionSignature } from './useTreeViewSelection.types';
5
4
  /**
6
5
  * Transform the `selectedItems` model to be an array if it was a string or null.
@@ -8,19 +7,20 @@ import { UseTreeViewSelectionSignature } from './useTreeViewSelection.types';
8
7
  * @returns {string[]} The converted model.
9
8
  */
10
9
  export declare const convertSelectedItemsToArray: (model: string[] | string | null) => string[];
10
+ export declare const createSelectedItemsMap: (selectedItems: string | string[] | null) => Map<string, true>;
11
11
  export declare const getLookupFromArray: (array: string[]) => {
12
12
  [itemId: string]: true;
13
13
  };
14
- export declare const getAddedAndRemovedItems: ({ instance, oldModel, newModel, }: {
15
- instance: TreeViewInstance<[UseTreeViewSelectionSignature]>;
14
+ export declare const getAddedAndRemovedItems: ({ store, oldModel, newModel, }: {
15
+ store: TreeViewUsedStore<UseTreeViewSelectionSignature>;
16
16
  oldModel: TreeViewItemId[];
17
17
  newModel: TreeViewItemId[];
18
18
  }) => {
19
19
  added: string[];
20
20
  removed: string[];
21
21
  };
22
- export declare const propagateSelection: ({ instance, selectionPropagation, newModel, oldModel, additionalItemsToPropagate, }: {
23
- instance: TreeViewInstance<[UseTreeViewItemsSignature, UseTreeViewSelectionSignature]>;
22
+ export declare const propagateSelection: ({ store, selectionPropagation, newModel, oldModel, additionalItemsToPropagate, }: {
23
+ store: TreeViewUsedStore<UseTreeViewSelectionSignature>;
24
24
  selectionPropagation: TreeViewSelectionPropagation;
25
25
  newModel: TreeViewItemId[];
26
26
  oldModel: TreeViewItemId[];
@@ -1,3 +1,6 @@
1
+ import { selectorIsItemSelected } from "./useTreeViewSelection.selectors.js";
2
+ import { selectorItemOrderedChildrenIds, selectorItemParentId } from "../useTreeViewItems/useTreeViewItems.selectors.js";
3
+
1
4
  /**
2
5
  * Transform the `selectedItems` model to be an array if it was a string or null.
3
6
  * @param {string[] | string | null} model The raw model.
@@ -12,6 +15,13 @@ export const convertSelectedItemsToArray = model => {
12
15
  }
13
16
  return [];
14
17
  };
18
+ export const createSelectedItemsMap = selectedItems => {
19
+ const selectedItemsMap = new Map();
20
+ convertSelectedItemsToArray(selectedItems).forEach(id => {
21
+ selectedItemsMap.set(id, true);
22
+ });
23
+ return selectedItemsMap;
24
+ };
15
25
  export const getLookupFromArray = array => {
16
26
  const lookup = {};
17
27
  array.forEach(itemId => {
@@ -20,18 +30,18 @@ export const getLookupFromArray = array => {
20
30
  return lookup;
21
31
  };
22
32
  export const getAddedAndRemovedItems = ({
23
- instance,
33
+ store,
24
34
  oldModel,
25
35
  newModel
26
36
  }) => {
27
- const newModelLookup = getLookupFromArray(newModel);
37
+ const newModelLookup = createSelectedItemsMap(newModel);
28
38
  return {
29
- added: newModel.filter(itemId => !instance.isItemSelected(itemId)),
30
- removed: oldModel.filter(itemId => !newModelLookup[itemId])
39
+ added: newModel.filter(itemId => !selectorIsItemSelected(store.value, itemId)),
40
+ removed: oldModel.filter(itemId => !newModelLookup.has(itemId))
31
41
  };
32
42
  };
33
43
  export const propagateSelection = ({
34
- instance,
44
+ store,
35
45
  selectionPropagation,
36
46
  newModel,
37
47
  oldModel,
@@ -43,7 +53,7 @@ export const propagateSelection = ({
43
53
  let shouldRegenerateModel = false;
44
54
  const newModelLookup = getLookupFromArray(newModel);
45
55
  const changes = getAddedAndRemovedItems({
46
- instance,
56
+ store,
47
57
  newModel,
48
58
  oldModel
49
59
  });
@@ -63,7 +73,7 @@ export const propagateSelection = ({
63
73
  shouldRegenerateModel = true;
64
74
  newModelLookup[itemId] = true;
65
75
  }
66
- instance.getItemOrderedChildrenIds(itemId).forEach(selectDescendants);
76
+ selectorItemOrderedChildrenIds(store.value, itemId).forEach(selectDescendants);
67
77
  };
68
78
  selectDescendants(addedItemId);
69
79
  }
@@ -72,15 +82,15 @@ export const propagateSelection = ({
72
82
  if (!newModelLookup[itemId]) {
73
83
  return false;
74
84
  }
75
- const children = instance.getItemOrderedChildrenIds(itemId);
85
+ const children = selectorItemOrderedChildrenIds(store.value, itemId);
76
86
  return children.every(checkAllDescendantsSelected);
77
87
  };
78
88
  const selectParents = itemId => {
79
- const parentId = instance.getItemMeta(itemId).parentId;
89
+ const parentId = selectorItemParentId(store.value, itemId);
80
90
  if (parentId == null) {
81
91
  return;
82
92
  }
83
- const siblings = instance.getItemOrderedChildrenIds(parentId);
93
+ const siblings = selectorItemOrderedChildrenIds(store.value, parentId);
84
94
  if (siblings.every(checkAllDescendantsSelected)) {
85
95
  shouldRegenerateModel = true;
86
96
  newModelLookup[parentId] = true;
@@ -92,13 +102,13 @@ export const propagateSelection = ({
92
102
  });
93
103
  changes.removed.forEach(removedItemId => {
94
104
  if (selectionPropagation.parents) {
95
- let parentId = instance.getItemMeta(removedItemId).parentId;
105
+ let parentId = selectorItemParentId(store.value, removedItemId);
96
106
  while (parentId != null) {
97
107
  if (newModelLookup[parentId]) {
98
108
  shouldRegenerateModel = true;
99
109
  delete newModelLookup[parentId];
100
110
  }
101
- parentId = instance.getItemMeta(parentId).parentId;
111
+ parentId = selectorItemParentId(store.value, parentId);
102
112
  }
103
113
  }
104
114
  if (selectionPropagation.descendants) {
@@ -107,7 +117,7 @@ export const propagateSelection = ({
107
117
  shouldRegenerateModel = true;
108
118
  delete newModelLookup[itemId];
109
119
  }
110
- instance.getItemOrderedChildrenIds(itemId).forEach(deSelectDescendants);
120
+ selectorItemOrderedChildrenIds(store.value, itemId).forEach(deSelectDescendants);
111
121
  };
112
122
  deSelectDescendants(removedItemId);
113
123
  }