@limble/limble-tree 1.0.0-alpha.1 → 1.0.0-alpha.2

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 (57) hide show
  1. package/README.md +446 -17
  2. package/esm2020/lib/components/branch/branch.component.mjs +6 -14
  3. package/esm2020/lib/components/root/root.component.mjs +3 -4
  4. package/esm2020/lib/core/branch-options.interface.mjs +1 -1
  5. package/esm2020/lib/core/configuration/tree-options.interface.mjs +1 -1
  6. package/esm2020/lib/core/relationship.interface.mjs +1 -1
  7. package/esm2020/lib/core/tree-branch/branch-controller.mjs +99 -0
  8. package/esm2020/lib/core/tree-branch/tree-branch.mjs +81 -103
  9. package/esm2020/lib/core/tree-node-base.mjs +10 -9
  10. package/esm2020/lib/core/tree-root/root-controller.mjs +42 -0
  11. package/esm2020/lib/core/tree-root/tree-root.mjs +43 -27
  12. package/esm2020/lib/events/drag/drag-end-event.mjs +1 -1
  13. package/esm2020/lib/events/drag/drop-event.mjs +1 -1
  14. package/esm2020/lib/events/general/destruction-event.mjs +12 -0
  15. package/esm2020/lib/events/general/index.mjs +2 -0
  16. package/esm2020/lib/events/index.mjs +2 -1
  17. package/esm2020/lib/extras/collapse/collapse.mjs +2 -2
  18. package/esm2020/lib/extras/drag-and-drop/drag-and-drop.mjs +6 -10
  19. package/esm2020/lib/extras/drag-and-drop/draggable.directive.mjs +1 -1
  20. package/esm2020/lib/extras/drag-and-drop/dropzone-renderer.mjs +9 -7
  21. package/esm2020/lib/legacy/legacy-tree.mjs +28 -29
  22. package/esm2020/lib/structure/branchable.interface.mjs +1 -1
  23. package/esm2020/lib/structure/component-container.interface.mjs +2 -0
  24. package/esm2020/lib/structure/index.mjs +2 -4
  25. package/esm2020/lib/structure/tree-branch-node.interface.mjs +1 -1
  26. package/esm2020/lib/structure/tree-node.interface.mjs +1 -1
  27. package/fesm2015/limble-limble-tree.mjs +317 -193
  28. package/fesm2015/limble-limble-tree.mjs.map +1 -1
  29. package/fesm2020/limble-limble-tree.mjs +315 -189
  30. package/fesm2020/limble-limble-tree.mjs.map +1 -1
  31. package/lib/core/branch-options.interface.d.ts +1 -1
  32. package/lib/core/configuration/tree-options.interface.d.ts +23 -26
  33. package/lib/core/relationship.interface.d.ts +2 -3
  34. package/lib/core/tree-branch/branch-controller.d.ts +25 -0
  35. package/lib/core/tree-branch/tree-branch.d.ts +18 -18
  36. package/lib/core/tree-node-base.d.ts +4 -5
  37. package/lib/core/tree-root/root-controller.d.ts +19 -0
  38. package/lib/core/tree-root/tree-root.d.ts +11 -9
  39. package/lib/events/drag/drag-end-event.d.ts +5 -6
  40. package/lib/events/drag/drop-event.d.ts +3 -6
  41. package/lib/events/general/destruction-event.d.ts +8 -0
  42. package/lib/events/general/index.d.ts +1 -0
  43. package/lib/events/index.d.ts +1 -0
  44. package/lib/extras/drag-and-drop/drag-and-drop.d.ts +2 -3
  45. package/lib/extras/drag-and-drop/draggable.directive.d.ts +1 -1
  46. package/lib/structure/branchable.interface.d.ts +0 -1
  47. package/lib/structure/component-container.interface.d.ts +8 -0
  48. package/lib/structure/index.d.ts +1 -3
  49. package/lib/structure/tree-branch-node.interface.d.ts +2 -2
  50. package/lib/structure/tree-node.interface.d.ts +3 -1
  51. package/package.json +1 -1
  52. package/esm2020/lib/structure/container-tree-node.interface.mjs +0 -2
  53. package/esm2020/lib/structure/content-container.interface.mjs +0 -2
  54. package/esm2020/lib/structure/tree-root.node.interface.mjs +0 -2
  55. package/lib/structure/container-tree-node.interface.d.ts +0 -3
  56. package/lib/structure/content-container.interface.d.ts +0 -3
  57. package/lib/structure/tree-root.node.interface.d.ts +0 -2
@@ -27,7 +27,7 @@ class TreeCollapser {
27
27
  branch.graftTo(treeBranch);
28
28
  });
29
29
  this.tempStorage.delete(treeBranch);
30
- treeBranch.getContents().changeDetectorRef.detectChanges();
30
+ treeBranch.detectChanges();
31
31
  }
32
32
  isCollapsed(treeBranch) {
33
33
  return this.tempStorage.has(treeBranch);
@@ -235,19 +235,15 @@ class DragAndDrop {
235
235
  }
236
236
  setDragEffects(treeBranch, event) {
237
237
  const dataTransfer = event.dataTransfer;
238
- if (!(dataTransfer instanceof DataTransfer)) {
239
- throw new Error("bad drag event");
240
- }
241
- const nativeElement = treeBranch.getContents().location.nativeElement;
238
+ assert(dataTransfer instanceof DataTransfer);
239
+ const nativeElement = treeBranch.getNativeElement();
242
240
  const [xOffset, yOffset] = this.getDragImageOffsets(event, nativeElement);
243
241
  dataTransfer.setDragImage(nativeElement, xOffset, yOffset);
244
242
  }
245
243
  watchForDragend(treeBranch, event) {
246
244
  const oldParent = treeBranch.parent();
247
245
  const oldIndex = treeBranch.index();
248
- if (oldParent === undefined || oldIndex === undefined) {
249
- throw new Error("branch must have a parent");
250
- }
246
+ assert(oldParent !== undefined && oldIndex !== undefined);
251
247
  event.target?.addEventListener("dragend", (dragend) => {
252
248
  if (dragState.state() !== DragStates.Dropped) {
253
249
  //The drag ended but a drop never occurred, so put the dragged branch back where it started.
@@ -268,13 +264,13 @@ class DragAndDrop {
268
264
  }, { once: true });
269
265
  }
270
266
  draggingAllowed(treeBranch) {
271
- const allowDragging = config.getConfig(treeBranch.root())?.allowDragging ??
267
+ const allowDragging = config.getConfig(treeBranch.root())?.dragAndDrop?.allowDragging ??
272
268
  (() => true);
273
269
  return allowDragging(treeBranch);
274
270
  }
275
271
  graftDraggedBranch(treeBranch, parent, index) {
276
272
  treeBranch.graftTo(parent, index);
277
- treeBranch.getContents().location.nativeElement.style.display = "block";
273
+ treeBranch.getNativeElement().style.display = "block";
278
274
  dragState.dropped();
279
275
  }
280
276
  }
@@ -359,22 +355,14 @@ class BranchComponent {
359
355
  this.showLateralDropzone = false;
360
356
  }
361
357
  ngAfterViewInit() {
362
- if (this.contentContainer === undefined) {
363
- throw new TreeError("Cannot get contentContainer");
364
- }
365
- if (this.contentToHost === undefined) {
366
- throw new TreeError("'content' is a required input");
367
- }
358
+ assert(this.contentContainer !== undefined);
359
+ assert(this.contentToHost !== undefined);
368
360
  this.hostedContent = this.contentContainer.createComponent(this.contentToHost);
369
361
  this.contentCreated.emit(this.hostedContent.instance);
370
- if (this.dropzones === undefined) {
371
- throw new Error("querylist not defined");
372
- }
362
+ assert(this.dropzones !== undefined);
373
363
  const inner = this.dropzones.get(0);
374
364
  const lateral = this.dropzones.get(1);
375
- if (inner === undefined || lateral == undefined) {
376
- throw new Error("dropzones not defined");
377
- }
365
+ assert(inner !== undefined && lateral !== undefined);
378
366
  merge(inner.dropped.pipe(map(() => "inner")), lateral.dropped.pipe(map(() => "lateral"))).subscribe(this.dropped);
379
367
  this.hostedContent.changeDetectorRef.detectChanges();
380
368
  }
@@ -476,6 +464,7 @@ class PruneEvent {
476
464
 
477
465
  class TreeNodeBase {
478
466
  constructor() {
467
+ this.destroyed = false;
479
468
  this._branches = [];
480
469
  this.events$ = new Subject();
481
470
  this.subscriptions = [
@@ -490,17 +479,14 @@ class TreeNodeBase {
490
479
  branches() {
491
480
  return [...this._branches];
492
481
  }
493
- deleteBranch(index) {
494
- if (index === undefined) {
495
- this._branches.pop();
496
- return;
497
- }
498
- this._branches.splice(index, 1);
499
- }
500
482
  destroy() {
483
+ this.branches().forEach((branch) => {
484
+ branch.destroy();
485
+ });
501
486
  this.subscriptions.forEach((sub) => {
502
487
  sub.unsubscribe();
503
488
  });
489
+ this.destroyed = true;
504
490
  }
505
491
  dispatch(event) {
506
492
  this.events$.next(event);
@@ -511,6 +497,9 @@ class TreeNodeBase {
511
497
  getBranch(index) {
512
498
  return this._branches[index];
513
499
  }
500
+ isDestroyed() {
501
+ return this.destroyed;
502
+ }
514
503
  plot() {
515
504
  return new Map(this.branches().map((branch, index) => [index, branch.plot()]));
516
505
  }
@@ -521,7 +510,7 @@ class TreeNodeBase {
521
510
  }
522
511
  deregisterChildRelationship(child) {
523
512
  const index = this.branches().findIndex((branch) => branch === child);
524
- this.deleteBranch(index);
513
+ this._branches.splice(index, 1);
525
514
  }
526
515
  graftsToSelf() {
527
516
  return this.events().pipe(filter((event) => event instanceof GraftEvent), filter((event) => event.parent().events() === this.events$));
@@ -538,6 +527,18 @@ class TreeNodeBase {
538
527
  }
539
528
  }
540
529
 
530
+ class DestructionEvent {
531
+ constructor(source) {
532
+ this._source = source;
533
+ }
534
+ type() {
535
+ return "destruction";
536
+ }
537
+ source() {
538
+ return this._source;
539
+ }
540
+ }
541
+
541
542
  class RootComponent {
542
543
  constructor() {
543
544
  this.branchesContainer = undefined;
@@ -548,9 +549,7 @@ class RootComponent {
548
549
  }
549
550
  ngAfterViewInit() {
550
551
  this.afterViewInit.emit();
551
- if (this.dropzone === undefined) {
552
- throw new Error("dropzone is not defined");
553
- }
552
+ assert(this.dropzone !== undefined);
554
553
  this.dropzone.dropped.subscribe(this.dropped);
555
554
  }
556
555
  }
@@ -571,42 +570,68 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
571
570
  type: Output
572
571
  }] } });
573
572
 
574
- class TreeRoot {
575
- constructor(viewContainerRef) {
576
- this.viewContainerRef = viewContainerRef;
577
- this.treeNodeBase = new TreeNodeBase();
578
- this.rootComponentRef =
579
- this.viewContainerRef.createComponent(RootComponent);
573
+ /**
574
+ * A wrapper around the BranchComponent that helps instantiate it and handles its events.
575
+ */
576
+ class RootController {
577
+ constructor(treeRoot, viewContainerRef) {
578
+ this.treeRoot = treeRoot;
579
+ this.rootComponentRef = viewContainerRef.createComponent(RootComponent);
580
580
  const viewInitSub = this.rootComponentRef.instance.afterViewInit.subscribe(() => {
581
581
  const dropzone = this.rootComponentRef.instance.dropzone;
582
- if (!dropzone) {
583
- throw new Error("dropzone not defined");
584
- }
585
- dropzoneRenderer.registerDropzone(dropzone, this);
582
+ assert(dropzone !== undefined);
583
+ dropzoneRenderer.registerDropzone(dropzone, this.treeRoot);
586
584
  });
587
585
  const droppedSub = this.rootComponentRef.instance.dropped.subscribe(() => {
588
- dropzoneRenderer.handleDrop(this, "inner");
586
+ dropzoneRenderer.handleDrop(this.treeRoot, "inner");
589
587
  });
590
588
  this.instanceSubscriptions = [viewInitSub, droppedSub];
589
+ }
590
+ destroy() {
591
+ this.instanceSubscriptions.forEach((sub) => {
592
+ sub.unsubscribe();
593
+ });
594
+ }
595
+ detectChanges() {
591
596
  this.rootComponentRef.changeDetectorRef.detectChanges();
592
597
  }
598
+ getBranchesContainer() {
599
+ return this.rootComponentRef.instance.branchesContainer;
600
+ }
601
+ getComponentInstance() {
602
+ return this.rootComponentRef.instance;
603
+ }
604
+ getHostView() {
605
+ return this.rootComponentRef.hostView;
606
+ }
607
+ getNativeElement() {
608
+ return this.rootComponentRef.location.nativeElement;
609
+ }
610
+ }
611
+
612
+ class TreeRoot {
613
+ constructor(viewContainerRef) {
614
+ this.viewContainerRef = viewContainerRef;
615
+ this.treeNodeBase = new TreeNodeBase();
616
+ this.rootController = new RootController(this, viewContainerRef);
617
+ this.detectChanges();
618
+ }
593
619
  branches() {
594
620
  return this.treeNodeBase.branches();
595
621
  }
596
- deleteBranch(index) {
597
- this.treeNodeBase.deleteBranch(index);
598
- }
599
622
  destroy() {
623
+ if (this.isDestroyed()) {
624
+ throw new TreeError("Cannot destroy a destroyed tree root");
625
+ }
600
626
  dropzoneRenderer.clearTreeFromRegistry(this);
601
- this.branches().forEach((branch) => {
602
- branch.destroy();
603
- });
604
627
  this.treeNodeBase.destroy();
605
- this.instanceSubscriptions.forEach((sub) => {
606
- sub.unsubscribe();
607
- });
628
+ this.rootController.destroy();
608
629
  this.viewContainerRef.clear();
609
630
  config.delete(this);
631
+ this.dispatch(new DestructionEvent(this));
632
+ }
633
+ detectChanges() {
634
+ this.rootController.detectChanges();
610
635
  }
611
636
  dispatch(event) {
612
637
  this.treeNodeBase.dispatch(event);
@@ -617,12 +642,39 @@ class TreeRoot {
617
642
  getBranch(index) {
618
643
  return this.treeNodeBase.getBranch(index);
619
644
  }
620
- getContents() {
621
- return this.rootComponentRef;
645
+ getBranchesContainer() {
646
+ if (this.isDestroyed()) {
647
+ throw new TreeError("Cannot get branches container from a destroyed tree root");
648
+ }
649
+ return this.rootController.getBranchesContainer();
650
+ }
651
+ getComponentInstance() {
652
+ if (this.isDestroyed()) {
653
+ throw new TreeError("Cannot get component instance from a destroyed tree root");
654
+ }
655
+ return this.rootController.getComponentInstance();
656
+ }
657
+ getHostView() {
658
+ if (this.isDestroyed()) {
659
+ throw new TreeError("Cannot get component host view from a destroyed tree root");
660
+ }
661
+ return this.rootController.getHostView();
662
+ }
663
+ getNativeElement() {
664
+ if (this.isDestroyed()) {
665
+ throw new TreeError("Cannot get native element from a destroyed tree root");
666
+ }
667
+ return this.rootController.getNativeElement();
622
668
  }
623
669
  grow(component, options) {
670
+ if (this.isDestroyed()) {
671
+ throw new TreeError("Cannot grow a branch on a destroyed tree root");
672
+ }
624
673
  return new TreeBranch(this, { component, ...options });
625
674
  }
675
+ isDestroyed() {
676
+ return this.treeNodeBase.isDestroyed();
677
+ }
626
678
  plot() {
627
679
  return this.treeNodeBase.plot();
628
680
  }
@@ -650,29 +702,117 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
650
702
  args: [{ providedIn: "root" }]
651
703
  }] });
652
704
 
705
+ /**
706
+ * A wrapper around the BranchComponent that helps instantiate it and handles its events.
707
+ */
708
+ class BranchController {
709
+ constructor(treeBranch, parentBranchesContainer) {
710
+ this.treeBranch = treeBranch;
711
+ this.outputBindingSubscriptions = [];
712
+ this.branchComponentRef = createComponent(BranchComponent, {
713
+ environmentInjector: parentBranchesContainer.injector.get(EnvironmentInjector)
714
+ });
715
+ this.branchComponentRef.instance.contentToHost =
716
+ this.treeBranch.branchOptions.component;
717
+ this.instanceSubscriptions = this.getInstanceSubscriptions(this.branchComponentRef.instance);
718
+ }
719
+ destroy() {
720
+ this.instanceSubscriptions.forEach((sub) => {
721
+ sub.unsubscribe();
722
+ });
723
+ this.outputBindingSubscriptions.forEach((sub) => {
724
+ sub.unsubscribe();
725
+ });
726
+ }
727
+ detectChanges() {
728
+ this.branchComponentRef.changeDetectorRef.detectChanges();
729
+ }
730
+ getBranchesContainer() {
731
+ return this.branchComponentRef.instance.branchesContainer;
732
+ }
733
+ getComponentInstance() {
734
+ return this.branchComponentRef.instance;
735
+ }
736
+ getHostView() {
737
+ return this.branchComponentRef.hostView;
738
+ }
739
+ getNativeElement() {
740
+ return this.branchComponentRef.location.nativeElement;
741
+ }
742
+ getUserlandComponentRef() {
743
+ return this.branchComponentRef.instance.getHostedContent();
744
+ }
745
+ getContentCreatedSub(instance) {
746
+ return instance.contentCreated.subscribe((userlandComponentInstance) => {
747
+ const component = userlandComponentInstance;
748
+ Object.entries(this.treeBranch.branchOptions.inputBindings ?? {}).forEach(([key, value]) => {
749
+ component[key] = value;
750
+ });
751
+ Object.entries(this.treeBranch.branchOptions.outputBindings ?? {}).forEach(([key, value]) => {
752
+ this.outputBindingSubscriptions.push(component[key].subscribe(value));
753
+ });
754
+ component.treeBranch = this.treeBranch;
755
+ const dropzones = instance.dropzones;
756
+ assert(dropzones !== undefined);
757
+ dropzoneRenderer.registerDropzones(dropzones, this.treeBranch);
758
+ });
759
+ }
760
+ getInstanceSubscriptions(instance) {
761
+ const droppedSub = instance.dropped.subscribe((placement) => {
762
+ dropzoneRenderer.handleDrop(this.treeBranch, placement);
763
+ });
764
+ return [
765
+ this.getContentCreatedSub(instance),
766
+ this.getShowLowerZonesSub(instance),
767
+ this.getShowUpperZonesSub(instance),
768
+ droppedSub
769
+ ];
770
+ }
771
+ getShowLowerZonesSub(instance) {
772
+ return instance.showDropzones
773
+ .pipe(filter((direction) => direction === "lower"))
774
+ .subscribe(() => {
775
+ const currentDropzoneDisplayed = dropzoneRenderer.getCurrentDisplay();
776
+ if (currentDropzoneDisplayed?.treeBranch === this.treeBranch &&
777
+ currentDropzoneDisplayed.direction === "lower") {
778
+ return;
779
+ }
780
+ dropzoneRenderer.showLowerZones(this.treeBranch);
781
+ instance.triggerChangeDetection();
782
+ });
783
+ }
784
+ getShowUpperZonesSub(instance) {
785
+ return instance.showDropzones
786
+ .pipe(filter((direction) => direction === "upper"))
787
+ .subscribe(() => {
788
+ const currentDropzoneDisplayed = dropzoneRenderer.getCurrentDisplay();
789
+ if (currentDropzoneDisplayed?.treeBranch === this.treeBranch &&
790
+ currentDropzoneDisplayed.direction === "upper") {
791
+ return;
792
+ }
793
+ dropzoneRenderer.showUpperZones(this.treeBranch);
794
+ instance.triggerChangeDetection();
795
+ });
796
+ }
797
+ }
798
+
653
799
  class TreeBranch {
654
800
  constructor(parent, branchOptions) {
655
801
  this.branchOptions = branchOptions;
656
802
  this.detachedView = null;
657
803
  this.treeNodeBase = new TreeNodeBase();
658
- this.userlandComponent = this.branchOptions.component;
659
- const parentBranchesContainer = parent.getContents().instance.branchesContainer;
804
+ const parentBranchesContainer = parent.getBranchesContainer();
660
805
  assert(parentBranchesContainer !== undefined);
661
- this.contents = createComponent(BranchComponent, {
662
- environmentInjector: parentBranchesContainer.injector.get(EnvironmentInjector)
663
- });
664
- this.contents.instance.contentToHost = this.userlandComponent;
806
+ this.branchController = new BranchController(this, parentBranchesContainer);
665
807
  this.setIndentation(parent);
666
- this.outputBindingSubscriptions = [];
667
- this.instanceSubscriptions = this.getInstanceSubscriptions();
668
808
  if (parent instanceof TreeBranch &&
669
- parent.branchOptions.startCollapsed === true) {
809
+ parent.branchOptions.defaultCollapsed === true) {
670
810
  treeCollapser.storePrecollapsedNode(parent, this);
671
- this.detachedView = this.contents.hostView;
811
+ this.detachedView = this.branchController.getHostView();
672
812
  }
673
813
  else {
674
- parentBranchesContainer.insert(this.contents.hostView);
675
- this.contents.changeDetectorRef.detectChanges();
814
+ parentBranchesContainer.insert(this.branchController.getHostView());
815
+ this.detectChanges();
676
816
  this._parent = parent;
677
817
  this.dispatch(new GraftEvent(this, {
678
818
  parent: this._parent,
@@ -684,32 +824,20 @@ class TreeBranch {
684
824
  branches() {
685
825
  return this.treeNodeBase.branches();
686
826
  }
687
- deleteBranch(index) {
688
- this.treeNodeBase.deleteBranch(index);
689
- }
690
827
  destroy() {
691
- if (treeCollapser.isCollapsed(this)) {
692
- treeCollapser.expand(this);
693
- dropzoneRenderer.clearTreeFromRegistry(this);
694
- }
695
- this.branches().forEach((branch) => {
696
- branch.destroy();
697
- });
698
- const parent = this._parent;
699
- const index = this.index();
700
- if (index !== undefined && parent !== undefined) {
701
- const container = parent.getContents().instance.branchesContainer;
702
- assert(container !== undefined);
703
- container.remove(index);
704
- parent.deleteBranch(index);
828
+ if (this.isDestroyed()) {
829
+ throw new TreeError("Cannot destroy a destroyed tree branch");
705
830
  }
831
+ this.prune();
832
+ treeCollapser.expand(this);
833
+ dropzoneRenderer.clearTreeFromRegistry(this);
834
+ this.branchController.getHostView().destroy();
706
835
  this.treeNodeBase.destroy();
707
- this.instanceSubscriptions.forEach((sub) => {
708
- sub.unsubscribe();
709
- });
710
- this.outputBindingSubscriptions.forEach((sub) => {
711
- sub.unsubscribe();
712
- });
836
+ this.branchController.destroy();
837
+ this.dispatch(new DestructionEvent(this));
838
+ }
839
+ detectChanges() {
840
+ this.branchController.detectChanges();
713
841
  }
714
842
  dispatch(event) {
715
843
  this.treeNodeBase.dispatch(event);
@@ -721,10 +849,38 @@ class TreeBranch {
721
849
  getBranch(index) {
722
850
  return this.treeNodeBase.getBranch(index);
723
851
  }
724
- getContents() {
725
- return this.contents;
852
+ getBranchesContainer() {
853
+ if (this.isDestroyed()) {
854
+ throw new TreeError("Cannot get branches container from a destroyed tree branch");
855
+ }
856
+ return this.branchController.getBranchesContainer();
857
+ }
858
+ getComponentInstance() {
859
+ if (this.isDestroyed()) {
860
+ throw new TreeError("Cannot get component instance from a destroyed tree branch");
861
+ }
862
+ return this.branchController.getComponentInstance();
863
+ }
864
+ getHostView() {
865
+ if (this.isDestroyed()) {
866
+ throw new TreeError("Cannot get component host view from a destroyed tree branch");
867
+ }
868
+ return this.branchController.getHostView();
869
+ }
870
+ getNativeElement() {
871
+ if (this.isDestroyed()) {
872
+ throw new TreeError("Cannot get native element from a destroyed tree branch");
873
+ }
874
+ return this.branchController.getNativeElement();
875
+ }
876
+ getUserlandComponentRef() {
877
+ if (this.isDestroyed()) {
878
+ throw new TreeError("Cannot get userland component from a destroyed tree branch");
879
+ }
880
+ return this.branchController.getUserlandComponentRef();
726
881
  }
727
882
  graftTo(newParent, index) {
883
+ this.checkGraftLocationValidity(newParent, index);
728
884
  const ownIndex = this.index();
729
885
  if (ownIndex !== undefined) {
730
886
  this.prune();
@@ -740,6 +896,9 @@ class TreeBranch {
740
896
  return newIndex;
741
897
  }
742
898
  grow(component, options) {
899
+ if (this.isDestroyed()) {
900
+ throw new TreeError("Cannot grow a branch on a destroyed tree branch");
901
+ }
743
902
  return new TreeBranch(this, { component, ...options });
744
903
  }
745
904
  index() {
@@ -752,6 +911,9 @@ class TreeBranch {
752
911
  assert(index >= 0);
753
912
  return index;
754
913
  }
914
+ isDestroyed() {
915
+ return this.treeNodeBase.isDestroyed();
916
+ }
755
917
  meta() {
756
918
  return this.branchOptions.meta ?? {};
757
919
  }
@@ -773,11 +935,14 @@ class TreeBranch {
773
935
  return [index];
774
936
  }
775
937
  prune() {
938
+ if (this.isDestroyed()) {
939
+ throw new TreeError("Cannot prune a destroyed tree branch");
940
+ }
776
941
  const parent = this._parent;
777
942
  const index = this.index();
778
943
  if (index === undefined || parent === undefined)
779
944
  return;
780
- const container = parent.getContents().instance.branchesContainer;
945
+ const container = parent.getBranchesContainer();
781
946
  assert(container !== undefined);
782
947
  this.detachedView = container.detach(index);
783
948
  assert(this.detachedView !== null);
@@ -792,81 +957,40 @@ class TreeBranch {
792
957
  }
793
958
  root() {
794
959
  const parent = this.parent();
795
- if (parent === undefined) {
796
- return undefined;
797
- }
798
960
  if (parent instanceof TreeBranch) {
799
961
  return parent.root();
800
962
  }
801
- if (parent instanceof TreeRoot) {
802
- return parent;
803
- }
804
- throw new Error("unexpected parent type");
963
+ assert(parent instanceof TreeRoot || parent === undefined);
964
+ return parent;
805
965
  }
806
966
  traverse(callback) {
807
967
  callback(this);
808
968
  this.treeNodeBase.traverse(callback);
809
969
  }
810
- getContentCreatedSub() {
811
- const instance = this.contents.instance;
812
- return instance.contentCreated.subscribe((userlandComponentInstance) => {
813
- for (const [key, value] of Object.entries(this.branchOptions.inputBindings ?? {})) {
814
- userlandComponentInstance[key] = value;
815
- }
816
- for (const [key, value] of Object.entries(this.branchOptions.outputBindings ?? {})) {
817
- this.outputBindingSubscriptions.push(userlandComponentInstance[key].subscribe(value));
818
- }
819
- userlandComponentInstance.treeBranch = this;
820
- const dropzones = instance.dropzones;
821
- if (!dropzones) {
822
- throw new Error("dropzones not defined");
823
- }
824
- dropzoneRenderer.registerDropzones(dropzones, this);
825
- });
826
- }
827
- getInstanceSubscriptions() {
828
- const droppedSub = this.contents.instance.dropped.subscribe((placement) => {
829
- dropzoneRenderer.handleDrop(this, placement);
830
- });
831
- return [
832
- this.getContentCreatedSub(),
833
- this.getShowLowerZonesSub(),
834
- this.getShowUpperZonesSub(),
835
- droppedSub
836
- ];
837
- }
838
- getShowLowerZonesSub() {
839
- const instance = this.contents.instance;
840
- return instance.showDropzones
841
- .pipe(filter((direction) => direction === "lower"))
842
- .subscribe(() => {
843
- const currentDropzoneDisplayed = dropzoneRenderer.getCurrentDisplay();
844
- if (currentDropzoneDisplayed?.treeBranch === this &&
845
- currentDropzoneDisplayed.direction === "lower") {
846
- return;
970
+ checkGraftLocationValidity(newParent, index) {
971
+ if (this.isDestroyed()) {
972
+ throw new TreeError("Cannot graft a destroyed tree branch");
973
+ }
974
+ if (newParent.isDestroyed()) {
975
+ throw new TreeError("Cannot graft to a destroyed tree branch");
976
+ }
977
+ if (typeof index === "number" &&
978
+ this.indexIsOutOfRange(newParent, index)) {
979
+ throw new TreeError(`Cannot graft branch at index ${index} of the parent. Out of range.`);
980
+ }
981
+ this.traverse((node) => {
982
+ if (node === newParent) {
983
+ throw new TreeError("Cannot graft a branch to itself or any of its own descendants");
847
984
  }
848
- dropzoneRenderer.showLowerZones(this);
849
- instance.triggerChangeDetection();
850
985
  });
851
986
  }
852
- getShowUpperZonesSub() {
853
- const instance = this.contents.instance;
854
- return instance.showDropzones
855
- .pipe(filter((direction) => direction === "upper"))
856
- .subscribe(() => {
857
- const currentDropzoneDisplayed = dropzoneRenderer.getCurrentDisplay();
858
- if (currentDropzoneDisplayed?.treeBranch === this &&
859
- currentDropzoneDisplayed.direction === "upper") {
860
- return;
861
- }
862
- dropzoneRenderer.showUpperZones(this);
863
- instance.triggerChangeDetection();
864
- });
987
+ indexIsOutOfRange(parent, index) {
988
+ return index < 0 || index > parent.branches().length;
865
989
  }
866
990
  reattachView(index) {
867
991
  assert(this._parent !== undefined);
868
992
  assert(this.detachedView !== null);
869
- const container = this._parent.getContents().instance.branchesContainer;
993
+ const container = this._parent.getBranchesContainer();
870
994
  assert(container !== undefined);
871
995
  this.detachedView.reattach();
872
996
  container.insert(this.detachedView, index);
@@ -876,7 +1000,8 @@ class TreeBranch {
876
1000
  const root = parent.root();
877
1001
  assert(root !== undefined);
878
1002
  const options = config.getConfig(root);
879
- const branchesContainerEl = this.contents.location.nativeElement
1003
+ const branchesContainerEl = this.branchController
1004
+ .getNativeElement()
880
1005
  .getElementsByClassName("branches-container")
881
1006
  .item(0);
882
1007
  assert(branchesContainerEl instanceof HTMLElement);
@@ -916,7 +1041,7 @@ class DropzoneRenderer {
916
1041
  if (this.currentDisplay === null)
917
1042
  return;
918
1043
  for (const branch of this.registry.values()) {
919
- const instance = branch.getContents().instance;
1044
+ const instance = branch.getComponentInstance();
920
1045
  instance.showInnerDropzone = false;
921
1046
  if (instance instanceof BranchComponent) {
922
1047
  instance.showLateralDropzone = false;
@@ -1017,7 +1142,7 @@ class DropzoneRenderer {
1017
1142
  return true;
1018
1143
  }
1019
1144
  if (treeNode instanceof TreeBranch) {
1020
- const allowNesting = config.getConfig(treeNode.root())?.allowNesting ??
1145
+ const allowNesting = config.getConfig(treeNode.root())?.dragAndDrop?.allowNesting ??
1021
1146
  (() => true);
1022
1147
  return allowNesting(treeNode);
1023
1148
  }
@@ -1027,11 +1152,13 @@ class DropzoneRenderer {
1027
1152
  const sourceNode = dragState.getDragData();
1028
1153
  assert(sourceNode instanceof TreeBranch);
1029
1154
  if (parent instanceof TreeRoot) {
1030
- const allowDrop = config.getConfig(parent)?.allowDrop ?? (() => true);
1155
+ const allowDrop = config.getConfig(parent)?.dragAndDrop?.allowDrop ??
1156
+ (() => true);
1031
1157
  return allowDrop(sourceNode, parent, index);
1032
1158
  }
1033
1159
  if (parent instanceof TreeBranch) {
1034
- const allowDrop = config.getConfig(parent.root())?.allowDrop ?? (() => true);
1160
+ const allowDrop = config.getConfig(parent.root())?.dragAndDrop?.allowDrop ??
1161
+ (() => true);
1035
1162
  return allowDrop(sourceNode, parent, index);
1036
1163
  }
1037
1164
  throw new Error("unsupported treeNode type");
@@ -1039,14 +1166,14 @@ class DropzoneRenderer {
1039
1166
  showInnerZone(treeNode) {
1040
1167
  if (!this.nestingAllowed(treeNode) || !this.dropAllowed(treeNode, 0))
1041
1168
  return;
1042
- treeNode.getContents().instance.showInnerDropzone = true;
1169
+ treeNode.getComponentInstance().showInnerDropzone = true;
1043
1170
  }
1044
1171
  showLateralZone(treeBranch) {
1045
1172
  const index = treeBranch.index();
1046
1173
  assert(index !== undefined);
1047
1174
  if (!this.dropAllowed(treeBranch.parent(), index + 1))
1048
1175
  return;
1049
- treeBranch.getContents().instance.showLateralDropzone = true;
1176
+ treeBranch.getComponentInstance().showLateralDropzone = true;
1050
1177
  }
1051
1178
  }
1052
1179
  const dropzoneRenderer = new DropzoneRenderer();
@@ -1132,32 +1259,34 @@ class LegacyTree {
1132
1259
  upgradeOptions(legacyOptions) {
1133
1260
  return {
1134
1261
  indentation: legacyOptions.indent,
1135
- allowNesting: (branch) => {
1136
- if (legacyOptions.listMode === true) {
1137
- return false;
1138
- }
1139
- if (legacyOptions.allowNesting === undefined) {
1140
- return true;
1141
- }
1142
- if (typeof legacyOptions.allowNesting === "boolean") {
1143
- return legacyOptions.allowNesting;
1144
- }
1145
- return legacyOptions.allowNesting(branch);
1146
- },
1147
- allowDragging: (branch) => {
1148
- if (legacyOptions.allowDragging === undefined) {
1149
- return true;
1150
- }
1151
- if (typeof legacyOptions.allowDragging === "boolean") {
1152
- return legacyOptions.allowDragging;
1153
- }
1154
- return legacyOptions.allowDragging(branch);
1155
- },
1156
- allowDrop: (source, parent, index) => {
1157
- if (legacyOptions.allowDrop === undefined) {
1158
- return true;
1262
+ dragAndDrop: {
1263
+ allowNesting: (branch) => {
1264
+ if (legacyOptions.listMode === true) {
1265
+ return false;
1266
+ }
1267
+ if (legacyOptions.allowNesting === undefined) {
1268
+ return true;
1269
+ }
1270
+ if (typeof legacyOptions.allowNesting === "boolean") {
1271
+ return legacyOptions.allowNesting;
1272
+ }
1273
+ return legacyOptions.allowNesting(branch);
1274
+ },
1275
+ allowDragging: (branch) => {
1276
+ if (legacyOptions.allowDragging === undefined) {
1277
+ return true;
1278
+ }
1279
+ if (typeof legacyOptions.allowDragging === "boolean") {
1280
+ return legacyOptions.allowDragging;
1281
+ }
1282
+ return legacyOptions.allowDragging(branch);
1283
+ },
1284
+ allowDrop: (source, parent, index) => {
1285
+ if (legacyOptions.allowDrop === undefined) {
1286
+ return true;
1287
+ }
1288
+ return legacyOptions.allowDrop(source, parent, index);
1159
1289
  }
1160
- return legacyOptions.allowDrop(source, parent, index);
1161
1290
  }
1162
1291
  };
1163
1292
  }
@@ -1170,9 +1299,6 @@ class LegacyTree {
1170
1299
  options.defaultComponent?.bindings ??
1171
1300
  {});
1172
1301
  const nodeData = node;
1173
- delete nodeData.nodes;
1174
- delete nodeData.collapsed;
1175
- delete nodeData.component;
1176
1302
  const branch = parent.grow(component, {
1177
1303
  inputBindings: bindings,
1178
1304
  meta: { nodeData }
@@ -1296,5 +1422,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
1296
1422
  * Generated bundle index. Do not edit.
1297
1423
  */
1298
1424
 
1299
- export { DragEndEvent, DragStartEvent, DraggableDirective, DragoverNoChangeDetectDirective, DropEvent, GraftEvent, LegacyTree, LimbleTreeLegacyModule, LimbleTreeModule, LimbleTreeRootComponent, PruneEvent, TreeBranch, TreeCollapseModule, TreeCollapseService, TreeDragAndDropModule, TreeDragAndDropService, TreeError, TreeRoot, TreeService, config };
1425
+ export { DestructionEvent, DragEndEvent, DragStartEvent, DraggableDirective, DragoverNoChangeDetectDirective, DropEvent, GraftEvent, LegacyTree, LimbleTreeLegacyModule, LimbleTreeModule, LimbleTreeRootComponent, PruneEvent, TreeBranch, TreeCollapseModule, TreeCollapseService, TreeDragAndDropModule, TreeDragAndDropService, TreeError, TreeRoot, TreeService, config };
1300
1426
  //# sourceMappingURL=limble-limble-tree.mjs.map