@headless-tree/core 0.0.5 → 0.0.7

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 (93) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/lib/cjs/core/create-tree.js +34 -12
  3. package/lib/cjs/features/async-data-loader/feature.js +30 -22
  4. package/lib/cjs/features/drag-and-drop/feature.js +58 -18
  5. package/lib/cjs/features/drag-and-drop/types.d.ts +21 -1
  6. package/lib/cjs/features/drag-and-drop/utils.d.ts +1 -1
  7. package/lib/cjs/features/drag-and-drop/utils.js +34 -17
  8. package/lib/cjs/features/expand-all/feature.js +12 -10
  9. package/lib/cjs/features/expand-all/types.d.ts +2 -2
  10. package/lib/cjs/features/hotkeys-core/feature.js +0 -1
  11. package/lib/cjs/features/main/types.d.ts +3 -1
  12. package/lib/cjs/features/renaming/feature.js +10 -11
  13. package/lib/cjs/features/search/feature.js +21 -8
  14. package/lib/cjs/features/search/types.d.ts +1 -1
  15. package/lib/cjs/features/selection/feature.js +6 -5
  16. package/lib/cjs/features/sync-data-loader/feature.js +6 -1
  17. package/lib/cjs/features/sync-data-loader/types.d.ts +1 -1
  18. package/lib/cjs/features/tree/feature.js +23 -24
  19. package/lib/cjs/features/tree/types.d.ts +2 -2
  20. package/lib/cjs/index.d.ts +3 -1
  21. package/lib/cjs/index.js +3 -1
  22. package/lib/cjs/types/core.d.ts +3 -4
  23. package/lib/cjs/utilities/create-on-drop-handler.d.ts +3 -0
  24. package/lib/cjs/utilities/create-on-drop-handler.js +11 -0
  25. package/lib/cjs/utilities/insert-items-at-target.d.ts +3 -0
  26. package/lib/cjs/utilities/insert-items-at-target.js +24 -0
  27. package/lib/cjs/utilities/remove-items-from-parents.d.ts +2 -0
  28. package/lib/cjs/utilities/remove-items-from-parents.js +17 -0
  29. package/lib/cjs/utils.d.ts +1 -4
  30. package/lib/cjs/utils.js +1 -53
  31. package/lib/esm/core/create-tree.js +34 -12
  32. package/lib/esm/features/async-data-loader/feature.js +30 -22
  33. package/lib/esm/features/drag-and-drop/feature.js +58 -18
  34. package/lib/esm/features/drag-and-drop/types.d.ts +21 -1
  35. package/lib/esm/features/drag-and-drop/utils.d.ts +1 -1
  36. package/lib/esm/features/drag-and-drop/utils.js +35 -18
  37. package/lib/esm/features/expand-all/feature.js +12 -10
  38. package/lib/esm/features/expand-all/types.d.ts +2 -2
  39. package/lib/esm/features/hotkeys-core/feature.js +0 -1
  40. package/lib/esm/features/main/types.d.ts +3 -1
  41. package/lib/esm/features/renaming/feature.js +10 -11
  42. package/lib/esm/features/search/feature.js +21 -8
  43. package/lib/esm/features/search/types.d.ts +1 -1
  44. package/lib/esm/features/selection/feature.js +6 -5
  45. package/lib/esm/features/sync-data-loader/feature.js +6 -1
  46. package/lib/esm/features/sync-data-loader/types.d.ts +1 -1
  47. package/lib/esm/features/tree/feature.js +23 -24
  48. package/lib/esm/features/tree/types.d.ts +2 -2
  49. package/lib/esm/index.d.ts +3 -1
  50. package/lib/esm/index.js +3 -1
  51. package/lib/esm/types/core.d.ts +3 -4
  52. package/lib/esm/utilities/create-on-drop-handler.d.ts +3 -0
  53. package/lib/esm/utilities/create-on-drop-handler.js +7 -0
  54. package/lib/esm/utilities/insert-items-at-target.d.ts +3 -0
  55. package/lib/esm/utilities/insert-items-at-target.js +20 -0
  56. package/lib/esm/utilities/remove-items-from-parents.d.ts +2 -0
  57. package/lib/esm/utilities/remove-items-from-parents.js +13 -0
  58. package/lib/esm/utils.d.ts +1 -4
  59. package/lib/esm/utils.js +0 -50
  60. package/package.json +1 -1
  61. package/src/core/create-tree.ts +42 -9
  62. package/src/features/async-data-loader/feature.ts +15 -6
  63. package/src/features/drag-and-drop/feature.ts +75 -14
  64. package/src/features/drag-and-drop/types.ts +30 -5
  65. package/src/features/drag-and-drop/utils.ts +53 -24
  66. package/src/features/expand-all/feature.ts +10 -9
  67. package/src/features/expand-all/types.ts +2 -2
  68. package/src/features/hotkeys-core/feature.ts +0 -1
  69. package/src/features/main/types.ts +6 -0
  70. package/src/features/renaming/feature.ts +10 -7
  71. package/src/features/search/feature.ts +22 -6
  72. package/src/features/search/types.ts +1 -0
  73. package/src/features/selection/feature.ts +7 -3
  74. package/src/features/sync-data-loader/feature.ts +17 -3
  75. package/src/features/sync-data-loader/types.ts +1 -1
  76. package/src/features/tree/feature.ts +23 -22
  77. package/src/features/tree/types.ts +4 -2
  78. package/src/index.ts +4 -1
  79. package/src/types/core.ts +6 -5
  80. package/src/utilities/create-on-drop-handler.ts +14 -0
  81. package/src/utilities/insert-items-at-target.ts +30 -0
  82. package/src/utilities/remove-items-from-parents.ts +21 -0
  83. package/src/utils.ts +1 -69
  84. package/lib/cjs/data-adapters/nested-data-adapter.d.ts +0 -9
  85. package/lib/cjs/data-adapters/nested-data-adapter.js +0 -32
  86. package/lib/cjs/data-adapters/types.d.ts +0 -7
  87. package/lib/cjs/data-adapters/types.js +0 -2
  88. package/lib/esm/data-adapters/nested-data-adapter.d.ts +0 -9
  89. package/lib/esm/data-adapters/nested-data-adapter.js +0 -28
  90. package/lib/esm/data-adapters/types.d.ts +0 -7
  91. package/lib/esm/data-adapters/types.js +0 -1
  92. package/src/data-adapters/nested-data-adapter.ts +0 -48
  93. package/src/data-adapters/types.ts +0 -9
@@ -4,20 +4,22 @@ exports.searchFeature = void 0;
4
4
  const utils_1 = require("../../utils");
5
5
  exports.searchFeature = {
6
6
  key: "search",
7
- dependingFeatures: ["main", "tree"],
8
7
  getInitialState: (initialState) => (Object.assign({ search: null }, initialState)),
9
8
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setSearch: (0, utils_1.makeStateUpdater)("search", tree), isSearchMatchingItem: (search, item) => search.length > 0 &&
10
9
  item.getItemName().toLowerCase().includes(search.toLowerCase()) }, defaultConfig)),
10
+ stateHandlerNames: {
11
+ search: "setSearch",
12
+ },
11
13
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { setSearch: (search) => {
12
- var _a, _b, _c;
13
- (_b = (_a = instance.getConfig()).setSearch) === null || _b === void 0 ? void 0 : _b.call(_a, search);
14
- (_c = instance
14
+ var _a;
15
+ instance.applySubStateUpdate("search", search);
16
+ (_a = instance
15
17
  .getItems()
16
18
  .find((item) => {
17
19
  var _a, _b;
18
20
  return (_b = (_a = instance
19
21
  .getConfig()).isSearchMatchingItem) === null || _b === void 0 ? void 0 : _b.call(_a, instance.getSearchValue(), item);
20
- })) === null || _c === void 0 ? void 0 : _c.setFocused();
22
+ })) === null || _a === void 0 ? void 0 : _a.setFocused();
21
23
  }, openSearch: (initialValue = "") => {
22
24
  instance.setSearch(initialValue);
23
25
  setTimeout(() => {
@@ -39,7 +41,7 @@ exports.searchFeature = {
39
41
  value: instance.getSearchValue(),
40
42
  onChange: (e) => instance.setSearch(e.target.value),
41
43
  onBlur: () => instance.closeSearch(),
42
- }), getSearchMatchingItems: (0, utils_1.memo)((search, items) => items.filter((item) => { var _a, _b; return (_b = (_a = instance.getConfig()).isSearchMatchingItem) === null || _b === void 0 ? void 0 : _b.call(_a, search, item); }), () => [instance.getSearchValue(), instance.getItems()]) })),
44
+ }), getSearchMatchingItems: (0, utils_1.memo)((search, items) => items.filter((item) => { var _a, _b; return search && ((_b = (_a = instance.getConfig()).isSearchMatchingItem) === null || _b === void 0 ? void 0 : _b.call(_a, search, item)); }), () => [instance.getSearchValue(), instance.getItems()]) })),
43
45
  createItemInstance: (prev, item, tree) => (Object.assign(Object.assign({}, prev), { isMatchingSearch: () => tree.getSearchMatchingItems().some((i) => i.getId() === item.getId()) })),
44
46
  hotkeys: {
45
47
  openSearch: {
@@ -60,30 +62,41 @@ exports.searchFeature = {
60
62
  tree.closeSearch();
61
63
  },
62
64
  },
65
+ submitSearch: {
66
+ hotkey: "Enter",
67
+ allowWhenInputFocused: true,
68
+ isEnabled: (tree) => tree.isSearchOpen(),
69
+ handler: (e, tree) => {
70
+ tree.closeSearch();
71
+ tree.setSelectedItems([tree.getFocusedItem().getId()]);
72
+ },
73
+ },
63
74
  nextSearchItem: {
64
75
  hotkey: "ArrowDown",
65
76
  allowWhenInputFocused: true,
77
+ canRepeat: true,
66
78
  isEnabled: (tree) => tree.isSearchOpen(),
67
79
  handler: (e, tree) => {
68
- // TODO scroll into view
69
80
  const focusItem = tree
70
81
  .getSearchMatchingItems()
71
82
  .find((item) => item.getItemMeta().index >
72
83
  tree.getFocusedItem().getItemMeta().index);
73
84
  focusItem === null || focusItem === void 0 ? void 0 : focusItem.setFocused();
85
+ focusItem === null || focusItem === void 0 ? void 0 : focusItem.scrollTo({ block: "nearest", inline: "nearest" });
74
86
  },
75
87
  },
76
88
  previousSearchItem: {
77
89
  hotkey: "ArrowUp",
78
90
  allowWhenInputFocused: true,
91
+ canRepeat: true,
79
92
  isEnabled: (tree) => tree.isSearchOpen(),
80
93
  handler: (e, tree) => {
81
- // TODO scroll into view
82
94
  const focusItem = [...tree.getSearchMatchingItems()]
83
95
  .reverse()
84
96
  .find((item) => item.getItemMeta().index <
85
97
  tree.getFocusedItem().getItemMeta().index);
86
98
  focusItem === null || focusItem === void 0 ? void 0 : focusItem.setFocused();
99
+ focusItem === null || focusItem === void 0 ? void 0 : focusItem.scrollTo({ block: "nearest", inline: "nearest" });
87
100
  },
88
101
  },
89
102
  },
@@ -29,5 +29,5 @@ export type SearchFeatureDef<T> = {
29
29
  itemInstance: {
30
30
  isMatchingSearch: () => boolean;
31
31
  };
32
- hotkeys: "openSearch" | "closeSearch" | "nextSearchItem" | "previousSearchItem";
32
+ hotkeys: "openSearch" | "closeSearch" | "submitSearch" | "nextSearchItem" | "previousSearchItem";
33
33
  };
@@ -4,12 +4,13 @@ exports.selectionFeature = void 0;
4
4
  const utils_1 = require("../../utils");
5
5
  exports.selectionFeature = {
6
6
  key: "selection",
7
- dependingFeatures: ["main", "tree"],
8
7
  getInitialState: (initialState) => (Object.assign({ selectedItems: [] }, initialState)),
9
8
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setSelectedItems: (0, utils_1.makeStateUpdater)("selectedItems", tree) }, defaultConfig)),
9
+ stateHandlerNames: {
10
+ selectedItems: "setSelectedItems",
11
+ },
10
12
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { setSelectedItems: (selectedItems) => {
11
- var _a, _b;
12
- (_b = (_a = instance.getConfig()).setSelectedItems) === null || _b === void 0 ? void 0 : _b.call(_a, selectedItems);
13
+ instance.applySubStateUpdate("selectedItems", selectedItems);
13
14
  },
14
15
  // TODO memo
15
16
  getSelectedItems: () => {
@@ -34,7 +35,7 @@ exports.selectionFeature = {
34
35
  const newSelectedItems = tree
35
36
  .getItems()
36
37
  .slice(a, b + 1)
37
- .map((item) => item.getItemMeta().itemId);
38
+ .map((treeItem) => treeItem.getItemMeta().itemId);
38
39
  if (!ctrl) {
39
40
  tree.setSelectedItems(newSelectedItems);
40
41
  return;
@@ -51,7 +52,7 @@ exports.selectionFeature = {
51
52
  else {
52
53
  item.select();
53
54
  }
54
- }, getProps: () => (Object.assign(Object.assign({}, prev.getProps()), { onClick: item.getMemoizedProp("selection/onClick", () => (e) => {
55
+ }, getProps: () => (Object.assign(Object.assign({}, prev.getProps()), { "aria-selected": item.isSelected() ? "true" : "false", onClick: item.getMemoizedProp("selection/onClick", () => (e) => {
55
56
  var _a, _b;
56
57
  if (e.shiftKey) {
57
58
  item.selectUpTo(e.ctrlKey || e.metaKey);
@@ -1,9 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.syncDataLoaderFeature = void 0;
4
+ const utils_1 = require("../../utils");
4
5
  exports.syncDataLoaderFeature = {
5
6
  key: "sync-data-loader",
6
- dependingFeatures: ["main"],
7
+ getInitialState: (initialState) => (Object.assign({ loadingItems: [] }, initialState)),
8
+ getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setLoadingItems: (0, utils_1.makeStateUpdater)("loadingItems", tree) }, defaultConfig)),
9
+ stateHandlerNames: {
10
+ loadingItems: "setLoadingItems",
11
+ },
7
12
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { retrieveItemData: (itemId) => instance.getConfig().dataLoader.getItem(itemId), retrieveChildrenIds: (itemId) => instance.getConfig().dataLoader.getChildren(itemId) })),
8
13
  createItemInstance: (prev) => (Object.assign(Object.assign({}, prev), { isLoading: () => false })),
9
14
  };
@@ -6,7 +6,7 @@ export type SyncDataLoaderFeatureDef<T> = {
6
6
  state: {};
7
7
  config: {
8
8
  rootItemId: string;
9
- dataLoader: SyncTreeDataLoader<T>;
9
+ dataLoader?: SyncTreeDataLoader<T>;
10
10
  };
11
11
  treeInstance: {
12
12
  retrieveItemData: (itemId: string) => T;
@@ -13,9 +13,12 @@ exports.treeFeature = void 0;
13
13
  const utils_1 = require("../../utils");
14
14
  exports.treeFeature = {
15
15
  key: "tree",
16
- dependingFeatures: ["main"],
17
16
  getInitialState: (initialState) => (Object.assign({ expandedItems: [], focusedItem: null }, initialState)),
18
17
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setExpandedItems: (0, utils_1.makeStateUpdater)("expandedItems", tree), setFocusedItem: (0, utils_1.makeStateUpdater)("focusedItem", tree) }, defaultConfig)),
18
+ stateHandlerNames: {
19
+ expandedItems: "setExpandedItems",
20
+ focusedItem: "setFocusedItem",
21
+ },
19
22
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { retrieveItemData: () => {
20
23
  throw new Error("No data-loader registered");
21
24
  }, retrieveChildrenIds: () => {
@@ -23,7 +26,6 @@ exports.treeFeature = {
23
26
  }, isItemExpanded: (itemId) => instance.getState().expandedItems.includes(itemId), getItemsMeta: () => {
24
27
  const { rootItemId } = instance.getConfig();
25
28
  const { expandedItems } = instance.getState();
26
- // console.log("!", instance.getConfig());
27
29
  const flatItems = [];
28
30
  const recursiveAdd = (itemId, parentId, level, setSize, posInSet) => {
29
31
  var _a;
@@ -50,23 +52,23 @@ exports.treeFeature = {
50
52
  }
51
53
  return flatItems;
52
54
  }, expandItem: (itemId) => {
53
- var _a, _b, _c;
55
+ var _a;
54
56
  if (!instance.getItemInstance(itemId).isFolder()) {
55
57
  return;
56
58
  }
57
59
  if ((_a = instance.getState().loadingItems) === null || _a === void 0 ? void 0 : _a.includes(itemId)) {
58
60
  return;
59
61
  }
60
- (_c = (_b = instance
61
- .getConfig()).setExpandedItems) === null || _c === void 0 ? void 0 : _c.call(_b, (expandedItems) => [...expandedItems, itemId]);
62
+ instance.applySubStateUpdate("expandedItems", (expandedItems) => [
63
+ ...expandedItems,
64
+ itemId,
65
+ ]);
62
66
  instance.rebuildTree();
63
67
  }, collapseItem: (itemId) => {
64
- var _a, _b;
65
68
  if (!instance.getItemInstance(itemId).isFolder()) {
66
69
  return;
67
70
  }
68
- (_b = (_a = instance
69
- .getConfig()).setExpandedItems) === null || _b === void 0 ? void 0 : _b.call(_a, (expandedItems) => expandedItems.filter((id) => id !== itemId));
71
+ instance.applySubStateUpdate("expandedItems", (expandedItems) => expandedItems.filter((id) => id !== itemId));
70
72
  instance.rebuildTree();
71
73
  },
72
74
  // TODO memo
@@ -74,8 +76,7 @@ exports.treeFeature = {
74
76
  var _a, _b;
75
77
  return ((_b = instance.getItemInstance((_a = instance.getState().focusedItem) !== null && _a !== void 0 ? _a : "")) !== null && _b !== void 0 ? _b : instance.getItems()[0]);
76
78
  }, focusItem: (itemId) => {
77
- var _a, _b;
78
- (_b = (_a = instance.getConfig()).setFocusedItem) === null || _b === void 0 ? void 0 : _b.call(_a, itemId);
79
+ instance.applySubStateUpdate("focusedItem", itemId);
79
80
  }, focusNextItem: () => {
80
81
  const { index } = instance.getFocusedItem().getItemMeta();
81
82
  const nextIndex = Math.min(index + 1, instance.getItems().length - 1);
@@ -84,7 +85,7 @@ exports.treeFeature = {
84
85
  const { index } = instance.getFocusedItem().getItemMeta();
85
86
  const nextIndex = Math.max(index - 1, 0);
86
87
  instance.focusItem(instance.getItems()[nextIndex].getId());
87
- }, updateDomFocus: (scrollIntoView) => {
88
+ }, updateDomFocus: () => {
88
89
  // Required because if the state is managed outside in react, the state only updated during next render
89
90
  setTimeout(() => __awaiter(void 0, void 0, void 0, function* () {
90
91
  var _a, _b;
@@ -95,9 +96,6 @@ exports.treeFeature = {
95
96
  if (!focusedElement)
96
97
  return;
97
98
  focusedElement.focus();
98
- // if (scrollIntoView) {
99
- // focusedElement.scrollIntoView();
100
- // }
101
99
  }));
102
100
  }, getContainerProps: () => {
103
101
  var _a;
@@ -105,11 +103,15 @@ exports.treeFeature = {
105
103
  } })),
106
104
  createItemInstance: (prev, item, tree) => (Object.assign(Object.assign({}, prev), { isLoading: () => {
107
105
  throw new Error("No data-loader registered");
108
- }, getId: () => item.getItemMeta().itemId, getProps: () => {
106
+ }, scrollTo: (scrollIntoViewArg) => __awaiter(void 0, void 0, void 0, function* () {
107
+ var _a, _b;
108
+ (_b = (_a = tree.getConfig()).scrollToItem) === null || _b === void 0 ? void 0 : _b.call(_a, item);
109
+ yield (0, utils_1.poll)(() => item.getElement() !== null, 20);
110
+ item.getElement().scrollIntoView(scrollIntoViewArg);
111
+ }), getId: () => item.getItemMeta().itemId, getProps: () => {
109
112
  var _a;
110
113
  const itemMeta = item.getItemMeta();
111
- return Object.assign(Object.assign({}, (_a = prev.getProps) === null || _a === void 0 ? void 0 : _a.call(prev)), { role: "treeitem", "aria-setsize": itemMeta.setSize, "aria-posinset": itemMeta.posInSet, "aria-selected": false, "aria-label": item.getItemName(), "aria-level": itemMeta.level, tabIndex: item.isFocused() ? 0 : -1, onClick: item.getMemoizedProp("tree/onClick", () => (e) => {
112
- console.log("onClick", item.getId());
114
+ return Object.assign(Object.assign({}, (_a = prev.getProps) === null || _a === void 0 ? void 0 : _a.call(prev)), { role: "treeitem", "aria-setsize": itemMeta.setSize, "aria-posinset": itemMeta.posInSet, "aria-selected": "false", "aria-label": item.getItemName(), "aria-level": itemMeta.level, tabIndex: item.isFocused() ? 0 : -1, onClick: item.getMemoizedProp("tree/onClick", () => (e) => {
113
115
  item.setFocused();
114
116
  item.primaryAction();
115
117
  if (e.ctrlKey || e.shiftKey || e.metaKey) {
@@ -138,12 +140,9 @@ exports.treeFeature = {
138
140
  }
139
141
  }
140
142
  return tree.getItemInstance(tree.getConfig().rootItemId);
141
- }, () => [item.getItemMeta()]), getIndexInParent: () => {
142
- var _a, _b;
143
- return item.getItemMeta().index -
144
- ((_b = (_a = item.getParent()) === null || _a === void 0 ? void 0 : _a.getItemMeta().index) !== null && _b !== void 0 ? _b : 0) -
145
- 1;
146
- }, getChildren: () => tree
143
+ }, () => [item.getItemMeta()]),
144
+ // TODO remove
145
+ getIndexInParent: () => item.getItemMeta().posInSet, getChildren: () => tree
147
146
  .retrieveChildrenIds(item.getItemMeta().itemId)
148
147
  .map((id) => tree.getItemInstance(id)), getTree: () => tree, getItemAbove: () => tree.getItems()[item.getItemMeta().index - 1], getItemBelow: () => tree.getItems()[item.getItemMeta().index + 1], getMemoizedProp: (name, create, deps) => {
149
148
  var _a, _b, _c, _d, _e;
@@ -206,7 +205,7 @@ exports.treeFeature = {
206
205
  if ((!item.isExpanded() || !item.isFolder()) &&
207
206
  item.getItemMeta().level !== 0) {
208
207
  (_a = item.getParent()) === null || _a === void 0 ? void 0 : _a.setFocused();
209
- tree.updateDomFocus(true);
208
+ tree.updateDomFocus();
210
209
  }
211
210
  else {
212
211
  item.collapse();
@@ -34,8 +34,7 @@ export type TreeFeatureDef<T> = {
34
34
  getFocusedItem: () => ItemInstance<any>;
35
35
  focusNextItem: () => void;
36
36
  focusPreviousItem: () => void;
37
- scrollToItem: (item: ItemInstance<any>) => void;
38
- updateDomFocus: (scrollIntoView?: boolean) => void;
37
+ updateDomFocus: () => void;
39
38
  getContainerProps: () => Record<string, any>;
40
39
  };
41
40
  itemInstance: {
@@ -57,6 +56,7 @@ export type TreeFeatureDef<T> = {
57
56
  getItemAbove: () => ItemInstance<T> | null;
58
57
  getItemBelow: () => ItemInstance<T> | null;
59
58
  getMemoizedProp: <X>(name: string, create: () => X, deps?: any[]) => X;
59
+ scrollTo: (scrollIntoViewArg?: boolean | ScrollIntoViewOptions) => Promise<void>;
60
60
  };
61
61
  hotkeys: "focusNextItem" | "focusPreviousItem" | "expandOrDown" | "collapseOrUp" | "focusFirstItem" | "focusLastItem";
62
62
  };
@@ -18,4 +18,6 @@ export * from "./features/drag-and-drop/feature";
18
18
  export * from "./features/search/feature";
19
19
  export * from "./features/renaming/feature";
20
20
  export * from "./features/expand-all/feature";
21
- export * from "./data-adapters/nested-data-adapter";
21
+ export * from "./utilities/create-on-drop-handler";
22
+ export * from "./utilities/insert-items-at-target";
23
+ export * from "./utilities/remove-items-from-parents";
package/lib/cjs/index.js CHANGED
@@ -34,4 +34,6 @@ __exportStar(require("./features/drag-and-drop/feature"), exports);
34
34
  __exportStar(require("./features/search/feature"), exports);
35
35
  __exportStar(require("./features/renaming/feature"), exports);
36
36
  __exportStar(require("./features/expand-all/feature"), exports);
37
- __exportStar(require("./data-adapters/nested-data-adapter"), exports);
37
+ __exportStar(require("./utilities/create-on-drop-handler"), exports);
38
+ __exportStar(require("./utilities/insert-items-at-target"), exports);
39
+ __exportStar(require("./utilities/remove-items-from-parents"), exports);
@@ -51,7 +51,9 @@ export type HotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Record<Hot
51
51
  export type CustomHotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Partial<Record<HotkeyName<F> | `custom${string}`, Partial<HotkeyConfig<T>>>>;
52
52
  export type FeatureImplementation<T = any, D extends FeatureDef = any, F extends FeatureDef = EmptyFeatureDef> = {
53
53
  key?: string;
54
- dependingFeatures?: string[];
54
+ deps?: string[];
55
+ overwrites?: string[];
56
+ stateHandlerNames?: Partial<Record<keyof MergedFeatures<F>["state"], keyof MergedFeatures<F>["config"]>>;
55
57
  getInitialState?: (initialState: Partial<MergedFeatures<F>["state"]>, tree: MergedFeatures<F>["treeInstance"]) => Partial<D["state"] & MergedFeatures<F>["state"]>;
56
58
  getDefaultConfig?: (defaultConfig: Partial<MergedFeatures<F>["config"]>, tree: MergedFeatures<F>["treeInstance"]) => Partial<D["config"] & MergedFeatures<F>["config"]>;
57
59
  createTreeInstance?: (prev: MergedFeatures<F>["treeInstance"], instance: MergedFeatures<F>["treeInstance"]) => D["treeInstance"] & MergedFeatures<F>["treeInstance"];
@@ -60,9 +62,6 @@ export type FeatureImplementation<T = any, D extends FeatureDef = any, F extends
60
62
  onTreeUnmount?: (instance: MergedFeatures<F>["treeInstance"], treeElement: HTMLElement) => void;
61
63
  onItemMount?: (instance: MergedFeatures<F>["itemInstance"], itemElement: HTMLElement, tree: MergedFeatures<F>["treeInstance"]) => void;
62
64
  onItemUnmount?: (instance: MergedFeatures<F>["itemInstance"], itemElement: HTMLElement, tree: MergedFeatures<F>["treeInstance"]) => void;
63
- setState?: (instance: MergedFeatures<F>["treeInstance"]) => void;
64
- onConfigChange?: (instance: MergedFeatures<F>["treeInstance"]) => void;
65
- onStateOrConfigChange?: (instance: MergedFeatures<F>["treeInstance"]) => void;
66
65
  hotkeys?: HotkeysConfig<T, D>;
67
66
  };
68
67
  export {};
@@ -0,0 +1,3 @@
1
+ import { ItemInstance } from "../types/core";
2
+ import { DropTarget } from "../features/drag-and-drop/types";
3
+ export declare const createOnDropHandler: <T>(onChangeChildren: (item: ItemInstance<T>, newChildren: string[]) => void) => (items: ItemInstance<T>[], target: DropTarget<T>) => void;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createOnDropHandler = void 0;
4
+ const remove_items_from_parents_1 = require("./remove-items-from-parents");
5
+ const insert_items_at_target_1 = require("./insert-items-at-target");
6
+ const createOnDropHandler = (onChangeChildren) => (items, target) => {
7
+ const itemIds = items.map((item) => item.getId());
8
+ (0, remove_items_from_parents_1.removeItemsFromParents)(items, onChangeChildren);
9
+ (0, insert_items_at_target_1.insertItemsAtTarget)(itemIds, target, onChangeChildren);
10
+ };
11
+ exports.createOnDropHandler = createOnDropHandler;
@@ -0,0 +1,3 @@
1
+ import { ItemInstance } from "../types/core";
2
+ import { DropTarget } from "../features/drag-and-drop/types";
3
+ export declare const insertItemsAtTarget: <T>(itemIds: string[], target: DropTarget<T>, onChangeChildren: (item: ItemInstance<T>, newChildrenIds: string[]) => void) => void;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.insertItemsAtTarget = void 0;
4
+ const insertItemsAtTarget = (itemIds, target, onChangeChildren) => {
5
+ // add moved items to new common parent, if dropped onto parent
6
+ if (target.childIndex === null) {
7
+ onChangeChildren(target.item, [
8
+ ...target.item.getChildren().map((item) => item.getId()),
9
+ ...itemIds,
10
+ ]);
11
+ // TODO items[0].getTree().rebuildTree();
12
+ return;
13
+ }
14
+ // add moved items to new common parent, if dropped between siblings
15
+ const oldChildren = target.item.getChildren();
16
+ const newChildren = [
17
+ ...oldChildren.slice(0, target.insertionIndex).map((item) => item.getId()),
18
+ ...itemIds,
19
+ ...oldChildren.slice(target.insertionIndex).map((item) => item.getId()),
20
+ ];
21
+ onChangeChildren(target.item, newChildren);
22
+ target.item.getTree().rebuildTree();
23
+ };
24
+ exports.insertItemsAtTarget = insertItemsAtTarget;
@@ -0,0 +1,2 @@
1
+ import { ItemInstance } from "../types/core";
2
+ export declare const removeItemsFromParents: <T>(movedItems: ItemInstance<T>[], onChangeChildren: (item: ItemInstance<T>, newChildrenIds: string[]) => void) => void;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.removeItemsFromParents = void 0;
4
+ const removeItemsFromParents = (movedItems, onChangeChildren) => {
5
+ var _a;
6
+ // TODO bulk sibling changes together
7
+ for (const item of movedItems) {
8
+ const siblings = (_a = item.getParent()) === null || _a === void 0 ? void 0 : _a.getChildren();
9
+ if (siblings) {
10
+ onChangeChildren(item.getParent(), siblings
11
+ .filter((sibling) => sibling.getId() !== item.getId())
12
+ .map((i) => i.getId()));
13
+ }
14
+ }
15
+ movedItems[0].getTree().rebuildTree();
16
+ };
17
+ exports.removeItemsFromParents = removeItemsFromParents;
@@ -1,9 +1,6 @@
1
- import { ItemInstance, TreeState, Updater } from "./types/core";
2
- import { DropTarget } from "./features/drag-and-drop/types";
1
+ import { TreeState, Updater } from "./types/core";
3
2
  export type NoInfer<T> = [T][T extends any ? 0 : never];
4
3
  export declare const memo: <D extends readonly any[], R>(fn: (...args_0: D) => R, deps: () => [...D]) => () => R;
5
4
  export declare function functionalUpdate<T>(updater: Updater<T>, input: T): T;
6
5
  export declare function makeStateUpdater<K extends keyof TreeState<any>>(key: K, instance: unknown): (updater: Updater<TreeState<any>[K]>) => void;
7
- export declare const scrollIntoView: (element: Element | undefined | null) => void;
8
- export declare const performItemsMove: <T>(items: ItemInstance<T>[], target: DropTarget<T>, onChangeChildren: (item: ItemInstance<T>, newChildren: ItemInstance<T>[]) => void) => void;
9
6
  export declare const poll: (fn: () => boolean, interval?: number, timeout?: number) => Promise<void>;
package/lib/cjs/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.poll = exports.performItemsMove = exports.scrollIntoView = exports.makeStateUpdater = exports.functionalUpdate = exports.memo = void 0;
3
+ exports.poll = exports.makeStateUpdater = exports.functionalUpdate = exports.memo = void 0;
4
4
  const memo = (fn, deps) => {
5
5
  let value;
6
6
  let oldDeps = null;
@@ -37,58 +37,6 @@ function makeStateUpdater(key, instance) {
37
37
  };
38
38
  }
39
39
  exports.makeStateUpdater = makeStateUpdater;
40
- const scrollIntoView = (element) => {
41
- if (!element) {
42
- return;
43
- }
44
- if (element.scrollIntoViewIfNeeded) {
45
- element.scrollIntoViewIfNeeded();
46
- }
47
- else {
48
- const boundingBox = element.getBoundingClientRect();
49
- const isElementInViewport = boundingBox.top >= 0 &&
50
- boundingBox.left >= 0 &&
51
- boundingBox.bottom <=
52
- (window.innerHeight || document.documentElement.clientHeight) &&
53
- boundingBox.right <=
54
- (window.innerWidth || document.documentElement.clientWidth);
55
- if (!isElementInViewport) {
56
- element.scrollIntoView();
57
- }
58
- }
59
- };
60
- exports.scrollIntoView = scrollIntoView;
61
- const performItemsMove = (items, target, onChangeChildren) => {
62
- var _a;
63
- const numberOfDragItemsBeforeTarget = !target.childIndex
64
- ? 0
65
- : target.item
66
- .getChildren()
67
- .slice(0, target.childIndex)
68
- .filter((child) => items.some((item) => item.getId() === child.getId()))
69
- .length;
70
- // TODO bulk sibling changes together
71
- for (const item of items) {
72
- const siblings = (_a = item.getParent()) === null || _a === void 0 ? void 0 : _a.getChildren();
73
- if (siblings) {
74
- onChangeChildren(item.getParent(), siblings.filter((sibling) => sibling.getId() !== item.getId()));
75
- }
76
- }
77
- if (target.childIndex === null) {
78
- onChangeChildren(target.item, [...target.item.getChildren(), ...items]);
79
- items[0].getTree().rebuildTree();
80
- return;
81
- }
82
- const oldChildren = target.item.getChildren();
83
- const newChildren = [
84
- ...oldChildren.slice(0, target.childIndex - numberOfDragItemsBeforeTarget),
85
- ...items,
86
- ...oldChildren.slice(target.childIndex - numberOfDragItemsBeforeTarget),
87
- ];
88
- onChangeChildren(target.item, newChildren);
89
- items[0].getTree().rebuildTree();
90
- };
91
- exports.performItemsMove = performItemsMove;
92
40
  const poll = (fn, interval = 100, timeout = 1000) => new Promise((resolve) => {
93
41
  let clear;
94
42
  const i = setInterval(() => {
@@ -7,12 +7,35 @@ const buildItemInstance = (features, tree, itemId) => {
7
7
  }
8
8
  return itemInstance;
9
9
  };
10
+ const verifyFeatures = (features) => {
11
+ var _a;
12
+ const loadedFeatures = features === null || features === void 0 ? void 0 : features.map((feature) => feature.key);
13
+ for (const feature of features !== null && features !== void 0 ? features : []) {
14
+ const missingDependency = (_a = feature.deps) === null || _a === void 0 ? void 0 : _a.find((dep) => !(loadedFeatures === null || loadedFeatures === void 0 ? void 0 : loadedFeatures.includes(dep)));
15
+ if (missingDependency) {
16
+ throw new Error(`${feature.key} needs ${missingDependency}`);
17
+ }
18
+ }
19
+ };
20
+ const compareFeatures = (feature1, feature2) => {
21
+ var _a;
22
+ if (feature2.key && ((_a = feature1.overwrites) === null || _a === void 0 ? void 0 : _a.includes(feature2.key))) {
23
+ return 1;
24
+ }
25
+ return -1;
26
+ };
27
+ const sortFeatures = (features = []) => features.sort(compareFeatures);
10
28
  export const createTree = (initialConfig) => {
11
- var _a, _b, _c, _d, _e, _f;
29
+ var _a, _b, _c, _d, _e;
12
30
  const treeInstance = {};
13
- const additionalFeatures = [treeFeature, ...((_a = initialConfig.features) !== null && _a !== void 0 ? _a : [])];
14
- let state = additionalFeatures.reduce((acc, feature) => { var _a, _b; return (_b = (_a = feature.getInitialState) === null || _a === void 0 ? void 0 : _a.call(feature, acc, treeInstance)) !== null && _b !== void 0 ? _b : acc; }, (_c = (_b = initialConfig.initialState) !== null && _b !== void 0 ? _b : initialConfig.state) !== null && _c !== void 0 ? _c : {});
31
+ const additionalFeatures = [
32
+ treeFeature,
33
+ ...sortFeatures(initialConfig.features),
34
+ ];
35
+ verifyFeatures(additionalFeatures);
36
+ let state = additionalFeatures.reduce((acc, feature) => { var _a, _b; return (_b = (_a = feature.getInitialState) === null || _a === void 0 ? void 0 : _a.call(feature, acc, treeInstance)) !== null && _b !== void 0 ? _b : acc; }, (_b = (_a = initialConfig.initialState) !== null && _a !== void 0 ? _a : initialConfig.state) !== null && _b !== void 0 ? _b : {});
15
37
  let config = additionalFeatures.reduce((acc, feature) => { var _a, _b; return (_b = (_a = feature.getDefaultConfig) === null || _a === void 0 ? void 0 : _a.call(feature, acc, treeInstance)) !== null && _b !== void 0 ? _b : acc; }, initialConfig);
38
+ const stateHandlerNames = additionalFeatures.reduce((acc, feature) => (Object.assign(Object.assign({}, acc), feature.stateHandlerNames)), {});
16
39
  let treeElement;
17
40
  const treeDataRef = { current: {} };
18
41
  const itemInstancesMap = {};
@@ -57,10 +80,13 @@ export const createTree = (initialConfig) => {
57
80
  key: "main",
58
81
  createTreeInstance: (prev) => (Object.assign(Object.assign({}, prev), { getState: () => state, setState: (updater) => {
59
82
  var _a;
60
- state = typeof updater === "function" ? updater(state) : updater;
83
+ // Not necessary, since I think the subupdate below keeps the state fresh anyways?
84
+ // state = typeof updater === "function" ? updater(state) : updater;
61
85
  (_a = config.setState) === null || _a === void 0 ? void 0 : _a.call(config, state);
62
- eachFeature((feature) => { var _a; return (_a = feature.setState) === null || _a === void 0 ? void 0 : _a.call(feature, treeInstance); });
63
- eachFeature((feature) => { var _a; return (_a = feature.onStateOrConfigChange) === null || _a === void 0 ? void 0 : _a.call(feature, treeInstance); });
86
+ }, applySubStateUpdate: (stateName, updater) => {
87
+ state[stateName] =
88
+ typeof updater === "function" ? updater(state[stateName]) : updater;
89
+ config[stateHandlerNames[stateName]](state[stateName]);
64
90
  }, rebuildTree: () => {
65
91
  var _a;
66
92
  rebuildItemMeta(mainFeature);
@@ -69,10 +95,7 @@ export const createTree = (initialConfig) => {
69
95
  config = typeof updater === "function" ? updater(config) : updater;
70
96
  if (config.state) {
71
97
  state = Object.assign(Object.assign({}, state), config.state);
72
- eachFeature((feature) => { var _a; return (_a = feature.setState) === null || _a === void 0 ? void 0 : _a.call(feature, treeInstance); });
73
98
  }
74
- eachFeature((feature) => { var _a; return (_a = feature.onConfigChange) === null || _a === void 0 ? void 0 : _a.call(feature, treeInstance); });
75
- eachFeature((feature) => { var _a; return (_a = feature.onStateOrConfigChange) === null || _a === void 0 ? void 0 : _a.call(feature, treeInstance); });
76
99
  }, getItemInstance: (itemId) => itemInstancesMap[itemId], getItems: () => itemInstances, registerElement: (element) => {
77
100
  if (treeElement === element) {
78
101
  return;
@@ -101,11 +124,10 @@ export const createTree = (initialConfig) => {
101
124
  // eslint-disable-next-line no-return-assign
102
125
  getDataRef: () => { var _a; return ((_a = itemDataRefs[itemId]) !== null && _a !== void 0 ? _a : (itemDataRefs[itemId] = { current: {} })); }, getItemMeta: () => itemMetaMap[itemId] })),
103
126
  };
104
- // todo sort features
105
127
  const features = [mainFeature, ...additionalFeatures];
106
128
  for (const feature of features) {
107
- Object.assign(treeInstance, (_e = (_d = feature.createTreeInstance) === null || _d === void 0 ? void 0 : _d.call(feature, Object.assign({}, treeInstance), treeInstance)) !== null && _e !== void 0 ? _e : {});
108
- Object.assign(hotkeyPresets, (_f = feature.hotkeys) !== null && _f !== void 0 ? _f : {});
129
+ Object.assign(treeInstance, (_d = (_c = feature.createTreeInstance) === null || _c === void 0 ? void 0 : _c.call(feature, Object.assign({}, treeInstance), treeInstance)) !== null && _d !== void 0 ? _d : {});
130
+ Object.assign(hotkeyPresets, (_e = feature.hotkeys) !== null && _e !== void 0 ? _e : {});
109
131
  }
110
132
  rebuildItemMeta(mainFeature);
111
133
  return treeInstance;