@headless-tree/core 0.0.9 → 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.
- package/CHANGELOG.md +12 -0
- package/lib/cjs/core/build-proxified-instance.d.ts +2 -0
- package/lib/cjs/core/build-proxified-instance.js +58 -0
- package/lib/cjs/core/build-static-instance.d.ts +2 -0
- package/lib/cjs/core/build-static-instance.js +27 -0
- package/lib/cjs/core/create-tree.js +55 -36
- package/lib/cjs/features/async-data-loader/feature.js +37 -23
- package/lib/cjs/features/async-data-loader/types.d.ts +2 -1
- package/lib/cjs/features/drag-and-drop/feature.js +64 -32
- package/lib/cjs/features/drag-and-drop/types.d.ts +13 -4
- package/lib/cjs/features/drag-and-drop/utils.d.ts +1 -2
- package/lib/cjs/features/drag-and-drop/utils.js +140 -37
- package/lib/cjs/features/expand-all/feature.js +12 -6
- package/lib/cjs/features/main/types.d.ts +8 -2
- package/lib/cjs/features/renaming/feature.js +33 -18
- package/lib/cjs/features/renaming/types.d.ts +1 -1
- package/lib/cjs/features/search/feature.js +38 -24
- package/lib/cjs/features/search/types.d.ts +0 -1
- package/lib/cjs/features/selection/feature.js +23 -14
- package/lib/cjs/features/sync-data-loader/feature.js +7 -2
- package/lib/cjs/features/tree/feature.d.ts +2 -1
- package/lib/cjs/features/tree/feature.js +85 -63
- package/lib/cjs/features/tree/types.d.ts +5 -3
- package/lib/cjs/index.d.ts +3 -1
- package/lib/cjs/index.js +2 -1
- package/lib/cjs/test-utils/test-tree-do.d.ts +23 -0
- package/lib/cjs/test-utils/test-tree-do.js +99 -0
- package/lib/cjs/test-utils/test-tree-expect.d.ts +15 -0
- package/lib/cjs/test-utils/test-tree-expect.js +62 -0
- package/lib/cjs/test-utils/test-tree.d.ts +47 -0
- package/lib/cjs/test-utils/test-tree.js +195 -0
- package/lib/cjs/types/core.d.ts +31 -15
- package/lib/cjs/utilities/errors.d.ts +1 -0
- package/lib/cjs/utilities/errors.js +5 -0
- package/lib/cjs/utilities/insert-items-at-target.js +10 -3
- package/lib/cjs/utilities/remove-items-from-parents.js +14 -8
- package/lib/cjs/utils.d.ts +3 -3
- package/lib/cjs/utils.js +6 -6
- package/lib/esm/core/build-proxified-instance.d.ts +2 -0
- package/lib/esm/core/build-proxified-instance.js +54 -0
- package/lib/esm/core/build-static-instance.d.ts +2 -0
- package/lib/esm/core/build-static-instance.js +23 -0
- package/lib/esm/core/create-tree.js +55 -36
- package/lib/esm/features/async-data-loader/feature.js +37 -23
- package/lib/esm/features/async-data-loader/types.d.ts +2 -1
- package/lib/esm/features/drag-and-drop/feature.js +64 -32
- package/lib/esm/features/drag-and-drop/types.d.ts +13 -4
- package/lib/esm/features/drag-and-drop/utils.d.ts +1 -2
- package/lib/esm/features/drag-and-drop/utils.js +138 -34
- package/lib/esm/features/expand-all/feature.js +12 -6
- package/lib/esm/features/main/types.d.ts +8 -2
- package/lib/esm/features/renaming/feature.js +33 -18
- package/lib/esm/features/renaming/types.d.ts +1 -1
- package/lib/esm/features/search/feature.js +38 -24
- package/lib/esm/features/search/types.d.ts +0 -1
- package/lib/esm/features/selection/feature.js +23 -14
- package/lib/esm/features/sync-data-loader/feature.js +7 -2
- package/lib/esm/features/tree/feature.d.ts +2 -1
- package/lib/esm/features/tree/feature.js +86 -64
- package/lib/esm/features/tree/types.d.ts +5 -3
- package/lib/esm/index.d.ts +3 -1
- package/lib/esm/index.js +2 -1
- package/lib/esm/test-utils/test-tree-do.d.ts +23 -0
- package/lib/esm/test-utils/test-tree-do.js +95 -0
- package/lib/esm/test-utils/test-tree-expect.d.ts +15 -0
- package/lib/esm/test-utils/test-tree-expect.js +58 -0
- package/lib/esm/test-utils/test-tree.d.ts +47 -0
- package/lib/esm/test-utils/test-tree.js +191 -0
- package/lib/esm/types/core.d.ts +31 -15
- package/lib/esm/utilities/errors.d.ts +1 -0
- package/lib/esm/utilities/errors.js +1 -0
- package/lib/esm/utilities/insert-items-at-target.js +10 -3
- package/lib/esm/utilities/remove-items-from-parents.js +14 -8
- package/lib/esm/utils.d.ts +3 -3
- package/lib/esm/utils.js +3 -3
- package/package.json +7 -3
- package/src/core/build-proxified-instance.ts +115 -0
- package/src/core/build-static-instance.ts +28 -0
- package/src/core/create-tree.ts +60 -62
- package/src/features/async-data-loader/async-data-loader.spec.ts +143 -0
- package/src/features/async-data-loader/feature.ts +33 -31
- package/src/features/async-data-loader/types.ts +3 -1
- package/src/features/drag-and-drop/drag-and-drop.spec.ts +716 -0
- package/src/features/drag-and-drop/feature.ts +109 -85
- package/src/features/drag-and-drop/types.ts +21 -7
- package/src/features/drag-and-drop/utils.ts +196 -55
- package/src/features/expand-all/expand-all.spec.ts +52 -0
- package/src/features/expand-all/feature.ts +8 -12
- package/src/features/hotkeys-core/feature.ts +1 -1
- package/src/features/main/types.ts +14 -1
- package/src/features/renaming/feature.ts +30 -29
- package/src/features/renaming/renaming.spec.ts +125 -0
- package/src/features/renaming/types.ts +1 -1
- package/src/features/search/feature.ts +34 -38
- package/src/features/search/search.spec.ts +115 -0
- package/src/features/search/types.ts +0 -1
- package/src/features/selection/feature.ts +29 -30
- package/src/features/selection/selection.spec.ts +220 -0
- package/src/features/sync-data-loader/feature.ts +8 -11
- package/src/features/tree/feature.ts +82 -87
- package/src/features/tree/tree.spec.ts +515 -0
- package/src/features/tree/types.ts +5 -3
- package/src/index.ts +4 -1
- package/src/test-utils/test-tree-do.ts +136 -0
- package/src/test-utils/test-tree-expect.ts +86 -0
- package/src/test-utils/test-tree.ts +217 -0
- package/src/types/core.ts +92 -33
- package/src/utilities/errors.ts +2 -0
- package/src/utilities/insert-items-at-target.ts +10 -3
- package/src/utilities/remove-items-from-parents.ts +15 -10
- package/src/utils.spec.ts +89 -0
- package/src/utils.ts +6 -6
- package/tsconfig.json +1 -0
- package/vitest.config.ts +6 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TestTree = void 0;
|
|
13
|
+
/* eslint-disable import/no-extraneous-dependencies */
|
|
14
|
+
const vitest_1 = require("vitest");
|
|
15
|
+
const create_tree_1 = require("../core/create-tree");
|
|
16
|
+
const test_tree_do_1 = require("./test-tree-do");
|
|
17
|
+
const test_tree_expect_1 = require("./test-tree-expect");
|
|
18
|
+
const feature_1 = require("../features/sync-data-loader/feature");
|
|
19
|
+
const feature_2 = require("../features/async-data-loader/feature");
|
|
20
|
+
const build_proxified_instance_1 = require("../core/build-proxified-instance");
|
|
21
|
+
vitest_1.vi.useFakeTimers({ shouldAdvanceTime: true });
|
|
22
|
+
class TestTree {
|
|
23
|
+
forSuits(runSuite) {
|
|
24
|
+
vitest_1.describe.for([
|
|
25
|
+
this.suits.sync(),
|
|
26
|
+
this.suits.async(),
|
|
27
|
+
this.suits.proxifiedSync(),
|
|
28
|
+
this.suits.proxifiedAsync(),
|
|
29
|
+
])("$title", ({ tree }) => {
|
|
30
|
+
tree.resetBeforeEach();
|
|
31
|
+
runSuite(tree);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
get instance() {
|
|
35
|
+
if (!this.treeInstance) {
|
|
36
|
+
this.treeInstance = (0, create_tree_1.createTree)(this.config);
|
|
37
|
+
}
|
|
38
|
+
return this.treeInstance;
|
|
39
|
+
}
|
|
40
|
+
constructor(config) {
|
|
41
|
+
this.config = config;
|
|
42
|
+
this.do = new test_tree_do_1.TestTreeDo(this);
|
|
43
|
+
this.expect = new test_tree_expect_1.TestTreeExpect(this);
|
|
44
|
+
this.treeInstance = null;
|
|
45
|
+
this.suits = {
|
|
46
|
+
sync: () => ({
|
|
47
|
+
tree: this.withFeatures(feature_1.syncDataLoaderFeature),
|
|
48
|
+
title: "Synchronous Data Loader",
|
|
49
|
+
}),
|
|
50
|
+
async: () => ({
|
|
51
|
+
tree: this.withFeatures(feature_2.asyncDataLoaderFeature),
|
|
52
|
+
title: "Asynchronous Data Loader",
|
|
53
|
+
}),
|
|
54
|
+
proxifiedSync: () => ({
|
|
55
|
+
tree: this.withFeatures(feature_1.syncDataLoaderFeature).with({
|
|
56
|
+
instanceBuilder: build_proxified_instance_1.buildProxiedInstance,
|
|
57
|
+
}),
|
|
58
|
+
title: "Proxified Synchronous Data Loader",
|
|
59
|
+
}),
|
|
60
|
+
proxifiedAsync: () => ({
|
|
61
|
+
tree: this.withFeatures(feature_2.asyncDataLoaderFeature).with({
|
|
62
|
+
instanceBuilder: build_proxified_instance_1.buildProxiedInstance,
|
|
63
|
+
}),
|
|
64
|
+
title: "Proxified Asynchronous Data Loader",
|
|
65
|
+
}),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
static resolveAsyncLoaders() {
|
|
69
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
70
|
+
var _a;
|
|
71
|
+
while (TestTree.asyncLoaderResolvers.length) {
|
|
72
|
+
(_a = TestTree.asyncLoaderResolvers.shift()) === null || _a === void 0 ? void 0 : _a();
|
|
73
|
+
yield new Promise((r) => {
|
|
74
|
+
setTimeout(r);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
resolveAsyncVisibleItems() {
|
|
80
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
this.instance.getItems();
|
|
82
|
+
yield TestTree.resolveAsyncLoaders();
|
|
83
|
+
this.instance.getItems().forEach((i) => i.getItemName());
|
|
84
|
+
yield TestTree.resolveAsyncLoaders();
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
static default(config) {
|
|
88
|
+
return new TestTree(Object.assign({ rootItemId: "x", createLoadingItemData: () => "loading", dataLoader: {
|
|
89
|
+
getItem: (id) => id,
|
|
90
|
+
getChildren: (id) => [`${id}1`, `${id}2`, `${id}3`, `${id}4`],
|
|
91
|
+
}, asyncDataLoader: {
|
|
92
|
+
getItem: (id) => __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
yield new Promise((r) => {
|
|
94
|
+
r.debugName = `Loading getItem ${id}`;
|
|
95
|
+
TestTree.asyncLoaderResolvers.push(r);
|
|
96
|
+
});
|
|
97
|
+
return id;
|
|
98
|
+
}),
|
|
99
|
+
getChildren: (id) => __awaiter(this, void 0, void 0, function* () {
|
|
100
|
+
yield new Promise((r) => {
|
|
101
|
+
r.debugName = `Loading getChildren ${id}`;
|
|
102
|
+
TestTree.asyncLoaderResolvers.push(r);
|
|
103
|
+
});
|
|
104
|
+
return [`${id}1`, `${id}2`, `${id}3`, `${id}4`];
|
|
105
|
+
}),
|
|
106
|
+
}, getItemName: (item) => item.getItemData(), indent: 20, isItemFolder: (item) => item.getItemMeta().level < 2, initialState: {
|
|
107
|
+
expandedItems: ["x1", "x11"],
|
|
108
|
+
}, features: [] }, config));
|
|
109
|
+
}
|
|
110
|
+
with(config) {
|
|
111
|
+
return new TestTree(Object.assign(Object.assign({}, this.config), config));
|
|
112
|
+
}
|
|
113
|
+
resetBeforeEach() {
|
|
114
|
+
(0, vitest_1.beforeEach)(() => __awaiter(this, void 0, void 0, function* () {
|
|
115
|
+
yield this.createTestCaseTree();
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
createTestCaseTree() {
|
|
119
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
this.reset();
|
|
121
|
+
vitest_1.vi.clearAllMocks();
|
|
122
|
+
// trigger instance creation
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
124
|
+
this.instance;
|
|
125
|
+
yield this.resolveAsyncVisibleItems();
|
|
126
|
+
return this;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
withFeatures(...features) {
|
|
130
|
+
var _a;
|
|
131
|
+
return this.with({
|
|
132
|
+
features: [...((_a = this.config.features) !== null && _a !== void 0 ? _a : []), ...features],
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
mockedHandler(handlerName) {
|
|
136
|
+
var _a;
|
|
137
|
+
const mock = vitest_1.vi.fn();
|
|
138
|
+
if (this.treeInstance) {
|
|
139
|
+
(_a = this.treeInstance) === null || _a === void 0 ? void 0 : _a.setConfig((prev) => (Object.assign(Object.assign({}, prev), { [handlerName]: mock })));
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this.config[handlerName] = mock;
|
|
143
|
+
}
|
|
144
|
+
return mock;
|
|
145
|
+
}
|
|
146
|
+
item(itemId) {
|
|
147
|
+
return this.instance.getItemInstance(itemId);
|
|
148
|
+
}
|
|
149
|
+
reset() {
|
|
150
|
+
this.treeInstance = null;
|
|
151
|
+
TestTree.asyncLoaderResolvers = [];
|
|
152
|
+
}
|
|
153
|
+
debug() {
|
|
154
|
+
console.log(this.instance
|
|
155
|
+
.getItems()
|
|
156
|
+
.map((item) => [
|
|
157
|
+
" ".repeat(item.getItemMeta().level),
|
|
158
|
+
'"',
|
|
159
|
+
item.getItemName(),
|
|
160
|
+
'"',
|
|
161
|
+
].join(""))
|
|
162
|
+
.join("\n"));
|
|
163
|
+
}
|
|
164
|
+
setElementBoundingBox(itemId, bb = {
|
|
165
|
+
left: 0,
|
|
166
|
+
right: 100,
|
|
167
|
+
top: 0,
|
|
168
|
+
height: 20,
|
|
169
|
+
}) {
|
|
170
|
+
this.instance.getItemInstance(itemId).registerElement({
|
|
171
|
+
getBoundingClientRect: () => bb,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
static dragEvent(pageX = 1000, pageY = 0) {
|
|
175
|
+
return {
|
|
176
|
+
preventDefault: vitest_1.vi.fn(),
|
|
177
|
+
stopPropagation: vitest_1.vi.fn(),
|
|
178
|
+
dataTransfer: {
|
|
179
|
+
setData: vitest_1.vi.fn(),
|
|
180
|
+
getData: vitest_1.vi.fn(),
|
|
181
|
+
dropEffect: "unchaged-from-test",
|
|
182
|
+
},
|
|
183
|
+
pageX,
|
|
184
|
+
pageY,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
createTopDragEvent(indent = 0) {
|
|
188
|
+
return TestTree.dragEvent(indent * 20, 1);
|
|
189
|
+
}
|
|
190
|
+
createBottomDragEvent(indent = 0) {
|
|
191
|
+
return TestTree.dragEvent(indent * 20, 19);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
exports.TestTree = TestTree;
|
|
195
|
+
TestTree.asyncLoaderResolvers = [];
|
package/lib/cjs/types/core.d.ts
CHANGED
|
@@ -10,11 +10,12 @@ import { RenamingFeatureDef } from "../features/renaming/types";
|
|
|
10
10
|
import { ExpandAllFeatureDef } from "../features/expand-all/types";
|
|
11
11
|
export type Updater<T> = T | ((old: T) => T);
|
|
12
12
|
export type SetStateFn<T> = (updaterOrValue: Updater<T>) => void;
|
|
13
|
+
type FunctionMap = Record<string, (...args: any[]) => any>;
|
|
13
14
|
export type FeatureDef = {
|
|
14
|
-
state:
|
|
15
|
-
config:
|
|
16
|
-
treeInstance:
|
|
17
|
-
itemInstance:
|
|
15
|
+
state: any;
|
|
16
|
+
config: any;
|
|
17
|
+
treeInstance: FunctionMap;
|
|
18
|
+
itemInstance: FunctionMap;
|
|
18
19
|
hotkeys: string;
|
|
19
20
|
};
|
|
20
21
|
export type EmptyFeatureDef = {
|
|
@@ -49,19 +50,34 @@ export interface ItemInstance<T> extends ItemInstanceType<T> {
|
|
|
49
50
|
export type HotkeyName<F extends FeatureDef = FeatureDefs<any>> = MergedFeatures<F>["hotkeys"];
|
|
50
51
|
export type HotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Record<HotkeyName<F>, HotkeyConfig<T>>;
|
|
51
52
|
export type CustomHotkeysConfig<T, F extends FeatureDef = FeatureDefs<T>> = Partial<Record<HotkeyName<F> | `custom${string}`, Partial<HotkeyConfig<T>>>>;
|
|
52
|
-
|
|
53
|
+
type MayReturnNull<T extends (...x: any[]) => any> = (...args: Parameters<T>) => ReturnType<T> | null;
|
|
54
|
+
export type ItemInstanceOpts<ItemInstance extends FunctionMap = FunctionMap, TreeInstance extends FunctionMap = FunctionMap, Key extends keyof ItemInstance = any> = {
|
|
55
|
+
item: ItemInstance;
|
|
56
|
+
tree: TreeInstance;
|
|
57
|
+
itemId: string;
|
|
58
|
+
prev?: MayReturnNull<ItemInstance[Key]>;
|
|
59
|
+
};
|
|
60
|
+
export type TreeInstanceOpts<TreeInstance extends FunctionMap = FunctionMap, Key extends keyof TreeInstance = any> = {
|
|
61
|
+
tree: TreeInstance;
|
|
62
|
+
prev?: MayReturnNull<TreeInstance[Key]>;
|
|
63
|
+
};
|
|
64
|
+
export type FeatureImplementation<T = any, SelfFeatureDef extends FeatureDef = any, DepFeaturesDef extends FeatureDef = any> = {
|
|
53
65
|
key?: string;
|
|
54
66
|
deps?: string[];
|
|
55
67
|
overwrites?: string[];
|
|
56
|
-
stateHandlerNames?: Partial<Record<keyof MergedFeatures<
|
|
57
|
-
getInitialState?: (initialState: Partial<MergedFeatures<
|
|
58
|
-
getDefaultConfig?: (defaultConfig: Partial<MergedFeatures<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
stateHandlerNames?: Partial<Record<keyof MergedFeatures<DepFeaturesDef>["state"], keyof MergedFeatures<DepFeaturesDef>["config"]>>;
|
|
69
|
+
getInitialState?: (initialState: Partial<MergedFeatures<DepFeaturesDef>["state"]>, tree: MergedFeatures<DepFeaturesDef>["treeInstance"]) => Partial<SelfFeatureDef["state"] & MergedFeatures<DepFeaturesDef>["state"]>;
|
|
70
|
+
getDefaultConfig?: (defaultConfig: Partial<MergedFeatures<DepFeaturesDef>["config"]>, tree: MergedFeatures<DepFeaturesDef>["treeInstance"]) => Partial<SelfFeatureDef["config"] & MergedFeatures<DepFeaturesDef>["config"]>;
|
|
71
|
+
treeInstance?: {
|
|
72
|
+
[key in keyof (SelfFeatureDef["treeInstance"] & MergedFeatures<DepFeaturesDef>["treeInstance"])]?: (opts: TreeInstanceOpts<SelfFeatureDef["treeInstance"] & MergedFeatures<DepFeaturesDef>["treeInstance"], key>, ...args: Parameters<(SelfFeatureDef["treeInstance"] & MergedFeatures<DepFeaturesDef>["treeInstance"])[key]>) => void;
|
|
73
|
+
};
|
|
74
|
+
itemInstance?: {
|
|
75
|
+
[key in keyof (SelfFeatureDef["itemInstance"] & MergedFeatures<DepFeaturesDef>["itemInstance"])]?: (opts: ItemInstanceOpts<SelfFeatureDef["itemInstance"] & MergedFeatures<DepFeaturesDef>["itemInstance"], SelfFeatureDef["treeInstance"] & MergedFeatures<DepFeaturesDef>["treeInstance"], key>, ...args: Parameters<(SelfFeatureDef["itemInstance"] & MergedFeatures<DepFeaturesDef>["itemInstance"])[key]>) => void;
|
|
76
|
+
};
|
|
77
|
+
onTreeMount?: (instance: MergedFeatures<DepFeaturesDef>["treeInstance"], treeElement: HTMLElement) => void;
|
|
78
|
+
onTreeUnmount?: (instance: MergedFeatures<DepFeaturesDef>["treeInstance"], treeElement: HTMLElement) => void;
|
|
79
|
+
onItemMount?: (instance: MergedFeatures<DepFeaturesDef>["itemInstance"], itemElement: HTMLElement, tree: MergedFeatures<DepFeaturesDef>["treeInstance"]) => void;
|
|
80
|
+
onItemUnmount?: (instance: MergedFeatures<DepFeaturesDef>["itemInstance"], itemElement: HTMLElement, tree: MergedFeatures<DepFeaturesDef>["treeInstance"]) => void;
|
|
81
|
+
hotkeys?: HotkeysConfig<T, SelfFeatureDef>;
|
|
66
82
|
};
|
|
67
83
|
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const throwError: (message: string) => Error;
|
|
@@ -4,11 +4,15 @@ exports.insertItemsAtTarget = void 0;
|
|
|
4
4
|
const insertItemsAtTarget = (itemIds, target, onChangeChildren) => {
|
|
5
5
|
// add moved items to new common parent, if dropped onto parent
|
|
6
6
|
if (target.childIndex === null) {
|
|
7
|
-
|
|
7
|
+
const newChildren = [
|
|
8
8
|
...target.item.getChildren().map((item) => item.getId()),
|
|
9
9
|
...itemIds,
|
|
10
|
-
]
|
|
11
|
-
|
|
10
|
+
];
|
|
11
|
+
onChangeChildren(target.item, newChildren);
|
|
12
|
+
if (target.item && "updateCachedChildrenIds" in target.item) {
|
|
13
|
+
target.item.updateCachedChildrenIds(newChildren);
|
|
14
|
+
}
|
|
15
|
+
target.item.getTree().rebuildTree();
|
|
12
16
|
return;
|
|
13
17
|
}
|
|
14
18
|
// add moved items to new common parent, if dropped between siblings
|
|
@@ -19,6 +23,9 @@ const insertItemsAtTarget = (itemIds, target, onChangeChildren) => {
|
|
|
19
23
|
...oldChildren.slice(target.insertionIndex).map((item) => item.getId()),
|
|
20
24
|
];
|
|
21
25
|
onChangeChildren(target.item, newChildren);
|
|
26
|
+
if (target.item && "updateCachedChildrenIds" in target.item) {
|
|
27
|
+
target.item.updateCachedChildrenIds(newChildren);
|
|
28
|
+
}
|
|
22
29
|
target.item.getTree().rebuildTree();
|
|
23
30
|
};
|
|
24
31
|
exports.insertItemsAtTarget = insertItemsAtTarget;
|
|
@@ -2,14 +2,20 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.removeItemsFromParents = void 0;
|
|
4
4
|
const removeItemsFromParents = (movedItems, onChangeChildren) => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
const movedItemsIds = movedItems.map((item) => item.getId());
|
|
6
|
+
const uniqueParents = [
|
|
7
|
+
...new Set(movedItems.map((item) => item.getParent())),
|
|
8
|
+
];
|
|
9
|
+
for (const parent of uniqueParents) {
|
|
10
|
+
const siblings = parent === null || parent === void 0 ? void 0 : parent.getChildren();
|
|
11
|
+
if (siblings && parent) {
|
|
12
|
+
const newChildren = siblings
|
|
13
|
+
.filter((sibling) => !movedItemsIds.includes(sibling.getId()))
|
|
14
|
+
.map((i) => i.getId());
|
|
15
|
+
onChangeChildren(parent, newChildren);
|
|
16
|
+
if (parent && "updateCachedChildrenIds" in parent) {
|
|
17
|
+
parent === null || parent === void 0 ? void 0 : parent.updateCachedChildrenIds(newChildren);
|
|
18
|
+
}
|
|
13
19
|
}
|
|
14
20
|
}
|
|
15
21
|
movedItems[0].getTree().rebuildTree();
|
package/lib/cjs/utils.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { TreeState, Updater } from "./types/core";
|
|
1
|
+
import { SetStateFn, TreeState, Updater } from "./types/core";
|
|
2
2
|
export type NoInfer<T> = [T][T extends any ? 0 : never];
|
|
3
|
-
export declare const memo: <D extends readonly any[], R>(
|
|
3
|
+
export declare const memo: <D extends readonly any[], P extends readonly any[], R>(deps: (...args: [...P]) => [...D], fn: (...args: [...D]) => R) => (...a: P) => R;
|
|
4
4
|
export declare function functionalUpdate<T>(updater: Updater<T>, input: T): T;
|
|
5
|
-
export declare function makeStateUpdater<K extends keyof TreeState<any>>(key: K, instance: unknown):
|
|
5
|
+
export declare function makeStateUpdater<K extends keyof TreeState<any>>(key: K, instance: unknown): SetStateFn<TreeState<any>[K]>;
|
|
6
6
|
export declare const poll: (fn: () => boolean, interval?: number, timeout?: number) => Promise<void>;
|
package/lib/cjs/utils.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.poll = exports.
|
|
4
|
-
|
|
3
|
+
exports.poll = exports.memo = void 0;
|
|
4
|
+
exports.functionalUpdate = functionalUpdate;
|
|
5
|
+
exports.makeStateUpdater = makeStateUpdater;
|
|
6
|
+
const memo = (deps, fn) => {
|
|
5
7
|
let value;
|
|
6
8
|
let oldDeps = null;
|
|
7
|
-
return () => {
|
|
8
|
-
const newDeps = deps();
|
|
9
|
+
return (...a) => {
|
|
10
|
+
const newDeps = deps(...a);
|
|
9
11
|
if (!value) {
|
|
10
12
|
value = fn(...newDeps);
|
|
11
13
|
oldDeps = newDeps;
|
|
@@ -28,7 +30,6 @@ function functionalUpdate(updater, input) {
|
|
|
28
30
|
? updater(input)
|
|
29
31
|
: updater;
|
|
30
32
|
}
|
|
31
|
-
exports.functionalUpdate = functionalUpdate;
|
|
32
33
|
function makeStateUpdater(key, instance) {
|
|
33
34
|
return (updater) => {
|
|
34
35
|
instance.setState((old) => {
|
|
@@ -36,7 +37,6 @@ function makeStateUpdater(key, instance) {
|
|
|
36
37
|
});
|
|
37
38
|
};
|
|
38
39
|
}
|
|
39
|
-
exports.makeStateUpdater = makeStateUpdater;
|
|
40
40
|
const poll = (fn, interval = 100, timeout = 1000) => new Promise((resolve) => {
|
|
41
41
|
let clear;
|
|
42
42
|
const i = setInterval(() => {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { throwError } from "../utilities/errors";
|
|
2
|
+
const noop = () => { };
|
|
3
|
+
const findPrevInstanceMethod = (features, instanceType, methodKey, featureSearchIndex) => {
|
|
4
|
+
var _a;
|
|
5
|
+
for (let i = featureSearchIndex; i >= 0; i--) {
|
|
6
|
+
const feature = features[i];
|
|
7
|
+
const itemInstanceMethod = (_a = feature[instanceType]) === null || _a === void 0 ? void 0 : _a[methodKey];
|
|
8
|
+
if (itemInstanceMethod) {
|
|
9
|
+
return i;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
};
|
|
14
|
+
const invokeInstanceMethod = (features, instanceType, opts, methodKey, featureIndex, args) => {
|
|
15
|
+
var _a;
|
|
16
|
+
const prevIndex = findPrevInstanceMethod(features, instanceType, methodKey, featureIndex - 1);
|
|
17
|
+
const itemInstanceMethod = (_a = features[featureIndex][instanceType]) === null || _a === void 0 ? void 0 : _a[methodKey];
|
|
18
|
+
return itemInstanceMethod(Object.assign(Object.assign({}, opts), { prev: prevIndex !== null
|
|
19
|
+
? (...newArgs) => invokeInstanceMethod(features, instanceType, opts, methodKey, prevIndex, newArgs)
|
|
20
|
+
: null }), ...args);
|
|
21
|
+
};
|
|
22
|
+
export const buildProxiedInstance = (features, instanceType, buildOpts) => {
|
|
23
|
+
// demo with prototypes: https://jsfiddle.net/bgenc58r/
|
|
24
|
+
const opts = {};
|
|
25
|
+
const item = new Proxy({}, {
|
|
26
|
+
has(target, key) {
|
|
27
|
+
if (typeof key === "symbol") {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (key === "toJSON") {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const hasInstanceMethod = findPrevInstanceMethod(features, instanceType, key, features.length - 1);
|
|
34
|
+
return Boolean(hasInstanceMethod);
|
|
35
|
+
},
|
|
36
|
+
get(target, key) {
|
|
37
|
+
if (typeof key === "symbol") {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
if (key === "toJSON") {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
return (...args) => {
|
|
44
|
+
const featureIndex = findPrevInstanceMethod(features, instanceType, key, features.length - 1);
|
|
45
|
+
if (featureIndex === null) {
|
|
46
|
+
throw throwError(`feature missing for method ${key}`);
|
|
47
|
+
}
|
|
48
|
+
return invokeInstanceMethod(features, instanceType, opts, key, featureIndex, args);
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
Object.assign(opts, buildOpts(item));
|
|
53
|
+
return [item, noop];
|
|
54
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* eslint-disable no-continue,no-labels,no-extra-label */
|
|
2
|
+
export const buildStaticInstance = (features, instanceType, buildOpts) => {
|
|
3
|
+
const instance = {};
|
|
4
|
+
const finalize = () => {
|
|
5
|
+
const opts = buildOpts(instance);
|
|
6
|
+
featureLoop: for (let i = 0; i < features.length; i++) {
|
|
7
|
+
// Loop goes in forward order, because later features overwrite previous ones
|
|
8
|
+
// TODO loop order correct? I think so...
|
|
9
|
+
const definition = features[i][instanceType];
|
|
10
|
+
if (!definition)
|
|
11
|
+
continue featureLoop;
|
|
12
|
+
methodLoop: for (const [key, method] of Object.entries(definition)) {
|
|
13
|
+
if (!method)
|
|
14
|
+
continue methodLoop;
|
|
15
|
+
const prev = instance[key];
|
|
16
|
+
instance[key] = (...args) => {
|
|
17
|
+
return method(Object.assign(Object.assign({}, opts), { prev }), ...args);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
return [instance, finalize];
|
|
23
|
+
};
|
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
import { treeFeature } from "../features/tree/feature";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const itemInstance = {};
|
|
5
|
-
for (const feature of features) {
|
|
6
|
-
Object.assign(
|
|
7
|
-
// TODO dont run createItemInstance, but assign prototype objects instead?
|
|
8
|
-
// https://jsfiddle.net/bgenc58r/
|
|
9
|
-
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 : {});
|
|
10
|
-
}
|
|
11
|
-
return itemInstance;
|
|
12
|
-
};
|
|
2
|
+
import { buildStaticInstance } from "./build-static-instance";
|
|
3
|
+
import { throwError } from "../utilities/errors";
|
|
13
4
|
const verifyFeatures = (features) => {
|
|
14
5
|
var _a;
|
|
15
6
|
const loadedFeatures = features === null || features === void 0 ? void 0 : features.map((feature) => feature.key);
|
|
16
7
|
for (const feature of features !== null && features !== void 0 ? features : []) {
|
|
17
8
|
const missingDependency = (_a = feature.deps) === null || _a === void 0 ? void 0 : _a.find((dep) => !(loadedFeatures === null || loadedFeatures === void 0 ? void 0 : loadedFeatures.includes(dep)));
|
|
18
9
|
if (missingDependency) {
|
|
19
|
-
throw
|
|
10
|
+
throw throwError(`${feature.key} needs ${missingDependency}`);
|
|
20
11
|
}
|
|
21
12
|
}
|
|
22
13
|
};
|
|
@@ -29,14 +20,16 @@ const compareFeatures = (feature1, feature2) => {
|
|
|
29
20
|
};
|
|
30
21
|
const sortFeatures = (features = []) => features.sort(compareFeatures);
|
|
31
22
|
export const createTree = (initialConfig) => {
|
|
32
|
-
var _a, _b, _c, _d
|
|
33
|
-
const
|
|
23
|
+
var _a, _b, _c, _d;
|
|
24
|
+
const buildInstance = (_a = initialConfig.instanceBuilder) !== null && _a !== void 0 ? _a : buildStaticInstance;
|
|
34
25
|
const additionalFeatures = [
|
|
35
26
|
treeFeature,
|
|
36
27
|
...sortFeatures(initialConfig.features),
|
|
37
28
|
];
|
|
38
29
|
verifyFeatures(additionalFeatures);
|
|
39
|
-
|
|
30
|
+
const features = [...additionalFeatures];
|
|
31
|
+
const [treeInstance, finalizeTree] = buildInstance(features, "treeInstance", (tree) => ({ tree }));
|
|
32
|
+
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 : {});
|
|
40
33
|
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);
|
|
41
34
|
const stateHandlerNames = additionalFeatures.reduce((acc, feature) => (Object.assign(Object.assign({}, acc), feature.stateHandlerNames)), {});
|
|
42
35
|
let treeElement;
|
|
@@ -47,11 +40,12 @@ export const createTree = (initialConfig) => {
|
|
|
47
40
|
const itemDataRefs = {};
|
|
48
41
|
let itemMetaMap = {};
|
|
49
42
|
const hotkeyPresets = {};
|
|
50
|
-
const rebuildItemMeta = (
|
|
43
|
+
const rebuildItemMeta = () => {
|
|
51
44
|
// TODO can we find a way to only run this for the changed substructure?
|
|
52
45
|
itemInstances = [];
|
|
53
46
|
itemMetaMap = {};
|
|
54
|
-
const rootInstance =
|
|
47
|
+
const [rootInstance, finalizeRootInstance] = buildInstance(features, "itemInstance", (item) => ({ item, tree: treeInstance, itemId: config.rootItemId }));
|
|
48
|
+
finalizeRootInstance();
|
|
55
49
|
itemInstancesMap[config.rootItemId] = rootInstance;
|
|
56
50
|
itemMetaMap[config.rootItemId] = {
|
|
57
51
|
itemId: config.rootItemId,
|
|
@@ -64,7 +58,12 @@ export const createTree = (initialConfig) => {
|
|
|
64
58
|
for (const item of treeInstance.getItemsMeta()) {
|
|
65
59
|
itemMetaMap[item.itemId] = item;
|
|
66
60
|
if (!itemInstancesMap[item.itemId]) {
|
|
67
|
-
const instance =
|
|
61
|
+
const [instance, finalizeInstance] = buildInstance(features, "itemInstance", (instance) => ({
|
|
62
|
+
item: instance,
|
|
63
|
+
tree: treeInstance,
|
|
64
|
+
itemId: item.itemId,
|
|
65
|
+
}));
|
|
66
|
+
finalizeInstance();
|
|
68
67
|
itemInstancesMap[item.itemId] = instance;
|
|
69
68
|
itemInstances.push(instance);
|
|
70
69
|
}
|
|
@@ -80,25 +79,36 @@ export const createTree = (initialConfig) => {
|
|
|
80
79
|
};
|
|
81
80
|
const mainFeature = {
|
|
82
81
|
key: "main",
|
|
83
|
-
|
|
82
|
+
treeInstance: {
|
|
83
|
+
getState: () => state,
|
|
84
|
+
setState: ({}, updater) => {
|
|
84
85
|
var _a;
|
|
85
86
|
// Not necessary, since I think the subupdate below keeps the state fresh anyways?
|
|
86
87
|
// state = typeof updater === "function" ? updater(state) : updater;
|
|
87
|
-
(_a = config.setState) === null || _a === void 0 ? void 0 : _a.call(config, state);
|
|
88
|
-
},
|
|
88
|
+
(_a = config.setState) === null || _a === void 0 ? void 0 : _a.call(config, state); // TODO this cant be right... This doesnt allow external state updates
|
|
89
|
+
},
|
|
90
|
+
applySubStateUpdate: ({}, stateName, updater) => {
|
|
89
91
|
state[stateName] =
|
|
90
92
|
typeof updater === "function" ? updater(state[stateName]) : updater;
|
|
91
|
-
config[stateHandlerNames[stateName]]
|
|
92
|
-
|
|
93
|
+
const externalStateSetter = config[stateHandlerNames[stateName]];
|
|
94
|
+
externalStateSetter === null || externalStateSetter === void 0 ? void 0 : externalStateSetter(state[stateName]);
|
|
95
|
+
},
|
|
96
|
+
// TODO rebuildSubTree: (itemId: string) => void;
|
|
97
|
+
rebuildTree: () => {
|
|
93
98
|
var _a;
|
|
94
|
-
rebuildItemMeta(
|
|
99
|
+
rebuildItemMeta();
|
|
95
100
|
(_a = config.setState) === null || _a === void 0 ? void 0 : _a.call(config, state);
|
|
96
|
-
},
|
|
101
|
+
},
|
|
102
|
+
getConfig: () => config,
|
|
103
|
+
setConfig: (_, updater) => {
|
|
97
104
|
config = typeof updater === "function" ? updater(config) : updater;
|
|
98
105
|
if (config.state) {
|
|
99
106
|
state = Object.assign(Object.assign({}, state), config.state);
|
|
100
107
|
}
|
|
101
|
-
},
|
|
108
|
+
},
|
|
109
|
+
getItemInstance: ({}, itemId) => itemInstancesMap[itemId],
|
|
110
|
+
getItems: () => itemInstances,
|
|
111
|
+
registerElement: ({}, element) => {
|
|
102
112
|
if (treeElement === element) {
|
|
103
113
|
return;
|
|
104
114
|
}
|
|
@@ -109,28 +119,37 @@ export const createTree = (initialConfig) => {
|
|
|
109
119
|
eachFeature((feature) => { var _a; return (_a = feature.onTreeMount) === null || _a === void 0 ? void 0 : _a.call(feature, treeInstance, element); });
|
|
110
120
|
}
|
|
111
121
|
treeElement = element;
|
|
112
|
-
},
|
|
113
|
-
|
|
122
|
+
},
|
|
123
|
+
getElement: () => treeElement,
|
|
124
|
+
getDataRef: () => treeDataRef,
|
|
125
|
+
getHotkeyPresets: () => hotkeyPresets,
|
|
126
|
+
},
|
|
127
|
+
itemInstance: {
|
|
128
|
+
// TODO just change to a getRef method that memoizes, maybe as part of getProps
|
|
129
|
+
registerElement: ({ itemId, item }, element) => {
|
|
114
130
|
if (itemElementsMap[itemId] === element) {
|
|
115
131
|
return;
|
|
116
132
|
}
|
|
117
133
|
const oldElement = itemElementsMap[itemId];
|
|
118
134
|
if (oldElement && !element) {
|
|
119
|
-
eachFeature((feature) => { var _a; return (_a = feature.onItemUnmount) === null || _a === void 0 ? void 0 : _a.call(feature,
|
|
135
|
+
eachFeature((feature) => { var _a; return (_a = feature.onItemUnmount) === null || _a === void 0 ? void 0 : _a.call(feature, item, oldElement, treeInstance); });
|
|
120
136
|
}
|
|
121
137
|
else if (!oldElement && element) {
|
|
122
|
-
eachFeature((feature) => { var _a; return (_a = feature.onItemMount) === null || _a === void 0 ? void 0 : _a.call(feature,
|
|
138
|
+
eachFeature((feature) => { var _a; return (_a = feature.onItemMount) === null || _a === void 0 ? void 0 : _a.call(feature, item, element, treeInstance); });
|
|
123
139
|
}
|
|
124
140
|
itemElementsMap[itemId] = element;
|
|
125
|
-
},
|
|
141
|
+
},
|
|
142
|
+
getElement: ({ itemId }) => itemElementsMap[itemId],
|
|
126
143
|
// eslint-disable-next-line no-return-assign
|
|
127
|
-
getDataRef: () => { var _a; return ((_a = itemDataRefs[itemId]) !== null && _a !== void 0 ? _a : (itemDataRefs[itemId] = { current: {} })); },
|
|
144
|
+
getDataRef: ({ itemId }) => { var _a; return ((_a = itemDataRefs[itemId]) !== null && _a !== void 0 ? _a : (itemDataRefs[itemId] = { current: {} })); },
|
|
145
|
+
getItemMeta: ({ itemId }) => itemMetaMap[itemId],
|
|
146
|
+
},
|
|
128
147
|
};
|
|
129
|
-
|
|
148
|
+
features.unshift(mainFeature);
|
|
130
149
|
for (const feature of features) {
|
|
131
|
-
Object.assign(
|
|
132
|
-
Object.assign(hotkeyPresets, (_e = feature.hotkeys) !== null && _e !== void 0 ? _e : {});
|
|
150
|
+
Object.assign(hotkeyPresets, (_d = feature.hotkeys) !== null && _d !== void 0 ? _d : {});
|
|
133
151
|
}
|
|
134
|
-
|
|
152
|
+
finalizeTree();
|
|
153
|
+
rebuildItemMeta();
|
|
135
154
|
return treeInstance;
|
|
136
155
|
};
|