@elliemae/ds-treeview 2.0.1 → 2.1.0-rc.3

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 (167) 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 +7 -9
  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 +16 -13
  11. package/cjs/parts/TreeList.js +6 -4
  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/selectable-helper.js +2 -2
  22. package/cjs/utils/tree-helpers.js +42 -27
  23. package/cjs/utils/useDnDHelpers.js +107 -100
  24. package/cjs/utils/useInstanceRefActions.js +3 -2
  25. package/cjs/utils/useTree.js +27 -8
  26. package/esm/TreeView.js +6 -103
  27. package/esm/TreeViewContext.js +44 -47
  28. package/esm/config/useTreeview.js +129 -0
  29. package/esm/index.js +0 -8
  30. package/esm/parts/CheckboxSelectable.js +7 -9
  31. package/esm/parts/ExpandCaret.js +17 -11
  32. package/esm/parts/Icon.js +4 -0
  33. package/esm/parts/NestingSpace.js +15 -21
  34. package/esm/parts/RadioSelectable.js +7 -9
  35. package/esm/parts/TreeItem.js +16 -13
  36. package/esm/parts/TreeList.js +6 -4
  37. package/esm/plugins/dnd/TreeDndPlugin.js +1 -1
  38. package/esm/react-desc-prop-types.js +28 -27
  39. package/esm/related-components/TreeViewSearchBar.js +9 -1
  40. package/esm/{deprecated/plugins/selectable/helper.js → sharedTypes.js} +0 -0
  41. package/esm/utils/dnd-helpers.js +2 -4
  42. package/esm/utils/group-expands-helpers.js +34 -9
  43. package/esm/utils/keyboard-helpers.js +62 -58
  44. package/esm/utils/object-helpers.js +25 -7
  45. package/esm/utils/refs-helpers.js +2 -2
  46. package/esm/utils/selectable-helper.js +2 -2
  47. package/esm/utils/tree-helpers.js +42 -27
  48. package/esm/utils/useDnDHelpers.js +107 -100
  49. package/esm/utils/useInstanceRefActions.js +3 -2
  50. package/esm/utils/useTree.js +27 -8
  51. package/package.json +17 -128
  52. package/types/TreeView.d.ts +4 -148
  53. package/types/TreeViewContext.d.ts +4 -106
  54. package/types/config/useTreeview.d.ts +7 -0
  55. package/types/index.d.ts +0 -1
  56. package/types/parts/CheckboxSelectable.d.ts +5 -6
  57. package/types/parts/ChildrenCountDisplayer.d.ts +6 -6
  58. package/types/parts/ExpandCaret.d.ts +6 -6
  59. package/types/parts/Icon.d.ts +7 -5
  60. package/types/parts/NestingSpace.d.ts +3 -8
  61. package/types/parts/RadioSelectable.d.ts +5 -6
  62. package/types/parts/TreeItem.d.ts +22 -14
  63. package/types/parts/TreeItemText.d.ts +5 -5
  64. package/types/parts/TreeList.d.ts +22 -9
  65. package/types/react-desc-prop-types.d.ts +124 -43
  66. package/types/related-components/TreeViewSearchBar.d.ts +14 -11
  67. package/types/sharedTypes.d.ts +54 -0
  68. package/types/utils/array-helpers.d.ts +2 -2
  69. package/types/utils/dnd-helpers.d.ts +26 -25
  70. package/types/utils/group-expands-helpers.d.ts +13 -10
  71. package/types/utils/keyboard-helpers.d.ts +5 -3
  72. package/types/utils/object-helpers.d.ts +6 -2
  73. package/types/utils/refs-helpers.d.ts +4 -1
  74. package/types/utils/selectable-helper.d.ts +7 -3
  75. package/types/utils/string-helpers.d.ts +1 -1
  76. package/types/utils/tree-helpers.d.ts +25 -11
  77. package/types/utils/useDnDHelpers.d.ts +23 -3
  78. package/types/utils/useInstanceRefActions.d.ts +3 -3
  79. package/types/utils/useTree.d.ts +8 -7
  80. package/cjs/deprecated/TreeView.js +0 -72
  81. package/cjs/deprecated/__testUtils__/utils.js +0 -89
  82. package/cjs/deprecated/index.js +0 -27
  83. package/cjs/deprecated/plugins/dnd/TreeDndPlugin.js +0 -119
  84. package/cjs/deprecated/plugins/dnd/index.js +0 -9
  85. package/cjs/deprecated/plugins/dnd/renderers/DraggableItem.js +0 -67
  86. package/cjs/deprecated/plugins/dnd/renderers/DroppableList.js +0 -181
  87. package/cjs/deprecated/plugins/dnd/utils.js +0 -157
  88. package/cjs/deprecated/plugins/index.js +0 -21
  89. package/cjs/deprecated/plugins/roving/TreeRovingPlugin.js +0 -109
  90. package/cjs/deprecated/plugins/roving/index.js +0 -9
  91. package/cjs/deprecated/plugins/searchable/SearchableTreePlugin.js +0 -109
  92. package/cjs/deprecated/plugins/searchable/index.js +0 -9
  93. package/cjs/deprecated/plugins/searchable/utils.js +0 -7
  94. package/cjs/deprecated/plugins/selectable/SelectablePluginTree.js +0 -290
  95. package/cjs/deprecated/plugins/selectable/index.js +0 -9
  96. package/cjs/deprecated/plugins/selectable/utils.js +0 -19
  97. package/cjs/deprecated/plugins/toolbar/TreeToolbarPlugin.js +0 -128
  98. package/cjs/deprecated/plugins/toolbar/index.js +0 -9
  99. package/cjs/deprecated/plugins/tree/TreeDataPlugin.js +0 -281
  100. package/cjs/deprecated/plugins/tree/index.js +0 -9
  101. package/cjs/deprecated/plugins/tree/useExpandTreeState.js +0 -143
  102. package/cjs/deprecated/plugins/tree/utils.js +0 -43
  103. package/cjs/deprecated/plugins/tree/walkTreeStrategy.js +0 -60
  104. package/cjs/deprecated/plugins/virtualization/TreeVirtualizationPlugin.js +0 -209
  105. package/cjs/deprecated/plugins/virtualization/index.js +0 -9
  106. package/cjs/deprecated/renderers.js +0 -76
  107. package/cjs/prop-types.js +0 -61
  108. package/esm/deprecated/TreeView.js +0 -61
  109. package/esm/deprecated/__testUtils__/utils.js +0 -79
  110. package/esm/deprecated/index.js +0 -14
  111. package/esm/deprecated/plugins/dnd/TreeDndPlugin.js +0 -110
  112. package/esm/deprecated/plugins/dnd/index.js +0 -1
  113. package/esm/deprecated/plugins/dnd/renderers/DraggableItem.js +0 -59
  114. package/esm/deprecated/plugins/dnd/renderers/DroppableList.js +0 -172
  115. package/esm/deprecated/plugins/dnd/utils.js +0 -139
  116. package/esm/deprecated/plugins/index.js +0 -7
  117. package/esm/deprecated/plugins/roving/TreeRovingPlugin.js +0 -96
  118. package/esm/deprecated/plugins/roving/index.js +0 -1
  119. package/esm/deprecated/plugins/searchable/SearchableTreePlugin.js +0 -99
  120. package/esm/deprecated/plugins/searchable/index.js +0 -1
  121. package/esm/deprecated/plugins/searchable/utils.js +0 -3
  122. package/esm/deprecated/plugins/selectable/SelectablePluginTree.js +0 -280
  123. package/esm/deprecated/plugins/selectable/index.js +0 -1
  124. package/esm/deprecated/plugins/selectable/utils.js +0 -15
  125. package/esm/deprecated/plugins/toolbar/TreeToolbarPlugin.js +0 -114
  126. package/esm/deprecated/plugins/toolbar/index.js +0 -1
  127. package/esm/deprecated/plugins/tree/TreeDataPlugin.js +0 -269
  128. package/esm/deprecated/plugins/tree/index.js +0 -1
  129. package/esm/deprecated/plugins/tree/useExpandTreeState.js +0 -135
  130. package/esm/deprecated/plugins/tree/utils.js +0 -28
  131. package/esm/deprecated/plugins/tree/walkTreeStrategy.js +0 -56
  132. package/esm/deprecated/plugins/virtualization/TreeVirtualizationPlugin.js +0 -197
  133. package/esm/deprecated/plugins/virtualization/index.js +0 -1
  134. package/esm/deprecated/renderers.js +0 -59
  135. package/esm/prop-types.js +0 -46
  136. package/types/deprecated/TreeView.d.ts +0 -8
  137. package/types/deprecated/__testUtils__/utils.d.ts +0 -72
  138. package/types/deprecated/index.d.ts +0 -2
  139. package/types/deprecated/plugins/dnd/TreeDndPlugin.d.ts +0 -1
  140. package/types/deprecated/plugins/dnd/index.d.ts +0 -1
  141. package/types/deprecated/plugins/dnd/renderers/DraggableItem.d.ts +0 -3
  142. package/types/deprecated/plugins/dnd/renderers/DroppableList.d.ts +0 -6
  143. package/types/deprecated/plugins/dnd/tests/TreeDndPlugin.test.d.ts +0 -0
  144. package/types/deprecated/plugins/dnd/utils.d.ts +0 -28
  145. package/types/deprecated/plugins/index.d.ts +0 -7
  146. package/types/deprecated/plugins/roving/TreeRovingPlugin.d.ts +0 -1
  147. package/types/deprecated/plugins/roving/index.d.ts +0 -1
  148. package/types/deprecated/plugins/searchable/SearchableTreePlugin.d.ts +0 -1
  149. package/types/deprecated/plugins/searchable/index.d.ts +0 -1
  150. package/types/deprecated/plugins/searchable/utils.d.ts +0 -1
  151. package/types/deprecated/plugins/selectable/SelectablePluginTree.d.ts +0 -1
  152. package/types/deprecated/plugins/selectable/helper.d.ts +0 -0
  153. package/types/deprecated/plugins/selectable/index.d.ts +0 -1
  154. package/types/deprecated/plugins/selectable/tests/SelectablePluginTree.test.d.ts +0 -1
  155. package/types/deprecated/plugins/selectable/utils.d.ts +0 -1
  156. package/types/deprecated/plugins/toolbar/TreeToolbarPlugin.d.ts +0 -1
  157. package/types/deprecated/plugins/toolbar/index.d.ts +0 -1
  158. package/types/deprecated/plugins/tree/TreeDataPlugin.d.ts +0 -1
  159. package/types/deprecated/plugins/tree/index.d.ts +0 -1
  160. package/types/deprecated/plugins/tree/tests/TreeDataPlugin.test.d.ts +0 -1
  161. package/types/deprecated/plugins/tree/useExpandTreeState.d.ts +0 -14
  162. package/types/deprecated/plugins/tree/utils.d.ts +0 -8
  163. package/types/deprecated/plugins/tree/walkTreeStrategy.d.ts +0 -9
  164. package/types/deprecated/plugins/virtualization/TreeVirtualizationPlugin.d.ts +0 -1
  165. package/types/deprecated/plugins/virtualization/index.d.ts +0 -1
  166. package/types/deprecated/renderers.d.ts +0 -19
  167. package/types/prop-types.d.ts +0 -26
@@ -1,49 +1,50 @@
1
1
  import { PropTypes } from 'react-desc';
2
2
 
3
3
  /* eslint-disable max-lines */
4
- const InstanceActionsPropTypes = PropTypes.shape({
5
- toggleExpandAll: PropTypes.func,
6
- scrollTo: PropTypes.func
7
- });
8
- const TreeViewItemPropType = PropTypes.shape({
9
- children: PropTypes.arrayOf(PropTypes.object),
10
- id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
11
- isGroup: PropTypes.oneOfType([PropTypes.oneOf([undefined, null]), PropTypes.bool]),
12
- name: PropTypes.string,
13
- node: PropTypes.object
14
- });
15
- const TreeViewInstancePropType = PropTypes.shape({
16
- current: PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.shape({
17
- actions: InstanceActionsPropTypes.isRequired
18
- })])
19
- });
20
4
  const TreeViewPropTypes = {
21
- data: PropTypes.arrayOf(TreeViewItemPropType).description('data to populate tree view').isRequired,
5
+ data: PropTypes.arrayOf(PropTypes.shape({
6
+ children: PropTypes.arrayOf(PropTypes.object),
7
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
8
+ isGroup: PropTypes.oneOfType([PropTypes.oneOf([undefined, null]), PropTypes.bool]),
9
+ name: PropTypes.string,
10
+ node: PropTypes.object
11
+ })).description('data to populate tree view').isRequired,
22
12
  plugins: PropTypes.arrayOf(PropTypes.string).description('list of enabled plugins').defaultValue([]),
23
13
  selection: PropTypes.object.description('pre selected items').defaultValue({}),
24
- instanceRef: TreeViewInstancePropType.description('instance ref'),
25
- disableGroups: PropTypes.bool.description('disable grouping funcionality').defaultValue(false),
26
- disableLeafs: PropTypes.bool.description('disable leaf items').defaultValue(false),
14
+ expanded: PropTypes.object.description('control what items are expanded').defaultValue(undefined),
15
+ instanceRef: PropTypes.shape({
16
+ current: PropTypes.oneOfType([PropTypes.oneOf([null, undefined]), PropTypes.shape({
17
+ actions: PropTypes.shape({
18
+ toggleExpandAll: PropTypes.func,
19
+ scrollTo: PropTypes.func
20
+ }).description('').isRequired
21
+ })])
22
+ }).description('instance ref'),
23
+ isItemDisabled: PropTypes.func.description('Function that receives an item, and returns whether that item should be disabled').defaultValue('() => false'),
27
24
  disableIcons: PropTypes.bool.description('disable icons').defaultValue(false),
28
25
  fluid: PropTypes.bool.description('whether is fluid or not').defaultValue(false),
29
26
  isMultiSelect: PropTypes.bool.description('whether the tree view is multi select or not').defaultValue(false),
30
27
  isSingleSelect: PropTypes.bool.description('wheter the tree view is single select or not').defaultValue(false),
28
+ isLoading: PropTypes.bool.description('wheter the tree should show the loading state or not').defaultValue(false),
31
29
  restrictDragAndDrop: PropTypes.bool.description('whether to restrict DnD functionality or not').defaultValue(false),
32
30
  showChildrenAmount: PropTypes.bool.description('whether to show the amount of children or not').defaultValue(false),
33
31
  sortable: PropTypes.bool.description('whether the sortable functionality is active or not').defaultValue(false),
34
- labelRenderer: PropTypes.func.description('custom renderer for labels'),
35
- onItemClick: PropTypes.func.description('function executed when clicking on an item'),
36
- onItemFocus: PropTypes.func.description('function executed when focusing on an item'),
37
- onVisibleItemsChange: PropTypes.func.description('function executed when the visible items change'),
38
- onOrderChange: PropTypes.func.description('function executed when the items order changes'),
39
- onSelectionChange: PropTypes.func.description('function executed when the selection changes'),
40
- onExpandChange: PropTypes.func.description('function executed when expanding an item'),
32
+ onItemClick: PropTypes.func.description('function executed when clicking on an item').defaultValue('() => {}'),
33
+ onItemFocus: PropTypes.func.description('function executed when focusing on an item').defaultValue('() => {}'),
34
+ onVisibleItemsChange: PropTypes.func.description('function executed when the visible items change').defaultValue('() => {}'),
35
+ onOrderChange: PropTypes.func.description('function executed when the items order changes').defaultValue('() => {}'),
36
+ onSelectionChange: PropTypes.func.description('function executed when the selection changes').defaultValue('() => {}'),
37
+ onExpandChange: PropTypes.func.description('function executed when expanding an item').defaultValue('() => {}'),
38
+ onInstanceRefInitialized: PropTypes.func.description('function executed when instanceRef has terminated instanciation').defaultValue('() => {}'),
41
39
  rowSize: PropTypes.oneOf(['normal', 'compact']).description('size of the row').defaultValue('normal'),
40
+ labelOverflow: PropTypes.oneOf(['wrap', 'wrap-all', 'truncate']).description('label truncation preference').defaultValue('wrap'),
42
41
  width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).description('components width').defaultValue('100%'),
43
42
  height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).description('components height').defaultValue('100%'),
44
43
  groupIcon: PropTypes.node.description('icon for groups'),
45
44
  itemIcon: PropTypes.node.description('icon for items'),
46
45
  searchQuery: PropTypes.string.description('search query value'),
46
+ noItemsPlaceholder: PropTypes.string.description('message to be shown when there are no items in the treeview to be displayed').defaultValue('No items found'),
47
+ name: PropTypes.string.description('property key to identifier for items labels').defaultValue('name'),
47
48
  highlightOnlyQuery: PropTypes.bool.description('whether to highlight results or not').defaultValue(false)
48
49
  };
49
50
 
@@ -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') {
@@ -6,9 +6,9 @@ import 'core-js/modules/esnext.iterator.for-each.js';
6
6
  import _defineProperty from '@babel/runtime/helpers/esm/defineProperty';
7
7
  import { walkAllNodeChildren, walkParents } from './tree-helpers.js';
8
8
 
9
- function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
9
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
10
10
 
11
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
11
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
12
12
  const getGroupCheckState = node => {
13
13
  let childAllChecked = true;
14
14
  let anyChildChecked = false;
@@ -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 => {