@headless-tree/core 0.0.6 → 0.0.8

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 (38) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/lib/cjs/core/create-tree.js +27 -6
  3. package/lib/cjs/features/async-data-loader/feature.js +0 -1
  4. package/lib/cjs/features/drag-and-drop/feature.js +29 -1
  5. package/lib/cjs/features/drag-and-drop/types.d.ts +7 -0
  6. package/lib/cjs/features/expand-all/feature.js +0 -1
  7. package/lib/cjs/features/hotkeys-core/feature.js +0 -1
  8. package/lib/cjs/features/renaming/feature.js +0 -2
  9. package/lib/cjs/features/search/feature.js +0 -1
  10. package/lib/cjs/features/selection/feature.js +1 -2
  11. package/lib/cjs/features/sync-data-loader/feature.js +0 -1
  12. package/lib/cjs/features/tree/feature.js +0 -1
  13. package/lib/cjs/types/core.d.ts +2 -1
  14. package/lib/esm/core/create-tree.js +27 -6
  15. package/lib/esm/features/async-data-loader/feature.js +0 -1
  16. package/lib/esm/features/drag-and-drop/feature.js +29 -1
  17. package/lib/esm/features/drag-and-drop/types.d.ts +7 -0
  18. package/lib/esm/features/expand-all/feature.js +0 -1
  19. package/lib/esm/features/hotkeys-core/feature.js +0 -1
  20. package/lib/esm/features/renaming/feature.js +0 -2
  21. package/lib/esm/features/search/feature.js +0 -1
  22. package/lib/esm/features/selection/feature.js +1 -2
  23. package/lib/esm/features/sync-data-loader/feature.js +0 -1
  24. package/lib/esm/features/tree/feature.js +0 -1
  25. package/lib/esm/types/core.d.ts +2 -1
  26. package/package.json +1 -1
  27. package/src/core/create-tree.ts +31 -2
  28. package/src/features/async-data-loader/feature.ts +0 -1
  29. package/src/features/drag-and-drop/feature.ts +41 -2
  30. package/src/features/drag-and-drop/types.ts +8 -0
  31. package/src/features/expand-all/feature.ts +0 -1
  32. package/src/features/hotkeys-core/feature.ts +0 -1
  33. package/src/features/renaming/feature.ts +0 -2
  34. package/src/features/search/feature.ts +0 -1
  35. package/src/features/selection/feature.ts +1 -2
  36. package/src/features/sync-data-loader/feature.ts +0 -1
  37. package/src/features/tree/feature.ts +0 -1
  38. package/src/types/core.ts +2 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @headless-tree/core
2
2
 
3
+ ## 0.0.8
4
+
5
+ ### Patch Changes
6
+
7
+ - 076dfc5: dev release
8
+
9
+ ## 0.0.7
10
+
11
+ ### Patch Changes
12
+
13
+ - 6ec53b3: dev release
14
+
3
15
  ## 0.0.6
4
16
 
5
17
  ### Patch Changes
@@ -10,11 +10,33 @@ const buildItemInstance = (features, tree, itemId) => {
10
10
  }
11
11
  return itemInstance;
12
12
  };
13
+ const verifyFeatures = (features) => {
14
+ var _a;
15
+ const loadedFeatures = features === null || features === void 0 ? void 0 : features.map((feature) => feature.key);
16
+ for (const feature of features !== null && features !== void 0 ? features : []) {
17
+ const missingDependency = (_a = feature.deps) === null || _a === void 0 ? void 0 : _a.find((dep) => !(loadedFeatures === null || loadedFeatures === void 0 ? void 0 : loadedFeatures.includes(dep)));
18
+ if (missingDependency) {
19
+ throw new Error(`${feature.key} needs ${missingDependency}`);
20
+ }
21
+ }
22
+ };
23
+ const compareFeatures = (feature1, feature2) => {
24
+ var _a;
25
+ if (feature2.key && ((_a = feature1.overwrites) === null || _a === void 0 ? void 0 : _a.includes(feature2.key))) {
26
+ return 1;
27
+ }
28
+ return -1;
29
+ };
30
+ const sortFeatures = (features = []) => features.sort(compareFeatures);
13
31
  const createTree = (initialConfig) => {
14
- var _a, _b, _c, _d, _e, _f;
32
+ var _a, _b, _c, _d, _e;
15
33
  const treeInstance = {};
16
- const additionalFeatures = [feature_1.treeFeature, ...((_a = initialConfig.features) !== null && _a !== void 0 ? _a : [])];
17
- let state = additionalFeatures.reduce((acc, feature) => { var _a, _b; return (_b = (_a = feature.getInitialState) === null || _a === void 0 ? void 0 : _a.call(feature, acc, treeInstance)) !== null && _b !== void 0 ? _b : acc; }, (_c = (_b = initialConfig.initialState) !== null && _b !== void 0 ? _b : initialConfig.state) !== null && _c !== void 0 ? _c : {});
34
+ const additionalFeatures = [
35
+ feature_1.treeFeature,
36
+ ...sortFeatures(initialConfig.features),
37
+ ];
38
+ verifyFeatures(additionalFeatures);
39
+ let state = additionalFeatures.reduce((acc, feature) => { var _a, _b; return (_b = (_a = feature.getInitialState) === null || _a === void 0 ? void 0 : _a.call(feature, acc, treeInstance)) !== null && _b !== void 0 ? _b : acc; }, (_b = (_a = initialConfig.initialState) !== null && _a !== void 0 ? _a : initialConfig.state) !== null && _b !== void 0 ? _b : {});
18
40
  let config = additionalFeatures.reduce((acc, feature) => { var _a, _b; return (_b = (_a = feature.getDefaultConfig) === null || _a === void 0 ? void 0 : _a.call(feature, acc, treeInstance)) !== null && _b !== void 0 ? _b : acc; }, initialConfig);
19
41
  const stateHandlerNames = additionalFeatures.reduce((acc, feature) => (Object.assign(Object.assign({}, acc), feature.stateHandlerNames)), {});
20
42
  let treeElement;
@@ -105,11 +127,10 @@ const createTree = (initialConfig) => {
105
127
  // eslint-disable-next-line no-return-assign
106
128
  getDataRef: () => { var _a; return ((_a = itemDataRefs[itemId]) !== null && _a !== void 0 ? _a : (itemDataRefs[itemId] = { current: {} })); }, getItemMeta: () => itemMetaMap[itemId] })),
107
129
  };
108
- // todo sort features
109
130
  const features = [mainFeature, ...additionalFeatures];
110
131
  for (const feature of features) {
111
- Object.assign(treeInstance, (_e = (_d = feature.createTreeInstance) === null || _d === void 0 ? void 0 : _d.call(feature, Object.assign({}, treeInstance), treeInstance)) !== null && _e !== void 0 ? _e : {});
112
- Object.assign(hotkeyPresets, (_f = feature.hotkeys) !== null && _f !== void 0 ? _f : {});
132
+ Object.assign(treeInstance, (_d = (_c = feature.createTreeInstance) === null || _c === void 0 ? void 0 : _c.call(feature, Object.assign({}, treeInstance), treeInstance)) !== null && _d !== void 0 ? _d : {});
133
+ Object.assign(hotkeyPresets, (_e = feature.hotkeys) !== null && _e !== void 0 ? _e : {});
113
134
  }
114
135
  rebuildItemMeta(mainFeature);
115
136
  return treeInstance;
@@ -4,7 +4,6 @@ exports.asyncDataLoaderFeature = void 0;
4
4
  const utils_1 = require("../../utils");
5
5
  exports.asyncDataLoaderFeature = {
6
6
  key: "async-data-loader",
7
- dependingFeatures: ["main"],
8
7
  getInitialState: (initialState) => (Object.assign({ loadingItems: [] }, initialState)),
9
8
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setLoadingItems: (0, utils_1.makeStateUpdater)("loadingItems", tree) }, defaultConfig)),
10
9
  stateHandlerNames: {
@@ -5,7 +5,7 @@ const utils_1 = require("./utils");
5
5
  const utils_2 = require("../../utils");
6
6
  exports.dragAndDropFeature = {
7
7
  key: "dragAndDrop",
8
- dependingFeatures: ["main", "tree", "selection"],
8
+ deps: ["selection"],
9
9
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ canDrop: (_, target) => target.item.isFolder(), canDropForeignDragObject: () => false, setDndState: (0, utils_2.makeStateUpdater)("dnd", tree) }, defaultConfig)),
10
10
  stateHandlerNames: {
11
11
  dnd: "setDndState",
@@ -13,6 +13,34 @@ exports.dragAndDropFeature = {
13
13
  createTreeInstance: (prev, tree) => (Object.assign(Object.assign({}, prev), { getDropTarget: () => {
14
14
  var _a, _b;
15
15
  return (_b = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.dragTarget) !== null && _b !== void 0 ? _b : null;
16
+ }, getDragLineData: () => {
17
+ var _a, _b, _c, _d, _e;
18
+ const target = tree.getDropTarget();
19
+ const intend = ((_a = target === null || target === void 0 ? void 0 : target.item.getItemMeta().level) !== null && _a !== void 0 ? _a : 0) + 1;
20
+ if (!target || target.childIndex === null)
21
+ return null;
22
+ const children = target.item.getChildren();
23
+ if (target.childIndex === children.length) {
24
+ const bb = (_c = (_b = children[target.childIndex - 1]) === null || _b === void 0 ? void 0 : _b.getElement()) === null || _c === void 0 ? void 0 : _c.getBoundingClientRect();
25
+ if (bb) {
26
+ return {
27
+ intend,
28
+ top: bb.bottom,
29
+ left: bb.left,
30
+ right: bb.right,
31
+ };
32
+ }
33
+ }
34
+ const bb = (_e = (_d = children[target.childIndex]) === null || _d === void 0 ? void 0 : _d.getElement()) === null || _e === void 0 ? void 0 : _e.getBoundingClientRect();
35
+ if (bb) {
36
+ return {
37
+ intend,
38
+ top: bb.top,
39
+ left: bb.left,
40
+ right: bb.right,
41
+ };
42
+ }
43
+ return null;
16
44
  } })),
17
45
  createItemInstance: (prev, item, tree) => (Object.assign(Object.assign({}, prev), { getProps: () => {
18
46
  var _a, _b, _c;
@@ -7,6 +7,12 @@ export type DndState<T> = {
7
7
  draggingOverItem?: ItemInstance<T>;
8
8
  dragTarget?: DropTarget<T>;
9
9
  };
10
+ export type DragLineData = {
11
+ intend: number;
12
+ top: number;
13
+ left: number;
14
+ right: number;
15
+ };
10
16
  export type DropTarget<T> = {
11
17
  item: ItemInstance<T>;
12
18
  childIndex: number;
@@ -51,6 +57,7 @@ export type DragAndDropFeatureDef<T> = {
51
57
  };
52
58
  treeInstance: {
53
59
  getDropTarget: () => DropTarget<T> | null;
60
+ getDragLineData: () => DragLineData | null;
54
61
  };
55
62
  itemInstance: {
56
63
  isDropTarget: () => boolean;
@@ -13,7 +13,6 @@ exports.expandAllFeature = void 0;
13
13
  const utils_1 = require("../../utils");
14
14
  exports.expandAllFeature = {
15
15
  key: "expand-all",
16
- dependingFeatures: ["main", "tree"],
17
16
  createTreeInstance: (prev, tree) => (Object.assign(Object.assign({}, prev), { expandAll: (cancelToken) => __awaiter(void 0, void 0, void 0, function* () {
18
17
  yield Promise.all(tree.getItems().map((item) => item.expandAll(cancelToken)));
19
18
  }), collapseAll: () => {
@@ -20,7 +20,6 @@ const findHotkeyMatch = (pressedKeys, tree, config1, config2) => {
20
20
  };
21
21
  exports.hotkeysCoreFeature = {
22
22
  key: "hotkeys-core",
23
- dependingFeatures: ["main", "tree"],
24
23
  onTreeMount: (tree, element) => {
25
24
  const data = tree.getDataRef();
26
25
  const keydown = (e) => {
@@ -4,14 +4,12 @@ exports.renamingFeature = void 0;
4
4
  const utils_1 = require("../../utils");
5
5
  exports.renamingFeature = {
6
6
  key: "renaming",
7
- dependingFeatures: ["main", "tree"],
8
7
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setRenamingItem: (0, utils_1.makeStateUpdater)("renamingItem", tree), setRenamingValue: (0, utils_1.makeStateUpdater)("renamingValue", tree), canRename: () => true }, defaultConfig)),
9
8
  stateHandlerNames: {
10
9
  renamingItem: "setRenamingItem",
11
10
  renamingValue: "setRenamingValue",
12
11
  },
13
12
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { startRenamingItem: (itemId) => {
14
- const config = instance.getConfig();
15
13
  const item = instance.getItemInstance(itemId);
16
14
  if (!item.canRename()) {
17
15
  return;
@@ -4,7 +4,6 @@ exports.searchFeature = void 0;
4
4
  const utils_1 = require("../../utils");
5
5
  exports.searchFeature = {
6
6
  key: "search",
7
- dependingFeatures: ["main", "tree"],
8
7
  getInitialState: (initialState) => (Object.assign({ search: null }, initialState)),
9
8
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setSearch: (0, utils_1.makeStateUpdater)("search", tree), isSearchMatchingItem: (search, item) => search.length > 0 &&
10
9
  item.getItemName().toLowerCase().includes(search.toLowerCase()) }, defaultConfig)),
@@ -4,7 +4,6 @@ exports.selectionFeature = void 0;
4
4
  const utils_1 = require("../../utils");
5
5
  exports.selectionFeature = {
6
6
  key: "selection",
7
- dependingFeatures: ["main", "tree"],
8
7
  getInitialState: (initialState) => (Object.assign({ selectedItems: [] }, initialState)),
9
8
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setSelectedItems: (0, utils_1.makeStateUpdater)("selectedItems", tree) }, defaultConfig)),
10
9
  stateHandlerNames: {
@@ -36,7 +35,7 @@ exports.selectionFeature = {
36
35
  const newSelectedItems = tree
37
36
  .getItems()
38
37
  .slice(a, b + 1)
39
- .map((item) => item.getItemMeta().itemId);
38
+ .map((treeItem) => treeItem.getItemMeta().itemId);
40
39
  if (!ctrl) {
41
40
  tree.setSelectedItems(newSelectedItems);
42
41
  return;
@@ -4,7 +4,6 @@ exports.syncDataLoaderFeature = void 0;
4
4
  const utils_1 = require("../../utils");
5
5
  exports.syncDataLoaderFeature = {
6
6
  key: "sync-data-loader",
7
- dependingFeatures: ["main"],
8
7
  getInitialState: (initialState) => (Object.assign({ loadingItems: [] }, initialState)),
9
8
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setLoadingItems: (0, utils_1.makeStateUpdater)("loadingItems", tree) }, defaultConfig)),
10
9
  stateHandlerNames: {
@@ -13,7 +13,6 @@ exports.treeFeature = void 0;
13
13
  const utils_1 = require("../../utils");
14
14
  exports.treeFeature = {
15
15
  key: "tree",
16
- dependingFeatures: ["main"],
17
16
  getInitialState: (initialState) => (Object.assign({ expandedItems: [], focusedItem: null }, initialState)),
18
17
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setExpandedItems: (0, utils_1.makeStateUpdater)("expandedItems", tree), setFocusedItem: (0, utils_1.makeStateUpdater)("focusedItem", tree) }, defaultConfig)),
19
18
  stateHandlerNames: {
@@ -51,7 +51,8 @@ export type HotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Record<Hot
51
51
  export type CustomHotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Partial<Record<HotkeyName<F> | `custom${string}`, Partial<HotkeyConfig<T>>>>;
52
52
  export type FeatureImplementation<T = any, D extends FeatureDef = any, F extends FeatureDef = EmptyFeatureDef> = {
53
53
  key?: string;
54
- dependingFeatures?: string[];
54
+ deps?: string[];
55
+ overwrites?: string[];
55
56
  stateHandlerNames?: Partial<Record<keyof MergedFeatures<F>["state"], keyof MergedFeatures<F>["config"]>>;
56
57
  getInitialState?: (initialState: Partial<MergedFeatures<F>["state"]>, tree: MergedFeatures<F>["treeInstance"]) => Partial<D["state"] & MergedFeatures<F>["state"]>;
57
58
  getDefaultConfig?: (defaultConfig: Partial<MergedFeatures<F>["config"]>, tree: MergedFeatures<F>["treeInstance"]) => Partial<D["config"] & MergedFeatures<F>["config"]>;
@@ -7,11 +7,33 @@ const buildItemInstance = (features, tree, itemId) => {
7
7
  }
8
8
  return itemInstance;
9
9
  };
10
+ const verifyFeatures = (features) => {
11
+ var _a;
12
+ const loadedFeatures = features === null || features === void 0 ? void 0 : features.map((feature) => feature.key);
13
+ for (const feature of features !== null && features !== void 0 ? features : []) {
14
+ const missingDependency = (_a = feature.deps) === null || _a === void 0 ? void 0 : _a.find((dep) => !(loadedFeatures === null || loadedFeatures === void 0 ? void 0 : loadedFeatures.includes(dep)));
15
+ if (missingDependency) {
16
+ throw new Error(`${feature.key} needs ${missingDependency}`);
17
+ }
18
+ }
19
+ };
20
+ const compareFeatures = (feature1, feature2) => {
21
+ var _a;
22
+ if (feature2.key && ((_a = feature1.overwrites) === null || _a === void 0 ? void 0 : _a.includes(feature2.key))) {
23
+ return 1;
24
+ }
25
+ return -1;
26
+ };
27
+ const sortFeatures = (features = []) => features.sort(compareFeatures);
10
28
  export const createTree = (initialConfig) => {
11
- var _a, _b, _c, _d, _e, _f;
29
+ var _a, _b, _c, _d, _e;
12
30
  const treeInstance = {};
13
- const additionalFeatures = [treeFeature, ...((_a = initialConfig.features) !== null && _a !== void 0 ? _a : [])];
14
- let state = additionalFeatures.reduce((acc, feature) => { var _a, _b; return (_b = (_a = feature.getInitialState) === null || _a === void 0 ? void 0 : _a.call(feature, acc, treeInstance)) !== null && _b !== void 0 ? _b : acc; }, (_c = (_b = initialConfig.initialState) !== null && _b !== void 0 ? _b : initialConfig.state) !== null && _c !== void 0 ? _c : {});
31
+ const additionalFeatures = [
32
+ treeFeature,
33
+ ...sortFeatures(initialConfig.features),
34
+ ];
35
+ verifyFeatures(additionalFeatures);
36
+ let state = additionalFeatures.reduce((acc, feature) => { var _a, _b; return (_b = (_a = feature.getInitialState) === null || _a === void 0 ? void 0 : _a.call(feature, acc, treeInstance)) !== null && _b !== void 0 ? _b : acc; }, (_b = (_a = initialConfig.initialState) !== null && _a !== void 0 ? _a : initialConfig.state) !== null && _b !== void 0 ? _b : {});
15
37
  let config = additionalFeatures.reduce((acc, feature) => { var _a, _b; return (_b = (_a = feature.getDefaultConfig) === null || _a === void 0 ? void 0 : _a.call(feature, acc, treeInstance)) !== null && _b !== void 0 ? _b : acc; }, initialConfig);
16
38
  const stateHandlerNames = additionalFeatures.reduce((acc, feature) => (Object.assign(Object.assign({}, acc), feature.stateHandlerNames)), {});
17
39
  let treeElement;
@@ -102,11 +124,10 @@ export const createTree = (initialConfig) => {
102
124
  // eslint-disable-next-line no-return-assign
103
125
  getDataRef: () => { var _a; return ((_a = itemDataRefs[itemId]) !== null && _a !== void 0 ? _a : (itemDataRefs[itemId] = { current: {} })); }, getItemMeta: () => itemMetaMap[itemId] })),
104
126
  };
105
- // todo sort features
106
127
  const features = [mainFeature, ...additionalFeatures];
107
128
  for (const feature of features) {
108
- Object.assign(treeInstance, (_e = (_d = feature.createTreeInstance) === null || _d === void 0 ? void 0 : _d.call(feature, Object.assign({}, treeInstance), treeInstance)) !== null && _e !== void 0 ? _e : {});
109
- Object.assign(hotkeyPresets, (_f = feature.hotkeys) !== null && _f !== void 0 ? _f : {});
129
+ Object.assign(treeInstance, (_d = (_c = feature.createTreeInstance) === null || _c === void 0 ? void 0 : _c.call(feature, Object.assign({}, treeInstance), treeInstance)) !== null && _d !== void 0 ? _d : {});
130
+ Object.assign(hotkeyPresets, (_e = feature.hotkeys) !== null && _e !== void 0 ? _e : {});
110
131
  }
111
132
  rebuildItemMeta(mainFeature);
112
133
  return treeInstance;
@@ -1,7 +1,6 @@
1
1
  import { makeStateUpdater } from "../../utils";
2
2
  export const asyncDataLoaderFeature = {
3
3
  key: "async-data-loader",
4
- dependingFeatures: ["main"],
5
4
  getInitialState: (initialState) => (Object.assign({ loadingItems: [] }, initialState)),
6
5
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setLoadingItems: makeStateUpdater("loadingItems", tree) }, defaultConfig)),
7
6
  stateHandlerNames: {
@@ -2,7 +2,7 @@ import { canDrop, getDragCode, getDropTarget } from "./utils";
2
2
  import { makeStateUpdater } from "../../utils";
3
3
  export const dragAndDropFeature = {
4
4
  key: "dragAndDrop",
5
- dependingFeatures: ["main", "tree", "selection"],
5
+ deps: ["selection"],
6
6
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ canDrop: (_, target) => target.item.isFolder(), canDropForeignDragObject: () => false, setDndState: makeStateUpdater("dnd", tree) }, defaultConfig)),
7
7
  stateHandlerNames: {
8
8
  dnd: "setDndState",
@@ -10,6 +10,34 @@ export const dragAndDropFeature = {
10
10
  createTreeInstance: (prev, tree) => (Object.assign(Object.assign({}, prev), { getDropTarget: () => {
11
11
  var _a, _b;
12
12
  return (_b = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.dragTarget) !== null && _b !== void 0 ? _b : null;
13
+ }, getDragLineData: () => {
14
+ var _a, _b, _c, _d, _e;
15
+ const target = tree.getDropTarget();
16
+ const intend = ((_a = target === null || target === void 0 ? void 0 : target.item.getItemMeta().level) !== null && _a !== void 0 ? _a : 0) + 1;
17
+ if (!target || target.childIndex === null)
18
+ return null;
19
+ const children = target.item.getChildren();
20
+ if (target.childIndex === children.length) {
21
+ const bb = (_c = (_b = children[target.childIndex - 1]) === null || _b === void 0 ? void 0 : _b.getElement()) === null || _c === void 0 ? void 0 : _c.getBoundingClientRect();
22
+ if (bb) {
23
+ return {
24
+ intend,
25
+ top: bb.bottom,
26
+ left: bb.left,
27
+ right: bb.right,
28
+ };
29
+ }
30
+ }
31
+ const bb = (_e = (_d = children[target.childIndex]) === null || _d === void 0 ? void 0 : _d.getElement()) === null || _e === void 0 ? void 0 : _e.getBoundingClientRect();
32
+ if (bb) {
33
+ return {
34
+ intend,
35
+ top: bb.top,
36
+ left: bb.left,
37
+ right: bb.right,
38
+ };
39
+ }
40
+ return null;
13
41
  } })),
14
42
  createItemInstance: (prev, item, tree) => (Object.assign(Object.assign({}, prev), { getProps: () => {
15
43
  var _a, _b, _c;
@@ -7,6 +7,12 @@ export type DndState<T> = {
7
7
  draggingOverItem?: ItemInstance<T>;
8
8
  dragTarget?: DropTarget<T>;
9
9
  };
10
+ export type DragLineData = {
11
+ intend: number;
12
+ top: number;
13
+ left: number;
14
+ right: number;
15
+ };
10
16
  export type DropTarget<T> = {
11
17
  item: ItemInstance<T>;
12
18
  childIndex: number;
@@ -51,6 +57,7 @@ export type DragAndDropFeatureDef<T> = {
51
57
  };
52
58
  treeInstance: {
53
59
  getDropTarget: () => DropTarget<T> | null;
60
+ getDragLineData: () => DragLineData | null;
54
61
  };
55
62
  itemInstance: {
56
63
  isDropTarget: () => boolean;
@@ -10,7 +10,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { poll } from "../../utils";
11
11
  export const expandAllFeature = {
12
12
  key: "expand-all",
13
- dependingFeatures: ["main", "tree"],
14
13
  createTreeInstance: (prev, tree) => (Object.assign(Object.assign({}, prev), { expandAll: (cancelToken) => __awaiter(void 0, void 0, void 0, function* () {
15
14
  yield Promise.all(tree.getItems().map((item) => item.expandAll(cancelToken)));
16
15
  }), collapseAll: () => {
@@ -17,7 +17,6 @@ const findHotkeyMatch = (pressedKeys, tree, config1, config2) => {
17
17
  };
18
18
  export const hotkeysCoreFeature = {
19
19
  key: "hotkeys-core",
20
- dependingFeatures: ["main", "tree"],
21
20
  onTreeMount: (tree, element) => {
22
21
  const data = tree.getDataRef();
23
22
  const keydown = (e) => {
@@ -1,14 +1,12 @@
1
1
  import { makeStateUpdater } from "../../utils";
2
2
  export const renamingFeature = {
3
3
  key: "renaming",
4
- dependingFeatures: ["main", "tree"],
5
4
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setRenamingItem: makeStateUpdater("renamingItem", tree), setRenamingValue: makeStateUpdater("renamingValue", tree), canRename: () => true }, defaultConfig)),
6
5
  stateHandlerNames: {
7
6
  renamingItem: "setRenamingItem",
8
7
  renamingValue: "setRenamingValue",
9
8
  },
10
9
  createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { startRenamingItem: (itemId) => {
11
- const config = instance.getConfig();
12
10
  const item = instance.getItemInstance(itemId);
13
11
  if (!item.canRename()) {
14
12
  return;
@@ -1,7 +1,6 @@
1
1
  import { makeStateUpdater, memo } from "../../utils";
2
2
  export const searchFeature = {
3
3
  key: "search",
4
- dependingFeatures: ["main", "tree"],
5
4
  getInitialState: (initialState) => (Object.assign({ search: null }, initialState)),
6
5
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setSearch: makeStateUpdater("search", tree), isSearchMatchingItem: (search, item) => search.length > 0 &&
7
6
  item.getItemName().toLowerCase().includes(search.toLowerCase()) }, defaultConfig)),
@@ -1,7 +1,6 @@
1
1
  import { makeStateUpdater } from "../../utils";
2
2
  export const selectionFeature = {
3
3
  key: "selection",
4
- dependingFeatures: ["main", "tree"],
5
4
  getInitialState: (initialState) => (Object.assign({ selectedItems: [] }, initialState)),
6
5
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setSelectedItems: makeStateUpdater("selectedItems", tree) }, defaultConfig)),
7
6
  stateHandlerNames: {
@@ -33,7 +32,7 @@ export const selectionFeature = {
33
32
  const newSelectedItems = tree
34
33
  .getItems()
35
34
  .slice(a, b + 1)
36
- .map((item) => item.getItemMeta().itemId);
35
+ .map((treeItem) => treeItem.getItemMeta().itemId);
37
36
  if (!ctrl) {
38
37
  tree.setSelectedItems(newSelectedItems);
39
38
  return;
@@ -1,7 +1,6 @@
1
1
  import { makeStateUpdater } from "../../utils";
2
2
  export const syncDataLoaderFeature = {
3
3
  key: "sync-data-loader",
4
- dependingFeatures: ["main"],
5
4
  getInitialState: (initialState) => (Object.assign({ loadingItems: [] }, initialState)),
6
5
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setLoadingItems: makeStateUpdater("loadingItems", tree) }, defaultConfig)),
7
6
  stateHandlerNames: {
@@ -10,7 +10,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { makeStateUpdater, memo, poll } from "../../utils";
11
11
  export const treeFeature = {
12
12
  key: "tree",
13
- dependingFeatures: ["main"],
14
13
  getInitialState: (initialState) => (Object.assign({ expandedItems: [], focusedItem: null }, initialState)),
15
14
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setExpandedItems: makeStateUpdater("expandedItems", tree), setFocusedItem: makeStateUpdater("focusedItem", tree) }, defaultConfig)),
16
15
  stateHandlerNames: {
@@ -51,7 +51,8 @@ export type HotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Record<Hot
51
51
  export type CustomHotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Partial<Record<HotkeyName<F> | `custom${string}`, Partial<HotkeyConfig<T>>>>;
52
52
  export type FeatureImplementation<T = any, D extends FeatureDef = any, F extends FeatureDef = EmptyFeatureDef> = {
53
53
  key?: string;
54
- dependingFeatures?: string[];
54
+ deps?: string[];
55
+ overwrites?: string[];
55
56
  stateHandlerNames?: Partial<Record<keyof MergedFeatures<F>["state"], keyof MergedFeatures<F>["config"]>>;
56
57
  getInitialState?: (initialState: Partial<MergedFeatures<F>["state"]>, tree: MergedFeatures<F>["treeInstance"]) => Partial<D["state"] & MergedFeatures<F>["state"]>;
57
58
  getDefaultConfig?: (defaultConfig: Partial<MergedFeatures<F>["config"]>, tree: MergedFeatures<F>["treeInstance"]) => Partial<D["config"] & MergedFeatures<F>["config"]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@headless-tree/core",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "main": "lib/cjs/index.js",
5
5
  "module": "lib/esm/index.js",
6
6
  "types": "lib/esm/index.d.ts",
@@ -30,12 +30,42 @@ const buildItemInstance = (
30
30
  return itemInstance;
31
31
  };
32
32
 
33
+ const verifyFeatures = (features: FeatureImplementation[] | undefined) => {
34
+ const loadedFeatures = features?.map((feature) => feature.key);
35
+ for (const feature of features ?? []) {
36
+ const missingDependency = feature.deps?.find(
37
+ (dep) => !loadedFeatures?.includes(dep)
38
+ );
39
+ if (missingDependency) {
40
+ throw new Error(`${feature.key} needs ${missingDependency}`);
41
+ }
42
+ }
43
+ };
44
+
45
+ const compareFeatures = (
46
+ feature1: FeatureImplementation,
47
+ feature2: FeatureImplementation
48
+ ) => {
49
+ if (feature2.key && feature1.overwrites?.includes(feature2.key)) {
50
+ return 1;
51
+ }
52
+ return -1;
53
+ };
54
+
55
+ const sortFeatures = (features: FeatureImplementation[] = []) =>
56
+ features.sort(compareFeatures);
57
+
33
58
  export const createTree = <T>(
34
59
  initialConfig: TreeConfig<T>
35
60
  ): TreeInstance<T> => {
36
61
  const treeInstance: TreeInstance<T> = {} as any;
37
62
 
38
- const additionalFeatures = [treeFeature, ...(initialConfig.features ?? [])];
63
+ const additionalFeatures = [
64
+ treeFeature,
65
+ ...sortFeatures(initialConfig.features),
66
+ ];
67
+ verifyFeatures(additionalFeatures);
68
+
39
69
  let state = additionalFeatures.reduce(
40
70
  (acc, feature) => feature.getInitialState?.(acc, treeInstance) ?? acc,
41
71
  initialConfig.initialState ?? initialConfig.state ?? {}
@@ -182,7 +212,6 @@ export const createTree = <T>(
182
212
  }),
183
213
  };
184
214
 
185
- // todo sort features
186
215
  const features = [mainFeature, ...additionalFeatures];
187
216
 
188
217
  for (const feature of features) {
@@ -10,7 +10,6 @@ export const asyncDataLoaderFeature: FeatureImplementation<
10
10
  MainFeatureDef | TreeFeatureDef<any> | AsyncDataLoaderFeatureDef<any>
11
11
  > = {
12
12
  key: "async-data-loader",
13
- dependingFeatures: ["main"],
14
13
 
15
14
  getInitialState: (initialState) => ({
16
15
  loadingItems: [],
@@ -1,5 +1,5 @@
1
1
  import { FeatureDefs, FeatureImplementation } from "../../types/core";
2
- import { DndDataRef, DragAndDropFeatureDef } from "./types";
2
+ import { DndDataRef, DragAndDropFeatureDef, DragLineData } from "./types";
3
3
  import { canDrop, getDragCode, getDropTarget } from "./utils";
4
4
  import { makeStateUpdater } from "../../utils";
5
5
 
@@ -9,7 +9,7 @@ export const dragAndDropFeature: FeatureImplementation<
9
9
  FeatureDefs<any>
10
10
  > = {
11
11
  key: "dragAndDrop",
12
- dependingFeatures: ["main", "tree", "selection"],
12
+ deps: ["selection"],
13
13
 
14
14
  getDefaultConfig: (defaultConfig, tree) => ({
15
15
  canDrop: (_, target) => target.item.isFolder(),
@@ -28,6 +28,45 @@ export const dragAndDropFeature: FeatureImplementation<
28
28
  getDropTarget: () => {
29
29
  return tree.getState().dnd?.dragTarget ?? null;
30
30
  },
31
+
32
+ getDragLineData: (): DragLineData | null => {
33
+ const target = tree.getDropTarget();
34
+ const intend = (target?.item.getItemMeta().level ?? 0) + 1;
35
+
36
+ if (!target || target.childIndex === null) return null;
37
+
38
+ const children = target.item.getChildren();
39
+
40
+ if (target.childIndex === children.length) {
41
+ const bb = children[target.childIndex - 1]
42
+ ?.getElement()
43
+ ?.getBoundingClientRect();
44
+
45
+ if (bb) {
46
+ return {
47
+ intend,
48
+ top: bb.bottom,
49
+ left: bb.left,
50
+ right: bb.right,
51
+ };
52
+ }
53
+ }
54
+
55
+ const bb = children[target.childIndex]
56
+ ?.getElement()
57
+ ?.getBoundingClientRect();
58
+
59
+ if (bb) {
60
+ return {
61
+ intend,
62
+ top: bb.top,
63
+ left: bb.left,
64
+ right: bb.right,
65
+ };
66
+ }
67
+
68
+ return null;
69
+ },
31
70
  }),
32
71
 
33
72
  createItemInstance: (prev, item, tree) => ({
@@ -10,6 +10,13 @@ export type DndState<T> = {
10
10
  dragTarget?: DropTarget<T>;
11
11
  };
12
12
 
13
+ export type DragLineData = {
14
+ intend: number;
15
+ top: number;
16
+ left: number;
17
+ right: number;
18
+ };
19
+
13
20
  export type DropTarget<T> =
14
21
  | {
15
22
  item: ItemInstance<T>;
@@ -70,6 +77,7 @@ export type DragAndDropFeatureDef<T> = {
70
77
  };
71
78
  treeInstance: {
72
79
  getDropTarget: () => DropTarget<T> | null;
80
+ getDragLineData: () => DragLineData | null;
73
81
  };
74
82
  itemInstance: {
75
83
  isDropTarget: () => boolean;
@@ -14,7 +14,6 @@ export const expandAllFeature: FeatureImplementation<
14
14
  | ExpandAllFeatureDef
15
15
  > = {
16
16
  key: "expand-all",
17
- dependingFeatures: ["main", "tree"],
18
17
 
19
18
  createTreeInstance: (prev, tree) => ({
20
19
  ...prev,
@@ -48,7 +48,6 @@ export const hotkeysCoreFeature: FeatureImplementation<
48
48
  MainFeatureDef | HotkeysCoreFeatureDef<any>
49
49
  > = {
50
50
  key: "hotkeys-core",
51
- dependingFeatures: ["main", "tree"],
52
51
 
53
52
  onTreeMount: (tree, element) => {
54
53
  const data = tree.getDataRef<HotkeysCoreDataRef>();
@@ -10,7 +10,6 @@ export const renamingFeature: FeatureImplementation<
10
10
  MainFeatureDef | TreeFeatureDef<any> | RenamingFeatureDef<any>
11
11
  > = {
12
12
  key: "renaming",
13
- dependingFeatures: ["main", "tree"],
14
13
 
15
14
  getDefaultConfig: (defaultConfig, tree) => ({
16
15
  setRenamingItem: makeStateUpdater("renamingItem", tree),
@@ -28,7 +27,6 @@ export const renamingFeature: FeatureImplementation<
28
27
  ...prev,
29
28
 
30
29
  startRenamingItem: (itemId) => {
31
- const config = instance.getConfig();
32
30
  const item = instance.getItemInstance(itemId);
33
31
 
34
32
  if (!item.canRename()) {
@@ -10,7 +10,6 @@ export const searchFeature: FeatureImplementation<
10
10
  MainFeatureDef | TreeFeatureDef<any> | SearchFeatureDef<any>
11
11
  > = {
12
12
  key: "search",
13
- dependingFeatures: ["main", "tree"],
14
13
 
15
14
  getInitialState: (initialState) => ({
16
15
  search: null,
@@ -10,7 +10,6 @@ export const selectionFeature: FeatureImplementation<
10
10
  MainFeatureDef | TreeFeatureDef<any> | SelectionFeatureDef<any>
11
11
  > = {
12
12
  key: "selection",
13
- dependingFeatures: ["main", "tree"],
14
13
 
15
14
  getInitialState: (initialState) => ({
16
15
  selectedItems: [],
@@ -71,7 +70,7 @@ export const selectionFeature: FeatureImplementation<
71
70
  const newSelectedItems = tree
72
71
  .getItems()
73
72
  .slice(a, b + 1)
74
- .map((item) => item.getItemMeta().itemId);
73
+ .map((treeItem) => treeItem.getItemMeta().itemId);
75
74
 
76
75
  if (!ctrl) {
77
76
  tree.setSelectedItems(newSelectedItems);
@@ -9,7 +9,6 @@ export const syncDataLoaderFeature: FeatureImplementation<
9
9
  MainFeatureDef | SyncDataLoaderFeatureDef<any>
10
10
  > = {
11
11
  key: "sync-data-loader",
12
- dependingFeatures: ["main"],
13
12
 
14
13
  getInitialState: (initialState) => ({
15
14
  loadingItems: [],
@@ -14,7 +14,6 @@ export const treeFeature: FeatureImplementation<
14
14
  | SyncDataLoaderFeatureDef<any>
15
15
  > = {
16
16
  key: "tree",
17
- dependingFeatures: ["main"],
18
17
 
19
18
  getInitialState: (initialState) => ({
20
19
  expandedItems: [],
package/src/types/core.ts CHANGED
@@ -128,7 +128,8 @@ export type FeatureImplementation<
128
128
  F extends FeatureDef = EmptyFeatureDef
129
129
  > = {
130
130
  key?: string;
131
- dependingFeatures?: string[];
131
+ deps?: string[];
132
+ overwrites?: string[];
132
133
 
133
134
  stateHandlerNames?: Partial<
134
135
  Record<keyof MergedFeatures<F>["state"], keyof MergedFeatures<F>["config"]>