@elliemae/ds-treeview 2.0.2 → 2.1.0-rc.4

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 (165) hide show
  1. package/cjs/TreeView.js +5 -103
  2. package/cjs/TreeViewContext.js +44 -47
  3. package/cjs/config/useTreeview.js +137 -0
  4. package/cjs/index.js +1 -7
  5. package/cjs/parts/CheckboxSelectable.js +5 -7
  6. package/cjs/parts/ExpandCaret.js +17 -11
  7. package/cjs/parts/Icon.js +4 -0
  8. package/cjs/parts/NestingSpace.js +15 -22
  9. package/cjs/parts/RadioSelectable.js +7 -9
  10. package/cjs/parts/TreeItem.js +14 -11
  11. package/cjs/parts/TreeList.js +4 -2
  12. package/cjs/plugins/dnd/TreeDndPlugin.js +1 -1
  13. package/cjs/react-desc-prop-types.js +28 -27
  14. package/cjs/related-components/TreeViewSearchBar.js +9 -1
  15. package/cjs/{deprecated/plugins/selectable/helper.js → sharedTypes.js} +0 -0
  16. package/cjs/utils/dnd-helpers.js +2 -4
  17. package/cjs/utils/group-expands-helpers.js +34 -8
  18. package/cjs/utils/keyboard-helpers.js +64 -60
  19. package/cjs/utils/object-helpers.js +25 -7
  20. package/cjs/utils/refs-helpers.js +2 -2
  21. package/cjs/utils/tree-helpers.js +42 -27
  22. package/cjs/utils/useDnDHelpers.js +107 -100
  23. package/cjs/utils/useInstanceRefActions.js +3 -2
  24. package/cjs/utils/useTree.js +25 -6
  25. package/esm/TreeView.js +6 -103
  26. package/esm/TreeViewContext.js +44 -47
  27. package/esm/config/useTreeview.js +129 -0
  28. package/esm/index.js +0 -8
  29. package/esm/parts/CheckboxSelectable.js +5 -7
  30. package/esm/parts/ExpandCaret.js +17 -11
  31. package/esm/parts/Icon.js +4 -0
  32. package/esm/parts/NestingSpace.js +15 -21
  33. package/esm/parts/RadioSelectable.js +7 -9
  34. package/esm/parts/TreeItem.js +14 -11
  35. package/esm/parts/TreeList.js +4 -2
  36. package/esm/plugins/dnd/TreeDndPlugin.js +1 -1
  37. package/esm/react-desc-prop-types.js +28 -27
  38. package/esm/related-components/TreeViewSearchBar.js +9 -1
  39. package/esm/{deprecated/plugins/selectable/helper.js → sharedTypes.js} +0 -0
  40. package/esm/utils/dnd-helpers.js +2 -4
  41. package/esm/utils/group-expands-helpers.js +34 -9
  42. package/esm/utils/keyboard-helpers.js +62 -58
  43. package/esm/utils/object-helpers.js +25 -7
  44. package/esm/utils/refs-helpers.js +2 -2
  45. package/esm/utils/tree-helpers.js +42 -27
  46. package/esm/utils/useDnDHelpers.js +107 -100
  47. package/esm/utils/useInstanceRefActions.js +3 -2
  48. package/esm/utils/useTree.js +25 -6
  49. package/package.json +17 -128
  50. package/types/TreeView.d.ts +4 -148
  51. package/types/TreeViewContext.d.ts +4 -106
  52. package/types/config/useTreeview.d.ts +7 -0
  53. package/types/index.d.ts +0 -1
  54. package/types/parts/CheckboxSelectable.d.ts +5 -6
  55. package/types/parts/ChildrenCountDisplayer.d.ts +6 -6
  56. package/types/parts/ExpandCaret.d.ts +6 -6
  57. package/types/parts/Icon.d.ts +7 -5
  58. package/types/parts/NestingSpace.d.ts +3 -8
  59. package/types/parts/RadioSelectable.d.ts +5 -6
  60. package/types/parts/TreeItem.d.ts +22 -14
  61. package/types/parts/TreeItemText.d.ts +5 -5
  62. package/types/parts/TreeList.d.ts +22 -9
  63. package/types/react-desc-prop-types.d.ts +124 -43
  64. package/types/related-components/TreeViewSearchBar.d.ts +14 -11
  65. package/types/sharedTypes.d.ts +54 -0
  66. package/types/utils/array-helpers.d.ts +2 -2
  67. package/types/utils/dnd-helpers.d.ts +26 -25
  68. package/types/utils/group-expands-helpers.d.ts +13 -10
  69. package/types/utils/keyboard-helpers.d.ts +5 -3
  70. package/types/utils/object-helpers.d.ts +6 -2
  71. package/types/utils/refs-helpers.d.ts +4 -1
  72. package/types/utils/selectable-helper.d.ts +7 -3
  73. package/types/utils/string-helpers.d.ts +1 -1
  74. package/types/utils/tree-helpers.d.ts +25 -11
  75. package/types/utils/useDnDHelpers.d.ts +23 -3
  76. package/types/utils/useInstanceRefActions.d.ts +3 -3
  77. package/types/utils/useTree.d.ts +8 -7
  78. package/cjs/deprecated/TreeView.js +0 -72
  79. package/cjs/deprecated/__testUtils__/utils.js +0 -89
  80. package/cjs/deprecated/index.js +0 -27
  81. package/cjs/deprecated/plugins/dnd/TreeDndPlugin.js +0 -119
  82. package/cjs/deprecated/plugins/dnd/index.js +0 -9
  83. package/cjs/deprecated/plugins/dnd/renderers/DraggableItem.js +0 -67
  84. package/cjs/deprecated/plugins/dnd/renderers/DroppableList.js +0 -181
  85. package/cjs/deprecated/plugins/dnd/utils.js +0 -157
  86. package/cjs/deprecated/plugins/index.js +0 -21
  87. package/cjs/deprecated/plugins/roving/TreeRovingPlugin.js +0 -109
  88. package/cjs/deprecated/plugins/roving/index.js +0 -9
  89. package/cjs/deprecated/plugins/searchable/SearchableTreePlugin.js +0 -109
  90. package/cjs/deprecated/plugins/searchable/index.js +0 -9
  91. package/cjs/deprecated/plugins/searchable/utils.js +0 -7
  92. package/cjs/deprecated/plugins/selectable/SelectablePluginTree.js +0 -290
  93. package/cjs/deprecated/plugins/selectable/index.js +0 -9
  94. package/cjs/deprecated/plugins/selectable/utils.js +0 -19
  95. package/cjs/deprecated/plugins/toolbar/TreeToolbarPlugin.js +0 -128
  96. package/cjs/deprecated/plugins/toolbar/index.js +0 -9
  97. package/cjs/deprecated/plugins/tree/TreeDataPlugin.js +0 -281
  98. package/cjs/deprecated/plugins/tree/index.js +0 -9
  99. package/cjs/deprecated/plugins/tree/useExpandTreeState.js +0 -143
  100. package/cjs/deprecated/plugins/tree/utils.js +0 -43
  101. package/cjs/deprecated/plugins/tree/walkTreeStrategy.js +0 -60
  102. package/cjs/deprecated/plugins/virtualization/TreeVirtualizationPlugin.js +0 -209
  103. package/cjs/deprecated/plugins/virtualization/index.js +0 -9
  104. package/cjs/deprecated/renderers.js +0 -76
  105. package/cjs/prop-types.js +0 -61
  106. package/esm/deprecated/TreeView.js +0 -61
  107. package/esm/deprecated/__testUtils__/utils.js +0 -79
  108. package/esm/deprecated/index.js +0 -14
  109. package/esm/deprecated/plugins/dnd/TreeDndPlugin.js +0 -110
  110. package/esm/deprecated/plugins/dnd/index.js +0 -1
  111. package/esm/deprecated/plugins/dnd/renderers/DraggableItem.js +0 -59
  112. package/esm/deprecated/plugins/dnd/renderers/DroppableList.js +0 -172
  113. package/esm/deprecated/plugins/dnd/utils.js +0 -139
  114. package/esm/deprecated/plugins/index.js +0 -7
  115. package/esm/deprecated/plugins/roving/TreeRovingPlugin.js +0 -96
  116. package/esm/deprecated/plugins/roving/index.js +0 -1
  117. package/esm/deprecated/plugins/searchable/SearchableTreePlugin.js +0 -99
  118. package/esm/deprecated/plugins/searchable/index.js +0 -1
  119. package/esm/deprecated/plugins/searchable/utils.js +0 -3
  120. package/esm/deprecated/plugins/selectable/SelectablePluginTree.js +0 -280
  121. package/esm/deprecated/plugins/selectable/index.js +0 -1
  122. package/esm/deprecated/plugins/selectable/utils.js +0 -15
  123. package/esm/deprecated/plugins/toolbar/TreeToolbarPlugin.js +0 -114
  124. package/esm/deprecated/plugins/toolbar/index.js +0 -1
  125. package/esm/deprecated/plugins/tree/TreeDataPlugin.js +0 -269
  126. package/esm/deprecated/plugins/tree/index.js +0 -1
  127. package/esm/deprecated/plugins/tree/useExpandTreeState.js +0 -135
  128. package/esm/deprecated/plugins/tree/utils.js +0 -28
  129. package/esm/deprecated/plugins/tree/walkTreeStrategy.js +0 -56
  130. package/esm/deprecated/plugins/virtualization/TreeVirtualizationPlugin.js +0 -197
  131. package/esm/deprecated/plugins/virtualization/index.js +0 -1
  132. package/esm/deprecated/renderers.js +0 -59
  133. package/esm/prop-types.js +0 -46
  134. package/types/deprecated/TreeView.d.ts +0 -8
  135. package/types/deprecated/__testUtils__/utils.d.ts +0 -72
  136. package/types/deprecated/index.d.ts +0 -2
  137. package/types/deprecated/plugins/dnd/TreeDndPlugin.d.ts +0 -1
  138. package/types/deprecated/plugins/dnd/index.d.ts +0 -1
  139. package/types/deprecated/plugins/dnd/renderers/DraggableItem.d.ts +0 -3
  140. package/types/deprecated/plugins/dnd/renderers/DroppableList.d.ts +0 -6
  141. package/types/deprecated/plugins/dnd/tests/TreeDndPlugin.test.d.ts +0 -0
  142. package/types/deprecated/plugins/dnd/utils.d.ts +0 -28
  143. package/types/deprecated/plugins/index.d.ts +0 -7
  144. package/types/deprecated/plugins/roving/TreeRovingPlugin.d.ts +0 -1
  145. package/types/deprecated/plugins/roving/index.d.ts +0 -1
  146. package/types/deprecated/plugins/searchable/SearchableTreePlugin.d.ts +0 -1
  147. package/types/deprecated/plugins/searchable/index.d.ts +0 -1
  148. package/types/deprecated/plugins/searchable/utils.d.ts +0 -1
  149. package/types/deprecated/plugins/selectable/SelectablePluginTree.d.ts +0 -1
  150. package/types/deprecated/plugins/selectable/helper.d.ts +0 -0
  151. package/types/deprecated/plugins/selectable/index.d.ts +0 -1
  152. package/types/deprecated/plugins/selectable/tests/SelectablePluginTree.test.d.ts +0 -1
  153. package/types/deprecated/plugins/selectable/utils.d.ts +0 -1
  154. package/types/deprecated/plugins/toolbar/TreeToolbarPlugin.d.ts +0 -1
  155. package/types/deprecated/plugins/toolbar/index.d.ts +0 -1
  156. package/types/deprecated/plugins/tree/TreeDataPlugin.d.ts +0 -1
  157. package/types/deprecated/plugins/tree/index.d.ts +0 -1
  158. package/types/deprecated/plugins/tree/tests/TreeDataPlugin.test.d.ts +0 -1
  159. package/types/deprecated/plugins/tree/useExpandTreeState.d.ts +0 -14
  160. package/types/deprecated/plugins/tree/utils.d.ts +0 -8
  161. package/types/deprecated/plugins/tree/walkTreeStrategy.d.ts +0 -9
  162. package/types/deprecated/plugins/virtualization/TreeVirtualizationPlugin.d.ts +0 -1
  163. package/types/deprecated/plugins/virtualization/index.d.ts +0 -1
  164. package/types/deprecated/renderers.d.ts +0 -19
  165. package/types/prop-types.d.ts +0 -26
@@ -27,6 +27,14 @@ function TreeViewSearchBar(props) {
27
27
  totalResults: totalResults,
28
28
  value: value
29
29
  });
30
- }
30
+ } // TreeViewSearchBar.propTypes = {
31
+ // value: PropTypes.string.isRequired,
32
+ // handleChange: PropTypes.func.isRequired,
33
+ // onNext: PropTypes.func.isRequired,
34
+ // onPrevious: PropTypes.func.isRequired,
35
+ // onClear: PropTypes.func.isRequired,
36
+ // currentResultIndex: PropTypes.number.isRequired,
37
+ // totalResults: PropTypes.number.isRequired,
38
+ // };
31
39
 
32
40
  export { TreeViewSearchBar };
@@ -111,11 +111,9 @@ const isDropValid = _ref3 => {
111
111
  const inferKeyboardKinshipDrop = _ref4 => {
112
112
  let {
113
113
  items,
114
- newIndex // movementDirection,
115
-
114
+ newIndex
116
115
  } = _ref4;
117
- const kinship = {}; // const directionOffset = movementDirection === 'topToBottom' ? 0 : 0;
118
-
116
+ const kinship = {};
119
117
  const itemBefore = items[newIndex];
120
118
  const itemBeforeModel = itemBefore.node.model;
121
119
  const isGroupAndIsExpanded = itemBeforeModel.isExpanded && itemBeforeModel.children.length > 0;
@@ -1,8 +1,22 @@
1
1
  import { useCallback, useEffect } from 'react';
2
2
 
3
3
  /* eslint-disable max-params */
4
- const toggleItemExpand = function (itemToToggle, triggerTreeRerender) {
5
- let scrollTo = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : () => {};
4
+ const updateExpandedState = (treeRoot, onExpandChange) => {
5
+ const shouldContinueWalking = true;
6
+ const newExpandedHashMap = {
7
+ __ds_tree_root: true
8
+ };
9
+ treeRoot.walk(node => {
10
+ if (!node.isRoot() && node.model.isGroup && node.model.isExpanded) {
11
+ newExpandedHashMap[node.model.id] = true;
12
+ }
13
+
14
+ return shouldContinueWalking;
15
+ });
16
+ if (onExpandChange) onExpandChange(newExpandedHashMap, null);
17
+ };
18
+ const toggleItemExpand = function (itemToToggle, triggerTreeRerender, updateUserExpandedState) {
19
+ let scrollTo = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : () => {};
6
20
  const {
7
21
  node: {
8
22
  model: {
@@ -15,8 +29,9 @@ const toggleItemExpand = function (itemToToggle, triggerTreeRerender) {
15
29
  if (itemToToggle.node.model.isGroup) {
16
30
  itemToToggle.node.model.isExpanded = !isExpanded;
17
31
  triggerTreeRerender();
32
+ updateUserExpandedState();
18
33
  setTimeout(() => {
19
- scrollTo(virtualIndex, {
34
+ if (typeof virtualIndex === 'number') scrollTo(virtualIndex, {
20
35
  align: 'start'
21
36
  });
22
37
  });
@@ -29,46 +44,56 @@ const collapseItemModel = modelToCollapse => {
29
44
  if (modelToCollapse.isGroup) modelToCollapse.isExpanded = false;
30
45
  };
31
46
  const expandAll = (treeRoot, triggerTreeRerender) => {
47
+ const shouldContinueWalking = true;
32
48
  treeRoot.walk(node => {
33
49
  if (!node.isRoot()) expandItemModel(node.model);
50
+ return shouldContinueWalking;
34
51
  });
35
52
  triggerTreeRerender();
36
53
  };
37
54
  const collapseAll = (treeRoot, triggerTreeRerender) => {
55
+ const shouldContinueWalking = true; // tree-model ask we return true if we want to continue walking
56
+
38
57
  treeRoot.walk(node => {
39
58
  if (!node.isRoot()) collapseItemModel(node.model);
59
+ return shouldContinueWalking;
40
60
  });
41
61
  triggerTreeRerender();
42
62
  };
43
63
  const getToggleExpandShouldExpand = treeRoot => {
44
64
  let shouldExpand = false;
65
+ const shouldContinueWalking = true; // tree-model ask we return true if we want to continue walking
66
+
45
67
  treeRoot.walk(node => {
46
68
  if (!node.isRoot()) {
47
69
  if (!node.model.isExpanded && node.model.isGroup) {
48
70
  shouldExpand = true; // eslint-disable-next-line no-useless-return
49
71
 
50
- return;
72
+ return shouldContinueWalking;
51
73
  }
52
74
  }
75
+
76
+ return shouldContinueWalking;
53
77
  });
54
78
  return shouldExpand;
55
79
  };
56
- const toggleExpandAllHelper = (isExpand, treeRoot, triggerTreeRerender, setLatestToggledItem) => {
80
+ const toggleExpandAllHelper = (isExpand, treeRoot, triggerTreeRerender, setLatestToggledItem, updateUserExpandedState) => {
57
81
  if (isExpand === true) expandAll(treeRoot, triggerTreeRerender);else if (isExpand === false) collapseAll(treeRoot, triggerTreeRerender);else {
58
82
  const shouldExpand = getToggleExpandShouldExpand(treeRoot);
59
83
  if (shouldExpand === true) expandAll(treeRoot, triggerTreeRerender);else collapseAll(treeRoot, triggerTreeRerender);
60
84
  }
61
85
  setLatestToggledItem(null);
86
+ updateUserExpandedState();
62
87
  };
63
88
  const useNotifyExpandedChange = (propsWithDefaults, _ref) => {
64
89
  let {
65
90
  latestToggledItem,
66
91
  expandedGroups
67
92
  } = _ref;
68
- const notifyExpandedChange = useCallback(expandedGroupHashmap => propsWithDefaults.onExpandChange(expandedGroupHashmap, latestToggledItem), [propsWithDefaults, latestToggledItem]);
93
+ const notifyExpandedChange = useCallback(expandedGroupHashmap => propsWithDefaults === null || propsWithDefaults === void 0 ? void 0 : propsWithDefaults.onExpandChange(expandedGroupHashmap || {}, latestToggledItem), [propsWithDefaults, latestToggledItem]);
69
94
  useEffect(() => {
70
- notifyExpandedChange(expandedGroups);
71
- }, [expandedGroups]);
95
+ if (!propsWithDefaults.expanded) notifyExpandedChange(expandedGroups);
96
+ }, [expandedGroups, notifyExpandedChange, propsWithDefaults.expanded]);
72
97
  };
73
98
 
74
- export { collapseAll, collapseItemModel, expandAll, expandItemModel, getToggleExpandShouldExpand, toggleExpandAllHelper, toggleItemExpand, useNotifyExpandedChange };
99
+ export { collapseAll, collapseItemModel, expandAll, expandItemModel, getToggleExpandShouldExpand, toggleExpandAllHelper, toggleItemExpand, updateExpandedState, useNotifyExpandedChange };
@@ -3,7 +3,8 @@ import TreeViewContext from '../TreeViewContext.js';
3
3
  import { focusItem } from './tree-helpers.js';
4
4
  import { toggleItemExpand, toggleExpandAllHelper } from './group-expands-helpers.js';
5
5
 
6
- /* eslint-disable max-lines */
6
+ /* eslint-disable max-params */
7
+ // 9 tab
7
8
  // 13 enter
8
9
  // 32 space
9
10
  // 37 left arrow
@@ -14,7 +15,6 @@ import { toggleItemExpand, toggleExpandAllHelper } from './group-expands-helpers
14
15
  // 36 home
15
16
  // 56 number 8
16
17
  // 106 (NumPad) *
17
-
18
18
  const useOnItemKeyDown = item => {
19
19
  const ctx = useContext(TreeViewContext);
20
20
  const {
@@ -27,6 +27,7 @@ const useOnItemKeyDown = item => {
27
27
  setFocusedItem,
28
28
  setSelectedItem,
29
29
  setLatestToggledItem,
30
+ updateUserExpandedState,
30
31
  virtualListHelpers: {
31
32
  scrollToIndex
32
33
  }
@@ -44,7 +45,7 @@ const useOnItemKeyDown = item => {
44
45
  return scrollToIndex(newFocusItem.virtualIndex, opts);
45
46
  },
46
47
  item: newFocusItem
47
- }), [onItemFocus]);
48
+ }), [onItemFocus, scrollToIndex]);
48
49
  const onItemKeyDown = useCallback(e => {
49
50
  const {
50
51
  keyCode
@@ -57,14 +58,14 @@ const useOnItemKeyDown = item => {
57
58
 
58
59
  if (keyCode === 37) {
59
60
  if (isGroup && isExpanded) {
60
- toggleItemExpand(item, triggerTreeRerender, scrollToIndex);
61
+ toggleItemExpand(item, triggerTreeRerender, updateUserExpandedState, scrollToIndex);
61
62
  setLatestToggledItem(item);
62
63
  }
63
64
  }
64
65
 
65
66
  if (keyCode === 39) {
66
67
  if (isGroup && !isExpanded) {
67
- toggleItemExpand(item, triggerTreeRerender, scrollToIndex);
68
+ toggleItemExpand(item, triggerTreeRerender, updateUserExpandedState, scrollToIndex);
68
69
  setLatestToggledItem(item);
69
70
  }
70
71
  }
@@ -72,73 +73,76 @@ const useOnItemKeyDown = item => {
72
73
  const {
73
74
  virtualIndex
74
75
  } = item;
75
- const lastItemIndex = visibleItems.length - 1;
76
76
 
77
- if (keyCode === 38) {
78
- const isLoopedFocus = virtualIndex === 0;
79
- const newFocusIndex = isLoopedFocus ? lastItemIndex : virtualIndex - 1;
80
- const newFocusItem = visibleItems[newFocusIndex];
81
- scrollToIndex(newFocusIndex);
77
+ if (typeof virtualIndex === 'number') {
78
+ const lastItemIndex = visibleItems.length - 1;
79
+
80
+ if (keyCode === 38) {
81
+ const isLoopedFocus = virtualIndex === 0;
82
+ const newFocusIndex = isLoopedFocus ? lastItemIndex : virtualIndex - 1;
83
+ const newFocusItem = visibleItems[newFocusIndex];
84
+ scrollToIndex(newFocusIndex);
82
85
 
83
- if (isLoopedFocus) {
84
- // when looping the list from start to end we need to wait a bit before focusing
85
- setTimeout(() => {
86
+ if (isLoopedFocus) {
87
+ // when looping the list from start to end we need to wait a bit before focusing
88
+ setTimeout(() => {
89
+ setFocusedItem(newFocusItem);
90
+ focusItem(newFocusItem);
91
+ onItemFocusCurry(newFocusItem);
92
+ }, 300);
93
+ } else {
86
94
  setFocusedItem(newFocusItem);
87
- focusItem(newFocusItem);
88
95
  onItemFocusCurry(newFocusItem);
89
- }, 300);
90
- } else {
91
- setFocusedItem(newFocusItem);
92
- onItemFocusCurry(newFocusItem);
93
- focusItem(newFocusItem);
96
+ focusItem(newFocusItem);
97
+ }
94
98
  }
95
- }
96
99
 
97
- if (keyCode === 40) {
98
- const isLoopedFocus = virtualIndex === lastItemIndex;
99
- const newFocusIndex = isLoopedFocus ? 0 : virtualIndex + 1;
100
- const newFocusItem = visibleItems[newFocusIndex];
101
- scrollToIndex(newFocusIndex);
100
+ if (keyCode === 40) {
101
+ const isLoopedFocus = virtualIndex === lastItemIndex;
102
+ const newFocusIndex = isLoopedFocus ? 0 : virtualIndex + 1;
103
+ const newFocusItem = visibleItems[newFocusIndex];
104
+ scrollToIndex(newFocusIndex);
102
105
 
103
- if (isLoopedFocus) {
104
- // when looping the list from start to end we need to wait a bit before focusing
105
- setTimeout(() => {
106
+ if (isLoopedFocus) {
107
+ // when looping the list from start to end we need to wait a bit before focusing
108
+ setTimeout(() => {
109
+ setFocusedItem(newFocusItem);
110
+ onItemFocusCurry(newFocusItem);
111
+ focusItem(newFocusItem);
112
+ }, 300);
113
+ } else {
106
114
  setFocusedItem(newFocusItem);
107
115
  onItemFocusCurry(newFocusItem);
108
116
  focusItem(newFocusItem);
109
- }, 300);
110
- } else {
111
- setFocusedItem(newFocusItem);
112
- onItemFocusCurry(newFocusItem);
113
- focusItem(newFocusItem);
117
+ }
114
118
  }
115
- }
116
119
 
117
- if (keyCode === 35) {
118
- if (virtualIndex !== lastItemIndex) {
119
- const newFocusItem = visibleItems[lastItemIndex];
120
- scrollToIndex(lastItemIndex);
121
- setTimeout(() => {
122
- setFocusedItem(newFocusItem);
123
- onItemFocusCurry(newFocusItem);
124
- focusItem(newFocusItem);
125
- }, 300);
120
+ if (keyCode === 35) {
121
+ if (virtualIndex !== lastItemIndex) {
122
+ const newFocusItem = visibleItems[lastItemIndex];
123
+ scrollToIndex(lastItemIndex);
124
+ setTimeout(() => {
125
+ setFocusedItem(newFocusItem);
126
+ onItemFocusCurry(newFocusItem);
127
+ focusItem(newFocusItem);
128
+ }, 300);
129
+ }
126
130
  }
127
- }
128
131
 
129
- if (keyCode === 36) {
130
- if (virtualIndex !== 0) {
131
- const newFocusItem = visibleItems[0];
132
- scrollToIndex(0);
133
- setTimeout(() => {
134
- setFocusedItem(newFocusItem);
135
- onItemFocusCurry(newFocusItem);
136
- focusItem(newFocusItem);
137
- }, 300);
132
+ if (keyCode === 36) {
133
+ if (virtualIndex !== 0) {
134
+ const newFocusItem = visibleItems[0];
135
+ scrollToIndex(0);
136
+ setTimeout(() => {
137
+ setFocusedItem(newFocusItem);
138
+ onItemFocusCurry(newFocusItem);
139
+ focusItem(newFocusItem);
140
+ }, 300);
141
+ }
138
142
  }
139
143
  }
140
144
  }
141
- }, [item, visibleItems, isGroup, isDragging, onItemFocusCurry, setFocusedItem, setSelectedItem]);
145
+ }, [isDragging, setSelectedItem, item, isGroup, isExpanded, triggerTreeRerender, scrollToIndex, setLatestToggledItem, visibleItems, setFocusedItem, onItemFocusCurry, updateUserExpandedState]);
142
146
  return onItemKeyDown;
143
147
  };
144
148
  const useGlobalKeyboardListener = func => {
@@ -147,7 +151,7 @@ const useGlobalKeyboardListener = func => {
147
151
  return () => document.removeEventListener('keydown', func);
148
152
  }, [func]);
149
153
  };
150
- const useGlobalToggleAllExpandShortcut = (treeRoot, triggerTreeRerender, setLatestToggledItem) => {
154
+ const useGlobalToggleAllExpandShortcut = (treeRoot, triggerTreeRerender, setLatestToggledItem, updateUserExpandedState) => {
151
155
  const memoizedToggleAllKeyboardShortcut = useCallback(e => {
152
156
  const {
153
157
  shiftKey,
@@ -155,9 +159,9 @@ const useGlobalToggleAllExpandShortcut = (treeRoot, triggerTreeRerender, setLate
155
159
  } = e;
156
160
 
157
161
  if (shiftKey && keyCode === 56) {
158
- toggleExpandAllHelper('toggle', treeRoot, triggerTreeRerender, setLatestToggledItem);
162
+ toggleExpandAllHelper('toggle', treeRoot, triggerTreeRerender, setLatestToggledItem, updateUserExpandedState);
159
163
  }
160
- }, [treeRoot, triggerTreeRerender]);
164
+ }, [setLatestToggledItem, treeRoot, triggerTreeRerender, updateUserExpandedState]);
161
165
  return useGlobalKeyboardListener(memoizedToggleAllKeyboardShortcut);
162
166
  };
163
167
 
@@ -3,8 +3,6 @@ import 'core-js/modules/esnext.iterator.constructor.js';
3
3
  import 'core-js/modules/esnext.iterator.for-each.js';
4
4
  import 'core-js/modules/esnext.async-iterator.reduce.js';
5
5
  import 'core-js/modules/esnext.iterator.reduce.js';
6
- import 'core-js/modules/esnext.async-iterator.filter.js';
7
- import 'core-js/modules/esnext.iterator.filter.js';
8
6
 
9
7
  /* eslint-disable no-sequences */
10
8
 
@@ -13,6 +11,20 @@ import 'core-js/modules/esnext.iterator.filter.js';
13
11
  /* eslint-disable no-restricted-syntax */
14
12
 
15
13
  /* eslint-disable guard-for-in */
14
+ function isObjectADate(o) {
15
+ var _o$constructor;
16
+
17
+ return (o === null || o === void 0 ? void 0 : (_o$constructor = o.constructor) === null || _o$constructor === void 0 ? void 0 : _o$constructor.name) === 'Date';
18
+ }
19
+
20
+ function isObjectAnArray(o) {
21
+ return Array.isArray(o);
22
+ }
23
+
24
+ function isObjectAString(o) {
25
+ return typeof o === 'object';
26
+ }
27
+
16
28
  const unfreeze = o => {
17
29
  let oo;
18
30
 
@@ -20,16 +32,16 @@ const unfreeze = o => {
20
32
  oo = null;
21
33
  } else if (o === undefined) {
22
34
  oo = undefined;
23
- } else if (o.constructor.name === 'Date') {
35
+ } else if (isObjectADate(o)) {
24
36
  oo = new Date(o);
25
- } else if (o instanceof Array) {
37
+ } else if (isObjectAnArray(o)) {
26
38
  oo = [];
27
39
  o.forEach(v => {
28
40
  oo.push(v);
29
41
  });
30
- } else if (o instanceof String) {
42
+ } else if (isObjectAString(o)) {
31
43
  oo = String(o);
32
- } else if (typeof o === 'object') {
44
+ } else if (isObjectAString(o)) {
33
45
  oo = {};
34
46
 
35
47
  for (const property in o) {
@@ -41,6 +53,12 @@ const unfreeze = o => {
41
53
 
42
54
  return oo;
43
55
  };
44
- const filterObject = (obj, predicate) => Object.keys(obj).filter(key => predicate(obj[key])).reduce((res, key) => (res[key] = obj[key], res), {});
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ function filterObject(obj, predicate) {
58
+ return Object.keys(obj).reduce((res, key) => {
59
+ if (predicate(obj[key])) res[key] = obj[key];
60
+ return res;
61
+ }, {});
62
+ }
45
63
 
46
64
  export { filterObject, unfreeze };
@@ -11,8 +11,8 @@ const setMultipleRefs = function () {
11
11
  }
12
12
 
13
13
  const filteredRefs = refs.filter(Boolean);
14
- if (!filteredRefs.length) return null;
15
- if (filteredRefs.length === 0) return filteredRefs[0];
14
+ if (!filteredRefs.length) return () => {};
15
+ if (filteredRefs.length === 0) return () => {};
16
16
  return newRef => {
17
17
  filteredRefs.forEach(refToBeUpdated => {
18
18
  if (typeof refToBeUpdated === 'function') {
@@ -23,9 +23,9 @@ const walkVisibles = function (node, callback) {
23
23
  matchesSearchQuery
24
24
  } = model;
25
25
  const shouldSkipBecauseIsRoot = skipRoot && name === '__root';
26
- const queryRestrictionsPassed = highlightOnlyQuery || childrenMatchesSearchQuery || matchesSearchQuery;
26
+ const queryRestrictionsPassed = !!(highlightOnlyQuery || childrenMatchesSearchQuery || matchesSearchQuery);
27
27
  if (!shouldSkipBecauseIsRoot && queryRestrictionsPassed) callback(node);
28
- const shouldWalkChildren = children.length && isExpanded && queryRestrictionsPassed;
28
+ const shouldWalkChildren = !!(children.length > 0 && isExpanded && queryRestrictionsPassed);
29
29
 
30
30
  if (shouldWalkChildren) {
31
31
  children.forEach(childNode => {
@@ -61,13 +61,10 @@ const getNodeById = (treeRoot, id) => {
61
61
  return nodes[0];
62
62
  };
63
63
  const cloneNode = (tree, node) => tree.parse(node.model);
64
- const getItemsParentNode = function () {
64
+ const itemsShareSameParent = function () {
65
65
  let items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
66
- let skipSameParentCheck = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
67
- if (items.length === 0) throw new Error("Received empty items, can't retrieve parent"); // in case we already know the items compared shares the same parent
68
-
69
- if (!skipSameParentCheck) if (!itemsShareSameParent(items)) // if we don't know (default, we don't) check if they share the parent
70
- throw new Error("Received items don't share the same parent, can't retrieve parent"); // when moving in first position we receive [undefined,...{items}]
66
+ if (items.length === 0) throw new Error('Received empty items for comparison');
67
+ if (items.length === 1) return true; // when moving in first position we receive [undefined,...{items}]
71
68
  // when moving in last position we receive [...{items}, undefined]
72
69
 
73
70
  const firstExistingItem = items.find(item => !!item);
@@ -78,12 +75,21 @@ const getItemsParentNode = function () {
78
75
  // root node is not printed and is always present so minimum path length is 2 (root + currentNode)
79
76
 
80
77
  const directParentNode = firstExistingItemPath[firstExistingItemPath.length - 2];
81
- return directParentNode;
78
+ return items.every(item => {
79
+ // undefined nodes count as having the same parent
80
+ if (!item) return true;
81
+ const itemPath = item.node.getPath();
82
+ const itemDirectParent = itemPath[itemPath.length - 2];
83
+ return directParentNode === itemDirectParent;
84
+ });
82
85
  };
83
- const itemsShareSameParent = function () {
86
+ const getItemsParentNode = function () {
84
87
  let items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
85
- if (items.length === 0) throw new Error('Received empty items for comparison');
86
- if (items.length === 1) return true; // when moving in first position we receive [undefined,...{items}]
88
+ let skipSameParentCheck = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
89
+ if (items.length === 0) throw new Error("Received empty items, can't retrieve parent"); // in case we already know the items compared shares the same parent
90
+
91
+ if (!skipSameParentCheck) if (!itemsShareSameParent(items)) // if we don't know (default, we don't) check if they share the parent
92
+ throw new Error("Received items don't share the same parent, can't retrieve parent"); // when moving in first position we receive [undefined,...{items}]
87
93
  // when moving in last position we receive [...{items}, undefined]
88
94
 
89
95
  const firstExistingItem = items.find(item => !!item);
@@ -94,13 +100,7 @@ const itemsShareSameParent = function () {
94
100
  // root node is not printed and is always present so minimum path length is 2 (root + currentNode)
95
101
 
96
102
  const directParentNode = firstExistingItemPath[firstExistingItemPath.length - 2];
97
- return items.every(item => {
98
- // undefined nodes count as having the same parent
99
- if (!item) return true;
100
- const itemPath = item.node.getPath();
101
- const itemDirectParent = itemPath[itemPath.length - 2];
102
- return directParentNode === itemDirectParent;
103
- });
103
+ return directParentNode;
104
104
  };
105
105
  const getNodeMatchesSearchQuery = function (node) {
106
106
  let searchQuery = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
@@ -116,11 +116,23 @@ const getChildrenMatchesSearchQuery = function (parentNode) {
116
116
  });
117
117
  return anyChildMatch;
118
118
  };
119
+ /**
120
+ * Mutate in place a node from tree-model normalizing object properties like "isGroup" even if user didn't specify it explicitly,
121
+ * after execution the node will have "nodePath" "treeDepth" "nodeItemRef" "isGroup" "isExpanded"
122
+ * (optionally, based on the provided arguments) "matchesSearchQuery" "childrenMatchesSearchQuery" "isChecked"
123
+ *
124
+ * @param {Object} node - node (based on 'tree-model' lib) that you wish to mutate with extra props
125
+ * @param {string} [searchQuery=''] - optional string used to parse "matchesSearchQuery"/"childrenMatchesSearchQuery" properties
126
+ * @param {Object} [selection=null] - optional selection hashmap used to parse "isChecked" properties
127
+ * @param {Object} [expanded=null] - optional expanded hashmap used to parse "isExpanded" properties
128
+ */
129
+
119
130
  const enrichNodeModelInPlace = function (node) {
120
131
  var _node$model, _node$model$children;
121
132
 
122
133
  let searchQuery = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
123
134
  let selection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
135
+ let expanded = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
124
136
  const path = node.getPath();
125
137
  const depth = path.length - 1;
126
138
  node.model.nodePath = path;
@@ -136,24 +148,27 @@ const enrichNodeModelInPlace = function (node) {
136
148
  node.model.isGroup = true;
137
149
  }
138
150
 
139
- if (typeof node.model.isExpanded !== 'boolean') {
140
- node.model.isExpanded = false;
151
+ if (node.model.isGroup && node.model.id !== '__ds_tree_root') {
152
+ if (expanded) node.model.isExpanded = !!expanded[node.model.id];else if (typeof node.model.isExpanded !== 'boolean') {
153
+ node.model.isExpanded = false;
154
+ }
141
155
  }
142
156
 
143
157
  node.model.matchesSearchQuery = true;
144
158
  node.model.childrenMatchesSearchQuery = false;
159
+ const partiallyEnrichedNode = node;
145
160
 
146
161
  if (searchQuery) {
147
- node.model.matchesSearchQuery = getNodeMatchesSearchQuery(node, searchQuery);
162
+ partiallyEnrichedNode.model.matchesSearchQuery = getNodeMatchesSearchQuery(partiallyEnrichedNode, searchQuery);
148
163
 
149
- if (node.model.isGroup === true) {
150
- node.model.childrenMatchesSearchQuery = getChildrenMatchesSearchQuery(node, searchQuery);
151
- if (node.model.childrenMatchesSearchQuery) node.model.isExpanded = true;
164
+ if (partiallyEnrichedNode.model.isGroup === true) {
165
+ partiallyEnrichedNode.model.childrenMatchesSearchQuery = getChildrenMatchesSearchQuery(partiallyEnrichedNode, searchQuery);
166
+ if (partiallyEnrichedNode.model.childrenMatchesSearchQuery) partiallyEnrichedNode.model.isExpanded = true;
152
167
  }
153
168
  }
154
169
 
155
- if (selection && selection[node.model.id]) {
156
- node.model.isChecked = selection[node.model.id];
170
+ if (selection && selection[partiallyEnrichedNode.model.id]) {
171
+ partiallyEnrichedNode.model.isChecked = selection[partiallyEnrichedNode.model.id];
157
172
  }
158
173
  };
159
174
  const focusItem = item => {