@headless-tree/core 0.0.14 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/lib/cjs/core/create-tree.js +13 -4
  3. package/lib/cjs/features/async-data-loader/feature.js +73 -48
  4. package/lib/cjs/features/async-data-loader/types.d.ts +17 -14
  5. package/lib/cjs/features/drag-and-drop/feature.js +98 -93
  6. package/lib/cjs/features/drag-and-drop/types.d.ts +17 -29
  7. package/lib/cjs/features/drag-and-drop/types.js +7 -7
  8. package/lib/cjs/features/drag-and-drop/utils.d.ts +25 -3
  9. package/lib/cjs/features/drag-and-drop/utils.js +51 -51
  10. package/lib/cjs/features/expand-all/feature.js +26 -3
  11. package/lib/cjs/features/expand-all/types.d.ts +3 -1
  12. package/lib/cjs/features/hotkeys-core/feature.js +7 -3
  13. package/lib/cjs/features/hotkeys-core/types.d.ts +4 -5
  14. package/lib/cjs/features/keyboard-drag-and-drop/feature.d.ts +2 -0
  15. package/lib/cjs/features/keyboard-drag-and-drop/feature.js +206 -0
  16. package/lib/cjs/features/keyboard-drag-and-drop/types.d.ts +27 -0
  17. package/lib/cjs/features/keyboard-drag-and-drop/types.js +11 -0
  18. package/lib/cjs/features/prop-memoization/feature.js +33 -11
  19. package/lib/cjs/features/prop-memoization/types.d.ts +8 -3
  20. package/lib/cjs/features/renaming/feature.js +1 -1
  21. package/lib/cjs/features/search/feature.js +2 -0
  22. package/lib/cjs/features/search/types.d.ts +2 -2
  23. package/lib/cjs/features/selection/feature.js +4 -4
  24. package/lib/cjs/features/selection/types.d.ts +1 -1
  25. package/lib/cjs/features/sync-data-loader/feature.js +31 -5
  26. package/lib/cjs/features/sync-data-loader/types.d.ts +5 -5
  27. package/lib/cjs/features/tree/feature.js +4 -9
  28. package/lib/cjs/features/tree/types.d.ts +7 -5
  29. package/lib/cjs/index.d.ts +2 -0
  30. package/lib/cjs/index.js +2 -0
  31. package/lib/cjs/mddocs-entry.d.ts +10 -0
  32. package/lib/cjs/test-utils/test-tree-do.d.ts +2 -2
  33. package/lib/cjs/test-utils/test-tree-do.js +19 -6
  34. package/lib/cjs/test-utils/test-tree-expect.d.ts +5 -3
  35. package/lib/cjs/test-utils/test-tree-expect.js +3 -0
  36. package/lib/cjs/test-utils/test-tree.d.ts +2 -1
  37. package/lib/cjs/test-utils/test-tree.js +24 -21
  38. package/lib/cjs/types/core.d.ts +2 -1
  39. package/lib/cjs/utilities/create-on-drop-handler.d.ts +2 -2
  40. package/lib/cjs/utilities/create-on-drop-handler.js +13 -4
  41. package/lib/cjs/utilities/insert-items-at-target.d.ts +2 -2
  42. package/lib/cjs/utilities/insert-items-at-target.js +21 -12
  43. package/lib/cjs/utilities/remove-items-from-parents.d.ts +1 -1
  44. package/lib/cjs/utilities/remove-items-from-parents.js +12 -3
  45. package/lib/esm/core/create-tree.js +13 -4
  46. package/lib/esm/features/async-data-loader/feature.js +73 -48
  47. package/lib/esm/features/async-data-loader/types.d.ts +17 -14
  48. package/lib/esm/features/drag-and-drop/feature.js +99 -94
  49. package/lib/esm/features/drag-and-drop/types.d.ts +17 -29
  50. package/lib/esm/features/drag-and-drop/types.js +6 -6
  51. package/lib/esm/features/drag-and-drop/utils.d.ts +25 -3
  52. package/lib/esm/features/drag-and-drop/utils.js +45 -49
  53. package/lib/esm/features/expand-all/feature.js +26 -3
  54. package/lib/esm/features/expand-all/types.d.ts +3 -1
  55. package/lib/esm/features/hotkeys-core/feature.js +7 -3
  56. package/lib/esm/features/hotkeys-core/types.d.ts +4 -5
  57. package/lib/esm/features/keyboard-drag-and-drop/feature.d.ts +2 -0
  58. package/lib/esm/features/keyboard-drag-and-drop/feature.js +203 -0
  59. package/lib/esm/features/keyboard-drag-and-drop/types.d.ts +27 -0
  60. package/lib/esm/features/keyboard-drag-and-drop/types.js +8 -0
  61. package/lib/esm/features/prop-memoization/feature.js +33 -11
  62. package/lib/esm/features/prop-memoization/types.d.ts +8 -3
  63. package/lib/esm/features/renaming/feature.js +1 -1
  64. package/lib/esm/features/search/feature.js +2 -0
  65. package/lib/esm/features/search/types.d.ts +2 -2
  66. package/lib/esm/features/selection/feature.js +4 -4
  67. package/lib/esm/features/selection/types.d.ts +1 -1
  68. package/lib/esm/features/sync-data-loader/feature.js +31 -5
  69. package/lib/esm/features/sync-data-loader/types.d.ts +5 -5
  70. package/lib/esm/features/tree/feature.js +4 -9
  71. package/lib/esm/features/tree/types.d.ts +7 -5
  72. package/lib/esm/index.d.ts +2 -0
  73. package/lib/esm/index.js +2 -0
  74. package/lib/esm/mddocs-entry.d.ts +10 -0
  75. package/lib/esm/test-utils/test-tree-do.d.ts +2 -2
  76. package/lib/esm/test-utils/test-tree-do.js +19 -6
  77. package/lib/esm/test-utils/test-tree-expect.d.ts +5 -3
  78. package/lib/esm/test-utils/test-tree-expect.js +3 -0
  79. package/lib/esm/test-utils/test-tree.d.ts +2 -1
  80. package/lib/esm/test-utils/test-tree.js +24 -21
  81. package/lib/esm/types/core.d.ts +2 -1
  82. package/lib/esm/utilities/create-on-drop-handler.d.ts +2 -2
  83. package/lib/esm/utilities/create-on-drop-handler.js +13 -4
  84. package/lib/esm/utilities/insert-items-at-target.d.ts +2 -2
  85. package/lib/esm/utilities/insert-items-at-target.js +21 -12
  86. package/lib/esm/utilities/remove-items-from-parents.d.ts +1 -1
  87. package/lib/esm/utilities/remove-items-from-parents.js +12 -3
  88. package/package.json +2 -2
  89. package/src/core/core.spec.ts +31 -0
  90. package/src/core/create-tree.ts +15 -5
  91. package/src/features/async-data-loader/async-data-loader.spec.ts +10 -6
  92. package/src/features/async-data-loader/feature.ts +76 -48
  93. package/src/features/async-data-loader/types.ts +18 -11
  94. package/src/features/drag-and-drop/drag-and-drop.spec.ts +75 -89
  95. package/src/features/drag-and-drop/feature.ts +26 -22
  96. package/src/features/drag-and-drop/types.ts +23 -35
  97. package/src/features/drag-and-drop/utils.ts +70 -57
  98. package/src/features/expand-all/feature.ts +29 -5
  99. package/src/features/expand-all/types.ts +3 -1
  100. package/src/features/hotkeys-core/feature.ts +4 -0
  101. package/src/features/hotkeys-core/types.ts +4 -13
  102. package/src/features/keyboard-drag-and-drop/feature.ts +255 -0
  103. package/src/features/keyboard-drag-and-drop/keyboard-drag-and-drop.spec.ts +402 -0
  104. package/src/features/keyboard-drag-and-drop/types.ts +30 -0
  105. package/src/features/prop-memoization/feature.ts +27 -8
  106. package/src/features/prop-memoization/prop-memoization.spec.ts +2 -2
  107. package/src/features/prop-memoization/types.ts +8 -3
  108. package/src/features/renaming/feature.ts +8 -2
  109. package/src/features/search/feature.ts +2 -0
  110. package/src/features/search/types.ts +2 -2
  111. package/src/features/selection/feature.ts +4 -4
  112. package/src/features/selection/types.ts +1 -1
  113. package/src/features/sync-data-loader/feature.ts +26 -7
  114. package/src/features/sync-data-loader/types.ts +5 -5
  115. package/src/features/tree/feature.ts +8 -13
  116. package/src/features/tree/types.ts +7 -5
  117. package/src/index.ts +2 -0
  118. package/src/mddocs-entry.ts +16 -0
  119. package/src/test-utils/test-tree-do.ts +3 -3
  120. package/src/test-utils/test-tree-expect.ts +7 -2
  121. package/src/test-utils/test-tree.ts +26 -22
  122. package/src/types/core.ts +2 -0
  123. package/src/utilities/create-on-drop-handler.ts +4 -4
  124. package/src/utilities/insert-items-at-target.ts +18 -14
  125. package/src/utilities/remove-items-from-parents.ts +6 -3
@@ -1,16 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.propMemoizationFeature = void 0;
4
- const memoize = (props, dataRef) => {
5
- var _a;
6
- (_a = dataRef.memoizedProps) !== null && _a !== void 0 ? _a : (dataRef.memoizedProps = {});
4
+ const memoize = (props, memoizedProps) => {
7
5
  for (const key in props) {
8
6
  if (typeof props[key] === "function") {
9
- if (key in dataRef.memoizedProps) {
10
- props[key] = dataRef.memoizedProps[key];
7
+ if (memoizedProps && key in memoizedProps) {
8
+ props[key] = memoizedProps[key];
11
9
  }
12
10
  else {
13
- dataRef.memoizedProps[key] = props[key];
11
+ memoizedProps[key] = props[key];
14
12
  }
15
13
  }
16
14
  }
@@ -30,19 +28,43 @@ exports.propMemoizationFeature = {
30
28
  "selection",
31
29
  ],
32
30
  treeInstance: {
33
- getContainerProps: ({ tree, prev }) => {
34
- var _a;
31
+ getContainerProps: ({ tree, prev }, treeLabel) => {
32
+ var _a, _b, _c;
33
+ var _d, _e;
34
+ const dataRef = tree.getDataRef();
35
+ const props = (_a = prev === null || prev === void 0 ? void 0 : prev(treeLabel)) !== null && _a !== void 0 ? _a : {};
36
+ (_b = (_d = dataRef.current).memo) !== null && _b !== void 0 ? _b : (_d.memo = {});
37
+ (_c = (_e = dataRef.current.memo).tree) !== null && _c !== void 0 ? _c : (_e.tree = {});
38
+ return memoize(props, dataRef.current.memo.tree);
39
+ },
40
+ getSearchInputElementProps: ({ tree, prev }) => {
41
+ var _a, _b, _c;
42
+ var _d, _e;
35
43
  const dataRef = tree.getDataRef();
36
44
  const props = (_a = prev === null || prev === void 0 ? void 0 : prev()) !== null && _a !== void 0 ? _a : {};
37
- return memoize(props, dataRef.current);
45
+ (_b = (_d = dataRef.current).memo) !== null && _b !== void 0 ? _b : (_d.memo = {});
46
+ (_c = (_e = dataRef.current.memo).search) !== null && _c !== void 0 ? _c : (_e.search = {});
47
+ return memoize(props, dataRef.current.memo.search);
38
48
  },
39
49
  },
40
50
  itemInstance: {
41
51
  getProps: ({ item, prev }) => {
42
- var _a;
52
+ var _a, _b, _c;
53
+ var _d, _e;
54
+ const dataRef = item.getDataRef();
55
+ const props = (_a = prev === null || prev === void 0 ? void 0 : prev()) !== null && _a !== void 0 ? _a : {};
56
+ (_b = (_d = dataRef.current).memo) !== null && _b !== void 0 ? _b : (_d.memo = {});
57
+ (_c = (_e = dataRef.current.memo).item) !== null && _c !== void 0 ? _c : (_e.item = {});
58
+ return memoize(props, dataRef.current.memo.item);
59
+ },
60
+ getRenameInputProps: ({ item, prev }) => {
61
+ var _a, _b, _c;
62
+ var _d, _e;
43
63
  const dataRef = item.getDataRef();
44
64
  const props = (_a = prev === null || prev === void 0 ? void 0 : prev()) !== null && _a !== void 0 ? _a : {};
45
- return memoize(props, dataRef.current);
65
+ (_b = (_d = dataRef.current).memo) !== null && _b !== void 0 ? _b : (_d.memo = {});
66
+ (_c = (_e = dataRef.current.memo).rename) !== null && _c !== void 0 ? _c : (_e.rename = {});
67
+ return memoize(props, dataRef.current.memo.rename);
46
68
  },
47
69
  },
48
70
  };
@@ -1,6 +1,11 @@
1
- export type PropMemoizationDataRef = {
2
- memoizedProps?: Record<string, any>;
3
- };
1
+ export interface PropMemoizationDataRef {
2
+ memo?: {
3
+ tree?: Record<string, any>;
4
+ item?: Record<string, any>;
5
+ search?: Record<string, any>;
6
+ rename?: Record<string, any>;
7
+ };
8
+ }
4
9
  export type PropMemoizationFeatureDef = {
5
10
  state: {};
6
11
  config: {};
@@ -40,11 +40,11 @@ exports.renamingFeature = {
40
40
  tree.applySubStateUpdate("renamingValue", item.getItemName());
41
41
  },
42
42
  getRenameInputProps: ({ tree }) => ({
43
+ ref: (r) => r === null || r === void 0 ? void 0 : r.focus(),
43
44
  onBlur: () => tree.abortRenaming(),
44
45
  value: tree.getRenamingValue(),
45
46
  onChange: (e) => {
46
47
  var _a;
47
- // TODO custom type with e.target.value
48
48
  tree.applySubStateUpdate("renamingValue", (_a = e.target) === null || _a === void 0 ? void 0 : _a.value);
49
49
  },
50
50
  }),
@@ -43,10 +43,12 @@ exports.searchFeature = {
43
43
  }
44
44
  },
45
45
  getSearchInputElement: ({ tree }) => { var _a; return (_a = tree.getDataRef().current.searchInput) !== null && _a !== void 0 ? _a : null; },
46
+ // TODO memoize with propMemoizationFeature
46
47
  getSearchInputElementProps: ({ tree }) => ({
47
48
  value: tree.getSearchValue(),
48
49
  onChange: (e) => tree.setSearch(e.target.value),
49
50
  onBlur: () => tree.closeSearch(),
51
+ ref: tree.registerSearchInputElement,
50
52
  }),
51
53
  getSearchMatchingItems: (0, utils_1.memo)(({ tree }) => [
52
54
  tree.getSearchValue(),
@@ -1,9 +1,9 @@
1
1
  import { ItemInstance, SetStateFn } from "../../types/core";
2
2
  import { HotkeysCoreDataRef } from "../hotkeys-core/types";
3
- export type SearchFeatureDataRef<T = any> = HotkeysCoreDataRef & {
3
+ export interface SearchFeatureDataRef<T = any> extends HotkeysCoreDataRef {
4
4
  matchingItems: ItemInstance<T>[];
5
5
  searchInput: HTMLInputElement | null;
6
- };
6
+ }
7
7
  export type SearchFeatureDef<T> = {
8
8
  state: {
9
9
  search: string | null;
@@ -13,7 +13,6 @@ exports.selectionFeature = {
13
13
  setSelectedItems: ({ tree }, selectedItems) => {
14
14
  tree.applySubStateUpdate("selectedItems", selectedItems);
15
15
  },
16
- // TODO memo
17
16
  getSelectedItems: ({ tree }) => {
18
17
  return tree.getState().selectedItems.map(tree.getItemInstance);
19
18
  },
@@ -81,9 +80,10 @@ exports.selectionFeature = {
81
80
  // tree.setSelectedItems([tree.getFocusedItem().getId()]);
82
81
  // },
83
82
  // },
84
- toggleSelectItem: {
85
- hotkey: "ctrl+space",
86
- handler: (e, tree) => {
83
+ toggleSelectedItem: {
84
+ hotkey: "Control+Space",
85
+ preventDefault: true,
86
+ handler: (_, tree) => {
87
87
  tree.getFocusedItem().toggleSelect();
88
88
  },
89
89
  },
@@ -17,5 +17,5 @@ export type SelectionFeatureDef<T> = {
17
17
  isSelected: () => boolean;
18
18
  selectUpTo: (ctrl: boolean) => void;
19
19
  };
20
- hotkeys: "toggleSelectItem" | "selectUpwards" | "selectDownwards" | "selectAll";
20
+ hotkeys: "toggleSelectedItem" | "selectUpwards" | "selectDownwards" | "selectAll";
21
21
  };
@@ -1,17 +1,43 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  exports.syncDataLoaderFeature = void 0;
4
13
  const utils_1 = require("../../utils");
14
+ const errors_1 = require("../../utilities/errors");
15
+ const promiseErrorMessage = "sync dataLoader returned promise";
5
16
  exports.syncDataLoaderFeature = {
6
17
  key: "sync-data-loader",
7
- getInitialState: (initialState) => (Object.assign({ loadingItems: [] }, initialState)),
8
- getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setLoadingItems: (0, utils_1.makeStateUpdater)("loadingItems", tree) }, defaultConfig)),
18
+ getInitialState: (initialState) => (Object.assign({ loadingItemData: [], loadingItemChildrens: [] }, initialState)),
19
+ getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setLoadingItemData: (0, utils_1.makeStateUpdater)("loadingItemData", tree), setLoadingItemChildrens: (0, utils_1.makeStateUpdater)("loadingItemChildrens", tree) }, defaultConfig)),
9
20
  stateHandlerNames: {
10
- loadingItems: "setLoadingItems",
21
+ loadingItemData: "setLoadingItemData",
22
+ loadingItemChildrens: "setLoadingItemChildrens",
11
23
  },
12
24
  treeInstance: {
13
- retrieveItemData: ({ tree }, itemId) => tree.getConfig().dataLoader.getItem(itemId),
14
- retrieveChildrenIds: ({ tree }, itemId) => tree.getConfig().dataLoader.getChildren(itemId),
25
+ waitForItemDataLoaded: () => __awaiter(void 0, void 0, void 0, function* () { }),
26
+ waitForItemChildrenLoaded: () => __awaiter(void 0, void 0, void 0, function* () { }),
27
+ retrieveItemData: ({ tree }, itemId) => {
28
+ const data = tree.getConfig().dataLoader.getItem(itemId);
29
+ if (typeof data === "object" && "then" in data) {
30
+ throw (0, errors_1.throwError)(promiseErrorMessage);
31
+ }
32
+ return data;
33
+ },
34
+ retrieveChildrenIds: ({ tree }, itemId) => {
35
+ const data = tree.getConfig().dataLoader.getChildren(itemId);
36
+ if (typeof data === "object" && "then" in data) {
37
+ throw (0, errors_1.throwError)(promiseErrorMessage);
38
+ }
39
+ return data;
40
+ },
15
41
  },
16
42
  itemInstance: {
17
43
  isLoading: () => false,
@@ -1,12 +1,12 @@
1
- export type SyncTreeDataLoader<T> = {
2
- getItem: (itemId: string) => T;
3
- getChildren: (itemId: string) => string[];
4
- };
1
+ export interface TreeDataLoader<T> {
2
+ getItem: (itemId: string) => T | Promise<T>;
3
+ getChildren: (itemId: string) => string[] | Promise<string[]>;
4
+ }
5
5
  export type SyncDataLoaderFeatureDef<T> = {
6
6
  state: {};
7
7
  config: {
8
8
  rootItemId: string;
9
- dataLoader?: SyncTreeDataLoader<T>;
9
+ dataLoader: TreeDataLoader<T>;
10
10
  };
11
11
  treeInstance: {
12
12
  retrieveItemData: (itemId: string) => T;
@@ -24,7 +24,7 @@ exports.treeFeature = {
24
24
  const { rootItemId } = tree.getConfig();
25
25
  const { expandedItems } = tree.getState();
26
26
  const flatItems = [];
27
- const expandedItemsSet = new Set(expandedItems);
27
+ const expandedItemsSet = new Set(expandedItems); // TODO support setting state expandedItems as set instead of array
28
28
  const recursiveAdd = (itemId, parentId, level, setSize, posInSet) => {
29
29
  var _a;
30
30
  flatItems.push({
@@ -36,7 +36,6 @@ exports.treeFeature = {
36
36
  posInSet,
37
37
  });
38
38
  if (expandedItemsSet.has(itemId)) {
39
- // TODO THIS MADE A HUGE DIFFERENCE!
40
39
  const children = (_a = tree.retrieveChildrenIds(itemId)) !== null && _a !== void 0 ? _a : [];
41
40
  let i = 0;
42
41
  for (const childId of children) {
@@ -51,7 +50,6 @@ exports.treeFeature = {
51
50
  }
52
51
  return flatItems;
53
52
  },
54
- // TODO memo
55
53
  getFocusedItem: ({ tree }) => {
56
54
  var _a, _b;
57
55
  return ((_b = tree.getItemInstance((_a = tree.getState().focusedItem) !== null && _a !== void 0 ? _a : "")) !== null && _b !== void 0 ? _b : tree.getItems()[0]);
@@ -81,8 +79,7 @@ exports.treeFeature = {
81
79
  focusedElement.focus();
82
80
  }));
83
81
  },
84
- // TODO add label parameter
85
- getContainerProps: ({ prev, tree }) => (Object.assign(Object.assign({}, prev === null || prev === void 0 ? void 0 : prev()), { role: "tree", "aria-label": "", ref: tree.registerElement })),
82
+ getContainerProps: ({ prev, tree }, treeLabel) => (Object.assign(Object.assign({}, prev === null || prev === void 0 ? void 0 : prev()), { role: "tree", "aria-label": treeLabel !== null && treeLabel !== void 0 ? treeLabel : "", ref: tree.registerElement })),
86
83
  // relevant for hotkeys of this feature
87
84
  isSearchOpen: () => false,
88
85
  },
@@ -118,7 +115,7 @@ exports.treeFeature = {
118
115
  if (!item.isFolder()) {
119
116
  return;
120
117
  }
121
- if ((_a = tree.getState().loadingItems) === null || _a === void 0 ? void 0 : _a.includes(itemId)) {
118
+ if ((_a = tree.getState().loadingItemChildrens) === null || _a === void 0 ? void 0 : _a.includes(itemId)) {
122
119
  return;
123
120
  }
124
121
  tree.applySubStateUpdate("expandedItems", (expandedItems) => [
@@ -157,9 +154,7 @@ exports.treeFeature = {
157
154
  ? tree.getItemInstance(item.getItemMeta().parentId)
158
155
  : undefined,
159
156
  getIndexInParent: ({ item }) => item.getItemMeta().posInSet,
160
- getChildren: ({ tree, item }) => tree
161
- .retrieveChildrenIds(item.getItemMeta().itemId)
162
- .map((id) => tree.getItemInstance(id)),
157
+ getChildren: ({ tree, itemId }) => tree.retrieveChildrenIds(itemId).map((id) => tree.getItemInstance(id)),
163
158
  getTree: ({ tree }) => tree,
164
159
  getItemAbove: ({ tree, item }) => tree.getItems()[item.getItemMeta().index - 1],
165
160
  getItemBelow: ({ tree, item }) => tree.getItems()[item.getItemMeta().index + 1],
@@ -1,16 +1,16 @@
1
1
  import { ItemInstance, SetStateFn, TreeInstance } from "../../types/core";
2
- export type ItemMeta = {
2
+ export interface ItemMeta {
3
3
  itemId: string;
4
4
  parentId: string;
5
5
  level: number;
6
6
  index: number;
7
7
  setSize: number;
8
8
  posInSet: number;
9
- };
10
- export type TreeItemDataRef = {
9
+ }
10
+ export interface TreeItemDataRef {
11
11
  memoizedValues: Record<string, any>;
12
12
  memoizedDeps: Record<string, any[] | undefined>;
13
- };
13
+ }
14
14
  export type TreeFeatureDef<T> = {
15
15
  state: {
16
16
  expandedItems: string[];
@@ -31,7 +31,9 @@ export type TreeFeatureDef<T> = {
31
31
  focusNextItem: () => void;
32
32
  focusPreviousItem: () => void;
33
33
  updateDomFocus: () => void;
34
- getContainerProps: () => Record<string, any>;
34
+ /** Pass to the container rendering the tree children. The `treeLabel` parameter
35
+ * will be passed as `aria-label` parameter, and is recommended to be set. */
36
+ getContainerProps: (treeLabel?: string) => Record<string, any>;
35
37
  };
36
38
  itemInstance: {
37
39
  getId: () => string;
@@ -3,6 +3,7 @@ export * from "./core/create-tree";
3
3
  export * from "./features/tree/types";
4
4
  export { MainFeatureDef, InstanceBuilder } from "./features/main/types";
5
5
  export * from "./features/drag-and-drop/types";
6
+ export * from "./features/keyboard-drag-and-drop/types";
6
7
  export * from "./features/selection/types";
7
8
  export * from "./features/async-data-loader/types";
8
9
  export * from "./features/sync-data-loader/types";
@@ -16,6 +17,7 @@ export * from "./features/hotkeys-core/feature";
16
17
  export * from "./features/async-data-loader/feature";
17
18
  export * from "./features/sync-data-loader/feature";
18
19
  export * from "./features/drag-and-drop/feature";
20
+ export * from "./features/keyboard-drag-and-drop/feature";
19
21
  export * from "./features/search/feature";
20
22
  export * from "./features/renaming/feature";
21
23
  export * from "./features/expand-all/feature";
package/lib/cjs/index.js CHANGED
@@ -18,6 +18,7 @@ __exportStar(require("./types/core"), exports);
18
18
  __exportStar(require("./core/create-tree"), exports);
19
19
  __exportStar(require("./features/tree/types"), exports);
20
20
  __exportStar(require("./features/drag-and-drop/types"), exports);
21
+ __exportStar(require("./features/keyboard-drag-and-drop/types"), exports);
21
22
  __exportStar(require("./features/selection/types"), exports);
22
23
  __exportStar(require("./features/async-data-loader/types"), exports);
23
24
  __exportStar(require("./features/sync-data-loader/types"), exports);
@@ -31,6 +32,7 @@ __exportStar(require("./features/hotkeys-core/feature"), exports);
31
32
  __exportStar(require("./features/async-data-loader/feature"), exports);
32
33
  __exportStar(require("./features/sync-data-loader/feature"), exports);
33
34
  __exportStar(require("./features/drag-and-drop/feature"), exports);
35
+ __exportStar(require("./features/keyboard-drag-and-drop/feature"), exports);
34
36
  __exportStar(require("./features/search/feature"), exports);
35
37
  __exportStar(require("./features/renaming/feature"), exports);
36
38
  __exportStar(require("./features/expand-all/feature"), exports);
@@ -9,6 +9,7 @@ import { SelectionFeatureDef } from "./features/selection/types";
9
9
  import { SyncDataLoaderFeatureDef } from "./features/sync-data-loader/types";
10
10
  import { TreeFeatureDef } from "./features/tree/types";
11
11
  import { PropMemoizationFeatureDef } from "./features/prop-memoization/types";
12
+ import { KeyboardDragAndDropFeatureDef } from "./features/keyboard-drag-and-drop/types";
12
13
  export * from ".";
13
14
  /** @interface */
14
15
  export type AsyncDataLoaderFeatureConfig<T> = AsyncDataLoaderFeatureDef<T>["config"];
@@ -29,6 +30,15 @@ export type DragAndDropFeatureTreeInstance<T> = DragAndDropFeatureDef<T>["treeIn
29
30
  export type DragAndDropFeatureItemInstance<T> = DragAndDropFeatureDef<T>["itemInstance"];
30
31
  export type DragAndDropFeatureHotkeys<T> = DragAndDropFeatureDef<T>["hotkeys"];
31
32
  /** @interface */
33
+ export type KeyboardDragAndDropFeatureConfig<T> = KeyboardDragAndDropFeatureDef<T>["config"];
34
+ /** @interface */
35
+ export type KeyboardDragAndDropFeatureState<T> = KeyboardDragAndDropFeatureDef<T>["state"];
36
+ /** @interface */
37
+ export type KeyboardDragAndDropFeatureTreeInstance<T> = KeyboardDragAndDropFeatureDef<T>["treeInstance"];
38
+ /** @interface */
39
+ export type KeyboardDragAndDropFeatureItemInstance<T> = KeyboardDragAndDropFeatureDef<T>["itemInstance"];
40
+ export type KeyboardDragAndDropFeatureHotkeys<T> = KeyboardDragAndDropFeatureDef<T>["hotkeys"];
41
+ /** @interface */
32
42
  export type ExpandAllFeatureConfig = ExpandAllFeatureDef["config"];
33
43
  /** @interface */
34
44
  export type ExpandAllFeatureState = ExpandAllFeatureDef["state"];
@@ -17,7 +17,7 @@ export declare class TestTreeDo<T> {
17
17
  dragOverNotAllowed(itemId: string, event?: DragEvent): DragEvent<Element>;
18
18
  dragLeave(itemId: string): void;
19
19
  dragEnd(itemId: string, event?: DragEvent): DragEvent<Element>;
20
- drop(itemId: string, event?: DragEvent): DragEvent<Element>;
21
- dragOverAndDrop(itemId: string, event?: DragEvent): DragEvent<Element>;
20
+ drop(itemId: string, event?: DragEvent): Promise<DragEvent<Element>>;
21
+ dragOverAndDrop(itemId: string, event?: DragEvent): Promise<DragEvent<Element>>;
22
22
  private consistentCalls;
23
23
  }
@@ -1,4 +1,13 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  exports.TestTreeDo = void 0;
4
13
  const vitest_1 = require("vitest");
@@ -79,14 +88,18 @@ class TestTreeDo {
79
88
  return e;
80
89
  }
81
90
  drop(itemId, event) {
82
- const e = event !== null && event !== void 0 ? event : test_tree_1.TestTree.dragEvent();
83
- this.itemProps(itemId).onDrop(e);
84
- return e;
91
+ return __awaiter(this, void 0, void 0, function* () {
92
+ const e = event !== null && event !== void 0 ? event : test_tree_1.TestTree.dragEvent();
93
+ yield this.itemProps(itemId).onDrop(e);
94
+ return e;
95
+ });
85
96
  }
86
97
  dragOverAndDrop(itemId, event) {
87
- const e = event !== null && event !== void 0 ? event : test_tree_1.TestTree.dragEvent();
88
- this.dragOver(itemId, e);
89
- return this.drop(itemId, e);
98
+ return __awaiter(this, void 0, void 0, function* () {
99
+ const e = event !== null && event !== void 0 ? event : test_tree_1.TestTree.dragEvent();
100
+ this.dragOver(itemId, e);
101
+ return this.drop(itemId, e);
102
+ });
90
103
  }
91
104
  consistentCalls(fn) {
92
105
  if (!vitest_1.vi.isMockFunction(fn)) {
@@ -1,15 +1,17 @@
1
1
  import { DragEvent } from "react";
2
2
  import { TestTree } from "./test-tree";
3
- import { DropTarget } from "../features/drag-and-drop/types";
3
+ import { DragTarget } from "../features/drag-and-drop/types";
4
+ import { TreeState } from "../types/core";
4
5
  export declare class TestTreeExpect<T> {
5
6
  private tree;
6
- protected itemInstance(itemId: string): import("..").ItemInstance<T>;
7
+ protected itemInstance(itemId: string): import("../types/core").ItemInstance<T>;
7
8
  protected itemProps(itemId: string): Record<string, any>;
8
9
  constructor(tree: TestTree<T>);
9
10
  foldersExpanded(...itemIds: string[]): void;
10
11
  foldersCollapsed(...itemIds: string[]): void;
11
12
  hasChildren(itemId: string, children: string[]): void;
12
- dropped(draggedItems: string[], target: DropTarget<any>): void;
13
+ substate<K extends keyof TreeState<T>>(key: K, value: TreeState<T>[K]): void;
14
+ dropped(draggedItems: string[], target: DragTarget<any>): void;
13
15
  dragOverNotAllowed(itemId: string, event?: DragEvent): DragEvent<Element>;
14
16
  defaultDragLineProps(indent?: number): void;
15
17
  }
@@ -29,6 +29,9 @@ class TestTreeExpect {
29
29
  const itemChildren = item.getChildren().map((child) => child.getId());
30
30
  (0, vitest_1.expect)(itemChildren).toEqual(children);
31
31
  }
32
+ substate(key, value) {
33
+ (0, vitest_1.expect)(this.tree.instance.getState()[key]).toEqual(value);
34
+ }
32
35
  dropped(draggedItems, target) {
33
36
  (0, vitest_1.expect)(this.tree.instance.getConfig().onDrop).toBeCalledWith(draggedItems.map((id) => this.tree.item(id)), target);
34
37
  }
@@ -8,6 +8,7 @@ export declare class TestTree<T = string> {
8
8
  readonly expect: TestTreeExpect<T>;
9
9
  private treeInstance;
10
10
  private static asyncLoaderResolvers;
11
+ private asyncDataLoaderImp;
11
12
  suits: {
12
13
  sync: () => {
13
14
  tree: TestTree<T>;
@@ -41,7 +42,7 @@ export declare class TestTree<T = string> {
41
42
  reset(): void;
42
43
  debug(): void;
43
44
  setElementBoundingBox(itemId: string, bb?: Partial<DOMRect>): void;
44
- static dragEvent(pageX?: number, pageY?: number): DragEvent;
45
+ static dragEvent(clientX?: number, clientY?: number): DragEvent;
45
46
  createTopDragEvent(indent?: number): DragEvent<Element>;
46
47
  createBottomDragEvent(indent?: number): DragEvent<Element>;
47
48
  }
@@ -42,13 +42,31 @@ class TestTree {
42
42
  this.do = new test_tree_do_1.TestTreeDo(this);
43
43
  this.expect = new test_tree_expect_1.TestTreeExpect(this);
44
44
  this.treeInstance = null;
45
+ this.asyncDataLoaderImp = {
46
+ getItem: (id) => __awaiter(this, void 0, void 0, function* () {
47
+ yield new Promise((r) => {
48
+ r.debugName = `Loading getItem ${id}`;
49
+ TestTree.asyncLoaderResolvers.push(r);
50
+ });
51
+ return id;
52
+ }),
53
+ getChildren: (id) => __awaiter(this, void 0, void 0, function* () {
54
+ yield new Promise((r) => {
55
+ r.debugName = `Loading getChildren ${id}`;
56
+ TestTree.asyncLoaderResolvers.push(r);
57
+ });
58
+ return [`${id}1`, `${id}2`, `${id}3`, `${id}4`];
59
+ }),
60
+ };
45
61
  this.suits = {
46
62
  sync: () => ({
47
63
  tree: this.withFeatures(feature_1.syncDataLoaderFeature),
48
64
  title: "Synchronous Data Loader",
49
65
  }),
50
66
  async: () => ({
51
- tree: this.withFeatures(feature_2.asyncDataLoaderFeature),
67
+ tree: this.withFeatures(feature_2.asyncDataLoaderFeature).with({
68
+ dataLoader: this.asyncDataLoaderImp,
69
+ }),
52
70
  title: "Asynchronous Data Loader",
53
71
  }),
54
72
  proxifiedSync: () => ({
@@ -68,12 +86,12 @@ class TestTree {
68
86
  static resolveAsyncLoaders() {
69
87
  return __awaiter(this, void 0, void 0, function* () {
70
88
  var _a;
71
- while (TestTree.asyncLoaderResolvers.length) {
89
+ do {
72
90
  (_a = TestTree.asyncLoaderResolvers.shift()) === null || _a === void 0 ? void 0 : _a();
73
91
  yield new Promise((r) => {
74
92
  setTimeout(r);
75
93
  });
76
- }
94
+ } while (TestTree.asyncLoaderResolvers.length);
77
95
  });
78
96
  }
79
97
  resolveAsyncVisibleItems() {
@@ -88,21 +106,6 @@ class TestTree {
88
106
  return new TestTree(Object.assign({ rootItemId: "x", createLoadingItemData: () => "loading", dataLoader: {
89
107
  getItem: (id) => id,
90
108
  getChildren: (id) => [`${id}1`, `${id}2`, `${id}3`, `${id}4`],
91
- }, asyncDataLoader: {
92
- getItem: (id) => __awaiter(this, void 0, void 0, function* () {
93
- yield new Promise((r) => {
94
- r.debugName = `Loading getItem ${id}`;
95
- TestTree.asyncLoaderResolvers.push(r);
96
- });
97
- return id;
98
- }),
99
- getChildren: (id) => __awaiter(this, void 0, void 0, function* () {
100
- yield new Promise((r) => {
101
- r.debugName = `Loading getChildren ${id}`;
102
- TestTree.asyncLoaderResolvers.push(r);
103
- });
104
- return [`${id}1`, `${id}2`, `${id}3`, `${id}4`];
105
- }),
106
109
  }, getItemName: (item) => item.getItemData(), indent: 20, isItemFolder: (item) => item.getItemMeta().level < 2, initialState: {
107
110
  expandedItems: ["x1", "x11"],
108
111
  }, features: [] }, config));
@@ -179,7 +182,7 @@ class TestTree {
179
182
  getBoundingClientRect: () => bb,
180
183
  });
181
184
  }
182
- static dragEvent(pageX = 1000, pageY = 0) {
185
+ static dragEvent(clientX = 1000, clientY = 0) {
183
186
  return {
184
187
  preventDefault: vitest_1.vi.fn(),
185
188
  stopPropagation: vitest_1.vi.fn(),
@@ -188,8 +191,8 @@ class TestTree {
188
191
  getData: vitest_1.vi.fn(),
189
192
  dropEffect: "unchaged-from-test",
190
193
  },
191
- pageX,
192
- pageY,
194
+ clientX,
195
+ clientY,
193
196
  };
194
197
  }
195
198
  createTopDragEvent(indent = 0) {
@@ -9,6 +9,7 @@ import { SearchFeatureDef } from "../features/search/types";
9
9
  import { RenamingFeatureDef } from "../features/renaming/types";
10
10
  import { ExpandAllFeatureDef } from "../features/expand-all/types";
11
11
  import { PropMemoizationFeatureDef } from "../features/prop-memoization/types";
12
+ import { KeyboardDragAndDropFeatureDef } from "../features/keyboard-drag-and-drop/types";
12
13
  export type Updater<T> = T | ((old: T) => T);
13
14
  export type SetStateFn<T> = (updaterOrValue: Updater<T>) => void;
14
15
  export type FeatureDef = {
@@ -33,7 +34,7 @@ type MergedFeatures<F extends FeatureDef> = {
33
34
  itemInstance: UnionToIntersection<F["itemInstance"]>;
34
35
  hotkeys: F["hotkeys"];
35
36
  };
36
- export type RegisteredFeatures<T> = MainFeatureDef<T> | TreeFeatureDef<T> | SelectionFeatureDef<T> | DragAndDropFeatureDef<T> | HotkeysCoreFeatureDef<T> | SyncDataLoaderFeatureDef<T> | AsyncDataLoaderFeatureDef<T> | SearchFeatureDef<T> | RenamingFeatureDef<T> | ExpandAllFeatureDef | PropMemoizationFeatureDef;
37
+ export type RegisteredFeatures<T> = MainFeatureDef<T> | TreeFeatureDef<T> | SelectionFeatureDef<T> | DragAndDropFeatureDef<T> | KeyboardDragAndDropFeatureDef<T> | HotkeysCoreFeatureDef<T> | SyncDataLoaderFeatureDef<T> | AsyncDataLoaderFeatureDef<T> | SearchFeatureDef<T> | RenamingFeatureDef<T> | ExpandAllFeatureDef | PropMemoizationFeatureDef;
37
38
  type TreeStateType<T> = MergedFeatures<RegisteredFeatures<T>>["state"];
38
39
  export interface TreeState<T> extends TreeStateType<T> {
39
40
  }
@@ -1,3 +1,3 @@
1
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;
2
+ import { DragTarget } from "../features/drag-and-drop/types";
3
+ export declare const createOnDropHandler: <T>(onChangeChildren: (item: ItemInstance<T>, newChildren: string[]) => void) => (items: ItemInstance<T>[], target: DragTarget<T>) => Promise<void>;
@@ -1,11 +1,20 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  exports.createOnDropHandler = void 0;
4
13
  const remove_items_from_parents_1 = require("./remove-items-from-parents");
5
14
  const insert_items_at_target_1 = require("./insert-items-at-target");
6
- const createOnDropHandler = (onChangeChildren) => (items, target) => {
15
+ const createOnDropHandler = (onChangeChildren) => (items, target) => __awaiter(void 0, void 0, void 0, function* () {
7
16
  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
- };
17
+ yield (0, remove_items_from_parents_1.removeItemsFromParents)(items, onChangeChildren);
18
+ yield (0, insert_items_at_target_1.insertItemsAtTarget)(itemIds, target, onChangeChildren);
19
+ });
11
20
  exports.createOnDropHandler = createOnDropHandler;
@@ -1,3 +1,3 @@
1
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;
2
+ import { DragTarget } from "../features/drag-and-drop/types";
3
+ export declare const insertItemsAtTarget: <T>(itemIds: string[], target: DragTarget<T>, onChangeChildren: (item: ItemInstance<T>, newChildrenIds: string[]) => Promise<void> | void) => Promise<void>;