@splicetree/core 3.0.1 → 3.1.0

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/dist/index.d.ts CHANGED
@@ -62,6 +62,7 @@ interface SpliceTreeNode<T = SpliceTreeData> {
62
62
  * @param expand 不传表示切换;true/false 表示显式设置
63
63
  */
64
64
  toggleExpand: Fn<void, boolean | undefined>;
65
+ updateOriginal: (patch: Partial<T>) => void;
65
66
  }
66
67
  /**
67
68
  * 事件负载映射(可被插件扩展)
@@ -216,6 +217,17 @@ interface SpliceTreeInstance<T = SpliceTreeData> {
216
217
  * @param beforeId 插入到指定兄弟节点之前(不传表示末尾)
217
218
  */
218
219
  moveNode: (id: string, newParentId: string | undefined, beforeId?: string) => void;
220
+ /**
221
+ * 移除指定节点或节点列表(包含其子孙)
222
+ * @param id 单个 id 或 id 数组
223
+ */
224
+ remove: (id: string | string[]) => void;
225
+ /**
226
+ * 更新节点原始数据(非结构字段)
227
+ * - 若包含父级字段变更,将转为 moveNode 处理
228
+ * - 不支持直接修改主键字段
229
+ */
230
+ updateOriginal: (id: string, patch: Partial<T>) => boolean;
219
231
  syncData: (next: T[]) => void;
220
232
  }
221
233
  /**
@@ -237,6 +249,13 @@ interface SpliceTreePluginContext<T = SpliceTreeData> {
237
249
  * 事件总线
238
250
  */
239
251
  events: SpliceTreeEvents;
252
+ /**
253
+ * 插件可访问的内部缓存
254
+ */
255
+ roots: SpliceTreeNode<T>[];
256
+ map: Map<string, SpliceTreeNode<T>>;
257
+ parentCache: Map<string, SpliceTreeNode<T> | undefined>;
258
+ childrenCache: Map<string, SpliceTreeNode<T>[]>;
240
259
  }
241
260
  /**
242
261
  * 插件定义接口
package/dist/index.js CHANGED
@@ -328,6 +328,36 @@ function moveNode(ctx, id, newParentId, beforeId) {
328
328
  setLevelRecursively(node, ctx.childrenCache, node.level);
329
329
  ctx.notify();
330
330
  }
331
+ function removeNodes(ctx, ids) {
332
+ const list = Array.isArray(ids) ? ids : [ids];
333
+ const toRemove = /* @__PURE__ */ new Set();
334
+ const collect = (id) => {
335
+ if (toRemove.has(id)) return;
336
+ toRemove.add(id);
337
+ const children = ctx.childrenCache.get(id) ?? [];
338
+ for (const c of children) collect(c.id);
339
+ };
340
+ for (const id of list) if (ctx.map.has(id)) collect(id);
341
+ for (const id of toRemove) {
342
+ const parent = ctx.parentCache.get(id);
343
+ if (parent) {
344
+ const arr = ctx.childrenCache.get(parent.id) ?? [];
345
+ const idx = arr.findIndex((n) => n.id === id);
346
+ if (idx >= 0) arr.splice(idx, 1);
347
+ } else {
348
+ const idx = ctx.roots.findIndex((n) => n.id === id);
349
+ if (idx >= 0) ctx.roots.splice(idx, 1);
350
+ }
351
+ }
352
+ for (const id of toRemove) {
353
+ ctx.childrenCache.delete(id);
354
+ ctx.parentCache.delete(id);
355
+ ctx.map.delete(id);
356
+ ctx.expandedKeys.delete(id);
357
+ }
358
+ for (const r of ctx.roots) setLevelRecursively(r, ctx.childrenCache, 0);
359
+ ctx.notify();
360
+ }
331
361
 
332
362
  //#endregion
333
363
  //#region src/use.ts
@@ -338,6 +368,7 @@ function moveNode(ctx, id, newParentId, beforeId) {
338
368
  * - 提供插件扩展点(setup/extendNode)
339
369
  */
340
370
  function createSpliceTree(data, options = {}) {
371
+ let pluginCtx;
341
372
  const cfg = options.configuration ?? {};
342
373
  const keyField = cfg.keyField ?? "id";
343
374
  const parentField = cfg.parentField ?? "parent";
@@ -407,7 +438,10 @@ function createSpliceTree(data, options = {}) {
407
438
  parentCache,
408
439
  childrenCache,
409
440
  expandedKeys,
410
- notify: emitVisibility
441
+ notify: () => {
442
+ applyNodeExtensions();
443
+ emitVisibility();
444
+ }
411
445
  }, parentId, children);
412
446
  },
413
447
  moveNode(id, newParentId, beforeId) {
@@ -422,6 +456,31 @@ function createSpliceTree(data, options = {}) {
422
456
  notify: emitVisibility
423
457
  }, id, newParentId, beforeId);
424
458
  },
459
+ remove(ids) {
460
+ removeNodes({
461
+ map,
462
+ tree,
463
+ roots,
464
+ keyField,
465
+ parentCache,
466
+ childrenCache,
467
+ expandedKeys,
468
+ notify: emitVisibility
469
+ }, ids);
470
+ },
471
+ updateOriginal(id, patch) {
472
+ const node = map.get(id);
473
+ if (!node) return false;
474
+ if (Object.prototype.hasOwnProperty.call(patch, keyField)) return false;
475
+ if (Object.prototype.hasOwnProperty.call(patch, parentField)) {
476
+ const nextParent = patch[parentField];
477
+ if (nextParent !== node.getParent()?.id) tree.moveNode(id, nextParent);
478
+ delete patch[parentField];
479
+ }
480
+ Object.assign(node.original, patch);
481
+ emitVisibility();
482
+ return true;
483
+ },
425
484
  syncData(next) {
426
485
  tree.data = next;
427
486
  const built = buildTree(next, keyField, parentField, expandedKeys);
@@ -436,13 +495,21 @@ function createSpliceTree(data, options = {}) {
436
495
  });
437
496
  for (const id of toDelete) expandedKeys.delete(id);
438
497
  applyNodeExtensions();
498
+ pluginCtx.roots = roots;
499
+ pluginCtx.map = map;
500
+ pluginCtx.parentCache = parentCache;
501
+ pluginCtx.childrenCache = childrenCache;
439
502
  emitVisibility();
440
503
  }
441
504
  };
442
- const pluginCtx = {
505
+ pluginCtx = {
443
506
  tree,
444
507
  options,
445
- events
508
+ events,
509
+ roots,
510
+ map,
511
+ parentCache,
512
+ childrenCache
446
513
  };
447
514
  options?.plugins?.forEach((plugin) => {
448
515
  const api = plugin.setup?.(pluginCtx);
@@ -457,6 +524,9 @@ function createSpliceTree(data, options = {}) {
457
524
  else if (expand) tree.expand(node.id);
458
525
  else tree.collapse(node.id);
459
526
  };
527
+ node.updateOriginal = (patch) => {
528
+ tree.updateOriginal(node.id, patch);
529
+ };
460
530
  }
461
531
  }
462
532
  applyNodeExtensions();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@splicetree/core",
3
3
  "type": "module",
4
- "version": "3.0.1",
4
+ "version": "3.1.0",
5
5
  "author": {
6
6
  "email": "michael.cocova@gmail.com",
7
7
  "name": "Michael Cocova"