@headless-tree/core 1.1.0 → 1.2.1

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 (50) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/lib/cjs/core/create-tree.js +22 -2
  3. package/lib/cjs/features/async-data-loader/feature.js +4 -4
  4. package/lib/cjs/features/checkboxes/feature.d.ts +2 -0
  5. package/lib/cjs/features/checkboxes/feature.js +94 -0
  6. package/lib/cjs/features/checkboxes/types.d.ts +26 -0
  7. package/lib/cjs/features/checkboxes/types.js +9 -0
  8. package/lib/cjs/features/drag-and-drop/feature.js +31 -5
  9. package/lib/cjs/features/drag-and-drop/types.d.ts +5 -0
  10. package/lib/cjs/features/main/types.d.ts +2 -0
  11. package/lib/cjs/features/renaming/feature.js +8 -0
  12. package/lib/cjs/features/tree/feature.js +5 -0
  13. package/lib/cjs/features/tree/types.d.ts +3 -1
  14. package/lib/cjs/index.d.ts +4 -0
  15. package/lib/cjs/index.js +7 -0
  16. package/lib/cjs/test-utils/test-tree.js +1 -0
  17. package/lib/cjs/types/core.d.ts +2 -1
  18. package/lib/esm/core/create-tree.js +22 -2
  19. package/lib/esm/features/async-data-loader/feature.js +4 -4
  20. package/lib/esm/features/checkboxes/feature.d.ts +2 -0
  21. package/lib/esm/features/checkboxes/feature.js +91 -0
  22. package/lib/esm/features/checkboxes/types.d.ts +26 -0
  23. package/lib/esm/features/checkboxes/types.js +6 -0
  24. package/lib/esm/features/drag-and-drop/feature.js +31 -5
  25. package/lib/esm/features/drag-and-drop/types.d.ts +5 -0
  26. package/lib/esm/features/main/types.d.ts +2 -0
  27. package/lib/esm/features/renaming/feature.js +8 -0
  28. package/lib/esm/features/tree/feature.js +5 -0
  29. package/lib/esm/features/tree/types.d.ts +3 -1
  30. package/lib/esm/index.d.ts +4 -0
  31. package/lib/esm/index.js +4 -0
  32. package/lib/esm/test-utils/test-tree.js +1 -0
  33. package/lib/esm/types/core.d.ts +2 -1
  34. package/package.json +7 -2
  35. package/readme.md +157 -0
  36. package/src/core/create-tree.ts +33 -2
  37. package/src/features/async-data-loader/feature.ts +4 -4
  38. package/src/features/checkboxes/checkboxes.spec.ts +134 -0
  39. package/src/features/checkboxes/feature.ts +119 -0
  40. package/src/features/checkboxes/types.ts +28 -0
  41. package/src/features/drag-and-drop/feature.ts +38 -2
  42. package/src/features/drag-and-drop/types.ts +5 -0
  43. package/src/features/main/types.ts +2 -0
  44. package/src/features/renaming/feature.ts +13 -0
  45. package/src/features/renaming/renaming.spec.ts +31 -0
  46. package/src/features/tree/feature.ts +6 -0
  47. package/src/features/tree/types.ts +4 -2
  48. package/src/index.ts +5 -0
  49. package/src/test-utils/test-tree.ts +1 -0
  50. package/src/types/core.ts +2 -0
@@ -0,0 +1,134 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { TestTree } from "../../test-utils/test-tree";
3
+ import { checkboxesFeature } from "./feature";
4
+ import { CheckedState } from "./types";
5
+
6
+ const factory = TestTree.default({})
7
+ .withFeatures(checkboxesFeature)
8
+ .suits.sync().tree;
9
+
10
+ describe("core-feature/checkboxes", () => {
11
+ it("should initialize with no checked items", async () => {
12
+ const tree = await factory.createTestCaseTree();
13
+ expect(tree.instance.getState().checkedItems).toEqual([]);
14
+ });
15
+
16
+ it("should check items", async () => {
17
+ const tree = await factory.createTestCaseTree();
18
+ tree.item("x111").setChecked();
19
+ tree.item("x112").setChecked();
20
+ expect(tree.instance.getState().checkedItems).toEqual(["x111", "x112"]);
21
+ });
22
+
23
+ it("should uncheck an item", async () => {
24
+ const tree = await factory
25
+ .with({ state: { checkedItems: ["x111"] } })
26
+ .createTestCaseTree();
27
+ tree.item("x111").setUnchecked();
28
+ expect(tree.instance.getState().checkedItems).not.toContain("x111");
29
+ });
30
+
31
+ it("should toggle checked state", async () => {
32
+ const tree = await factory.createTestCaseTree();
33
+ const item = tree.item("x111");
34
+
35
+ item.toggleCheckedState();
36
+ expect(tree.instance.getState().checkedItems).toContain("x111");
37
+
38
+ item.toggleCheckedState();
39
+ expect(tree.instance.getState().checkedItems).not.toContain("x111");
40
+ });
41
+
42
+ describe("props", () => {
43
+ it("should toggle checked state", async () => {
44
+ const tree = await factory.createTestCaseTree();
45
+ const item = tree.item("x111");
46
+
47
+ item.getCheckboxProps().onChange();
48
+ expect(tree.instance.getState().checkedItems).toContain("x111");
49
+
50
+ item.getCheckboxProps().onChange();
51
+ expect(tree.instance.getState().checkedItems).not.toContain("x111");
52
+ });
53
+
54
+ it("should return checked state in props", async () => {
55
+ const tree = await factory.createTestCaseTree();
56
+ tree.item("x111").setChecked();
57
+ expect(tree.item("x111").getCheckboxProps().checked).toBe(true);
58
+ expect(tree.item("x112").getCheckboxProps().checked).toBe(false);
59
+ });
60
+
61
+ it("should create indeterminate state", async () => {
62
+ const tree = await factory.createTestCaseTree();
63
+ tree.item("x111").setChecked();
64
+ const refObject = { indeterminate: undefined };
65
+ tree.item("x11").getCheckboxProps().ref(refObject);
66
+ expect(refObject.indeterminate).toBe(true);
67
+ });
68
+
69
+ it("should not create indeterminate state", async () => {
70
+ const tree = await factory.createTestCaseTree();
71
+ const refObject = { indeterminate: undefined };
72
+ tree.item("x11").getCheckboxProps().ref(refObject);
73
+ expect(refObject.indeterminate).toBe(false);
74
+ });
75
+ });
76
+
77
+ it("should handle folder checking when canCheckFolders is true", async () => {
78
+ const tree = await factory
79
+ .with({ canCheckFolders: true })
80
+ .createTestCaseTree();
81
+
82
+ tree.item("x11").setChecked();
83
+ expect(tree.instance.getState().checkedItems).toContain("x11");
84
+ });
85
+
86
+ it("should handle folder checking when canCheckFolders is false", async () => {
87
+ const tree = await factory.createTestCaseTree();
88
+
89
+ tree.item("x11").setChecked();
90
+ expect(tree.instance.getState().checkedItems).toEqual(
91
+ expect.arrayContaining(["x111", "x112", "x113", "x114"]),
92
+ );
93
+ });
94
+
95
+ it("should turn folder indeterminate", async () => {
96
+ const tree = await factory.createTestCaseTree();
97
+
98
+ tree.item("x111").setChecked();
99
+ expect(tree.item("x11").getCheckedState()).toBe(CheckedState.Indeterminate);
100
+ });
101
+
102
+ it("should turn folder checked if all children are checked", async () => {
103
+ const tree = await factory
104
+ .with({
105
+ isItemFolder: (item) => item.getItemData().length < 4,
106
+ })
107
+ .createTestCaseTree();
108
+
109
+ tree.item("x11").setChecked();
110
+ tree.item("x12").setChecked();
111
+ tree.item("x13").setChecked();
112
+ expect(tree.item("x1").getCheckedState()).toBe(CheckedState.Indeterminate);
113
+ tree.do.selectItem("x14");
114
+ tree.item("x141").setChecked();
115
+ tree.item("x142").setChecked();
116
+ tree.item("x143").setChecked();
117
+ expect(tree.item("x1").getCheckedState()).toBe(CheckedState.Indeterminate);
118
+ tree.item("x144").setChecked();
119
+ expect(tree.item("x1").getCheckedState()).toBe(CheckedState.Checked);
120
+ });
121
+
122
+ it("should return correct checked state for items", async () => {
123
+ const tree = await factory.createTestCaseTree();
124
+ const item = tree.instance.getItemInstance("x111");
125
+
126
+ expect(item.getCheckedState()).toBe(CheckedState.Unchecked);
127
+
128
+ item.setChecked();
129
+ expect(item.getCheckedState()).toBe(CheckedState.Checked);
130
+
131
+ item.setUnchecked();
132
+ expect(item.getCheckedState()).toBe(CheckedState.Unchecked);
133
+ });
134
+ });
@@ -0,0 +1,119 @@
1
+ import { FeatureImplementation, TreeInstance } from "../../types/core";
2
+ import { makeStateUpdater } from "../../utils";
3
+ import { CheckedState } from "./types";
4
+ import { throwError } from "../../utilities/errors";
5
+
6
+ const getAllLoadedDescendants = <T>(
7
+ tree: TreeInstance<T>,
8
+ itemId: string,
9
+ ): string[] => {
10
+ if (!tree.getConfig().isItemFolder(tree.buildItemInstance(itemId))) {
11
+ return [itemId];
12
+ }
13
+ return tree
14
+ .retrieveChildrenIds(itemId)
15
+ .map((child) => getAllLoadedDescendants(tree, child))
16
+ .flat();
17
+ };
18
+
19
+ export const checkboxesFeature: FeatureImplementation = {
20
+ key: "checkboxes",
21
+
22
+ overwrites: ["selection"],
23
+
24
+ getInitialState: (initialState) => ({
25
+ checkedItems: [],
26
+ ...initialState,
27
+ }),
28
+
29
+ getDefaultConfig: (defaultConfig, tree) => {
30
+ const hasAsyncLoader = defaultConfig.features?.some(
31
+ (f) => f.key === "async-data-loader",
32
+ );
33
+ if (hasAsyncLoader && !defaultConfig.canCheckFolders) {
34
+ throwError(`!canCheckFolders not supported with async trees`);
35
+ }
36
+ return {
37
+ setCheckedItems: makeStateUpdater("checkedItems", tree),
38
+ canCheckFolders: hasAsyncLoader ?? false,
39
+ ...defaultConfig,
40
+ };
41
+ },
42
+
43
+ stateHandlerNames: {
44
+ checkedItems: "setCheckedItems",
45
+ },
46
+
47
+ treeInstance: {
48
+ setCheckedItems: ({ tree }, checkedItems) => {
49
+ tree.applySubStateUpdate("checkedItems", checkedItems);
50
+ },
51
+ },
52
+
53
+ itemInstance: {
54
+ getCheckboxProps: ({ item }) => {
55
+ const checkedState = item.getCheckedState();
56
+ return {
57
+ onChange: item.toggleCheckedState,
58
+ checked: checkedState === CheckedState.Checked,
59
+ ref: (r: any) => {
60
+ if (r) {
61
+ r.indeterminate = checkedState === CheckedState.Indeterminate;
62
+ }
63
+ },
64
+ };
65
+ },
66
+
67
+ toggleCheckedState: ({ item }) => {
68
+ if (item.getCheckedState() === CheckedState.Checked) {
69
+ item.setUnchecked();
70
+ } else {
71
+ item.setChecked();
72
+ }
73
+ },
74
+
75
+ getCheckedState: ({ item, tree, itemId }) => {
76
+ const { checkedItems } = tree.getState();
77
+
78
+ if (checkedItems.includes(itemId)) {
79
+ return CheckedState.Checked;
80
+ }
81
+
82
+ if (item.isFolder() && !tree.getConfig().canCheckFolders) {
83
+ const descendants = getAllLoadedDescendants(tree, itemId);
84
+ if (descendants.every((d) => checkedItems.includes(d))) {
85
+ return CheckedState.Checked;
86
+ }
87
+ if (descendants.some((d) => checkedItems.includes(d))) {
88
+ return CheckedState.Indeterminate;
89
+ }
90
+ }
91
+
92
+ return CheckedState.Unchecked;
93
+ },
94
+
95
+ setChecked: ({ item, tree, itemId }) => {
96
+ if (!item.isFolder() || tree.getConfig().canCheckFolders) {
97
+ tree.applySubStateUpdate("checkedItems", (items) => [...items, itemId]);
98
+ } else {
99
+ tree.applySubStateUpdate("checkedItems", (items) => [
100
+ ...items,
101
+ ...getAllLoadedDescendants(tree, itemId),
102
+ ]);
103
+ }
104
+ },
105
+
106
+ setUnchecked: ({ item, tree, itemId }) => {
107
+ if (!item.isFolder() || tree.getConfig().canCheckFolders) {
108
+ tree.applySubStateUpdate("checkedItems", (items) =>
109
+ items.filter((id) => id !== itemId),
110
+ );
111
+ } else {
112
+ const descendants = getAllLoadedDescendants(tree, itemId);
113
+ tree.applySubStateUpdate("checkedItems", (items) =>
114
+ items.filter((id) => !descendants.includes(id)),
115
+ );
116
+ }
117
+ },
118
+ },
119
+ };
@@ -0,0 +1,28 @@
1
+ import { SetStateFn } from "../../types/core";
2
+
3
+ export enum CheckedState {
4
+ Checked = "checked",
5
+ Unchecked = "unchecked",
6
+ Indeterminate = "indeterminate",
7
+ }
8
+
9
+ export type CheckboxesFeatureDef<T> = {
10
+ state: {
11
+ checkedItems: string[];
12
+ };
13
+ config: {
14
+ setCheckedItems?: SetStateFn<string[]>;
15
+ canCheckFolders?: boolean;
16
+ };
17
+ treeInstance: {
18
+ setCheckedItems: (checkedItems: string[]) => void;
19
+ };
20
+ itemInstance: {
21
+ setChecked: () => void;
22
+ setUnchecked: () => void;
23
+ toggleCheckedState: () => void;
24
+ getCheckedState: () => CheckedState;
25
+ getCheckboxProps: () => Record<string, any>;
26
+ };
27
+ hotkeys: never;
28
+ };
@@ -1,5 +1,5 @@
1
1
  import { FeatureImplementation } from "../../types/core";
2
- import { DndDataRef, DragLineData } from "./types";
2
+ import { DndDataRef, DragLineData, DragTarget } from "./types";
3
3
  import {
4
4
  canDrop,
5
5
  getDragCode,
@@ -83,10 +83,38 @@ export const dragAndDropFeature: FeatureImplementation = {
83
83
  : { display: "none" };
84
84
  },
85
85
 
86
- getContainerProps: ({ prev }, treeLabel) => {
86
+ getContainerProps: ({ prev, tree }, treeLabel) => {
87
87
  const prevProps = prev?.(treeLabel);
88
88
  return {
89
89
  ...prevProps,
90
+
91
+ onDragOver: (e: DragEvent) => {
92
+ e.preventDefault();
93
+ },
94
+
95
+ onDrop: async (e: DragEvent) => {
96
+ // TODO merge implementation with itemInstance.onDrop
97
+ const dataRef = tree.getDataRef<DndDataRef>();
98
+ const target: DragTarget<any> = { item: tree.getRootItem() };
99
+
100
+ if (!canDrop(e.dataTransfer, target, tree)) {
101
+ return;
102
+ }
103
+
104
+ e.preventDefault();
105
+ const config = tree.getConfig();
106
+ const draggedItems = tree.getState().dnd?.draggedItems;
107
+
108
+ dataRef.current.lastDragCode = undefined;
109
+ tree.applySubStateUpdate("dnd", null);
110
+
111
+ if (draggedItems) {
112
+ await config.onDrop?.(draggedItems, target);
113
+ } else if (e.dataTransfer) {
114
+ await config.onDropForeignDragObject?.(e.dataTransfer, target);
115
+ }
116
+ },
117
+
90
118
  style: {
91
119
  ...prevProps?.style,
92
120
  position: "relative",
@@ -101,6 +129,8 @@ export const dragAndDropFeature: FeatureImplementation = {
101
129
 
102
130
  draggable: true,
103
131
 
132
+ onDragEnter: (e: DragEvent) => e.preventDefault(),
133
+
104
134
  onDragStart: (e: DragEvent) => {
105
135
  const selectedItems = tree.getSelectedItems();
106
136
  const items = selectedItems.includes(item) ? selectedItems : [item];
@@ -115,6 +145,11 @@ export const dragAndDropFeature: FeatureImplementation = {
115
145
  return;
116
146
  }
117
147
 
148
+ if (config.setDragImage) {
149
+ const { imgElement, xOffset, yOffset } = config.setDragImage(items);
150
+ e.dataTransfer?.setDragImage(imgElement, xOffset ?? 0, yOffset ?? 0);
151
+ }
152
+
118
153
  if (config.createForeignDragObject) {
119
154
  const { format, data } = config.createForeignDragObject(items);
120
155
  e.dataTransfer?.setData(format, data);
@@ -186,6 +221,7 @@ export const dragAndDropFeature: FeatureImplementation = {
186
221
  },
187
222
 
188
223
  onDrop: async (e: DragEvent) => {
224
+ e.stopPropagation();
189
225
  const dataRef = tree.getDataRef<DndDataRef>();
190
226
  const target = getDragTarget(e, item, tree);
191
227
 
@@ -58,6 +58,11 @@ export type DragAndDropFeatureDef<T> = {
58
58
  format: string;
59
59
  data: any;
60
60
  };
61
+ setDragImage?: (items: ItemInstance<T>[]) => {
62
+ imgElement: Element;
63
+ xOffset?: number;
64
+ yOffset?: number;
65
+ };
61
66
  canDropForeignDragObject?: (
62
67
  dataTransfer: DataTransfer,
63
68
  target: DragTarget<T>,
@@ -36,6 +36,8 @@ export type MainFeatureDef<T = any> = {
36
36
  stateName: K,
37
37
  updater: Updater<TreeState<T>[K]>,
38
38
  ) => void;
39
+ /** @internal */
40
+ buildItemInstance: (itemId: string) => ItemInstance<T>;
39
41
  setState: SetStateFn<TreeState<T>>;
40
42
  getState: () => TreeState<T>;
41
43
  setConfig: SetStateFn<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");
@@ -82,6 +82,11 @@ export const treeFeature: FeatureImplementation<any> = {
82
82
  );
83
83
  },
84
84
 
85
+ getRootItem: ({ tree }) => {
86
+ const { rootItemId } = tree.getConfig();
87
+ return tree.getItemInstance(rootItemId);
88
+ },
89
+
85
90
  focusNextItem: ({ tree }) => {
86
91
  const focused = tree.getFocusedItem().getItemMeta();
87
92
  if (!focused) return;
@@ -126,6 +131,7 @@ export const treeFeature: FeatureImplementation<any> = {
126
131
  item.getElement()?.scrollIntoView(scrollIntoViewArg);
127
132
  },
128
133
  getId: ({ itemId }) => itemId,
134
+ getKey: ({ itemId }) => itemId, // TODO apply to all stories to use
129
135
  getProps: ({ item, prev }) => {
130
136
  const itemMeta = item.getItemMeta();
131
137
  return {
@@ -20,7 +20,7 @@ export type TreeFeatureDef<T> = {
20
20
  focusedItem: string | null;
21
21
  };
22
22
  config: {
23
- isItemFolder: (item: ItemInstance<T>) => boolean;
23
+ isItemFolder: (item: ItemInstance<T>) => boolean; // TODO:breaking use item data as payload
24
24
  getItemName: (item: ItemInstance<T>) => string;
25
25
 
26
26
  onPrimaryAction?: (item: ItemInstance<T>) => void;
@@ -33,7 +33,8 @@ export type TreeFeatureDef<T> = {
33
33
  /** @internal */
34
34
  getItemsMeta: () => ItemMeta[];
35
35
 
36
- getFocusedItem: () => ItemInstance<any>;
36
+ getFocusedItem: () => ItemInstance<T>;
37
+ getRootItem: () => ItemInstance<T>;
37
38
  focusNextItem: () => void;
38
39
  focusPreviousItem: () => void;
39
40
  updateDomFocus: () => void;
@@ -44,6 +45,7 @@ export type TreeFeatureDef<T> = {
44
45
  };
45
46
  itemInstance: {
46
47
  getId: () => string;
48
+ getKey: () => string;
47
49
  getProps: () => Record<string, any>;
48
50
  getItemName: () => string;
49
51
  getItemData: () => T;
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ export { MainFeatureDef, InstanceBuilder } from "./features/main/types";
6
6
  export * from "./features/drag-and-drop/types";
7
7
  export * from "./features/keyboard-drag-and-drop/types";
8
8
  export * from "./features/selection/types";
9
+ export * from "./features/checkboxes/types";
9
10
  export * from "./features/async-data-loader/types";
10
11
  export * from "./features/sync-data-loader/types";
11
12
  export * from "./features/hotkeys-core/types";
@@ -15,6 +16,7 @@ export * from "./features/expand-all/types";
15
16
  export * from "./features/prop-memoization/types";
16
17
 
17
18
  export * from "./features/selection/feature";
19
+ export * from "./features/checkboxes/feature";
18
20
  export * from "./features/hotkeys-core/feature";
19
21
  export * from "./features/async-data-loader/feature";
20
22
  export * from "./features/sync-data-loader/feature";
@@ -31,3 +33,6 @@ export * from "./utilities/remove-items-from-parents";
31
33
 
32
34
  export * from "./core/build-proxified-instance";
33
35
  export * from "./core/build-static-instance";
36
+
37
+ export { makeStateUpdater } from "./utils";
38
+ export { isOrderedDragTarget } from "./features/drag-and-drop/utils";
@@ -78,6 +78,7 @@ export class TestTree<T = string> {
78
78
  get instance() {
79
79
  if (!this.treeInstance) {
80
80
  this.treeInstance = createTree(this.config);
81
+ this.treeInstance.rebuildTree();
81
82
  }
82
83
  return this.treeInstance;
83
84
  }
package/src/types/core.ts CHANGED
@@ -13,6 +13,7 @@ import { RenamingFeatureDef } from "../features/renaming/types";
13
13
  import { ExpandAllFeatureDef } from "../features/expand-all/types";
14
14
  import { PropMemoizationFeatureDef } from "../features/prop-memoization/types";
15
15
  import { KeyboardDragAndDropFeatureDef } from "../features/keyboard-drag-and-drop/types";
16
+ import { CheckboxesFeatureDef } from "../features/checkboxes/types";
16
17
 
17
18
  export type Updater<T> = T | ((old: T) => T);
18
19
  export type SetStateFn<T> = (updaterOrValue: Updater<T>) => void;
@@ -53,6 +54,7 @@ export type RegisteredFeatures<T> =
53
54
  | MainFeatureDef<T>
54
55
  | TreeFeatureDef<T>
55
56
  | SelectionFeatureDef<T>
57
+ | CheckboxesFeatureDef<T>
56
58
  | DragAndDropFeatureDef<T>
57
59
  | KeyboardDragAndDropFeatureDef<T>
58
60
  | HotkeysCoreFeatureDef<T>