@headless-tree/core 0.0.0-20250611223358 → 0.0.0-20250611231952

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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @headless-tree/core
2
2
 
3
- ## 0.0.0-20250611223358
3
+ ## 0.0.0-20250611231952
4
4
 
5
5
  ### Minor Changes
6
6
 
@@ -17,6 +17,8 @@
17
17
  - 727c982: export makeStateUpdater from core package
18
18
  - c041a3f: expose `isOrderedDragTarget` as utility method to differentiate between ordered and unordered drag targets (#108)
19
19
  - 2887b0c: Added a `item.getKey()` utility method to use for generating React keys. For now, this just returns the item id, so no migration is needed from using `item.getId()` as React keys.
20
+ - 4e79bc7: Fixed a bug where `feature.overwrites` is not always respected when features are sorted during tree initialization
21
+ - 0669580: Fixed a bug where items that call `item.getProps` while being renamed, can be dragged while being renamed (#110)
20
22
 
21
23
  ## 1.1.0
22
24
 
@@ -14,6 +14,18 @@ const verifyFeatures = (features) => {
14
14
  }
15
15
  }
16
16
  };
17
+ // Check all possible pairs and sort the array
18
+ const exhaustiveSort = (arr, compareFn) => {
19
+ const n = arr.length;
20
+ for (let i = 0; i < n; i++) {
21
+ for (let j = i + 1; j < n; j++) {
22
+ if (compareFn(arr[j], arr[i]) < 0) {
23
+ [arr[i], arr[j]] = [arr[j], arr[i]];
24
+ }
25
+ }
26
+ }
27
+ return arr;
28
+ };
17
29
  const compareFeatures = (originalOrder) => (feature1, feature2) => {
18
30
  var _a, _b;
19
31
  if (feature2.key && ((_a = feature1.overwrites) === null || _a === void 0 ? void 0 : _a.includes(feature2.key))) {
@@ -24,7 +36,7 @@ const compareFeatures = (originalOrder) => (feature1, feature2) => {
24
36
  }
25
37
  return originalOrder.indexOf(feature1) - originalOrder.indexOf(feature2);
26
38
  };
27
- const sortFeatures = (features = []) => features.sort(compareFeatures(features));
39
+ const sortFeatures = (features = []) => exhaustiveSort(features, compareFeatures(features));
28
40
  const createTree = (initialConfig) => {
29
41
  var _a, _b, _c, _d;
30
42
  const buildInstance = (_a = initialConfig.instanceBuilder) !== null && _a !== void 0 ? _a : build_static_instance_1.buildStaticInstance;
@@ -4,6 +4,7 @@ exports.renamingFeature = void 0;
4
4
  const utils_1 = require("../../utils");
5
5
  exports.renamingFeature = {
6
6
  key: "renaming",
7
+ overwrites: ["drag-and-drop"],
7
8
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setRenamingItem: (0, utils_1.makeStateUpdater)("renamingItem", tree), setRenamingValue: (0, utils_1.makeStateUpdater)("renamingValue", tree), canRename: () => true }, defaultConfig)),
8
9
  stateHandlerNames: {
9
10
  renamingItem: "setRenamingItem",
@@ -50,6 +51,13 @@ exports.renamingFeature = {
50
51
  }),
51
52
  canRename: ({ tree, item }) => { var _a, _b, _c; return (_c = (_b = (_a = tree.getConfig()).canRename) === null || _b === void 0 ? void 0 : _b.call(_a, item)) !== null && _c !== void 0 ? _c : true; },
52
53
  isRenaming: ({ tree, item }) => item.getId() === tree.getState().renamingItem,
54
+ getProps: ({ prev, item }) => {
55
+ var _a;
56
+ const isRenaming = item.isRenaming();
57
+ const prevProps = (_a = prev === null || prev === void 0 ? void 0 : prev()) !== null && _a !== void 0 ? _a : {};
58
+ return isRenaming
59
+ ? Object.assign(Object.assign({}, prevProps), { draggable: false, onDragStart: () => { } }) : prevProps;
60
+ },
53
61
  },
54
62
  hotkeys: {
55
63
  renameItem: {
@@ -11,6 +11,18 @@ const verifyFeatures = (features) => {
11
11
  }
12
12
  }
13
13
  };
14
+ // Check all possible pairs and sort the array
15
+ const exhaustiveSort = (arr, compareFn) => {
16
+ const n = arr.length;
17
+ for (let i = 0; i < n; i++) {
18
+ for (let j = i + 1; j < n; j++) {
19
+ if (compareFn(arr[j], arr[i]) < 0) {
20
+ [arr[i], arr[j]] = [arr[j], arr[i]];
21
+ }
22
+ }
23
+ }
24
+ return arr;
25
+ };
14
26
  const compareFeatures = (originalOrder) => (feature1, feature2) => {
15
27
  var _a, _b;
16
28
  if (feature2.key && ((_a = feature1.overwrites) === null || _a === void 0 ? void 0 : _a.includes(feature2.key))) {
@@ -21,7 +33,7 @@ const compareFeatures = (originalOrder) => (feature1, feature2) => {
21
33
  }
22
34
  return originalOrder.indexOf(feature1) - originalOrder.indexOf(feature2);
23
35
  };
24
- const sortFeatures = (features = []) => features.sort(compareFeatures(features));
36
+ const sortFeatures = (features = []) => exhaustiveSort(features, compareFeatures(features));
25
37
  export const createTree = (initialConfig) => {
26
38
  var _a, _b, _c, _d;
27
39
  const buildInstance = (_a = initialConfig.instanceBuilder) !== null && _a !== void 0 ? _a : buildStaticInstance;
@@ -1,6 +1,7 @@
1
1
  import { makeStateUpdater } from "../../utils";
2
2
  export const renamingFeature = {
3
3
  key: "renaming",
4
+ overwrites: ["drag-and-drop"],
4
5
  getDefaultConfig: (defaultConfig, tree) => (Object.assign({ setRenamingItem: makeStateUpdater("renamingItem", tree), setRenamingValue: makeStateUpdater("renamingValue", tree), canRename: () => true }, defaultConfig)),
5
6
  stateHandlerNames: {
6
7
  renamingItem: "setRenamingItem",
@@ -47,6 +48,13 @@ export const renamingFeature = {
47
48
  }),
48
49
  canRename: ({ tree, item }) => { var _a, _b, _c; return (_c = (_b = (_a = tree.getConfig()).canRename) === null || _b === void 0 ? void 0 : _b.call(_a, item)) !== null && _c !== void 0 ? _c : true; },
49
50
  isRenaming: ({ tree, item }) => item.getId() === tree.getState().renamingItem,
51
+ getProps: ({ prev, item }) => {
52
+ var _a;
53
+ const isRenaming = item.isRenaming();
54
+ const prevProps = (_a = prev === null || prev === void 0 ? void 0 : prev()) !== null && _a !== void 0 ? _a : {};
55
+ return isRenaming
56
+ ? Object.assign(Object.assign({}, prevProps), { draggable: false, onDragStart: () => { } }) : prevProps;
57
+ },
50
58
  },
51
59
  hotkeys: {
52
60
  renameItem: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@headless-tree/core",
3
- "version": "0.0.0-20250611223358",
3
+ "version": "0.0.0-20250611231952",
4
4
  "main": "lib/cjs/index.js",
5
5
  "module": "lib/esm/index.js",
6
6
  "types": "lib/esm/index.d.ts",
@@ -24,6 +24,24 @@ const verifyFeatures = (features: FeatureImplementation[] | undefined) => {
24
24
  }
25
25
  };
26
26
 
27
+ // Check all possible pairs and sort the array
28
+ const exhaustiveSort = <T>(
29
+ arr: T[],
30
+ compareFn: (param1: T, param2: T) => number,
31
+ ) => {
32
+ const n = arr.length;
33
+
34
+ for (let i = 0; i < n; i++) {
35
+ for (let j = i + 1; j < n; j++) {
36
+ if (compareFn(arr[j], arr[i]) < 0) {
37
+ [arr[i], arr[j]] = [arr[j], arr[i]];
38
+ }
39
+ }
40
+ }
41
+
42
+ return arr;
43
+ };
44
+
27
45
  const compareFeatures =
28
46
  (originalOrder: FeatureImplementation[]) =>
29
47
  (feature1: FeatureImplementation, feature2: FeatureImplementation) => {
@@ -33,11 +51,12 @@ const compareFeatures =
33
51
  if (feature1.key && feature2.overwrites?.includes(feature1.key)) {
34
52
  return -1;
35
53
  }
54
+
36
55
  return originalOrder.indexOf(feature1) - originalOrder.indexOf(feature2);
37
56
  };
38
57
 
39
58
  const sortFeatures = (features: FeatureImplementation[] = []) =>
40
- features.sort(compareFeatures(features));
59
+ exhaustiveSort(features, compareFeatures(features));
41
60
 
42
61
  export const createTree = <T>(
43
62
  initialConfig: TreeConfig<T>,
@@ -9,6 +9,7 @@ type InputEvent = {
9
9
 
10
10
  export const renamingFeature: FeatureImplementation = {
11
11
  key: "renaming",
12
+ overwrites: ["drag-and-drop"],
12
13
 
13
14
  getDefaultConfig: (defaultConfig, tree) => ({
14
15
  setRenamingItem: makeStateUpdater("renamingItem", tree),
@@ -72,6 +73,18 @@ export const renamingFeature: FeatureImplementation = {
72
73
 
73
74
  isRenaming: ({ tree, item }) =>
74
75
  item.getId() === tree.getState().renamingItem,
76
+
77
+ getProps: ({ prev, item }) => {
78
+ const isRenaming = item.isRenaming();
79
+ const prevProps = prev?.() ?? {};
80
+ return isRenaming
81
+ ? {
82
+ ...prevProps,
83
+ draggable: false,
84
+ onDragStart: () => {},
85
+ }
86
+ : prevProps;
87
+ },
75
88
  },
76
89
 
77
90
  hotkeys: {
@@ -93,6 +93,37 @@ describe("core-feature/renaming", () => {
93
93
  expect(setRenamingItem).toHaveBeenCalledWith(null);
94
94
  });
95
95
 
96
+ describe("dragging", async () => {
97
+ const suiteTree = await tree
98
+ .withFeatures({
99
+ key: "drag-and-drop",
100
+ itemInstance: {
101
+ getProps: ({ prev }: any) => ({
102
+ ...prev?.(),
103
+ draggable: true,
104
+ onDragStart: "initialOnDragStart",
105
+ }),
106
+ },
107
+ })
108
+ .createTestCaseTree();
109
+ suiteTree.resetBeforeEach();
110
+
111
+ it("sets draggable to undefined for items being renamed", () => {
112
+ const item = suiteTree.item("x1");
113
+ item.startRenaming();
114
+ const props = item.getProps();
115
+ expect(props.draggable).toBe(false);
116
+ expect(props.onDragStart).toStrictEqual(expect.any(Function));
117
+ });
118
+
119
+ it("retains draggable for items not being renamed", () => {
120
+ const item = suiteTree.item("x1");
121
+ const props = item.getProps();
122
+ expect(props.draggable).toBe(true);
123
+ expect(props.onDragStart).toBe("initialOnDragStart");
124
+ });
125
+ });
126
+
96
127
  describe("hotkeys", () => {
97
128
  it("starts renaming", () => {
98
129
  const setRenamingItem = tree.mockedHandler("setRenamingItem");