@headless-tree/core 0.0.0-20230802230636

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 (149) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/lib/cjs/core/create-tree.d.ts +2 -0
  3. package/lib/cjs/core/create-tree.js +138 -0
  4. package/lib/cjs/features/async-data-loader/feature.d.ts +5 -0
  5. package/lib/cjs/features/async-data-loader/feature.js +88 -0
  6. package/lib/cjs/features/async-data-loader/types.d.ts +41 -0
  7. package/lib/cjs/features/async-data-loader/types.js +2 -0
  8. package/lib/cjs/features/drag-and-drop/feature.d.ts +3 -0
  9. package/lib/cjs/features/drag-and-drop/feature.js +138 -0
  10. package/lib/cjs/features/drag-and-drop/types.d.ts +69 -0
  11. package/lib/cjs/features/drag-and-drop/types.js +9 -0
  12. package/lib/cjs/features/drag-and-drop/utils.d.ts +6 -0
  13. package/lib/cjs/features/drag-and-drop/utils.js +79 -0
  14. package/lib/cjs/features/expand-all/feature.d.ts +6 -0
  15. package/lib/cjs/features/expand-all/feature.js +41 -0
  16. package/lib/cjs/features/expand-all/types.d.ts +17 -0
  17. package/lib/cjs/features/expand-all/types.js +2 -0
  18. package/lib/cjs/features/hotkeys-core/feature.d.ts +4 -0
  19. package/lib/cjs/features/hotkeys-core/feature.js +71 -0
  20. package/lib/cjs/features/hotkeys-core/types.d.ts +25 -0
  21. package/lib/cjs/features/hotkeys-core/types.js +2 -0
  22. package/lib/cjs/features/main/types.d.ts +39 -0
  23. package/lib/cjs/features/main/types.js +2 -0
  24. package/lib/cjs/features/renaming/feature.d.ts +5 -0
  25. package/lib/cjs/features/renaming/feature.js +64 -0
  26. package/lib/cjs/features/renaming/types.d.ts +27 -0
  27. package/lib/cjs/features/renaming/types.js +2 -0
  28. package/lib/cjs/features/search/feature.d.ts +5 -0
  29. package/lib/cjs/features/search/feature.js +103 -0
  30. package/lib/cjs/features/search/types.d.ts +33 -0
  31. package/lib/cjs/features/search/types.js +2 -0
  32. package/lib/cjs/features/selection/feature.d.ts +5 -0
  33. package/lib/cjs/features/selection/feature.js +113 -0
  34. package/lib/cjs/features/selection/types.d.ts +21 -0
  35. package/lib/cjs/features/selection/types.js +2 -0
  36. package/lib/cjs/features/sync-data-loader/feature.d.ts +4 -0
  37. package/lib/cjs/features/sync-data-loader/feature.js +14 -0
  38. package/lib/cjs/features/sync-data-loader/types.d.ts +19 -0
  39. package/lib/cjs/features/sync-data-loader/types.js +2 -0
  40. package/lib/cjs/features/tree/feature.d.ts +6 -0
  41. package/lib/cjs/features/tree/feature.js +230 -0
  42. package/lib/cjs/features/tree/types.d.ts +62 -0
  43. package/lib/cjs/features/tree/types.js +2 -0
  44. package/lib/cjs/index.d.ts +23 -0
  45. package/lib/cjs/index.js +39 -0
  46. package/lib/cjs/mddocs-entry.d.ts +21 -0
  47. package/lib/cjs/mddocs-entry.js +17 -0
  48. package/lib/cjs/types/core.d.ts +67 -0
  49. package/lib/cjs/types/core.js +2 -0
  50. package/lib/cjs/types/deep-merge.d.ts +13 -0
  51. package/lib/cjs/types/deep-merge.js +2 -0
  52. package/lib/cjs/utilities/create-on-drop-handler.d.ts +3 -0
  53. package/lib/cjs/utilities/create-on-drop-handler.js +11 -0
  54. package/lib/cjs/utilities/insert-items-at-target.d.ts +3 -0
  55. package/lib/cjs/utilities/insert-items-at-target.js +24 -0
  56. package/lib/cjs/utilities/remove-items-from-parents.d.ts +2 -0
  57. package/lib/cjs/utilities/remove-items-from-parents.js +17 -0
  58. package/lib/cjs/utils.d.ts +6 -0
  59. package/lib/cjs/utils.js +53 -0
  60. package/lib/esm/core/create-tree.d.ts +2 -0
  61. package/lib/esm/core/create-tree.js +134 -0
  62. package/lib/esm/features/async-data-loader/feature.d.ts +5 -0
  63. package/lib/esm/features/async-data-loader/feature.js +85 -0
  64. package/lib/esm/features/async-data-loader/types.d.ts +41 -0
  65. package/lib/esm/features/async-data-loader/types.js +1 -0
  66. package/lib/esm/features/drag-and-drop/feature.d.ts +3 -0
  67. package/lib/esm/features/drag-and-drop/feature.js +135 -0
  68. package/lib/esm/features/drag-and-drop/types.d.ts +69 -0
  69. package/lib/esm/features/drag-and-drop/types.js +6 -0
  70. package/lib/esm/features/drag-and-drop/utils.d.ts +6 -0
  71. package/lib/esm/features/drag-and-drop/utils.js +72 -0
  72. package/lib/esm/features/expand-all/feature.d.ts +6 -0
  73. package/lib/esm/features/expand-all/feature.js +38 -0
  74. package/lib/esm/features/expand-all/types.d.ts +17 -0
  75. package/lib/esm/features/expand-all/types.js +1 -0
  76. package/lib/esm/features/hotkeys-core/feature.d.ts +4 -0
  77. package/lib/esm/features/hotkeys-core/feature.js +68 -0
  78. package/lib/esm/features/hotkeys-core/types.d.ts +25 -0
  79. package/lib/esm/features/hotkeys-core/types.js +1 -0
  80. package/lib/esm/features/main/types.d.ts +39 -0
  81. package/lib/esm/features/main/types.js +1 -0
  82. package/lib/esm/features/renaming/feature.d.ts +5 -0
  83. package/lib/esm/features/renaming/feature.js +61 -0
  84. package/lib/esm/features/renaming/types.d.ts +27 -0
  85. package/lib/esm/features/renaming/types.js +1 -0
  86. package/lib/esm/features/search/feature.d.ts +5 -0
  87. package/lib/esm/features/search/feature.js +100 -0
  88. package/lib/esm/features/search/types.d.ts +33 -0
  89. package/lib/esm/features/search/types.js +1 -0
  90. package/lib/esm/features/selection/feature.d.ts +5 -0
  91. package/lib/esm/features/selection/feature.js +110 -0
  92. package/lib/esm/features/selection/types.d.ts +21 -0
  93. package/lib/esm/features/selection/types.js +1 -0
  94. package/lib/esm/features/sync-data-loader/feature.d.ts +4 -0
  95. package/lib/esm/features/sync-data-loader/feature.js +11 -0
  96. package/lib/esm/features/sync-data-loader/types.d.ts +19 -0
  97. package/lib/esm/features/sync-data-loader/types.js +1 -0
  98. package/lib/esm/features/tree/feature.d.ts +6 -0
  99. package/lib/esm/features/tree/feature.js +227 -0
  100. package/lib/esm/features/tree/types.d.ts +62 -0
  101. package/lib/esm/features/tree/types.js +1 -0
  102. package/lib/esm/index.d.ts +23 -0
  103. package/lib/esm/index.js +23 -0
  104. package/lib/esm/mddocs-entry.d.ts +21 -0
  105. package/lib/esm/mddocs-entry.js +1 -0
  106. package/lib/esm/types/core.d.ts +67 -0
  107. package/lib/esm/types/core.js +1 -0
  108. package/lib/esm/types/deep-merge.d.ts +13 -0
  109. package/lib/esm/types/deep-merge.js +1 -0
  110. package/lib/esm/utilities/create-on-drop-handler.d.ts +3 -0
  111. package/lib/esm/utilities/create-on-drop-handler.js +7 -0
  112. package/lib/esm/utilities/insert-items-at-target.d.ts +3 -0
  113. package/lib/esm/utilities/insert-items-at-target.js +20 -0
  114. package/lib/esm/utilities/remove-items-from-parents.d.ts +2 -0
  115. package/lib/esm/utilities/remove-items-from-parents.js +13 -0
  116. package/lib/esm/utils.d.ts +6 -0
  117. package/lib/esm/utils.js +46 -0
  118. package/package.json +23 -0
  119. package/src/core/create-tree.ts +228 -0
  120. package/src/features/async-data-loader/feature.ts +126 -0
  121. package/src/features/async-data-loader/types.ts +41 -0
  122. package/src/features/drag-and-drop/feature.ts +214 -0
  123. package/src/features/drag-and-drop/types.ts +89 -0
  124. package/src/features/drag-and-drop/utils.ts +117 -0
  125. package/src/features/expand-all/feature.ts +63 -0
  126. package/src/features/expand-all/types.ts +13 -0
  127. package/src/features/hotkeys-core/feature.ts +110 -0
  128. package/src/features/hotkeys-core/types.ts +36 -0
  129. package/src/features/main/types.ts +48 -0
  130. package/src/features/renaming/feature.ts +105 -0
  131. package/src/features/renaming/types.ts +28 -0
  132. package/src/features/search/feature.ts +158 -0
  133. package/src/features/search/types.ts +40 -0
  134. package/src/features/selection/feature.ts +157 -0
  135. package/src/features/selection/types.ts +28 -0
  136. package/src/features/sync-data-loader/feature.ts +41 -0
  137. package/src/features/sync-data-loader/types.ts +20 -0
  138. package/src/features/tree/feature.ts +326 -0
  139. package/src/features/tree/types.ts +78 -0
  140. package/src/index.ts +26 -0
  141. package/src/mddocs-entry.ts +26 -0
  142. package/src/types/core.ts +183 -0
  143. package/src/types/deep-merge.ts +31 -0
  144. package/src/utilities/create-on-drop-handler.ts +14 -0
  145. package/src/utilities/insert-items-at-target.ts +30 -0
  146. package/src/utilities/remove-items-from-parents.ts +21 -0
  147. package/src/utils.ts +68 -0
  148. package/tsconfig.json +7 -0
  149. package/typedoc.json +4 -0
@@ -0,0 +1,227 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { makeStateUpdater, memo, poll } from "../../utils";
11
+ export const treeFeature = {
12
+ key: "tree",
13
+ getInitialState: (initialState) => (Object.assign({ expandedItems: [], focusedItem: null }, initialState)),
14
+ getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setExpandedItems: makeStateUpdater("expandedItems", tree), setFocusedItem: makeStateUpdater("focusedItem", tree) }, defaultConfig)),
15
+ stateHandlerNames: {
16
+ expandedItems: "setExpandedItems",
17
+ focusedItem: "setFocusedItem",
18
+ },
19
+ createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { retrieveItemData: () => {
20
+ throw new Error("No data-loader registered");
21
+ }, retrieveChildrenIds: () => {
22
+ throw new Error("No data-loader registered");
23
+ }, isItemExpanded: (itemId) => instance.getState().expandedItems.includes(itemId), getItemsMeta: () => {
24
+ const { rootItemId } = instance.getConfig();
25
+ const { expandedItems } = instance.getState();
26
+ const flatItems = [];
27
+ const recursiveAdd = (itemId, parentId, level, setSize, posInSet) => {
28
+ var _a;
29
+ flatItems.push({
30
+ itemId,
31
+ level,
32
+ index: flatItems.length,
33
+ parentId,
34
+ setSize,
35
+ posInSet,
36
+ });
37
+ if (expandedItems.includes(itemId)) {
38
+ const children = (_a = instance.retrieveChildrenIds(itemId)) !== null && _a !== void 0 ? _a : [];
39
+ let i = 0;
40
+ for (const childId of children) {
41
+ recursiveAdd(childId, itemId, level + 1, children.length, i++);
42
+ }
43
+ }
44
+ };
45
+ const children = instance.retrieveChildrenIds(rootItemId);
46
+ let i = 0;
47
+ for (const itemId of children) {
48
+ recursiveAdd(itemId, rootItemId, 0, children.length, i++);
49
+ }
50
+ return flatItems;
51
+ }, expandItem: (itemId) => {
52
+ var _a;
53
+ if (!instance.getItemInstance(itemId).isFolder()) {
54
+ return;
55
+ }
56
+ if ((_a = instance.getState().loadingItems) === null || _a === void 0 ? void 0 : _a.includes(itemId)) {
57
+ return;
58
+ }
59
+ instance.applySubStateUpdate("expandedItems", (expandedItems) => [
60
+ ...expandedItems,
61
+ itemId,
62
+ ]);
63
+ instance.rebuildTree();
64
+ }, collapseItem: (itemId) => {
65
+ if (!instance.getItemInstance(itemId).isFolder()) {
66
+ return;
67
+ }
68
+ instance.applySubStateUpdate("expandedItems", (expandedItems) => expandedItems.filter((id) => id !== itemId));
69
+ instance.rebuildTree();
70
+ },
71
+ // TODO memo
72
+ getFocusedItem: () => {
73
+ var _a, _b;
74
+ return ((_b = instance.getItemInstance((_a = instance.getState().focusedItem) !== null && _a !== void 0 ? _a : "")) !== null && _b !== void 0 ? _b : instance.getItems()[0]);
75
+ }, focusItem: (itemId) => {
76
+ instance.applySubStateUpdate("focusedItem", itemId);
77
+ }, focusNextItem: () => {
78
+ const { index } = instance.getFocusedItem().getItemMeta();
79
+ const nextIndex = Math.min(index + 1, instance.getItems().length - 1);
80
+ instance.focusItem(instance.getItems()[nextIndex].getId());
81
+ }, focusPreviousItem: () => {
82
+ const { index } = instance.getFocusedItem().getItemMeta();
83
+ const nextIndex = Math.max(index - 1, 0);
84
+ instance.focusItem(instance.getItems()[nextIndex].getId());
85
+ }, updateDomFocus: () => {
86
+ // Required because if the state is managed outside in react, the state only updated during next render
87
+ setTimeout(() => __awaiter(void 0, void 0, void 0, function* () {
88
+ var _a, _b;
89
+ const focusedItem = instance.getFocusedItem();
90
+ (_b = (_a = instance.getConfig()).scrollToItem) === null || _b === void 0 ? void 0 : _b.call(_a, focusedItem);
91
+ yield poll(() => focusedItem.getElement() !== null, 20);
92
+ const focusedElement = focusedItem.getElement();
93
+ if (!focusedElement)
94
+ return;
95
+ focusedElement.focus();
96
+ }));
97
+ }, getContainerProps: () => {
98
+ var _a;
99
+ return (Object.assign(Object.assign({}, (_a = prev.getContainerProps) === null || _a === void 0 ? void 0 : _a.call(prev)), { role: "tree", ariaLabel: "", ariaActivedescendant: "" }));
100
+ } })),
101
+ createItemInstance: (prev, item, tree) => (Object.assign(Object.assign({}, prev), { isLoading: () => {
102
+ throw new Error("No data-loader registered");
103
+ }, scrollTo: (scrollIntoViewArg) => __awaiter(void 0, void 0, void 0, function* () {
104
+ var _a, _b;
105
+ (_b = (_a = tree.getConfig()).scrollToItem) === null || _b === void 0 ? void 0 : _b.call(_a, item);
106
+ yield poll(() => item.getElement() !== null, 20);
107
+ item.getElement().scrollIntoView(scrollIntoViewArg);
108
+ }), getId: () => item.getItemMeta().itemId, getProps: () => {
109
+ var _a;
110
+ 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
+ item.setFocused();
113
+ item.primaryAction();
114
+ if (e.ctrlKey || e.shiftKey || e.metaKey) {
115
+ return;
116
+ }
117
+ if (!item.isFolder()) {
118
+ return;
119
+ }
120
+ if (item.isExpanded()) {
121
+ item.collapse();
122
+ }
123
+ else {
124
+ item.expand();
125
+ }
126
+ }) });
127
+ }, 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 ||
128
+ (tree.getState().focusedItem === null && item.getItemMeta().index === 0), isFolder: () => item.getItemMeta().level === -1 ||
129
+ tree.getConfig().isItemFolder(item), getItemName: () => {
130
+ const config = tree.getConfig();
131
+ return config.getItemName(item);
132
+ }, 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: memo((itemMeta) => {
133
+ for (let i = itemMeta.index - 1; i >= 0; i--) {
134
+ const potentialParent = tree.getItems()[i];
135
+ if (potentialParent.getItemMeta().level < itemMeta.level) {
136
+ return potentialParent;
137
+ }
138
+ }
139
+ return tree.getItemInstance(tree.getConfig().rootItemId);
140
+ }, () => [item.getItemMeta()]),
141
+ // TODO remove
142
+ getIndexInParent: () => item.getItemMeta().posInSet, getChildren: () => tree
143
+ .retrieveChildrenIds(item.getItemMeta().itemId)
144
+ .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) => {
145
+ var _a, _b, _c, _d, _e;
146
+ var _f, _g;
147
+ const data = item.getDataRef();
148
+ const memoizedValue = (_a = data.current.memoizedValues) === null || _a === void 0 ? void 0 : _a[name];
149
+ if (memoizedValue &&
150
+ (!deps ||
151
+ ((_c = (_b = data.current.memoizedDeps) === null || _b === void 0 ? void 0 : _b[name]) === null || _c === void 0 ? void 0 : _c.every((d, i) => d === deps[i])))) {
152
+ return memoizedValue;
153
+ }
154
+ (_d = (_f = data.current).memoizedDeps) !== null && _d !== void 0 ? _d : (_f.memoizedDeps = {});
155
+ (_e = (_g = data.current).memoizedValues) !== null && _e !== void 0 ? _e : (_g.memoizedValues = {});
156
+ const value = create();
157
+ data.current.memoizedDeps[name] = deps;
158
+ data.current.memoizedValues[name] = value;
159
+ return value;
160
+ } })),
161
+ hotkeys: {
162
+ focusNextItem: {
163
+ hotkey: "ArrowDown",
164
+ canRepeat: true,
165
+ preventDefault: true,
166
+ 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; },
167
+ handler: (e, tree) => {
168
+ tree.focusNextItem();
169
+ tree.updateDomFocus();
170
+ },
171
+ },
172
+ focusPreviousItem: {
173
+ hotkey: "ArrowUp",
174
+ canRepeat: true,
175
+ preventDefault: true,
176
+ 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; },
177
+ handler: (e, tree) => {
178
+ tree.focusPreviousItem();
179
+ tree.updateDomFocus();
180
+ },
181
+ },
182
+ expandOrDown: {
183
+ hotkey: "ArrowRight",
184
+ canRepeat: true,
185
+ handler: (e, tree) => {
186
+ const item = tree.getFocusedItem();
187
+ if (item.isExpanded() || !item.isFolder()) {
188
+ tree.focusNextItem();
189
+ tree.updateDomFocus();
190
+ }
191
+ else {
192
+ item.expand();
193
+ }
194
+ },
195
+ },
196
+ collapseOrUp: {
197
+ hotkey: "ArrowLeft",
198
+ canRepeat: true,
199
+ handler: (e, tree) => {
200
+ var _a;
201
+ const item = tree.getFocusedItem();
202
+ if ((!item.isExpanded() || !item.isFolder()) &&
203
+ item.getItemMeta().level !== 0) {
204
+ (_a = item.getParent()) === null || _a === void 0 ? void 0 : _a.setFocused();
205
+ tree.updateDomFocus();
206
+ }
207
+ else {
208
+ item.collapse();
209
+ }
210
+ },
211
+ },
212
+ focusFirstItem: {
213
+ hotkey: "Home",
214
+ handler: (e, tree) => {
215
+ tree.focusItem(tree.getItems()[0].getId());
216
+ tree.updateDomFocus();
217
+ },
218
+ },
219
+ focusLastItem: {
220
+ hotkey: "End",
221
+ handler: (e, tree) => {
222
+ tree.focusItem(tree.getItems()[tree.getItems().length - 1].getId());
223
+ tree.updateDomFocus();
224
+ },
225
+ },
226
+ },
227
+ };
@@ -0,0 +1,62 @@
1
+ import { ItemInstance, SetStateFn, TreeInstance } from "../../types/core";
2
+ export type ItemMeta = {
3
+ itemId: string;
4
+ parentId: string;
5
+ level: number;
6
+ index: number;
7
+ setSize: number;
8
+ posInSet: number;
9
+ };
10
+ export type TreeItemDataRef = {
11
+ memoizedValues: Record<string, any>;
12
+ memoizedDeps: Record<string, any[] | undefined>;
13
+ };
14
+ export type TreeFeatureDef<T> = {
15
+ state: {
16
+ expandedItems: string[];
17
+ focusedItem: string | null;
18
+ };
19
+ config: {
20
+ isItemFolder: (item: ItemInstance<T>) => boolean;
21
+ getItemName: (item: ItemInstance<T>) => string;
22
+ onPrimaryAction?: (item: ItemInstance<T>) => void;
23
+ scrollToItem?: (item: ItemInstance<T>) => void;
24
+ setExpandedItems?: SetStateFn<string[]>;
25
+ setFocusedItem?: SetStateFn<string | null>;
26
+ };
27
+ treeInstance: {
28
+ /** @internal */
29
+ getItemsMeta: () => ItemMeta[];
30
+ expandItem: (itemId: string) => void;
31
+ collapseItem: (itemId: string) => void;
32
+ isItemExpanded: (itemId: string) => boolean;
33
+ focusItem: (itemId: string) => void;
34
+ getFocusedItem: () => ItemInstance<any>;
35
+ focusNextItem: () => void;
36
+ focusPreviousItem: () => void;
37
+ updateDomFocus: () => void;
38
+ getContainerProps: () => Record<string, any>;
39
+ };
40
+ itemInstance: {
41
+ getId: () => string;
42
+ getProps: () => Record<string, any>;
43
+ getItemName: () => string;
44
+ getItemData: () => T;
45
+ expand: () => void;
46
+ collapse: () => void;
47
+ isExpanded: () => boolean;
48
+ isFocused: () => boolean;
49
+ isFolder: () => boolean;
50
+ setFocused: () => void;
51
+ getParent: () => ItemInstance<T>;
52
+ getChildren: () => ItemInstance<T>[];
53
+ getIndexInParent: () => number;
54
+ primaryAction: () => void;
55
+ getTree: () => TreeInstance<T>;
56
+ getItemAbove: () => ItemInstance<T> | null;
57
+ getItemBelow: () => ItemInstance<T> | null;
58
+ getMemoizedProp: <X>(name: string, create: () => X, deps?: any[]) => X;
59
+ scrollTo: (scrollIntoViewArg?: boolean | ScrollIntoViewOptions) => Promise<void>;
60
+ };
61
+ hotkeys: "focusNextItem" | "focusPreviousItem" | "expandOrDown" | "collapseOrUp" | "focusFirstItem" | "focusLastItem";
62
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ export * from "./types/core";
2
+ export * from "./core/create-tree";
3
+ export * from "./features/tree/types";
4
+ export * from "./features/main/types";
5
+ export * from "./features/drag-and-drop/types";
6
+ export * from "./features/selection/types";
7
+ export * from "./features/async-data-loader/types";
8
+ export * from "./features/sync-data-loader/types";
9
+ export * from "./features/hotkeys-core/types";
10
+ export * from "./features/search/types";
11
+ export * from "./features/renaming/types";
12
+ export * from "./features/expand-all/types";
13
+ export * from "./features/selection/feature";
14
+ export * from "./features/hotkeys-core/feature";
15
+ export * from "./features/async-data-loader/feature";
16
+ export * from "./features/sync-data-loader/feature";
17
+ export * from "./features/drag-and-drop/feature";
18
+ export * from "./features/search/feature";
19
+ export * from "./features/renaming/feature";
20
+ export * from "./features/expand-all/feature";
21
+ export * from "./utilities/create-on-drop-handler";
22
+ export * from "./utilities/insert-items-at-target";
23
+ export * from "./utilities/remove-items-from-parents";
@@ -0,0 +1,23 @@
1
+ export * from "./types/core";
2
+ export * from "./core/create-tree";
3
+ export * from "./features/tree/types";
4
+ export * from "./features/main/types";
5
+ export * from "./features/drag-and-drop/types";
6
+ export * from "./features/selection/types";
7
+ export * from "./features/async-data-loader/types";
8
+ export * from "./features/sync-data-loader/types";
9
+ export * from "./features/hotkeys-core/types";
10
+ export * from "./features/search/types";
11
+ export * from "./features/renaming/types";
12
+ export * from "./features/expand-all/types";
13
+ export * from "./features/selection/feature";
14
+ export * from "./features/hotkeys-core/feature";
15
+ export * from "./features/async-data-loader/feature";
16
+ export * from "./features/sync-data-loader/feature";
17
+ export * from "./features/drag-and-drop/feature";
18
+ export * from "./features/search/feature";
19
+ export * from "./features/renaming/feature";
20
+ export * from "./features/expand-all/feature";
21
+ export * from "./utilities/create-on-drop-handler";
22
+ export * from "./utilities/insert-items-at-target";
23
+ export * from "./utilities/remove-items-from-parents";
@@ -0,0 +1,21 @@
1
+ import { MainFeatureDef } from "./features/main/types";
2
+ import { DragAndDropFeatureDef } from "./features/drag-and-drop/types";
3
+ export * from ".";
4
+ /** @interface */
5
+ export type MainFeatureConfig = MainFeatureDef["config"];
6
+ /** @interface */
7
+ export type MainFeatureState = MainFeatureDef["state"];
8
+ /** @interface */
9
+ export type MainFeatureTreeInstance = MainFeatureDef["treeInstance"];
10
+ /** @interface */
11
+ export type MainFeatureItemInstance = MainFeatureDef["itemInstance"];
12
+ export type MainFeatureHotkeys = MainFeatureDef["hotkeys"];
13
+ /** @interface */
14
+ export type DragAndDropFeatureConfig<T> = DragAndDropFeatureDef<T>["config"];
15
+ /** @interface */
16
+ export type DragAndDropFeatureState<T> = DragAndDropFeatureDef<T>["state"];
17
+ /** @interface */
18
+ export type DragAndDropFeatureTreeInstance<T> = DragAndDropFeatureDef<T>["treeInstance"];
19
+ /** @interface */
20
+ export type DragAndDropFeatureItemInstance<T> = DragAndDropFeatureDef<T>["itemInstance"];
21
+ export type DragAndDropFeatureHotkeys<T> = DragAndDropFeatureDef<T>["hotkeys"];
@@ -0,0 +1 @@
1
+ export * from ".";
@@ -0,0 +1,67 @@
1
+ import { DragAndDropFeatureDef } from "../features/drag-and-drop/types";
2
+ import { MainFeatureDef } from "../features/main/types";
3
+ import { SelectionFeatureDef } from "../features/selection/types";
4
+ import { TreeFeatureDef } from "../features/tree/types";
5
+ import { HotkeyConfig, HotkeysCoreFeatureDef } from "../features/hotkeys-core/types";
6
+ import { SyncDataLoaderFeatureDef } from "../features/sync-data-loader/types";
7
+ import { AsyncDataLoaderFeatureDef } from "../features/async-data-loader/types";
8
+ import { SearchFeatureDef } from "../features/search/types";
9
+ import { RenamingFeatureDef } from "../features/renaming/types";
10
+ import { ExpandAllFeatureDef } from "../features/expand-all/types";
11
+ export type Updater<T> = T | ((old: T) => T);
12
+ export type SetStateFn<T> = (updaterOrValue: Updater<T>) => void;
13
+ export type FeatureDef = {
14
+ state: object;
15
+ config: object;
16
+ treeInstance: object;
17
+ itemInstance: object;
18
+ hotkeys: string;
19
+ };
20
+ export type EmptyFeatureDef = {
21
+ state: {};
22
+ config: {};
23
+ treeInstance: {};
24
+ itemInstance: {};
25
+ hotkeys: never;
26
+ };
27
+ type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
28
+ export type DefaultFeatures<T> = MainFeatureDef | TreeFeatureDef<T>;
29
+ export type FeatureDefs<T> = MainFeatureDef | TreeFeatureDef<T> | SelectionFeatureDef<T> | DragAndDropFeatureDef<T> | HotkeysCoreFeatureDef<T> | SyncDataLoaderFeatureDef<T> | AsyncDataLoaderFeatureDef<T> | SearchFeatureDef<T> | RenamingFeatureDef<T> | ExpandAllFeatureDef;
30
+ type MergedFeatures<F extends FeatureDef> = {
31
+ state: UnionToIntersection<F["state"]>;
32
+ config: UnionToIntersection<F["config"]>;
33
+ treeInstance: UnionToIntersection<F["treeInstance"]>;
34
+ itemInstance: UnionToIntersection<F["itemInstance"]>;
35
+ hotkeys: F["hotkeys"];
36
+ };
37
+ type TreeStateType<T> = MainFeatureDef["state"] & TreeFeatureDef<T>["state"] & SelectionFeatureDef<T>["state"] & DragAndDropFeatureDef<T>["state"] & HotkeysCoreFeatureDef<T>["state"] & SyncDataLoaderFeatureDef<T>["state"] & AsyncDataLoaderFeatureDef<T>["state"] & SearchFeatureDef<T>["state"] & RenamingFeatureDef<T>["state"] & ExpandAllFeatureDef["state"];
38
+ export interface TreeState<T> extends TreeStateType<T> {
39
+ }
40
+ type TreeConfigType<T> = MainFeatureDef["config"] & TreeFeatureDef<T>["config"] & SelectionFeatureDef<T>["config"] & DragAndDropFeatureDef<T>["config"] & HotkeysCoreFeatureDef<T>["config"] & SyncDataLoaderFeatureDef<T>["config"] & AsyncDataLoaderFeatureDef<T>["config"] & SearchFeatureDef<T>["config"] & RenamingFeatureDef<T>["config"] & ExpandAllFeatureDef["config"];
41
+ export interface TreeConfig<T> extends TreeConfigType<T> {
42
+ }
43
+ type TreeInstanceType<T> = MainFeatureDef["treeInstance"] & TreeFeatureDef<T>["treeInstance"] & SelectionFeatureDef<T>["treeInstance"] & DragAndDropFeatureDef<T>["treeInstance"] & HotkeysCoreFeatureDef<T>["treeInstance"] & SyncDataLoaderFeatureDef<T>["treeInstance"] & AsyncDataLoaderFeatureDef<T>["treeInstance"] & SearchFeatureDef<T>["treeInstance"] & RenamingFeatureDef<T>["treeInstance"] & ExpandAllFeatureDef["treeInstance"];
44
+ export interface TreeInstance<T> extends TreeInstanceType<T> {
45
+ }
46
+ type ItemInstanceType<T> = MainFeatureDef["itemInstance"] & TreeFeatureDef<T>["itemInstance"] & SelectionFeatureDef<T>["itemInstance"] & DragAndDropFeatureDef<T>["itemInstance"] & HotkeysCoreFeatureDef<T>["itemInstance"] & SyncDataLoaderFeatureDef<T>["itemInstance"] & AsyncDataLoaderFeatureDef<T>["itemInstance"] & SearchFeatureDef<T>["itemInstance"] & RenamingFeatureDef<T>["itemInstance"] & ExpandAllFeatureDef["itemInstance"];
47
+ export interface ItemInstance<T> extends ItemInstanceType<T> {
48
+ }
49
+ export type HotkeyName<F extends FeatureDef = FeatureDefs<any>> = MergedFeatures<F>["hotkeys"];
50
+ export type HotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Record<HotkeyName<F>, HotkeyConfig<T>>;
51
+ export type CustomHotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Partial<Record<HotkeyName<F> | `custom${string}`, Partial<HotkeyConfig<T>>>>;
52
+ export type FeatureImplementation<T = any, D extends FeatureDef = any, F extends FeatureDef = EmptyFeatureDef> = {
53
+ key?: string;
54
+ deps?: string[];
55
+ overwrites?: string[];
56
+ stateHandlerNames?: Partial<Record<keyof MergedFeatures<F>["state"], keyof MergedFeatures<F>["config"]>>;
57
+ getInitialState?: (initialState: Partial<MergedFeatures<F>["state"]>, tree: MergedFeatures<F>["treeInstance"]) => Partial<D["state"] & MergedFeatures<F>["state"]>;
58
+ getDefaultConfig?: (defaultConfig: Partial<MergedFeatures<F>["config"]>, tree: MergedFeatures<F>["treeInstance"]) => Partial<D["config"] & MergedFeatures<F>["config"]>;
59
+ createTreeInstance?: (prev: MergedFeatures<F>["treeInstance"], instance: MergedFeatures<F>["treeInstance"]) => D["treeInstance"] & MergedFeatures<F>["treeInstance"];
60
+ createItemInstance?: (prev: MergedFeatures<F>["itemInstance"], item: MergedFeatures<F>["itemInstance"], tree: MergedFeatures<F>["treeInstance"], itemId: string) => D["itemInstance"] & MergedFeatures<F>["itemInstance"];
61
+ onTreeMount?: (instance: MergedFeatures<F>["treeInstance"], treeElement: HTMLElement) => void;
62
+ onTreeUnmount?: (instance: MergedFeatures<F>["treeInstance"], treeElement: HTMLElement) => void;
63
+ onItemMount?: (instance: MergedFeatures<F>["itemInstance"], itemElement: HTMLElement, tree: MergedFeatures<F>["treeInstance"]) => void;
64
+ onItemUnmount?: (instance: MergedFeatures<F>["itemInstance"], itemElement: HTMLElement, tree: MergedFeatures<F>["treeInstance"]) => void;
65
+ hotkeys?: HotkeysConfig<T, D>;
66
+ };
67
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ type TAllKeys<T> = T extends any ? keyof T : never;
2
+ type TIndexValue<T, K extends PropertyKey, D = never> = T extends any ? K extends keyof T ? T[K] : D : never;
3
+ type TPartialKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>> extends infer O ? {
4
+ [P in keyof O]: O[P];
5
+ } : never;
6
+ type TFunction = (...a: any[]) => any;
7
+ type TPrimitives = string | number | boolean | bigint | symbol | Date | TFunction;
8
+ export type TMerged<T> = [T] extends [Array<any>] ? {
9
+ [K in keyof T]: TMerged<T[K]>;
10
+ } : [T] extends [TPrimitives] ? T : [T] extends [object] ? TPartialKeys<{
11
+ [K in TAllKeys<T>]: TMerged<TIndexValue<T, K>>;
12
+ }, never> : T;
13
+ export {};
@@ -0,0 +1 @@
1
+ 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,7 @@
1
+ import { removeItemsFromParents } from "./remove-items-from-parents";
2
+ import { insertItemsAtTarget } from "./insert-items-at-target";
3
+ export const createOnDropHandler = (onChangeChildren) => (items, target) => {
4
+ const itemIds = items.map((item) => item.getId());
5
+ removeItemsFromParents(items, onChangeChildren);
6
+ insertItemsAtTarget(itemIds, target, onChangeChildren);
7
+ };
@@ -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,20 @@
1
+ export const insertItemsAtTarget = (itemIds, target, onChangeChildren) => {
2
+ // add moved items to new common parent, if dropped onto parent
3
+ if (target.childIndex === null) {
4
+ onChangeChildren(target.item, [
5
+ ...target.item.getChildren().map((item) => item.getId()),
6
+ ...itemIds,
7
+ ]);
8
+ // TODO items[0].getTree().rebuildTree();
9
+ return;
10
+ }
11
+ // add moved items to new common parent, if dropped between siblings
12
+ const oldChildren = target.item.getChildren();
13
+ const newChildren = [
14
+ ...oldChildren.slice(0, target.insertionIndex).map((item) => item.getId()),
15
+ ...itemIds,
16
+ ...oldChildren.slice(target.insertionIndex).map((item) => item.getId()),
17
+ ];
18
+ onChangeChildren(target.item, newChildren);
19
+ target.item.getTree().rebuildTree();
20
+ };
@@ -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,13 @@
1
+ export const removeItemsFromParents = (movedItems, onChangeChildren) => {
2
+ var _a;
3
+ // TODO bulk sibling changes together
4
+ for (const item of movedItems) {
5
+ const siblings = (_a = item.getParent()) === null || _a === void 0 ? void 0 : _a.getChildren();
6
+ if (siblings) {
7
+ onChangeChildren(item.getParent(), siblings
8
+ .filter((sibling) => sibling.getId() !== item.getId())
9
+ .map((i) => i.getId()));
10
+ }
11
+ }
12
+ movedItems[0].getTree().rebuildTree();
13
+ };
@@ -0,0 +1,6 @@
1
+ import { TreeState, Updater } from "./types/core";
2
+ export type NoInfer<T> = [T][T extends any ? 0 : never];
3
+ export declare const memo: <D extends readonly any[], R>(fn: (...args_0: D) => R, deps: () => [...D]) => () => R;
4
+ export declare function functionalUpdate<T>(updater: Updater<T>, input: T): T;
5
+ export declare function makeStateUpdater<K extends keyof TreeState<any>>(key: K, instance: unknown): (updater: Updater<TreeState<any>[K]>) => void;
6
+ export declare const poll: (fn: () => boolean, interval?: number, timeout?: number) => Promise<void>;
@@ -0,0 +1,46 @@
1
+ export const memo = (fn, deps) => {
2
+ let value;
3
+ let oldDeps = null;
4
+ return () => {
5
+ const newDeps = deps();
6
+ if (!value) {
7
+ value = fn(...newDeps);
8
+ oldDeps = newDeps;
9
+ return value;
10
+ }
11
+ const match = oldDeps &&
12
+ oldDeps.length === newDeps.length &&
13
+ !oldDeps.some((dep, i) => dep !== newDeps[i]);
14
+ if (match) {
15
+ return value;
16
+ }
17
+ value = fn(...newDeps);
18
+ oldDeps = newDeps;
19
+ return value;
20
+ };
21
+ };
22
+ export function functionalUpdate(updater, input) {
23
+ return typeof updater === "function"
24
+ ? updater(input)
25
+ : updater;
26
+ }
27
+ export function makeStateUpdater(key, instance) {
28
+ return (updater) => {
29
+ instance.setState((old) => {
30
+ return Object.assign(Object.assign({}, old), { [key]: functionalUpdate(updater, old[key]) });
31
+ });
32
+ };
33
+ }
34
+ export const poll = (fn, interval = 100, timeout = 1000) => new Promise((resolve) => {
35
+ let clear;
36
+ const i = setInterval(() => {
37
+ if (fn()) {
38
+ resolve();
39
+ clearInterval(i);
40
+ clearTimeout(clear);
41
+ }
42
+ }, interval);
43
+ clear = setTimeout(() => {
44
+ clearInterval(i);
45
+ }, timeout);
46
+ });
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@headless-tree/core",
3
+ "version": "0.0.0-20230802230636",
4
+ "main": "lib/cjs/index.js",
5
+ "module": "lib/esm/index.js",
6
+ "types": "lib/esm/index.d.ts",
7
+ "sideEffects": false,
8
+ "scripts": {
9
+ "build:cjs": "tsc -m commonjs --outDir lib/cjs",
10
+ "build:esm": "tsc",
11
+ "start": "tsc -w"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git@github.com:lukasbach/headless-tree.git",
16
+ "directory": "packages/core"
17
+ },
18
+ "author": "Lukas Bach <npm@lukasbach.com>",
19
+ "license": "MIT",
20
+ "devDependencies": {
21
+ "typescript": "^5.0.4"
22
+ }
23
+ }