@headless-tree/core 0.0.9 → 0.0.11

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 (114) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/lib/cjs/core/build-proxified-instance.d.ts +2 -0
  3. package/lib/cjs/core/build-proxified-instance.js +58 -0
  4. package/lib/cjs/core/build-static-instance.d.ts +2 -0
  5. package/lib/cjs/core/build-static-instance.js +27 -0
  6. package/lib/cjs/core/create-tree.js +55 -36
  7. package/lib/cjs/features/async-data-loader/feature.js +37 -23
  8. package/lib/cjs/features/async-data-loader/types.d.ts +2 -1
  9. package/lib/cjs/features/drag-and-drop/feature.js +64 -32
  10. package/lib/cjs/features/drag-and-drop/types.d.ts +13 -4
  11. package/lib/cjs/features/drag-and-drop/utils.d.ts +1 -2
  12. package/lib/cjs/features/drag-and-drop/utils.js +140 -37
  13. package/lib/cjs/features/expand-all/feature.js +12 -6
  14. package/lib/cjs/features/main/types.d.ts +8 -2
  15. package/lib/cjs/features/renaming/feature.js +33 -18
  16. package/lib/cjs/features/renaming/types.d.ts +1 -1
  17. package/lib/cjs/features/search/feature.js +38 -24
  18. package/lib/cjs/features/search/types.d.ts +0 -1
  19. package/lib/cjs/features/selection/feature.js +23 -14
  20. package/lib/cjs/features/sync-data-loader/feature.js +7 -2
  21. package/lib/cjs/features/tree/feature.d.ts +2 -1
  22. package/lib/cjs/features/tree/feature.js +85 -63
  23. package/lib/cjs/features/tree/types.d.ts +5 -3
  24. package/lib/cjs/index.d.ts +3 -1
  25. package/lib/cjs/index.js +2 -1
  26. package/lib/cjs/test-utils/test-tree-do.d.ts +23 -0
  27. package/lib/cjs/test-utils/test-tree-do.js +99 -0
  28. package/lib/cjs/test-utils/test-tree-expect.d.ts +15 -0
  29. package/lib/cjs/test-utils/test-tree-expect.js +62 -0
  30. package/lib/cjs/test-utils/test-tree.d.ts +47 -0
  31. package/lib/cjs/test-utils/test-tree.js +195 -0
  32. package/lib/cjs/types/core.d.ts +31 -15
  33. package/lib/cjs/utilities/errors.d.ts +1 -0
  34. package/lib/cjs/utilities/errors.js +5 -0
  35. package/lib/cjs/utilities/insert-items-at-target.js +10 -3
  36. package/lib/cjs/utilities/remove-items-from-parents.js +14 -8
  37. package/lib/cjs/utils.d.ts +3 -3
  38. package/lib/cjs/utils.js +6 -6
  39. package/lib/esm/core/build-proxified-instance.d.ts +2 -0
  40. package/lib/esm/core/build-proxified-instance.js +54 -0
  41. package/lib/esm/core/build-static-instance.d.ts +2 -0
  42. package/lib/esm/core/build-static-instance.js +23 -0
  43. package/lib/esm/core/create-tree.js +55 -36
  44. package/lib/esm/features/async-data-loader/feature.js +37 -23
  45. package/lib/esm/features/async-data-loader/types.d.ts +2 -1
  46. package/lib/esm/features/drag-and-drop/feature.js +64 -32
  47. package/lib/esm/features/drag-and-drop/types.d.ts +13 -4
  48. package/lib/esm/features/drag-and-drop/utils.d.ts +1 -2
  49. package/lib/esm/features/drag-and-drop/utils.js +138 -34
  50. package/lib/esm/features/expand-all/feature.js +12 -6
  51. package/lib/esm/features/main/types.d.ts +8 -2
  52. package/lib/esm/features/renaming/feature.js +33 -18
  53. package/lib/esm/features/renaming/types.d.ts +1 -1
  54. package/lib/esm/features/search/feature.js +38 -24
  55. package/lib/esm/features/search/types.d.ts +0 -1
  56. package/lib/esm/features/selection/feature.js +23 -14
  57. package/lib/esm/features/sync-data-loader/feature.js +7 -2
  58. package/lib/esm/features/tree/feature.d.ts +2 -1
  59. package/lib/esm/features/tree/feature.js +86 -64
  60. package/lib/esm/features/tree/types.d.ts +5 -3
  61. package/lib/esm/index.d.ts +3 -1
  62. package/lib/esm/index.js +2 -1
  63. package/lib/esm/test-utils/test-tree-do.d.ts +23 -0
  64. package/lib/esm/test-utils/test-tree-do.js +95 -0
  65. package/lib/esm/test-utils/test-tree-expect.d.ts +15 -0
  66. package/lib/esm/test-utils/test-tree-expect.js +58 -0
  67. package/lib/esm/test-utils/test-tree.d.ts +47 -0
  68. package/lib/esm/test-utils/test-tree.js +191 -0
  69. package/lib/esm/types/core.d.ts +31 -15
  70. package/lib/esm/utilities/errors.d.ts +1 -0
  71. package/lib/esm/utilities/errors.js +1 -0
  72. package/lib/esm/utilities/insert-items-at-target.js +10 -3
  73. package/lib/esm/utilities/remove-items-from-parents.js +14 -8
  74. package/lib/esm/utils.d.ts +3 -3
  75. package/lib/esm/utils.js +3 -3
  76. package/package.json +7 -3
  77. package/src/core/build-proxified-instance.ts +115 -0
  78. package/src/core/build-static-instance.ts +28 -0
  79. package/src/core/create-tree.ts +60 -62
  80. package/src/features/async-data-loader/async-data-loader.spec.ts +143 -0
  81. package/src/features/async-data-loader/feature.ts +33 -31
  82. package/src/features/async-data-loader/types.ts +3 -1
  83. package/src/features/drag-and-drop/drag-and-drop.spec.ts +716 -0
  84. package/src/features/drag-and-drop/feature.ts +109 -85
  85. package/src/features/drag-and-drop/types.ts +21 -7
  86. package/src/features/drag-and-drop/utils.ts +196 -55
  87. package/src/features/expand-all/expand-all.spec.ts +52 -0
  88. package/src/features/expand-all/feature.ts +8 -12
  89. package/src/features/hotkeys-core/feature.ts +1 -1
  90. package/src/features/main/types.ts +14 -1
  91. package/src/features/renaming/feature.ts +30 -29
  92. package/src/features/renaming/renaming.spec.ts +125 -0
  93. package/src/features/renaming/types.ts +1 -1
  94. package/src/features/search/feature.ts +34 -38
  95. package/src/features/search/search.spec.ts +115 -0
  96. package/src/features/search/types.ts +0 -1
  97. package/src/features/selection/feature.ts +29 -30
  98. package/src/features/selection/selection.spec.ts +220 -0
  99. package/src/features/sync-data-loader/feature.ts +8 -11
  100. package/src/features/tree/feature.ts +82 -87
  101. package/src/features/tree/tree.spec.ts +515 -0
  102. package/src/features/tree/types.ts +5 -3
  103. package/src/index.ts +4 -1
  104. package/src/test-utils/test-tree-do.ts +136 -0
  105. package/src/test-utils/test-tree-expect.ts +86 -0
  106. package/src/test-utils/test-tree.ts +217 -0
  107. package/src/types/core.ts +92 -33
  108. package/src/utilities/errors.ts +2 -0
  109. package/src/utilities/insert-items-at-target.ts +10 -3
  110. package/src/utilities/remove-items-from-parents.ts +15 -10
  111. package/src/utils.spec.ts +89 -0
  112. package/src/utils.ts +6 -6
  113. package/tsconfig.json +1 -0
  114. package/vitest.config.ts +6 -0
@@ -19,14 +19,13 @@ exports.treeFeature = {
19
19
  expandedItems: "setExpandedItems",
20
20
  focusedItem: "setFocusedItem",
21
21
  },
22
- createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { retrieveItemData: () => {
23
- throw new Error("No data-loader registered");
24
- }, retrieveChildrenIds: () => {
25
- throw new Error("No data-loader registered");
26
- }, isItemExpanded: (itemId) => instance.getState().expandedItems.includes(itemId), getItemsMeta: () => {
27
- const { rootItemId } = instance.getConfig();
28
- const { expandedItems } = instance.getState();
22
+ treeInstance: {
23
+ isItemExpanded: ({ tree }, itemId) => tree.getState().expandedItems.includes(itemId),
24
+ getItemsMeta: ({ tree }) => {
25
+ const { rootItemId } = tree.getConfig();
26
+ const { expandedItems } = tree.getState();
29
27
  const flatItems = [];
28
+ const expandedItemsSet = new Set(expandedItems);
30
29
  const recursiveAdd = (itemId, parentId, level, setSize, posInSet) => {
31
30
  var _a;
32
31
  flatItems.push({
@@ -37,81 +36,89 @@ exports.treeFeature = {
37
36
  setSize,
38
37
  posInSet,
39
38
  });
40
- if (expandedItems.includes(itemId)) {
41
- const children = (_a = instance.retrieveChildrenIds(itemId)) !== null && _a !== void 0 ? _a : [];
39
+ if (expandedItemsSet.has(itemId)) {
40
+ // TODO THIS MADE A HUGE DIFFERENCE!
41
+ const children = (_a = tree.retrieveChildrenIds(itemId)) !== null && _a !== void 0 ? _a : [];
42
42
  let i = 0;
43
43
  for (const childId of children) {
44
44
  recursiveAdd(childId, itemId, level + 1, children.length, i++);
45
45
  }
46
46
  }
47
47
  };
48
- const children = instance.retrieveChildrenIds(rootItemId);
48
+ const children = tree.retrieveChildrenIds(rootItemId);
49
49
  let i = 0;
50
50
  for (const itemId of children) {
51
51
  recursiveAdd(itemId, rootItemId, 0, children.length, i++);
52
52
  }
53
53
  return flatItems;
54
- }, expandItem: (itemId) => {
54
+ },
55
+ expandItem: ({ tree }, itemId) => {
55
56
  var _a;
56
- if (!instance.getItemInstance(itemId).isFolder()) {
57
+ if (!tree.getItemInstance(itemId).isFolder()) {
57
58
  return;
58
59
  }
59
- if ((_a = instance.getState().loadingItems) === null || _a === void 0 ? void 0 : _a.includes(itemId)) {
60
+ if ((_a = tree.getState().loadingItems) === null || _a === void 0 ? void 0 : _a.includes(itemId)) {
60
61
  return;
61
62
  }
62
- instance.applySubStateUpdate("expandedItems", (expandedItems) => [
63
+ tree.applySubStateUpdate("expandedItems", (expandedItems) => [
63
64
  ...expandedItems,
64
65
  itemId,
65
66
  ]);
66
- instance.rebuildTree();
67
- }, collapseItem: (itemId) => {
68
- if (!instance.getItemInstance(itemId).isFolder()) {
67
+ tree.rebuildTree();
68
+ },
69
+ collapseItem: ({ tree }, itemId) => {
70
+ if (!tree.getItemInstance(itemId).isFolder()) {
69
71
  return;
70
72
  }
71
- instance.applySubStateUpdate("expandedItems", (expandedItems) => expandedItems.filter((id) => id !== itemId));
72
- instance.rebuildTree();
73
- },
73
+ tree.applySubStateUpdate("expandedItems", (expandedItems) => expandedItems.filter((id) => id !== itemId));
74
+ tree.rebuildTree();
75
+ },
74
76
  // TODO memo
75
- getFocusedItem: () => {
77
+ getFocusedItem: ({ tree }) => {
76
78
  var _a, _b;
77
- return ((_b = instance.getItemInstance((_a = instance.getState().focusedItem) !== null && _a !== void 0 ? _a : "")) !== null && _b !== void 0 ? _b : instance.getItems()[0]);
78
- }, focusItem: (itemId) => {
79
- instance.applySubStateUpdate("focusedItem", itemId);
80
- }, focusNextItem: () => {
81
- const { index } = instance.getFocusedItem().getItemMeta();
82
- const nextIndex = Math.min(index + 1, instance.getItems().length - 1);
83
- instance.focusItem(instance.getItems()[nextIndex].getId());
84
- }, focusPreviousItem: () => {
85
- const { index } = instance.getFocusedItem().getItemMeta();
79
+ return ((_b = tree.getItemInstance((_a = tree.getState().focusedItem) !== null && _a !== void 0 ? _a : "")) !== null && _b !== void 0 ? _b : tree.getItems()[0]);
80
+ },
81
+ focusItem: ({ tree }, itemId) => {
82
+ tree.applySubStateUpdate("focusedItem", itemId);
83
+ },
84
+ focusNextItem: ({ tree }) => {
85
+ const { index } = tree.getFocusedItem().getItemMeta();
86
+ const nextIndex = Math.min(index + 1, tree.getItems().length - 1);
87
+ tree.focusItem(tree.getItems()[nextIndex].getId());
88
+ },
89
+ focusPreviousItem: ({ tree }) => {
90
+ const { index } = tree.getFocusedItem().getItemMeta();
86
91
  const nextIndex = Math.max(index - 1, 0);
87
- instance.focusItem(instance.getItems()[nextIndex].getId());
88
- }, updateDomFocus: () => {
92
+ tree.focusItem(tree.getItems()[nextIndex].getId());
93
+ },
94
+ updateDomFocus: ({ tree }) => {
89
95
  // Required because if the state is managed outside in react, the state only updated during next render
90
96
  setTimeout(() => __awaiter(void 0, void 0, void 0, function* () {
91
97
  var _a, _b;
92
- const focusedItem = instance.getFocusedItem();
93
- (_b = (_a = instance.getConfig()).scrollToItem) === null || _b === void 0 ? void 0 : _b.call(_a, focusedItem);
98
+ const focusedItem = tree.getFocusedItem();
99
+ (_b = (_a = tree.getConfig()).scrollToItem) === null || _b === void 0 ? void 0 : _b.call(_a, focusedItem);
94
100
  yield (0, utils_1.poll)(() => focusedItem.getElement() !== null, 20);
95
101
  const focusedElement = focusedItem.getElement();
96
102
  if (!focusedElement)
97
103
  return;
98
104
  focusedElement.focus();
99
105
  }));
100
- }, getContainerProps: () => {
101
- var _a;
102
- return (Object.assign(Object.assign({}, (_a = prev.getContainerProps) === null || _a === void 0 ? void 0 : _a.call(prev)), { role: "tree", ariaLabel: "", ariaActivedescendant: "" }));
103
- } })),
104
- createItemInstance: (prev, item, tree) => (Object.assign(Object.assign({}, prev), { isLoading: () => {
105
- throw new Error("No data-loader registered");
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);
106
+ },
107
+ getContainerProps: ({ prev }) => (Object.assign(Object.assign({}, prev === null || prev === void 0 ? void 0 : prev()), { role: "tree", ariaLabel: "", ariaActivedescendant: "" })),
108
+ // relevant for hotkeys of this feature
109
+ isSearchOpen: () => false,
110
+ },
111
+ itemInstance: {
112
+ scrollTo: (_a, scrollIntoViewArg_1) => __awaiter(void 0, [_a, scrollIntoViewArg_1], void 0, function* ({ tree, item }, scrollIntoViewArg) {
113
+ var _b, _c, _d;
114
+ (_c = (_b = tree.getConfig()).scrollToItem) === null || _c === void 0 ? void 0 : _c.call(_b, item);
109
115
  yield (0, utils_1.poll)(() => item.getElement() !== null, 20);
110
- item.getElement().scrollIntoView(scrollIntoViewArg);
111
- }), getId: () => item.getItemMeta().itemId, getProps: () => {
112
- var _a;
116
+ (_d = item.getElement()) === null || _d === void 0 ? void 0 : _d.scrollIntoView(scrollIntoViewArg);
117
+ }),
118
+ getId: ({ item }) => item.getItemMeta().itemId,
119
+ getProps: ({ item, prev }) => {
113
120
  const itemMeta = item.getItemMeta();
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) => {
121
+ return Object.assign(Object.assign({}, prev === null || prev === void 0 ? void 0 : 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) => {
115
122
  item.setFocused();
116
123
  item.primaryAction();
117
124
  if (e.ctrlKey || e.shiftKey || e.metaKey) {
@@ -127,24 +134,38 @@ exports.treeFeature = {
127
134
  item.expand();
128
135
  }
129
136
  }) });
130
- }, expand: () => tree.expandItem(item.getItemMeta().itemId), collapse: () => tree.collapseItem(item.getItemMeta().itemId), getItemData: () => tree.retrieveItemData(item.getItemMeta().itemId), isExpanded: () => tree.getState().expandedItems.includes(item.getItemMeta().itemId), isFocused: () => tree.getState().focusedItem === item.getItemMeta().itemId ||
131
- (tree.getState().focusedItem === null && item.getItemMeta().index === 0), isFolder: () => item.getItemMeta().level === -1 ||
132
- tree.getConfig().isItemFolder(item), getItemName: () => {
137
+ },
138
+ expand: ({ tree, item }) => tree.expandItem(item.getItemMeta().itemId),
139
+ collapse: ({ tree, item }) => tree.collapseItem(item.getItemMeta().itemId),
140
+ getItemData: ({ tree, item }) => tree.retrieveItemData(item.getItemMeta().itemId),
141
+ equals: ({ item }, other) => item.getId() === (other === null || other === void 0 ? void 0 : other.getId()),
142
+ isExpanded: ({ tree, item }) => tree.getState().expandedItems.includes(item.getItemMeta().itemId),
143
+ isDescendentOf: ({ item }, parentId) => {
144
+ const parent = item.getParent();
145
+ return Boolean((parent === null || parent === void 0 ? void 0 : parent.getId()) === parentId || (parent === null || parent === void 0 ? void 0 : parent.isDescendentOf(parentId)));
146
+ },
147
+ isFocused: ({ tree, item }) => tree.getState().focusedItem === item.getItemMeta().itemId ||
148
+ (tree.getState().focusedItem === null && item.getItemMeta().index === 0),
149
+ isFolder: ({ tree, item }) => item.getItemMeta().level === -1 ||
150
+ tree.getConfig().isItemFolder(item),
151
+ getItemName: ({ tree, item }) => {
133
152
  const config = tree.getConfig();
134
153
  return config.getItemName(item);
135
- }, setFocused: () => tree.focusItem(item.getItemMeta().itemId), primaryAction: () => { var _a, _b; return (_b = (_a = tree.getConfig()).onPrimaryAction) === null || _b === void 0 ? void 0 : _b.call(_a, item); }, getParent: (0, utils_1.memo)((itemMeta) => {
136
- for (let i = itemMeta.index - 1; i >= 0; i--) {
137
- const potentialParent = tree.getItems()[i];
138
- if (potentialParent.getItemMeta().level < itemMeta.level) {
139
- return potentialParent;
140
- }
141
- }
142
- return tree.getItemInstance(tree.getConfig().rootItemId);
143
- }, () => [item.getItemMeta()]),
154
+ },
155
+ setFocused: ({ tree, item }) => tree.focusItem(item.getItemMeta().itemId),
156
+ primaryAction: ({ tree, item }) => { var _a, _b; return (_b = (_a = tree.getConfig()).onPrimaryAction) === null || _b === void 0 ? void 0 : _b.call(_a, item); },
157
+ getParent: ({ tree, item }) => item.getItemMeta().parentId
158
+ ? tree.getItemInstance(item.getItemMeta().parentId)
159
+ : undefined,
144
160
  // TODO remove
145
- getIndexInParent: () => item.getItemMeta().posInSet, getChildren: () => tree
161
+ getIndexInParent: ({ item }) => item.getItemMeta().posInSet,
162
+ getChildren: ({ tree, item }) => tree
146
163
  .retrieveChildrenIds(item.getItemMeta().itemId)
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) => {
164
+ .map((id) => tree.getItemInstance(id)),
165
+ getTree: ({ tree }) => tree,
166
+ getItemAbove: ({ tree, item }) => tree.getItems()[item.getItemMeta().index - 1],
167
+ getItemBelow: ({ tree, item }) => tree.getItems()[item.getItemMeta().index + 1],
168
+ getMemoizedProp: ({ item }, name, create, deps) => {
148
169
  var _a, _b, _c, _d, _e;
149
170
  var _f, _g;
150
171
  const data = item.getDataRef();
@@ -160,13 +181,14 @@ exports.treeFeature = {
160
181
  data.current.memoizedDeps[name] = deps;
161
182
  data.current.memoizedValues[name] = value;
162
183
  return value;
163
- } })),
184
+ },
185
+ },
164
186
  hotkeys: {
165
187
  focusNextItem: {
166
188
  hotkey: "ArrowDown",
167
189
  canRepeat: true,
168
190
  preventDefault: true,
169
- isEnabled: (tree) => { var _a, _b; return !((_b = (_a = tree.isSearchOpen) === null || _a === void 0 ? void 0 : _a.call(tree)) !== null && _b !== void 0 ? _b : false) && !tree.getState().dnd; },
191
+ isEnabled: (tree) => { var _a, _b; return !((_b = (_a = tree.isSearchOpen) === null || _a === void 0 ? void 0 : _a.call(tree)) !== null && _b !== void 0 ? _b : false) && !tree.getState().dnd; }, // TODO what happens when the feature doesnt exist? proxy method still claims to exist
170
192
  handler: (e, tree) => {
171
193
  tree.focusNextItem();
172
194
  tree.updateDomFocus();
@@ -42,19 +42,21 @@ export type TreeFeatureDef<T> = {
42
42
  getProps: () => Record<string, any>;
43
43
  getItemName: () => string;
44
44
  getItemData: () => T;
45
+ equals: (other?: ItemInstance<any> | null) => boolean;
45
46
  expand: () => void;
46
47
  collapse: () => void;
47
48
  isExpanded: () => boolean;
49
+ isDescendentOf: (parentId: string) => boolean;
48
50
  isFocused: () => boolean;
49
51
  isFolder: () => boolean;
50
52
  setFocused: () => void;
51
- getParent: () => ItemInstance<T>;
53
+ getParent: () => ItemInstance<T> | undefined;
52
54
  getChildren: () => ItemInstance<T>[];
53
55
  getIndexInParent: () => number;
54
56
  primaryAction: () => void;
55
57
  getTree: () => TreeInstance<T>;
56
- getItemAbove: () => ItemInstance<T> | null;
57
- getItemBelow: () => ItemInstance<T> | null;
58
+ getItemAbove: () => ItemInstance<T> | undefined;
59
+ getItemBelow: () => ItemInstance<T> | undefined;
58
60
  getMemoizedProp: <X>(name: string, create: () => X, deps?: any[]) => X;
59
61
  scrollTo: (scrollIntoViewArg?: boolean | ScrollIntoViewOptions) => Promise<void>;
60
62
  };
@@ -1,7 +1,7 @@
1
1
  export * from "./types/core";
2
2
  export * from "./core/create-tree";
3
3
  export * from "./features/tree/types";
4
- export * from "./features/main/types";
4
+ export { MainFeatureDef, InstanceBuilder } from "./features/main/types";
5
5
  export * from "./features/drag-and-drop/types";
6
6
  export * from "./features/selection/types";
7
7
  export * from "./features/async-data-loader/types";
@@ -21,3 +21,5 @@ export * from "./features/expand-all/feature";
21
21
  export * from "./utilities/create-on-drop-handler";
22
22
  export * from "./utilities/insert-items-at-target";
23
23
  export * from "./utilities/remove-items-from-parents";
24
+ export * from "./core/build-proxified-instance";
25
+ export * from "./core/build-static-instance";
package/lib/cjs/index.js CHANGED
@@ -17,7 +17,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./types/core"), exports);
18
18
  __exportStar(require("./core/create-tree"), exports);
19
19
  __exportStar(require("./features/tree/types"), exports);
20
- __exportStar(require("./features/main/types"), exports);
21
20
  __exportStar(require("./features/drag-and-drop/types"), exports);
22
21
  __exportStar(require("./features/selection/types"), exports);
23
22
  __exportStar(require("./features/async-data-loader/types"), exports);
@@ -37,3 +36,5 @@ __exportStar(require("./features/expand-all/feature"), exports);
37
36
  __exportStar(require("./utilities/create-on-drop-handler"), exports);
38
37
  __exportStar(require("./utilities/insert-items-at-target"), exports);
39
38
  __exportStar(require("./utilities/remove-items-from-parents"), exports);
39
+ __exportStar(require("./core/build-proxified-instance"), exports);
40
+ __exportStar(require("./core/build-static-instance"), exports);
@@ -0,0 +1,23 @@
1
+ import { DragEvent } from "react";
2
+ import { TestTree } from "./test-tree";
3
+ import { HotkeyName } from "../types/core";
4
+ export declare class TestTreeDo<T> {
5
+ protected tree: TestTree<T>;
6
+ protected itemInstance(itemId: string): import("../types/core").ItemInstance<any>;
7
+ protected itemProps(itemId: string): Record<string, any>;
8
+ constructor(tree: TestTree<T>);
9
+ selectItem(id: string): void;
10
+ shiftSelectItem(id: string): void;
11
+ ctrlSelectItem(id: string): void;
12
+ ctrlShiftSelectItem(id: string): void;
13
+ selectMultiple(...ids: string[]): void;
14
+ hotkey(hotkey: HotkeyName, e?: Partial<KeyboardEvent>): void;
15
+ startDrag(itemId: string, event?: DragEvent): DragEvent<Element>;
16
+ dragOver(itemId: string, event?: DragEvent): DragEvent<Element>;
17
+ dragOverNotAllowed(itemId: string, event?: DragEvent): DragEvent<Element>;
18
+ dragLeave(itemId: string): void;
19
+ dragEnd(itemId: string, event?: DragEvent): DragEvent<Element>;
20
+ drop(itemId: string, event?: DragEvent): DragEvent<Element>;
21
+ dragOverAndDrop(itemId: string, event?: DragEvent): DragEvent<Element>;
22
+ private consistentCalls;
23
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TestTreeDo = void 0;
4
+ const vitest_1 = require("vitest");
5
+ const test_tree_1 = require("./test-tree");
6
+ class TestTreeDo {
7
+ itemInstance(itemId) {
8
+ return this.tree.instance.getItemInstance(itemId);
9
+ }
10
+ itemProps(itemId) {
11
+ return this.itemInstance(itemId).getProps();
12
+ }
13
+ constructor(tree) {
14
+ this.tree = tree;
15
+ }
16
+ selectItem(id) {
17
+ this.itemProps(id).onClick({});
18
+ }
19
+ shiftSelectItem(id) {
20
+ this.itemProps(id).onClick({ shiftKey: true });
21
+ }
22
+ ctrlSelectItem(id) {
23
+ this.itemProps(id).onClick({ ctrlKey: true });
24
+ }
25
+ ctrlShiftSelectItem(id) {
26
+ this.itemProps(id).onClick({ shiftKey: true, ctrlKey: true });
27
+ }
28
+ selectMultiple(...ids) {
29
+ ids.forEach((id) => this.ctrlSelectItem(id));
30
+ }
31
+ hotkey(hotkey, e = {}) {
32
+ var _a, _b;
33
+ const hotkeyConfig = Object.assign(Object.assign({}, this.tree.instance.getHotkeyPresets()[hotkey]), (_a = this.tree.instance.getConfig().hotkeys) === null || _a === void 0 ? void 0 : _a[hotkey]);
34
+ if (hotkeyConfig.isEnabled &&
35
+ !((_b = hotkeyConfig.isEnabled) === null || _b === void 0 ? void 0 : _b.call(hotkeyConfig, this.tree.instance))) {
36
+ throw new Error(`Hotkey "${hotkey}" is disabled`);
37
+ }
38
+ if (!hotkeyConfig.handler) {
39
+ throw new Error(`Hotkey "${hotkey}" has no handler`);
40
+ }
41
+ hotkeyConfig.handler(Object.assign(Object.assign({}, e), { stopPropagation: () => { }, preventDefault: () => { } }), this.tree.instance);
42
+ }
43
+ startDrag(itemId, event) {
44
+ if (!this.itemProps(itemId).draggable) {
45
+ throw new Error(`Can't drag item ${itemId}, has attribute draggable=false`);
46
+ }
47
+ const e = event !== null && event !== void 0 ? event : test_tree_1.TestTree.dragEvent();
48
+ this.itemProps(itemId).onDragStart(e);
49
+ return e;
50
+ }
51
+ dragOver(itemId, event) {
52
+ const e = event !== null && event !== void 0 ? event : test_tree_1.TestTree.dragEvent();
53
+ e.preventDefault.mockClear();
54
+ this.itemProps(itemId).onDragOver(e);
55
+ this.itemProps(itemId).onDragOver(e);
56
+ this.itemProps(itemId).onDragOver(e);
57
+ (0, vitest_1.expect)(e.preventDefault).toBeCalledTimes(3);
58
+ this.consistentCalls(e.preventDefault);
59
+ this.consistentCalls(e.stopPropagation);
60
+ return e;
61
+ }
62
+ dragOverNotAllowed(itemId, event) {
63
+ const e = event !== null && event !== void 0 ? event : test_tree_1.TestTree.dragEvent();
64
+ e.preventDefault.mockClear();
65
+ this.itemProps(itemId).onDragOver(e);
66
+ this.itemProps(itemId).onDragOver(e);
67
+ this.itemProps(itemId).onDragOver(e);
68
+ (0, vitest_1.expect)(e.preventDefault).toBeCalledTimes(0);
69
+ this.consistentCalls(e.preventDefault);
70
+ this.consistentCalls(e.stopPropagation);
71
+ return e;
72
+ }
73
+ dragLeave(itemId) {
74
+ this.itemProps(itemId).onDragLeave({});
75
+ }
76
+ dragEnd(itemId, event) {
77
+ const e = event !== null && event !== void 0 ? event : test_tree_1.TestTree.dragEvent();
78
+ this.itemProps(itemId).onDragEnd(e);
79
+ return e;
80
+ }
81
+ 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;
85
+ }
86
+ 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);
90
+ }
91
+ consistentCalls(fn) {
92
+ if (!vitest_1.vi.isMockFunction(fn)) {
93
+ throw new Error("fn is not a mock");
94
+ }
95
+ (0, vitest_1.expect)(fn.mock.calls.length, "function called inconsistent times").toBeOneOf([0, 3]);
96
+ (0, vitest_1.expect)(new Set(fn.mock.calls.map((call) => call.join("__"))).size, "function called with inconsistent parameters").toBeOneOf([0, 1]);
97
+ }
98
+ }
99
+ exports.TestTreeDo = TestTreeDo;
@@ -0,0 +1,15 @@
1
+ import { DragEvent } from "react";
2
+ import { TestTree } from "./test-tree";
3
+ import { DropTarget } from "../features/drag-and-drop/types";
4
+ export declare class TestTreeExpect<T> {
5
+ private tree;
6
+ protected itemInstance(itemId: string): import("..").ItemInstance<any>;
7
+ protected itemProps(itemId: string): Record<string, any>;
8
+ constructor(tree: TestTree<T>);
9
+ foldersExpanded(...itemIds: string[]): void;
10
+ foldersCollapsed(...itemIds: string[]): void;
11
+ hasChildren(itemId: string, children: string[]): void;
12
+ dropped(draggedItems: string[], target: DropTarget<any>): void;
13
+ dragOverNotAllowed(itemId: string, event?: DragEvent): DragEvent<Element>;
14
+ defaultDragLineProps(indent?: number): void;
15
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TestTreeExpect = void 0;
4
+ /* eslint-disable import/no-extraneous-dependencies */
5
+ const vitest_1 = require("vitest");
6
+ const test_tree_1 = require("./test-tree");
7
+ class TestTreeExpect {
8
+ itemInstance(itemId) {
9
+ return this.tree.instance.getItemInstance(itemId);
10
+ }
11
+ itemProps(itemId) {
12
+ return this.itemInstance(itemId).getProps();
13
+ }
14
+ constructor(tree) {
15
+ this.tree = tree;
16
+ }
17
+ foldersExpanded(...itemIds) {
18
+ for (const itemId of itemIds) {
19
+ (0, vitest_1.expect)(this.tree.instance.getItemInstance(itemId).isExpanded(), `Expected ${itemId} to be expanded`).toBe(true);
20
+ }
21
+ }
22
+ foldersCollapsed(...itemIds) {
23
+ for (const itemId of itemIds) {
24
+ (0, vitest_1.expect)(this.tree.instance.getItemInstance(itemId).isExpanded(), `Expected ${itemId} to be collapsed`).toBe(false);
25
+ }
26
+ }
27
+ hasChildren(itemId, children) {
28
+ const item = this.tree.instance.getItemInstance(itemId);
29
+ const itemChildren = item.getChildren().map((child) => child.getId());
30
+ (0, vitest_1.expect)(itemChildren).toEqual(children);
31
+ }
32
+ dropped(draggedItems, target) {
33
+ (0, vitest_1.expect)(this.tree.instance.getConfig().onDrop).toBeCalledWith(draggedItems.map((id) => this.tree.item(id)), target);
34
+ }
35
+ dragOverNotAllowed(itemId, event) {
36
+ const e = event !== null && event !== void 0 ? event : test_tree_1.TestTree.dragEvent();
37
+ e.preventDefault.mockClear();
38
+ this.itemProps(itemId).onDragOver(e);
39
+ this.itemProps(itemId).onDragOver(e);
40
+ this.itemProps(itemId).onDragOver(e);
41
+ (0, vitest_1.expect)(e.preventDefault, "onDragOver shouldn't call e.preventDefault if drag is not allowed").not.toBeCalled();
42
+ this.itemProps(itemId).onDrop(e);
43
+ (0, vitest_1.expect)(e.preventDefault, "onDrop shouldn't call e.preventDefault if drag is not allowed").not.toBeCalled();
44
+ (0, vitest_1.expect)(this.tree.instance.getConfig().onDrop, "onDrop handler shouldn't be called if drag is not allowed").not.toBeCalled();
45
+ return e;
46
+ }
47
+ defaultDragLineProps(indent = 0) {
48
+ (0, vitest_1.expect)(this.tree.instance.getDragLineData()).toEqual({
49
+ indent,
50
+ left: indent * 20,
51
+ right: 100,
52
+ top: 0,
53
+ });
54
+ (0, vitest_1.expect)(this.tree.instance.getDragLineStyle(0, 0)).toEqual({
55
+ left: `${indent * 20}px`,
56
+ pointerEvents: "none",
57
+ top: "0px",
58
+ width: `${100 - indent * 20}px`,
59
+ });
60
+ }
61
+ }
62
+ exports.TestTreeExpect = TestTreeExpect;
@@ -0,0 +1,47 @@
1
+ import { DragEvent } from "react";
2
+ import { TreeConfig, TreeInstance } from "../types/core";
3
+ import { TestTreeDo } from "./test-tree-do";
4
+ import { TestTreeExpect } from "./test-tree-expect";
5
+ export declare class TestTree<T = string> {
6
+ private config;
7
+ readonly do: TestTreeDo<T>;
8
+ readonly expect: TestTreeExpect<T>;
9
+ private treeInstance;
10
+ private static asyncLoaderResolvers;
11
+ suits: {
12
+ sync: () => {
13
+ tree: TestTree<T>;
14
+ title: string;
15
+ };
16
+ async: () => {
17
+ tree: TestTree<T>;
18
+ title: string;
19
+ };
20
+ proxifiedSync: () => {
21
+ tree: TestTree<T>;
22
+ title: string;
23
+ };
24
+ proxifiedAsync: () => {
25
+ tree: TestTree<T>;
26
+ title: string;
27
+ };
28
+ };
29
+ forSuits(runSuite: (tree: TestTree<T>) => void): void;
30
+ get instance(): TreeInstance<T>;
31
+ private constructor();
32
+ static resolveAsyncLoaders(): Promise<void>;
33
+ resolveAsyncVisibleItems(): Promise<void>;
34
+ static default(config: Partial<TreeConfig<string>>): TestTree<string>;
35
+ with(config: Partial<TreeConfig<T>>): TestTree<T>;
36
+ resetBeforeEach(): void;
37
+ createTestCaseTree(): Promise<this>;
38
+ withFeatures(...features: any): TestTree<T>;
39
+ mockedHandler(handlerName: keyof TreeConfig<T>): import("vitest").Mock<(...args: any[]) => any>;
40
+ item(itemId: string): import("../types/core").ItemInstance<any>;
41
+ reset(): void;
42
+ debug(): void;
43
+ setElementBoundingBox(itemId: string, bb?: Partial<DOMRect>): void;
44
+ static dragEvent(pageX?: number, pageY?: number): DragEvent;
45
+ createTopDragEvent(indent?: number): DragEvent<Element>;
46
+ createBottomDragEvent(indent?: number): DragEvent<Element>;
47
+ }