@headless-tree/core 0.0.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 (87) hide show
  1. package/lib/core/create-tree.d.ts +2 -0
  2. package/lib/core/create-tree.js +116 -0
  3. package/lib/data-adapters/nested-data-adapter.d.ts +9 -0
  4. package/lib/data-adapters/nested-data-adapter.js +32 -0
  5. package/lib/data-adapters/types.d.ts +7 -0
  6. package/lib/data-adapters/types.js +2 -0
  7. package/lib/features/async-data-loader/feature.d.ts +5 -0
  8. package/lib/features/async-data-loader/feature.js +80 -0
  9. package/lib/features/async-data-loader/types.d.ts +41 -0
  10. package/lib/features/async-data-loader/types.js +2 -0
  11. package/lib/features/drag-and-drop/feature.d.ts +3 -0
  12. package/lib/features/drag-and-drop/feature.js +144 -0
  13. package/lib/features/drag-and-drop/types.d.ts +49 -0
  14. package/lib/features/drag-and-drop/types.js +9 -0
  15. package/lib/features/drag-and-drop/utils.d.ts +7 -0
  16. package/lib/features/drag-and-drop/utils.js +121 -0
  17. package/lib/features/expand-all/feature.d.ts +6 -0
  18. package/lib/features/expand-all/feature.js +39 -0
  19. package/lib/features/expand-all/types.d.ts +17 -0
  20. package/lib/features/expand-all/types.js +2 -0
  21. package/lib/features/hotkeys-core/feature.d.ts +4 -0
  22. package/lib/features/hotkeys-core/feature.js +73 -0
  23. package/lib/features/hotkeys-core/types.d.ts +25 -0
  24. package/lib/features/hotkeys-core/types.js +2 -0
  25. package/lib/features/main/types.d.ts +36 -0
  26. package/lib/features/main/types.js +2 -0
  27. package/lib/features/renaming/feature.d.ts +5 -0
  28. package/lib/features/renaming/feature.js +65 -0
  29. package/lib/features/renaming/types.d.ts +27 -0
  30. package/lib/features/renaming/types.js +2 -0
  31. package/lib/features/search/feature.d.ts +5 -0
  32. package/lib/features/search/feature.js +90 -0
  33. package/lib/features/search/types.d.ts +33 -0
  34. package/lib/features/search/types.js +2 -0
  35. package/lib/features/selection/feature.d.ts +5 -0
  36. package/lib/features/selection/feature.js +112 -0
  37. package/lib/features/selection/types.d.ts +21 -0
  38. package/lib/features/selection/types.js +2 -0
  39. package/lib/features/sync-data-loader/feature.d.ts +4 -0
  40. package/lib/features/sync-data-loader/feature.js +9 -0
  41. package/lib/features/sync-data-loader/types.d.ts +19 -0
  42. package/lib/features/sync-data-loader/types.js +2 -0
  43. package/lib/features/tree/feature.d.ts +6 -0
  44. package/lib/features/tree/feature.js +216 -0
  45. package/lib/features/tree/types.d.ts +57 -0
  46. package/lib/features/tree/types.js +2 -0
  47. package/lib/index.d.ts +21 -0
  48. package/lib/index.js +37 -0
  49. package/lib/mddocs-entry.d.ts +21 -0
  50. package/lib/mddocs-entry.js +17 -0
  51. package/lib/types/core.d.ts +68 -0
  52. package/lib/types/core.js +2 -0
  53. package/lib/types/deep-merge.d.ts +13 -0
  54. package/lib/types/deep-merge.js +2 -0
  55. package/lib/utils.d.ts +9 -0
  56. package/lib/utils.js +105 -0
  57. package/package.json +15 -0
  58. package/src/core/create-tree.ts +195 -0
  59. package/src/data-adapters/nested-data-adapter.ts +48 -0
  60. package/src/data-adapters/types.ts +9 -0
  61. package/src/features/async-data-loader/feature.ts +117 -0
  62. package/src/features/async-data-loader/types.ts +41 -0
  63. package/src/features/drag-and-drop/feature.ts +153 -0
  64. package/src/features/drag-and-drop/types.ts +64 -0
  65. package/src/features/drag-and-drop/utils.ts +88 -0
  66. package/src/features/expand-all/feature.ts +62 -0
  67. package/src/features/expand-all/types.ts +13 -0
  68. package/src/features/hotkeys-core/feature.ts +111 -0
  69. package/src/features/hotkeys-core/types.ts +36 -0
  70. package/src/features/main/types.ts +41 -0
  71. package/src/features/renaming/feature.ts +102 -0
  72. package/src/features/renaming/types.ts +28 -0
  73. package/src/features/search/feature.ts +142 -0
  74. package/src/features/search/types.ts +39 -0
  75. package/src/features/selection/feature.ts +153 -0
  76. package/src/features/selection/types.ts +28 -0
  77. package/src/features/sync-data-loader/feature.ts +27 -0
  78. package/src/features/sync-data-loader/types.ts +20 -0
  79. package/src/features/tree/feature.ts +307 -0
  80. package/src/features/tree/types.ts +70 -0
  81. package/src/index.ts +23 -0
  82. package/src/mddocs-entry.ts +26 -0
  83. package/src/types/core.ts +182 -0
  84. package/src/types/deep-merge.ts +31 -0
  85. package/src/utils.ts +136 -0
  86. package/tsconfig.json +7 -0
  87. package/typedoc.json +4 -0
@@ -0,0 +1,182 @@
1
+ import { DragAndDropFeatureDef } from "../features/drag-and-drop/types";
2
+ import { MainFeatureDef } from "../features/main/types";
3
+ import { SelectionFeatureDef } from "../features/selection/types";
4
+ import { TreeFeatureDef } from "../features/tree/types";
5
+ import {
6
+ HotkeyConfig,
7
+ HotkeysCoreFeatureDef,
8
+ } from "../features/hotkeys-core/types";
9
+ import { SyncDataLoaderFeatureDef } from "../features/sync-data-loader/types";
10
+ import { AsyncDataLoaderFeatureDef } from "../features/async-data-loader/types";
11
+ import { SearchFeatureDef } from "../features/search/types";
12
+ import { RenamingFeatureDef } from "../features/renaming/types";
13
+ import { ExpandAllFeatureDef } from "../features/expand-all/types";
14
+
15
+ export type Updater<T> = T | ((old: T) => T);
16
+ export type SetStateFn<T> = (updaterOrValue: Updater<T>) => void;
17
+
18
+ export type FeatureDef = {
19
+ state: object;
20
+ config: object;
21
+ treeInstance: object;
22
+ itemInstance: object;
23
+ hotkeys: string;
24
+ };
25
+
26
+ export type EmptyFeatureDef = {
27
+ state: {};
28
+ config: {};
29
+ treeInstance: {};
30
+ itemInstance: {};
31
+ hotkeys: never;
32
+ };
33
+
34
+ type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (
35
+ x: infer R
36
+ ) => any
37
+ ? R
38
+ : never;
39
+
40
+ export type DefaultFeatures<T> = MainFeatureDef | TreeFeatureDef<T>;
41
+
42
+ export type FeatureDefs<T> =
43
+ | MainFeatureDef
44
+ | TreeFeatureDef<T>
45
+ | SelectionFeatureDef<T>
46
+ | DragAndDropFeatureDef<T>
47
+ | HotkeysCoreFeatureDef<T>
48
+ | SyncDataLoaderFeatureDef<T>
49
+ | AsyncDataLoaderFeatureDef<T>
50
+ | SearchFeatureDef<T>
51
+ | RenamingFeatureDef<T>
52
+ | ExpandAllFeatureDef;
53
+
54
+ type MergedFeatures<F extends FeatureDef> = {
55
+ state: UnionToIntersection<F["state"]>;
56
+ config: UnionToIntersection<F["config"]>;
57
+ treeInstance: UnionToIntersection<F["treeInstance"]>;
58
+ itemInstance: UnionToIntersection<F["itemInstance"]>;
59
+ hotkeys: F["hotkeys"];
60
+ };
61
+
62
+ type TreeStateType<T> = MainFeatureDef["state"] &
63
+ TreeFeatureDef<T>["state"] &
64
+ SelectionFeatureDef<T>["state"] &
65
+ DragAndDropFeatureDef<T>["state"] &
66
+ HotkeysCoreFeatureDef<T>["state"] &
67
+ SyncDataLoaderFeatureDef<T>["state"] &
68
+ AsyncDataLoaderFeatureDef<T>["state"] &
69
+ SearchFeatureDef<T>["state"] &
70
+ RenamingFeatureDef<T>["state"] &
71
+ ExpandAllFeatureDef["state"];
72
+ export interface TreeState<T> extends TreeStateType<T> {}
73
+
74
+ type TreeConfigType<T> = MainFeatureDef["config"] &
75
+ TreeFeatureDef<T>["config"] &
76
+ SelectionFeatureDef<T>["config"] &
77
+ DragAndDropFeatureDef<T>["config"] &
78
+ HotkeysCoreFeatureDef<T>["config"] &
79
+ SyncDataLoaderFeatureDef<T>["config"] &
80
+ AsyncDataLoaderFeatureDef<T>["config"] &
81
+ SearchFeatureDef<T>["config"] &
82
+ RenamingFeatureDef<T>["config"] &
83
+ ExpandAllFeatureDef["config"];
84
+ export interface TreeConfig<T> extends TreeConfigType<T> {}
85
+
86
+ type TreeInstanceType<T> = MainFeatureDef["treeInstance"] &
87
+ TreeFeatureDef<T>["treeInstance"] &
88
+ SelectionFeatureDef<T>["treeInstance"] &
89
+ DragAndDropFeatureDef<T>["treeInstance"] &
90
+ HotkeysCoreFeatureDef<T>["treeInstance"] &
91
+ SyncDataLoaderFeatureDef<T>["treeInstance"] &
92
+ AsyncDataLoaderFeatureDef<T>["treeInstance"] &
93
+ SearchFeatureDef<T>["treeInstance"] &
94
+ RenamingFeatureDef<T>["treeInstance"] &
95
+ ExpandAllFeatureDef["treeInstance"];
96
+ export interface TreeInstance<T> extends TreeInstanceType<T> {}
97
+
98
+ type ItemInstanceType<T> = MainFeatureDef["itemInstance"] &
99
+ TreeFeatureDef<T>["itemInstance"] &
100
+ SelectionFeatureDef<T>["itemInstance"] &
101
+ DragAndDropFeatureDef<T>["itemInstance"] &
102
+ HotkeysCoreFeatureDef<T>["itemInstance"] &
103
+ SyncDataLoaderFeatureDef<T>["itemInstance"] &
104
+ AsyncDataLoaderFeatureDef<T>["itemInstance"] &
105
+ SearchFeatureDef<T>["itemInstance"] &
106
+ RenamingFeatureDef<T>["itemInstance"] &
107
+ ExpandAllFeatureDef["itemInstance"];
108
+ export interface ItemInstance<T> extends ItemInstanceType<T> {}
109
+
110
+ export type HotkeyName<F extends FeatureDef = FeatureDefs<any>> =
111
+ MergedFeatures<F>["hotkeys"];
112
+
113
+ export type HotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Record<
114
+ HotkeyName<F>,
115
+ HotkeyConfig<T>
116
+ >;
117
+
118
+ export type CustomHotkeysConfig<
119
+ T,
120
+ F extends FeatureDef = FeatureDefs<T>
121
+ > = Partial<
122
+ Record<HotkeyName<F> | `custom${string}`, Partial<HotkeyConfig<T>>>
123
+ >;
124
+
125
+ export type FeatureImplementation<
126
+ T = any,
127
+ D extends FeatureDef = any,
128
+ F extends FeatureDef = EmptyFeatureDef
129
+ > = {
130
+ key?: string;
131
+ dependingFeatures?: string[];
132
+
133
+ getInitialState?: (
134
+ initialState: Partial<MergedFeatures<F>["state"]>,
135
+ tree: MergedFeatures<F>["treeInstance"]
136
+ ) => Partial<D["state"] & MergedFeatures<F>["state"]>;
137
+
138
+ getDefaultConfig?: (
139
+ defaultConfig: Partial<MergedFeatures<F>["config"]>,
140
+ tree: MergedFeatures<F>["treeInstance"]
141
+ ) => Partial<D["config"] & MergedFeatures<F>["config"]>;
142
+
143
+ createTreeInstance?: (
144
+ prev: MergedFeatures<F>["treeInstance"],
145
+ instance: MergedFeatures<F>["treeInstance"]
146
+ ) => D["treeInstance"] & MergedFeatures<F>["treeInstance"];
147
+
148
+ createItemInstance?: (
149
+ prev: MergedFeatures<F>["itemInstance"],
150
+ item: MergedFeatures<F>["itemInstance"],
151
+ tree: MergedFeatures<F>["treeInstance"],
152
+ itemId: string
153
+ ) => D["itemInstance"] & MergedFeatures<F>["itemInstance"];
154
+
155
+ onTreeMount?: (
156
+ instance: MergedFeatures<F>["treeInstance"],
157
+ treeElement: HTMLElement
158
+ ) => void;
159
+
160
+ onTreeUnmount?: (
161
+ instance: MergedFeatures<F>["treeInstance"],
162
+ treeElement: HTMLElement
163
+ ) => void;
164
+
165
+ onItemMount?: (
166
+ instance: MergedFeatures<F>["itemInstance"],
167
+ itemElement: HTMLElement,
168
+ tree: MergedFeatures<F>["treeInstance"]
169
+ ) => void;
170
+
171
+ onItemUnmount?: (
172
+ instance: MergedFeatures<F>["itemInstance"],
173
+ itemElement: HTMLElement,
174
+ tree: MergedFeatures<F>["treeInstance"]
175
+ ) => void;
176
+
177
+ setState?: (instance: MergedFeatures<F>["treeInstance"]) => void;
178
+ onConfigChange?: (instance: MergedFeatures<F>["treeInstance"]) => void;
179
+ onStateOrConfigChange?: (instance: MergedFeatures<F>["treeInstance"]) => void;
180
+
181
+ hotkeys?: HotkeysConfig<T, D>;
182
+ };
@@ -0,0 +1,31 @@
1
+ type TAllKeys<T> = T extends any ? keyof T : never;
2
+
3
+ type TIndexValue<T, K extends PropertyKey, D = never> = T extends any
4
+ ? K extends keyof T
5
+ ? T[K]
6
+ : D
7
+ : never;
8
+
9
+ type TPartialKeys<T, K extends keyof T> = Omit<T, K> &
10
+ Partial<Pick<T, K>> extends infer O
11
+ ? { [P in keyof O]: O[P] }
12
+ : never;
13
+
14
+ type TFunction = (...a: any[]) => any;
15
+
16
+ type TPrimitives =
17
+ | string
18
+ | number
19
+ | boolean
20
+ | bigint
21
+ | symbol
22
+ | Date
23
+ | TFunction;
24
+
25
+ export type TMerged<T> = [T] extends [Array<any>]
26
+ ? { [K in keyof T]: TMerged<T[K]> }
27
+ : [T] extends [TPrimitives]
28
+ ? T
29
+ : [T] extends [object]
30
+ ? TPartialKeys<{ [K in TAllKeys<T>]: TMerged<TIndexValue<T, K>> }, never>
31
+ : T;
package/src/utils.ts ADDED
@@ -0,0 +1,136 @@
1
+ import { ItemInstance, TreeState, Updater } from "./types/core";
2
+ import { DropTarget } from "./features/drag-and-drop/types";
3
+
4
+ export type NoInfer<T> = [T][T extends any ? 0 : never];
5
+
6
+ export const memo = <D extends readonly any[], R>(
7
+ fn: (...args: [...D]) => R,
8
+ deps: () => [...D]
9
+ ) => {
10
+ let value: R | undefined;
11
+ let oldDeps: D | null = null;
12
+
13
+ return () => {
14
+ const newDeps = deps();
15
+
16
+ if (!value) {
17
+ value = fn(...newDeps);
18
+ oldDeps = newDeps;
19
+ return value;
20
+ }
21
+
22
+ const match =
23
+ oldDeps &&
24
+ oldDeps.length === newDeps.length &&
25
+ !oldDeps.some((dep, i) => dep !== newDeps[i]);
26
+
27
+ if (match) {
28
+ return value;
29
+ }
30
+
31
+ value = fn(...newDeps);
32
+ oldDeps = newDeps;
33
+ return value;
34
+ };
35
+ };
36
+
37
+ export function functionalUpdate<T>(updater: Updater<T>, input: T): T {
38
+ return typeof updater === "function"
39
+ ? (updater as (input: T) => T)(input)
40
+ : updater;
41
+ }
42
+ export function makeStateUpdater<K extends keyof TreeState<any>>(
43
+ key: K,
44
+ instance: unknown
45
+ ) {
46
+ return (updater: Updater<TreeState<any>[K]>) => {
47
+ (instance as any).setState(<TTableState>(old: TTableState) => {
48
+ return {
49
+ ...old,
50
+ [key]: functionalUpdate(updater, (old as any)[key]),
51
+ };
52
+ });
53
+ };
54
+ }
55
+
56
+ export const scrollIntoView = (element: Element | undefined | null) => {
57
+ if (!element) {
58
+ return;
59
+ }
60
+
61
+ if ((element as any).scrollIntoViewIfNeeded) {
62
+ (element as any).scrollIntoViewIfNeeded();
63
+ } else {
64
+ const boundingBox = element.getBoundingClientRect();
65
+ const isElementInViewport =
66
+ boundingBox.top >= 0 &&
67
+ boundingBox.left >= 0 &&
68
+ boundingBox.bottom <=
69
+ (window.innerHeight || document.documentElement.clientHeight) &&
70
+ boundingBox.right <=
71
+ (window.innerWidth || document.documentElement.clientWidth);
72
+ if (!isElementInViewport) {
73
+ element.scrollIntoView();
74
+ }
75
+ }
76
+ };
77
+
78
+ export const performItemsMove = <T>(
79
+ items: ItemInstance<T>[],
80
+ target: DropTarget<T>,
81
+ onChangeChildren: (
82
+ item: ItemInstance<T>,
83
+ newChildren: ItemInstance<T>[]
84
+ ) => void
85
+ ) => {
86
+ const numberOfDragItemsBeforeTarget = !target.childIndex
87
+ ? 0
88
+ : target.item
89
+ .getChildren()
90
+ .slice(0, target.childIndex)
91
+ .filter((child) => items.some((item) => item.getId() === child.getId()))
92
+ .length;
93
+
94
+ // TODO bulk sibling changes together
95
+ for (const item of items) {
96
+ const siblings = item.getParent()?.getChildren();
97
+ if (siblings) {
98
+ onChangeChildren(
99
+ item.getParent(),
100
+ siblings.filter((sibling) => sibling.getId() !== item.getId())
101
+ );
102
+ }
103
+ }
104
+
105
+ if (target.childIndex === null) {
106
+ onChangeChildren(target.item, [...target.item.getChildren(), ...items]);
107
+ items[0].getTree().rebuildTree();
108
+ return;
109
+ }
110
+
111
+ const oldChildren = target.item.getChildren();
112
+ const newChildren = [
113
+ ...oldChildren.slice(0, target.childIndex - numberOfDragItemsBeforeTarget),
114
+ ...items,
115
+ ...oldChildren.slice(target.childIndex - numberOfDragItemsBeforeTarget),
116
+ ];
117
+
118
+ onChangeChildren(target.item, newChildren);
119
+
120
+ items[0].getTree().rebuildTree();
121
+ };
122
+
123
+ export const poll = (fn: () => boolean, interval = 100, timeout = 1000) =>
124
+ new Promise<void>((resolve) => {
125
+ let clear: ReturnType<typeof setTimeout>;
126
+ const i = setInterval(() => {
127
+ if (fn()) {
128
+ resolve();
129
+ clearInterval(i);
130
+ clearTimeout(clear);
131
+ }
132
+ }, interval);
133
+ clear = setTimeout(() => {
134
+ clearInterval(i);
135
+ }, timeout);
136
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "include": ["./src/**/*"],
4
+ "compilerOptions": {
5
+ "outDir": "lib"
6
+ }
7
+ }
package/typedoc.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": ["../../typedoc.base.json"],
3
+ "entryPoints": ["src/index.ts"]
4
+ }