@headless-tree/core 0.0.10 → 0.0.12

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 (148) hide show
  1. package/CHANGELOG.md +12 -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 +26 -0
  6. package/lib/cjs/core/create-tree.js +62 -40
  7. package/lib/cjs/features/async-data-loader/feature.d.ts +1 -4
  8. package/lib/cjs/features/async-data-loader/feature.js +35 -23
  9. package/lib/cjs/features/async-data-loader/types.d.ts +4 -6
  10. package/lib/cjs/features/drag-and-drop/feature.d.ts +2 -3
  11. package/lib/cjs/features/drag-and-drop/feature.js +79 -44
  12. package/lib/cjs/features/drag-and-drop/types.d.ts +15 -6
  13. package/lib/cjs/features/drag-and-drop/utils.d.ts +2 -3
  14. package/lib/cjs/features/drag-and-drop/utils.js +140 -37
  15. package/lib/cjs/features/expand-all/feature.d.ts +1 -5
  16. package/lib/cjs/features/expand-all/feature.js +12 -6
  17. package/lib/cjs/features/hotkeys-core/feature.d.ts +1 -3
  18. package/lib/cjs/features/main/types.d.ts +8 -2
  19. package/lib/cjs/features/prop-memoization/feature.d.ts +2 -0
  20. package/lib/cjs/features/prop-memoization/feature.js +48 -0
  21. package/lib/cjs/features/prop-memoization/types.d.ts +10 -0
  22. package/lib/cjs/features/prop-memoization/types.js +2 -0
  23. package/lib/cjs/features/renaming/feature.d.ts +1 -4
  24. package/lib/cjs/features/renaming/feature.js +36 -22
  25. package/lib/cjs/features/renaming/types.d.ts +2 -2
  26. package/lib/cjs/features/search/feature.d.ts +1 -4
  27. package/lib/cjs/features/search/feature.js +38 -24
  28. package/lib/cjs/features/search/types.d.ts +0 -1
  29. package/lib/cjs/features/selection/feature.d.ts +1 -4
  30. package/lib/cjs/features/selection/feature.js +54 -35
  31. package/lib/cjs/features/selection/types.d.ts +1 -1
  32. package/lib/cjs/features/sync-data-loader/feature.d.ts +1 -3
  33. package/lib/cjs/features/sync-data-loader/feature.js +7 -2
  34. package/lib/cjs/features/tree/feature.d.ts +1 -5
  35. package/lib/cjs/features/tree/feature.js +97 -92
  36. package/lib/cjs/features/tree/types.d.ts +5 -8
  37. package/lib/cjs/index.d.ts +5 -1
  38. package/lib/cjs/index.js +4 -1
  39. package/lib/cjs/mddocs-entry.d.ts +10 -0
  40. package/lib/cjs/test-utils/test-tree-do.d.ts +23 -0
  41. package/lib/cjs/test-utils/test-tree-do.js +99 -0
  42. package/lib/cjs/test-utils/test-tree-expect.d.ts +15 -0
  43. package/lib/cjs/test-utils/test-tree-expect.js +62 -0
  44. package/lib/cjs/test-utils/test-tree.d.ts +47 -0
  45. package/lib/cjs/test-utils/test-tree.js +203 -0
  46. package/lib/cjs/types/core.d.ts +39 -24
  47. package/lib/cjs/utilities/errors.d.ts +1 -0
  48. package/lib/cjs/utilities/errors.js +5 -0
  49. package/lib/cjs/utilities/insert-items-at-target.js +10 -3
  50. package/lib/cjs/utilities/remove-items-from-parents.js +14 -8
  51. package/lib/cjs/utils.d.ts +3 -3
  52. package/lib/cjs/utils.js +6 -6
  53. package/lib/esm/core/build-proxified-instance.d.ts +2 -0
  54. package/lib/esm/core/build-proxified-instance.js +54 -0
  55. package/lib/esm/core/build-static-instance.d.ts +2 -0
  56. package/lib/esm/core/build-static-instance.js +22 -0
  57. package/lib/esm/core/create-tree.js +62 -40
  58. package/lib/esm/features/async-data-loader/feature.d.ts +1 -4
  59. package/lib/esm/features/async-data-loader/feature.js +35 -23
  60. package/lib/esm/features/async-data-loader/types.d.ts +4 -6
  61. package/lib/esm/features/drag-and-drop/feature.d.ts +2 -3
  62. package/lib/esm/features/drag-and-drop/feature.js +79 -44
  63. package/lib/esm/features/drag-and-drop/types.d.ts +15 -6
  64. package/lib/esm/features/drag-and-drop/utils.d.ts +2 -3
  65. package/lib/esm/features/drag-and-drop/utils.js +138 -34
  66. package/lib/esm/features/expand-all/feature.d.ts +1 -5
  67. package/lib/esm/features/expand-all/feature.js +12 -6
  68. package/lib/esm/features/hotkeys-core/feature.d.ts +1 -3
  69. package/lib/esm/features/main/types.d.ts +8 -2
  70. package/lib/esm/features/prop-memoization/feature.d.ts +2 -0
  71. package/lib/esm/features/prop-memoization/feature.js +45 -0
  72. package/lib/esm/features/prop-memoization/types.d.ts +10 -0
  73. package/lib/esm/features/prop-memoization/types.js +1 -0
  74. package/lib/esm/features/renaming/feature.d.ts +1 -4
  75. package/lib/esm/features/renaming/feature.js +36 -22
  76. package/lib/esm/features/renaming/types.d.ts +2 -2
  77. package/lib/esm/features/search/feature.d.ts +1 -4
  78. package/lib/esm/features/search/feature.js +38 -24
  79. package/lib/esm/features/search/types.d.ts +0 -1
  80. package/lib/esm/features/selection/feature.d.ts +1 -4
  81. package/lib/esm/features/selection/feature.js +54 -35
  82. package/lib/esm/features/selection/types.d.ts +1 -1
  83. package/lib/esm/features/sync-data-loader/feature.d.ts +1 -3
  84. package/lib/esm/features/sync-data-loader/feature.js +7 -2
  85. package/lib/esm/features/tree/feature.d.ts +1 -5
  86. package/lib/esm/features/tree/feature.js +98 -93
  87. package/lib/esm/features/tree/types.d.ts +5 -8
  88. package/lib/esm/index.d.ts +5 -1
  89. package/lib/esm/index.js +4 -1
  90. package/lib/esm/mddocs-entry.d.ts +10 -0
  91. package/lib/esm/test-utils/test-tree-do.d.ts +23 -0
  92. package/lib/esm/test-utils/test-tree-do.js +95 -0
  93. package/lib/esm/test-utils/test-tree-expect.d.ts +15 -0
  94. package/lib/esm/test-utils/test-tree-expect.js +58 -0
  95. package/lib/esm/test-utils/test-tree.d.ts +47 -0
  96. package/lib/esm/test-utils/test-tree.js +199 -0
  97. package/lib/esm/types/core.d.ts +39 -24
  98. package/lib/esm/utilities/errors.d.ts +1 -0
  99. package/lib/esm/utilities/errors.js +1 -0
  100. package/lib/esm/utilities/insert-items-at-target.js +10 -3
  101. package/lib/esm/utilities/remove-items-from-parents.js +14 -8
  102. package/lib/esm/utils.d.ts +3 -3
  103. package/lib/esm/utils.js +3 -3
  104. package/package.json +7 -3
  105. package/src/core/build-proxified-instance.ts +117 -0
  106. package/src/core/build-static-instance.ts +27 -0
  107. package/src/core/core.spec.ts +210 -0
  108. package/src/core/create-tree.ts +73 -78
  109. package/src/features/async-data-loader/async-data-loader.spec.ts +124 -0
  110. package/src/features/async-data-loader/feature.ts +34 -44
  111. package/src/features/async-data-loader/types.ts +4 -6
  112. package/src/features/drag-and-drop/drag-and-drop.spec.ts +717 -0
  113. package/src/features/drag-and-drop/feature.ts +88 -63
  114. package/src/features/drag-and-drop/types.ts +24 -10
  115. package/src/features/drag-and-drop/utils.ts +197 -56
  116. package/src/features/expand-all/expand-all.spec.ts +56 -0
  117. package/src/features/expand-all/feature.ts +9 -24
  118. package/src/features/hotkeys-core/feature.ts +5 -14
  119. package/src/features/main/types.ts +14 -1
  120. package/src/features/prop-memoization/feature.ts +51 -0
  121. package/src/features/prop-memoization/prop-memoization.spec.ts +68 -0
  122. package/src/features/prop-memoization/types.ts +11 -0
  123. package/src/features/renaming/feature.ts +37 -45
  124. package/src/features/renaming/renaming.spec.ts +127 -0
  125. package/src/features/renaming/types.ts +2 -2
  126. package/src/features/search/feature.ts +36 -46
  127. package/src/features/search/search.spec.ts +117 -0
  128. package/src/features/search/types.ts +0 -1
  129. package/src/features/selection/feature.ts +50 -53
  130. package/src/features/selection/selection.spec.ts +219 -0
  131. package/src/features/selection/types.ts +0 -2
  132. package/src/features/sync-data-loader/feature.ts +9 -18
  133. package/src/features/tree/feature.ts +101 -144
  134. package/src/features/tree/tree.spec.ts +475 -0
  135. package/src/features/tree/types.ts +5 -9
  136. package/src/index.ts +6 -1
  137. package/src/mddocs-entry.ts +13 -0
  138. package/src/test-utils/test-tree-do.ts +136 -0
  139. package/src/test-utils/test-tree-expect.ts +86 -0
  140. package/src/test-utils/test-tree.ts +227 -0
  141. package/src/types/core.ts +76 -108
  142. package/src/utilities/errors.ts +2 -0
  143. package/src/utilities/insert-items-at-target.ts +10 -3
  144. package/src/utilities/remove-items-from-parents.ts +15 -10
  145. package/src/utils.spec.ts +89 -0
  146. package/src/utils.ts +6 -6
  147. package/tsconfig.json +1 -0
  148. package/vitest.config.ts +6 -0
@@ -8,11 +8,15 @@ export const insertItemsAtTarget = <T>(
8
8
  ) => {
9
9
  // add moved items to new common parent, if dropped onto parent
10
10
  if (target.childIndex === null) {
11
- onChangeChildren(target.item, [
11
+ const newChildren = [
12
12
  ...target.item.getChildren().map((item) => item.getId()),
13
13
  ...itemIds,
14
- ]);
15
- // TODO items[0].getTree().rebuildTree();
14
+ ];
15
+ onChangeChildren(target.item, newChildren);
16
+ if (target.item && "updateCachedChildrenIds" in target.item) {
17
+ target.item.updateCachedChildrenIds(newChildren);
18
+ }
19
+ target.item.getTree().rebuildTree();
16
20
  return;
17
21
  }
18
22
 
@@ -26,5 +30,8 @@ export const insertItemsAtTarget = <T>(
26
30
 
27
31
  onChangeChildren(target.item, newChildren);
28
32
 
33
+ if (target.item && "updateCachedChildrenIds" in target.item) {
34
+ target.item.updateCachedChildrenIds(newChildren);
35
+ }
29
36
  target.item.getTree().rebuildTree();
30
37
  };
@@ -4,16 +4,21 @@ export const removeItemsFromParents = <T>(
4
4
  movedItems: ItemInstance<T>[],
5
5
  onChangeChildren: (item: ItemInstance<T>, newChildrenIds: string[]) => void,
6
6
  ) => {
7
- // TODO bulk sibling changes together
8
- for (const item of movedItems) {
9
- const siblings = item.getParent()?.getChildren();
10
- if (siblings) {
11
- onChangeChildren(
12
- item.getParent(),
13
- siblings
14
- .filter((sibling) => sibling.getId() !== item.getId())
15
- .map((i) => i.getId()),
16
- );
7
+ const movedItemsIds = movedItems.map((item) => item.getId());
8
+ const uniqueParents = [
9
+ ...new Set(movedItems.map((item) => item.getParent())),
10
+ ];
11
+
12
+ for (const parent of uniqueParents) {
13
+ const siblings = parent?.getChildren();
14
+ if (siblings && parent) {
15
+ const newChildren = siblings
16
+ .filter((sibling) => !movedItemsIds.includes(sibling.getId()))
17
+ .map((i) => i.getId());
18
+ onChangeChildren(parent, newChildren);
19
+ if (parent && "updateCachedChildrenIds" in parent) {
20
+ parent?.updateCachedChildrenIds(newChildren);
21
+ }
17
22
  }
18
23
  }
19
24
 
@@ -0,0 +1,89 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { makeStateUpdater, memo, poll } from "./utils";
3
+
4
+ vi.useFakeTimers({ shouldAdvanceTime: true });
5
+
6
+ describe("utilities", () => {
7
+ describe("memo", () => {
8
+ it("returns same value for same arguments", () => {
9
+ const fn = vi.fn(
10
+ (a: number, b: number, c: number, d: number) => a + b + c + d,
11
+ );
12
+ const memoized = memo((c: number, d: number) => [1, 1, c, d], fn);
13
+ expect(memoized(1, 1)).toBe(4);
14
+ expect(memoized(1, 1)).toBe(4);
15
+ expect(memoized(1, 1)).toBe(4);
16
+ expect(fn).toHaveBeenCalledTimes(1);
17
+ });
18
+
19
+ it("returns different values for different arguments", () => {
20
+ const fn = vi.fn(
21
+ (a: number, b: number, c: number, d: number) => a + b + c + d,
22
+ );
23
+ const memoized = memo((c: number, d: number) => [1, 1, c, d], fn);
24
+ expect(memoized(1, 1)).toBe(4);
25
+ expect(memoized(1, 2)).toBe(5);
26
+ expect(memoized(1, 2)).toBe(5);
27
+ expect(fn).toHaveBeenCalledTimes(2);
28
+ });
29
+ });
30
+
31
+ describe("makeStateUpdater", () => {
32
+ it("updates the state correctly", () => {
33
+ const instance = {
34
+ setState: vi.fn((updater) => {
35
+ const oldState = { focusedItem: "oldValue" };
36
+ const newState = updater(oldState);
37
+ return newState;
38
+ }),
39
+ };
40
+
41
+ const updater = makeStateUpdater("focusedItem", instance);
42
+ updater("newValue");
43
+
44
+ expect(instance.setState).toHaveBeenCalledTimes(1);
45
+ expect(instance.setState).toHaveBeenCalledWith(expect.any(Function));
46
+ const stateUpdateFn = instance.setState.mock.calls[0][0];
47
+ expect(stateUpdateFn({ focusedItem: "oldValue" })).toEqual({
48
+ focusedItem: "newValue",
49
+ });
50
+ });
51
+
52
+ it("updates the state using a function updater", () => {
53
+ const instance = {
54
+ setState: vi.fn((updater) => {
55
+ const oldState = { focusedItem: "oldValue" };
56
+ const newState = updater(oldState);
57
+ return newState;
58
+ }),
59
+ };
60
+
61
+ const updater = makeStateUpdater("focusedItem", instance);
62
+ updater((prev) => `${prev}Updated`);
63
+
64
+ expect(instance.setState).toHaveBeenCalledTimes(1);
65
+ expect(instance.setState).toHaveBeenCalledWith(expect.any(Function));
66
+ const stateUpdateFn = instance.setState.mock.calls[0][0];
67
+ expect(stateUpdateFn({ focusedItem: "oldValue" })).toEqual({
68
+ focusedItem: "oldValueUpdated",
69
+ });
70
+ });
71
+ });
72
+
73
+ describe("poll", () => {
74
+ it("resolves when the condition is met within the timeout", async () => {
75
+ const condition = vi
76
+ .fn()
77
+ .mockReturnValueOnce(false)
78
+ .mockReturnValueOnce(true);
79
+ await expect(poll(condition, 50, 200)).resolves.toBeUndefined();
80
+ expect(condition).toHaveBeenCalledTimes(2);
81
+ });
82
+
83
+ it("resolves immediately if the condition is already met", async () => {
84
+ const condition = vi.fn().mockReturnValue(true);
85
+ await expect(poll(condition, 50, 200)).resolves.toBeUndefined();
86
+ expect(condition).toHaveBeenCalledTimes(1);
87
+ });
88
+ });
89
+ });
package/src/utils.ts CHANGED
@@ -1,16 +1,16 @@
1
- import { TreeState, Updater } from "./types/core";
1
+ import { SetStateFn, TreeState, Updater } from "./types/core";
2
2
 
3
3
  export type NoInfer<T> = [T][T extends any ? 0 : never];
4
4
 
5
- export const memo = <D extends readonly any[], R>(
5
+ export const memo = <D extends readonly any[], P extends readonly any[], R>(
6
+ deps: (...args: [...P]) => [...D],
6
7
  fn: (...args: [...D]) => R,
7
- deps: () => [...D],
8
8
  ) => {
9
9
  let value: R | undefined;
10
10
  let oldDeps: D | null = null;
11
11
 
12
- return () => {
13
- const newDeps = deps();
12
+ return (...a: [...P]) => {
13
+ const newDeps = deps(...a);
14
14
 
15
15
  if (!value) {
16
16
  value = fn(...newDeps);
@@ -41,7 +41,7 @@ export function functionalUpdate<T>(updater: Updater<T>, input: T): T {
41
41
  export function makeStateUpdater<K extends keyof TreeState<any>>(
42
42
  key: K,
43
43
  instance: unknown,
44
- ) {
44
+ ): SetStateFn<TreeState<any>[K]> {
45
45
  return (updater: Updater<TreeState<any>[K]>) => {
46
46
  (instance as any).setState(<TTableState>(old: TTableState) => {
47
47
  return {
package/tsconfig.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "extends": "../../tsconfig.json",
3
3
  "include": ["./src/**/*"],
4
+ "exclude": ["./src/**/*.spec.tsx", "./src/**/*.spec.ts", "./src/**/*.stories.tsx"],
4
5
  "compilerOptions": {
5
6
  "outDir": "lib/esm"
6
7
  }
@@ -0,0 +1,6 @@
1
+ // eslint-disable-next-line import/no-extraneous-dependencies
2
+ import { defineConfig } from "vitest/config";
3
+
4
+ export default defineConfig({
5
+ test: {},
6
+ });