@limble/limble-tree 0.13.0 → 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 (163) hide show
  1. package/README.md +418 -96
  2. package/esm2020/lib/components/branch/branch.component.mjs +79 -0
  3. package/esm2020/lib/components/dropzone/dropzone.component.mjs +35 -0
  4. package/esm2020/lib/components/host-component.interface.mjs +2 -0
  5. package/esm2020/lib/components/node-component.interface.mjs +2 -0
  6. package/esm2020/lib/components/root/root.component.mjs +36 -0
  7. package/esm2020/lib/core/branch-options.interface.mjs +2 -0
  8. package/esm2020/lib/core/configuration/configuration.mjs +17 -0
  9. package/esm2020/lib/core/configuration/tree-options.interface.mjs +2 -0
  10. package/esm2020/lib/core/index.mjs +6 -0
  11. package/esm2020/lib/core/relationship.interface.mjs +2 -0
  12. package/esm2020/lib/core/tree-branch/branch-controller.mjs +99 -0
  13. package/esm2020/lib/core/tree-branch/tree-branch.mjs +224 -0
  14. package/esm2020/lib/core/tree-node-base.mjs +69 -0
  15. package/esm2020/lib/core/tree-root/root-controller.mjs +42 -0
  16. package/esm2020/lib/core/tree-root/tree-root.mjs +85 -0
  17. package/esm2020/lib/core/tree-service/tree.service.mjs +19 -0
  18. package/esm2020/lib/errors/index.mjs +2 -0
  19. package/esm2020/lib/errors/tree-error.mjs +3 -0
  20. package/esm2020/lib/events/drag/drag-end-event.mjs +28 -0
  21. package/esm2020/lib/events/drag/drag-start-event.mjs +12 -0
  22. package/esm2020/lib/events/drag/drop-event.mjs +20 -0
  23. package/esm2020/lib/events/drag/index.mjs +4 -0
  24. package/esm2020/lib/events/general/destruction-event.mjs +12 -0
  25. package/esm2020/lib/events/general/index.mjs +2 -0
  26. package/esm2020/lib/events/index.mjs +4 -0
  27. package/esm2020/lib/events/relational/graft-event.mjs +24 -0
  28. package/esm2020/lib/events/relational/index.mjs +4 -0
  29. package/esm2020/lib/events/relational/prune-event.mjs +24 -0
  30. package/esm2020/lib/events/relational/relational-tree-event.interface.mjs +2 -0
  31. package/esm2020/lib/extras/collapse/collapse.mjs +33 -0
  32. package/esm2020/lib/extras/collapse/collapse.module.mjs +15 -0
  33. package/esm2020/lib/extras/collapse/collapse.service.mjs +20 -0
  34. package/esm2020/lib/extras/collapse/index.mjs +3 -0
  35. package/esm2020/lib/extras/drag-and-drop/drag-and-drop.mjs +85 -0
  36. package/esm2020/lib/extras/drag-and-drop/drag-and-drop.module.mjs +19 -0
  37. package/esm2020/lib/extras/drag-and-drop/drag-and-drop.service.mjs +28 -0
  38. package/esm2020/lib/extras/drag-and-drop/drag-state.mjs +47 -0
  39. package/esm2020/lib/extras/drag-and-drop/draggable.directive.mjs +30 -0
  40. package/esm2020/lib/extras/drag-and-drop/dragover-no-change-detect.mjs +40 -0
  41. package/esm2020/lib/extras/drag-and-drop/dropzone-renderer.mjs +178 -0
  42. package/esm2020/lib/extras/drag-and-drop/index.mjs +5 -0
  43. package/esm2020/lib/legacy/index.mjs +7 -0
  44. package/esm2020/lib/legacy/legacy-component-obj.interface.mjs +2 -0
  45. package/esm2020/lib/legacy/legacy-tree-data.interface.mjs +2 -0
  46. package/esm2020/lib/legacy/legacy-tree-options.interface.mjs +2 -0
  47. package/esm2020/lib/legacy/legacy-tree.mjs +73 -0
  48. package/esm2020/lib/legacy/limble-tree-legacy.module.mjs +20 -0
  49. package/esm2020/lib/legacy/limble-tree-root/limble-tree-root.component.mjs +78 -0
  50. package/esm2020/lib/limble-tree.module.mjs +21 -47
  51. package/esm2020/lib/structure/branchable.interface.mjs +2 -0
  52. package/esm2020/lib/structure/component-container.interface.mjs +2 -0
  53. package/esm2020/lib/structure/event-conduit.interface.mjs +2 -0
  54. package/esm2020/lib/structure/graftable.interface.mjs +2 -0
  55. package/esm2020/lib/structure/index.mjs +10 -0
  56. package/esm2020/lib/structure/tree-branch-node.interface.mjs +2 -0
  57. package/esm2020/lib/structure/tree-event.interface.mjs +2 -0
  58. package/esm2020/lib/structure/tree-node.interface.mjs +2 -0
  59. package/esm2020/lib/structure/tree-plot.mjs +2 -0
  60. package/esm2020/lib/structure/tree-relationship.interface.mjs +2 -0
  61. package/esm2020/limble-limble-tree.mjs +4 -4
  62. package/esm2020/public-api.mjs +12 -6
  63. package/esm2020/shared/assert.mjs +7 -0
  64. package/fesm2015/limble-limble-tree.mjs +1404 -2226
  65. package/fesm2015/limble-limble-tree.mjs.map +1 -1
  66. package/fesm2020/limble-limble-tree.mjs +1396 -2132
  67. package/fesm2020/limble-limble-tree.mjs.map +1 -1
  68. package/index.d.ts +5 -5
  69. package/lib/components/branch/branch.component.d.ts +26 -0
  70. package/lib/components/dropzone/dropzone.component.d.ts +13 -0
  71. package/lib/components/host-component.interface.d.ts +6 -0
  72. package/lib/components/node-component.interface.d.ts +5 -0
  73. package/lib/components/root/root.component.d.ts +14 -0
  74. package/lib/core/branch-options.interface.d.ts +14 -0
  75. package/lib/core/configuration/configuration.d.ts +11 -0
  76. package/lib/core/configuration/tree-options.interface.d.ts +32 -0
  77. package/lib/core/index.d.ts +5 -0
  78. package/lib/core/relationship.interface.d.ts +5 -0
  79. package/lib/core/tree-branch/branch-controller.d.ts +25 -0
  80. package/lib/core/tree-branch/tree-branch.d.ts +44 -0
  81. package/lib/core/tree-node-base.d.ts +25 -0
  82. package/lib/core/tree-root/root-controller.d.ts +19 -0
  83. package/lib/core/tree-root/tree-root.d.ts +30 -0
  84. package/lib/core/tree-service/tree.service.d.ts +9 -0
  85. package/lib/errors/index.d.ts +1 -0
  86. package/lib/errors/tree-error.d.ts +2 -0
  87. package/lib/events/drag/drag-end-event.d.ts +24 -0
  88. package/lib/events/drag/drag-start-event.d.ts +8 -0
  89. package/lib/events/drag/drop-event.d.ts +13 -0
  90. package/lib/events/drag/index.d.ts +3 -0
  91. package/lib/events/general/destruction-event.d.ts +8 -0
  92. package/lib/events/general/index.d.ts +1 -0
  93. package/lib/events/index.d.ts +3 -0
  94. package/lib/events/relational/graft-event.d.ts +15 -0
  95. package/lib/events/relational/index.d.ts +3 -0
  96. package/lib/events/relational/prune-event.d.ts +15 -0
  97. package/lib/events/relational/relational-tree-event.interface.d.ts +6 -0
  98. package/lib/extras/collapse/collapse.d.ts +11 -0
  99. package/lib/extras/collapse/collapse.module.d.ts +6 -0
  100. package/lib/extras/collapse/collapse.service.d.ts +9 -0
  101. package/lib/extras/collapse/index.d.ts +2 -0
  102. package/lib/extras/drag-and-drop/drag-and-drop.d.ts +16 -0
  103. package/lib/extras/drag-and-drop/drag-and-drop.module.d.ts +8 -0
  104. package/lib/extras/drag-and-drop/drag-and-drop.service.d.ts +16 -0
  105. package/lib/extras/drag-and-drop/drag-state.d.ts +23 -0
  106. package/lib/extras/drag-and-drop/draggable.directive.d.ts +12 -0
  107. package/lib/{custom-event-bindings/dragover-no-change-detect.directive.d.ts → extras/drag-and-drop/dragover-no-change-detect.d.ts} +14 -14
  108. package/lib/extras/drag-and-drop/dropzone-renderer.d.ts +28 -0
  109. package/lib/extras/drag-and-drop/index.d.ts +4 -0
  110. package/lib/legacy/index.d.ts +6 -0
  111. package/lib/legacy/legacy-component-obj.interface.d.ts +13 -0
  112. package/lib/legacy/legacy-tree-data.interface.d.ts +18 -0
  113. package/lib/legacy/legacy-tree-options.interface.d.ts +34 -0
  114. package/lib/legacy/legacy-tree.d.ts +14 -0
  115. package/lib/legacy/limble-tree-legacy.module.d.ts +8 -0
  116. package/lib/legacy/limble-tree-root/limble-tree-root.component.d.ts +28 -0
  117. package/lib/limble-tree.module.d.ts +9 -14
  118. package/lib/structure/branchable.interface.d.ts +4 -0
  119. package/lib/structure/component-container.interface.d.ts +8 -0
  120. package/lib/structure/event-conduit.interface.d.ts +6 -0
  121. package/lib/structure/graftable.interface.d.ts +6 -0
  122. package/lib/structure/index.d.ts +9 -0
  123. package/lib/structure/tree-branch-node.interface.d.ts +5 -0
  124. package/lib/structure/tree-event.interface.d.ts +5 -0
  125. package/lib/structure/tree-node.interface.d.ts +11 -0
  126. package/lib/structure/tree-plot.d.ts +1 -0
  127. package/lib/structure/tree-relationship.interface.d.ts +7 -0
  128. package/package.json +6 -14
  129. package/public-api.d.ts +8 -3
  130. package/shared/assert.d.ts +1 -0
  131. package/esm2020/lib/classes/Branch.mjs +0 -153
  132. package/esm2020/lib/classes/DropZone.mjs +0 -71
  133. package/esm2020/lib/classes/DropZoneLocation.mjs +0 -16
  134. package/esm2020/lib/custom-event-bindings/dragleave-no-change-detect.directive.mjs +0 -33
  135. package/esm2020/lib/custom-event-bindings/dragover-no-change-detect.directive.mjs +0 -39
  136. package/esm2020/lib/drop-zone/drop-zone.component.mjs +0 -75
  137. package/esm2020/lib/limble-tree-branch/limble-tree-branch.component.mjs +0 -110
  138. package/esm2020/lib/limble-tree-node/limble-tree-node.component.mjs +0 -467
  139. package/esm2020/lib/limble-tree-placeholder/limble-tree-placeholder.component.mjs +0 -70
  140. package/esm2020/lib/limble-tree-root/drop-zone.service.mjs +0 -376
  141. package/esm2020/lib/limble-tree-root/limble-tree-root.component.mjs +0 -172
  142. package/esm2020/lib/limble-tree-root/tree-construction-status.service.mjs +0 -33
  143. package/esm2020/lib/limble-tree-root/tree.service.mjs +0 -297
  144. package/esm2020/lib/singletons/component-creator.service.mjs +0 -19
  145. package/esm2020/lib/singletons/drag-state.service.mjs +0 -63
  146. package/esm2020/lib/singletons/global-events.service.mjs +0 -136
  147. package/esm2020/lib/util.mjs +0 -74
  148. package/lib/classes/Branch.d.ts +0 -26
  149. package/lib/classes/DropZone.d.ts +0 -27
  150. package/lib/classes/DropZoneLocation.d.ts +0 -9
  151. package/lib/custom-event-bindings/dragleave-no-change-detect.directive.d.ts +0 -13
  152. package/lib/drop-zone/drop-zone.component.d.ts +0 -18
  153. package/lib/limble-tree-branch/limble-tree-branch.component.d.ts +0 -28
  154. package/lib/limble-tree-node/limble-tree-node.component.d.ts +0 -53
  155. package/lib/limble-tree-placeholder/limble-tree-placeholder.component.d.ts +0 -21
  156. package/lib/limble-tree-root/drop-zone.service.d.ts +0 -62
  157. package/lib/limble-tree-root/limble-tree-root.component.d.ts +0 -37
  158. package/lib/limble-tree-root/tree-construction-status.service.d.ts +0 -15
  159. package/lib/limble-tree-root/tree.service.d.ts +0 -132
  160. package/lib/singletons/component-creator.service.d.ts +0 -9
  161. package/lib/singletons/drag-state.service.d.ts +0 -35
  162. package/lib/singletons/global-events.service.d.ts +0 -13
  163. package/lib/util.d.ts +0 -13
@@ -1,2162 +1,1426 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, Component, ChangeDetectionStrategy, Input, ViewContainerRef, ViewChild, ElementRef, EventEmitter, Directive, Output, NgZone, NgModule } from '@angular/core';
3
- import { Subject, BehaviorSubject, fromEvent, merge, EMPTY } from 'rxjs';
4
- import { tap, filter, debounce, throttleTime, skip, first, skipUntil, debounceTime } from 'rxjs/operators';
5
- import * as i2 from '@angular/common';
2
+ import { Injectable, NgModule, EventEmitter, Directive, Input, Output, Component, NgZone, ViewContainerRef, ViewChild, ViewChildren, createComponent, EnvironmentInjector, HostListener } from '@angular/core';
3
+ import { BehaviorSubject, Subject, fromEvent, merge, map, filter, first } from 'rxjs';
4
+ import * as i1 from '@angular/common';
6
5
  import { CommonModule } from '@angular/common';
6
+ import { throttleTime } from 'rxjs/operators';
7
7
 
8
- function arraysAreEqual(array1, array2) {
9
- if (array1.length !== array2.length) {
10
- return false;
11
- }
12
- for (const [index, value1] of array1.entries()) {
13
- const value2 = array2[index];
14
- if (value1 instanceof Array && value2 instanceof Array) {
15
- if (!arraysAreEqual(value1, value2)) {
16
- return false;
17
- }
18
- }
19
- else if (value1 !== value2) {
20
- return false;
21
- }
22
- }
23
- return true;
24
- }
25
- function isElementDescendant(potentialAncestor, potentialDescendant) {
26
- if (potentialAncestor === potentialDescendant) {
27
- return true;
28
- }
29
- let cursor = potentialDescendant.parentNode;
30
- while (cursor !== document) {
31
- if (cursor === null) {
32
- //Did not reach `document` or ancestor. potentialDescendant is not part of the DOM.
33
- return undefined;
34
- }
35
- if (cursor === potentialAncestor) {
36
- return true;
37
- }
38
- cursor = cursor.parentNode;
39
- }
40
- return false;
41
- }
42
- function isNestingAllowed(options, nodeData) {
43
- return (options !== undefined &&
44
- (options.allowNesting === true ||
45
- (typeof options.allowNesting === "function" &&
46
- nodeData !== undefined &&
47
- options.allowNesting(nodeData) === true)));
48
- }
49
- function isDraggingAllowed(options, nodeData) {
50
- return (options !== undefined &&
51
- (options.allowDragging === true ||
52
- (typeof options.allowDragging === "function" &&
53
- nodeData !== undefined &&
54
- options.allowDragging(nodeData) === true)));
55
- }
56
- function isFirefox() {
57
- return navigator.userAgent.includes("Firefox");
58
- }
59
- /** Because drop zones can disappear when the mouse moves, sometimes
60
- * moving the mouse just a little bit inside the tree causes the tree to
61
- * shrink such that the mouse is no longer over the tree. In this case,
62
- * a dragleave event may not fire, and we can't clear the drop zones. This
63
- * function is used to catch this edge case.
64
- */
65
- function suddenTreeExit(event) {
66
- if (event.target === null || !(event.target instanceof Element)) {
67
- throw new Error("failed to get event target element");
68
- }
69
- const treeEventHost = event.target.closest(".tree-event-host");
70
- if (treeEventHost === null) {
71
- console.log(event.target);
72
- throw new Error("failed to find treeEventHost");
73
- }
74
- const rect = treeEventHost.getBoundingClientRect();
75
- const clientY = event.clientY;
76
- if (clientY > rect.bottom || clientY < rect.top) {
77
- return true;
78
- }
79
- return false;
8
+ class TreeCollapser {
9
+ constructor() {
10
+ this.tempStorage = new Map();
11
+ }
12
+ collapse(treeBranch) {
13
+ if (treeBranch.branches().length === 0)
14
+ return;
15
+ const branches = [];
16
+ this.tempStorage.set(treeBranch, branches);
17
+ treeBranch.branches().forEach((branch) => {
18
+ branch.prune();
19
+ branches.push(branch);
20
+ });
21
+ }
22
+ expand(treeBranch) {
23
+ const branches = this.tempStorage.get(treeBranch);
24
+ if (branches === undefined)
25
+ return;
26
+ branches.forEach((branch) => {
27
+ branch.graftTo(treeBranch);
28
+ });
29
+ this.tempStorage.delete(treeBranch);
30
+ treeBranch.detectChanges();
31
+ }
32
+ isCollapsed(treeBranch) {
33
+ return this.tempStorage.has(treeBranch);
34
+ }
35
+ storePrecollapsedNode(parent, branch) {
36
+ this.tempStorage.set(parent, (this.tempStorage.get(parent) ?? []).concat(branch));
37
+ }
80
38
  }
39
+ const treeCollapser = new TreeCollapser();
81
40
 
82
- class DropZoneLocation {
83
- constructor(parentCoordinates, insertIndex) {
84
- this.parentCoordinates = parentCoordinates;
85
- this.insertIndex = insertIndex;
86
- }
87
- getFullInsertCoordinates() {
88
- return [...this.parentCoordinates, this.insertIndex];
89
- }
90
- setParentCoordinates(coordinates) {
91
- this.parentCoordinates = coordinates;
92
- }
93
- setInsertIndex(index) {
94
- this.insertIndex = index;
95
- }
41
+ class TreeCollapseService {
42
+ collapse(treeBranch) {
43
+ treeCollapser.collapse(treeBranch);
44
+ }
45
+ expand(treeBranch) {
46
+ treeCollapser.expand(treeBranch);
47
+ }
48
+ isCollapsed(treeBranch) {
49
+ return treeCollapser.isCollapsed(treeBranch);
50
+ }
96
51
  }
52
+ TreeCollapseService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeCollapseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
53
+ TreeCollapseService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeCollapseService });
54
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeCollapseService, decorators: [{
55
+ type: Injectable
56
+ }] });
97
57
 
98
- class DropZone {
99
- constructor(parentCoordinates, insertIndex) {
100
- this.location = new DropZoneLocation(parentCoordinates, insertIndex);
101
- this.rendered = false;
102
- this.visible = false;
103
- this.active = false;
104
- this.commChannel = new Subject();
105
- }
106
- static dropZoneLocationsAreEqual(valueA, valueB) {
107
- return arraysAreEqual(valueA.getFullInsertCoordinates(), valueB.getFullInsertCoordinates());
108
- }
109
- isRendered(set = undefined) {
110
- if (set !== undefined) {
111
- this.rendered = set;
112
- if (this.commChannel !== undefined) {
113
- this.commChannel.next("checkRendered");
114
- }
115
- if (this.rendered === false) {
116
- this.isVisible(false);
117
- }
118
- }
119
- return this.rendered;
120
- }
121
- isVisible(set = undefined) {
122
- if (set !== undefined) {
123
- this.visible = set;
124
- if (this.commChannel !== undefined) {
125
- this.commChannel.next("checkVisible");
126
- }
127
- if (this.visible === false) {
128
- this.isActive(false);
129
- }
130
- }
131
- return this.visible;
132
- }
133
- isActive(set = undefined) {
134
- if (set !== undefined) {
135
- this.active = set;
136
- if (this.commChannel !== undefined) {
137
- this.commChannel.next("checkActive");
138
- }
139
- }
140
- return this.active;
141
- }
142
- getLocation() {
143
- return this.location;
144
- }
145
- getFamily() {
146
- return this.family;
147
- }
148
- setFamily(family) {
149
- this.family = family;
150
- }
151
- getCommChannel() {
152
- return this.commChannel;
153
- }
154
- getFullInsertCoordinates() {
155
- return this.location.getFullInsertCoordinates();
156
- }
157
- setHost(host) {
158
- this.host = host;
159
- return this.host;
160
- }
161
- getHost() {
162
- return this.host;
163
- }
58
+ class TreeCollapseModule {
164
59
  }
60
+ TreeCollapseModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeCollapseModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
61
+ TreeCollapseModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: TreeCollapseModule });
62
+ TreeCollapseModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeCollapseModule, providers: [TreeCollapseService] });
63
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeCollapseModule, decorators: [{
64
+ type: NgModule,
65
+ args: [{
66
+ providers: [TreeCollapseService]
67
+ }]
68
+ }] });
165
69
 
166
- class DragStateService {
167
- constructor() {
168
- this.state = "idle";
169
- this.state$ = new BehaviorSubject(this.state);
170
- }
171
- /** Called to indicate that something is being dragged. Stores that something for later. */
172
- dragging(branch, parentContainer) {
173
- this._tempData = {
174
- branch: branch,
175
- parentContainer: parentContainer
176
- };
177
- this.state = "dragging";
178
- this.state$.next(this.state);
179
- }
180
- /** Called to indicate that there is a valid active drop zone. Drop is now possible. */
181
- droppable() {
182
- if (this.state !== "dragging") {
183
- throw new Error("Can only call `droppable` when state is `dragging`");
184
- }
185
- this.state = "droppable";
186
- this.state$.next(this.state);
187
- }
188
- /** Called to indicate that there is no longer a valid active drop zone. Drop is no longer possible. */
189
- notDroppable() {
190
- if (this.state !== "droppable") {
191
- throw new Error("Can only call `notDroppable` when state is `droppable`");
192
- }
193
- this.state = "dragging";
194
- this.state$.next(this.state);
195
- }
196
- /** Called to indicate that a drop into a valid drop zone has occurred. Returns the item that was dropped. */
197
- capture() {
198
- if (this.state !== "droppable") {
199
- throw new Error("Can only move to `captured` state from `droppable` state");
200
- }
201
- this.state = "captured";
202
- this.state$.next(this.state);
203
- return this._tempData?.branch;
204
- }
205
- /** Called to reset the service for future drags */
206
- release() {
207
- this._tempData = undefined;
208
- this.state = "idle";
209
- this.state$.next(this.state);
210
- }
211
- /** gets the current thing being dragged, if any. */
212
- getData() {
213
- return this._tempData;
214
- }
215
- /** gets the current state */
216
- getState() {
217
- return this.state;
218
- }
219
- }
220
- DragStateService.ɵfac = function DragStateService_Factory(t) { return new (t || DragStateService)(); };
221
- DragStateService.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: DragStateService, factory: DragStateService.ɵfac });
222
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DragStateService, [{
223
- type: Injectable
224
- }], function () { return []; }, null); })();
70
+ class Configuration {
71
+ constructor() {
72
+ this.configStorage = new Map();
73
+ this.configStorage = new Map();
74
+ }
75
+ setConfig(root, options) {
76
+ this.configStorage.set(root, options);
77
+ }
78
+ getConfig(root) {
79
+ return this.configStorage.get(root);
80
+ }
81
+ delete(root) {
82
+ this.configStorage.delete(root);
83
+ }
84
+ }
85
+ const config = new Configuration();
225
86
 
226
- class TreeConstructionStatus {
227
- constructor() {
228
- this.beingBuilt = 0;
229
- this.isReady = false;
230
- this.stable$ = new BehaviorSubject(this.treeIsStable());
231
- }
232
- constructing() {
233
- this.beingBuilt++;
234
- this.emit();
235
- }
236
- doneConstructing() {
237
- this.beingBuilt--;
238
- this.emit();
239
- }
240
- treeIsStable() {
241
- return this.isReady === true && this.beingBuilt === 0;
242
- }
243
- ready(val) {
244
- this.isReady = val;
245
- }
246
- emit() {
247
- this.stable$.next(this.treeIsStable());
248
- }
249
- }
250
- TreeConstructionStatus.ɵfac = function TreeConstructionStatus_Factory(t) { return new (t || TreeConstructionStatus)(); };
251
- TreeConstructionStatus.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: TreeConstructionStatus, factory: TreeConstructionStatus.ɵfac });
252
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TreeConstructionStatus, [{
253
- type: Injectable
254
- }], function () { return []; }, null); })();
87
+ class TreeError extends Error {
88
+ }
255
89
 
256
- function sortFamily(memberA, memberB) {
257
- const aCoordinates = memberA.getFullInsertCoordinates();
258
- const bCoordinates = memberB.getFullInsertCoordinates();
259
- if (aCoordinates.length > bCoordinates.length) {
260
- return -1;
261
- }
262
- if (aCoordinates.length < bCoordinates.length) {
263
- return 1;
264
- }
265
- return 0;
266
- }
267
- class DropZoneService {
268
- constructor(dragStateService, treeConstructionStatus) {
269
- this.dragStateService = dragStateService;
270
- this.dropZoneArchive = new Set();
271
- this.dropZoneInventory = [];
272
- this.dropZoneFamilies = [];
273
- this.visibleFamily = null;
274
- this.activeDropZone = null;
275
- this.tempFamilies = [];
276
- this.setActiveDropZone(null);
277
- let treeIsStable = false;
278
- const treeIsStable$ = treeConstructionStatus.stable$.pipe(tap((value) => {
279
- treeIsStable = value;
280
- }), filter((value) => value === true));
281
- this.update$ = new Subject();
282
- this.update$
283
- .pipe(debounce(() => {
284
- if (treeIsStable === true) {
285
- //If tree is stable, continue right away
286
- return Promise.resolve();
287
- }
288
- //If tree is not stable, wait for it to become so.
289
- return treeIsStable$;
290
- }))
291
- .subscribe(() => {
292
- setTimeout(() => {
293
- this.updateDropZones();
294
- });
295
- });
296
- }
297
- addDropZone(newDropZone) {
298
- this.dropZoneArchive.add(newDropZone);
299
- }
300
- /** hides all drop zones */
301
- clearVisibleZones() {
302
- if (this.visibleFamily !== null) {
303
- for (const member of this.visibleFamily.members) {
304
- member.isVisible(false);
305
- }
306
- this.visibleFamily = null;
307
- }
308
- this.setActiveDropZone(null);
309
- }
310
- getActiveDropZone() {
311
- return this.activeDropZone;
312
- }
313
- getDropZone(coordinates) {
314
- const parent = [...coordinates];
315
- parent.pop();
316
- const index = coordinates[coordinates.length - 1];
317
- const location = new DropZoneLocation(parent, index);
318
- return this.dropZoneInventory.find((dropZone) => DropZone.dropZoneLocationsAreEqual(dropZone, location));
319
- }
320
- init(tree, treeOptions) {
321
- this.tree = tree;
322
- this.treeOptions = treeOptions;
323
- this.update();
324
- }
325
- removeDropZone(dropZone) {
326
- this.dropZoneArchive.delete(dropZone);
327
- }
328
- /** hides all drop zones, deletes all the family assignments,
329
- * and empties the dropZoneInventory
330
- */
331
- reset() {
332
- this.clearVisibleZones();
333
- this.dropZoneFamilies.length = 0;
334
- this.dropZoneInventory.length = 0;
335
- }
336
- /**
337
- * Restores the service to its initial state: hides all drop zones,
338
- * deletes all the family assignments, and empties the dropZoneInventory
339
- * and dropZoneArchive.
340
- */
341
- restart() {
342
- this.reset();
343
- this.dropZoneArchive.clear();
344
- }
345
- restoreFamilies() {
346
- if (this.tempFamilies.length === 2) {
347
- this.dropZoneFamilies.pop();
348
- this.dropZoneFamilies.push(this.tempFamilies[0]);
349
- for (const member of this.tempFamilies[0].members) {
350
- member.setFamily(this.tempFamilies[0]);
351
- }
352
- if (this.tempFamilies[1] !== null) {
353
- this.dropZoneFamilies.push(this.tempFamilies[1]);
354
- for (const member of this.tempFamilies[1].members) {
355
- member.setFamily(this.tempFamilies[1]);
356
- }
357
- }
358
- this.tempFamilies = [];
359
- }
360
- }
361
- /**
362
- * Shows the drop zone family of the drop zone indicated by `coordinates`.
363
- */
364
- showDropZoneFamily(
365
- /** Note: this drop zone may not exist in the dropZoneInventory; we have to search the inventory based on its location */
366
- dropZone, options = { joinFamilies: false, activateLowestInsteadOfFounder: false }) {
367
- if (this.activeDropZone !== null &&
368
- DropZone.dropZoneLocationsAreEqual(this.activeDropZone, dropZone)) {
369
- //Already showing the family with the appropriate active drop zone
370
- return;
371
- }
372
- if (this.visibleFamily !== null || this.activeDropZone !== null) {
373
- this.clearVisibleZones();
374
- }
375
- const target = this.dropZoneInventory.find((zone) => DropZone.dropZoneLocationsAreEqual(zone, dropZone));
376
- if (target === undefined) {
377
- throw new Error(`Could not find drop zone to show. location: ${JSON.stringify(dropZone.getLocation())}`);
378
- }
379
- const family = target.getFamily();
380
- if (options.joinFamilies === true) {
381
- const location1 = dropZone.getLocation();
382
- const location2 = new DropZoneLocation([...location1.parentCoordinates], location1.insertIndex + 1);
383
- const target2 = this.dropZoneInventory.find((zone) => DropZone.dropZoneLocationsAreEqual(zone, location2));
384
- if (target2 === undefined) {
385
- throw new Error("Could not find drop zone to show");
386
- }
387
- const family2 = target2.getFamily();
388
- if (family === undefined || family2 === undefined) {
389
- throw new Error("No family");
390
- }
391
- const newFamily = {
392
- founder: family.founder,
393
- members: [...family.members]
394
- };
395
- this.showDropZone(family.founder, true);
396
- for (const member of family.members.sort(sortFamily)) {
397
- member.setFamily(newFamily);
398
- if (member !== family.founder) {
399
- if (this.activeDropZone === null) {
400
- //Failed to activate a zone so far, so activate this one instead
401
- this.showDropZone(member, true);
402
- }
403
- else {
404
- this.showDropZone(member);
405
- }
406
- }
407
- }
408
- for (const member of family2.members) {
409
- member.setFamily(newFamily);
410
- if (member.getLocation().parentCoordinates.length <
411
- target2.getLocation().parentCoordinates.length) {
412
- newFamily.members.push(member);
413
- this.showDropZone(member);
414
- }
415
- }
416
- //Temporarily store the old families
417
- this.tempFamilies = [family, family2];
418
- //Remove the old families
419
- const familyIndex = this.dropZoneFamilies.indexOf(family);
420
- this.dropZoneFamilies.splice(familyIndex, 1);
421
- const family2Index = this.dropZoneFamilies.indexOf(family2);
422
- this.dropZoneFamilies.splice(family2Index, 1);
423
- //Add the new family
424
- this.dropZoneFamilies.push(newFamily);
425
- this.visibleFamily = newFamily;
426
- }
427
- else {
428
- if (family === undefined) {
429
- throw new Error("No family");
430
- }
431
- this.visibleFamily = family;
432
- this.showDropZone(family.founder, true);
433
- if (family.members.length > 1) {
434
- for (const member of family.members.sort(sortFamily)) {
435
- if (member !== family.founder) {
436
- if (this.activeDropZone === null) {
437
- //Failed to activate a zone so far, so activate this one instead
438
- this.showDropZone(member, true);
439
- }
440
- else {
441
- this.showDropZone(member);
442
- }
443
- }
444
- }
445
- }
446
- }
447
- if (options.activateLowestInsteadOfFounder === true &&
448
- this.visibleFamily.members.length > 1) {
449
- const lowestMember = [...this.visibleFamily.members]
450
- .sort(sortFamily)
451
- .pop();
452
- if (lowestMember === undefined) {
453
- throw new Error("Could not get lowest member");
454
- }
455
- this.swapActiveDropZone(lowestMember);
456
- }
457
- }
458
- swapActiveDropZone(newActiveDropZone) {
459
- if (this.visibleFamily === null) {
460
- throw new Error("No visible family available for swapping");
461
- }
462
- const index = this.visibleFamily.members.findIndex((dropZone) => dropZone === newActiveDropZone);
463
- if (index === -1) {
464
- throw new Error("failed to swap active drop zone");
465
- }
466
- this.setActiveDropZone(newActiveDropZone);
467
- }
468
- update() {
469
- this.update$.next(null);
470
- }
471
- assignFamilies() {
472
- const orphanZones = [...this.dropZoneInventory];
473
- const deepestMembers = orphanZones
474
- .filter((zone) => {
475
- const location = zone.getLocation();
476
- return (location.insertIndex === 0 &&
477
- location.parentCoordinates.length > 0);
478
- })
479
- .sort((valueA, valueB) => {
480
- const aCoordinates = valueA.getFullInsertCoordinates();
481
- const bCoordinates = valueB.getFullInsertCoordinates();
482
- const length = Math.max(aCoordinates.length, bCoordinates.length);
483
- for (let index = 0; index < length; index++) {
484
- if ((aCoordinates[index] ?? -1) > (bCoordinates[index] ?? -1)) {
485
- return -1;
486
- }
487
- else if ((aCoordinates[index] ?? -1) < (bCoordinates[index] ?? -1)) {
488
- return 1;
489
- }
490
- }
491
- return 0;
492
- });
493
- for (const dropZone of deepestMembers) {
494
- if (!orphanZones.includes(dropZone)) {
495
- continue;
496
- }
497
- const family = {
498
- founder: dropZone,
499
- members: []
500
- };
501
- dropZone.setFamily(family);
502
- //See if there are any orphans that belong to this family and claim them.
503
- const cursor = [...dropZone.getFullInsertCoordinates()];
504
- while (cursor.length > 0) {
505
- const familyMemberIndex = orphanZones.findIndex((zone) => arraysAreEqual(zone.getFullInsertCoordinates(), cursor));
506
- if (familyMemberIndex !== -1) {
507
- const familyMember = orphanZones.splice(familyMemberIndex, 1)[0];
508
- family.members.push(familyMember);
509
- familyMember.setFamily(family);
510
- }
511
- cursor.pop();
512
- cursor[cursor.length - 1]++;
513
- }
514
- this.dropZoneFamilies.push(family);
515
- }
516
- for (const dropZone of orphanZones.filter((zone) => zone.getFullInsertCoordinates().length === 1)) {
517
- const family = {
518
- founder: dropZone,
519
- members: [dropZone]
520
- };
521
- dropZone.setFamily(family);
522
- this.dropZoneFamilies.push(family);
523
- orphanZones.splice(orphanZones.indexOf(dropZone), 1);
524
- }
525
- if (orphanZones.length !== 0) {
526
- let orphans = "";
527
- for (const zone of orphanZones) {
528
- orphans += `${JSON.stringify(zone.getLocation())}, `;
529
- }
530
- orphans = orphans.slice(0, orphans.length - 2);
531
- throw new Error(`Some zones were not assigned to a family. The orphan zones have the following locations: ${orphans}`);
532
- }
533
- }
534
- buildInventory() {
535
- //We do this funky string array because it is faster than doing direct array comparisons
536
- const inventoryCoordinates = [];
537
- for (const dropZone of this.dropZoneArchive) {
538
- const coordinates = dropZone.getFullInsertCoordinates().join(",");
539
- if (inventoryCoordinates.includes(coordinates)) {
540
- dropZone.isRendered(false);
541
- }
542
- else {
543
- this.dropZoneInventory.push(dropZone);
544
- inventoryCoordinates.push(coordinates);
545
- dropZone.isRendered(true);
546
- }
547
- }
548
- }
549
- setActiveDropZone(dropZone) {
550
- if (this.activeDropZone !== null) {
551
- this.activeDropZone.isActive(false);
552
- }
553
- this.activeDropZone = dropZone;
554
- if (this.activeDropZone !== null &&
555
- this.dragStateService.getState() !== "droppable") {
556
- this.dragStateService.droppable();
557
- }
558
- else if (this.activeDropZone === null &&
559
- this.dragStateService.getState() === "droppable") {
560
- this.dragStateService.notDroppable();
561
- }
562
- if (this.activeDropZone !== null) {
563
- this.activeDropZone.isActive(true);
564
- }
565
- }
566
- showDropZone(dropZone, active = false) {
567
- if (this.tree === undefined) {
568
- throw new Error("DropZoneService not initialized");
569
- }
570
- if (!this.zoneIsAllowed(dropZone)) {
571
- //User settings indicate to skip this drop zone
572
- return false;
573
- }
574
- const parent = this.tree.findByCoordinates(dropZone.getLocation().parentCoordinates);
575
- if (parent === undefined) {
576
- throw new Error("Bad family member");
577
- }
578
- dropZone.isVisible(true);
579
- if (active === true) {
580
- this.setActiveDropZone(dropZone);
581
- }
582
- return true;
583
- }
584
- updateDropZones() {
585
- this.reset();
586
- this.buildInventory();
587
- this.assignFamilies();
588
- }
589
- zoneIsAllowed(dropZone) {
590
- if (this.treeOptions === undefined || this.tree === undefined) {
591
- throw new Error("dropZoneService not initialized");
592
- }
593
- const data = this.dragStateService.getData();
594
- if (data === undefined) {
595
- throw new Error("Can't get dragged node");
596
- }
597
- const parentCoordinates = dropZone.getLocation().parentCoordinates;
598
- const dropZoneParent = this.tree.findByCoordinates(parentCoordinates);
599
- if (dropZoneParent === undefined) {
600
- throw new Error("Could not get drop zone parent");
601
- }
602
- if (parentCoordinates.length > 0 &&
603
- !isNestingAllowed(this.treeOptions, dropZoneParent.data)) {
604
- return false;
605
- }
606
- const dropZoneIndex = dropZone.getLocation().insertIndex;
607
- if (dropZoneIndex === undefined) {
608
- throw new Error("Could not get drop zone index");
609
- }
610
- const draggedNode = data.branch;
611
- if (!this.treeOptions.allowDrop(draggedNode.data, dropZoneParent.data, dropZoneIndex)) {
612
- return false;
613
- }
614
- return true;
615
- }
616
- }
617
- DropZoneService.ɵfac = function DropZoneService_Factory(t) { return new (t || DropZoneService)(i0.ɵɵinject(DragStateService), i0.ɵɵinject(TreeConstructionStatus)); };
618
- DropZoneService.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: DropZoneService, factory: DropZoneService.ɵfac });
619
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DropZoneService, [{
620
- type: Injectable
621
- }], function () { return [{ type: DragStateService }, { type: TreeConstructionStatus }]; }, null); })();
90
+ class DragEndEvent {
91
+ constructor(source, endpoints) {
92
+ this._source = source;
93
+ this._oldParent = endpoints.oldParent;
94
+ this._oldIndex = endpoints.oldIndex;
95
+ this._newParent = endpoints.newParent;
96
+ this._newIndex = endpoints.newIndex;
97
+ }
98
+ type() {
99
+ return "drag end";
100
+ }
101
+ source() {
102
+ return this._source;
103
+ }
104
+ newIndex() {
105
+ return this._newIndex;
106
+ }
107
+ newParent() {
108
+ return this._newParent;
109
+ }
110
+ oldIndex() {
111
+ return this._oldIndex;
112
+ }
113
+ oldParent() {
114
+ return this._oldParent;
115
+ }
116
+ }
622
117
 
623
- class Branch {
624
- constructor(data) {
625
- this.data = data;
626
- this.children = [];
627
- this.parent = null;
628
- }
629
- isRoot() {
630
- return this.parent === null;
631
- }
632
- getParent() {
633
- return this.parent;
634
- }
635
- setParent(parent) {
636
- this.parent = parent;
637
- }
638
- findByCoordinates(relativeCoordinates) {
639
- let cursor = this;
640
- for (const index of relativeCoordinates.values()) {
641
- cursor = cursor.getChild(index);
642
- if (cursor === undefined) {
643
- return undefined;
644
- }
645
- }
646
- return cursor;
647
- }
648
- getCoordinates() {
649
- const coordinates = [];
650
- let cursor = this;
651
- while (cursor.parent !== null) {
652
- const cursorIndex = cursor.getIndex();
653
- if (cursorIndex === undefined || cursorIndex === null) {
654
- throw new Error("Could not get cursor index");
655
- }
656
- coordinates.unshift(cursorIndex);
657
- cursor = cursor.parent;
658
- }
659
- return coordinates;
660
- }
661
- getChild(index) {
662
- return this.children[index];
663
- }
664
- getChildren() {
665
- return this.children;
666
- }
667
- clearChildren() {
668
- for (const child of this.children) {
669
- child.setParent(null);
670
- }
671
- this.children.length = 0;
672
- }
673
- getIndex() {
674
- if (this.parent === null) {
675
- return null;
676
- }
677
- const index = this.parent.children.findIndex((branch) => branch === this);
678
- if (index === -1) {
679
- return undefined;
680
- }
681
- return index;
682
- }
683
- getDescendant(relativeCoordinates) {
684
- let cursor = this;
685
- for (const index of relativeCoordinates) {
686
- cursor = cursor.children[index];
687
- if (cursor === undefined) {
688
- return undefined;
689
- }
690
- }
691
- return cursor;
692
- }
693
- findDescendant(predicate) {
694
- if (predicate(this.data) === true) {
695
- return this;
696
- }
697
- if (this.children.length > 0) {
698
- for (const child of this.children) {
699
- const foundDeeper = child.findDescendant(predicate);
700
- if (foundDeeper !== undefined) {
701
- return foundDeeper;
702
- }
703
- }
704
- }
705
- return undefined;
706
- }
707
- getAncestors() {
708
- const result = [];
709
- let cursor = this;
710
- while (cursor.parent !== null) {
711
- result.push(cursor.parent);
712
- cursor = cursor.parent;
713
- }
714
- return result;
715
- }
716
- appendChild(child) {
717
- if (child.getParent() !== null) {
718
- child.remove();
719
- }
720
- child.setParent(this);
721
- this.children.push(child);
722
- return child;
723
- }
724
- insertChild(child, index) {
725
- const isOwnChild = this.children.indexOf(child);
726
- if (isOwnChild === -1) {
727
- if (child.getParent() !== null) {
728
- child.remove();
729
- }
730
- child.setParent(this);
731
- this.children.splice(index, 0, child);
732
- }
733
- else {
734
- if (index > isOwnChild) {
735
- //Insert first, then remove
736
- this.children.splice(index, 0, child);
737
- this.removeChild(isOwnChild);
738
- child.setParent(this);
739
- }
740
- else {
741
- //remove first, then insert
742
- child.remove();
743
- this.children.splice(index, 0, child);
744
- child.setParent(this);
745
- }
746
- }
747
- return child;
748
- }
749
- removeChild(index) {
750
- const target = this.children.splice(index, 1)[0];
751
- target.setParent(null);
752
- return target;
753
- }
754
- remove() {
755
- const index = this.getIndex();
756
- if (this.parent === null || index === undefined || index === null) {
757
- throw new Error("can't remove root");
758
- }
759
- return this.parent.removeChild(index);
760
- }
761
- copy() {
762
- const copy = this.copyHelper();
763
- copy.setParent(null);
764
- return copy;
765
- }
766
- copyHelper() {
767
- const copy = new Branch(this.data);
768
- for (const child of this.children) {
769
- const newChild = copy.appendChild(child.copyHelper());
770
- newChild.parent = copy;
771
- }
772
- return copy;
773
- }
118
+ class DragStartEvent {
119
+ constructor(source) {
120
+ this._source = source;
121
+ }
122
+ type() {
123
+ return "drag start";
124
+ }
125
+ source() {
126
+ return this._source;
127
+ }
774
128
  }
775
129
 
776
- const _c0$3 = function (a0, a1) { return { active: a0, related: a1 }; };
777
- function DropZoneComponent_div_0_Template(rf, ctx) { if (rf & 1) {
778
- const _r2 = i0.ɵɵgetCurrentView();
779
- i0.ɵɵelementStart(0, "div", 1);
780
- i0.ɵɵlistener("dragenter", function DropZoneComponent_div_0_Template_div_dragenter_0_listener() { i0.ɵɵrestoreView(_r2); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.dragenterHandler()); });
781
- i0.ɵɵelement(1, "div", 2);
782
- i0.ɵɵelementEnd();
783
- } if (rf & 2) {
784
- const ctx_r0 = i0.ɵɵnextContext();
785
- i0.ɵɵadvance(1);
786
- i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction2(1, _c0$3, ctx_r0.active, !ctx_r0.active));
787
- } }
788
- class DropZoneComponent {
789
- constructor(dropZoneService, changeDetectorRef) {
790
- this.dropZoneService = dropZoneService;
791
- this.changeDetectorRef = changeDetectorRef;
792
- this.visible = false;
793
- this.active = false;
794
- }
795
- dragenterHandler() {
796
- if (this.dropZone === undefined || this.active === true) {
797
- return;
798
- }
799
- this.dropZoneService.swapActiveDropZone(this.dropZone);
800
- }
801
- ngOnInit() {
802
- if (this.dropZone === undefined) {
803
- throw new Error("No drop zone object at component initialization");
804
- }
805
- this.commSubscription = this.dropZone
806
- .getCommChannel()
807
- .subscribe((message) => {
808
- switch (message) {
809
- case "checkVisible": {
810
- this.visible = this.dropZone?.isVisible() ?? false;
811
- break;
812
- }
813
- case "checkActive": {
814
- this.active = this.dropZone?.isActive() ?? false;
815
- break;
816
- }
817
- case "checkRendered": {
818
- //Do nothing. This message is for other subscribers
819
- break;
820
- }
821
- default: {
822
- throw new Error("unhandled comm message");
823
- }
824
- }
825
- this.changeDetectorRef.detectChanges();
826
- });
827
- }
828
- ngOnDestroy() {
829
- if (this.commSubscription !== undefined) {
830
- this.commSubscription.unsubscribe();
831
- }
832
- }
833
- }
834
- DropZoneComponent.ɵfac = function DropZoneComponent_Factory(t) { return new (t || DropZoneComponent)(i0.ɵɵdirectiveInject(DropZoneService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
835
- DropZoneComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DropZoneComponent, selectors: [["drop-zone"]], inputs: { dropZone: "dropZone" }, decls: 1, vars: 1, consts: [["class", "dropZoneContainer", 3, "dragenter", 4, "ngIf"], [1, "dropZoneContainer", 3, "dragenter"], [3, "ngClass"]], template: function DropZoneComponent_Template(rf, ctx) { if (rf & 1) {
836
- i0.ɵɵtemplate(0, DropZoneComponent_div_0_Template, 2, 4, "div", 0);
837
- } if (rf & 2) {
838
- i0.ɵɵproperty("ngIf", ctx.visible);
839
- } }, dependencies: [i2.NgClass, i2.NgIf], styles: [".active[_ngcontent-%COMP%]{margin:10px 0;width:calc(100% - 20px);height:105px;border-radius:5px;background:#f0f9ff;border:2px dashed #bed2db;box-sizing:border-box}.related[_ngcontent-%COMP%]{margin:10px 0;width:calc(100% - 20px);height:30px;border-radius:5px;background:#fff6f0;border:1px dashed #dbccbe;box-sizing:border-box;opacity:.8}.dropZoneContainer[_ngcontent-%COMP%]{position:relative}"], changeDetection: 0 });
840
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DropZoneComponent, [{
841
- type: Component,
842
- args: [{ selector: "drop-zone", changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"dropZoneContainer\" (dragenter)=\"dragenterHandler()\" *ngIf=\"visible\">\n <div [ngClass]=\"{ active: active, related: !active }\"></div>\n</div>\n", styles: [".active{margin:10px 0;width:calc(100% - 20px);height:105px;border-radius:5px;background:#f0f9ff;border:2px dashed #bed2db;box-sizing:border-box}.related{margin:10px 0;width:calc(100% - 20px);height:30px;border-radius:5px;background:#fff6f0;border:1px dashed #dbccbe;box-sizing:border-box;opacity:.8}.dropZoneContainer{position:relative}\n"] }]
843
- }], function () { return [{ type: DropZoneService }, { type: i0.ChangeDetectorRef }]; }, { dropZone: [{
844
- type: Input
845
- }] }); })();
130
+ function assert(condition) {
131
+ if (condition) {
132
+ return;
133
+ }
134
+ throw new Error("Assertion Failed!");
135
+ }
846
136
 
847
- const _c0$2 = ["children"];
848
- function LimbleTreeBranchComponent_drop_zone_1_Template(rf, ctx) { if (rf & 1) {
849
- i0.ɵɵelement(0, "drop-zone", 3);
850
- } if (rf & 2) {
851
- const ctx_r0 = i0.ɵɵnextContext();
852
- i0.ɵɵproperty("dropZone", ctx_r0.dropZoneInside);
853
- } }
854
- function LimbleTreeBranchComponent_ng_template_3_Template(rf, ctx) { }
855
- class LimbleTreeBranchComponent {
856
- constructor(treeService, changeDetectorRef, dropZoneService, treeConstructionStatus, ngZone) {
857
- this.treeService = treeService;
858
- this.changeDetectorRef = changeDetectorRef;
859
- this.dropZoneService = dropZoneService;
860
- this.treeConstructionStatus = treeConstructionStatus;
861
- this.ngZone = ngZone;
862
- this.treeConstructionStatus.constructing();
863
- this.indent = this.treeService.treeOptions?.indent;
864
- this.renderDropZoneInside = false;
865
- }
866
- ngOnInit() {
867
- this.addDropZoneInside();
868
- this.ngZone.runOutsideAngular(() => {
869
- if (this.dropZoneInside === undefined) {
870
- throw new Error("drop zone inside is not defined");
871
- }
872
- this.dropZoneInside
873
- .getCommChannel()
874
- .pipe(filter((message) => message === "checkRendered"))
875
- .subscribe(() => {
876
- if (this.dropZoneInside === undefined ||
877
- this.branch === undefined) {
878
- throw new Error("Zones not registered");
879
- }
880
- if (isNestingAllowed(this.treeService.treeOptions, this.branch.data)) {
881
- this.renderDropZoneInside = this.dropZoneInside.isRendered();
882
- }
883
- });
884
- });
885
- }
886
- ngAfterViewInit() {
887
- this.reRender();
888
- this.setDropZoneHost();
889
- this.treeConstructionStatus.doneConstructing();
890
- this.changeDetectorRef.detectChanges();
891
- }
892
- ngOnDestroy() {
893
- if (this.dropZoneInside !== undefined) {
894
- this.dropZoneService.removeDropZone(this.dropZoneInside);
895
- }
896
- this.treeService.cleanupSignal$.next(true);
897
- }
898
- addDropZoneInside() {
899
- if (this.branch === undefined) {
900
- throw new Error("failed to register drop zone inside");
901
- }
902
- this.dropZoneInside = new DropZone([...this.branch.getCoordinates()], 0);
903
- this.dropZoneService.addDropZone(this.dropZoneInside);
904
- }
905
- reRender() {
906
- if (this.children === undefined || this.branch === undefined) {
907
- throw new Error("Failed to render limble tree branch");
908
- }
909
- this.treeService.renderBranch(this.children, this.branch);
910
- }
911
- setDropZoneHost() {
912
- if (this.children === undefined || this.dropZoneInside === undefined) {
913
- throw new Error("Failed to add drop zone host");
914
- }
915
- this.dropZoneInside.setHost(this.children);
916
- }
917
- }
918
- LimbleTreeBranchComponent.ɵfac = function LimbleTreeBranchComponent_Factory(t) { return new (t || LimbleTreeBranchComponent)(i0.ɵɵdirectiveInject(TreeService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(DropZoneService), i0.ɵɵdirectiveInject(TreeConstructionStatus), i0.ɵɵdirectiveInject(i0.NgZone)); };
919
- LimbleTreeBranchComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: LimbleTreeBranchComponent, selectors: [["limble-tree-branch"]], viewQuery: function LimbleTreeBranchComponent_Query(rf, ctx) { if (rf & 1) {
920
- i0.ɵɵviewQuery(_c0$2, 5, ViewContainerRef);
921
- } if (rf & 2) {
922
- let _t;
923
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.children = _t.first);
924
- } }, inputs: { branch: "branch" }, decls: 5, vars: 4, consts: [[3, "dropZone", 4, "ngIf"], [1, "limble-child-nodes", 3, "hidden"], ["children", ""], [3, "dropZone"]], template: function LimbleTreeBranchComponent_Template(rf, ctx) { if (rf & 1) {
925
- i0.ɵɵelementStart(0, "div");
926
- i0.ɵɵtemplate(1, LimbleTreeBranchComponent_drop_zone_1_Template, 1, 1, "drop-zone", 0);
927
- i0.ɵɵelementStart(2, "span", 1);
928
- i0.ɵɵtemplate(3, LimbleTreeBranchComponent_ng_template_3_Template, 0, 0, "ng-template", null, 2, i0.ɵɵtemplateRefExtractor);
929
- i0.ɵɵelementEnd()();
930
- } if (rf & 2) {
931
- i0.ɵɵstyleProp("margin-left", ctx.indent, "px");
932
- i0.ɵɵadvance(1);
933
- i0.ɵɵproperty("ngIf", ctx.renderDropZoneInside);
934
- i0.ɵɵadvance(1);
935
- i0.ɵɵproperty("hidden", ctx.branch && ctx.branch.data.collapsed);
936
- } }, dependencies: [i2.NgIf, DropZoneComponent] });
937
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(LimbleTreeBranchComponent, [{
938
- type: Component,
939
- args: [{ selector: "limble-tree-branch", template: "<div [style.marginLeft.px]=\"indent\">\n <drop-zone\n *ngIf=\"renderDropZoneInside\"\n [dropZone]=\"dropZoneInside\"\n ></drop-zone>\n <span [hidden]=\"branch && branch.data.collapsed\" class=\"limble-child-nodes\">\n <ng-template #children></ng-template>\n </span>\n</div>\n" }]
940
- }], function () { return [{ type: TreeService }, { type: i0.ChangeDetectorRef }, { type: DropZoneService }, { type: TreeConstructionStatus }, { type: i0.NgZone }]; }, { branch: [{
941
- type: Input
942
- }], children: [{
943
- type: ViewChild,
944
- args: ["children", { read: ViewContainerRef }]
945
- }] }); })();
137
+ var DragStates;
138
+ (function (DragStates) {
139
+ DragStates[DragStates["Idle"] = 0] = "Idle";
140
+ DragStates[DragStates["Starting"] = 1] = "Starting";
141
+ DragStates[DragStates["Dragging"] = 2] = "Dragging";
142
+ DragStates[DragStates["Dropped"] = 3] = "Dropped";
143
+ })(DragStates || (DragStates = {}));
144
+ class DragState {
145
+ constructor() {
146
+ this._state = DragStates.Idle;
147
+ this.state$ = new BehaviorSubject(DragStates.Idle);
148
+ this.state$.subscribe((state) => {
149
+ this._state = state;
150
+ });
151
+ this.dragData = undefined;
152
+ }
153
+ getDragData() {
154
+ return this.dragData;
155
+ }
156
+ starting(treeBranch) {
157
+ assert(this._state === DragStates.Idle);
158
+ this.dragData = treeBranch;
159
+ this.state$.next(DragStates.Starting);
160
+ }
161
+ dragging() {
162
+ assert(this._state === DragStates.Starting);
163
+ this.state$.next(DragStates.Dragging);
164
+ }
165
+ dropped() {
166
+ assert(this._state === DragStates.Dragging);
167
+ this.state$.next(DragStates.Dropped);
168
+ }
169
+ restart() {
170
+ this.dragData = undefined;
171
+ this.state$.next(DragStates.Idle);
172
+ }
173
+ events() {
174
+ return this.state$;
175
+ }
176
+ state() {
177
+ return this._state;
178
+ }
179
+ }
180
+ const dragState = new DragState();
946
181
 
947
- class ComponentCreatorService {
948
- constructor(factoryResolver) {
949
- this.factoryResolver = factoryResolver;
950
- this.factoryResolver = factoryResolver;
951
- }
952
- appendComponent(component, viewContainerRef, index = undefined) {
953
- const componentFactory = this.factoryResolver.resolveComponentFactory(component);
954
- const componentRef = viewContainerRef.createComponent(componentFactory, index);
955
- return componentRef;
956
- }
957
- }
958
- ComponentCreatorService.ɵfac = function ComponentCreatorService_Factory(t) { return new (t || ComponentCreatorService)(i0.ɵɵinject(i0.ComponentFactoryResolver)); };
959
- ComponentCreatorService.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: ComponentCreatorService, factory: ComponentCreatorService.ɵfac });
960
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ComponentCreatorService, [{
961
- type: Injectable
962
- }], function () { return [{ type: i0.ComponentFactoryResolver }]; }, null); })();
182
+ class DropEvent {
183
+ constructor(source, parent, index) {
184
+ this._source = source;
185
+ this._parent = parent;
186
+ this._index = index;
187
+ }
188
+ type() {
189
+ return "drag end";
190
+ }
191
+ source() {
192
+ return this._source;
193
+ }
194
+ index() {
195
+ return this._index;
196
+ }
197
+ parent() {
198
+ return this._parent;
199
+ }
200
+ }
963
201
 
964
- function getScrollParent(element) {
965
- const regex = /(auto|scroll)/;
966
- const parents = (_node, parentList) => {
967
- if (_node === null || _node.parentNode === null) {
968
- return parentList;
969
- }
970
- return parents(_node.parentElement, parentList.concat([_node]));
971
- };
972
- const style = (_node, prop) => getComputedStyle(_node, null).getPropertyValue(prop);
973
- const overflow = (_node) => style(_node, "overflow") +
974
- style(_node, "overflow-y") +
975
- style(_node, "overflow-x");
976
- const scroll = (_node) => regex.test(overflow(_node));
977
- const parentList = parents(element.parentElement, []);
978
- for (const parent of parentList) {
979
- if (scroll(parent)) {
980
- return parent;
981
- }
982
- }
983
- return (document.scrollingElement ?? document.documentElement);
984
- }
985
- class GlobalEventsService {
986
- constructor(ngZone) {
987
- this.ngZone = ngZone;
988
- this.ngZone.runOutsideAngular(() => {
989
- this.globalDrag$ = fromEvent(document, "drag");
990
- });
991
- this.scrolling = false;
992
- }
993
- addScrolling() {
994
- this.ngZone.runOutsideAngular(() => {
995
- if (this.globalDragSubscription !== undefined) {
996
- return;
997
- }
998
- if (this.globalDrag$ === undefined) {
999
- throw new Error("Could not get observable");
1000
- }
1001
- let viewPortHeight;
1002
- let scrollAreaSize;
1003
- let edgeTop;
1004
- let edgeBottom;
1005
- let isInTopScrollArea;
1006
- let isInBottomScrollArea;
1007
- let timer;
1008
- let scrollableDiv;
1009
- let relativeY;
1010
- this.globalDragSubscription = this.globalDrag$
1011
- .pipe(throttleTime(25), filter((event) => {
1012
- if (!event.target) {
1013
- return false;
1014
- }
1015
- scrollableDiv = getScrollParent(event.target);
1016
- viewPortHeight = scrollableDiv.clientHeight;
1017
- const viewPortWidth = scrollableDiv.clientWidth;
1018
- let relativeX;
1019
- if (window
1020
- .getComputedStyle(scrollableDiv)
1021
- .getPropertyValue("position")
1022
- .toLowerCase() === "fixed") {
1023
- relativeX = event.clientX;
1024
- relativeY = event.clientY;
1025
- }
1026
- else {
1027
- const boundingRect = scrollableDiv.getBoundingClientRect();
1028
- const scrollableDivAncestor = getScrollParent(scrollableDiv);
1029
- relativeX =
1030
- event.clientX -
1031
- (boundingRect.left + scrollableDivAncestor.scrollLeft);
1032
- relativeY =
1033
- event.clientY -
1034
- (boundingRect.top + scrollableDivAncestor.scrollTop);
1035
- }
1036
- if (relativeX < 0 ||
1037
- relativeX > viewPortWidth ||
1038
- relativeY < 0 ||
1039
- relativeY > viewPortHeight) {
1040
- //Outside of scrollable div
1041
- return false;
1042
- }
1043
- scrollAreaSize = Math.max(viewPortHeight * 0.1, 100);
1044
- edgeTop = scrollAreaSize;
1045
- edgeBottom = viewPortHeight - scrollAreaSize;
1046
- isInTopScrollArea = relativeY < edgeTop;
1047
- isInBottomScrollArea = relativeY > edgeBottom;
1048
- return isInTopScrollArea || isInBottomScrollArea;
1049
- }))
1050
- .subscribe(() => {
1051
- if (scrollableDiv === null) {
1052
- return;
1053
- }
1054
- const height = scrollableDiv.scrollHeight;
1055
- const maxScrollY = height - viewPortHeight;
1056
- const currentScrollY = scrollableDiv.scrollTop;
1057
- const canScrollUp = currentScrollY > 0;
1058
- const canScrollDown = currentScrollY < maxScrollY;
1059
- let nextScrollY;
1060
- const maxStep = 75;
1061
- if (isInTopScrollArea && canScrollUp) {
1062
- const intensity = (edgeTop - relativeY) / scrollAreaSize;
1063
- nextScrollY = currentScrollY - maxStep * intensity;
1064
- }
1065
- else if (isInBottomScrollArea && canScrollDown) {
1066
- const intensity = (relativeY - edgeBottom) / scrollAreaSize;
1067
- nextScrollY = currentScrollY + maxStep * intensity;
1068
- }
1069
- else {
1070
- return;
1071
- }
1072
- nextScrollY = Math.max(0, Math.min(maxScrollY, nextScrollY));
1073
- if (nextScrollY !== currentScrollY) {
1074
- scrollableDiv.scrollTo({ top: nextScrollY });
1075
- this.scrolling = true;
1076
- clearTimeout(timer);
1077
- timer = setTimeout(() => {
1078
- this.scrolling = false;
1079
- }, 100);
1080
- }
1081
- });
1082
- });
1083
- }
1084
- removeScrolling() {
1085
- if (this.globalDragSubscription !== undefined) {
1086
- this.globalDragSubscription.unsubscribe();
1087
- }
1088
- }
1089
- }
1090
- GlobalEventsService.ɵfac = function GlobalEventsService_Factory(t) { return new (t || GlobalEventsService)(i0.ɵɵinject(i0.NgZone)); };
1091
- GlobalEventsService.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: GlobalEventsService, factory: GlobalEventsService.ɵfac });
1092
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(GlobalEventsService, [{
1093
- type: Injectable
1094
- }], function () { return [{ type: i0.NgZone }]; }, null); })();
202
+ class DragAndDrop {
203
+ constructor() {
204
+ this.dragAborted$ = new Subject();
205
+ }
206
+ dragStart(treeBranch, event) {
207
+ if (!this.draggingAllowed(treeBranch)) {
208
+ event.preventDefault();
209
+ return;
210
+ }
211
+ treeBranch.dispatch(new DragStartEvent(treeBranch));
212
+ this.setDragEffects(treeBranch, event);
213
+ this.watchForDragend(treeBranch, event);
214
+ // We have to do a setTimeout because DOM changes are not allowed during a
215
+ // dragstart event.
216
+ setTimeout(() => {
217
+ dragState.starting(treeBranch);
218
+ treeBranch.prune();
219
+ dragState.dragging();
220
+ });
221
+ }
222
+ drop(parent, index) {
223
+ const treeBranch = dragState.getDragData();
224
+ if (treeBranch === undefined) {
225
+ throw new TreeError("Cannot get dragged branch");
226
+ }
227
+ this.graftDraggedBranch(treeBranch, parent, index);
228
+ treeBranch.dispatch(new DropEvent(treeBranch, parent, index));
229
+ }
230
+ getDragImageOffsets(event, element) {
231
+ const bounds = element.getBoundingClientRect();
232
+ const xOffset = event.clientX - bounds.left;
233
+ const yOffset = event.clientY - bounds.top;
234
+ return [xOffset, yOffset];
235
+ }
236
+ setDragEffects(treeBranch, event) {
237
+ const dataTransfer = event.dataTransfer;
238
+ assert(dataTransfer instanceof DataTransfer);
239
+ const nativeElement = treeBranch.getNativeElement();
240
+ const [xOffset, yOffset] = this.getDragImageOffsets(event, nativeElement);
241
+ dataTransfer.setDragImage(nativeElement, xOffset, yOffset);
242
+ }
243
+ watchForDragend(treeBranch, event) {
244
+ const oldParent = treeBranch.parent();
245
+ const oldIndex = treeBranch.index();
246
+ assert(oldParent !== undefined && oldIndex !== undefined);
247
+ event.target?.addEventListener("dragend", (dragend) => {
248
+ if (dragState.state() !== DragStates.Dropped) {
249
+ //The drag ended but a drop never occurred, so put the dragged branch back where it started.
250
+ this.dragAborted$.next(dragend);
251
+ this.graftDraggedBranch(treeBranch, oldParent, oldIndex);
252
+ }
253
+ dragState.restart();
254
+ const newParent = treeBranch.parent();
255
+ assert(newParent !== undefined);
256
+ const newIndex = treeBranch.index();
257
+ assert(newIndex !== undefined);
258
+ treeBranch.dispatch(new DragEndEvent(treeBranch, {
259
+ oldParent,
260
+ oldIndex,
261
+ newParent,
262
+ newIndex
263
+ }));
264
+ }, { once: true });
265
+ }
266
+ draggingAllowed(treeBranch) {
267
+ const allowDragging = config.getConfig(treeBranch.root())?.dragAndDrop?.allowDragging ??
268
+ (() => true);
269
+ return allowDragging(treeBranch);
270
+ }
271
+ graftDraggedBranch(treeBranch, parent, index) {
272
+ treeBranch.graftTo(parent, index);
273
+ treeBranch.getNativeElement().style.display = "block";
274
+ dragState.dropped();
275
+ }
276
+ }
277
+ const dragAndDrop = new DragAndDrop();
1095
278
 
1096
- const _c0$1 = ["nodeHost"];
1097
- const _c1 = ["draggableDiv"];
1098
- const _c2 = ["nodeHostContainer"];
1099
- const _c3 = ["innerBranch"];
1100
- function LimbleTreeNodeComponent_drop_zone_0_Template(rf, ctx) { if (rf & 1) {
1101
- i0.ɵɵelement(0, "drop-zone", 7);
1102
- } if (rf & 2) {
1103
- const ctx_r0 = i0.ɵɵnextContext();
1104
- i0.ɵɵproperty("dropZone", ctx_r0.dropZoneAbove);
1105
- } }
1106
- function LimbleTreeNodeComponent_ng_template_5_Template(rf, ctx) { }
1107
- function LimbleTreeNodeComponent_limble_tree_branch_7_Template(rf, ctx) { if (rf & 1) {
1108
- i0.ɵɵelement(0, "limble-tree-branch", 8, 9);
1109
- } if (rf & 2) {
1110
- const ctx_r5 = i0.ɵɵnextContext();
1111
- i0.ɵɵproperty("branch", ctx_r5.branch);
1112
- } }
1113
- function LimbleTreeNodeComponent_drop_zone_8_Template(rf, ctx) { if (rf & 1) {
1114
- i0.ɵɵelement(0, "drop-zone", 7);
1115
- } if (rf & 2) {
1116
- const ctx_r6 = i0.ɵɵnextContext();
1117
- i0.ɵɵproperty("dropZone", ctx_r6.dropZoneBelow);
1118
- } }
1119
- class LimbleTreeNodeComponent {
1120
- constructor(componentCreatorService, changeDetectorRef, dragStateService, dropZoneService, treeService, globalEventsService, ngZone, treeConstructionStatus) {
1121
- this.componentCreatorService = componentCreatorService;
1122
- this.changeDetectorRef = changeDetectorRef;
1123
- this.dragStateService = dragStateService;
1124
- this.dropZoneService = dropZoneService;
1125
- this.treeService = treeService;
1126
- this.globalEventsService = globalEventsService;
1127
- this.ngZone = ngZone;
1128
- this.treeConstructionStatus = treeConstructionStatus;
1129
- this.treeConstructionStatus.constructing();
1130
- if (this.treeService.treeOptions !== undefined &&
1131
- this.treeService.treeOptions.listMode !== true) {
1132
- this.renderInnerBranch = true;
1133
- }
1134
- else {
1135
- this.renderInnerBranch = false;
1136
- }
1137
- this.renderDropZoneBelow = false;
1138
- this.renderDropZoneAbove = false;
1139
- this.treeChangeSubscription = this.treeService.changes$
1140
- .pipe(
1141
- //The first one is the initial tree render, which we can ignore
1142
- skip(1))
1143
- .subscribe(() => {
1144
- this.treeChangeHandler();
1145
- });
1146
- }
1147
- treeChangeHandler() {
1148
- if (this.branch !== undefined &&
1149
- this.currentBranchCoordinates !== undefined &&
1150
- !arraysAreEqual(this.branch.getCoordinates(), this.currentBranchCoordinates)) {
1151
- this.updateDropZones();
1152
- }
1153
- }
1154
- ngOnInit() {
1155
- if (this.treeService.treeOptions?.allowDragging === false) {
1156
- return;
1157
- }
1158
- const parent = this.branch?.getParent();
1159
- if (parent?.data !== null) {
1160
- const parentData = parent?.data;
1161
- if (!isNestingAllowed(this.treeService.treeOptions, parentData)) {
1162
- this.renderInnerBranch = false;
1163
- return;
1164
- }
1165
- }
1166
- this.registerDropZones();
1167
- this.currentBranchCoordinates = this.branch?.getCoordinates();
1168
- this.ngZone.runOutsideAngular(() => {
1169
- if (this.dropZoneAbove === undefined ||
1170
- this.dropZoneBelow === undefined) {
1171
- throw new Error("Zones not registered");
1172
- }
1173
- merge(this.dropZoneAbove.getCommChannel(), this.dropZoneBelow.getCommChannel())
1174
- .pipe(filter((message) => message === "checkRendered"))
1175
- .subscribe(() => {
1176
- if (this.dropZoneAbove === undefined ||
1177
- this.dropZoneBelow === undefined ||
1178
- this.branch === undefined) {
1179
- throw new Error("Zones not registered");
1180
- }
1181
- this.renderDropZoneAbove = this.dropZoneAbove.isRendered();
1182
- this.renderDropZoneBelow = this.dropZoneBelow.isRendered();
1183
- });
1184
- });
1185
- }
1186
- ngAfterViewInit() {
1187
- this.renderNode();
1188
- this.setDropZoneHosts();
1189
- this.checkForHandle();
1190
- this.treeConstructionStatus.doneConstructing();
1191
- this.changeDetectorRef.detectChanges();
1192
- }
1193
- ngOnDestroy() {
1194
- this.treeChangeSubscription.unsubscribe();
1195
- if (this.dropZoneAbove !== undefined) {
1196
- this.dropZoneService.removeDropZone(this.dropZoneAbove);
1197
- }
1198
- if (this.dropZoneBelow !== undefined) {
1199
- this.dropZoneService.removeDropZone(this.dropZoneBelow);
1200
- }
1201
- this.treeService.cleanupSignal$.next(true);
1202
- }
1203
- dragstartHandler(event) {
1204
- event.stopPropagation();
1205
- if (event.dataTransfer === null ||
1206
- this.branch === undefined ||
1207
- this.parentHost === undefined) {
1208
- throw new Error("failed to run dragstartHandler");
1209
- }
1210
- const draggedElement = event.target;
1211
- if (draggedElement.parentElement?.tagName !== "LIMBLE-TREE-NODE") {
1212
- //Don't drag stuff that isn't part of the tree
1213
- event.preventDefault();
1214
- return;
1215
- }
1216
- event.dataTransfer.effectAllowed = "move";
1217
- this.dragStateService.dragging(this.branch, this.parentHost);
1218
- const treeElement = draggedElement.closest("limble-tree-root");
1219
- if (treeElement === null) {
1220
- throw new Error("could not get root of tree");
1221
- }
1222
- this.ngZone.runOutsideAngular(() => {
1223
- //We have to use a setTimeout due to a bug in chrome: https://stackoverflow.com/a/20733870/8796651
1224
- setTimeout(() => {
1225
- draggedElement.classList.add("dragging");
1226
- if (this.treeService.treeData?.length === 1 &&
1227
- this.branch?.getCoordinates().length === 1) {
1228
- //We are dragging the only element in the tree, so we have to use the placeholder system
1229
- this.treeService.placeholder$.next(true);
1230
- }
1231
- });
1232
- //We use this weird subscription/timeout combo in order to avoid a strange bug where the dragleave event
1233
- //does not fire if the user drags out of the tree too quickly. This issue would make the drop zone
1234
- //remain in the UI, potentially causing drop zones to appear in multiple trees at a time. This subscription
1235
- //waits for the dragover event on the tree to fire before rendering any drop zones. If the dragover
1236
- //event does not fire within a half second, we know the mouse left the tree too quickly, and we won't
1237
- //render the drop zone at all.
1238
- const dragSubscription = fromEvent(treeElement, "dragover")
1239
- .pipe(first())
1240
- .subscribe((dragoverEvent) => {
1241
- dragoverEvent.stopPropagation();
1242
- if (this.branch === undefined) {
1243
- throw new Error("Could not show surrounding drop zones");
1244
- }
1245
- const parent = this.branch.getParent();
1246
- let parentData;
1247
- let parentNestingAllowed = true;
1248
- if (parent?.data !== null) {
1249
- parentData = parent?.data;
1250
- parentNestingAllowed = isNestingAllowed(this.treeService.treeOptions, parentData);
1251
- }
1252
- if (this.dropZoneAbove !== undefined && parentNestingAllowed) {
1253
- this.dropZoneService.showDropZoneFamily(this.dropZoneAbove, {
1254
- joinFamilies: true
1255
- });
1256
- }
1257
- });
1258
- setTimeout(() => {
1259
- if (!dragSubscription.closed) {
1260
- dragSubscription.unsubscribe();
1261
- }
1262
- }, 500);
1263
- });
1264
- }
1265
- dragendHandler(event) {
1266
- event?.stopPropagation();
1267
- if (this.draggableDiv === undefined) {
1268
- throw new Error("could not get draggable div");
1269
- }
1270
- this.draggableDiv.nativeElement.classList.remove("dragging");
1271
- if (this.dragStateService.getState() === "captured") {
1272
- if (this.treeService.captured === false) {
1273
- //Dropped in a different tree. Remove the one in this tree
1274
- if (this.branch === undefined) {
1275
- throw new Error("could not get branch in dragendHandler");
1276
- }
1277
- this.treeService.cleanupSignal$.next(true);
1278
- this.treeService.captured = false;
1279
- }
1280
- this.dragStateService.release();
1281
- }
1282
- else {
1283
- //Wasn't dropped into a valid tree, so reset for next drag and
1284
- //don't do anything else.
1285
- this.dragStateService.release();
1286
- this.dropZoneService.clearVisibleZones();
1287
- this.dropZoneService.restoreFamilies();
1288
- if (this.treeService.treeData?.length === 1 &&
1289
- this.branch?.getCoordinates().length === 1) {
1290
- //We were dragging the only element in the tree, so we have to
1291
- //remove the placeholder that we added in the dragstart
1292
- this.treeService.placeholder$.next(false);
1293
- }
1294
- }
1295
- }
1296
- dragoverHandler(event) {
1297
- if (this.globalEventsService.scrolling === true) {
1298
- return;
1299
- }
1300
- if (this.branch === undefined) {
1301
- throw new Error("Can't get current branch during dragover event");
1302
- }
1303
- const data = this.dragStateService.getData();
1304
- if (data === undefined) {
1305
- //They might be dragging something that isn't a node. Just ignore it.
1306
- return;
1307
- }
1308
- const sourceBranch = data.branch;
1309
- //If trying to drop on self, return.
1310
- if (sourceBranch === this.branch ||
1311
- this.branch.getAncestors().includes(sourceBranch)) {
1312
- return;
1313
- }
1314
- const target = event.currentTarget.closest(".node-host-container");
1315
- if (!(target instanceof HTMLElement)) {
1316
- throw new Error("Failed to find node host container while dragging");
1317
- }
1318
- let topLine;
1319
- let bottomLine;
1320
- if (this.innerBranch?.renderDropZoneInside === false) {
1321
- topLine = target.offsetHeight / 2 - 6;
1322
- bottomLine = topLine;
1323
- }
1324
- else {
1325
- topLine = target.offsetHeight / 3 - 3; //an imaginary line 1/3 of the way down from the top of the element;
1326
- bottomLine = topLine * 2; //an imaginary line 1/3 of the way up from the bottom of the element;
1327
- }
1328
- const parent = this.branch.getParent();
1329
- let parentData;
1330
- let parentNestingAllowed = true;
1331
- if (parent?.data !== null) {
1332
- parentData = parent?.data;
1333
- parentNestingAllowed = isNestingAllowed(this.treeService.treeOptions, parentData);
1334
- }
1335
- /** The y-coordinates of the mouse in relation to the node it is hovering over */
1336
- const offsetY = event.clientY - target.getBoundingClientRect().top;
1337
- const activeDropZone = this.dropZoneService.getActiveDropZone();
1338
- if (offsetY < topLine &&
1339
- this.dropZoneAbove !== undefined &&
1340
- parentNestingAllowed &&
1341
- (activeDropZone === null ||
1342
- !DropZone.dropZoneLocationsAreEqual(activeDropZone, this.dropZoneAbove))) {
1343
- const index = this.branch.getIndex();
1344
- if (index === undefined || index === null) {
1345
- throw new Error("can't get branch index");
1346
- }
1347
- this.dropZoneService.showDropZoneFamily(this.dropZoneAbove, {
1348
- activateLowestInsteadOfFounder: true
1349
- });
1350
- if (suddenTreeExit(event)) {
1351
- this.dropZoneService.clearVisibleZones();
1352
- }
1353
- }
1354
- else if (offsetY < bottomLine &&
1355
- offsetY > topLine &&
1356
- this.innerBranch?.renderDropZoneInside === true &&
1357
- this.innerBranch?.dropZoneInside !== undefined &&
1358
- (activeDropZone === null ||
1359
- !DropZone.dropZoneLocationsAreEqual(activeDropZone, this.innerBranch.dropZoneInside))) {
1360
- this.dropZoneService.showDropZoneFamily(this.innerBranch.dropZoneInside);
1361
- if (suddenTreeExit(event)) {
1362
- this.dropZoneService.clearVisibleZones();
1363
- }
1364
- }
1365
- else if (offsetY >= bottomLine &&
1366
- this.dropZoneBelow !== undefined &&
1367
- parentNestingAllowed &&
1368
- (activeDropZone === null ||
1369
- !DropZone.dropZoneLocationsAreEqual(activeDropZone, this.dropZoneBelow)) &&
1370
- this.branch.getChildren().length === 0) {
1371
- const index = this.branch.getIndex();
1372
- if (index === undefined || index === null) {
1373
- throw new Error("can't get branch index");
1374
- }
1375
- this.dropZoneService.showDropZoneFamily(this.dropZoneBelow);
1376
- if (suddenTreeExit(event)) {
1377
- this.dropZoneService.clearVisibleZones();
1378
- }
1379
- }
1380
- }
1381
- renderNode() {
1382
- if (this.nodeHost === undefined || this.branch === undefined) {
1383
- throw new Error("Failed to render tree node");
1384
- }
1385
- let component = this.branch.data.component;
1386
- if (component === undefined) {
1387
- component = this.treeService.treeOptions?.defaultComponent;
1388
- }
1389
- if (component === undefined) {
1390
- throw new Error("limbleTree requires a component to render");
1391
- }
1392
- const componentRef = this.componentCreatorService.appendComponent(component.class, this.nodeHost);
1393
- componentRef.instance.nodeData = this.branch.data;
1394
- for (const binding in component.bindings) {
1395
- componentRef.instance[binding] = component.bindings[binding];
1396
- }
1397
- }
1398
- registerDropZones() {
1399
- this.addDropZoneAbove();
1400
- this.addDropZoneBelow();
1401
- }
1402
- addDropZoneAbove() {
1403
- if (this.branch === undefined) {
1404
- throw new Error("failed to register drop zone above");
1405
- }
1406
- const parent = this.branch.getParent();
1407
- const currentCoordinates = this.branch.getCoordinates();
1408
- const index = currentCoordinates[currentCoordinates.length - 1];
1409
- this.dropZoneAbove = new DropZone([...(parent?.getCoordinates() ?? [])], index);
1410
- this.dropZoneService.addDropZone(this.dropZoneAbove);
1411
- }
1412
- addDropZoneBelow() {
1413
- if (this.branch === undefined) {
1414
- throw new Error("failed to register drop zone below");
1415
- }
1416
- const parent = this.branch.getParent();
1417
- const currentCoordinates = this.branch.getCoordinates();
1418
- const index = currentCoordinates[currentCoordinates.length - 1];
1419
- this.dropZoneBelow = new DropZone([...(parent?.getCoordinates() ?? [])], index + 1);
1420
- this.dropZoneService.addDropZone(this.dropZoneBelow);
1421
- }
1422
- updateDropZones() {
1423
- this.currentBranchCoordinates = this.branch?.getCoordinates();
1424
- this.updateDropZoneAbove();
1425
- this.updateDropZoneBelow();
1426
- this.updateDropZoneInside();
1427
- this.setDropZoneHosts();
1428
- }
1429
- updateDropZoneAbove() {
1430
- if (this.branch === undefined || this.dropZoneAbove === undefined) {
1431
- throw new Error("failed to update drop zone above");
1432
- }
1433
- const parent = this.branch.getParent();
1434
- const currentCoordinates = this.branch.getCoordinates();
1435
- const index = currentCoordinates[currentCoordinates.length - 1];
1436
- const location = this.dropZoneAbove.getLocation();
1437
- location.setParentCoordinates([...(parent?.getCoordinates() ?? [])]);
1438
- location.setInsertIndex(index);
1439
- }
1440
- updateDropZoneBelow() {
1441
- if (this.branch === undefined || this.dropZoneBelow === undefined) {
1442
- throw new Error("failed to update drop zone below");
1443
- }
1444
- const parent = this.branch.getParent();
1445
- const currentCoordinates = this.branch.getCoordinates();
1446
- const index = currentCoordinates[currentCoordinates.length - 1];
1447
- const location = this.dropZoneBelow.getLocation();
1448
- location.setParentCoordinates([...(parent?.getCoordinates() ?? [])]);
1449
- location.setInsertIndex(index + 1);
1450
- }
1451
- updateDropZoneInside() {
1452
- if (this.innerBranch?.dropZoneInside === undefined) {
1453
- return;
1454
- }
1455
- if (this.branch === undefined) {
1456
- throw new Error("failed to update drop zone inside");
1457
- }
1458
- const location = this.innerBranch.dropZoneInside.getLocation();
1459
- location.setParentCoordinates([...this.branch.getCoordinates()]);
1460
- }
1461
- setDropZoneHosts() {
1462
- if (this.dropZoneAbove === undefined ||
1463
- this.dropZoneBelow === undefined) {
1464
- return;
1465
- }
1466
- this.dropZoneAbove.setHost(this.parentHost);
1467
- this.dropZoneBelow.setHost(this.parentHost);
1468
- }
1469
- checkForHandle() {
1470
- if (this.nodeHostContainer === undefined ||
1471
- this.draggableDiv === undefined) {
1472
- return;
1473
- }
1474
- const nodeHostContainerElement = this.nodeHostContainer.nativeElement;
1475
- const handle = nodeHostContainerElement.querySelector(".limble-tree-handle");
1476
- const draggableDivElement = this.draggableDiv.nativeElement;
1477
- if (!isDraggingAllowed(this.treeService.treeOptions, this.branch?.data)) {
1478
- draggableDivElement.setAttribute("draggable", "false");
1479
- }
1480
- else if (handle === null) {
1481
- draggableDivElement.setAttribute("draggable", "true");
1482
- }
1483
- else {
1484
- handle.addEventListener("mousedown", () => {
1485
- draggableDivElement.setAttribute("draggable", "true");
1486
- //For some reason mouseup doesn't fire after a drag, so we use this observable sequence instead.
1487
- const dragging = this.dragStateService.state$.pipe(filter((state) => state === "dragging"), first());
1488
- this.dragStateService.state$
1489
- .pipe(skipUntil(dragging), filter((state) => state === "idle"), first())
1490
- .subscribe(() => {
1491
- draggableDivElement.setAttribute("draggable", "false");
1492
- });
1493
- });
1494
- }
1495
- }
1496
- }
1497
- LimbleTreeNodeComponent.ɵfac = function LimbleTreeNodeComponent_Factory(t) { return new (t || LimbleTreeNodeComponent)(i0.ɵɵdirectiveInject(ComponentCreatorService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(DragStateService), i0.ɵɵdirectiveInject(DropZoneService), i0.ɵɵdirectiveInject(TreeService), i0.ɵɵdirectiveInject(GlobalEventsService), i0.ɵɵdirectiveInject(i0.NgZone), i0.ɵɵdirectiveInject(TreeConstructionStatus)); };
1498
- LimbleTreeNodeComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: LimbleTreeNodeComponent, selectors: [["limble-tree-node"]], viewQuery: function LimbleTreeNodeComponent_Query(rf, ctx) { if (rf & 1) {
1499
- i0.ɵɵviewQuery(_c0$1, 5, ViewContainerRef);
1500
- i0.ɵɵviewQuery(_c1, 5, ElementRef);
1501
- i0.ɵɵviewQuery(_c2, 5, ElementRef);
1502
- i0.ɵɵviewQuery(_c3, 5, LimbleTreeBranchComponent);
1503
- } if (rf & 2) {
1504
- let _t;
1505
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.nodeHost = _t.first);
1506
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.draggableDiv = _t.first);
1507
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.nodeHostContainer = _t.first);
1508
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.innerBranch = _t.first);
1509
- } }, inputs: { branch: "branch", parentHost: "parentHost" }, decls: 9, vars: 4, consts: [[3, "dropZone", 4, "ngIf"], [1, "limble-parent-container", 3, "dragstart", "dragend"], ["draggableDiv", ""], [1, "node-host-container", 3, "dragoverEventThrottle", "dragoverNoChangeDetect"], ["nodeHostContainer", ""], ["nodeHost", ""], [3, "branch", 4, "ngIf"], [3, "dropZone"], [3, "branch"], ["innerBranch", ""]], template: function LimbleTreeNodeComponent_Template(rf, ctx) { if (rf & 1) {
1510
- i0.ɵɵtemplate(0, LimbleTreeNodeComponent_drop_zone_0_Template, 1, 1, "drop-zone", 0);
1511
- i0.ɵɵelementStart(1, "div", 1, 2);
1512
- i0.ɵɵlistener("dragstart", function LimbleTreeNodeComponent_Template_div_dragstart_1_listener($event) { return ctx.dragstartHandler($event); })("dragend", function LimbleTreeNodeComponent_Template_div_dragend_1_listener($event) { return ctx.dragendHandler($event); });
1513
- i0.ɵɵelementStart(3, "div", 3, 4);
1514
- i0.ɵɵlistener("dragoverNoChangeDetect", function LimbleTreeNodeComponent_Template_div_dragoverNoChangeDetect_3_listener($event) { return ctx.dragoverHandler($event); });
1515
- i0.ɵɵtemplate(5, LimbleTreeNodeComponent_ng_template_5_Template, 0, 0, "ng-template", null, 5, i0.ɵɵtemplateRefExtractor);
1516
- i0.ɵɵelementEnd();
1517
- i0.ɵɵtemplate(7, LimbleTreeNodeComponent_limble_tree_branch_7_Template, 2, 1, "limble-tree-branch", 6);
1518
- i0.ɵɵelementEnd();
1519
- i0.ɵɵtemplate(8, LimbleTreeNodeComponent_drop_zone_8_Template, 1, 1, "drop-zone", 0);
1520
- } if (rf & 2) {
1521
- i0.ɵɵproperty("ngIf", ctx.renderDropZoneAbove === true);
1522
- i0.ɵɵadvance(3);
1523
- i0.ɵɵproperty("dragoverEventThrottle", 10);
1524
- i0.ɵɵadvance(4);
1525
- i0.ɵɵproperty("ngIf", ctx.branch !== undefined && ctx.renderInnerBranch === true);
1526
- i0.ɵɵadvance(1);
1527
- i0.ɵɵproperty("ngIf", ctx.renderDropZoneBelow === true);
1528
- } }, styles: [".dragging[_ngcontent-%COMP%]{position:absolute;transition:transform .01s;transform:translate(-9999px)}"] });
1529
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(LimbleTreeNodeComponent, [{
1530
- type: Component,
1531
- args: [{ selector: "limble-tree-node", template: "<drop-zone\n *ngIf=\"renderDropZoneAbove === true\"\n [dropZone]=\"dropZoneAbove\"\n></drop-zone>\n<div\n (dragstart)=\"dragstartHandler($event)\"\n (dragend)=\"dragendHandler($event)\"\n class=\"limble-parent-container\"\n #draggableDiv\n>\n <div\n class=\"node-host-container\"\n (dragoverNoChangeDetect)=\"dragoverHandler($event)\"\n [dragoverEventThrottle]=\"10\"\n #nodeHostContainer\n >\n <ng-template #nodeHost></ng-template>\n </div>\n <limble-tree-branch\n *ngIf=\"branch !== undefined && renderInnerBranch === true\"\n [branch]=\"branch\"\n #innerBranch\n ></limble-tree-branch>\n</div>\n<drop-zone\n *ngIf=\"renderDropZoneBelow === true\"\n [dropZone]=\"dropZoneBelow\"\n></drop-zone>\n", styles: [".dragging{position:absolute;transition:transform .01s;transform:translate(-9999px)}\n"] }]
1532
- }], function () { return [{ type: ComponentCreatorService }, { type: i0.ChangeDetectorRef }, { type: DragStateService }, { type: DropZoneService }, { type: TreeService }, { type: GlobalEventsService }, { type: i0.NgZone }, { type: TreeConstructionStatus }]; }, { branch: [{
1533
- type: Input
1534
- }], nodeHost: [{
1535
- type: ViewChild,
1536
- args: ["nodeHost", { read: ViewContainerRef }]
1537
- }], draggableDiv: [{
1538
- type: ViewChild,
1539
- args: ["draggableDiv", { read: ElementRef }]
1540
- }], nodeHostContainer: [{
1541
- type: ViewChild,
1542
- args: ["nodeHostContainer", { read: ElementRef }]
1543
- }], parentHost: [{
1544
- type: Input
1545
- }], innerBranch: [{
1546
- type: ViewChild,
1547
- args: ["innerBranch", { read: LimbleTreeBranchComponent }]
1548
- }] }); })();
279
+ class DragoverNoChangeDetectDirective {
280
+ constructor(ngZone, el) {
281
+ this.ngZone = ngZone;
282
+ this.el = el;
283
+ this.dragoverNoChangeDetect = new EventEmitter();
284
+ this.dragoverEventThrottle = 25;
285
+ }
286
+ ngOnInit() {
287
+ this.ngZone.runOutsideAngular(() => {
288
+ this.eventSubscription = fromEvent(this.el.nativeElement, "dragover")
289
+ .pipe(throttleTime(this.dragoverEventThrottle))
290
+ .subscribe(($event) => {
291
+ this.dragoverNoChangeDetect.emit($event);
292
+ });
293
+ });
294
+ }
295
+ ngOnDestroy() {
296
+ if (this.eventSubscription !== undefined) {
297
+ this.eventSubscription.unsubscribe();
298
+ }
299
+ }
300
+ }
301
+ DragoverNoChangeDetectDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: DragoverNoChangeDetectDirective, deps: [{ token: i0.NgZone }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
302
+ DragoverNoChangeDetectDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.12", type: DragoverNoChangeDetectDirective, isStandalone: true, selector: "[dragoverNoChangeDetect]", inputs: { dragoverEventThrottle: "dragoverEventThrottle" }, outputs: { dragoverNoChangeDetect: "dragoverNoChangeDetect" }, ngImport: i0 });
303
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: DragoverNoChangeDetectDirective, decorators: [{
304
+ type: Directive,
305
+ args: [{
306
+ standalone: true,
307
+ selector: "[dragoverNoChangeDetect]"
308
+ }]
309
+ }], ctorParameters: function () { return [{ type: i0.NgZone }, { type: i0.ElementRef }]; }, propDecorators: { dragoverEventThrottle: [{
310
+ type: Input
311
+ }], dragoverNoChangeDetect: [{
312
+ type: Output
313
+ }] } });
1549
314
 
1550
- /** The default value for the `indent` option */
1551
- const INDENT = 45;
1552
- class TreeService {
1553
- constructor(componentCreatorService, dropZoneService, dragStateService, treeConstructionStatus) {
1554
- this.componentCreatorService = componentCreatorService;
1555
- this.dropZoneService = dropZoneService;
1556
- this.dragStateService = dragStateService;
1557
- this.treeConstructionStatus = treeConstructionStatus;
1558
- this.changes$ = new Subject();
1559
- this.drops$ = new Subject();
1560
- this.treeModel = new Branch(null);
1561
- this.placeholder = false;
1562
- this.captured = false;
1563
- this.cleanupSignal$ = new Subject();
1564
- let rebuild = false;
1565
- let treeIsStable = false;
1566
- const treeIsStable$ = this.treeConstructionStatus.stable$.pipe(tap((value) => {
1567
- treeIsStable = value;
1568
- }), filter((value) => value === true));
1569
- this.cleanupSignal$
1570
- .pipe(tap((value) => {
1571
- rebuild = value;
1572
- }), debounce(() => {
1573
- if (treeIsStable === true) {
1574
- //If tree is stable, continue right away
1575
- return EMPTY;
1576
- }
1577
- //If tree is not stable, wait for it to become so.
1578
- return treeIsStable$;
1579
- }),
1580
- //We use this timed debounce to throttle chained destruction of components
1581
- debounceTime(5))
1582
- .subscribe(() => {
1583
- this.cleanup(rebuild);
1584
- rebuild = false;
1585
- });
1586
- this.placeholder$ = new BehaviorSubject(false);
1587
- this.placeholder$.subscribe((value) => {
1588
- this.placeholder = value;
1589
- });
1590
- }
1591
- drop(source, targetCoordinates) {
1592
- //prep
1593
- const sourceParent = source.getParent();
1594
- if (sourceParent === null) {
1595
- throw new Error("can't drop root of tree");
1596
- }
1597
- const sourceIndex = source.getIndex();
1598
- if (sourceIndex === undefined || sourceIndex === null) {
1599
- throw new Error("Cannot move the hidden root node");
1600
- }
1601
- let targetParentCoordinates;
1602
- let newIndex;
1603
- if (this.placeholder === true) {
1604
- targetParentCoordinates = [];
1605
- newIndex = 0;
1606
- }
1607
- else {
1608
- targetParentCoordinates = [...targetCoordinates];
1609
- newIndex = targetParentCoordinates.pop();
1610
- }
1611
- if (newIndex === undefined) {
1612
- throw new Error("target coordinates are empty");
1613
- }
1614
- const targetParent = this.treeModel.getDescendant(targetParentCoordinates);
1615
- if (targetParent === undefined) {
1616
- throw new Error("could not get to target");
1617
- }
1618
- const target = this.dropZoneService.getDropZone(targetCoordinates);
1619
- const targetIndex = target?.getLocation().insertIndex;
1620
- const targetHost = target?.getHost();
1621
- const sourceHost = this.dragStateService.getData()?.parentContainer;
1622
- if (this.placeholder === true) {
1623
- this.placeholder$.next(false);
1624
- }
1625
- //Change the treeModel
1626
- targetParent.insertChild(source, newIndex);
1627
- //Prepare to update the view
1628
- if (targetHost === undefined ||
1629
- sourceHost === undefined ||
1630
- targetIndex === undefined) {
1631
- //Hitting this means there is a bug, but not a fatal one.
1632
- //Just render the whole tree again.
1633
- console.warn("Could not perform a precise update. Re-rendering the entire tree instead");
1634
- this.render();
1635
- this.changes$.next(null);
1636
- return;
1637
- }
1638
- //Update the view
1639
- const nodesInSource = sourceHost.length;
1640
- const componentRef = this.componentCreatorService.appendComponent(LimbleTreeNodeComponent, targetHost, newIndex);
1641
- componentRef.instance.branch = source;
1642
- componentRef.instance.parentHost = targetHost;
1643
- if (targetIndex < sourceIndex &&
1644
- sourceHost.length > nodesInSource &&
1645
- arraysAreEqual(sourceParent.getCoordinates(), targetParentCoordinates)) {
1646
- sourceHost.remove(sourceIndex + 1);
1647
- }
1648
- else {
1649
- sourceHost.remove(sourceIndex);
1650
- }
1651
- //Update the tree data
1652
- this.rebuildTreeData();
1653
- //Publish drop data
1654
- this.drops$.next({
1655
- target: source.data,
1656
- oldParent: sourceParent.data,
1657
- oldIndex: sourceIndex,
1658
- newParent: targetParent.data,
1659
- newIndex: newIndex
1660
- });
1661
- this.cleanupSignal$.next(false);
1662
- }
1663
- /** Initializes the service and renders the tree.
1664
- * @param host - The ViewContainerRef into which the tree will be rendered.
1665
- * @param data - The data array that was passed in to LimbleTreeRoot, which is
1666
- * the users' representation of the tree
1667
- * @param options - The options object that was passed in to LimbleTreeRoot
1668
- */
1669
- init(host, data, options, itemsPerPage, page) {
1670
- this.host = host;
1671
- this.originalData = data;
1672
- this.treeOptions = this.processOptions(options, itemsPerPage, page);
1673
- if (this.treeOptions.listMode === true) {
1674
- let start = this.treeOptions.itemsPerPage * (this.treeOptions.page - 1);
1675
- if (isNaN(start)) {
1676
- //This catches the case where itemsPerPage was not passed by the user,
1677
- //causing `start` to equal infinity*0, which is NaN.
1678
- start = 0;
1679
- }
1680
- const end = start + this.treeOptions.itemsPerPage;
1681
- this.treeData = this.originalData.slice(start, end);
1682
- }
1683
- else {
1684
- this.treeData = [...this.originalData];
1685
- }
1686
- this.render();
1687
- }
1688
- cleanup(rebuild = false) {
1689
- if (rebuild) {
1690
- this.rebuildTreeData();
1691
- }
1692
- if (this.treeData?.length === 0) {
1693
- //We do a full render here because it isn't actually any slower
1694
- //when there are no nodes, and it is a little more straightforward
1695
- this.render();
1696
- }
1697
- else {
1698
- this.changes$.next(null);
1699
- this.dropZoneService.update();
1700
- }
1701
- }
1702
- /** Renders the entire tree from root to leaves */
1703
- render() {
1704
- if (this.host === undefined ||
1705
- this.treeData === undefined ||
1706
- this.treeOptions === undefined) {
1707
- throw new Error("TreeModel not initialized");
1708
- }
1709
- this.treeConstructionStatus.ready(false);
1710
- this.host.clear();
1711
- this.dropZoneService.restart();
1712
- this.placeholder$.next(false);
1713
- this.treeModel = new Branch(null);
1714
- if (this.treeData.length === 0) {
1715
- //Tree is empty, but we have to to have something there so other trees' items can be dropped into it
1716
- this.placeholder$.next(true);
1717
- }
1718
- else {
1719
- for (const node of this.treeData) {
1720
- const branch = new Branch(node);
1721
- this.treeModel.appendChild(branch);
1722
- }
1723
- for (const branch of this.treeModel.getChildren()) {
1724
- const componentRef = this.componentCreatorService.appendComponent(LimbleTreeNodeComponent, this.host);
1725
- componentRef.instance.branch = branch;
1726
- componentRef.instance.parentHost = this.host;
1727
- //The LimbleTreeNodeComponent will (indirectly) call the `renderBranch` method of this service to render
1728
- //its own children
1729
- }
1730
- }
1731
- this.treeConstructionStatus.ready(true);
1732
- this.changes$.next(null);
1733
- this.dropZoneService.init(this.treeModel, this.treeOptions);
1734
- }
1735
- /** Renders a branch of the tree and all of its descendants */
1736
- renderBranch(host, branch) {
1737
- if (this.treeModel === undefined) {
1738
- throw new Error("TreeModel not initialized");
1739
- }
1740
- host.clear();
1741
- branch.clearChildren();
1742
- for (const node of branch.data?.nodes ?? []) {
1743
- const newBranch = new Branch(node);
1744
- branch.appendChild(newBranch);
1745
- const componentRef = this.componentCreatorService.appendComponent(LimbleTreeNodeComponent, host);
1746
- componentRef.instance.branch = newBranch;
1747
- componentRef.instance.parentHost = host;
1748
- //The LimbleTreeNodeComponent will (indirectly) call the `renderBranch` method of this service to render
1749
- //its own children
1750
- }
1751
- }
1752
- processOptions(options = {}, itemsPerPage = Infinity, page = 1) {
1753
- if (options.listMode === true &&
1754
- options.allowNesting !== undefined &&
1755
- options.allowNesting !== false) {
1756
- console.warn("The value of `allowNesting` will be ignored; it must be false when `listMode` is true");
1757
- }
1758
- const result = {
1759
- defaultComponent: options.defaultComponent,
1760
- indent: options.indent ?? INDENT,
1761
- allowNesting: options.listMode !== true && (options.allowNesting ?? true),
1762
- allowDragging: options.allowDragging ?? true,
1763
- allowDrop: options.allowDrop ?? (() => true),
1764
- listMode: options.listMode ?? false,
1765
- itemsPerPage: options.listMode ? itemsPerPage : undefined,
1766
- page: options.listMode ? page : undefined
1767
- };
1768
- return result;
1769
- }
1770
- rebuildTreeData() {
1771
- if (this.originalData === undefined ||
1772
- this.treeData === undefined ||
1773
- this.treeOptions === undefined ||
1774
- this.host === undefined) {
1775
- throw new Error("Tree data not initialized");
1776
- }
1777
- this.treeData = [];
1778
- for (const branch of this.treeModel.getChildren()) {
1779
- this.treeData.push(this.rebuildBranch(branch));
1780
- }
1781
- if (this.treeOptions.listMode === true &&
1782
- this.treeOptions.itemsPerPage < Infinity) {
1783
- const itemsPerPage = this.treeOptions.itemsPerPage;
1784
- const start = itemsPerPage * (this.treeOptions.page - 1);
1785
- this.originalData.splice(start, itemsPerPage, ...this.treeData);
1786
- if (this.treeData.length !== itemsPerPage) {
1787
- let action = false;
1788
- if (this.treeData.length < itemsPerPage &&
1789
- start + itemsPerPage <= this.originalData.length) {
1790
- //The current page does not have enough nodes. Add some to the view from the next page.
1791
- const count = itemsPerPage - this.treeData.length;
1792
- for (let index = itemsPerPage - 1; index < itemsPerPage + count - 1; index++) {
1793
- const branch = new Branch(this.originalData[start + index]);
1794
- this.treeModel.appendChild(branch);
1795
- const componentRef = this.componentCreatorService.appendComponent(LimbleTreeNodeComponent, this.host);
1796
- componentRef.instance.branch = branch;
1797
- componentRef.instance.parentHost = this.host;
1798
- }
1799
- action = true;
1800
- }
1801
- else if (this.treeData.length > itemsPerPage) {
1802
- //The current page has too many nodes. Remove some of them from the view.
1803
- const count = this.treeData.length - itemsPerPage;
1804
- for (let index = itemsPerPage + count - 1; index >= itemsPerPage; index--) {
1805
- this.treeModel.removeChild(index);
1806
- this.host.remove(index);
1807
- }
1808
- action = true;
1809
- }
1810
- if (action === true) {
1811
- const end = start + itemsPerPage;
1812
- this.treeData = this.originalData.slice(start, end);
1813
- }
1814
- }
1815
- }
1816
- else {
1817
- this.originalData.length = 0;
1818
- this.originalData.push(...this.treeData);
1819
- }
1820
- }
1821
- rebuildBranch(branch) {
1822
- const temp = branch.data;
1823
- temp.nodes = [];
1824
- for (const child of branch.getChildren()) {
1825
- temp.nodes.push(this.rebuildBranch(child));
1826
- }
1827
- return temp;
1828
- }
1829
- }
1830
- TreeService.ɵfac = function TreeService_Factory(t) { return new (t || TreeService)(i0.ɵɵinject(ComponentCreatorService), i0.ɵɵinject(DropZoneService), i0.ɵɵinject(DragStateService), i0.ɵɵinject(TreeConstructionStatus)); };
1831
- TreeService.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: TreeService, factory: TreeService.ɵfac });
1832
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TreeService, [{
1833
- type: Injectable
1834
- }], function () { return [{ type: ComponentCreatorService }, { type: DropZoneService }, { type: DragStateService }, { type: TreeConstructionStatus }]; }, null); })();
315
+ class DropzoneComponent {
316
+ constructor() {
317
+ this.dropped = new EventEmitter();
318
+ this.active = false;
319
+ }
320
+ dragenterHandler() {
321
+ this.active = true;
322
+ }
323
+ dragleaveHandler() {
324
+ this.active = false;
325
+ }
326
+ dragoverHandler(event) {
327
+ event.preventDefault();
328
+ event.dataTransfer.dropEffect = "move";
329
+ }
330
+ dropHandler(event) {
331
+ this.dropped.emit(event);
332
+ }
333
+ }
334
+ DropzoneComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: DropzoneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
335
+ DropzoneComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: DropzoneComponent, isStandalone: true, selector: "dropzone", inputs: { placement: "placement" }, outputs: { dropped: "dropped" }, ngImport: i0, template: "<div\n class=\"dropzone\"\n [ngClass]=\"{ active: active }\"\n (dragenter)=\"dragenterHandler()\"\n (dragleave)=\"dragleaveHandler()\"\n (dragoverNoChangeDetect)=\"dragoverHandler($event)\"\n (drop)=\"dropHandler($event)\"\n></div>\n", styles: [".dropzone{border-radius:8px;border:1px dashed #727374;background-color:#ededed;height:20px;margin:8px 0;transition:height .2s ease-out;animation:animation .2s ease-out}.dropzone.active{border-color:#289e49;border-width:2px;background-color:#d0e8d6;height:48px}@keyframes animation{0%{height:0px;opacity:0}to{height:20px;opacity:1}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: DragoverNoChangeDetectDirective, selector: "[dragoverNoChangeDetect]", inputs: ["dragoverEventThrottle"], outputs: ["dragoverNoChangeDetect"] }] });
336
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: DropzoneComponent, decorators: [{
337
+ type: Component,
338
+ args: [{ standalone: true, selector: "dropzone", imports: [CommonModule, DragoverNoChangeDetectDirective], template: "<div\n class=\"dropzone\"\n [ngClass]=\"{ active: active }\"\n (dragenter)=\"dragenterHandler()\"\n (dragleave)=\"dragleaveHandler()\"\n (dragoverNoChangeDetect)=\"dragoverHandler($event)\"\n (drop)=\"dropHandler($event)\"\n></div>\n", styles: [".dropzone{border-radius:8px;border:1px dashed #727374;background-color:#ededed;height:20px;margin:8px 0;transition:height .2s ease-out;animation:animation .2s ease-out}.dropzone.active{border-color:#289e49;border-width:2px;background-color:#d0e8d6;height:48px}@keyframes animation{0%{height:0px;opacity:0}to{height:20px;opacity:1}}\n"] }]
339
+ }], propDecorators: { placement: [{
340
+ type: Input
341
+ }], dropped: [{
342
+ type: Output
343
+ }] } });
1835
344
 
1836
- class DragoverNoChangeDetectDirective {
1837
- constructor(ngZone, el) {
1838
- this.ngZone = ngZone;
1839
- this.el = el;
1840
- this.dragoverNoChangeDetect = new EventEmitter();
1841
- this.dragoverEventThrottle = 25;
1842
- }
1843
- ngOnInit() {
1844
- this.ngZone.runOutsideAngular(() => {
1845
- this.eventSubscription = fromEvent(this.el.nativeElement, "dragover")
1846
- .pipe(throttleTime(this.dragoverEventThrottle))
1847
- .subscribe(($event) => {
1848
- this.dragoverNoChangeDetect.emit($event);
1849
- });
1850
- });
1851
- }
1852
- ngOnDestroy() {
1853
- if (this.eventSubscription !== undefined) {
1854
- this.eventSubscription.unsubscribe();
1855
- }
1856
- }
1857
- }
1858
- DragoverNoChangeDetectDirective.ɵfac = function DragoverNoChangeDetectDirective_Factory(t) { return new (t || DragoverNoChangeDetectDirective)(i0.ɵɵdirectiveInject(i0.NgZone), i0.ɵɵdirectiveInject(i0.ElementRef)); };
1859
- DragoverNoChangeDetectDirective.ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: DragoverNoChangeDetectDirective, selectors: [["", "dragoverNoChangeDetect", ""]], inputs: { dragoverEventThrottle: "dragoverEventThrottle" }, outputs: { dragoverNoChangeDetect: "dragoverNoChangeDetect" } });
1860
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DragoverNoChangeDetectDirective, [{
1861
- type: Directive,
1862
- args: [{
1863
- selector: "[dragoverNoChangeDetect]"
1864
- }]
1865
- }], function () { return [{ type: i0.NgZone }, { type: i0.ElementRef }]; }, { dragoverEventThrottle: [{
1866
- type: Input
1867
- }], dragoverNoChangeDetect: [{
1868
- type: Output
1869
- }] }); })();
345
+ class BranchComponent {
346
+ constructor(appRef) {
347
+ this.appRef = appRef;
348
+ this.branchesContainer = undefined;
349
+ this.contentContainer = undefined;
350
+ this.dropzones = undefined;
351
+ this.contentCreated = new EventEmitter();
352
+ this.showDropzones = new EventEmitter();
353
+ this.dropped = new EventEmitter();
354
+ this.showInnerDropzone = false;
355
+ this.showLateralDropzone = false;
356
+ }
357
+ ngAfterViewInit() {
358
+ assert(this.contentContainer !== undefined);
359
+ assert(this.contentToHost !== undefined);
360
+ this.hostedContent = this.contentContainer.createComponent(this.contentToHost);
361
+ this.contentCreated.emit(this.hostedContent.instance);
362
+ assert(this.dropzones !== undefined);
363
+ const inner = this.dropzones.get(0);
364
+ const lateral = this.dropzones.get(1);
365
+ assert(inner !== undefined && lateral !== undefined);
366
+ merge(inner.dropped.pipe(map(() => "inner")), lateral.dropped.pipe(map(() => "lateral"))).subscribe(this.dropped);
367
+ this.hostedContent.changeDetectorRef.detectChanges();
368
+ }
369
+ getHostedContent() {
370
+ return this.hostedContent;
371
+ }
372
+ triggerChangeDetection() {
373
+ if (!NgZone.isInAngularZone()) {
374
+ this.appRef.tick();
375
+ }
376
+ }
377
+ dragoverHandler(event) {
378
+ const elementHeight = event.currentTarget.getBoundingClientRect().height;
379
+ if (event.offsetY < elementHeight / 2) {
380
+ this.showDropzones.emit("upper");
381
+ }
382
+ else {
383
+ this.showDropzones.emit("lower");
384
+ }
385
+ }
386
+ ngOnDestroy() {
387
+ //I'm not 100% sure why, but we have to remove any reference to the
388
+ //componentRef otherwise Angular will never release it for garbage
389
+ //collection.
390
+ this.hostedContent = undefined;
391
+ }
392
+ }
393
+ BranchComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BranchComponent, deps: [{ token: i0.ApplicationRef }], target: i0.ɵɵFactoryTarget.Component });
394
+ BranchComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: BranchComponent, isStandalone: true, selector: "branch", inputs: { contentToHost: "contentToHost" }, outputs: { contentCreated: "contentCreated", showDropzones: "showDropzones", dropped: "dropped" }, viewQueries: [{ propertyName: "branchesContainer", first: true, predicate: ["branchesContainer"], descendants: true, read: ViewContainerRef }, { propertyName: "contentContainer", first: true, predicate: ["contentContainer"], descendants: true, read: ViewContainerRef }, { propertyName: "dropzones", predicate: DropzoneComponent, descendants: true }], ngImport: i0, template: "<div class=\"content\" (dragoverNoChangeDetect)=\"dragoverHandler($event)\">\n <div #contentContainer></div>\n</div>\n<div class=\"branches-container\">\n <dropzone placement=\"inner\" [hidden]=\"!showInnerDropzone\"></dropzone>\n <div #branchesContainer></div>\n</div>\n<dropzone placement=\"lateral\" [hidden]=\"!showLateralDropzone\"></dropzone>\n", styles: [".branches-container{margin-left:16px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DropzoneComponent, selector: "dropzone", inputs: ["placement"], outputs: ["dropped"] }, { kind: "directive", type: DragoverNoChangeDetectDirective, selector: "[dragoverNoChangeDetect]", inputs: ["dragoverEventThrottle"], outputs: ["dragoverNoChangeDetect"] }] });
395
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BranchComponent, decorators: [{
396
+ type: Component,
397
+ args: [{ standalone: true, selector: "branch", imports: [CommonModule, DropzoneComponent, DragoverNoChangeDetectDirective], template: "<div class=\"content\" (dragoverNoChangeDetect)=\"dragoverHandler($event)\">\n <div #contentContainer></div>\n</div>\n<div class=\"branches-container\">\n <dropzone placement=\"inner\" [hidden]=\"!showInnerDropzone\"></dropzone>\n <div #branchesContainer></div>\n</div>\n<dropzone placement=\"lateral\" [hidden]=\"!showLateralDropzone\"></dropzone>\n", styles: [".branches-container{margin-left:16px}\n"] }]
398
+ }], ctorParameters: function () { return [{ type: i0.ApplicationRef }]; }, propDecorators: { branchesContainer: [{
399
+ type: ViewChild,
400
+ args: ["branchesContainer", { read: ViewContainerRef }]
401
+ }], contentContainer: [{
402
+ type: ViewChild,
403
+ args: ["contentContainer", { read: ViewContainerRef }]
404
+ }], dropzones: [{
405
+ type: ViewChildren,
406
+ args: [DropzoneComponent]
407
+ }], contentToHost: [{
408
+ type: Input
409
+ }], contentCreated: [{
410
+ type: Output
411
+ }], showDropzones: [{
412
+ type: Output
413
+ }], dropped: [{
414
+ type: Output
415
+ }] } });
1870
416
 
1871
- class LimbleTreePlaceholderComponent {
1872
- constructor(dropZoneService, changeDetectorRef, treeService, treeConstructionStatus) {
1873
- this.dropZoneService = dropZoneService;
1874
- this.changeDetectorRef = changeDetectorRef;
1875
- this.treeService = treeService;
1876
- this.treeConstructionStatus = treeConstructionStatus;
1877
- this.treeConstructionStatus.constructing();
1878
- //This logic is very similar to what the lifecycle hooks of this component do.
1879
- //We use this subscription because sometimes we can't wait for the lifecycle hooks:
1880
- //Specifically, the drop zone registration and deregistration sometimes can't happen
1881
- //asynchronously without causing bugs. So we handle it synchronously here as well.
1882
- this.placeholderSubscription = this.treeService.placeholder$.subscribe((value) => {
1883
- if (this.dropZone === undefined) {
1884
- return;
1885
- }
1886
- this.dropZoneService.removeDropZone(this.dropZone);
1887
- if (value === true) {
1888
- this.dropZoneService.addDropZone(this.dropZone);
1889
- this.dropZone.setHost(this.treeService.host);
1890
- }
1891
- });
1892
- }
1893
- ngOnInit() {
1894
- this.dropZone = new DropZone([], 0);
1895
- this.dropZoneService.addDropZone(this.dropZone);
1896
- }
1897
- ngAfterViewInit() {
1898
- if (this.dropZone === undefined) {
1899
- throw new Error("placeholder drop zone is not defined");
1900
- }
1901
- this.dropZone.setHost(this.treeService.host);
1902
- this.treeConstructionStatus.doneConstructing();
1903
- this.changeDetectorRef.detectChanges();
1904
- }
1905
- dragoverHandler() {
1906
- if (this.dropZone === undefined) {
1907
- return;
1908
- }
1909
- this.dropZoneService.showDropZoneFamily(this.dropZone);
1910
- }
1911
- ngOnDestroy() {
1912
- this.placeholderSubscription.unsubscribe();
1913
- if (this.dropZone === undefined) {
1914
- throw new Error("could not remove placeholder drop zone");
1915
- }
1916
- this.dropZoneService.removeDropZone(this.dropZone);
1917
- }
1918
- }
1919
- LimbleTreePlaceholderComponent.ɵfac = function LimbleTreePlaceholderComponent_Factory(t) { return new (t || LimbleTreePlaceholderComponent)(i0.ɵɵdirectiveInject(DropZoneService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(TreeService), i0.ɵɵdirectiveInject(TreeConstructionStatus)); };
1920
- LimbleTreePlaceholderComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: LimbleTreePlaceholderComponent, selectors: [["limble-tree-placeholder"]], decls: 2, vars: 1, consts: [[3, "dropZone"], [1, "placeholder", 3, "dragoverNoChangeDetect"]], template: function LimbleTreePlaceholderComponent_Template(rf, ctx) { if (rf & 1) {
1921
- i0.ɵɵelement(0, "drop-zone", 0);
1922
- i0.ɵɵelementStart(1, "div", 1);
1923
- i0.ɵɵlistener("dragoverNoChangeDetect", function LimbleTreePlaceholderComponent_Template_div_dragoverNoChangeDetect_1_listener() { return ctx.dragoverHandler(); });
1924
- i0.ɵɵelementEnd();
1925
- } if (rf & 2) {
1926
- i0.ɵɵproperty("dropZone", ctx.dropZone);
1927
- } }, dependencies: [DropZoneComponent, DragoverNoChangeDetectDirective], styles: [".placeholder[_ngcontent-%COMP%]{width:100%;height:20px}"], changeDetection: 0 });
1928
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(LimbleTreePlaceholderComponent, [{
1929
- type: Component,
1930
- args: [{ selector: "limble-tree-placeholder", changeDetection: ChangeDetectionStrategy.OnPush, template: "<drop-zone [dropZone]=\"dropZone\"></drop-zone>\n<div class=\"placeholder\" (dragoverNoChangeDetect)=\"dragoverHandler()\"></div>\n", styles: [".placeholder{width:100%;height:20px}\n"] }]
1931
- }], function () { return [{ type: DropZoneService }, { type: i0.ChangeDetectorRef }, { type: TreeService }, { type: TreeConstructionStatus }]; }, null); })();
417
+ class GraftEvent {
418
+ constructor(source, relationship) {
419
+ this._source = source;
420
+ this._child = relationship.child;
421
+ this._parent = relationship.parent;
422
+ this._index = relationship.index;
423
+ }
424
+ child() {
425
+ return this._child;
426
+ }
427
+ type() {
428
+ return "graft";
429
+ }
430
+ index() {
431
+ return this._index;
432
+ }
433
+ parent() {
434
+ return this._parent;
435
+ }
436
+ source() {
437
+ return this._source;
438
+ }
439
+ }
1932
440
 
1933
- class DragleaveNoChangeDetectDirective {
1934
- constructor(ngZone, el) {
1935
- this.ngZone = ngZone;
1936
- this.el = el;
1937
- this.dragleaveNoChangeDetect = new EventEmitter();
1938
- }
1939
- ngOnInit() {
1940
- this.ngZone.runOutsideAngular(() => {
1941
- this.eventSubscription = fromEvent(this.el.nativeElement, "dragleave").subscribe(($event) => {
1942
- this.dragleaveNoChangeDetect.emit($event);
1943
- });
1944
- });
1945
- }
1946
- ngOnDestroy() {
1947
- if (this.eventSubscription !== undefined) {
1948
- this.eventSubscription.unsubscribe();
1949
- }
1950
- }
1951
- }
1952
- DragleaveNoChangeDetectDirective.ɵfac = function DragleaveNoChangeDetectDirective_Factory(t) { return new (t || DragleaveNoChangeDetectDirective)(i0.ɵɵdirectiveInject(i0.NgZone), i0.ɵɵdirectiveInject(i0.ElementRef)); };
1953
- DragleaveNoChangeDetectDirective.ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: DragleaveNoChangeDetectDirective, selectors: [["", "dragleaveNoChangeDetect", ""]], outputs: { dragleaveNoChangeDetect: "dragleaveNoChangeDetect" } });
1954
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DragleaveNoChangeDetectDirective, [{
1955
- type: Directive,
1956
- args: [{
1957
- selector: "[dragleaveNoChangeDetect]"
1958
- }]
1959
- }], function () { return [{ type: i0.NgZone }, { type: i0.ElementRef }]; }, { dragleaveNoChangeDetect: [{
1960
- type: Output
1961
- }] }); })();
441
+ class PruneEvent {
442
+ constructor(source, relationship) {
443
+ this._source = source;
444
+ this._child = relationship.child;
445
+ this._parent = relationship.parent;
446
+ this._index = relationship.index;
447
+ }
448
+ child() {
449
+ return this._child;
450
+ }
451
+ type() {
452
+ return "prune";
453
+ }
454
+ index() {
455
+ return this._index;
456
+ }
457
+ parent() {
458
+ return this._parent;
459
+ }
460
+ source() {
461
+ return this._source;
462
+ }
463
+ }
1962
464
 
1963
- const _c0 = ["host"];
1964
- function LimbleTreeRootComponent_limble_tree_placeholder_1_Template(rf, ctx) { if (rf & 1) {
1965
- i0.ɵɵelement(0, "limble-tree-placeholder");
1966
- } }
1967
- function LimbleTreeRootComponent_ng_template_2_Template(rf, ctx) { }
1968
- class LimbleTreeRootComponent {
1969
- constructor(treeService, dropZoneService, dragStateService, globalEventsService, ngZone, changeDetectorRef, el) {
1970
- this.treeService = treeService;
1971
- this.dropZoneService = dropZoneService;
1972
- this.dragStateService = dragStateService;
1973
- this.globalEventsService = globalEventsService;
1974
- this.ngZone = ngZone;
1975
- this.changeDetectorRef = changeDetectorRef;
1976
- this.el = el;
1977
- this.treeChange = new EventEmitter();
1978
- this.treeDrop = new EventEmitter();
1979
- this.changesSubscription = this.treeService.changes$.subscribe(() => {
1980
- //"In dev mode, Angular performs an additional check after each change
1981
- //detection run, to ensure the bindings haven’t changed." We use a timeout here
1982
- //to preclude the possibility of causing a binding to update in the parent
1983
- //component after change detection runs but before the additional check.
1984
- //See https://angular.io/errors/NG0100 for details.
1985
- setTimeout(() => {
1986
- this.treeChange.emit();
1987
- });
1988
- });
1989
- this.dropSubscription = this.treeService.drops$.subscribe((drop) => {
1990
- setTimeout(() => {
1991
- this.treeDrop.emit(drop);
1992
- });
1993
- });
1994
- this.placeholder = false;
1995
- this.treeService.placeholder$.subscribe((value) => {
1996
- this.placeholder = value;
1997
- if (!NgZone.isInAngularZone()) {
1998
- this.changeDetectorRef.detectChanges();
1999
- }
2000
- });
2001
- }
2002
- ngAfterViewInit() {
2003
- if (this.options?.listMode !== true &&
2004
- (this.itemsPerPage !== undefined || this.page !== undefined)) {
2005
- console.warn("pagination is only allowed in listMode; `itemsPerPage` and `page` inputs will be ignored");
2006
- }
2007
- this.update();
2008
- this.changeDetectorRef.detectChanges();
2009
- this.ngZone.runOutsideAngular(() => {
2010
- //this is for mac os - without this dragover handler drop events aren't firing correctly
2011
- this.el.nativeElement.addEventListener("dragover", (event) => {
2012
- event.preventDefault();
2013
- });
2014
- });
2015
- }
2016
- ngOnChanges() {
2017
- if (this.host !== undefined && this.data !== undefined) {
2018
- this.update();
2019
- }
2020
- }
2021
- /** Rebuild the tree */
2022
- update() {
2023
- if (this.host === undefined) {
2024
- throw new Error("Failed to render limble tree. Failure occurred at root.");
2025
- }
2026
- if (this.data === undefined) {
2027
- throw new Error(`limbleTree requires a data object`);
2028
- }
2029
- this.treeService.init(this.host, this.data, this.options, this.itemsPerPage, this.page);
2030
- //We check for firefox here because there is a bug in Firefox that causes the
2031
- //custom scrolling to break. See https://bugzilla.mozilla.org/show_bug.cgi?id=505521#c80
2032
- if (!isFirefox()) {
2033
- this.globalEventsService.addScrolling();
2034
- }
2035
- }
2036
- dragoverHandler(event) {
2037
- if (event.dataTransfer === null) {
2038
- return;
2039
- }
2040
- event.stopPropagation();
2041
- event.preventDefault();
2042
- event.dataTransfer.dropEffect = "move";
2043
- }
2044
- dragleaveHandler(event) {
2045
- const currentTarget = event.currentTarget;
2046
- const relatedTarget = event.relatedTarget;
2047
- if (!(currentTarget instanceof Node) ||
2048
- !(relatedTarget instanceof Node) ||
2049
- isElementDescendant(currentTarget, relatedTarget) !== false) {
2050
- //event came from deeper in the tree. Ignore it.
2051
- return;
2052
- }
2053
- //Mouse has left the tree, so clear the drop zones
2054
- this.dropZoneService.clearVisibleZones();
2055
- this.changeDetectorRef.detectChanges();
2056
- }
2057
- dropHandler(event) {
2058
- event.stopPropagation();
2059
- if (this.dragStateService.getState() !== "droppable") {
2060
- return;
2061
- }
2062
- const sourceBranch = this.dragStateService.capture();
2063
- if (sourceBranch === undefined) {
2064
- throw new Error("failed to get current branch in dragendHandler");
2065
- }
2066
- const dropZone = this.dropZoneService.getActiveDropZone();
2067
- if (dropZone === null) {
2068
- throw new Error("failed to get active drop zone at drop handler");
2069
- }
2070
- this.treeService.captured = true;
2071
- this.dragStateService.state$
2072
- .pipe(first((message) => message === "idle"))
2073
- .subscribe(() => {
2074
- this.treeService.captured = false;
2075
- });
2076
- this.dropZoneService.clearVisibleZones();
2077
- this.treeService.drop(sourceBranch, dropZone.getFullInsertCoordinates());
2078
- }
2079
- ngOnDestroy() {
2080
- this.changesSubscription.unsubscribe();
2081
- this.dropSubscription.unsubscribe();
2082
- }
2083
- }
2084
- LimbleTreeRootComponent.ɵfac = function LimbleTreeRootComponent_Factory(t) { return new (t || LimbleTreeRootComponent)(i0.ɵɵdirectiveInject(TreeService), i0.ɵɵdirectiveInject(DropZoneService), i0.ɵɵdirectiveInject(DragStateService), i0.ɵɵdirectiveInject(GlobalEventsService), i0.ɵɵdirectiveInject(i0.NgZone), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.ElementRef)); };
2085
- LimbleTreeRootComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: LimbleTreeRootComponent, selectors: [["limble-tree-root"]], viewQuery: function LimbleTreeRootComponent_Query(rf, ctx) { if (rf & 1) {
2086
- i0.ɵɵviewQuery(_c0, 5, ViewContainerRef);
2087
- } if (rf & 2) {
2088
- let _t;
2089
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.host = _t.first);
2090
- } }, inputs: { data: "data", options: "options", itemsPerPage: "itemsPerPage", page: "page" }, outputs: { treeChange: "treeChange", treeDrop: "treeDrop" }, features: [i0.ɵɵProvidersFeature([TreeService, DropZoneService, TreeConstructionStatus]), i0.ɵɵNgOnChangesFeature], decls: 4, vars: 1, consts: [[1, "tree-event-host", 3, "dragoverNoChangeDetect", "dragleaveNoChangeDetect", "drop"], [4, "ngIf"], ["host", ""]], template: function LimbleTreeRootComponent_Template(rf, ctx) { if (rf & 1) {
2091
- i0.ɵɵelementStart(0, "div", 0);
2092
- i0.ɵɵlistener("dragoverNoChangeDetect", function LimbleTreeRootComponent_Template_div_dragoverNoChangeDetect_0_listener($event) { return ctx.dragoverHandler($event); })("dragleaveNoChangeDetect", function LimbleTreeRootComponent_Template_div_dragleaveNoChangeDetect_0_listener($event) { return ctx.dragleaveHandler($event); })("drop", function LimbleTreeRootComponent_Template_div_drop_0_listener($event) { return ctx.dropHandler($event); });
2093
- i0.ɵɵtemplate(1, LimbleTreeRootComponent_limble_tree_placeholder_1_Template, 1, 0, "limble-tree-placeholder", 1);
2094
- i0.ɵɵtemplate(2, LimbleTreeRootComponent_ng_template_2_Template, 0, 0, "ng-template", null, 2, i0.ɵɵtemplateRefExtractor);
2095
- i0.ɵɵelementEnd();
2096
- } if (rf & 2) {
2097
- i0.ɵɵadvance(1);
2098
- i0.ɵɵproperty("ngIf", ctx.placeholder === true);
2099
- } }, dependencies: [i2.NgIf, LimbleTreePlaceholderComponent, DragoverNoChangeDetectDirective, DragleaveNoChangeDetectDirective] });
2100
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(LimbleTreeRootComponent, [{
2101
- type: Component,
2102
- args: [{ selector: "limble-tree-root", providers: [TreeService, DropZoneService, TreeConstructionStatus], template: "<div\n (dragoverNoChangeDetect)=\"dragoverHandler($event)\"\n (dragleaveNoChangeDetect)=\"dragleaveHandler($event)\"\n (drop)=\"dropHandler($event)\"\n class=\"tree-event-host\"\n>\n <limble-tree-placeholder\n *ngIf=\"placeholder === true\"\n ></limble-tree-placeholder>\n <ng-template #host></ng-template>\n</div>\n" }]
2103
- }], function () { return [{ type: TreeService }, { type: DropZoneService }, { type: DragStateService }, { type: GlobalEventsService }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }]; }, { data: [{
2104
- type: Input
2105
- }], options: [{
2106
- type: Input
2107
- }], itemsPerPage: [{
2108
- type: Input
2109
- }], page: [{
2110
- type: Input
2111
- }], host: [{
2112
- type: ViewChild,
2113
- args: ["host", { read: ViewContainerRef }]
2114
- }], treeChange: [{
2115
- type: Output
2116
- }], treeDrop: [{
2117
- type: Output
2118
- }] }); })();
465
+ class TreeNodeBase {
466
+ constructor() {
467
+ this.destroyed = false;
468
+ this._branches = [];
469
+ this.events$ = new Subject();
470
+ this.subscriptions = [
471
+ this.graftsToSelf().subscribe((event) => {
472
+ this.registerChildRelationship(event.child(), event.index());
473
+ }),
474
+ this.prunesToSelf().subscribe((event) => {
475
+ this.deregisterChildRelationship(event.child());
476
+ })
477
+ ];
478
+ }
479
+ branches() {
480
+ return [...this._branches];
481
+ }
482
+ destroy() {
483
+ this.branches().forEach((branch) => {
484
+ branch.destroy();
485
+ });
486
+ this.subscriptions.forEach((sub) => {
487
+ sub.unsubscribe();
488
+ });
489
+ this.destroyed = true;
490
+ }
491
+ dispatch(event) {
492
+ this.events$.next(event);
493
+ }
494
+ events() {
495
+ return this.events$;
496
+ }
497
+ getBranch(index) {
498
+ return this._branches[index];
499
+ }
500
+ isDestroyed() {
501
+ return this.destroyed;
502
+ }
503
+ plot() {
504
+ return new Map(this.branches().map((branch, index) => [index, branch.plot()]));
505
+ }
506
+ traverse(callback) {
507
+ this.branches().forEach((branch) => {
508
+ branch.traverse(callback);
509
+ });
510
+ }
511
+ deregisterChildRelationship(child) {
512
+ const index = this.branches().findIndex((branch) => branch === child);
513
+ this._branches.splice(index, 1);
514
+ }
515
+ graftsToSelf() {
516
+ return this.events().pipe(filter((event) => event instanceof GraftEvent), filter((event) => event.parent().events() === this.events$));
517
+ }
518
+ prunesToSelf() {
519
+ return this.events().pipe(filter((event) => event instanceof PruneEvent), filter((event) => event.parent().events() === this.events$));
520
+ }
521
+ registerChildRelationship(child, index) {
522
+ const branches = this.branches();
523
+ if (index < 0 || index > branches.length) {
524
+ throw new TreeError(`Can't register child at index ${index}. Out of range.`);
525
+ }
526
+ this._branches.splice(index, 0, child);
527
+ }
528
+ }
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
+ }
2119
541
 
2120
- class LimbleTreeModule {
2121
- }
2122
- LimbleTreeModule.ɵfac = function LimbleTreeModule_Factory(t) { return new (t || LimbleTreeModule)(); };
2123
- LimbleTreeModule.ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: LimbleTreeModule });
2124
- LimbleTreeModule.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ providers: [ComponentCreatorService, DragStateService, GlobalEventsService], imports: [CommonModule] });
2125
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(LimbleTreeModule, [{
2126
- type: NgModule,
2127
- args: [{
2128
- declarations: [
2129
- LimbleTreeBranchComponent,
2130
- DropZoneComponent,
2131
- LimbleTreeNodeComponent,
2132
- LimbleTreeRootComponent,
2133
- LimbleTreePlaceholderComponent,
2134
- DragoverNoChangeDetectDirective,
2135
- DragleaveNoChangeDetectDirective
2136
- ],
2137
- imports: [CommonModule],
2138
- exports: [LimbleTreeRootComponent],
2139
- providers: [ComponentCreatorService, DragStateService, GlobalEventsService]
2140
- }]
2141
- }], null, null); })();
2142
- (function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(LimbleTreeModule, { declarations: [LimbleTreeBranchComponent,
2143
- DropZoneComponent,
2144
- LimbleTreeNodeComponent,
2145
- LimbleTreeRootComponent,
2146
- LimbleTreePlaceholderComponent,
2147
- DragoverNoChangeDetectDirective,
2148
- DragleaveNoChangeDetectDirective], imports: [CommonModule], exports: [LimbleTreeRootComponent] }); })();
2149
- i0.ɵɵsetComponentScope(LimbleTreeNodeComponent, [i2.NgIf, LimbleTreeBranchComponent,
2150
- DropZoneComponent,
2151
- DragoverNoChangeDetectDirective], []);
542
+ class RootComponent {
543
+ constructor() {
544
+ this.branchesContainer = undefined;
545
+ this.dropzone = undefined;
546
+ this.afterViewInit = new EventEmitter();
547
+ this.dropped = new EventEmitter();
548
+ this.showInnerDropzone = false;
549
+ }
550
+ ngAfterViewInit() {
551
+ this.afterViewInit.emit();
552
+ assert(this.dropzone !== undefined);
553
+ this.dropzone.dropped.subscribe(this.dropped);
554
+ }
555
+ }
556
+ RootComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RootComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
557
+ RootComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: RootComponent, isStandalone: true, selector: "root", outputs: { afterViewInit: "afterViewInit", dropped: "dropped" }, viewQueries: [{ propertyName: "branchesContainer", first: true, predicate: ["branchesContainer"], descendants: true, read: ViewContainerRef }, { propertyName: "dropzone", first: true, predicate: DropzoneComponent, descendants: true }], ngImport: i0, template: "<div class=\"branches-container\">\n <dropzone placement=\"inner\" [hidden]=\"!showInnerDropzone\"></dropzone>\n <div #branchesContainer></div>\n</div>\n", styles: [".branches-container{min-width:64px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DropzoneComponent, selector: "dropzone", inputs: ["placement"], outputs: ["dropped"] }] });
558
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RootComponent, decorators: [{
559
+ type: Component,
560
+ args: [{ standalone: true, selector: "root", imports: [CommonModule, DropzoneComponent], template: "<div class=\"branches-container\">\n <dropzone placement=\"inner\" [hidden]=\"!showInnerDropzone\"></dropzone>\n <div #branchesContainer></div>\n</div>\n", styles: [".branches-container{min-width:64px}\n"] }]
561
+ }], propDecorators: { branchesContainer: [{
562
+ type: ViewChild,
563
+ args: ["branchesContainer", { read: ViewContainerRef }]
564
+ }], dropzone: [{
565
+ type: ViewChild,
566
+ args: [DropzoneComponent]
567
+ }], afterViewInit: [{
568
+ type: Output
569
+ }], dropped: [{
570
+ type: Output
571
+ }] } });
572
+
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
+ const viewInitSub = this.rootComponentRef.instance.afterViewInit.subscribe(() => {
581
+ const dropzone = this.rootComponentRef.instance.dropzone;
582
+ assert(dropzone !== undefined);
583
+ dropzoneRenderer.registerDropzone(dropzone, this.treeRoot);
584
+ });
585
+ const droppedSub = this.rootComponentRef.instance.dropped.subscribe(() => {
586
+ dropzoneRenderer.handleDrop(this.treeRoot, "inner");
587
+ });
588
+ this.instanceSubscriptions = [viewInitSub, droppedSub];
589
+ }
590
+ destroy() {
591
+ this.instanceSubscriptions.forEach((sub) => {
592
+ sub.unsubscribe();
593
+ });
594
+ }
595
+ detectChanges() {
596
+ this.rootComponentRef.changeDetectorRef.detectChanges();
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
+ }
619
+ branches() {
620
+ return this.treeNodeBase.branches();
621
+ }
622
+ destroy() {
623
+ if (this.isDestroyed()) {
624
+ throw new TreeError("Cannot destroy a destroyed tree root");
625
+ }
626
+ dropzoneRenderer.clearTreeFromRegistry(this);
627
+ this.treeNodeBase.destroy();
628
+ this.rootController.destroy();
629
+ this.viewContainerRef.clear();
630
+ config.delete(this);
631
+ this.dispatch(new DestructionEvent(this));
632
+ }
633
+ detectChanges() {
634
+ this.rootController.detectChanges();
635
+ }
636
+ dispatch(event) {
637
+ this.treeNodeBase.dispatch(event);
638
+ }
639
+ events() {
640
+ return this.treeNodeBase.events();
641
+ }
642
+ getBranch(index) {
643
+ return this.treeNodeBase.getBranch(index);
644
+ }
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();
668
+ }
669
+ grow(component, options) {
670
+ if (this.isDestroyed()) {
671
+ throw new TreeError("Cannot grow a branch on a destroyed tree root");
672
+ }
673
+ return new TreeBranch(this, { component, ...options });
674
+ }
675
+ isDestroyed() {
676
+ return this.treeNodeBase.isDestroyed();
677
+ }
678
+ plot() {
679
+ return this.treeNodeBase.plot();
680
+ }
681
+ root() {
682
+ return this;
683
+ }
684
+ traverse(callback) {
685
+ callback(this);
686
+ this.treeNodeBase.traverse(callback);
687
+ }
688
+ }
689
+
690
+ class TreeService {
691
+ createEmptyTree(container, options = {}) {
692
+ container.clear();
693
+ const root = new TreeRoot(container);
694
+ config.setConfig(root, options);
695
+ return root;
696
+ }
697
+ }
698
+ TreeService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
699
+ TreeService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeService, providedIn: "root" });
700
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeService, decorators: [{
701
+ type: Injectable,
702
+ args: [{ providedIn: "root" }]
703
+ }] });
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
+
799
+ class TreeBranch {
800
+ constructor(parent, branchOptions) {
801
+ this.branchOptions = branchOptions;
802
+ this.detachedView = null;
803
+ this.treeNodeBase = new TreeNodeBase();
804
+ const parentBranchesContainer = parent.getBranchesContainer();
805
+ assert(parentBranchesContainer !== undefined);
806
+ this.branchController = new BranchController(this, parentBranchesContainer);
807
+ this.setIndentation(parent);
808
+ if (parent instanceof TreeBranch &&
809
+ parent.branchOptions.defaultCollapsed === true) {
810
+ treeCollapser.storePrecollapsedNode(parent, this);
811
+ this.detachedView = this.branchController.getHostView();
812
+ }
813
+ else {
814
+ parentBranchesContainer.insert(this.branchController.getHostView());
815
+ this.detectChanges();
816
+ this._parent = parent;
817
+ this.dispatch(new GraftEvent(this, {
818
+ parent: this._parent,
819
+ child: this,
820
+ index: this._parent.branches().length
821
+ }));
822
+ }
823
+ }
824
+ branches() {
825
+ return this.treeNodeBase.branches();
826
+ }
827
+ destroy() {
828
+ if (this.isDestroyed()) {
829
+ throw new TreeError("Cannot destroy a destroyed tree branch");
830
+ }
831
+ this.prune();
832
+ treeCollapser.expand(this);
833
+ dropzoneRenderer.clearTreeFromRegistry(this);
834
+ this.branchController.getHostView().destroy();
835
+ this.treeNodeBase.destroy();
836
+ this.branchController.destroy();
837
+ this.dispatch(new DestructionEvent(this));
838
+ }
839
+ detectChanges() {
840
+ this.branchController.detectChanges();
841
+ }
842
+ dispatch(event) {
843
+ this.treeNodeBase.dispatch(event);
844
+ this._parent?.dispatch(event);
845
+ }
846
+ events() {
847
+ return this.treeNodeBase.events();
848
+ }
849
+ getBranch(index) {
850
+ return this.treeNodeBase.getBranch(index);
851
+ }
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();
881
+ }
882
+ graftTo(newParent, index) {
883
+ this.checkGraftLocationValidity(newParent, index);
884
+ const ownIndex = this.index();
885
+ if (ownIndex !== undefined) {
886
+ this.prune();
887
+ }
888
+ this._parent = newParent;
889
+ const newIndex = index ?? newParent.branches().length;
890
+ this.reattachView(newIndex);
891
+ this.dispatch(new GraftEvent(this, {
892
+ parent: newParent,
893
+ child: this,
894
+ index: newIndex
895
+ }));
896
+ return newIndex;
897
+ }
898
+ grow(component, options) {
899
+ if (this.isDestroyed()) {
900
+ throw new TreeError("Cannot grow a branch on a destroyed tree branch");
901
+ }
902
+ return new TreeBranch(this, { component, ...options });
903
+ }
904
+ index() {
905
+ if (!this._parent) {
906
+ return undefined;
907
+ }
908
+ const index = this._parent
909
+ .branches()
910
+ .findIndex((branch) => branch === this);
911
+ assert(index >= 0);
912
+ return index;
913
+ }
914
+ isDestroyed() {
915
+ return this.treeNodeBase.isDestroyed();
916
+ }
917
+ meta() {
918
+ return this.branchOptions.meta ?? {};
919
+ }
920
+ parent() {
921
+ return this._parent;
922
+ }
923
+ plot() {
924
+ return this.treeNodeBase.plot();
925
+ }
926
+ position() {
927
+ const index = this.index();
928
+ if (index === undefined) {
929
+ throw new TreeError("branch has no parent. Position cannot be determined.");
930
+ }
931
+ if (this._parent instanceof TreeBranch) {
932
+ const parentPosition = this._parent.position();
933
+ return [...parentPosition, index];
934
+ }
935
+ return [index];
936
+ }
937
+ prune() {
938
+ if (this.isDestroyed()) {
939
+ throw new TreeError("Cannot prune a destroyed tree branch");
940
+ }
941
+ const parent = this._parent;
942
+ const index = this.index();
943
+ if (index === undefined || parent === undefined)
944
+ return;
945
+ const container = parent.getBranchesContainer();
946
+ assert(container !== undefined);
947
+ this.detachedView = container.detach(index);
948
+ assert(this.detachedView !== null);
949
+ this.detachedView.detach();
950
+ this.dispatch(new PruneEvent(this, {
951
+ parent: parent,
952
+ child: this,
953
+ index: index
954
+ }));
955
+ this._parent = undefined;
956
+ return this;
957
+ }
958
+ root() {
959
+ const parent = this.parent();
960
+ if (parent instanceof TreeBranch) {
961
+ return parent.root();
962
+ }
963
+ assert(parent instanceof TreeRoot || parent === undefined);
964
+ return parent;
965
+ }
966
+ traverse(callback) {
967
+ callback(this);
968
+ this.treeNodeBase.traverse(callback);
969
+ }
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");
984
+ }
985
+ });
986
+ }
987
+ indexIsOutOfRange(parent, index) {
988
+ return index < 0 || index > parent.branches().length;
989
+ }
990
+ reattachView(index) {
991
+ assert(this._parent !== undefined);
992
+ assert(this.detachedView !== null);
993
+ const container = this._parent.getBranchesContainer();
994
+ assert(container !== undefined);
995
+ this.detachedView.reattach();
996
+ container.insert(this.detachedView, index);
997
+ this.detachedView = null;
998
+ }
999
+ setIndentation(parent) {
1000
+ const root = parent.root();
1001
+ assert(root !== undefined);
1002
+ const options = config.getConfig(root);
1003
+ const branchesContainerEl = this.branchController
1004
+ .getNativeElement()
1005
+ .getElementsByClassName("branches-container")
1006
+ .item(0);
1007
+ assert(branchesContainerEl instanceof HTMLElement);
1008
+ branchesContainerEl.style.marginLeft = `${options?.indentation ?? 16}px`;
1009
+ }
1010
+ }
1011
+
1012
+ class DropzoneRenderer {
1013
+ constructor() {
1014
+ this.registry = new Map();
1015
+ this.currentDisplay = null;
1016
+ dragAndDrop.dragAborted$.subscribe(() => {
1017
+ this.clearCurrentDisplay();
1018
+ });
1019
+ dragState
1020
+ .events()
1021
+ .pipe(filter((event) => event === DragStates.Starting))
1022
+ .subscribe(() => {
1023
+ const branch = dragState.getDragData();
1024
+ assert(branch !== undefined);
1025
+ let cursor = branch.parent()?.getBranch((branch.index() ?? 0) - 1);
1026
+ let final = branch.parent();
1027
+ while (cursor !== undefined) {
1028
+ final = cursor;
1029
+ cursor = cursor.branches().at(-1);
1030
+ }
1031
+ branch
1032
+ .events()
1033
+ .pipe(filter((event) => event instanceof PruneEvent), first())
1034
+ .subscribe(() => {
1035
+ assert(final instanceof TreeBranch || final instanceof TreeRoot);
1036
+ this.showLowerZones(final);
1037
+ });
1038
+ });
1039
+ }
1040
+ clearCurrentDisplay() {
1041
+ if (this.currentDisplay === null)
1042
+ return;
1043
+ for (const branch of this.registry.values()) {
1044
+ const instance = branch.getComponentInstance();
1045
+ instance.showInnerDropzone = false;
1046
+ if (instance instanceof BranchComponent) {
1047
+ instance.showLateralDropzone = false;
1048
+ }
1049
+ }
1050
+ this.currentDisplay = null;
1051
+ }
1052
+ clearTreeFromRegistry(tree) {
1053
+ const nodes = [];
1054
+ tree.traverse((node) => nodes.push(node));
1055
+ for (const [dropzoneComponent, treeNode] of this.registry) {
1056
+ if (nodes.includes(treeNode)) {
1057
+ this.registry.delete(dropzoneComponent);
1058
+ }
1059
+ }
1060
+ }
1061
+ getCurrentDisplay() {
1062
+ if (this.currentDisplay === null)
1063
+ return null;
1064
+ return { ...this.currentDisplay };
1065
+ }
1066
+ getDropzoneLocation(dropzone) {
1067
+ const branch = this.registry.get(dropzone);
1068
+ const placement = dropzone.placement;
1069
+ if (branch === undefined) {
1070
+ throw new Error("dropzone not found in registry");
1071
+ }
1072
+ if (placement === undefined) {
1073
+ throw new Error("dropzone has an undefined placement");
1074
+ }
1075
+ return [branch, placement];
1076
+ }
1077
+ handleDrop(treeNode, placement) {
1078
+ if (placement === "inner") {
1079
+ dragAndDrop.drop(treeNode, 0);
1080
+ }
1081
+ else if (treeNode instanceof TreeBranch && placement === "lateral") {
1082
+ const currentParent = treeNode.parent();
1083
+ const index = treeNode.index();
1084
+ if (currentParent === undefined || index === undefined) {
1085
+ throw new Error("branch must have a parent");
1086
+ }
1087
+ dragAndDrop.drop(currentParent, index + 1);
1088
+ }
1089
+ this.clearCurrentDisplay();
1090
+ }
1091
+ registerDropzone(dropzone, treeNode) {
1092
+ this.registry.set(dropzone, treeNode);
1093
+ }
1094
+ registerDropzones(dropzones, treeBranch) {
1095
+ for (const dropzone of dropzones) {
1096
+ this.registry.set(dropzone, treeBranch);
1097
+ }
1098
+ }
1099
+ showLowerZones(treeNode) {
1100
+ this.clearCurrentDisplay();
1101
+ this.showInnerZone(treeNode);
1102
+ if (treeNode.branches().length === 0) {
1103
+ this.loopThroughLowerZones(treeNode);
1104
+ }
1105
+ this.currentDisplay = { treeBranch: treeNode, direction: "lower" };
1106
+ }
1107
+ showUpperZones(treeBranch) {
1108
+ this.clearCurrentDisplay();
1109
+ this.loopThroughUpperZones(treeBranch);
1110
+ this.currentDisplay = { treeBranch, direction: "upper" };
1111
+ }
1112
+ loopThroughLowerZones(treeNode) {
1113
+ let cursor = treeNode;
1114
+ while (cursor instanceof TreeBranch) {
1115
+ this.showLateralZone(cursor);
1116
+ const parent = cursor.parent();
1117
+ const index = cursor.index();
1118
+ assert(parent !== undefined && index !== undefined);
1119
+ if (parent.branches().length > index + 1) {
1120
+ return;
1121
+ }
1122
+ cursor = cursor.parent();
1123
+ }
1124
+ }
1125
+ loopThroughUpperZones(treeBranch) {
1126
+ let cursor = treeBranch
1127
+ .parent()
1128
+ ?.getBranch((treeBranch.index() ?? 0) - 1);
1129
+ let final = treeBranch.parent();
1130
+ while (cursor !== undefined) {
1131
+ this.showLateralZone(cursor);
1132
+ final = cursor;
1133
+ cursor = cursor.branches().at(-1);
1134
+ }
1135
+ if (final !== undefined &&
1136
+ (final instanceof TreeRoot || final instanceof TreeBranch)) {
1137
+ this.showInnerZone(final);
1138
+ }
1139
+ }
1140
+ nestingAllowed(treeNode) {
1141
+ if (treeNode instanceof TreeRoot) {
1142
+ return true;
1143
+ }
1144
+ if (treeNode instanceof TreeBranch) {
1145
+ const allowNesting = config.getConfig(treeNode.root())?.dragAndDrop?.allowNesting ??
1146
+ (() => true);
1147
+ return allowNesting(treeNode);
1148
+ }
1149
+ throw new Error("unsupported treeNode type");
1150
+ }
1151
+ dropAllowed(parent, index) {
1152
+ const sourceNode = dragState.getDragData();
1153
+ assert(sourceNode instanceof TreeBranch);
1154
+ if (parent instanceof TreeRoot) {
1155
+ const allowDrop = config.getConfig(parent)?.dragAndDrop?.allowDrop ??
1156
+ (() => true);
1157
+ return allowDrop(sourceNode, parent, index);
1158
+ }
1159
+ if (parent instanceof TreeBranch) {
1160
+ const allowDrop = config.getConfig(parent.root())?.dragAndDrop?.allowDrop ??
1161
+ (() => true);
1162
+ return allowDrop(sourceNode, parent, index);
1163
+ }
1164
+ throw new Error("unsupported treeNode type");
1165
+ }
1166
+ showInnerZone(treeNode) {
1167
+ if (!this.nestingAllowed(treeNode) || !this.dropAllowed(treeNode, 0))
1168
+ return;
1169
+ treeNode.getComponentInstance().showInnerDropzone = true;
1170
+ }
1171
+ showLateralZone(treeBranch) {
1172
+ const index = treeBranch.index();
1173
+ assert(index !== undefined);
1174
+ if (!this.dropAllowed(treeBranch.parent(), index + 1))
1175
+ return;
1176
+ treeBranch.getComponentInstance().showLateralDropzone = true;
1177
+ }
1178
+ }
1179
+ const dropzoneRenderer = new DropzoneRenderer();
1180
+
1181
+ class TreeDragAndDropService {
1182
+ clearDropzones() {
1183
+ dropzoneRenderer.clearCurrentDisplay();
1184
+ }
1185
+ dragStart(treeBranch, event) {
1186
+ dragAndDrop.dragStart(treeBranch, event);
1187
+ }
1188
+ getCurrentlyDisplayedDropzoneFamily() {
1189
+ return dropzoneRenderer.getCurrentDisplay();
1190
+ }
1191
+ showRootDropzone(root) {
1192
+ dropzoneRenderer.showLowerZones(root);
1193
+ }
1194
+ state() {
1195
+ return dragState.events();
1196
+ }
1197
+ }
1198
+ TreeDragAndDropService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeDragAndDropService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1199
+ TreeDragAndDropService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeDragAndDropService });
1200
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeDragAndDropService, decorators: [{
1201
+ type: Injectable
1202
+ }] });
1203
+
1204
+ class DraggableDirective {
1205
+ constructor(dragAndDropService, renderer, hostElement) {
1206
+ this.dragAndDropService = dragAndDropService;
1207
+ renderer.setAttribute(hostElement.nativeElement, "draggable", "true");
1208
+ renderer.setStyle(hostElement.nativeElement, "cursor", "grab");
1209
+ }
1210
+ onDragstart(event) {
1211
+ if (this.limbleTreeDraggable === undefined)
1212
+ return;
1213
+ this.dragAndDropService.dragStart(this.limbleTreeDraggable, event);
1214
+ }
1215
+ }
1216
+ DraggableDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: DraggableDirective, deps: [{ token: TreeDragAndDropService }, { token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
1217
+ DraggableDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.12", type: DraggableDirective, isStandalone: true, selector: "[limbleTreeDraggable]", inputs: { limbleTreeDraggable: "limbleTreeDraggable" }, host: { listeners: { "dragstart": "onDragstart($event)" } }, ngImport: i0 });
1218
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: DraggableDirective, decorators: [{
1219
+ type: Directive,
1220
+ args: [{
1221
+ selector: "[limbleTreeDraggable]",
1222
+ standalone: true
1223
+ }]
1224
+ }], ctorParameters: function () { return [{ type: TreeDragAndDropService }, { type: i0.Renderer2 }, { type: i0.ElementRef }]; }, propDecorators: { limbleTreeDraggable: [{
1225
+ type: Input
1226
+ }], onDragstart: [{
1227
+ type: HostListener,
1228
+ args: ["dragstart", ["$event"]]
1229
+ }] } });
1230
+
1231
+ class TreeDragAndDropModule {
1232
+ }
1233
+ TreeDragAndDropModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeDragAndDropModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1234
+ TreeDragAndDropModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: TreeDragAndDropModule, imports: [DraggableDirective, DragoverNoChangeDetectDirective], exports: [DraggableDirective, DragoverNoChangeDetectDirective] });
1235
+ TreeDragAndDropModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeDragAndDropModule, providers: [TreeDragAndDropService] });
1236
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TreeDragAndDropModule, decorators: [{
1237
+ type: NgModule,
1238
+ args: [{
1239
+ imports: [DraggableDirective, DragoverNoChangeDetectDirective],
1240
+ providers: [TreeDragAndDropService],
1241
+ exports: [DraggableDirective, DragoverNoChangeDetectDirective]
1242
+ }]
1243
+ }] });
1244
+
1245
+ /**
1246
+ * A shim to help with the transition from v0 to v1.
1247
+ * @deprecated
1248
+ */
1249
+ class LegacyTree {
1250
+ /** Creates a v1 tree structure from a v0 data array and v0 tree options. */
1251
+ createTreeFromLegacyArray(container, data, treeOptions = {}) {
1252
+ const root = new TreeRoot(container);
1253
+ config.setConfig(root, this.upgradeOptions(treeOptions));
1254
+ for (const node of data) {
1255
+ this.renderTreeNode(root, node, treeOptions);
1256
+ }
1257
+ return root;
1258
+ }
1259
+ upgradeOptions(legacyOptions) {
1260
+ return {
1261
+ indentation: legacyOptions.indent,
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);
1289
+ }
1290
+ }
1291
+ };
1292
+ }
1293
+ renderTreeNode(parent, node, options) {
1294
+ const component = node.component?.class ?? options.defaultComponent?.class;
1295
+ if (component === undefined) {
1296
+ throw new TreeError("A component must be provided");
1297
+ }
1298
+ const bindings = (node.component?.bindings ??
1299
+ options.defaultComponent?.bindings ??
1300
+ {});
1301
+ const nodeData = node;
1302
+ const branch = parent.grow(component, {
1303
+ inputBindings: bindings,
1304
+ meta: { nodeData }
1305
+ });
1306
+ for (const childNode of node.nodes ?? []) {
1307
+ this.renderTreeNode(branch, childNode, options);
1308
+ }
1309
+ if (node.collapsed === true) {
1310
+ treeCollapser.collapse(branch);
1311
+ }
1312
+ }
1313
+ }
1314
+
1315
+ /** @deprecated */
1316
+ class LimbleTreeRootComponent {
1317
+ constructor() {
1318
+ this.itemsPerPage = Infinity;
1319
+ this.page = 1;
1320
+ this.treeChange = new EventEmitter();
1321
+ this.treeDrop = new EventEmitter();
1322
+ this.legacyTree = new LegacyTree();
1323
+ }
1324
+ ngAfterViewInit() {
1325
+ this.update();
1326
+ }
1327
+ ngOnChanges() {
1328
+ if (this.host !== undefined && this.data !== undefined) {
1329
+ this.update();
1330
+ }
1331
+ }
1332
+ ngOnDestroy() {
1333
+ this.dropSubscription?.unsubscribe();
1334
+ this.root?.destroy();
1335
+ }
1336
+ getRoot() {
1337
+ return this.root;
1338
+ }
1339
+ update() {
1340
+ if (this.data === undefined) {
1341
+ throw new TreeError("LimbleTreeRootComponent's `data` input is required");
1342
+ }
1343
+ if (this.host === undefined) {
1344
+ throw new TreeError("LimbleTreeRootComponent's `host` property is not defined");
1345
+ }
1346
+ this.root?.destroy();
1347
+ this.dropSubscription?.unsubscribe();
1348
+ const dataSlice = this.paginatedData();
1349
+ this.root = this.legacyTree.createTreeFromLegacyArray(this.host, dataSlice, this.options);
1350
+ this.dropSubscription = this.root
1351
+ .events()
1352
+ .pipe(filter((event) => event instanceof DragEndEvent))
1353
+ .subscribe(this.treeDrop);
1354
+ this.treeChange.emit(this.root);
1355
+ }
1356
+ paginatedData() {
1357
+ assert(this.data !== undefined);
1358
+ if (this.options?.listMode !== true) {
1359
+ return this.data;
1360
+ }
1361
+ return this.data.slice((this.page - 1) * this.itemsPerPage, this.page * this.itemsPerPage);
1362
+ }
1363
+ }
1364
+ LimbleTreeRootComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LimbleTreeRootComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1365
+ LimbleTreeRootComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: LimbleTreeRootComponent, isStandalone: true, selector: "limble-tree-root", inputs: { data: "data", options: "options", itemsPerPage: "itemsPerPage", page: "page" }, outputs: { treeChange: "treeChange", treeDrop: "treeDrop" }, viewQueries: [{ propertyName: "host", first: true, predicate: ["host"], descendants: true, read: ViewContainerRef }], usesOnChanges: true, ngImport: i0, template: "<div #host></div>\n" });
1366
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LimbleTreeRootComponent, decorators: [{
1367
+ type: Component,
1368
+ args: [{ selector: "limble-tree-root", standalone: true, template: "<div #host></div>\n" }]
1369
+ }], ctorParameters: function () { return []; }, propDecorators: { data: [{
1370
+ type: Input
1371
+ }], options: [{
1372
+ type: Input
1373
+ }], itemsPerPage: [{
1374
+ type: Input
1375
+ }], page: [{
1376
+ type: Input
1377
+ }], host: [{
1378
+ type: ViewChild,
1379
+ args: ["host", { read: ViewContainerRef }]
1380
+ }], treeChange: [{
1381
+ type: Output
1382
+ }], treeDrop: [{
1383
+ type: Output
1384
+ }] } });
1385
+
1386
+ /** @deprecated */
1387
+ class LimbleTreeLegacyModule {
1388
+ }
1389
+ LimbleTreeLegacyModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LimbleTreeLegacyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1390
+ LimbleTreeLegacyModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: LimbleTreeLegacyModule, imports: [LimbleTreeRootComponent], exports: [LimbleTreeRootComponent] });
1391
+ LimbleTreeLegacyModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LimbleTreeLegacyModule, providers: [LegacyTree], imports: [LimbleTreeRootComponent] });
1392
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LimbleTreeLegacyModule, decorators: [{
1393
+ type: NgModule,
1394
+ args: [{
1395
+ declarations: [],
1396
+ imports: [LimbleTreeRootComponent],
1397
+ providers: [LegacyTree],
1398
+ exports: [LimbleTreeRootComponent]
1399
+ }]
1400
+ }] });
1401
+
1402
+ class LimbleTreeModule {
1403
+ }
1404
+ LimbleTreeModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LimbleTreeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1405
+ LimbleTreeModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: LimbleTreeModule, imports: [LimbleTreeLegacyModule, TreeCollapseModule, TreeDragAndDropModule], exports: [LimbleTreeLegacyModule, TreeCollapseModule, TreeDragAndDropModule] });
1406
+ LimbleTreeModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LimbleTreeModule, providers: [TreeService], imports: [LimbleTreeLegacyModule, TreeCollapseModule, TreeDragAndDropModule, LimbleTreeLegacyModule, TreeCollapseModule, TreeDragAndDropModule] });
1407
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: LimbleTreeModule, decorators: [{
1408
+ type: NgModule,
1409
+ args: [{
1410
+ declarations: [],
1411
+ imports: [LimbleTreeLegacyModule, TreeCollapseModule, TreeDragAndDropModule],
1412
+ providers: [TreeService],
1413
+ exports: [LimbleTreeLegacyModule, TreeCollapseModule, TreeDragAndDropModule]
1414
+ }]
1415
+ }] });
2152
1416
 
2153
- /*
2154
- * Public API Surface of limble-tree
1417
+ /*
1418
+ * Public API Surface of limble-tree
2155
1419
  */
2156
1420
 
2157
- /**
2158
- * Generated bundle index. Do not edit.
1421
+ /**
1422
+ * Generated bundle index. Do not edit.
2159
1423
  */
2160
1424
 
2161
- export { LimbleTreeModule, LimbleTreeRootComponent };
1425
+ export { DestructionEvent, DragEndEvent, DragStartEvent, DraggableDirective, DragoverNoChangeDetectDirective, DropEvent, GraftEvent, LegacyTree, LimbleTreeLegacyModule, LimbleTreeModule, LimbleTreeRootComponent, PruneEvent, TreeBranch, TreeCollapseModule, TreeCollapseService, TreeDragAndDropModule, TreeDragAndDropService, TreeError, TreeRoot, TreeService, config };
2162
1426
  //# sourceMappingURL=limble-limble-tree.mjs.map