@headless-tree/core 0.0.10 → 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 +6 -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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @headless-tree/core
2
2
 
3
+ ## 0.0.11
4
+
5
+ ### Patch Changes
6
+
7
+ - 7ed33ac: dev release
8
+
3
9
  ## 0.0.10
4
10
 
5
11
  ### Patch Changes
@@ -0,0 +1,2 @@
1
+ import { InstanceBuilder } from "../features/main/types";
2
+ export declare const buildProxiedInstance: InstanceBuilder;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildProxiedInstance = void 0;
4
+ const errors_1 = require("../utilities/errors");
5
+ const noop = () => { };
6
+ const findPrevInstanceMethod = (features, instanceType, methodKey, featureSearchIndex) => {
7
+ var _a;
8
+ for (let i = featureSearchIndex; i >= 0; i--) {
9
+ const feature = features[i];
10
+ const itemInstanceMethod = (_a = feature[instanceType]) === null || _a === void 0 ? void 0 : _a[methodKey];
11
+ if (itemInstanceMethod) {
12
+ return i;
13
+ }
14
+ }
15
+ return null;
16
+ };
17
+ const invokeInstanceMethod = (features, instanceType, opts, methodKey, featureIndex, args) => {
18
+ var _a;
19
+ const prevIndex = findPrevInstanceMethod(features, instanceType, methodKey, featureIndex - 1);
20
+ const itemInstanceMethod = (_a = features[featureIndex][instanceType]) === null || _a === void 0 ? void 0 : _a[methodKey];
21
+ return itemInstanceMethod(Object.assign(Object.assign({}, opts), { prev: prevIndex !== null
22
+ ? (...newArgs) => invokeInstanceMethod(features, instanceType, opts, methodKey, prevIndex, newArgs)
23
+ : null }), ...args);
24
+ };
25
+ const buildProxiedInstance = (features, instanceType, buildOpts) => {
26
+ // demo with prototypes: https://jsfiddle.net/bgenc58r/
27
+ const opts = {};
28
+ const item = new Proxy({}, {
29
+ has(target, key) {
30
+ if (typeof key === "symbol") {
31
+ return false;
32
+ }
33
+ if (key === "toJSON") {
34
+ return false;
35
+ }
36
+ const hasInstanceMethod = findPrevInstanceMethod(features, instanceType, key, features.length - 1);
37
+ return Boolean(hasInstanceMethod);
38
+ },
39
+ get(target, key) {
40
+ if (typeof key === "symbol") {
41
+ return undefined;
42
+ }
43
+ if (key === "toJSON") {
44
+ return {};
45
+ }
46
+ return (...args) => {
47
+ const featureIndex = findPrevInstanceMethod(features, instanceType, key, features.length - 1);
48
+ if (featureIndex === null) {
49
+ throw (0, errors_1.throwError)(`feature missing for method ${key}`);
50
+ }
51
+ return invokeInstanceMethod(features, instanceType, opts, key, featureIndex, args);
52
+ };
53
+ },
54
+ });
55
+ Object.assign(opts, buildOpts(item));
56
+ return [item, noop];
57
+ };
58
+ exports.buildProxiedInstance = buildProxiedInstance;
@@ -0,0 +1,2 @@
1
+ import { InstanceBuilder } from "../features/main/types";
2
+ export declare const buildStaticInstance: InstanceBuilder;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ /* eslint-disable no-continue,no-labels,no-extra-label */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.buildStaticInstance = void 0;
5
+ const buildStaticInstance = (features, instanceType, buildOpts) => {
6
+ const instance = {};
7
+ const finalize = () => {
8
+ const opts = buildOpts(instance);
9
+ featureLoop: for (let i = 0; i < features.length; i++) {
10
+ // Loop goes in forward order, because later features overwrite previous ones
11
+ // TODO loop order correct? I think so...
12
+ const definition = features[i][instanceType];
13
+ if (!definition)
14
+ continue featureLoop;
15
+ methodLoop: for (const [key, method] of Object.entries(definition)) {
16
+ if (!method)
17
+ continue methodLoop;
18
+ const prev = instance[key];
19
+ instance[key] = (...args) => {
20
+ return method(Object.assign(Object.assign({}, opts), { prev }), ...args);
21
+ };
22
+ }
23
+ }
24
+ };
25
+ return [instance, finalize];
26
+ };
27
+ exports.buildStaticInstance = buildStaticInstance;
@@ -2,24 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createTree = void 0;
4
4
  const feature_1 = require("../features/tree/feature");
5
- const buildItemInstance = (features, tree, itemId) => {
6
- var _a, _b;
7
- const itemInstance = {};
8
- for (const feature of features) {
9
- Object.assign(
10
- // TODO dont run createItemInstance, but assign prototype objects instead?
11
- // https://jsfiddle.net/bgenc58r/
12
- itemInstance, (_b = (_a = feature.createItemInstance) === null || _a === void 0 ? void 0 : _a.call(feature, Object.assign({}, itemInstance), itemInstance, tree, itemId)) !== null && _b !== void 0 ? _b : {});
13
- }
14
- return itemInstance;
15
- };
5
+ const build_static_instance_1 = require("./build-static-instance");
6
+ const errors_1 = require("../utilities/errors");
16
7
  const verifyFeatures = (features) => {
17
8
  var _a;
18
9
  const loadedFeatures = features === null || features === void 0 ? void 0 : features.map((feature) => feature.key);
19
10
  for (const feature of features !== null && features !== void 0 ? features : []) {
20
11
  const missingDependency = (_a = feature.deps) === null || _a === void 0 ? void 0 : _a.find((dep) => !(loadedFeatures === null || loadedFeatures === void 0 ? void 0 : loadedFeatures.includes(dep)));
21
12
  if (missingDependency) {
22
- throw new Error(`${feature.key} needs ${missingDependency}`);
13
+ throw (0, errors_1.throwError)(`${feature.key} needs ${missingDependency}`);
23
14
  }
24
15
  }
25
16
  };
@@ -32,14 +23,16 @@ const compareFeatures = (feature1, feature2) => {
32
23
  };
33
24
  const sortFeatures = (features = []) => features.sort(compareFeatures);
34
25
  const createTree = (initialConfig) => {
35
- var _a, _b, _c, _d, _e;
36
- const treeInstance = {};
26
+ var _a, _b, _c, _d;
27
+ const buildInstance = (_a = initialConfig.instanceBuilder) !== null && _a !== void 0 ? _a : build_static_instance_1.buildStaticInstance;
37
28
  const additionalFeatures = [
38
29
  feature_1.treeFeature,
39
30
  ...sortFeatures(initialConfig.features),
40
31
  ];
41
32
  verifyFeatures(additionalFeatures);
42
- 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 : {});
33
+ const features = [...additionalFeatures];
34
+ const [treeInstance, finalizeTree] = buildInstance(features, "treeInstance", (tree) => ({ tree }));
35
+ 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 : {});
43
36
  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);
44
37
  const stateHandlerNames = additionalFeatures.reduce((acc, feature) => (Object.assign(Object.assign({}, acc), feature.stateHandlerNames)), {});
45
38
  let treeElement;
@@ -50,11 +43,12 @@ const createTree = (initialConfig) => {
50
43
  const itemDataRefs = {};
51
44
  let itemMetaMap = {};
52
45
  const hotkeyPresets = {};
53
- const rebuildItemMeta = (main) => {
46
+ const rebuildItemMeta = () => {
54
47
  // TODO can we find a way to only run this for the changed substructure?
55
48
  itemInstances = [];
56
49
  itemMetaMap = {};
57
- const rootInstance = buildItemInstance([main, ...additionalFeatures], treeInstance, config.rootItemId);
50
+ const [rootInstance, finalizeRootInstance] = buildInstance(features, "itemInstance", (item) => ({ item, tree: treeInstance, itemId: config.rootItemId }));
51
+ finalizeRootInstance();
58
52
  itemInstancesMap[config.rootItemId] = rootInstance;
59
53
  itemMetaMap[config.rootItemId] = {
60
54
  itemId: config.rootItemId,
@@ -67,7 +61,12 @@ const createTree = (initialConfig) => {
67
61
  for (const item of treeInstance.getItemsMeta()) {
68
62
  itemMetaMap[item.itemId] = item;
69
63
  if (!itemInstancesMap[item.itemId]) {
70
- const instance = buildItemInstance([main, ...additionalFeatures], treeInstance, item.itemId);
64
+ const [instance, finalizeInstance] = buildInstance(features, "itemInstance", (instance) => ({
65
+ item: instance,
66
+ tree: treeInstance,
67
+ itemId: item.itemId,
68
+ }));
69
+ finalizeInstance();
71
70
  itemInstancesMap[item.itemId] = instance;
72
71
  itemInstances.push(instance);
73
72
  }
@@ -83,25 +82,36 @@ const createTree = (initialConfig) => {
83
82
  };
84
83
  const mainFeature = {
85
84
  key: "main",
86
- createTreeInstance: (prev) => (Object.assign(Object.assign({}, prev), { getState: () => state, setState: (updater) => {
85
+ treeInstance: {
86
+ getState: () => state,
87
+ setState: ({}, updater) => {
87
88
  var _a;
88
89
  // Not necessary, since I think the subupdate below keeps the state fresh anyways?
89
90
  // state = typeof updater === "function" ? updater(state) : updater;
90
- (_a = config.setState) === null || _a === void 0 ? void 0 : _a.call(config, state);
91
- }, applySubStateUpdate: (stateName, updater) => {
91
+ (_a = config.setState) === null || _a === void 0 ? void 0 : _a.call(config, state); // TODO this cant be right... This doesnt allow external state updates
92
+ },
93
+ applySubStateUpdate: ({}, stateName, updater) => {
92
94
  state[stateName] =
93
95
  typeof updater === "function" ? updater(state[stateName]) : updater;
94
- config[stateHandlerNames[stateName]](state[stateName]);
95
- }, rebuildTree: () => {
96
+ const externalStateSetter = config[stateHandlerNames[stateName]];
97
+ externalStateSetter === null || externalStateSetter === void 0 ? void 0 : externalStateSetter(state[stateName]);
98
+ },
99
+ // TODO rebuildSubTree: (itemId: string) => void;
100
+ rebuildTree: () => {
96
101
  var _a;
97
- rebuildItemMeta(mainFeature);
102
+ rebuildItemMeta();
98
103
  (_a = config.setState) === null || _a === void 0 ? void 0 : _a.call(config, state);
99
- }, getConfig: () => config, setConfig: (updater) => {
104
+ },
105
+ getConfig: () => config,
106
+ setConfig: (_, updater) => {
100
107
  config = typeof updater === "function" ? updater(config) : updater;
101
108
  if (config.state) {
102
109
  state = Object.assign(Object.assign({}, state), config.state);
103
110
  }
104
- }, getItemInstance: (itemId) => itemInstancesMap[itemId], getItems: () => itemInstances, registerElement: (element) => {
111
+ },
112
+ getItemInstance: ({}, itemId) => itemInstancesMap[itemId],
113
+ getItems: () => itemInstances,
114
+ registerElement: ({}, element) => {
105
115
  if (treeElement === element) {
106
116
  return;
107
117
  }
@@ -112,29 +122,38 @@ const createTree = (initialConfig) => {
112
122
  eachFeature((feature) => { var _a; return (_a = feature.onTreeMount) === null || _a === void 0 ? void 0 : _a.call(feature, treeInstance, element); });
113
123
  }
114
124
  treeElement = element;
115
- }, getElement: () => treeElement, getDataRef: () => treeDataRef, getHotkeyPresets: () => hotkeyPresets })),
116
- createItemInstance: (prev, instance, _, itemId) => (Object.assign(Object.assign({}, prev), { registerElement: (element) => {
125
+ },
126
+ getElement: () => treeElement,
127
+ getDataRef: () => treeDataRef,
128
+ getHotkeyPresets: () => hotkeyPresets,
129
+ },
130
+ itemInstance: {
131
+ // TODO just change to a getRef method that memoizes, maybe as part of getProps
132
+ registerElement: ({ itemId, item }, element) => {
117
133
  if (itemElementsMap[itemId] === element) {
118
134
  return;
119
135
  }
120
136
  const oldElement = itemElementsMap[itemId];
121
137
  if (oldElement && !element) {
122
- eachFeature((feature) => { var _a; return (_a = feature.onItemUnmount) === null || _a === void 0 ? void 0 : _a.call(feature, instance, oldElement, treeInstance); });
138
+ eachFeature((feature) => { var _a; return (_a = feature.onItemUnmount) === null || _a === void 0 ? void 0 : _a.call(feature, item, oldElement, treeInstance); });
123
139
  }
124
140
  else if (!oldElement && element) {
125
- eachFeature((feature) => { var _a; return (_a = feature.onItemMount) === null || _a === void 0 ? void 0 : _a.call(feature, instance, element, treeInstance); });
141
+ eachFeature((feature) => { var _a; return (_a = feature.onItemMount) === null || _a === void 0 ? void 0 : _a.call(feature, item, element, treeInstance); });
126
142
  }
127
143
  itemElementsMap[itemId] = element;
128
- }, getElement: () => itemElementsMap[itemId],
144
+ },
145
+ getElement: ({ itemId }) => itemElementsMap[itemId],
129
146
  // eslint-disable-next-line no-return-assign
130
- getDataRef: () => { var _a; return ((_a = itemDataRefs[itemId]) !== null && _a !== void 0 ? _a : (itemDataRefs[itemId] = { current: {} })); }, getItemMeta: () => itemMetaMap[itemId] })),
147
+ getDataRef: ({ itemId }) => { var _a; return ((_a = itemDataRefs[itemId]) !== null && _a !== void 0 ? _a : (itemDataRefs[itemId] = { current: {} })); },
148
+ getItemMeta: ({ itemId }) => itemMetaMap[itemId],
149
+ },
131
150
  };
132
- const features = [mainFeature, ...additionalFeatures];
151
+ features.unshift(mainFeature);
133
152
  for (const feature of features) {
134
- 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 : {});
135
- Object.assign(hotkeyPresets, (_e = feature.hotkeys) !== null && _e !== void 0 ? _e : {});
153
+ Object.assign(hotkeyPresets, (_d = feature.hotkeys) !== null && _d !== void 0 ? _d : {});
136
154
  }
137
- rebuildItemMeta(mainFeature);
155
+ finalizeTree();
156
+ rebuildItemMeta();
138
157
  return treeInstance;
139
158
  };
140
159
  exports.createTree = createTree;
@@ -9,18 +9,19 @@ exports.asyncDataLoaderFeature = {
9
9
  stateHandlerNames: {
10
10
  loadingItems: "setLoadingItems",
11
11
  },
12
- createTreeInstance: (prev, instance) => (Object.assign(Object.assign({}, prev), { retrieveItemData: (itemId) => {
12
+ treeInstance: {
13
+ retrieveItemData: ({ tree }, itemId) => {
13
14
  var _a, _b, _c, _d, _e;
14
15
  var _f, _g;
15
- const config = instance.getConfig();
16
- const dataRef = instance.getDataRef();
16
+ const config = tree.getConfig();
17
+ const dataRef = tree.getDataRef();
17
18
  (_a = (_f = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_f.itemData = {});
18
19
  (_b = (_g = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_g.childrenIds = {});
19
20
  if (dataRef.current.itemData[itemId]) {
20
21
  return dataRef.current.itemData[itemId];
21
22
  }
22
- if (!instance.getState().loadingItems.includes(itemId)) {
23
- instance.applySubStateUpdate("loadingItems", (loadingItems) => [
23
+ if (!tree.getState().loadingItems.includes(itemId)) {
24
+ tree.applySubStateUpdate("loadingItems", (loadingItems) => [
24
25
  ...loadingItems,
25
26
  itemId,
26
27
  ]);
@@ -28,24 +29,25 @@ exports.asyncDataLoaderFeature = {
28
29
  var _a;
29
30
  dataRef.current.itemData[itemId] = item;
30
31
  (_a = config.onLoadedItem) === null || _a === void 0 ? void 0 : _a.call(config, itemId, item);
31
- instance.applySubStateUpdate("loadingItems", (loadingItems) => loadingItems.filter((id) => id !== itemId));
32
+ tree.applySubStateUpdate("loadingItems", (loadingItems) => loadingItems.filter((id) => id !== itemId));
32
33
  });
33
34
  }
34
35
  return (_e = (_d = config.createLoadingItemData) === null || _d === void 0 ? void 0 : _d.call(config)) !== null && _e !== void 0 ? _e : null;
35
- }, retrieveChildrenIds: (itemId) => {
36
+ },
37
+ retrieveChildrenIds: ({ tree }, itemId) => {
36
38
  var _a, _b, _c, _d, _e;
37
39
  var _f, _g;
38
- const config = instance.getConfig();
39
- const dataRef = instance.getDataRef();
40
+ const config = tree.getConfig();
41
+ const dataRef = tree.getDataRef();
40
42
  (_a = (_f = dataRef.current).itemData) !== null && _a !== void 0 ? _a : (_f.itemData = {});
41
43
  (_b = (_g = dataRef.current).childrenIds) !== null && _b !== void 0 ? _b : (_g.childrenIds = {});
42
44
  if (dataRef.current.childrenIds[itemId]) {
43
45
  return dataRef.current.childrenIds[itemId];
44
46
  }
45
- if (instance.getState().loadingItems.includes(itemId)) {
47
+ if (tree.getState().loadingItems.includes(itemId)) {
46
48
  return [];
47
49
  }
48
- instance.applySubStateUpdate("loadingItems", (loadingItems) => [
50
+ tree.applySubStateUpdate("loadingItems", (loadingItems) => [
49
51
  ...loadingItems,
50
52
  itemId,
51
53
  ]);
@@ -59,8 +61,8 @@ exports.asyncDataLoaderFeature = {
59
61
  const childrenIds = children.map(({ id }) => id);
60
62
  dataRef.current.childrenIds[itemId] = childrenIds;
61
63
  (_b = config.onLoadedChildren) === null || _b === void 0 ? void 0 : _b.call(config, itemId, childrenIds);
62
- instance.applySubStateUpdate("loadingItems", (loadingItems) => loadingItems.filter((id) => id !== itemId));
63
- instance.rebuildTree();
64
+ tree.applySubStateUpdate("loadingItems", (loadingItems) => loadingItems.filter((id) => id !== itemId));
65
+ tree.rebuildTree();
64
66
  });
65
67
  }
66
68
  else {
@@ -68,21 +70,33 @@ exports.asyncDataLoaderFeature = {
68
70
  var _a;
69
71
  dataRef.current.childrenIds[itemId] = childrenIds;
70
72
  (_a = config.onLoadedChildren) === null || _a === void 0 ? void 0 : _a.call(config, itemId, childrenIds);
71
- instance.applySubStateUpdate("loadingItems", (loadingItems) => loadingItems.filter((id) => id !== itemId));
72
- instance.rebuildTree();
73
+ tree.applySubStateUpdate("loadingItems", (loadingItems) => loadingItems.filter((id) => id !== itemId));
74
+ tree.rebuildTree();
73
75
  });
74
76
  }
75
77
  return [];
76
- }, invalidateItemData: (itemId) => {
78
+ },
79
+ invalidateItemData: ({ tree }, itemId) => {
77
80
  var _a;
78
- const dataRef = instance.getDataRef();
81
+ const dataRef = tree.getDataRef();
79
82
  (_a = dataRef.current.itemData) === null || _a === void 0 ? true : delete _a[itemId];
80
- instance.retrieveItemData(itemId);
81
- }, invalidateChildrenIds: (itemId) => {
83
+ tree.retrieveItemData(itemId);
84
+ },
85
+ invalidateChildrenIds: ({ tree }, itemId) => {
82
86
  var _a;
83
- const dataRef = instance.getDataRef();
87
+ const dataRef = tree.getDataRef();
84
88
  (_a = dataRef.current.childrenIds) === null || _a === void 0 ? true : delete _a[itemId];
85
- instance.retrieveChildrenIds(itemId);
86
- } })),
87
- createItemInstance: (prev, item, tree) => (Object.assign(Object.assign({}, prev), { isLoading: () => tree.getState().loadingItems.includes(item.getItemMeta().itemId), invalidateItemData: () => tree.invalidateItemData(item.getItemMeta().itemId), invalidateChildrenIds: () => tree.invalidateChildrenIds(item.getItemMeta().itemId) })),
89
+ tree.retrieveChildrenIds(itemId);
90
+ },
91
+ },
92
+ itemInstance: {
93
+ isLoading: ({ tree, item }) => tree.getState().loadingItems.includes(item.getItemMeta().itemId),
94
+ invalidateItemData: ({ tree, item }) => tree.invalidateItemData(item.getItemMeta().itemId),
95
+ invalidateChildrenIds: ({ tree, item }) => tree.invalidateChildrenIds(item.getItemMeta().itemId),
96
+ updateCachedChildrenIds: ({ tree, itemId }, childrenIds) => {
97
+ const dataRef = tree.getDataRef();
98
+ dataRef.current.childrenIds[itemId] = childrenIds;
99
+ tree.rebuildTree();
100
+ },
101
+ },
88
102
  };
@@ -35,7 +35,8 @@ export type AsyncDataLoaderFeatureDef<T> = {
35
35
  itemInstance: SyncDataLoaderFeatureDef<T>["itemInstance"] & {
36
36
  invalidateItemData: () => void;
37
37
  invalidateChildrenIds: () => void;
38
- isLoading: () => void;
38
+ updateCachedChildrenIds: (childrenIds: string[]) => void;
39
+ isLoading: () => boolean;
39
40
  };
40
41
  hotkeys: SyncDataLoaderFeatureDef<T>["hotkeys"];
41
42
  };
@@ -6,45 +6,64 @@ const utils_2 = require("../../utils");
6
6
  exports.dragAndDropFeature = {
7
7
  key: "dragAndDrop",
8
8
  deps: ["selection"],
9
- getDefaultConfig: (defaultConfig, tree) => (Object.assign({ canDrop: (_, target) => target.item.isFolder(), canDropForeignDragObject: () => false, setDndState: (0, utils_2.makeStateUpdater)("dnd", tree) }, defaultConfig)),
9
+ getDefaultConfig: (defaultConfig, tree) => (Object.assign({ canDrop: (_, target) => target.item.isFolder(), canDropForeignDragObject: () => false, setDndState: (0, utils_2.makeStateUpdater)("dnd", tree), canDropInbetween: true }, defaultConfig)),
10
10
  stateHandlerNames: {
11
11
  dnd: "setDndState",
12
12
  },
13
- createTreeInstance: (prev, tree) => (Object.assign(Object.assign({}, prev), { getDropTarget: () => {
13
+ treeInstance: {
14
+ getDropTarget: ({ tree }) => {
14
15
  var _a, _b;
15
16
  return (_b = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.dragTarget) !== null && _b !== void 0 ? _b : null;
16
- }, getDragLineData: () => {
17
+ },
18
+ getDragLineData: ({ tree }) => {
17
19
  var _a, _b, _c, _d, _e;
20
+ // TODO doesnt work if scrolled down!
18
21
  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;
22
+ const indent = ((_a = target === null || target === void 0 ? void 0 : target.item.getItemMeta().level) !== null && _a !== void 0 ? _a : 0) + 1; // TODO rename to indent
20
23
  if (!target || target.childIndex === null)
21
24
  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
+ const leftOffset = target.dragLineLevel * ((_b = tree.getConfig().indent) !== null && _b !== void 0 ? _b : 1);
26
+ const targetItem = tree.getItems()[target.dragLineIndex];
27
+ if (!targetItem) {
28
+ const bb = (_d = (_c = tree
29
+ .getItems()[target.dragLineIndex - 1]) === null || _c === void 0 ? void 0 : _c.getElement()) === null || _d === void 0 ? void 0 : _d.getBoundingClientRect();
25
30
  if (bb) {
26
31
  return {
27
- intend,
32
+ indent,
28
33
  top: bb.bottom,
29
- left: bb.left,
34
+ left: bb.left + leftOffset,
30
35
  right: bb.right,
31
36
  };
32
37
  }
33
38
  }
34
- const bb = (_e = (_d = children[target.childIndex]) === null || _d === void 0 ? void 0 : _d.getElement()) === null || _e === void 0 ? void 0 : _e.getBoundingClientRect();
39
+ const bb = (_e = targetItem.getElement()) === null || _e === void 0 ? void 0 : _e.getBoundingClientRect();
35
40
  if (bb) {
36
41
  return {
37
- intend,
42
+ indent,
38
43
  top: bb.top,
39
- left: bb.left,
44
+ left: bb.left + leftOffset,
40
45
  right: bb.right,
41
46
  };
42
47
  }
43
48
  return null;
44
- } })),
45
- createItemInstance: (prev, item, tree) => (Object.assign(Object.assign({}, prev), { getProps: () => {
49
+ },
50
+ getDragLineStyle: ({ tree }, topOffset = -1, leftOffset = -8) => {
51
+ const dragLine = tree.getDragLineData();
52
+ return dragLine
53
+ ? {
54
+ top: `${dragLine.top + topOffset}px`,
55
+ left: `${dragLine.left + leftOffset}px`,
56
+ width: `${dragLine.right - dragLine.left - leftOffset}px`,
57
+ pointerEvents: "none", // important to prevent capturing drag events
58
+ }
59
+ : { display: "none" };
60
+ },
61
+ },
62
+ itemInstance: {
63
+ // TODO instead of individual getMemoizedProp calls, use a wrapped getMemoizedProps or something (getProps: () => getMemoized({...})
64
+ getProps: ({ tree, item, prev }) => {
46
65
  var _a, _b, _c;
47
- return (Object.assign(Object.assign({}, prev.getProps()), { draggable: (_c = (_b = (_a = tree.getConfig()).isItemDraggable) === null || _b === void 0 ? void 0 : _b.call(_a, item)) !== null && _c !== void 0 ? _c : true, onDragStart: item.getMemoizedProp("dnd/onDragStart", () => (e) => {
66
+ return (Object.assign(Object.assign({}, prev === null || prev === void 0 ? void 0 : prev()), { draggable: (_c = (_b = (_a = tree.getConfig()).isItemDraggable) === null || _b === void 0 ? void 0 : _b.call(_a, item)) !== null && _c !== void 0 ? _c : true, onDragStart: item.getMemoizedProp("dnd/onDragStart", () => (e) => {
48
67
  var _a, _b, _c;
49
68
  const selectedItems = tree.getSelectedItems();
50
69
  const items = selectedItems.includes(item) ? selectedItems : [item];
@@ -66,34 +85,42 @@ exports.dragAndDropFeature = {
66
85
  });
67
86
  }), onDragOver: item.getMemoizedProp("dnd/onDragOver", () => (e) => {
68
87
  var _a, _b, _c;
69
- const target = (0, utils_1.getDropTarget)(e, item, tree);
70
88
  const dataRef = tree.getDataRef();
71
- if (!((_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems) &&
72
- !((_c = (_b = tree.getConfig()).canDropForeignDragObject) === null || _c === void 0 ? void 0 : _c.call(_b, e.dataTransfer, target))) {
89
+ const nextDragCode = (0, utils_1.getDragCode)(e, item, tree);
90
+ if (nextDragCode === dataRef.current.lastDragCode) {
91
+ if (dataRef.current.lastAllowDrop) {
92
+ e.preventDefault();
93
+ }
73
94
  return;
74
95
  }
75
- if (!(0, utils_1.canDrop)(e.dataTransfer, target, tree)) {
96
+ dataRef.current.lastDragCode = nextDragCode;
97
+ const target = (0, utils_1.getDropTarget)(e, item, tree);
98
+ if (!((_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems) &&
99
+ (!e.dataTransfer ||
100
+ !((_c = (_b = tree
101
+ .getConfig()).canDropForeignDragObject) === null || _c === void 0 ? void 0 : _c.call(_b, e.dataTransfer, target)))) {
102
+ dataRef.current.lastAllowDrop = false;
76
103
  return;
77
104
  }
78
- e.preventDefault();
79
- const nextDragCode = (0, utils_1.getDragCode)(target);
80
- if (nextDragCode === dataRef.current.lastDragCode) {
105
+ if (!(0, utils_1.canDrop)(e.dataTransfer, target, tree)) {
106
+ dataRef.current.lastAllowDrop = false;
81
107
  return;
82
108
  }
83
- dataRef.current.lastDragCode = nextDragCode;
84
109
  tree.applySubStateUpdate("dnd", (state) => (Object.assign(Object.assign({}, state), { dragTarget: target, draggingOverItem: item })));
110
+ dataRef.current.lastAllowDrop = true;
111
+ e.preventDefault();
85
112
  }), onDragLeave: item.getMemoizedProp("dnd/onDragLeave", () => () => {
86
113
  const dataRef = tree.getDataRef();
87
114
  dataRef.current.lastDragCode = "no-drag";
88
115
  tree.applySubStateUpdate("dnd", (state) => (Object.assign(Object.assign({}, state), { draggingOverItem: undefined, dragTarget: undefined })));
89
116
  }), onDragEnd: item.getMemoizedProp("dnd/onDragEnd", () => (e) => {
90
- var _a, _b, _c;
117
+ var _a, _b, _c, _d;
91
118
  const draggedItems = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggedItems;
92
119
  tree.applySubStateUpdate("dnd", null);
93
- if (e.dataTransfer.dropEffect === "none" || !draggedItems) {
120
+ if (((_b = e.dataTransfer) === null || _b === void 0 ? void 0 : _b.dropEffect) === "none" || !draggedItems) {
94
121
  return;
95
122
  }
96
- (_c = (_b = tree.getConfig()).onCompleteForeignDrop) === null || _c === void 0 ? void 0 : _c.call(_b, draggedItems);
123
+ (_d = (_c = tree.getConfig()).onCompleteForeignDrop) === null || _d === void 0 ? void 0 : _d.call(_c, draggedItems);
97
124
  }), onDrop: item.getMemoizedProp("dnd/onDrop", () => (e) => {
98
125
  var _a, _b, _c;
99
126
  const dataRef = tree.getDataRef();
@@ -109,30 +136,35 @@ exports.dragAndDropFeature = {
109
136
  if (draggedItems) {
110
137
  (_b = config.onDrop) === null || _b === void 0 ? void 0 : _b.call(config, draggedItems, target);
111
138
  }
112
- else {
139
+ else if (e.dataTransfer) {
113
140
  (_c = config.onDropForeignDragObject) === null || _c === void 0 ? void 0 : _c.call(config, e.dataTransfer, target);
114
141
  }
115
142
  // TODO rebuild tree?
116
143
  }) }));
117
- }, isDropTarget: () => {
144
+ },
145
+ isDropTarget: ({ tree, item }) => {
118
146
  const target = tree.getDropTarget();
119
147
  return target ? target.item.getId() === item.getId() : false;
120
- }, isDropTargetAbove: () => {
148
+ },
149
+ isDropTargetAbove: ({ tree, item }) => {
121
150
  const target = tree.getDropTarget();
122
151
  if (!target ||
123
152
  target.childIndex === null ||
124
153
  target.item !== item.getParent())
125
154
  return false;
126
155
  return target.childIndex === item.getItemMeta().posInSet;
127
- }, isDropTargetBelow: () => {
156
+ },
157
+ isDropTargetBelow: ({ tree, item }) => {
128
158
  const target = tree.getDropTarget();
129
159
  if (!target ||
130
160
  target.childIndex === null ||
131
161
  target.item !== item.getParent())
132
162
  return false;
133
163
  return target.childIndex - 1 === item.getItemMeta().posInSet;
134
- }, isDraggingOver: () => {
164
+ },
165
+ isDraggingOver: ({ tree, item }) => {
135
166
  var _a, _b;
136
167
  return ((_b = (_a = tree.getState().dnd) === null || _a === void 0 ? void 0 : _a.draggingOverItem) === null || _b === void 0 ? void 0 : _b.getId()) === item.getId();
137
- } })),
168
+ },
169
+ },
138
170
  };
@@ -1,6 +1,7 @@
1
1
  import { ItemInstance, SetStateFn } from "../../types/core";
2
2
  export type DndDataRef = {
3
3
  lastDragCode?: string;
4
+ lastAllowDrop?: boolean;
4
5
  };
5
6
  export type DndState<T> = {
6
7
  draggedItems?: ItemInstance<T>[];
@@ -8,7 +9,7 @@ export type DndState<T> = {
8
9
  dragTarget?: DropTarget<T>;
9
10
  };
10
11
  export type DragLineData = {
11
- intend: number;
12
+ indent: number;
12
13
  top: number;
13
14
  left: number;
14
15
  right: number;
@@ -17,10 +18,14 @@ export type DropTarget<T> = {
17
18
  item: ItemInstance<T>;
18
19
  childIndex: number;
19
20
  insertionIndex: number;
21
+ dragLineIndex: number;
22
+ dragLineLevel: number;
20
23
  } | {
21
24
  item: ItemInstance<T>;
22
25
  childIndex: null;
23
26
  insertionIndex: null;
27
+ dragLineIndex: null;
28
+ dragLineLevel: null;
24
29
  };
25
30
  export declare enum DropTargetPosition {
26
31
  Top = "top",
@@ -32,13 +37,16 @@ export type DragAndDropFeatureDef<T> = {
32
37
  dnd?: DndState<T> | null;
33
38
  };
34
39
  config: {
35
- setDndState?: SetStateFn<DndState<T> | null>;
36
- topLinePercentage?: number;
37
- bottomLinePercentage?: number;
40
+ setDndState?: SetStateFn<DndState<T> | undefined | null>;
41
+ /** Defines the size of the area at the top and bottom of an item where, when an item is dropped, the item willö
42
+ * be placed above or below the item within the same parent, as opposed to being placed inside the item.
43
+ * If `canDropInbetween` is `false`, this is ignored. */
44
+ reorderAreaPercentage?: number;
38
45
  canDropInbetween?: boolean;
39
46
  isItemDraggable?: (item: ItemInstance<T>) => boolean;
40
47
  canDrag?: (items: ItemInstance<T>[]) => boolean;
41
48
  canDrop?: (items: ItemInstance<T>[], target: DropTarget<T>) => boolean;
49
+ indent?: number;
42
50
  createForeignDragObject?: (items: ItemInstance<T>[]) => {
43
51
  format: string;
44
52
  data: any;
@@ -58,6 +66,7 @@ export type DragAndDropFeatureDef<T> = {
58
66
  treeInstance: {
59
67
  getDropTarget: () => DropTarget<T> | null;
60
68
  getDragLineData: () => DragLineData | null;
69
+ getDragLineStyle: (topOffset?: number, leftOffset?: number) => Record<string, any>;
61
70
  };
62
71
  itemInstance: {
63
72
  isDropTarget: () => boolean;
@@ -1,6 +1,5 @@
1
1
  import { ItemInstance, TreeInstance } from "../../types/core";
2
2
  import { DropTarget } from "./types";
3
- export declare const getDragCode: ({ item, childIndex }: DropTarget<any>) => string;
4
- export declare const getDropOffset: (e: any, item: ItemInstance<any>) => number;
5
3
  export declare const canDrop: (dataTransfer: DataTransfer | null, target: DropTarget<any>, tree: TreeInstance<any>) => boolean;
4
+ export declare const getDragCode: (e: any, item: ItemInstance<any>, tree: TreeInstance<any>) => string;
6
5
  export declare const getDropTarget: (e: any, item: ItemInstance<any>, tree: TreeInstance<any>, canDropInbetween?: boolean | undefined) => DropTarget<any>;