react-arborist 3.10.3 → 3.10.5
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/main/data/simple-tree.d.ts +14 -8
- package/dist/main/data/simple-tree.js +34 -15
- package/dist/main/data/simple-tree.test.d.ts +1 -0
- package/dist/main/data/simple-tree.test.js +63 -0
- package/dist/main/hooks/use-simple-tree.d.ts +2 -1
- package/dist/main/hooks/use-simple-tree.js +19 -4
- package/dist/main/hooks/use-simple-tree.test.d.ts +1 -0
- package/dist/main/hooks/use-simple-tree.test.js +32 -0
- package/dist/main/hooks/use-validated-props.js +4 -1
- package/dist/main/interfaces/tree-api.d.ts +33 -20
- package/dist/main/interfaces/tree-api.js +48 -23
- package/dist/main/interfaces/tree-api.test.js +48 -0
- package/dist/main/types/handlers.d.ts +1 -1
- package/dist/module/data/simple-tree.d.ts +14 -8
- package/dist/module/data/simple-tree.js +34 -15
- package/dist/module/data/simple-tree.test.d.ts +1 -0
- package/dist/module/data/simple-tree.test.js +61 -0
- package/dist/module/hooks/use-simple-tree.d.ts +2 -1
- package/dist/module/hooks/use-simple-tree.js +19 -4
- package/dist/module/hooks/use-simple-tree.test.d.ts +1 -0
- package/dist/module/hooks/use-simple-tree.test.js +30 -0
- package/dist/module/hooks/use-validated-props.js +4 -1
- package/dist/module/interfaces/tree-api.d.ts +33 -20
- package/dist/module/interfaces/tree-api.js +48 -23
- package/dist/module/interfaces/tree-api.test.js +48 -0
- package/dist/module/types/handlers.d.ts +1 -1
- package/package.json +1 -1
- package/src/data/simple-tree.test.ts +68 -0
- package/src/data/simple-tree.ts +53 -16
- package/src/hooks/use-simple-tree.test.ts +39 -0
- package/src/hooks/use-simple-tree.ts +26 -8
- package/src/hooks/use-validated-props.ts +4 -1
- package/src/interfaces/tree-api.test.ts +46 -0
- package/src/interfaces/tree-api.ts +68 -41
- package/src/types/handlers.ts +3 -1
|
@@ -22,7 +22,7 @@ import { Store } from "redux";
|
|
|
22
22
|
import { createList } from "../data/create-list";
|
|
23
23
|
import { createIndex } from "../data/create-index";
|
|
24
24
|
|
|
25
|
-
const { safeRun
|
|
25
|
+
const { safeRun } = utils;
|
|
26
26
|
export class TreeApi<T> {
|
|
27
27
|
static editPromise: null | ((args: EditResult) => void);
|
|
28
28
|
root: NodeApi<T>;
|
|
@@ -187,6 +187,29 @@ export class TreeApi<T> {
|
|
|
187
187
|
return id;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Resolve an identifier to a node id. Public methods accept an id string, a
|
|
192
|
+
* NodeApi, or the raw row data; this is the one place that turns any of those
|
|
193
|
+
* into the string id used internally. Raw data is run through the configured
|
|
194
|
+
* `idAccessor` so a custom accessor (e.g. `uuid`) is honored everywhere, not
|
|
195
|
+
* just where nodes were built. A NodeApi already carries its accessor-derived
|
|
196
|
+
* `id`, so it is used directly rather than re-accessed (the accessor reads the
|
|
197
|
+
* underlying data, which a NodeApi does not expose under that key). Unlike
|
|
198
|
+
* `accessId`, an unresolved id comes back as `undefined` rather than throwing,
|
|
199
|
+
* preserving the previous behavior of the `id`-only lookup.
|
|
200
|
+
*/
|
|
201
|
+
identify(identity: string | IdObj | T): string {
|
|
202
|
+
if (typeof identity === "string") return identity;
|
|
203
|
+
if (identity instanceof NodeApi) return identity.id;
|
|
204
|
+
const get = this.props.idAccessor || "id";
|
|
205
|
+
return utils.access<string>(identity, get);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
identifyNull(identity: Identity | T): string | null {
|
|
209
|
+
if (identity === null || identity === undefined) return null;
|
|
210
|
+
return this.identify(identity);
|
|
211
|
+
}
|
|
212
|
+
|
|
190
213
|
/* Node Access */
|
|
191
214
|
|
|
192
215
|
get firstNode() {
|
|
@@ -237,8 +260,8 @@ export class TreeApi<T> {
|
|
|
237
260
|
return this.visibleNodes.slice(start, end + 1);
|
|
238
261
|
}
|
|
239
262
|
|
|
240
|
-
indexOf(id: Identity) {
|
|
241
|
-
const key =
|
|
263
|
+
indexOf(id: Identity | T) {
|
|
264
|
+
const key = this.identifyNull(id);
|
|
242
265
|
if (!key) return null;
|
|
243
266
|
return this.idToIndex[key];
|
|
244
267
|
}
|
|
@@ -284,10 +307,10 @@ export class TreeApi<T> {
|
|
|
284
307
|
}
|
|
285
308
|
}
|
|
286
309
|
|
|
287
|
-
async delete(node: Identity | string
|
|
310
|
+
async delete(node: Identity | T | (string | IdObj | T)[]) {
|
|
288
311
|
if (!node) return;
|
|
289
312
|
const idents = Array.isArray(node) ? node : [node];
|
|
290
|
-
const ids = idents.map(identify);
|
|
313
|
+
const ids = idents.map((i) => this.identify(i));
|
|
291
314
|
const nodes = ids.map((id) => this.get(id)!).filter((n) => !!n);
|
|
292
315
|
/* Guard against Math.min(...[]) === Infinity when no ids resolve to nodes. */
|
|
293
316
|
const fromIndex = nodes.length ? Math.min(...nodes.map((n) => n.rowIndex ?? 0)) : 0;
|
|
@@ -295,8 +318,8 @@ export class TreeApi<T> {
|
|
|
295
318
|
this.redrawList(fromIndex);
|
|
296
319
|
}
|
|
297
320
|
|
|
298
|
-
edit(node: string | IdObj): Promise<EditResult> {
|
|
299
|
-
const id = identify(node);
|
|
321
|
+
edit(node: string | IdObj | T): Promise<EditResult> {
|
|
322
|
+
const id = this.identify(node);
|
|
300
323
|
this.resolveEdit({ cancelled: true });
|
|
301
324
|
this.scrollTo(id);
|
|
302
325
|
this.dispatch(edit(id));
|
|
@@ -306,9 +329,9 @@ export class TreeApi<T> {
|
|
|
306
329
|
});
|
|
307
330
|
}
|
|
308
331
|
|
|
309
|
-
async submit(identity: Identity, value: string) {
|
|
332
|
+
async submit(identity: Identity | T, value: string) {
|
|
310
333
|
if (!identity) return;
|
|
311
|
-
const id = identify(identity);
|
|
334
|
+
const id = this.identify(identity);
|
|
312
335
|
await safeRun(this.props.onRename, {
|
|
313
336
|
id,
|
|
314
337
|
name: value,
|
|
@@ -327,8 +350,8 @@ export class TreeApi<T> {
|
|
|
327
350
|
setTimeout(() => this.onFocus()); // Return focus to element;
|
|
328
351
|
}
|
|
329
352
|
|
|
330
|
-
activate(id: Identity) {
|
|
331
|
-
const node = this.get(identifyNull(id));
|
|
353
|
+
activate(id: Identity | T) {
|
|
354
|
+
const node = this.get(this.identifyNull(id));
|
|
332
355
|
if (!node) return;
|
|
333
356
|
safeRun(this.props.onActivate, node);
|
|
334
357
|
}
|
|
@@ -354,7 +377,7 @@ export class TreeApi<T> {
|
|
|
354
377
|
return nodes;
|
|
355
378
|
}
|
|
356
379
|
|
|
357
|
-
focus(node: Identity, opts: { scroll?: boolean } = {}) {
|
|
380
|
+
focus(node: Identity | T, opts: { scroll?: boolean } = {}) {
|
|
358
381
|
if (!node) return;
|
|
359
382
|
/* Focus is responsible for scrolling, while selection is
|
|
360
383
|
* responsible for focus. If selectionFollowsFocus, then
|
|
@@ -362,7 +385,7 @@ export class TreeApi<T> {
|
|
|
362
385
|
if (this.props.selectionFollowsFocus) {
|
|
363
386
|
this.select(node);
|
|
364
387
|
} else {
|
|
365
|
-
this.dispatch(focus(identify(node)));
|
|
388
|
+
this.dispatch(focus(this.identify(node)));
|
|
366
389
|
if (opts.scroll !== false) this.scrollTo(node);
|
|
367
390
|
if (this.focusedNode) safeRun(this.props.onFocus, this.focusedNode);
|
|
368
391
|
}
|
|
@@ -394,10 +417,10 @@ export class TreeApi<T> {
|
|
|
394
417
|
this.focus(this.at(index));
|
|
395
418
|
}
|
|
396
419
|
|
|
397
|
-
select(node: Identity, opts: { align?: Align; focus?: boolean } = {}) {
|
|
420
|
+
select(node: Identity | T, opts: { align?: Align; focus?: boolean } = {}) {
|
|
398
421
|
if (!node) return;
|
|
399
422
|
const changeFocus = opts.focus !== false;
|
|
400
|
-
const id = identify(node);
|
|
423
|
+
const id = this.identify(node);
|
|
401
424
|
if (changeFocus) this.dispatch(focus(id));
|
|
402
425
|
if (this.get(id)?.isSelectable) {
|
|
403
426
|
this.setSelection({
|
|
@@ -412,15 +435,15 @@ export class TreeApi<T> {
|
|
|
412
435
|
}
|
|
413
436
|
}
|
|
414
437
|
|
|
415
|
-
deselect(node: Identity) {
|
|
438
|
+
deselect(node: Identity | T) {
|
|
416
439
|
if (!node) return;
|
|
417
|
-
const id = identify(node);
|
|
440
|
+
const id = this.identify(node);
|
|
418
441
|
this.dispatch(selection.remove(id));
|
|
419
442
|
safeRun(this.props.onSelect, this.selectedNodes);
|
|
420
443
|
}
|
|
421
444
|
|
|
422
|
-
selectMulti(identity: Identity, opts: { align?: Align; focus?: boolean } = {}) {
|
|
423
|
-
const node = this.get(identifyNull(identity));
|
|
445
|
+
selectMulti(identity: Identity | T, opts: { align?: Align; focus?: boolean } = {}) {
|
|
446
|
+
const node = this.get(this.identifyNull(identity));
|
|
424
447
|
if (!node) return;
|
|
425
448
|
const changeFocus = opts.focus !== false;
|
|
426
449
|
if (changeFocus) this.dispatch(focus(node.id));
|
|
@@ -436,14 +459,14 @@ export class TreeApi<T> {
|
|
|
436
459
|
safeRun(this.props.onSelect, this.selectedNodes);
|
|
437
460
|
}
|
|
438
461
|
|
|
439
|
-
selectContiguous(identity: Identity) {
|
|
462
|
+
selectContiguous(identity: Identity | T) {
|
|
440
463
|
if (!identity) return;
|
|
441
|
-
const id = identify(identity);
|
|
464
|
+
const id = this.identify(identity);
|
|
442
465
|
this.dispatch(focus(id));
|
|
443
466
|
if (this.get(id)?.isSelectable) {
|
|
444
467
|
const { anchor, mostRecent } = this.state.nodes.selection;
|
|
445
468
|
const selectableNodes = this.filterSelectableNodes(
|
|
446
|
-
this.nodesBetween(anchor, identifyNull(id)),
|
|
469
|
+
this.nodesBetween(anchor, this.identifyNull(id)),
|
|
447
470
|
);
|
|
448
471
|
this.dispatch(selection.remove(this.nodesBetween(anchor, mostRecent)));
|
|
449
472
|
this.dispatch(selection.add(selectableNodes));
|
|
@@ -473,14 +496,18 @@ export class TreeApi<T> {
|
|
|
473
496
|
|
|
474
497
|
private filterSelectableNodes(nodes: (IdObj | string)[]) {
|
|
475
498
|
return nodes
|
|
476
|
-
.map((n) => this.get(identify(n)))
|
|
499
|
+
.map((n) => this.get(this.identify(n)))
|
|
477
500
|
.filter((n): n is NodeApi<T> => !!n && n.isSelectable);
|
|
478
501
|
}
|
|
479
502
|
|
|
480
|
-
setSelection(args: {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
503
|
+
setSelection(args: {
|
|
504
|
+
ids: (IdObj | string | T)[] | null;
|
|
505
|
+
anchor: Identity | T;
|
|
506
|
+
mostRecent: Identity | T;
|
|
507
|
+
}) {
|
|
508
|
+
const ids = new Set(args.ids?.map((i) => this.identify(i)));
|
|
509
|
+
const anchor = this.identifyNull(args.anchor);
|
|
510
|
+
const mostRecent = this.identifyNull(args.mostRecent);
|
|
484
511
|
this.dispatch(selection.set({ ids, anchor, mostRecent }));
|
|
485
512
|
safeRun(this.props.onSelect, this.selectedNodes);
|
|
486
513
|
}
|
|
@@ -568,8 +595,8 @@ export class TreeApi<T> {
|
|
|
568
595
|
|
|
569
596
|
/* Visibility */
|
|
570
597
|
|
|
571
|
-
open(identity: Identity, redraw: boolean = true) {
|
|
572
|
-
const id = identifyNull(identity);
|
|
598
|
+
open(identity: Identity | T, redraw: boolean = true) {
|
|
599
|
+
const id = this.identifyNull(identity);
|
|
573
600
|
if (!id) return;
|
|
574
601
|
if (this.isOpen(id)) return;
|
|
575
602
|
this.dispatch(visibility.open(id, this.isFiltered));
|
|
@@ -577,8 +604,8 @@ export class TreeApi<T> {
|
|
|
577
604
|
safeRun(this.props.onToggle, id);
|
|
578
605
|
}
|
|
579
606
|
|
|
580
|
-
close(identity: Identity, redraw: boolean = true) {
|
|
581
|
-
const id = identifyNull(identity);
|
|
607
|
+
close(identity: Identity | T, redraw: boolean = true) {
|
|
608
|
+
const id = this.identifyNull(identity);
|
|
582
609
|
if (!id) return;
|
|
583
610
|
if (!this.isOpen(id)) return;
|
|
584
611
|
this.dispatch(visibility.close(id, this.isFiltered));
|
|
@@ -586,14 +613,14 @@ export class TreeApi<T> {
|
|
|
586
613
|
safeRun(this.props.onToggle, id);
|
|
587
614
|
}
|
|
588
615
|
|
|
589
|
-
toggle(identity: Identity) {
|
|
590
|
-
const id = identifyNull(identity);
|
|
616
|
+
toggle(identity: Identity | T) {
|
|
617
|
+
const id = this.identifyNull(identity);
|
|
591
618
|
if (!id) return;
|
|
592
619
|
return this.isOpen(id) ? this.close(id) : this.open(id);
|
|
593
620
|
}
|
|
594
621
|
|
|
595
|
-
openParents(identity: Identity) {
|
|
596
|
-
const id = identifyNull(identity);
|
|
622
|
+
openParents(identity: Identity | T) {
|
|
623
|
+
const id = this.identifyNull(identity);
|
|
597
624
|
if (!id) return;
|
|
598
625
|
const node = utils.dfs(this.root, id);
|
|
599
626
|
let parent = node?.parent;
|
|
@@ -638,9 +665,9 @@ export class TreeApi<T> {
|
|
|
638
665
|
|
|
639
666
|
/* Scrolling */
|
|
640
667
|
|
|
641
|
-
scrollTo(identity: Identity, align: Align = "smart") {
|
|
668
|
+
scrollTo(identity: Identity | T, align: Align = "smart") {
|
|
642
669
|
if (!identity) return;
|
|
643
|
-
const id = identify(identity);
|
|
670
|
+
const id = this.identify(identity);
|
|
644
671
|
this.openParents(id);
|
|
645
672
|
return utils
|
|
646
673
|
.waitFor(() => id in this.idToIndex)
|
|
@@ -712,8 +739,8 @@ export class TreeApi<T> {
|
|
|
712
739
|
return !utils.access(data, disabler);
|
|
713
740
|
}
|
|
714
741
|
|
|
715
|
-
isDragging(node: Identity) {
|
|
716
|
-
const id = identifyNull(node);
|
|
742
|
+
isDragging(node: Identity | T) {
|
|
743
|
+
const id = this.identifyNull(node);
|
|
717
744
|
if (!id) return false;
|
|
718
745
|
return this.state.nodes.drag.id === id;
|
|
719
746
|
}
|
|
@@ -726,8 +753,8 @@ export class TreeApi<T> {
|
|
|
726
753
|
return this.matchFn(node);
|
|
727
754
|
}
|
|
728
755
|
|
|
729
|
-
willReceiveDrop(node: Identity) {
|
|
730
|
-
const id = identifyNull(node);
|
|
756
|
+
willReceiveDrop(node: Identity | T) {
|
|
757
|
+
const id = this.identifyNull(node);
|
|
731
758
|
if (!id) return false;
|
|
732
759
|
const { destinationParentId, destinationIndex } = this.state.nodes.drag;
|
|
733
760
|
return id === destinationParentId && destinationIndex === null;
|
package/src/types/handlers.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { NodeApi } from "../interfaces/node-api";
|
|
2
2
|
import { IdObj } from "./utils";
|
|
3
3
|
|
|
4
|
+
// Returns the newly created row data, whose id is read via idAccessor. `IdObj`
|
|
5
|
+
// is kept for back-compat with handlers that return a bare `{ id }` (#347).
|
|
4
6
|
export type CreateHandler<T> = (args: {
|
|
5
7
|
parentId: string | null;
|
|
6
8
|
parentNode: NodeApi<T> | null;
|
|
7
9
|
index: number;
|
|
8
10
|
type: "internal" | "leaf";
|
|
9
|
-
}) => (IdObj | null) | Promise<IdObj | null>;
|
|
11
|
+
}) => (T | IdObj | null) | Promise<T | IdObj | null>;
|
|
10
12
|
|
|
11
13
|
export type MoveHandler<T> = (args: {
|
|
12
14
|
dragIds: string[];
|