@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
package/README.md CHANGED
@@ -1,149 +1,471 @@
1
1
  # Limble Tree
2
2
 
3
- An Angular library for creating highly dynamic drag-and-drop tree structures
3
+ This library is currently a work in progress. It may not be suitable for production environments yet.
4
4
 
5
- ## About
5
+ ## Description
6
6
 
7
- ### Limble
7
+ An Angular library for building visual tree structures. Built and used by the team at [Limble](https://limblecmms.com/);
8
8
 
9
- Limble is a CMMS SaaS company providing great software to customers around the world. See [limblecmms.com](https://limblecmms.com) for more information. The `limble-tree` library is built by the Limble team and used in Limble's web applications.
9
+ Features:
10
10
 
11
- ### Status
11
+ - Allows any number of different components to be rendered in the same tree.
12
+ - Collapsible tree branches
13
+ - Move branches around both programmatically and with built-in drag-and-drop.
14
+ - Branches can be moved between trees.
15
+ - No limit on tree depth or size.
16
+ - Recently redesigned to allow better performance with large/complex trees.
12
17
 
13
- This library is currently in **beta** development. It may not be ready for use in a production environment.
18
+ ## Installation
14
19
 
15
- ### Features
20
+ ```bash
21
+ npm install @limble/limble-tree
22
+ ```
16
23
 
17
- - Unlimited tree depth
18
- - Can have a different component rendered for each node in the tree
19
- - Can drag nodes from one location in the tree to other locations
20
- - Dragging can be turned off for all or some of the nodes
21
- - Easy nesting of nodes
22
- - Nesting can be turned off for all or some of the nodes
23
- - Nodes can be dropped into other limble trees
24
- - Supports drag handles
25
- - Catchable events are fired when the tree renders and when a drop occurs
26
- - Pagination available for flat trees
24
+ ## Basic Usage
27
25
 
28
- ### Warning
26
+ A basic tree is very easy to set up.
29
27
 
30
- This library is compiled using Angular IVY, and therefore will not work in applications that do not also use IVY. IVY has been the default compiler for Angular since Angular 9.
28
+ ### Step 1
31
29
 
32
- ### Versioning
30
+ Import the limble-tree module into your own Angular module or standalone component.
33
31
 
34
- To the best of our ability, this library follows the [Semantic Versioning](https://semver.org/) standard.
32
+ ```typescript
33
+ import { NgModule } from "@angular/core";
34
+ import { LimbleTreeModule } from "@limble/limble-tree";
35
35
 
36
- ## Installation
36
+ @NgModule({
37
+ imports: [LimbleTreeModule]
38
+ })
39
+ export class AppModule {}
40
+ ```
41
+
42
+ or
43
+
44
+ ```typescript
45
+ import { Component } from "@angular/core";
46
+ import { LimbleTreeModule } from "@limble/limble-tree";
47
+
48
+ @Component({
49
+ standalone: true,
50
+ imports: [LimbleTreeModule],
51
+ template: `<div></div>`
52
+ })
53
+ export class MyComponent {}
54
+ ```
55
+
56
+ ### Step 2
57
+
58
+ Add a template variable to the HTML element in which the tree should be rendered.
59
+
60
+ ```html
61
+ <div #treeContainer></div>
62
+ ```
63
+
64
+ ### Step 3
65
+
66
+ Use `@ViewChild` to get the `ViewContainerRef` of that element.
67
+
68
+ ```typescript
69
+ @ViewChild("treeContainer", { read: ViewContainerRef }) treeContainer?: ViewContainerRef;
70
+ ```
71
+
72
+ ### Step 4
73
+
74
+ Inject TreeService into your component.
75
+
76
+ ```typescript
77
+ constructor(private readonly treeService: TreeService) {}
78
+ ```
79
+
80
+ ### Step 5
81
+
82
+ In the `ngAfterViewInit` lifecycle hook, call `createEmptyTree()`, passing in the ViewContainerRef obtained in step 3.
83
+
84
+ ```typescript
85
+ protected tree?: TreeRoot<MyTreeContentComponent>;
37
86
 
38
- `npm install @limble/limble-tree`
87
+ public ngAfterViewInit(): void {
88
+ this.tree = this.treeService.createEmptyTree<MyTreeContentComponent>(this.treeContainer);
89
+ }
90
+ ```
91
+
92
+ ### Step 6
93
+
94
+ Render components in the tree by calling `grow()`.
95
+
96
+ ```typescript
97
+ //Renders four instances of the MyTreeContentComponent; the fourth is nested under the third.
98
+ const branch1 = this.tree.grow(MyTreeContentComponent);
99
+ const branch2 = this.tree.grow(MyTreeContentComponent);
100
+ const branch3 = this.tree.grow(MyTreeContentComponent);
101
+ const branch3a = branch3.grow(MyTreContentComponent);
102
+ ```
103
+
104
+ ### Step 7
105
+
106
+ That's it, you've built your first limble-tree. Here is the full component code we just built:
107
+
108
+ ```typescript
109
+ import {
110
+ Component,
111
+ AfterViewInit,
112
+ ViewChild,
113
+ ViewContainerRef
114
+ } from "@angular/core";
115
+ import { LimbleTreeModule, TreeRoot } from "@limble/limble-tree";
116
+ import { MyTreeContentComponent } from "somewhere in your filesystem";
117
+
118
+ @Component({
119
+ standalone: true,
120
+ imports: [LimbleTreeModule],
121
+ template: `<div #treeContainer></div>`
122
+ })
123
+ export class MyComponent implements AfterViewInit {
124
+ @ViewChild("treeContainer", { read: ViewContainerRef })
125
+ treeContainer?: ViewContainerRef;
126
+
127
+ protected tree?: TreeRoot<MyTreeContentComponent>;
128
+
129
+ public constructor(private readonly treeService: TreeService) {}
130
+
131
+ public ngAfterViewInit(): void {
132
+ this.tree = this.treeService.createEmptyTree<MyTreeContentComponent>(
133
+ this.treeContainer
134
+ );
135
+ // Renders four instances of the MyTreeContentComponent;
136
+ // the fourth is nested under the third.
137
+ const branch1 = this.tree.grow(MyTreeContentComponent);
138
+ const branch2 = this.tree.grow(MyTreeContentComponent);
139
+ const branch3 = this.tree.grow(MyTreeContentComponent);
140
+ const branch3a = branch3.grow(MyTreContentComponent);
141
+ }
142
+ }
143
+ ```
144
+
145
+ ## Communicating With Your Components
39
146
 
40
- ## Usage
147
+ ### Inputs and Outputs
41
148
 
42
- ### Basic Setup
149
+ If the components passed to `grow()` require inputs, or if you want to watch for output events, you can pass those things into the optional second parameter.
43
150
 
44
- 1. Add the `LimbleTreeModule` to the `imports` array of one of your own modules.
151
+ ```typescript
152
+ tree.grow(MyTreeContentComponent, {
153
+ inputBindings: { myInput1: "hello world", myInput2: 1000 },
154
+ outputBindings: {
155
+ myOutput: (event) => {
156
+ /* Do stuff */
157
+ }
158
+ }
159
+ });
160
+ ```
161
+
162
+ The inputBindings object tells the tree to pass "hello world" into the component field named `myInput1`. Similarly, the tree will pass 1000 into the component field named "myInput2".
163
+
164
+ The outputBindings object works similarly, but instead of passing values, it registers callbacks to run when the specified outputs emit events. In the example, the arrow function will be run each time the field named `myOutput` emits a value.
165
+
166
+ ### Special `treeBranch` Input
167
+
168
+ No matter what you decide to pass through inputs, if anything, the tree will always automatically pass a TreeBranch object into the component. That object can be accessed within the component by declaring a `treeBranch` Input property.
169
+
170
+ ```typescript
171
+ @Input() treeBranch?: TreeBranch<MyTreeContentComponent>;
172
+ ```
173
+
174
+ This treeBranch object can be very useful for determining things like where the component lives in the tree, if it has any child branches, etc. It is also used for many of the more advanced features described elsewhere in this document.
175
+
176
+ ### Passing Custom Data Through The `treeBranch` Input Using `meta`
45
177
 
46
- 2. Create an array where each element in the array represents an item in the tree (called a "node"). Children can be assigned to a node via the "nodes" property:
178
+ If desired, you can add custom data to the treeBranch object associated with your rendered components. You can do this using another property of the second parameter of `grow()`, called `meta`.
47
179
 
48
180
  ```typescript
49
- const treeData: LimbleTreeData = [
50
- {
51
- myValue: "abc",
52
- mySecondValue: 10,
53
- nodes: [
54
- { myValue: "def", mySecondValue: 20 },
55
- {
56
- myValue: "ghi",
57
- mySecondValue: 30,
58
- nodes: [
59
- { myValue: "jkl", mySecondValue: 40 },
60
- { myOtherValue: { prop1: "mno", prop2: "pqr" } }
61
- ]
62
- }
63
- ]
64
- },
65
- { myOtherValue: { prop1: "stu", prop2: "vwx" } }
66
- ];
181
+ tree.grow(MyTreeContentComponent, {
182
+ meta: { myCustomDataField: "Hello World!" }
183
+ });
67
184
  ```
68
185
 
69
- 3. Create an object describing the tree's options.
186
+ The value of `meta` can be accessed within your rendered component by calling `this.treeBranch.meta()`.
70
187
 
71
188
  ```typescript
72
- const treeOptions: LimbleTreeOptions = {
73
- defaultComponent: {
74
- class: MyComponentClass,
75
- bindings: { binding1: value1, binding2: value2 }
76
- },
77
- indent: 60
78
- };
189
+ @Input() treeBranch?: TreeBranch<MyTreeContentComponent>;
190
+
191
+ public ngOnInit(): void {
192
+ console.log(this.treeBranch?.meta()) // outputs `{ myCustomDataField: "Hello World!" }`
193
+ }
79
194
  ```
80
195
 
81
- 4. Add a `<limble-tree-root>` component to one of your components' templates and pass it the data array and the options object:
196
+ ## Indentation
197
+
198
+ By default, each level of the tree beyond the first will be indented an additional 16px. This value is configurable:
199
+
200
+ ```typescript
201
+ treeService.createEmptyTree(this.treeContainer, { indentation: 32 });
202
+ ```
203
+
204
+ ## Collapsing Branches
205
+
206
+ Tree branches which have descendant branches can be collapsed, temporarily removing its descendants from the tree. The descendants can be grafted back into the tree later, as if they were never removed.
207
+
208
+ To collapse a branch, simply inject the TreeCollapseService into your component and call `collapse()`, passing in the branch to be collapsed. To restore the hidden branches, call `expand()`, passing in the same branch that was passed to `collapse()`
209
+
210
+ ```typescript
211
+ constructor(private readonly collapseService: TreeCollapseService) {}
212
+
213
+ public ngAfterViewInit(): void {
214
+ this.tree = this.treeService.createEmptyTree<MyTreeContentComponent>(
215
+ this.treeContainer
216
+ );
217
+ // Renders four instances of the MyTreeContentComponent;
218
+ // the fourth is nested under the third.
219
+ const branch1 = this.tree.grow(MyTreeContentComponent);
220
+ const branch2 = this.tree.grow(MyTreeContentComponent);
221
+ const branch3 = this.tree.grow(MyTreeContentComponent);
222
+ const branch3a = branch3.grow(MyTreContentComponent);
223
+
224
+ //Hides the fourth branch
225
+ this.collapseService.collapse(branch3);
226
+
227
+ //restores the fourth branch to its original place after 2 seconds have passed.
228
+ setTimeout(() => {
229
+ this.collapseService.expand(branch3);
230
+ }, 2000);
231
+ }
232
+ ```
233
+
234
+ A branch can be configured to be collapsed by default, which means that any children grown onto it will not be visible until `expand()` is called.
235
+
236
+ ```typescript
237
+ constructor(private readonly collapseService: TreeCollapseService) {}
238
+
239
+ public ngAfterViewInit(): void {
240
+ this.tree = this.treeService.createEmptyTree<MyTreeContentComponent>(
241
+ this.treeContainer
242
+ );
243
+ // Renders four instances of the MyTreeContentComponent;
244
+ // the fourth is nested under the third.
245
+ const branch1 = this.tree.grow(MyTreeContentComponent);
246
+ const branch2 = this.tree.grow(MyTreeContentComponent);
247
+ const branch3 = this.tree.grow(MyTreeContentComponent, {defaultCollapsed: true});
248
+ const branch3a = branch3.grow(MyTreContentComponent); // this branch will be created but not rendered
249
+
250
+ //renders the fourth branch after 2 seconds have passed.
251
+ setTimeout(() => {
252
+ this.collapseService.expand(branch3);
253
+ }, 2000);
254
+ }
255
+ ```
256
+
257
+ You can check if a branch can be expanded using the collapse service's `isCollapsed()` method.
258
+
259
+ ## Moving Branches Programmatically
260
+
261
+ Branches can be moved around, both within a single tree and between different trees.
262
+
263
+ ```typescript
264
+ public ngAfterViewInit(): void {
265
+ this.tree1 = this.treeService.createEmptyTree<MyTreeContentComponent>(
266
+ this.treeContainer1
267
+ );
268
+ // Renders four instances of the MyTreeContentComponent;
269
+ // the fourth is nested under the third.
270
+ const branch1 = this.tree1.grow(MyTreeContentComponent);
271
+ const branch2 = this.tree1.grow(MyTreeContentComponent);
272
+ const branch3 = this.tree1.grow(MyTreeContentComponent);
273
+ const branch3a = branch3.grow(MyTreContentComponent);
274
+
275
+ setTimeout(() => {
276
+ //moves branch1 so it is now the second child of branch3
277
+ branch1.graftTo(branch3);
278
+ }, 2000);
279
+
280
+ setTimeout(() => {
281
+ // moves branch1 back to its original position.
282
+ branch1.graftTo(this.tree1, 0);
283
+ }, 4000);
284
+
285
+ setTimeout(() => {
286
+ this.tree2 = this.treeService.createEmptyTree<MyTreeContentComponent>(
287
+ this.treeContainer2
288
+ );
289
+ // moves branch1 to a different tree
290
+ branch1.graftTo(this.tree2);
291
+ }, 8000);
292
+ }
293
+ ```
294
+
295
+ A branch can also be removed from a tree without immediately grafting it to a new position.
296
+
297
+ ```typescript
298
+ public ngAfterViewInit(): void {
299
+ this.tree = this.treeService.createEmptyTree<MyTreeContentComponent>(
300
+ this.treeContainer
301
+ );
302
+ // Renders four instances of the MyTreeContentComponent;
303
+ // the fourth is nested under the third.
304
+ const branch1 = this.tree.grow(MyTreeContentComponent);
305
+ const branch2 = this.tree.grow(MyTreeContentComponent);
306
+ const branch3 = this.tree.grow(MyTreeContentComponent);
307
+ const branch3a = branch3.grow(MyTreContentComponent);
308
+
309
+ //removes branch1 from the tree after 2 seconds
310
+ setTimeout(() => {
311
+ branch1.prune();
312
+ }, 2000);
313
+
314
+ //restores branch1 to its original position after an additional 2 seconds
315
+ setTimeout(() => {
316
+ branch1.graftTo(this.tree, 0);
317
+ }, 4000);
318
+ ```
319
+
320
+ Both the `prune()` and `graftTo()` methods will include all of the children of the branch as well. For example, pruning a branch that has 3 child branches will remove the parent as well as all three children; grafting the parent back into a tree will also graft the children into that tree, retaining their position as children of the moved parent.
321
+
322
+ Note: use `destroy()` rather than `prune()` when you wish to completely delete a branch. See "Destroying Trees and Branches" below.
323
+
324
+ ## Moving Branches With Drag-And-Drop
325
+
326
+ Drag and drop functionality is easy to use. Simply add the `[limbleTreeDraggable]` directive to an element of your rendered component, and pass it that component's associated TreeBranch. When the user clicks and drags the element, the branch will be pruned from the tree and dragged with the mouse.
82
327
 
83
328
  ```html
84
- <limble-tree-root [data]="treeData" [options]="treeOptions"></limble-tree-root>
329
+ <div [limbleTreeDraggable]="treeBranch">Drag Me!</div>
85
330
  ```
86
331
 
87
- This should render the tree, producing an instance of `MyComponentClass` for each node in the tree data.
332
+ As the user drags the branch over other branches of the tree, dropzones will appear to indicate where the dragged branch may be placed. Dropping into a dropzone will graft the dragged branch at that location.
88
333
 
89
- ### The LimbleTreeData Array
334
+ ### Configurable Restrictions For Drag-And-Drop
90
335
 
91
- The LimbleTreeData array can have objects of any configuration. There are two properties that the library looks for on these objects:
336
+ A tree can be configured to disallow certain branches from...
92
337
 
93
- - `nodes`: This property is an array of objects just like LimbleTreeData. Objects in this array are considered children of that object, and will cause a component to be rendered for each element in the array. The children will be rendered on a new branch "under" the parent.
94
- - `component`: This property is an object in the form of `{class: <ComponentClass>, bindings: {bindingName: bindingValue, ...}}`. It is optional as long as there is a `defaultComponent` declared in the tree options object. If this property is found on a node, it will be used instead of the `defaultComponent` for rendering that node. See the `defaultComponent` option below for more information.
338
+ 1. being dragged at all
339
+ 2. being dropped at specific locations in the tree
95
340
 
96
- ### The LimbleTreeOptions Object
341
+ When calling `createEmptyTree()`, there are three options that can be passed to the second parameter to control these restrictions. There are no restrictions by default.
97
342
 
98
- The LimbleTreeOptions object is used to configure the tree's general settings. Options include:
343
+ ```typescript
344
+ public ngAfterViewInit(): void {
345
+ this.tree = this.treeService.createEmptyTree<MyTreeContentComponent>(
346
+ this.treeContainer,
347
+ {
348
+ // Prevents dragging branches which have less than three children
349
+ allowDragging: (treeBranch) => treeBranch.branches().length < 3,
350
+ // Prevents drops on first-level branches
351
+ allowDrop: (source, parent, index) => parent.parent() !== undefined,
352
+ // Does not allow any branches to be dropped under a branch whose metaData property `noChildren` is set to `false`
353
+ allowNesting: (treeBranch) => treeBranch.meta().noChildren === false
354
+ }
355
+ );
356
+ ```
99
357
 
100
- - `defaultComponent`: This property is an object in the form of `{class: <ComponentClass>, bindings: {bindingName: bindingValue, ...}}`. For each node in the data array, the component described by this object will be rendered. The tree node object will be passed in to the component as an input called `nodeData`. The component's inputs and outputs will be initialized using the bindings object. If a tree node contains a `component` property, that component information will be used instead of the `defaultComponent`. An error will be thrown if (1) the `defaultComponent` is not defined; and (2) the library encounters a tree node that does not have a `component` property.
101
- - `indent`: The number of pixels to indent for each level of the tree. Defaults to 45.
102
- - `allowNesting`: Whether to allow "nesting" (placing a node one level deeper than currently exists on the branch) under a node. May be a boolean or a callback function that returns a boolean. If it is a callback, the callback will be called for each node when another node is attempting to nest under it. The parent node (the one which is potentially being nested under) will be passed in to the callback. Defaults to true.
103
- - `allowDragging`: Whether to allow drag-and-drop functionality. May be a boolean or a callback function that returns a boolean. If it is a callback, the callback will be called for each node when a drag is attempted on it, and that node will be passed in to the callback. Defaults to true.
104
- - `allowDrop`: A callback that determines whether a sourceNode can be dropped at a particular location. If it returns true, the drop is allowed; if it returns false, the drop is not allowed. This function takes three parameters: the node being dragged, the proposed parent node, and the proposed index under that parent. Defaults to `() => true`.
105
- - `listMode`: When set to true, list mode will enforce a flat tree structure, meaning there can only be one level of the tree. `allowNesting` is automatically set to `false` and any children will be deleted. This mode can be used when the same dynamic drag and drop functionality of the tree is desired, but the tree structure itself is not necessary. This also opens up the pagination API on the limble-tree-root component. See the pagination section below for details about pagination.
358
+ These options only apply to drag-and-drop functionality. They do not restrict programmatic movement of branches, such as when using the `prune()` or `graftTo()` methods.
106
359
 
107
- ### The LimbleTreeRoot Component
360
+ More details about these configuration options can be found in `tree-options.interface.ts`.
108
361
 
109
- Here are the inputs and outputs of the `<limble-tree-root>` component:
362
+ ## Watching For Tree Events
110
363
 
111
- - input `data` -- a LimbleTreeData array. Required.
112
- - input `options` -- a LimbleTreeOptions object.
113
- - input `itemsPerPage` -- A number indicating how many many items to display at a time. See the "Pagination" section below.
114
- - input `page` -- A number indicating the current page og items. See the "Pagination" section below.
115
- - output `treeChange` -- an event that fires whenever the tree is rendered or re-rendered.
116
- - output `treeDrop` -- an event that fires after a node is dropped in the tree. The event contains data described by the TreeDrop interface, given here:
364
+ Both the TreeBranch and TreeRoot objects will emit information when certain events occur. These events can be captured by subscribing to the observable returned by the `events()` method.
117
365
 
118
366
  ```typescript
119
- export interface TreeDrop {
120
- /** The node that was dropped */
121
- target: LimbleTreeNode;
122
- /** the target's parent before the drag and drop, or null if it was a top-level node */
123
- oldParent: LimbleTreeNode | null;
124
- /** the index of the node before the drag and drop relative to its old siblings */
125
- oldIndex: number;
126
- /** the target's parent after the drag and drop, or null if it is now a top-level node */
127
- newParent: LimbleTreeNode | null;
128
- /** the index of the node after the drag and drop relative to its new siblings */
129
- newIndex: number;
367
+ public ngAfterViewInit(): void {
368
+ this.tree = this.treeService.createEmptyTree<MyTreeContentComponent>(
369
+ this.treeContainer
370
+ );
371
+ this.tree.events().subscribe((event) => {
372
+ console.log(event.type());
373
+ });
374
+ const branch1 = this.tree.grow(MyTreeContentComponent); // will output "graft"
375
+ const branch2 = this.tree.grow(MyTreeContentComponent); // will output "graft"
376
+ const branch3 = branch2.grow(MyTreeContentComponent); //will output "graft"
377
+ this.tree.destroy(); //will output "destruction" four times; one for each branch, and one for the root.
130
378
  }
131
379
  ```
132
380
 
133
- ### Drag Handles
381
+ Events bubble up the tree. So subscribing to the root's `events()` observable will capture all tree events for that tree. Subscribing to the `events()` observable of a branch will capture all tree events for that branch and its descendants.
382
+
383
+ Below is a list of event types that are currently emitted by the tree.
384
+
385
+ - drag start
386
+ - drop
387
+ - drag end
388
+ - prune
389
+ - graft
390
+ - destruction
391
+
392
+ Each event contains applicable information about the event, such as the nodes involved, position in the tree, etc.
393
+
394
+ ## Traversing The Tree
395
+
396
+ ### Parents and Children
397
+
398
+ Trees are doubly-linked, meaning that each node in the tree holds a pointer to its parent and a pointer to each of its children. These pointers are accessed using the `parent()` and `branches()` methods, respectively. The order of child branches is maintained. The `getBranch()` method takes an index as an argument and returns the branch at that index. A branch's position relative to its siblings is obtained using the `index()` method.
399
+
400
+ Unlike TreeBranch instances, a TreeRoot does not have a `parent()` or `index()` method, but it does have a `branches()` and `getBranch()` methods.
401
+
402
+ TreeBranch instances have a method `root()`, which returns the root of their current tree. TreeRoot instances also have this method, and in that case it always returns itself.
403
+
404
+ ```typescript
405
+ public ngAfterViewInit(): void {
406
+ this.tree = this.treeService.createEmptyTree<MyTreeContentComponent>(
407
+ this.treeContainer
408
+ );
409
+ this.tree.events().subscribe((event) => {
410
+ console.log(event.type());
411
+ });
412
+ const branch1 = this.tree.grow(MyTreeContentComponent);
413
+ const branch2 = this.tree.grow(MyTreeContentComponent);
414
+ const branch2a = branch2.grow(MyTreeContentComponent);
415
+ const branch2b = branch2.grow(MyTreeContentComponent);
416
+ const branch2a1 = branch2a.grow(MyTreeContentComponent);
417
+ const branch2a1 = branch2a.grow(MyTreeContentComponent);
418
+
419
+ //All of these expressions are true
420
+ assert(branch2b.parent() === branch2);
421
+ assert(branch2b.index() === 1);
422
+ assert(branch2.branches()[1] === branch2b);
423
+ assert(branch2a1.branches().length === 0);
424
+ assert(branch1.parent() === this.tree);
425
+ assert(this.tree.getBranch(2) === branch2);
426
+ assert(branch2a1.root() === this.tree);
427
+ }
428
+ ```
429
+
430
+ ### Traversal Utility Methods
431
+
432
+ There are a couple other methods used to traverse the tree.
433
+
434
+ - `traverse()` traverses the tree in [depth-first pre-order](https://en.wikipedia.org/wiki/Tree_traversal#Pre-order,_NLR) and executes a provided callback on each node.
435
+ - `plot()` traverses the tree and returns a many-dimensional [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)representing the shape of the tree.
436
+
437
+ ### Get Branch's Current Location in the Tree
438
+
439
+ You can call `position()` on a TreeBranch to get an array of indexes, indicating the path from the root to that branch.
440
+
441
+ Example: If `position()` returns [2, 2, 4], that means the branch is a great-grandchild of the root (three elements == three levels deep), and the branch could be accessed like so:
442
+
443
+ ```typescript
444
+ root.getBranch(2).getBranch(2).getBranch(4);
445
+ ```
446
+
447
+ ## Destroying Trees And Branches
448
+
449
+ The `destroy()` method must be called on a TreeRoot in order for its resources to be properly released. Failing to do so will cause memory leaks and other performance problems.
134
450
 
135
- Adding the `limble-tree-handle` css class to an element in a node component will designate that element as the drag handle, making it so the node can only be dragged by clicking on that element.
451
+ Branches also have a `destroy()` method. Calling `destroy()` on the TreeRoot will automatically call the `destroy()` method of each branch in the tree, including branches that have been collapsed.
136
452
 
137
- ### Pagination
453
+ Branches that have been otherwise pruned are not part of any tree; their `destroy()` method must be called manually in order to release their resources. Calling a branch's `destroy()` method will automatically call the `destroy()` method of each descendant branch, including descendants that are collapsed.
138
454
 
139
- When the `listMode` option is set to true, the pagination API is made available. Pagination is accomplished using two of the inputs of the `limble-tree-root` component: the `itemsPerPage` input and the `page` input. These inputs will not do anything unless `listMode` is turned on. When in `listMode`, the list will split into pages, where each page contains `itemsPerPage` number of items. (Note that the last page may have fewer items than the `itemsPerPage` number.) Only one page will be displayed at a time. The `page` input indicates which page to show: When `page` is 1, the first page will display, and so on.
455
+ Destroyed roots and branches have very limited functionality. Many methods will simply throw an error if the instance has been previously destroyed. You can check if a node is destroyed with the `isDestroyed()` method.
140
456
 
141
- ### Demo App
457
+ ## Accessing Underlying Structures
142
458
 
143
- A demo app can be run by following the instructions on our [github repo](https://github.com/LimbleCMMS/limble-tree).
459
+ There are methods on TreeBranch and TreeRoot instances which grant access to underlying structures. We recommend only using these in advanced scenarios. They are not fully documented here at this time.
144
460
 
145
- ## Issues, Feature Requests, Etc
461
+ - detectChanges()
462
+ - dispatch()
463
+ - getBranchesContainer()
464
+ - getComponentInstance()
465
+ - getHostView()
466
+ - getNativeElement()
467
+ - getUserlandComponentRef()
146
468
 
147
- If you find an issue or you would like to see an improvement, you may create an "issue" or start a "discussion" on our [github repo](https://github.com/LimbleCMMS/limble-tree).
469
+ ## Development and Contributions
148
470
 
149
- We truly appreciate feedback; but keep in mind that we, the Limble team, built this library for our own needs, and requests from outside our organization may not always be a high priority.
471
+ See the readme in our [Github repository](https://github.com/LimbleCMMS/limble-tree).