@knotx/data 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.
- package/LICENSE +21 -0
- package/dist/index.cjs +807 -0
- package/dist/index.d.cts +314 -0
- package/dist/index.d.mts +314 -0
- package/dist/index.d.ts +314 -0
- package/dist/index.mjs +803 -0
- package/package.json +45 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
import { Subject, BehaviorSubject, pipe } from 'rxjs';
|
|
2
|
+
import { map, distinctUntilChanged, tap, scan } from 'rxjs/operators';
|
|
3
|
+
|
|
4
|
+
var __defProp$1 = Object.defineProperty;
|
|
5
|
+
var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
|
|
6
|
+
var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
|
|
8
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __spreadValues$1 = (a, b) => {
|
|
10
|
+
for (var prop in b || (b = {}))
|
|
11
|
+
if (__hasOwnProp$1.call(b, prop))
|
|
12
|
+
__defNormalProp$1(a, prop, b[prop]);
|
|
13
|
+
if (__getOwnPropSymbols$1)
|
|
14
|
+
for (var prop of __getOwnPropSymbols$1(b)) {
|
|
15
|
+
if (__propIsEnum$1.call(b, prop))
|
|
16
|
+
__defNormalProp$1(a, prop, b[prop]);
|
|
17
|
+
}
|
|
18
|
+
return a;
|
|
19
|
+
};
|
|
20
|
+
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
21
|
+
class DataManager {
|
|
22
|
+
constructor(tag) {
|
|
23
|
+
this.tag = tag;
|
|
24
|
+
__publicField$1(this, "operations$", new Subject());
|
|
25
|
+
__publicField$1(this, "operationPipes$", new BehaviorSubject({
|
|
26
|
+
transform: [],
|
|
27
|
+
preOperation: [],
|
|
28
|
+
postOperation: []
|
|
29
|
+
}));
|
|
30
|
+
__publicField$1(this, "drafts$", new BehaviorSubject(/* @__PURE__ */ new Map()));
|
|
31
|
+
__publicField$1(this, "dataMap$", new BehaviorSubject(/* @__PURE__ */ new Map()));
|
|
32
|
+
__publicField$1(this, "currentDraftDataMap$", new BehaviorSubject(void 0));
|
|
33
|
+
/**
|
|
34
|
+
* 数据版本号,用于标识数据的变化,数据更新时,patchVersion 会递增
|
|
35
|
+
*/
|
|
36
|
+
__publicField$1(this, "patchVersion$", new BehaviorSubject(0));
|
|
37
|
+
/**
|
|
38
|
+
* 数据版本号,用于标识数据的变化,数据增删时,mapVersion 会递增
|
|
39
|
+
*/
|
|
40
|
+
__publicField$1(this, "mapVersion$", new BehaviorSubject(0));
|
|
41
|
+
__publicField$1(this, "_patchVersionUpdating", false);
|
|
42
|
+
__publicField$1(this, "_mapVersionUpdating", false);
|
|
43
|
+
}
|
|
44
|
+
get version() {
|
|
45
|
+
return this.patchVersion$.value + this.mapVersion$.value;
|
|
46
|
+
}
|
|
47
|
+
getDataList$() {
|
|
48
|
+
return this.dataMap$.pipe(
|
|
49
|
+
map((dataMap) => Array.from(dataMap.values()))
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
getDataList() {
|
|
53
|
+
return Array.from(this.dataMap$.value.values());
|
|
54
|
+
}
|
|
55
|
+
getData$(id, equal = Object.is) {
|
|
56
|
+
return this.dataMap$.pipe(
|
|
57
|
+
map((dataMap) => dataMap.get(id)),
|
|
58
|
+
distinctUntilChanged(equal)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
getData(id) {
|
|
62
|
+
return this.dataMap$.value.get(id);
|
|
63
|
+
}
|
|
64
|
+
addDataOperationPipe(pipe2) {
|
|
65
|
+
const currentPipes = this.operationPipes$.value;
|
|
66
|
+
if (pipe2.transform) {
|
|
67
|
+
currentPipes.transform.push(pipe2.transform);
|
|
68
|
+
}
|
|
69
|
+
if (pipe2.preOperation) {
|
|
70
|
+
currentPipes.preOperation.push(pipe2.preOperation);
|
|
71
|
+
}
|
|
72
|
+
if (pipe2.postOperation) {
|
|
73
|
+
currentPipes.postOperation.push(pipe2.postOperation);
|
|
74
|
+
}
|
|
75
|
+
this.operationPipes$.next(currentPipes);
|
|
76
|
+
}
|
|
77
|
+
init(initialDataList = []) {
|
|
78
|
+
this.operations$.pipe(
|
|
79
|
+
// 执行预设转换管道
|
|
80
|
+
pipe(...this.operationPipes$.value.transform.map((pipe2) => pipe2(this.operations$))),
|
|
81
|
+
// 执行预处理管道
|
|
82
|
+
pipe(...this.operationPipes$.value.preOperation.map((pipe2) => pipe2(this.operations$))),
|
|
83
|
+
// 更新版本号
|
|
84
|
+
tap((operation) => this.updateVersion(operation)),
|
|
85
|
+
scan((dataMap, operation) => this.applyOperation(dataMap, operation), this.dataMap$.value),
|
|
86
|
+
map((dataMap) => ({ dataMap, operations: [] })),
|
|
87
|
+
// 执行后处理管道
|
|
88
|
+
pipe(...this.operationPipes$.value.postOperation.map((pipe2) => pipe2(this.operations$))),
|
|
89
|
+
// 更新版本号
|
|
90
|
+
tap(({ operations }) => this.updateVersion({ type: "batch", operations })),
|
|
91
|
+
map(({ dataMap, operations }) => this.applyOperation(dataMap, { type: "batch", operations }))
|
|
92
|
+
).subscribe(this.dataMap$);
|
|
93
|
+
this.operations$.next({ type: "batch", operations: initialDataList.map((data) => ({ type: "add", data })), isInit: true });
|
|
94
|
+
}
|
|
95
|
+
getOperations$() {
|
|
96
|
+
return this.operations$.asObservable();
|
|
97
|
+
}
|
|
98
|
+
dispatch(operation) {
|
|
99
|
+
this.operations$.next(operation);
|
|
100
|
+
}
|
|
101
|
+
getDraftDataList(draftId) {
|
|
102
|
+
const draftDataMap = this.drafts$.value.get(draftId);
|
|
103
|
+
return draftDataMap ? Array.from(draftDataMap.value.values()).filter((data) => data !== false) : void 0;
|
|
104
|
+
}
|
|
105
|
+
getDraftData(draftId, dataId) {
|
|
106
|
+
const draftDataMap = this.drafts$.value.get(draftId);
|
|
107
|
+
const data = draftDataMap == null ? void 0 : draftDataMap.value.get(dataId);
|
|
108
|
+
if (data === false) {
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
return data;
|
|
112
|
+
}
|
|
113
|
+
getCurrentDraftData(dataId) {
|
|
114
|
+
var _a;
|
|
115
|
+
const data = (_a = this.currentDraftDataMap$.value) == null ? void 0 : _a.get(dataId);
|
|
116
|
+
if (data === false) {
|
|
117
|
+
return void 0;
|
|
118
|
+
}
|
|
119
|
+
return data;
|
|
120
|
+
}
|
|
121
|
+
applyOperation(dataMap, operation) {
|
|
122
|
+
switch (operation.type) {
|
|
123
|
+
case "add":
|
|
124
|
+
dataMap.delete(operation.data.id);
|
|
125
|
+
dataMap.set(operation.data.id, operation.data);
|
|
126
|
+
break;
|
|
127
|
+
case "remove":
|
|
128
|
+
dataMap.delete(operation.id);
|
|
129
|
+
break;
|
|
130
|
+
case "update": {
|
|
131
|
+
const current = dataMap.get(operation.id);
|
|
132
|
+
if (current) {
|
|
133
|
+
dataMap.set(operation.id, __spreadValues$1(__spreadValues$1({}, current), operation.data));
|
|
134
|
+
}
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
case "batch":
|
|
138
|
+
operation.operations.reduce(
|
|
139
|
+
(acc, op) => this.applyOperation(acc, op),
|
|
140
|
+
dataMap
|
|
141
|
+
);
|
|
142
|
+
break;
|
|
143
|
+
case "startDraft": {
|
|
144
|
+
const draftDataMap$ = new BehaviorSubject(/* @__PURE__ */ new Map());
|
|
145
|
+
this.drafts$.next(this.drafts$.value.set(operation.draftId, draftDataMap$));
|
|
146
|
+
this.currentDraftDataMap$.next(draftDataMap$.value);
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
case "draftOperation": {
|
|
150
|
+
const draftDataMap = this.drafts$.value.get(operation.draftId);
|
|
151
|
+
if (draftDataMap) {
|
|
152
|
+
this.applyOperation(new Proxy(draftDataMap.value, {
|
|
153
|
+
get(target, prop) {
|
|
154
|
+
const draft = target;
|
|
155
|
+
switch (prop) {
|
|
156
|
+
case "get":
|
|
157
|
+
return (id) => {
|
|
158
|
+
if (!draft.has(id)) {
|
|
159
|
+
return dataMap.get(id);
|
|
160
|
+
}
|
|
161
|
+
return draft.get(id) || void 0;
|
|
162
|
+
};
|
|
163
|
+
case "set":
|
|
164
|
+
return (id, value) => draft.set(id, value);
|
|
165
|
+
case "delete":
|
|
166
|
+
return (id) => draft.set(id, false);
|
|
167
|
+
default:
|
|
168
|
+
return Reflect.get(target, prop);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}), operation.operation);
|
|
172
|
+
this.currentDraftDataMap$.next(draftDataMap.value);
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
case "commitDraft": {
|
|
177
|
+
const draftData = this.drafts$.value.get(operation.draftId);
|
|
178
|
+
if (draftData) {
|
|
179
|
+
const operations = [];
|
|
180
|
+
draftData.value.forEach((data, id) => {
|
|
181
|
+
if (data === false) {
|
|
182
|
+
if (dataMap.has(id)) {
|
|
183
|
+
operations.push({ type: "remove", id });
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (!dataMap.has(id)) {
|
|
188
|
+
operations.push({ type: "add", data });
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const currentData = dataMap.get(id);
|
|
192
|
+
if (!Object.is(currentData, data)) {
|
|
193
|
+
operations.push({ type: "update", id, data });
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
if (operations.length > 0) {
|
|
197
|
+
this.dispatch({ type: "batch", operations });
|
|
198
|
+
}
|
|
199
|
+
this.drafts$.value.delete(operation.draftId);
|
|
200
|
+
this.drafts$.next(this.drafts$.value);
|
|
201
|
+
if (this.currentDraftDataMap$.value === draftData.value) {
|
|
202
|
+
this.currentDraftDataMap$.next(void 0);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case "discardDraft": {
|
|
208
|
+
this.drafts$.value.delete(operation.draftId);
|
|
209
|
+
this.drafts$.next(this.drafts$.value);
|
|
210
|
+
const draftData = this.drafts$.value.get(operation.draftId);
|
|
211
|
+
if (this.currentDraftDataMap$.value === (draftData == null ? void 0 : draftData.value)) {
|
|
212
|
+
this.currentDraftDataMap$.next(void 0);
|
|
213
|
+
}
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return dataMap;
|
|
218
|
+
}
|
|
219
|
+
updateVersion(operation, scheduler = queueMicrotask) {
|
|
220
|
+
if (this.isPatchOperation(operation)) {
|
|
221
|
+
if (this._patchVersionUpdating) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
this._patchVersionUpdating = true;
|
|
225
|
+
scheduler(() => {
|
|
226
|
+
this.patchVersion$.next(this.patchVersion$.value + 1);
|
|
227
|
+
this._patchVersionUpdating = false;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
if (this.isMapOperation(operation)) {
|
|
231
|
+
if (this._mapVersionUpdating) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
this._mapVersionUpdating = true;
|
|
235
|
+
scheduler(() => {
|
|
236
|
+
this.mapVersion$.next(this.mapVersion$.value + 1);
|
|
237
|
+
this._mapVersionUpdating = false;
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
isPatchOperation(operation) {
|
|
242
|
+
switch (operation.type) {
|
|
243
|
+
case "update":
|
|
244
|
+
return true;
|
|
245
|
+
case "batch": {
|
|
246
|
+
return operation.isInit ? false : operation.operations.some((op) => this.isPatchOperation(op));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
isMapOperation(operation) {
|
|
252
|
+
switch (operation.type) {
|
|
253
|
+
case "add":
|
|
254
|
+
case "remove":
|
|
255
|
+
return true;
|
|
256
|
+
case "batch": {
|
|
257
|
+
return operation.isInit ? false : operation.operations.some((op) => this.isMapOperation(op));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
var __defProp = Object.defineProperty;
|
|
265
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
266
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
267
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
268
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
269
|
+
var __spreadValues = (a, b) => {
|
|
270
|
+
for (var prop in b || (b = {}))
|
|
271
|
+
if (__hasOwnProp.call(b, prop))
|
|
272
|
+
__defNormalProp(a, prop, b[prop]);
|
|
273
|
+
if (__getOwnPropSymbols)
|
|
274
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
275
|
+
if (__propIsEnum.call(b, prop))
|
|
276
|
+
__defNormalProp(a, prop, b[prop]);
|
|
277
|
+
}
|
|
278
|
+
return a;
|
|
279
|
+
};
|
|
280
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
281
|
+
var ChangeType = /* @__PURE__ */ ((ChangeType2) => {
|
|
282
|
+
ChangeType2["ADD"] = "add";
|
|
283
|
+
ChangeType2["REMOVE"] = "remove";
|
|
284
|
+
return ChangeType2;
|
|
285
|
+
})(ChangeType || {});
|
|
286
|
+
class DualRelation {
|
|
287
|
+
constructor(options = {}) {
|
|
288
|
+
__publicField(this, "parentToChildren");
|
|
289
|
+
// 父节点到子节点集合的映射
|
|
290
|
+
__publicField(this, "childToParent");
|
|
291
|
+
// 子节点到父节点的映射
|
|
292
|
+
__publicField(this, "_version", 0);
|
|
293
|
+
// 内部版本计数器
|
|
294
|
+
__publicField(this, "_versionSubject");
|
|
295
|
+
// 版本变更的Observable
|
|
296
|
+
__publicField(this, "_batchUpdateMode", false);
|
|
297
|
+
// 是否处于批量更新模式
|
|
298
|
+
__publicField(this, "_hasPendingChanges", false);
|
|
299
|
+
// 是否有待处理的变更
|
|
300
|
+
__publicField(this, "_changeHistory", []);
|
|
301
|
+
// 变更历史记录
|
|
302
|
+
__publicField(this, "_options");
|
|
303
|
+
// 配置选项
|
|
304
|
+
__publicField(this, "_pendingChanges", []);
|
|
305
|
+
// 批量模式下的待处理变更
|
|
306
|
+
__publicField(this, "_nodeLevels", /* @__PURE__ */ new Map());
|
|
307
|
+
// 节点层级映射
|
|
308
|
+
__publicField(this, "_needsLevelRecalculation", false);
|
|
309
|
+
var _a, _b;
|
|
310
|
+
this.parentToChildren = /* @__PURE__ */ new Map();
|
|
311
|
+
this.childToParent = /* @__PURE__ */ new Map();
|
|
312
|
+
this._versionSubject = new BehaviorSubject(this._version);
|
|
313
|
+
this._options = {
|
|
314
|
+
allowEmptyParent: (_a = options.allowEmptyParent) != null ? _a : false,
|
|
315
|
+
historyDepth: (_b = options.historyDepth) != null ? _b : 10
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
// 是否需要重新计算所有层级
|
|
319
|
+
/**
|
|
320
|
+
* 获取版本变更的Observable
|
|
321
|
+
*/
|
|
322
|
+
get version() {
|
|
323
|
+
return this._versionSubject;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* 获取变更历史记录
|
|
327
|
+
*/
|
|
328
|
+
get changeHistory() {
|
|
329
|
+
return this._changeHistory;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* 更新配置选项
|
|
333
|
+
* @param options 新的配置选项
|
|
334
|
+
*/
|
|
335
|
+
updateOptions(options) {
|
|
336
|
+
this._options = __spreadValues(__spreadValues({}, this._options), options);
|
|
337
|
+
if (options.historyDepth !== void 0 && this._changeHistory.length > options.historyDepth) {
|
|
338
|
+
this._changeHistory = this._changeHistory.slice(-options.historyDepth);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* 获取节点的层级
|
|
343
|
+
* 层级定义:
|
|
344
|
+
* 0: 不在关系中的节点
|
|
345
|
+
* 1: 顶层父节点(没有父节点的节点)
|
|
346
|
+
* n > 1: 子节点的层级是其父节点的层级 + 1
|
|
347
|
+
* @param node 要查询的节点
|
|
348
|
+
* @returns 节点的层级
|
|
349
|
+
*/
|
|
350
|
+
getNodeLevel(node) {
|
|
351
|
+
if (this._needsLevelRecalculation) {
|
|
352
|
+
this.recalculateAllLevels();
|
|
353
|
+
}
|
|
354
|
+
return this._nodeLevels.get(node) || 0;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* 重新计算所有节点的层级
|
|
358
|
+
* @private
|
|
359
|
+
*/
|
|
360
|
+
recalculateAllLevels() {
|
|
361
|
+
this._nodeLevels.clear();
|
|
362
|
+
for (const parent of this.parentToChildren.keys()) {
|
|
363
|
+
if (!this.childToParent.has(parent)) {
|
|
364
|
+
this._nodeLevels.set(parent, 1);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const processed = /* @__PURE__ */ new Set();
|
|
368
|
+
let currentLevel = 1;
|
|
369
|
+
let currentLevelNodes = Array.from(this._nodeLevels.keys());
|
|
370
|
+
while (currentLevelNodes.length > 0) {
|
|
371
|
+
const nextLevelNodes = [];
|
|
372
|
+
currentLevel++;
|
|
373
|
+
for (const node of currentLevelNodes) {
|
|
374
|
+
processed.add(node);
|
|
375
|
+
const children = this.getChildren(node);
|
|
376
|
+
for (const child of children) {
|
|
377
|
+
if (!processed.has(child)) {
|
|
378
|
+
this._nodeLevels.set(child, currentLevel);
|
|
379
|
+
nextLevelNodes.push(child);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
currentLevelNodes = nextLevelNodes;
|
|
384
|
+
}
|
|
385
|
+
this._needsLevelRecalculation = false;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* 更新节点层级
|
|
389
|
+
* @param node 要更新的节点
|
|
390
|
+
* @param parent 节点的父节点
|
|
391
|
+
* @private
|
|
392
|
+
*/
|
|
393
|
+
updateNodeLevel(node, parent) {
|
|
394
|
+
if (!parent) {
|
|
395
|
+
this._nodeLevels.delete(node);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
let parentLevel = this._nodeLevels.get(parent);
|
|
399
|
+
if (parentLevel === void 0) {
|
|
400
|
+
const grandParent = this.getParent(parent);
|
|
401
|
+
if (grandParent) {
|
|
402
|
+
this.updateNodeLevel(parent, grandParent);
|
|
403
|
+
parentLevel = this._nodeLevels.get(parent);
|
|
404
|
+
} else {
|
|
405
|
+
parentLevel = 1;
|
|
406
|
+
this._nodeLevels.set(parent, parentLevel);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (parentLevel === void 0) {
|
|
410
|
+
this._needsLevelRecalculation = true;
|
|
411
|
+
parentLevel = 1;
|
|
412
|
+
}
|
|
413
|
+
this._nodeLevels.set(node, parentLevel + 1);
|
|
414
|
+
this.updateChildrenLevels(node, parentLevel + 1);
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* 递归更新所有子节点的层级
|
|
418
|
+
* @param parent 父节点
|
|
419
|
+
* @param parentLevel 父节点的层级
|
|
420
|
+
* @private
|
|
421
|
+
*/
|
|
422
|
+
updateChildrenLevels(parent, parentLevel) {
|
|
423
|
+
const children = this.getChildren(parent);
|
|
424
|
+
for (const child of children) {
|
|
425
|
+
const childLevel = parentLevel + 1;
|
|
426
|
+
this._nodeLevels.set(child, childLevel);
|
|
427
|
+
this.updateChildrenLevels(child, childLevel);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* 记录变更
|
|
432
|
+
* @param parent 父节点
|
|
433
|
+
* @param children 子节点数组
|
|
434
|
+
* @param type 变更类型
|
|
435
|
+
* @private
|
|
436
|
+
*/
|
|
437
|
+
recordChange(parent, children, type) {
|
|
438
|
+
const change = {
|
|
439
|
+
parent,
|
|
440
|
+
children: [...children],
|
|
441
|
+
type,
|
|
442
|
+
timestamp: Date.now()
|
|
443
|
+
};
|
|
444
|
+
if (this._batchUpdateMode) {
|
|
445
|
+
this._pendingChanges.push(change);
|
|
446
|
+
} else {
|
|
447
|
+
this._changeHistory.push(change);
|
|
448
|
+
if (this._changeHistory.length > this._options.historyDepth) {
|
|
449
|
+
this._changeHistory.shift();
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* 更新版本号,用于通知外部关系发生了变化
|
|
455
|
+
* @private
|
|
456
|
+
*/
|
|
457
|
+
updateVersion() {
|
|
458
|
+
if (this._batchUpdateMode) {
|
|
459
|
+
this._hasPendingChanges = true;
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
this._version++;
|
|
463
|
+
this._versionSubject.next(this._version);
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* 开始批量更新操作
|
|
467
|
+
* 在此模式下,所有导致版本变更的操作将被延迟,直到调用commitBatch
|
|
468
|
+
*/
|
|
469
|
+
beginBatch() {
|
|
470
|
+
this._batchUpdateMode = true;
|
|
471
|
+
this._hasPendingChanges = false;
|
|
472
|
+
this._pendingChanges = [];
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* 提交批量更新,如果有变更则更新版本
|
|
476
|
+
* @returns 是否有变更被提交
|
|
477
|
+
*/
|
|
478
|
+
commitBatch() {
|
|
479
|
+
if (!this._batchUpdateMode) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
this._batchUpdateMode = false;
|
|
483
|
+
if (this._hasPendingChanges) {
|
|
484
|
+
for (const change of this._pendingChanges) {
|
|
485
|
+
this._changeHistory.push(change);
|
|
486
|
+
if (this._changeHistory.length > this._options.historyDepth) {
|
|
487
|
+
this._changeHistory.shift();
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
this._pendingChanges = [];
|
|
491
|
+
this._version++;
|
|
492
|
+
this._versionSubject.next(this._version);
|
|
493
|
+
this._hasPendingChanges = false;
|
|
494
|
+
if (this._needsLevelRecalculation) {
|
|
495
|
+
this.recalculateAllLevels();
|
|
496
|
+
}
|
|
497
|
+
return true;
|
|
498
|
+
}
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* 回滚批量更新,取消所有未提交的变更
|
|
503
|
+
* 注意:此方法不会撤销已执行的关系变更,只会阻止版本更新
|
|
504
|
+
*/
|
|
505
|
+
rollbackBatch() {
|
|
506
|
+
this._batchUpdateMode = false;
|
|
507
|
+
this._hasPendingChanges = false;
|
|
508
|
+
this._pendingChanges = [];
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* 在批量模式中执行多个操作并自动提交
|
|
512
|
+
* @param operations 要执行的函数
|
|
513
|
+
* @returns 批量操作是否导致了版本变更
|
|
514
|
+
*/
|
|
515
|
+
batch(operations) {
|
|
516
|
+
this.beginBatch();
|
|
517
|
+
try {
|
|
518
|
+
operations();
|
|
519
|
+
return this.commitBatch();
|
|
520
|
+
} catch (error) {
|
|
521
|
+
this.rollbackBatch();
|
|
522
|
+
throw error;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* 添加父子关系
|
|
527
|
+
* @param parent 父节点
|
|
528
|
+
* @param child 子节点
|
|
529
|
+
*/
|
|
530
|
+
add(parent, child) {
|
|
531
|
+
const hadParent = this.childToParent.has(child);
|
|
532
|
+
const oldParent = hadParent ? this.childToParent.get(child) : null;
|
|
533
|
+
const isNewRelation = !hadParent || oldParent !== parent;
|
|
534
|
+
if (!isNewRelation) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
if (this.childToParent.has(child)) {
|
|
538
|
+
const oldParent2 = this.childToParent.get(child);
|
|
539
|
+
const children = this.parentToChildren.get(oldParent2);
|
|
540
|
+
if (children) {
|
|
541
|
+
children.delete(child);
|
|
542
|
+
if (children.size === 0 && !this._options.allowEmptyParent) {
|
|
543
|
+
this.parentToChildren.delete(oldParent2);
|
|
544
|
+
if (!this.childToParent.has(oldParent2)) {
|
|
545
|
+
this._nodeLevels.delete(oldParent2);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
this.recordChange(oldParent2, [child], "remove" /* REMOVE */);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (!this.parentToChildren.has(parent)) {
|
|
552
|
+
this.parentToChildren.set(parent, /* @__PURE__ */ new Set());
|
|
553
|
+
if (!this.childToParent.has(parent)) {
|
|
554
|
+
this._nodeLevels.set(parent, 1);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
this.parentToChildren.get(parent).add(child);
|
|
558
|
+
this.childToParent.set(child, parent);
|
|
559
|
+
this.updateNodeLevel(child, parent);
|
|
560
|
+
this.recordChange(parent, [child], "add" /* ADD */);
|
|
561
|
+
this.updateVersion();
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* 移除子节点及其父关系
|
|
565
|
+
* @param child 子节点
|
|
566
|
+
* @returns 是否成功移除
|
|
567
|
+
*/
|
|
568
|
+
removeChild(child) {
|
|
569
|
+
if (!this.childToParent.has(child)) {
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
const parent = this.childToParent.get(child);
|
|
573
|
+
const children = this.parentToChildren.get(parent);
|
|
574
|
+
if (children) {
|
|
575
|
+
children.delete(child);
|
|
576
|
+
if (children.size === 0 && !this._options.allowEmptyParent) {
|
|
577
|
+
this.parentToChildren.delete(parent);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
this.childToParent.delete(child);
|
|
581
|
+
this._nodeLevels.delete(child);
|
|
582
|
+
if (this.parentToChildren.has(child)) {
|
|
583
|
+
this._needsLevelRecalculation = true;
|
|
584
|
+
}
|
|
585
|
+
this.recordChange(parent, [child], "remove" /* REMOVE */);
|
|
586
|
+
this.updateVersion();
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* 移除父节点及其所有子节点的关系
|
|
591
|
+
* @param parent 父节点
|
|
592
|
+
* @returns 是否成功移除
|
|
593
|
+
*/
|
|
594
|
+
removeParent(parent) {
|
|
595
|
+
if (!this.parentToChildren.has(parent)) {
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
const children = this.parentToChildren.get(parent);
|
|
599
|
+
const childrenArray = Array.from(children);
|
|
600
|
+
for (const child of children) {
|
|
601
|
+
if (this.parentToChildren.has(child)) {
|
|
602
|
+
this._needsLevelRecalculation = true;
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
children.forEach((child) => {
|
|
607
|
+
this.childToParent.delete(child);
|
|
608
|
+
this._nodeLevels.delete(child);
|
|
609
|
+
});
|
|
610
|
+
this.parentToChildren.delete(parent);
|
|
611
|
+
if (!this.childToParent.has(parent)) {
|
|
612
|
+
this._nodeLevels.delete(parent);
|
|
613
|
+
}
|
|
614
|
+
this.recordChange(parent, childrenArray, "remove" /* REMOVE */);
|
|
615
|
+
this.updateVersion();
|
|
616
|
+
return true;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* 清空所有关系并清除历史记录
|
|
620
|
+
* @param clearHistory 是否清除历史记录,默认为true
|
|
621
|
+
*/
|
|
622
|
+
clear(clearHistory = true) {
|
|
623
|
+
if (this.parentToChildren.size > 0 || this.childToParent.size > 0) {
|
|
624
|
+
if (!clearHistory) {
|
|
625
|
+
for (const [parent, children] of this.parentToChildren.entries()) {
|
|
626
|
+
this.recordChange(parent, Array.from(children), "remove" /* REMOVE */);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
this.parentToChildren.clear();
|
|
630
|
+
this.childToParent.clear();
|
|
631
|
+
this._nodeLevels.clear();
|
|
632
|
+
this._needsLevelRecalculation = false;
|
|
633
|
+
if (clearHistory) {
|
|
634
|
+
this._changeHistory = [];
|
|
635
|
+
}
|
|
636
|
+
this.updateVersion();
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* 获取所有子节点
|
|
641
|
+
* @param parent 父节点
|
|
642
|
+
* @returns 子节点集合
|
|
643
|
+
*/
|
|
644
|
+
getChildren(parent) {
|
|
645
|
+
return this.parentToChildren.get(parent) || /* @__PURE__ */ new Set();
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* 获取父节点
|
|
649
|
+
* @param child 子节点
|
|
650
|
+
* @returns 父节点,如果不存在则返回undefined
|
|
651
|
+
*/
|
|
652
|
+
getParent(child) {
|
|
653
|
+
return this.childToParent.get(child);
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* 获取根节点
|
|
657
|
+
* @param node 子节点
|
|
658
|
+
* @returns 根节点,如果节点无父节点则返回该节点本身,如果检测到循环引用则返回null
|
|
659
|
+
*/
|
|
660
|
+
getRootParent(node) {
|
|
661
|
+
if (!this.childToParent.has(node)) {
|
|
662
|
+
return node;
|
|
663
|
+
}
|
|
664
|
+
const visited = /* @__PURE__ */ new Set();
|
|
665
|
+
let current = node;
|
|
666
|
+
while (true) {
|
|
667
|
+
visited.add(current);
|
|
668
|
+
const parent = this.childToParent.get(current);
|
|
669
|
+
if (!parent) {
|
|
670
|
+
return current;
|
|
671
|
+
}
|
|
672
|
+
if (visited.has(parent)) {
|
|
673
|
+
return null;
|
|
674
|
+
}
|
|
675
|
+
current = parent;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* 检查是否存在循环引用
|
|
680
|
+
* @returns 是否存在循环引用
|
|
681
|
+
*/
|
|
682
|
+
hasCircularReference() {
|
|
683
|
+
for (const child of this.childToParent.keys()) {
|
|
684
|
+
const visited = /* @__PURE__ */ new Set();
|
|
685
|
+
let current = child;
|
|
686
|
+
while (true) {
|
|
687
|
+
visited.add(current);
|
|
688
|
+
const parent = this.childToParent.get(current);
|
|
689
|
+
if (!parent) {
|
|
690
|
+
break;
|
|
691
|
+
}
|
|
692
|
+
if (visited.has(parent)) {
|
|
693
|
+
return true;
|
|
694
|
+
}
|
|
695
|
+
current = parent;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* 添加父子关系,但会检查是否会形成循环引用
|
|
702
|
+
* @param parent 父节点
|
|
703
|
+
* @param child 子节点
|
|
704
|
+
* @returns 是否成功添加关系(如果会形成循环引用则返回false)
|
|
705
|
+
*/
|
|
706
|
+
addSafe(parent, child) {
|
|
707
|
+
if (parent === child) {
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
if (this.isAncestorOf(child, parent)) {
|
|
711
|
+
return false;
|
|
712
|
+
}
|
|
713
|
+
this.add(parent, child);
|
|
714
|
+
return true;
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* 检查节点A是否是节点B的祖先节点
|
|
718
|
+
* @param nodeA 可能的祖先节点
|
|
719
|
+
* @param nodeB 待检查节点
|
|
720
|
+
* @returns 是否是祖先关系
|
|
721
|
+
*/
|
|
722
|
+
isAncestorOf(nodeA, nodeB) {
|
|
723
|
+
if (!this.parentToChildren.has(nodeB)) {
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
const queue = [nodeB];
|
|
727
|
+
const visited = /* @__PURE__ */ new Set();
|
|
728
|
+
while (queue.length > 0) {
|
|
729
|
+
const current = queue.shift();
|
|
730
|
+
visited.add(current);
|
|
731
|
+
const parent = this.getParent(current);
|
|
732
|
+
if (parent === nodeA) {
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
if (parent && !visited.has(parent)) {
|
|
736
|
+
queue.push(parent);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* 获取所有父节点
|
|
743
|
+
* @returns 父节点集合
|
|
744
|
+
*/
|
|
745
|
+
getAllParents() {
|
|
746
|
+
return new Set(this.parentToChildren.keys());
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* 获取所有子节点
|
|
750
|
+
* @returns 子节点集合
|
|
751
|
+
*/
|
|
752
|
+
getAllChildren() {
|
|
753
|
+
return new Set(this.childToParent.keys());
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* 检查父子关系是否存在
|
|
757
|
+
* @param parent 父节点
|
|
758
|
+
* @param child 子节点
|
|
759
|
+
* @returns 是否存在关系
|
|
760
|
+
*/
|
|
761
|
+
hasRelation(parent, child) {
|
|
762
|
+
const children = this.parentToChildren.get(parent);
|
|
763
|
+
return children ? children.has(child) : false;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* 获取关系数量
|
|
767
|
+
* @returns 关系数量
|
|
768
|
+
*/
|
|
769
|
+
size() {
|
|
770
|
+
return this.childToParent.size;
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* 获取指定节点的所有后代节点(包括子节点、孙节点等)
|
|
774
|
+
* @param parent 父节点
|
|
775
|
+
* @param includeSelf 是否包含自身,默认为false
|
|
776
|
+
* @returns 所有后代节点的集合
|
|
777
|
+
*/
|
|
778
|
+
getDescendants(parent, includeSelf = false) {
|
|
779
|
+
const result = /* @__PURE__ */ new Set();
|
|
780
|
+
if (!this.parentToChildren.has(parent)) {
|
|
781
|
+
return result;
|
|
782
|
+
}
|
|
783
|
+
const queue = [parent];
|
|
784
|
+
const visitedParents = /* @__PURE__ */ new Set();
|
|
785
|
+
while (queue.length > 0) {
|
|
786
|
+
const current = queue.shift();
|
|
787
|
+
visitedParents.add(current);
|
|
788
|
+
const children = this.getChildren(current);
|
|
789
|
+
for (const child of children) {
|
|
790
|
+
result.add(child);
|
|
791
|
+
if (this.parentToChildren.has(child) && !visitedParents.has(child)) {
|
|
792
|
+
queue.push(child);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
if (includeSelf) {
|
|
797
|
+
result.add(parent);
|
|
798
|
+
}
|
|
799
|
+
return result;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
export { ChangeType, DataManager, DualRelation };
|