@acorex/components 20.2.48 → 20.2.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/calendar/index.d.ts +4 -0
- package/fesm2022/acorex-components-calendar.mjs +15 -0
- package/fesm2022/acorex-components-calendar.mjs.map +1 -1
- package/fesm2022/acorex-components-data-pager.mjs +4 -1
- package/fesm2022/acorex-components-data-pager.mjs.map +1 -1
- package/fesm2022/acorex-components-number-box.mjs +6 -5
- package/fesm2022/acorex-components-number-box.mjs.map +1 -1
- package/fesm2022/acorex-components-tree-view.mjs +1213 -238
- package/fesm2022/acorex-components-tree-view.mjs.map +1 -1
- package/fesm2022/acorex-components-wysiwyg.mjs +2 -2
- package/fesm2022/acorex-components-wysiwyg.mjs.map +1 -1
- package/number-box/index.d.ts +1 -1
- package/package.json +3 -3
- package/tree-view/index.d.ts +451 -62
- package/wysiwyg/index.d.ts +1 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { moveItemInArray, transferArrayItem, AXDragDirective, AXDragHandleDirective, AXDropListDirective } from '@acorex/cdk/drag-drop';
|
|
2
|
+
import { AXFocusTrapDirective } from '@acorex/cdk/focus-trap';
|
|
2
3
|
import { AXBadgeComponent } from '@acorex/components/badge';
|
|
3
4
|
import { AXButtonComponent } from '@acorex/components/button';
|
|
4
5
|
import { AXCheckBoxComponent } from '@acorex/components/check-box';
|
|
5
6
|
import { AXDecoratorIconComponent } from '@acorex/components/decorators';
|
|
7
|
+
import { AXTooltipDirective } from '@acorex/components/tooltip';
|
|
6
8
|
import { AXPlatform } from '@acorex/core/platform';
|
|
7
9
|
import * as i1 from '@angular/common';
|
|
8
10
|
import { CommonModule, NgTemplateOutlet } from '@angular/common';
|
|
@@ -26,12 +28,13 @@ class AXTreeViewService {
|
|
|
26
28
|
/**
|
|
27
29
|
* Find a node by ID in the tree
|
|
28
30
|
*/
|
|
29
|
-
findNodeById(nodes, id) {
|
|
31
|
+
findNodeById(nodes, id, idField = 'id') {
|
|
30
32
|
for (const node of nodes) {
|
|
31
|
-
if (node
|
|
33
|
+
if (node[idField] === id)
|
|
32
34
|
return node;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
const children = node['children'];
|
|
36
|
+
if (children) {
|
|
37
|
+
const found = this.findNodeById(children, id, idField);
|
|
35
38
|
if (found)
|
|
36
39
|
return found;
|
|
37
40
|
}
|
|
@@ -41,13 +44,15 @@ class AXTreeViewService {
|
|
|
41
44
|
/**
|
|
42
45
|
* Find parent node of a given node
|
|
43
46
|
*/
|
|
44
|
-
findParentNode(nodes, targetNode) {
|
|
47
|
+
findParentNode(nodes, targetNode, idField = 'id', childrenField = 'children') {
|
|
48
|
+
const targetId = targetNode[idField];
|
|
45
49
|
for (const node of nodes) {
|
|
46
|
-
|
|
50
|
+
const children = node[childrenField];
|
|
51
|
+
if (children?.some((child) => child[idField] === targetId)) {
|
|
47
52
|
return node;
|
|
48
53
|
}
|
|
49
|
-
if (
|
|
50
|
-
const found = this.findParentNode(
|
|
54
|
+
if (children) {
|
|
55
|
+
const found = this.findParentNode(children, targetNode, idField, childrenField);
|
|
51
56
|
if (found)
|
|
52
57
|
return found;
|
|
53
58
|
}
|
|
@@ -58,8 +63,10 @@ class AXTreeViewService {
|
|
|
58
63
|
* Check if targetNode is a descendant of ancestorNode (or the same node)
|
|
59
64
|
* Prevents circular references by checking if target exists in ancestor's children tree
|
|
60
65
|
*/
|
|
61
|
-
isValidDropTarget(movedNode, targetNode) {
|
|
62
|
-
|
|
66
|
+
isValidDropTarget(movedNode, targetNode, idField = 'id', childrenField = 'children') {
|
|
67
|
+
const movedId = movedNode[idField];
|
|
68
|
+
const targetId = targetNode[idField];
|
|
69
|
+
if (movedId === targetId || this.isNodeDescendantOf(targetNode, movedNode, idField, childrenField)) {
|
|
63
70
|
return false;
|
|
64
71
|
}
|
|
65
72
|
return true;
|
|
@@ -67,18 +74,22 @@ class AXTreeViewService {
|
|
|
67
74
|
/**
|
|
68
75
|
* Check if targetNode is a descendant of ancestorNode
|
|
69
76
|
*/
|
|
70
|
-
isNodeDescendantOf(targetNode, ancestorNode) {
|
|
71
|
-
|
|
77
|
+
isNodeDescendantOf(targetNode, ancestorNode, idField = 'id', childrenField = 'children') {
|
|
78
|
+
const targetId = targetNode[idField];
|
|
79
|
+
const ancestorId = ancestorNode[idField];
|
|
80
|
+
if (targetId === ancestorId) {
|
|
72
81
|
return true;
|
|
73
82
|
}
|
|
74
|
-
|
|
83
|
+
const children = ancestorNode[childrenField];
|
|
84
|
+
if (!children || children.length === 0) {
|
|
75
85
|
return false;
|
|
76
86
|
}
|
|
77
|
-
for (const child of
|
|
78
|
-
|
|
87
|
+
for (const child of children) {
|
|
88
|
+
const childId = child[idField];
|
|
89
|
+
if (childId === targetId) {
|
|
79
90
|
return true;
|
|
80
91
|
}
|
|
81
|
-
if (this.isNodeDescendantOf(targetNode, child)) {
|
|
92
|
+
if (this.isNodeDescendantOf(targetNode, child, idField, childrenField)) {
|
|
82
93
|
return true;
|
|
83
94
|
}
|
|
84
95
|
}
|
|
@@ -87,14 +98,18 @@ class AXTreeViewService {
|
|
|
87
98
|
/**
|
|
88
99
|
* Build a flat list of all visible focusable nodes
|
|
89
100
|
*/
|
|
90
|
-
buildFlatNodeList(nodes) {
|
|
101
|
+
buildFlatNodeList(nodes, hiddenField = 'hidden', disabledField = 'disabled', expandedField = 'expanded', childrenField = 'children') {
|
|
91
102
|
const flatList = [];
|
|
92
103
|
const traverse = (nodeList, level, parent) => {
|
|
93
104
|
for (const node of nodeList) {
|
|
94
|
-
|
|
105
|
+
const hidden = node[hiddenField];
|
|
106
|
+
const disabled = node[disabledField];
|
|
107
|
+
if (hidden !== true && !disabled) {
|
|
95
108
|
flatList.push({ node, level, parent });
|
|
96
|
-
|
|
97
|
-
|
|
109
|
+
const expanded = node[expandedField];
|
|
110
|
+
const children = node[childrenField];
|
|
111
|
+
if (expanded && children) {
|
|
112
|
+
traverse(children, level + 1, node);
|
|
98
113
|
}
|
|
99
114
|
}
|
|
100
115
|
}
|
|
@@ -106,39 +121,44 @@ class AXTreeViewService {
|
|
|
106
121
|
/**
|
|
107
122
|
* Check if node has children
|
|
108
123
|
*/
|
|
109
|
-
hasChildren(node) {
|
|
110
|
-
|
|
124
|
+
hasChildren(node, childrenField = 'children') {
|
|
125
|
+
const children = node[childrenField];
|
|
126
|
+
return Boolean(children?.length);
|
|
111
127
|
}
|
|
112
128
|
/**
|
|
113
129
|
* Check if node can be lazy loaded
|
|
114
130
|
*/
|
|
115
|
-
canLazyLoad(node, isLazyDataSource) {
|
|
116
|
-
|
|
131
|
+
canLazyLoad(node, isLazyDataSource, childrenCountField = 'childrenCount', childrenField = 'children') {
|
|
132
|
+
const childrenCount = node[childrenCountField];
|
|
133
|
+
return isLazyDataSource && Boolean(childrenCount && childrenCount > 0 && !this.hasChildren(node, childrenField));
|
|
117
134
|
}
|
|
118
135
|
// ==================== Selection Management ====================
|
|
119
136
|
/**
|
|
120
137
|
* Recursively select/deselect all children
|
|
121
138
|
*/
|
|
122
|
-
selectAllChildren(children, selected) {
|
|
139
|
+
selectAllChildren(children, selected, selectedField = 'selected', indeterminateField = 'indeterminate', childrenField = 'children') {
|
|
123
140
|
for (const child of children) {
|
|
124
|
-
child
|
|
125
|
-
child
|
|
126
|
-
|
|
127
|
-
|
|
141
|
+
child[selectedField] = selected;
|
|
142
|
+
child[indeterminateField] = false;
|
|
143
|
+
const childChildren = child[childrenField];
|
|
144
|
+
if (childChildren) {
|
|
145
|
+
this.selectAllChildren(childChildren, selected, selectedField, indeterminateField, childrenField);
|
|
128
146
|
}
|
|
129
147
|
}
|
|
130
148
|
}
|
|
131
149
|
/**
|
|
132
150
|
* Get selection state of children
|
|
133
151
|
*/
|
|
134
|
-
getChildrenSelectionState(children) {
|
|
152
|
+
getChildrenSelectionState(children, selectedField = 'selected', indeterminateField = 'indeterminate') {
|
|
135
153
|
let selectedCount = 0;
|
|
136
154
|
let indeterminateCount = 0;
|
|
137
155
|
for (const child of children) {
|
|
138
|
-
|
|
156
|
+
const selected = child[selectedField];
|
|
157
|
+
const indeterminate = child[indeterminateField];
|
|
158
|
+
if (selected && !indeterminate) {
|
|
139
159
|
selectedCount++;
|
|
140
160
|
}
|
|
141
|
-
if (
|
|
161
|
+
if (indeterminate) {
|
|
142
162
|
indeterminateCount++;
|
|
143
163
|
}
|
|
144
164
|
}
|
|
@@ -150,65 +170,69 @@ class AXTreeViewService {
|
|
|
150
170
|
/**
|
|
151
171
|
* Update parent node states based on children selection (with intermediate state support)
|
|
152
172
|
*/
|
|
153
|
-
updateParentStates(nodes, changedNode, intermediateState) {
|
|
154
|
-
const parent = this.findParentNode(nodes, changedNode);
|
|
155
|
-
|
|
173
|
+
updateParentStates(nodes, changedNode, intermediateState, idField = 'id', childrenField = 'children', selectedField = 'selected', indeterminateField = 'indeterminate') {
|
|
174
|
+
const parent = this.findParentNode(nodes, changedNode, idField, childrenField);
|
|
175
|
+
const parentChildren = parent?.[childrenField];
|
|
176
|
+
if (!parent || !parentChildren)
|
|
156
177
|
return;
|
|
157
|
-
const { allSelected, someSelected } = this.getChildrenSelectionState(
|
|
178
|
+
const { allSelected, someSelected } = this.getChildrenSelectionState(parentChildren, selectedField, indeterminateField);
|
|
158
179
|
if (allSelected) {
|
|
159
|
-
parent
|
|
160
|
-
parent
|
|
180
|
+
parent[selectedField] = true;
|
|
181
|
+
parent[indeterminateField] = false;
|
|
161
182
|
}
|
|
162
183
|
else if (someSelected) {
|
|
163
184
|
if (intermediateState) {
|
|
164
|
-
parent
|
|
165
|
-
parent
|
|
185
|
+
parent[selectedField] = true;
|
|
186
|
+
parent[indeterminateField] = true;
|
|
166
187
|
}
|
|
167
188
|
else {
|
|
168
|
-
parent
|
|
169
|
-
parent
|
|
189
|
+
parent[selectedField] = false;
|
|
190
|
+
parent[indeterminateField] = false;
|
|
170
191
|
}
|
|
171
192
|
}
|
|
172
193
|
else {
|
|
173
|
-
parent
|
|
174
|
-
parent
|
|
194
|
+
parent[selectedField] = false;
|
|
195
|
+
parent[indeterminateField] = false;
|
|
175
196
|
}
|
|
176
|
-
this.updateParentStates(nodes, parent, intermediateState);
|
|
197
|
+
this.updateParentStates(nodes, parent, intermediateState, idField, childrenField, selectedField, indeterminateField);
|
|
177
198
|
}
|
|
178
199
|
/**
|
|
179
200
|
* Recursively deselect all nodes
|
|
180
201
|
*/
|
|
181
|
-
deselectAllNodes(nodes) {
|
|
202
|
+
deselectAllNodes(nodes, selectedField = 'selected', indeterminateField = 'indeterminate', childrenField = 'children') {
|
|
182
203
|
for (const node of nodes) {
|
|
183
|
-
node
|
|
184
|
-
node
|
|
185
|
-
|
|
186
|
-
|
|
204
|
+
node[selectedField] = false;
|
|
205
|
+
node[indeterminateField] = false;
|
|
206
|
+
const children = node[childrenField];
|
|
207
|
+
if (children) {
|
|
208
|
+
this.deselectAllNodes(children, selectedField, indeterminateField, childrenField);
|
|
187
209
|
}
|
|
188
210
|
}
|
|
189
211
|
}
|
|
190
212
|
/**
|
|
191
213
|
* Recursively set selection state for all nodes
|
|
192
214
|
*/
|
|
193
|
-
setAllSelection(nodes, selected) {
|
|
215
|
+
setAllSelection(nodes, selected, selectedField = 'selected', indeterminateField = 'indeterminate', childrenField = 'children') {
|
|
194
216
|
for (const node of nodes) {
|
|
195
|
-
node
|
|
196
|
-
node
|
|
197
|
-
|
|
198
|
-
|
|
217
|
+
node[selectedField] = selected;
|
|
218
|
+
node[indeterminateField] = false;
|
|
219
|
+
const children = node[childrenField];
|
|
220
|
+
if (children) {
|
|
221
|
+
this.setAllSelection(children, selected, selectedField, indeterminateField, childrenField);
|
|
199
222
|
}
|
|
200
223
|
}
|
|
201
224
|
}
|
|
202
225
|
/**
|
|
203
226
|
* Recursively count selected nodes
|
|
204
227
|
*/
|
|
205
|
-
countSelected(nodes) {
|
|
228
|
+
countSelected(nodes, selectedField = 'selected', childrenField = 'children') {
|
|
206
229
|
let count = 0;
|
|
207
230
|
for (const node of nodes) {
|
|
208
|
-
if (node
|
|
231
|
+
if (node[selectedField])
|
|
209
232
|
count++;
|
|
210
|
-
|
|
211
|
-
|
|
233
|
+
const children = node[childrenField];
|
|
234
|
+
if (children) {
|
|
235
|
+
count += this.countSelected(children, selectedField, childrenField);
|
|
212
236
|
}
|
|
213
237
|
}
|
|
214
238
|
return count;
|
|
@@ -216,53 +240,61 @@ class AXTreeViewService {
|
|
|
216
240
|
/**
|
|
217
241
|
* Recursively collect selected nodes
|
|
218
242
|
*/
|
|
219
|
-
collectSelected(nodes, result) {
|
|
243
|
+
collectSelected(nodes, result, selectedField = 'selected', childrenField = 'children') {
|
|
220
244
|
for (const node of nodes) {
|
|
221
|
-
if (node
|
|
245
|
+
if (node[selectedField])
|
|
222
246
|
result.push(node);
|
|
223
|
-
|
|
224
|
-
|
|
247
|
+
const children = node[childrenField];
|
|
248
|
+
if (children) {
|
|
249
|
+
this.collectSelected(children, result, selectedField, childrenField);
|
|
225
250
|
}
|
|
226
251
|
}
|
|
227
252
|
}
|
|
228
253
|
/**
|
|
229
254
|
* Recursively remove selected nodes
|
|
230
255
|
*/
|
|
231
|
-
removeSelected(nodes) {
|
|
256
|
+
removeSelected(nodes, selectedField = 'selected', indeterminateField = 'indeterminate', childrenField = 'children') {
|
|
232
257
|
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
233
|
-
|
|
258
|
+
const node = nodes[i];
|
|
259
|
+
const selected = node[selectedField];
|
|
260
|
+
const indeterminate = node[indeterminateField];
|
|
261
|
+
if (selected && !indeterminate) {
|
|
234
262
|
nodes.splice(i, 1);
|
|
235
263
|
}
|
|
236
|
-
else
|
|
237
|
-
|
|
264
|
+
else {
|
|
265
|
+
const children = node[childrenField];
|
|
266
|
+
if (children) {
|
|
267
|
+
this.removeSelected(children, selectedField, indeterminateField, childrenField);
|
|
268
|
+
}
|
|
238
269
|
}
|
|
239
270
|
}
|
|
240
271
|
}
|
|
241
272
|
/**
|
|
242
273
|
* Recursively update all parent states in the tree (used after deletion)
|
|
243
274
|
*/
|
|
244
|
-
updateAllParentStates(nodes, intermediateState) {
|
|
275
|
+
updateAllParentStates(nodes, intermediateState, childrenField = 'children', selectedField = 'selected', indeterminateField = 'indeterminate') {
|
|
245
276
|
for (const node of nodes) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
277
|
+
const children = node[childrenField];
|
|
278
|
+
if (children && children.length > 0) {
|
|
279
|
+
this.updateAllParentStates(children, intermediateState, childrenField, selectedField, indeterminateField);
|
|
280
|
+
const { allSelected, someSelected } = this.getChildrenSelectionState(children, selectedField, indeterminateField);
|
|
249
281
|
if (allSelected) {
|
|
250
|
-
node
|
|
251
|
-
node
|
|
282
|
+
node[selectedField] = true;
|
|
283
|
+
node[indeterminateField] = false;
|
|
252
284
|
}
|
|
253
285
|
else if (someSelected) {
|
|
254
286
|
if (intermediateState) {
|
|
255
|
-
node
|
|
256
|
-
node
|
|
287
|
+
node[selectedField] = true;
|
|
288
|
+
node[indeterminateField] = true;
|
|
257
289
|
}
|
|
258
290
|
else {
|
|
259
|
-
node
|
|
260
|
-
node
|
|
291
|
+
node[selectedField] = false;
|
|
292
|
+
node[indeterminateField] = false;
|
|
261
293
|
}
|
|
262
294
|
}
|
|
263
295
|
else {
|
|
264
|
-
node
|
|
265
|
-
node
|
|
296
|
+
node[selectedField] = false;
|
|
297
|
+
node[indeterminateField] = false;
|
|
266
298
|
}
|
|
267
299
|
}
|
|
268
300
|
}
|
|
@@ -271,17 +303,18 @@ class AXTreeViewService {
|
|
|
271
303
|
/**
|
|
272
304
|
* Recursively set expanded state (with lazy loading)
|
|
273
305
|
*/
|
|
274
|
-
async setExpandedState(nodes, expanded, isLazyDataSource, loadNodeChildren) {
|
|
306
|
+
async setExpandedState(nodes, expanded, isLazyDataSource, loadNodeChildren, expandedField = 'expanded', childrenField = 'children', childrenCountField = 'childrenCount') {
|
|
275
307
|
for (const node of nodes) {
|
|
276
|
-
const hasChildren = this.hasChildren(node);
|
|
277
|
-
const canLazyLoad = this.canLazyLoad(node, isLazyDataSource);
|
|
308
|
+
const hasChildren = this.hasChildren(node, childrenField);
|
|
309
|
+
const canLazyLoad = this.canLazyLoad(node, isLazyDataSource, childrenCountField, childrenField);
|
|
278
310
|
if (hasChildren || canLazyLoad) {
|
|
279
311
|
if (expanded && canLazyLoad) {
|
|
280
312
|
await loadNodeChildren(node);
|
|
281
313
|
}
|
|
282
|
-
node
|
|
283
|
-
|
|
284
|
-
|
|
314
|
+
node[expandedField] = expanded;
|
|
315
|
+
const children = node[childrenField];
|
|
316
|
+
if (children) {
|
|
317
|
+
await this.setExpandedState(children, expanded, isLazyDataSource, loadNodeChildren, expandedField, childrenField, childrenCountField);
|
|
285
318
|
}
|
|
286
319
|
}
|
|
287
320
|
}
|
|
@@ -290,23 +323,24 @@ class AXTreeViewService {
|
|
|
290
323
|
/**
|
|
291
324
|
* Get array reference by drop list ID
|
|
292
325
|
*/
|
|
293
|
-
getArrayByListId(nodes, listId) {
|
|
326
|
+
getArrayByListId(nodes, listId, idField = 'id', childrenField = 'children') {
|
|
294
327
|
if (listId === AXTreeViewService.ROOT_LIST_ID) {
|
|
295
328
|
return nodes;
|
|
296
329
|
}
|
|
297
330
|
if (listId.startsWith(AXTreeViewService.NODE_DROP_PREFIX)) {
|
|
298
331
|
const nodeId = listId.replace(AXTreeViewService.NODE_DROP_PREFIX, '');
|
|
299
|
-
const node = this.findNodeById(nodes, nodeId);
|
|
332
|
+
const node = this.findNodeById(nodes, nodeId, idField);
|
|
300
333
|
return node ? [node] : null;
|
|
301
334
|
}
|
|
302
335
|
const nodeId = listId.replace(AXTreeViewService.LIST_PREFIX, '');
|
|
303
|
-
const node = this.findNodeById(nodes, nodeId);
|
|
304
|
-
|
|
336
|
+
const node = this.findNodeById(nodes, nodeId, idField);
|
|
337
|
+
const children = node?.[childrenField];
|
|
338
|
+
return children ?? null;
|
|
305
339
|
}
|
|
306
340
|
/**
|
|
307
341
|
* Find parent node by list ID
|
|
308
342
|
*/
|
|
309
|
-
findParentByListId(nodes, listId) {
|
|
343
|
+
findParentByListId(nodes, listId, idField = 'id') {
|
|
310
344
|
if (listId === AXTreeViewService.ROOT_LIST_ID) {
|
|
311
345
|
return undefined;
|
|
312
346
|
}
|
|
@@ -314,13 +348,16 @@ class AXTreeViewService {
|
|
|
314
348
|
? AXTreeViewService.NODE_DROP_PREFIX
|
|
315
349
|
: AXTreeViewService.LIST_PREFIX;
|
|
316
350
|
const nodeId = listId.replace(prefix, '');
|
|
317
|
-
return this.findNodeById(nodes, nodeId) ?? undefined;
|
|
351
|
+
return this.findNodeById(nodes, nodeId, idField) ?? undefined;
|
|
318
352
|
}
|
|
319
353
|
/**
|
|
320
354
|
* Generate unique list ID for each node
|
|
321
355
|
*/
|
|
322
|
-
getListId(node) {
|
|
323
|
-
|
|
356
|
+
getListId(node, idField = 'id') {
|
|
357
|
+
if (!node)
|
|
358
|
+
return AXTreeViewService.ROOT_LIST_ID;
|
|
359
|
+
const nodeId = node[idField];
|
|
360
|
+
return `${AXTreeViewService.LIST_PREFIX}${nodeId}`;
|
|
324
361
|
}
|
|
325
362
|
/**
|
|
326
363
|
* Get root list ID constant
|
|
@@ -340,6 +377,300 @@ class AXTreeViewService {
|
|
|
340
377
|
getListPrefix() {
|
|
341
378
|
return AXTreeViewService.LIST_PREFIX;
|
|
342
379
|
}
|
|
380
|
+
// ==================== Node Utilities ====================
|
|
381
|
+
/**
|
|
382
|
+
* Get all nodes in a flat array
|
|
383
|
+
*/
|
|
384
|
+
getAllNodes(nodes, childrenField = 'children') {
|
|
385
|
+
const allNodes = [];
|
|
386
|
+
const traverse = (nodeList) => {
|
|
387
|
+
for (const node of nodeList) {
|
|
388
|
+
allNodes.push(node);
|
|
389
|
+
const children = node[childrenField];
|
|
390
|
+
if (children) {
|
|
391
|
+
traverse(children);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
traverse(nodes);
|
|
396
|
+
return allNodes;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Get the path to a node (array of parent nodes from root to node)
|
|
400
|
+
*/
|
|
401
|
+
getNodePath(nodes, nodeId, idField = 'id', childrenField = 'children') {
|
|
402
|
+
const path = [];
|
|
403
|
+
const node = this.findNodeById(nodes, nodeId, idField);
|
|
404
|
+
if (!node) {
|
|
405
|
+
return path;
|
|
406
|
+
}
|
|
407
|
+
let current = node;
|
|
408
|
+
while (current) {
|
|
409
|
+
path.unshift(current);
|
|
410
|
+
const parent = this.findParentNode(nodes, current, idField, childrenField);
|
|
411
|
+
current = parent ?? null;
|
|
412
|
+
}
|
|
413
|
+
return path;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Get the level/depth of a node (0 = root level)
|
|
417
|
+
*/
|
|
418
|
+
getNodeLevel(nodes, nodeId, idField = 'id', childrenField = 'children') {
|
|
419
|
+
const path = this.getNodePath(nodes, nodeId, idField, childrenField);
|
|
420
|
+
return path.length > 0 ? path.length - 1 : -1;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Get sibling nodes of a given node
|
|
424
|
+
*/
|
|
425
|
+
getSiblings(nodes, nodeId, idField = 'id', childrenField = 'children') {
|
|
426
|
+
const node = this.findNodeById(nodes, nodeId, idField);
|
|
427
|
+
if (!node) {
|
|
428
|
+
return [];
|
|
429
|
+
}
|
|
430
|
+
const parent = this.findParentNode(nodes, node, idField, childrenField);
|
|
431
|
+
const siblingsArray = parent?.[childrenField] ?? nodes;
|
|
432
|
+
return siblingsArray.filter((n) => n[idField] !== nodeId);
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Clone a node (creates a deep copy)
|
|
436
|
+
*/
|
|
437
|
+
cloneNode(node, idField = 'id', titleField = 'title', tooltipField = 'tooltip', iconField = 'icon', expandedField = 'expanded', selectedField = 'selected', indeterminateField = 'indeterminate', disabledField = 'disabled', hiddenField = 'hidden', childrenCountField = 'childrenCount', dataField = 'data', childrenField = 'children') {
|
|
438
|
+
const cloned = {
|
|
439
|
+
[idField]: `${node[idField]}-clone-${Date.now()}`,
|
|
440
|
+
[titleField]: node[titleField],
|
|
441
|
+
[tooltipField]: node[tooltipField],
|
|
442
|
+
[iconField]: node[iconField],
|
|
443
|
+
[expandedField]: node[expandedField],
|
|
444
|
+
[selectedField]: false, // Cloned nodes are not selected by default
|
|
445
|
+
[indeterminateField]: false,
|
|
446
|
+
[disabledField]: node[disabledField],
|
|
447
|
+
[hiddenField]: node[hiddenField],
|
|
448
|
+
[childrenCountField]: node[childrenCountField],
|
|
449
|
+
[dataField]: node[dataField] ? JSON.parse(JSON.stringify(node[dataField])) : undefined,
|
|
450
|
+
};
|
|
451
|
+
const children = node[childrenField];
|
|
452
|
+
if (children && children.length > 0) {
|
|
453
|
+
cloned[childrenField] = children.map((child) => this.cloneNode(child, idField, titleField, tooltipField, iconField, expandedField, selectedField, indeterminateField, disabledField, hiddenField, childrenCountField, dataField, childrenField));
|
|
454
|
+
cloned[childrenCountField] = cloned[childrenField].length;
|
|
455
|
+
}
|
|
456
|
+
return cloned;
|
|
457
|
+
}
|
|
458
|
+
// ==================== Node Manipulation ====================
|
|
459
|
+
/**
|
|
460
|
+
* Move a node to a new parent in the tree
|
|
461
|
+
* @param nodes - Root nodes array
|
|
462
|
+
* @param nodeId - The ID of the node to move
|
|
463
|
+
* @param newParentId - The ID of the new parent (undefined for root level)
|
|
464
|
+
* @param index - Optional index to insert at (default: append to end)
|
|
465
|
+
* @param idField - Field name for node ID
|
|
466
|
+
* @param childrenField - Field name for children array
|
|
467
|
+
* @param childrenCountField - Field name for children count
|
|
468
|
+
* @param expandedField - Field name for expanded state
|
|
469
|
+
* @returns Object with success status, moved node, previous parent, new parent, and indices
|
|
470
|
+
*/
|
|
471
|
+
moveNode(nodes, nodeId, newParentId, index, idField = 'id', childrenField = 'children', childrenCountField = 'childrenCount', expandedField = 'expanded') {
|
|
472
|
+
const node = this.findNodeById(nodes, nodeId, idField);
|
|
473
|
+
if (!node) {
|
|
474
|
+
return { success: false, previousIndex: -1, currentIndex: -1 };
|
|
475
|
+
}
|
|
476
|
+
// Find current parent
|
|
477
|
+
const currentParent = this.findParentNode(nodes, node, idField, childrenField);
|
|
478
|
+
const currentParentChildren = currentParent
|
|
479
|
+
? currentParent[childrenField]
|
|
480
|
+
: undefined;
|
|
481
|
+
const currentArray = currentParentChildren ?? nodes;
|
|
482
|
+
// Find and remove from current location
|
|
483
|
+
const currentIndex = currentArray.findIndex((n) => n[idField] === nodeId);
|
|
484
|
+
if (currentIndex === -1) {
|
|
485
|
+
return { success: false, previousIndex: -1, currentIndex: -1 };
|
|
486
|
+
}
|
|
487
|
+
const movedNode = currentArray.splice(currentIndex, 1)[0];
|
|
488
|
+
// Find new parent
|
|
489
|
+
let targetArray;
|
|
490
|
+
let newParent;
|
|
491
|
+
if (newParentId) {
|
|
492
|
+
newParent = this.findNodeById(nodes, newParentId, idField);
|
|
493
|
+
if (!newParent) {
|
|
494
|
+
// Restore node if new parent not found
|
|
495
|
+
currentArray.splice(currentIndex, 0, movedNode);
|
|
496
|
+
return { success: false, previousIndex: currentIndex, currentIndex: -1 };
|
|
497
|
+
}
|
|
498
|
+
// Validate drop target
|
|
499
|
+
if (!this.isValidDropTarget(movedNode, newParent, idField, childrenField)) {
|
|
500
|
+
// Restore node if invalid drop target
|
|
501
|
+
currentArray.splice(currentIndex, 0, movedNode);
|
|
502
|
+
return { success: false, previousIndex: currentIndex, currentIndex: -1 };
|
|
503
|
+
}
|
|
504
|
+
let newParentChildren = newParent[childrenField];
|
|
505
|
+
if (!newParentChildren) {
|
|
506
|
+
newParentChildren = [];
|
|
507
|
+
newParent[childrenField] = newParentChildren;
|
|
508
|
+
}
|
|
509
|
+
targetArray = newParentChildren;
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
targetArray = nodes;
|
|
513
|
+
}
|
|
514
|
+
// Calculate new index before inserting
|
|
515
|
+
const newIndex = index !== undefined && index >= 0 && index <= targetArray.length ? index : targetArray.length;
|
|
516
|
+
// Insert at new location
|
|
517
|
+
if (index !== undefined && index >= 0 && index <= targetArray.length) {
|
|
518
|
+
targetArray.splice(index, 0, movedNode);
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
targetArray.push(movedNode);
|
|
522
|
+
}
|
|
523
|
+
// Update childrenCount
|
|
524
|
+
if (currentParent) {
|
|
525
|
+
const updatedChildren = currentParent[childrenField];
|
|
526
|
+
currentParent[childrenCountField] = updatedChildren?.length ?? 0;
|
|
527
|
+
}
|
|
528
|
+
if (newParent) {
|
|
529
|
+
const updatedChildren = newParent[childrenField];
|
|
530
|
+
newParent[childrenCountField] = updatedChildren?.length ?? 0;
|
|
531
|
+
newParent[expandedField] = true; // Auto-expand new parent
|
|
532
|
+
}
|
|
533
|
+
return {
|
|
534
|
+
success: true,
|
|
535
|
+
movedNode,
|
|
536
|
+
previousParent: currentParent,
|
|
537
|
+
newParent,
|
|
538
|
+
previousIndex: currentIndex,
|
|
539
|
+
currentIndex: newIndex,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Edit/update a node's properties
|
|
544
|
+
* @param nodes - Root nodes array
|
|
545
|
+
* @param nodeId - The ID of the node to edit
|
|
546
|
+
* @param updates - Partial node object with properties to update
|
|
547
|
+
* @param idField - Field name for node ID
|
|
548
|
+
* @param childrenField - Field name for children array
|
|
549
|
+
* @param childrenCountField - Field name for children count
|
|
550
|
+
* @returns The updated node if found, null otherwise
|
|
551
|
+
*/
|
|
552
|
+
editNode(nodes, nodeId, updates, idField = 'id', childrenField = 'children', childrenCountField = 'childrenCount') {
|
|
553
|
+
const node = this.findNodeById(nodes, nodeId, idField);
|
|
554
|
+
if (!node) {
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
// Update node properties
|
|
558
|
+
Object.assign(node, updates);
|
|
559
|
+
// If children array is provided, ensure it exists and update childrenCount
|
|
560
|
+
if (updates[childrenField] !== undefined) {
|
|
561
|
+
const children = updates[childrenField];
|
|
562
|
+
node[childrenField] = children;
|
|
563
|
+
node[childrenCountField] = children?.length;
|
|
564
|
+
}
|
|
565
|
+
return node;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Add a child node to a parent node
|
|
569
|
+
* @param nodes - Root nodes array
|
|
570
|
+
* @param parentId - The ID of the parent node
|
|
571
|
+
* @param childNode - The child node to add
|
|
572
|
+
* @param index - Optional index to insert at (default: append to end)
|
|
573
|
+
* @param idField - Field name for node ID
|
|
574
|
+
* @param childrenField - Field name for children array
|
|
575
|
+
* @param childrenCountField - Field name for children count
|
|
576
|
+
* @param expandedField - Field name for expanded state
|
|
577
|
+
* @returns The parent node if found and child was added, null otherwise
|
|
578
|
+
*/
|
|
579
|
+
addChild(nodes, parentId, childNode, index, idField = 'id', childrenField = 'children', childrenCountField = 'childrenCount', expandedField = 'expanded') {
|
|
580
|
+
const parent = this.findNodeById(nodes, parentId, idField);
|
|
581
|
+
if (!parent) {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
// Ensure children array exists
|
|
585
|
+
let children = parent[childrenField];
|
|
586
|
+
if (!children) {
|
|
587
|
+
children = [];
|
|
588
|
+
parent[childrenField] = children;
|
|
589
|
+
}
|
|
590
|
+
// Insert or append child
|
|
591
|
+
if (index !== undefined && index >= 0 && index <= children.length) {
|
|
592
|
+
children.splice(index, 0, childNode);
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
children.push(childNode);
|
|
596
|
+
}
|
|
597
|
+
// Update childrenCount
|
|
598
|
+
parent[childrenCountField] = children.length;
|
|
599
|
+
// Auto-expand parent if it was collapsed
|
|
600
|
+
if (!parent[expandedField]) {
|
|
601
|
+
parent[expandedField] = true;
|
|
602
|
+
}
|
|
603
|
+
return parent;
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Remove a node from the tree
|
|
607
|
+
* @param nodes - Root nodes array
|
|
608
|
+
* @param nodeId - The ID of the node to remove
|
|
609
|
+
* @param idField - Field name for node ID
|
|
610
|
+
* @param childrenField - Field name for children array
|
|
611
|
+
* @param childrenCountField - Field name for children count
|
|
612
|
+
* @returns The removed node if found, null otherwise
|
|
613
|
+
*/
|
|
614
|
+
removeNode(nodes, nodeId, idField = 'id', childrenField = 'children', childrenCountField = 'childrenCount') {
|
|
615
|
+
const node = this.findNodeById(nodes, nodeId, idField);
|
|
616
|
+
if (!node) {
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
// Find parent to remove from its children array
|
|
620
|
+
const parent = this.findParentNode(nodes, node, idField, childrenField);
|
|
621
|
+
const parentChildren = parent ? parent[childrenField] : undefined;
|
|
622
|
+
const targetArray = parentChildren ?? nodes;
|
|
623
|
+
// Find and remove the node
|
|
624
|
+
const index = targetArray.findIndex((n) => n[idField] === nodeId);
|
|
625
|
+
if (index !== -1) {
|
|
626
|
+
const removed = targetArray.splice(index, 1)[0];
|
|
627
|
+
// Update parent's childrenCount if it exists
|
|
628
|
+
if (parent) {
|
|
629
|
+
const updatedChildren = parent[childrenField];
|
|
630
|
+
parent[childrenCountField] = updatedChildren?.length ?? 0;
|
|
631
|
+
}
|
|
632
|
+
return removed;
|
|
633
|
+
}
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Validate node structure (check for required fields and circular references)
|
|
638
|
+
*/
|
|
639
|
+
validateNode(node, visitedIds = new Set(), idField = 'id', titleField = 'title', childrenField = 'children', childrenCountField = 'childrenCount') {
|
|
640
|
+
const errors = [];
|
|
641
|
+
const nodeId = node[idField];
|
|
642
|
+
const nodeTitle = node[titleField];
|
|
643
|
+
if (!nodeId) {
|
|
644
|
+
errors.push(`Node must have an ${idField}`);
|
|
645
|
+
}
|
|
646
|
+
if (!nodeTitle) {
|
|
647
|
+
errors.push(`Node must have a ${titleField}`);
|
|
648
|
+
}
|
|
649
|
+
if (nodeId && visitedIds.has(nodeId)) {
|
|
650
|
+
errors.push(`Circular reference detected: node ${nodeId} appears multiple times in the tree`);
|
|
651
|
+
}
|
|
652
|
+
const children = node[childrenField];
|
|
653
|
+
if (children) {
|
|
654
|
+
const newVisited = new Set(visitedIds);
|
|
655
|
+
if (nodeId) {
|
|
656
|
+
newVisited.add(nodeId);
|
|
657
|
+
}
|
|
658
|
+
for (const child of children) {
|
|
659
|
+
const childValidation = this.validateNode(child, newVisited, idField, titleField, childrenField, childrenCountField);
|
|
660
|
+
if (!childValidation.valid) {
|
|
661
|
+
errors.push(...childValidation.errors.map((e) => `Child of ${nodeId}: ${e}`));
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
const childrenCount = node[childrenCountField];
|
|
665
|
+
if (childrenCount !== undefined && childrenCount !== children.length) {
|
|
666
|
+
errors.push(`Node ${nodeId}: ${childrenCountField} (${childrenCount}) does not match ${childrenField} array length (${children.length})`);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return {
|
|
670
|
+
valid: errors.length === 0,
|
|
671
|
+
errors,
|
|
672
|
+
};
|
|
673
|
+
}
|
|
343
674
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
344
675
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewService }); }
|
|
345
676
|
}
|
|
@@ -356,36 +687,54 @@ class AXTreeViewComponent {
|
|
|
356
687
|
// ==================== Inputs ====================
|
|
357
688
|
/** Tree data source - can be static array or lazy loading function */
|
|
358
689
|
this.datasource = model.required(...(ngDevMode ? [{ debugName: "datasource" }] : []));
|
|
359
|
-
/** Selection mode: 'single' (click to select) or 'multiple' (checkbox selection) */
|
|
360
|
-
this.selectMode = input('
|
|
361
|
-
/** Whether to show checkboxes for selection (only applies to multiple mode) */
|
|
362
|
-
this.showCheckbox = input(
|
|
363
|
-
/**
|
|
364
|
-
this.
|
|
365
|
-
/**
|
|
366
|
-
this.
|
|
367
|
-
/**
|
|
368
|
-
this.
|
|
369
|
-
/**
|
|
370
|
-
this.dragMode = input('handler', ...(ngDevMode ? [{ debugName: "dragMode" }] : []));
|
|
371
|
-
/** Drag operation type: 'order-only' (reorder only), 'move' (move between parents), 'both' (allow both) */
|
|
372
|
-
this.dragOperationType = input('both', ...(ngDevMode ? [{ debugName: "dragOperationType" }] : []));
|
|
373
|
-
/** Whether to show icons */
|
|
690
|
+
/** Selection mode: 'single' (click to select) or 'multiple' (checkbox selection). Default: `'single'` */
|
|
691
|
+
this.selectMode = input('single', ...(ngDevMode ? [{ debugName: "selectMode" }] : []));
|
|
692
|
+
/** Whether to show checkboxes for selection (only applies to multiple mode). Default: `false`. When false and selectMode is not 'none', clicking on a node toggles its selection. */
|
|
693
|
+
this.showCheckbox = input(false, ...(ngDevMode ? [{ debugName: "showCheckbox" }] : []));
|
|
694
|
+
/** Selection behavior: 'all' (select anything, no special behavior), 'intermediate' (parent indeterminate state when children selected), 'leaf' (only leaf nodes selectable), 'nested' (selecting parent selects all children). Default: `'intermediate'` */
|
|
695
|
+
this.selectionBehavior = input('intermediate', ...(ngDevMode ? [{ debugName: "selectionBehavior" }] : []));
|
|
696
|
+
/** Drag area: 'handler' (drag handle), 'item' (entire item). Default: `'handler'` */
|
|
697
|
+
this.dragArea = input('handler', ...(ngDevMode ? [{ debugName: "dragArea" }] : []));
|
|
698
|
+
/** Drag behavior: 'none' (disabled), 'order-only' (reorder only), 'move' (move between parents), 'both' (allow both). Default: `'none'` */
|
|
699
|
+
this.dragBehavior = input('none', ...(ngDevMode ? [{ debugName: "dragBehavior" }] : []));
|
|
700
|
+
/** Whether to show icons. Default: `true` */
|
|
374
701
|
this.showIcons = input(true, ...(ngDevMode ? [{ debugName: "showIcons" }] : []));
|
|
375
|
-
/** Whether to show children count badge */
|
|
702
|
+
/** Whether to show children count badge. Default: `true` */
|
|
376
703
|
this.showChildrenBadge = input(true, ...(ngDevMode ? [{ debugName: "showChildrenBadge" }] : []));
|
|
377
|
-
/** Custom icon for expanded nodes */
|
|
704
|
+
/** Custom icon for expanded nodes. Default: `'fa-solid fa-chevron-down'` */
|
|
378
705
|
this.expandedIcon = input('fa-solid fa-chevron-down', ...(ngDevMode ? [{ debugName: "expandedIcon" }] : []));
|
|
379
|
-
/** Custom icon for collapsed nodes */
|
|
706
|
+
/** Custom icon for collapsed nodes. Default: `'fa-solid fa-chevron-right'` */
|
|
380
707
|
this.collapsedIcon = input('fa-solid fa-chevron-right', ...(ngDevMode ? [{ debugName: "collapsedIcon" }] : []));
|
|
381
|
-
/** Indent size in pixels for each level */
|
|
382
|
-
this.indentSize = input(
|
|
383
|
-
/**
|
|
384
|
-
this.nodeHeight = input('normal', ...(ngDevMode ? [{ debugName: "nodeHeight" }] : []));
|
|
385
|
-
/** Visual style variant */
|
|
708
|
+
/** Indent size in pixels for each level. Default: `16` */
|
|
709
|
+
this.indentSize = input(16, ...(ngDevMode ? [{ debugName: "indentSize" }] : []));
|
|
710
|
+
/** Visual style variant. Default: `'default'` */
|
|
386
711
|
this.look = input('default', ...(ngDevMode ? [{ debugName: "look" }] : []));
|
|
387
|
-
/** Custom template for tree items */
|
|
388
|
-
this.
|
|
712
|
+
/** Custom template for tree items. Default: `undefined` */
|
|
713
|
+
this.nodeTemplate = input(...(ngDevMode ? [undefined, { debugName: "nodeTemplate" }] : []));
|
|
714
|
+
/** Field name for node ID. Default: `'id'` */
|
|
715
|
+
this.idField = input('id', ...(ngDevMode ? [{ debugName: "idField" }] : []));
|
|
716
|
+
/** Field name for node title. Default: `'title'` */
|
|
717
|
+
this.titleField = input('title', ...(ngDevMode ? [{ debugName: "titleField" }] : []));
|
|
718
|
+
/** Field name for node tooltip. Default: `'tooltip'` */
|
|
719
|
+
this.tooltipField = input('tooltip', ...(ngDevMode ? [{ debugName: "tooltipField" }] : []));
|
|
720
|
+
/** Field name for node icon. Default: `'icon'` */
|
|
721
|
+
this.iconField = input('icon', ...(ngDevMode ? [{ debugName: "iconField" }] : []));
|
|
722
|
+
/** Field name for expanded state. Default: `'expanded'` */
|
|
723
|
+
this.expandedField = input('expanded', ...(ngDevMode ? [{ debugName: "expandedField" }] : []));
|
|
724
|
+
/** Field name for selected state. Default: `'selected'` */
|
|
725
|
+
this.selectedField = input('selected', ...(ngDevMode ? [{ debugName: "selectedField" }] : []));
|
|
726
|
+
/** Field name for indeterminate state. Default: `'indeterminate'` */
|
|
727
|
+
this.indeterminateField = input('indeterminate', ...(ngDevMode ? [{ debugName: "indeterminateField" }] : []));
|
|
728
|
+
/** Field name for disabled state. Default: `'disabled'` */
|
|
729
|
+
this.disabledField = input('disabled', ...(ngDevMode ? [{ debugName: "disabledField" }] : []));
|
|
730
|
+
/** Field name for hidden state. Default: `'hidden'` */
|
|
731
|
+
this.hiddenField = input('hidden', ...(ngDevMode ? [{ debugName: "hiddenField" }] : []));
|
|
732
|
+
/** Field name for children array. Default: `'children'` */
|
|
733
|
+
this.childrenField = input('children', ...(ngDevMode ? [{ debugName: "childrenField" }] : []));
|
|
734
|
+
/** Field name for children count. Default: `'childrenCount'` */
|
|
735
|
+
this.childrenCountField = input('childrenCount', ...(ngDevMode ? [{ debugName: "childrenCountField" }] : []));
|
|
736
|
+
/** Field name for custom data. Default: `'data'` */
|
|
737
|
+
this.dataField = input('data', ...(ngDevMode ? [{ debugName: "dataField" }] : []));
|
|
389
738
|
// ==================== Outputs ====================
|
|
390
739
|
/** Emitted before a drop operation - set canceled to true to prevent drop */
|
|
391
740
|
this.onBeforeDrop = output();
|
|
@@ -393,6 +742,8 @@ class AXTreeViewComponent {
|
|
|
393
742
|
this.onNodeToggle = output();
|
|
394
743
|
/** Emitted when a node is selected/deselected */
|
|
395
744
|
this.onNodeSelect = output();
|
|
745
|
+
/** Emitted when selection changes - returns all currently selected nodes */
|
|
746
|
+
this.onSelectionChange = output();
|
|
396
747
|
/** Emitted when nodes are reordered within the same parent */
|
|
397
748
|
this.onOrderChange = output();
|
|
398
749
|
/** Emitted when a node is moved to a different parent */
|
|
@@ -425,19 +776,35 @@ class AXTreeViewComponent {
|
|
|
425
776
|
this.isUpdatingFromDatasource = false;
|
|
426
777
|
/** Computed to check if datasource is a function */
|
|
427
778
|
this.isLazyDataSource = computed(() => typeof this.datasource() === 'function', ...(ngDevMode ? [{ debugName: "isLazyDataSource" }] : []));
|
|
779
|
+
/** Computed: Returns true when selection is restricted to leaf nodes only */
|
|
780
|
+
this.isLeafOnlyMode = computed(() => this.selectionBehavior() === 'leaf', ...(ngDevMode ? [{ debugName: "isLeafOnlyMode" }] : []));
|
|
781
|
+
/** Computed: Returns true when selecting a parent automatically selects all its children */
|
|
782
|
+
this.cascadesToChildren = computed(() => {
|
|
783
|
+
const behavior = this.selectionBehavior();
|
|
784
|
+
return behavior === 'nested' || behavior === 'intermediate-nested';
|
|
785
|
+
}, ...(ngDevMode ? [{ debugName: "cascadesToChildren" }] : []));
|
|
786
|
+
/** Computed: Returns true when parent nodes show indeterminate state based on children selection */
|
|
787
|
+
this.hasIntermediateState = computed(() => {
|
|
788
|
+
const behavior = this.selectionBehavior();
|
|
789
|
+
return behavior === 'intermediate' || behavior === 'intermediate-nested';
|
|
790
|
+
}, ...(ngDevMode ? [{ debugName: "hasIntermediateState" }] : []));
|
|
428
791
|
// ==================== Effects ====================
|
|
429
792
|
/** Effect to handle datasource changes */
|
|
430
|
-
this.#datasourceEffect = effect(() => {
|
|
431
|
-
if (this.isUpdatingFromDatasource)
|
|
793
|
+
this.#datasourceEffect = effect(async () => {
|
|
794
|
+
if (this.isUpdatingFromDatasource) {
|
|
432
795
|
return;
|
|
796
|
+
}
|
|
433
797
|
const ds = this.datasource();
|
|
434
798
|
if (Array.isArray(ds)) {
|
|
435
799
|
this.nodes.set([...ds]);
|
|
436
800
|
}
|
|
437
801
|
else if (typeof ds === 'function') {
|
|
438
|
-
|
|
802
|
+
try {
|
|
803
|
+
await this.loadRootItems(ds);
|
|
804
|
+
}
|
|
805
|
+
catch (error) {
|
|
439
806
|
this.handleError('Failed to load root items', error);
|
|
440
|
-
}
|
|
807
|
+
}
|
|
441
808
|
}
|
|
442
809
|
}, ...(ngDevMode ? [{ debugName: "#datasourceEffect" }] : []));
|
|
443
810
|
/** Initialize direction change listener */
|
|
@@ -447,6 +814,115 @@ class AXTreeViewComponent {
|
|
|
447
814
|
.subscribe((isRtl) => this.isRtl.set(isRtl));
|
|
448
815
|
});
|
|
449
816
|
}
|
|
817
|
+
// ==================== Node Property Helpers ====================
|
|
818
|
+
/**
|
|
819
|
+
* Get a property value from a node using the configured field name
|
|
820
|
+
*/
|
|
821
|
+
getNodeProp(node, fieldName, defaultValue) {
|
|
822
|
+
return node[fieldName] ?? defaultValue;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Set a property value on a node using the configured field name
|
|
826
|
+
*/
|
|
827
|
+
setNodeProp(node, fieldName, value) {
|
|
828
|
+
node[fieldName] = value;
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Get node ID
|
|
832
|
+
*/
|
|
833
|
+
getNodeId(node) {
|
|
834
|
+
return this.getNodeProp(node, this.idField(), '');
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Get node title
|
|
838
|
+
*/
|
|
839
|
+
getNodeTitle(node) {
|
|
840
|
+
return this.getNodeProp(node, this.titleField(), '');
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Get node tooltip
|
|
844
|
+
*/
|
|
845
|
+
getNodeTooltip(node) {
|
|
846
|
+
return this.getNodeProp(node, this.tooltipField(), undefined);
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Get node icon
|
|
850
|
+
*/
|
|
851
|
+
getNodeIcon(node) {
|
|
852
|
+
return this.getNodeProp(node, this.iconField(), undefined);
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Get node expanded state
|
|
856
|
+
*/
|
|
857
|
+
getNodeExpanded(node) {
|
|
858
|
+
return this.getNodeProp(node, this.expandedField(), false);
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Set node expanded state
|
|
862
|
+
*/
|
|
863
|
+
setNodeExpanded(node, value) {
|
|
864
|
+
this.setNodeProp(node, this.expandedField(), value);
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Get node selected state
|
|
868
|
+
*/
|
|
869
|
+
getNodeSelected(node) {
|
|
870
|
+
return this.getNodeProp(node, this.selectedField(), false);
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Set node selected state
|
|
874
|
+
*/
|
|
875
|
+
setNodeSelected(node, value) {
|
|
876
|
+
this.setNodeProp(node, this.selectedField(), value);
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Get node indeterminate state
|
|
880
|
+
*/
|
|
881
|
+
getNodeIndeterminate(node) {
|
|
882
|
+
return this.getNodeProp(node, this.indeterminateField(), false);
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Set node indeterminate state
|
|
886
|
+
*/
|
|
887
|
+
setNodeIndeterminate(node, value) {
|
|
888
|
+
this.setNodeProp(node, this.indeterminateField(), value);
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Get node disabled state
|
|
892
|
+
*/
|
|
893
|
+
getNodeDisabled(node) {
|
|
894
|
+
return this.getNodeProp(node, this.disabledField(), false);
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Get node hidden state
|
|
898
|
+
*/
|
|
899
|
+
getNodeHidden(node) {
|
|
900
|
+
return this.getNodeProp(node, this.hiddenField(), false);
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Get node children array
|
|
904
|
+
*/
|
|
905
|
+
getNodeChildren(node) {
|
|
906
|
+
return this.getNodeProp(node, this.childrenField(), undefined);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Set node children array
|
|
910
|
+
*/
|
|
911
|
+
setNodeChildren(node, value) {
|
|
912
|
+
this.setNodeProp(node, this.childrenField(), value);
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Get node children count
|
|
916
|
+
*/
|
|
917
|
+
getNodeChildrenCount(node) {
|
|
918
|
+
return this.getNodeProp(node, this.childrenCountField(), undefined);
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Set node children count
|
|
922
|
+
*/
|
|
923
|
+
setNodeChildrenCount(node, value) {
|
|
924
|
+
this.setNodeProp(node, this.childrenCountField(), value);
|
|
925
|
+
}
|
|
450
926
|
// ==================== Effects ====================
|
|
451
927
|
/** Effect to handle datasource changes */
|
|
452
928
|
#datasourceEffect;
|
|
@@ -457,21 +933,21 @@ class AXTreeViewComponent {
|
|
|
457
933
|
* Expand all nodes in the tree (with lazy loading support)
|
|
458
934
|
*/
|
|
459
935
|
async expandAll() {
|
|
460
|
-
await this.treeService.setExpandedState(this.nodes(), true, this.isLazyDataSource(), (node) => this.loadNodeChildren(node));
|
|
936
|
+
await this.treeService.setExpandedState(this.nodes(), true, this.isLazyDataSource(), (node) => this.loadNodeChildren(node), this.expandedField(), this.childrenField(), this.childrenCountField());
|
|
461
937
|
this.refreshNodes();
|
|
462
938
|
}
|
|
463
939
|
/**
|
|
464
940
|
* Collapse all nodes in the tree
|
|
465
941
|
*/
|
|
466
942
|
collapseAll() {
|
|
467
|
-
this.treeService.setExpandedState(this.nodes(), false, this.isLazyDataSource(), (node) => this.loadNodeChildren(node));
|
|
943
|
+
this.treeService.setExpandedState(this.nodes(), false, this.isLazyDataSource(), (node) => this.loadNodeChildren(node), this.expandedField(), this.childrenField(), this.childrenCountField());
|
|
468
944
|
this.refreshNodes();
|
|
469
945
|
}
|
|
470
946
|
/**
|
|
471
947
|
* Get count of selected nodes
|
|
472
948
|
*/
|
|
473
949
|
getSelectedCount() {
|
|
474
|
-
return this.treeService.countSelected(this.nodes());
|
|
950
|
+
return this.treeService.countSelected(this.nodes(), this.selectedField(), this.childrenField());
|
|
475
951
|
}
|
|
476
952
|
/**
|
|
477
953
|
* Check if any nodes are selected
|
|
@@ -484,36 +960,62 @@ class AXTreeViewComponent {
|
|
|
484
960
|
*/
|
|
485
961
|
getSelectedNodes() {
|
|
486
962
|
const selected = [];
|
|
487
|
-
this.treeService.collectSelected(this.nodes(), selected);
|
|
963
|
+
this.treeService.collectSelected(this.nodes(), selected, this.selectedField(), this.childrenField());
|
|
488
964
|
return selected;
|
|
489
965
|
}
|
|
490
966
|
/**
|
|
491
967
|
* Delete selected nodes from the tree
|
|
492
968
|
*/
|
|
493
969
|
deleteSelected() {
|
|
494
|
-
this.treeService.removeSelected(this.nodes());
|
|
495
|
-
|
|
970
|
+
this.treeService.removeSelected(this.nodes(), this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
971
|
+
if (!this.isLeafOnlyMode()) {
|
|
972
|
+
this.treeService.updateAllParentStates(this.nodes(), this.hasIntermediateState(), this.childrenField(), this.selectedField(), this.indeterminateField());
|
|
973
|
+
}
|
|
496
974
|
this.refreshNodes();
|
|
975
|
+
this.emitSelectionChange();
|
|
497
976
|
}
|
|
498
977
|
/**
|
|
499
978
|
* Select all nodes in the tree
|
|
500
979
|
*/
|
|
501
980
|
selectAll() {
|
|
502
|
-
|
|
981
|
+
if (this.selectMode() === 'none') {
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
if (this.isLeafOnlyMode()) {
|
|
985
|
+
// Only select leaf nodes
|
|
986
|
+
const selectLeafs = (nodes) => {
|
|
987
|
+
for (const node of nodes) {
|
|
988
|
+
if (this.isLeafNode(node) && !this.getNodeDisabled(node)) {
|
|
989
|
+
this.setNodeSelected(node, true);
|
|
990
|
+
this.setNodeIndeterminate(node, false);
|
|
991
|
+
}
|
|
992
|
+
const children = this.getNodeChildren(node);
|
|
993
|
+
if (children) {
|
|
994
|
+
selectLeafs(children);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
selectLeafs(this.nodes());
|
|
999
|
+
}
|
|
1000
|
+
else {
|
|
1001
|
+
this.treeService.setAllSelection(this.nodes(), true, this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
1002
|
+
}
|
|
503
1003
|
this.refreshNodes();
|
|
1004
|
+
this.emitSelectionChange();
|
|
504
1005
|
}
|
|
505
1006
|
/**
|
|
506
1007
|
* Deselect all nodes in the tree
|
|
507
1008
|
*/
|
|
508
1009
|
deselectAll() {
|
|
509
|
-
this.treeService.setAllSelection(this.nodes(), false);
|
|
1010
|
+
this.treeService.setAllSelection(this.nodes(), false, this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
510
1011
|
this.refreshNodes();
|
|
1012
|
+
this.emitSelectionChange();
|
|
511
1013
|
}
|
|
512
1014
|
/**
|
|
513
1015
|
* Find a node by ID in the tree
|
|
514
1016
|
*/
|
|
515
1017
|
findNode(id) {
|
|
516
|
-
return this.treeService.findNodeById(this.nodes(), id);
|
|
1018
|
+
return this.treeService.findNodeById(this.nodes(), id, this.idField());
|
|
517
1019
|
}
|
|
518
1020
|
/**
|
|
519
1021
|
* Refresh the tree to trigger change detection
|
|
@@ -527,17 +1029,355 @@ class AXTreeViewComponent {
|
|
|
527
1029
|
isNodeLoading(nodeId) {
|
|
528
1030
|
return this.loadingNodes().has(nodeId);
|
|
529
1031
|
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Get loading state for a node (internal state)
|
|
1034
|
+
*/
|
|
1035
|
+
getNodeLoading(node) {
|
|
1036
|
+
return this.loadingNodes().has(this.getNodeId(node));
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Edit/update a node's properties
|
|
1040
|
+
* @param nodeId - The ID of the node to edit
|
|
1041
|
+
* @param updates - Partial node object with properties to update
|
|
1042
|
+
* @returns true if node was found and updated, false otherwise
|
|
1043
|
+
*/
|
|
1044
|
+
editNode(nodeId, updates) {
|
|
1045
|
+
const updated = this.treeService.editNode(this.nodes(), nodeId, updates, this.idField(), this.childrenField(), this.childrenCountField());
|
|
1046
|
+
if (updated) {
|
|
1047
|
+
this.refreshNodes();
|
|
1048
|
+
return true;
|
|
1049
|
+
}
|
|
1050
|
+
return false;
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Add a child node to a parent node
|
|
1054
|
+
* @param parentId - The ID of the parent node
|
|
1055
|
+
* @param childNode - The child node to add
|
|
1056
|
+
* @param index - Optional index to insert at (default: append to end)
|
|
1057
|
+
* @returns true if parent was found and child was added, false otherwise
|
|
1058
|
+
*/
|
|
1059
|
+
addChild(parentId, childNode, index) {
|
|
1060
|
+
const parent = this.treeService.addChild(this.nodes(), parentId, childNode, index, this.idField(), this.childrenField(), this.childrenCountField(), this.expandedField());
|
|
1061
|
+
if (parent) {
|
|
1062
|
+
this.refreshNodes();
|
|
1063
|
+
return true;
|
|
1064
|
+
}
|
|
1065
|
+
return false;
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Remove a node from the tree
|
|
1069
|
+
* @param nodeId - The ID of the node to remove
|
|
1070
|
+
* @returns The removed node if found, null otherwise
|
|
1071
|
+
*/
|
|
1072
|
+
removeNode(nodeId) {
|
|
1073
|
+
const removed = this.treeService.removeNode(this.nodes(), nodeId, this.idField(), this.childrenField(), this.childrenCountField());
|
|
1074
|
+
if (removed) {
|
|
1075
|
+
// Update parent states if needed
|
|
1076
|
+
if (this.hasIntermediateState()) {
|
|
1077
|
+
this.treeService.updateAllParentStates(this.nodes(), this.hasIntermediateState(), this.childrenField(), this.selectedField(), this.indeterminateField());
|
|
1078
|
+
}
|
|
1079
|
+
this.refreshNodes();
|
|
1080
|
+
}
|
|
1081
|
+
return removed;
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Expand a specific node
|
|
1085
|
+
* @param nodeId - The ID of the node to expand
|
|
1086
|
+
* @returns Promise that resolves when expansion is complete (if lazy loading)
|
|
1087
|
+
*/
|
|
1088
|
+
async expandNode(nodeId) {
|
|
1089
|
+
const node = this.findNode(nodeId);
|
|
1090
|
+
if (!node) {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
const hasChildren = this.treeService.hasChildren(node, this.childrenField());
|
|
1094
|
+
const canLazyLoad = this.treeService.canLazyLoad(node, this.isLazyDataSource(), this.childrenCountField(), this.childrenField());
|
|
1095
|
+
if (hasChildren || canLazyLoad) {
|
|
1096
|
+
if (canLazyLoad) {
|
|
1097
|
+
await this.loadNodeChildren(node);
|
|
1098
|
+
}
|
|
1099
|
+
this.setNodeExpanded(node, true);
|
|
1100
|
+
this.refreshNodes();
|
|
1101
|
+
this.onNodeToggle.emit({ component: this, node, nativeEvent: new Event('expand') });
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Collapse a specific node
|
|
1106
|
+
* @param nodeId - The ID of the node to collapse
|
|
1107
|
+
*/
|
|
1108
|
+
collapseNode(nodeId) {
|
|
1109
|
+
const node = this.findNode(nodeId);
|
|
1110
|
+
if (!node) {
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
if (this.getNodeExpanded(node)) {
|
|
1114
|
+
this.setNodeExpanded(node, false);
|
|
1115
|
+
this.refreshNodes();
|
|
1116
|
+
this.onNodeToggle.emit({ component: this, node, nativeEvent: new Event('collapse') });
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Toggle expansion state of a specific node
|
|
1121
|
+
* @param nodeId - The ID of the node to toggle
|
|
1122
|
+
* @returns Promise that resolves when toggle is complete (if lazy loading)
|
|
1123
|
+
*/
|
|
1124
|
+
async toggleNodeExpansion(nodeId) {
|
|
1125
|
+
const node = this.findNode(nodeId);
|
|
1126
|
+
if (!node) {
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
if (this.getNodeExpanded(node)) {
|
|
1130
|
+
this.collapseNode(nodeId);
|
|
1131
|
+
}
|
|
1132
|
+
else {
|
|
1133
|
+
await this.expandNode(nodeId);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Programmatically select a node
|
|
1138
|
+
* @param nodeId - The ID of the node to select
|
|
1139
|
+
* @returns true if node was found and selected, false otherwise
|
|
1140
|
+
*/
|
|
1141
|
+
selectNode(nodeId) {
|
|
1142
|
+
if (this.selectMode() === 'none') {
|
|
1143
|
+
return false;
|
|
1144
|
+
}
|
|
1145
|
+
const node = this.findNode(nodeId);
|
|
1146
|
+
if (!node || this.getNodeDisabled(node) || !this.canSelectNode(node)) {
|
|
1147
|
+
return false;
|
|
1148
|
+
}
|
|
1149
|
+
const mode = this.selectMode();
|
|
1150
|
+
if (mode === 'single') {
|
|
1151
|
+
this.treeService.deselectAllNodes(this.nodes(), this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
1152
|
+
this.setNodeSelected(node, true);
|
|
1153
|
+
this.setNodeIndeterminate(node, false);
|
|
1154
|
+
}
|
|
1155
|
+
else {
|
|
1156
|
+
this.setNodeSelected(node, true);
|
|
1157
|
+
this.setNodeIndeterminate(node, false);
|
|
1158
|
+
const children = this.getNodeChildren(node);
|
|
1159
|
+
if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
|
|
1160
|
+
this.treeService.selectAllChildren(children, true, this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
1161
|
+
}
|
|
1162
|
+
if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
|
|
1163
|
+
this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
this.refreshNodes();
|
|
1167
|
+
this.onNodeSelect.emit({
|
|
1168
|
+
component: this,
|
|
1169
|
+
node,
|
|
1170
|
+
isUserInteraction: false,
|
|
1171
|
+
});
|
|
1172
|
+
this.emitSelectionChange();
|
|
1173
|
+
return true;
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Programmatically deselect a node
|
|
1177
|
+
* @param nodeId - The ID of the node to deselect
|
|
1178
|
+
* @returns true if node was found and deselected, false otherwise
|
|
1179
|
+
*/
|
|
1180
|
+
deselectNode(nodeId) {
|
|
1181
|
+
const node = this.findNode(nodeId);
|
|
1182
|
+
if (!node) {
|
|
1183
|
+
return false;
|
|
1184
|
+
}
|
|
1185
|
+
this.setNodeSelected(node, false);
|
|
1186
|
+
this.setNodeIndeterminate(node, false);
|
|
1187
|
+
const children = this.getNodeChildren(node);
|
|
1188
|
+
if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
|
|
1189
|
+
this.treeService.selectAllChildren(children, false, this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
1190
|
+
}
|
|
1191
|
+
if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
|
|
1192
|
+
this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
|
|
1193
|
+
}
|
|
1194
|
+
this.refreshNodes();
|
|
1195
|
+
this.onNodeSelect.emit({
|
|
1196
|
+
component: this,
|
|
1197
|
+
node,
|
|
1198
|
+
isUserInteraction: false,
|
|
1199
|
+
});
|
|
1200
|
+
this.emitSelectionChange();
|
|
1201
|
+
return true;
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Get parent node of a given node
|
|
1205
|
+
* @param nodeId - The ID of the node
|
|
1206
|
+
* @returns The parent node if found, null otherwise
|
|
1207
|
+
*/
|
|
1208
|
+
getParent(nodeId) {
|
|
1209
|
+
const node = this.findNode(nodeId);
|
|
1210
|
+
if (!node) {
|
|
1211
|
+
return null;
|
|
1212
|
+
}
|
|
1213
|
+
return this.treeService.findParentNode(this.nodes(), node, this.idField(), this.childrenField()) ?? null;
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Get children of a node
|
|
1217
|
+
* @param nodeId - The ID of the parent node
|
|
1218
|
+
* @returns Array of child nodes, or null if node not found
|
|
1219
|
+
*/
|
|
1220
|
+
getChildren(nodeId) {
|
|
1221
|
+
const node = this.findNode(nodeId);
|
|
1222
|
+
if (!node) {
|
|
1223
|
+
return null;
|
|
1224
|
+
}
|
|
1225
|
+
return this.getNodeChildren(node) ?? [];
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Get all root nodes
|
|
1229
|
+
* @returns Array of root nodes
|
|
1230
|
+
*/
|
|
1231
|
+
getRootNodes() {
|
|
1232
|
+
return [...this.nodes()];
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Get all nodes in a flat array
|
|
1236
|
+
* @returns Array of all nodes in the tree
|
|
1237
|
+
*/
|
|
1238
|
+
getAllNodes() {
|
|
1239
|
+
return this.treeService.getAllNodes(this.nodes(), this.childrenField());
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Get the path to a node (array of parent IDs from root to node)
|
|
1243
|
+
* @param nodeId - The ID of the node
|
|
1244
|
+
* @returns Array of node IDs representing the path, or empty array if node not found
|
|
1245
|
+
*/
|
|
1246
|
+
getNodePath(nodeId) {
|
|
1247
|
+
const nodePath = this.treeService.getNodePath(this.nodes(), nodeId, this.idField(), this.childrenField());
|
|
1248
|
+
return nodePath.map((node) => this.getNodeId(node));
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Get the level/depth of a node (0 = root level)
|
|
1252
|
+
* @param nodeId - The ID of the node
|
|
1253
|
+
* @returns The level of the node, or -1 if node not found
|
|
1254
|
+
*/
|
|
1255
|
+
getNodeLevel(nodeId) {
|
|
1256
|
+
return this.treeService.getNodeLevel(this.nodes(), nodeId, this.idField(), this.childrenField());
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Programmatically move a node to a new parent
|
|
1260
|
+
* @param nodeId - The ID of the node to move
|
|
1261
|
+
* @param newParentId - The ID of the new parent (undefined for root level)
|
|
1262
|
+
* @param index - Optional index to insert at (default: append to end)
|
|
1263
|
+
* @returns true if move was successful, false otherwise
|
|
1264
|
+
*/
|
|
1265
|
+
moveNode(nodeId, newParentId, index) {
|
|
1266
|
+
const result = this.treeService.moveNode(this.nodes(), nodeId, newParentId, index, this.idField(), this.childrenField(), this.childrenCountField(), this.expandedField());
|
|
1267
|
+
if (result.success && result.movedNode) {
|
|
1268
|
+
// Emit drop events
|
|
1269
|
+
this.emitDropEvents(result.movedNode, result.previousParent, result.newParent, result.previousIndex, result.currentIndex, false);
|
|
1270
|
+
this.refreshNodes();
|
|
1271
|
+
return true;
|
|
1272
|
+
}
|
|
1273
|
+
return false;
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Clone a node (creates a deep copy)
|
|
1277
|
+
* @param nodeId - The ID of the node to clone
|
|
1278
|
+
* @returns The cloned node, or null if node not found
|
|
1279
|
+
*/
|
|
1280
|
+
cloneNode(nodeId) {
|
|
1281
|
+
const node = this.findNode(nodeId);
|
|
1282
|
+
if (!node) {
|
|
1283
|
+
return null;
|
|
1284
|
+
}
|
|
1285
|
+
return this.treeService.cloneNode(node, this.idField(), this.titleField(), this.tooltipField(), this.iconField(), this.expandedField(), this.selectedField(), this.indeterminateField(), this.disabledField(), this.hiddenField(), this.childrenCountField(), this.dataField(), this.childrenField());
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Focus a specific node by ID
|
|
1289
|
+
* @param nodeId - The ID of the node to focus
|
|
1290
|
+
* @returns true if node was found and focused, false otherwise
|
|
1291
|
+
*/
|
|
1292
|
+
focusNode(nodeId) {
|
|
1293
|
+
const node = this.findNode(nodeId);
|
|
1294
|
+
if (!node || this.getNodeHidden(node) === true || this.getNodeDisabled(node)) {
|
|
1295
|
+
return false;
|
|
1296
|
+
}
|
|
1297
|
+
this.focusNodeById(nodeId);
|
|
1298
|
+
return true;
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Get all expanded nodes
|
|
1302
|
+
* @returns Array of expanded nodes
|
|
1303
|
+
*/
|
|
1304
|
+
getExpandedNodes() {
|
|
1305
|
+
const expanded = [];
|
|
1306
|
+
const traverse = (nodes) => {
|
|
1307
|
+
for (const node of nodes) {
|
|
1308
|
+
if (this.getNodeExpanded(node)) {
|
|
1309
|
+
expanded.push(node);
|
|
1310
|
+
}
|
|
1311
|
+
const children = this.getNodeChildren(node);
|
|
1312
|
+
if (children) {
|
|
1313
|
+
traverse(children);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
traverse(this.nodes());
|
|
1318
|
+
return expanded;
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1321
|
+
* Get all collapsed nodes that have children
|
|
1322
|
+
* @returns Array of collapsed nodes with children
|
|
1323
|
+
*/
|
|
1324
|
+
getCollapsedNodes() {
|
|
1325
|
+
const collapsed = [];
|
|
1326
|
+
const traverse = (nodes) => {
|
|
1327
|
+
for (const node of nodes) {
|
|
1328
|
+
const children = this.getNodeChildren(node);
|
|
1329
|
+
const childrenCount = this.getNodeChildrenCount(node);
|
|
1330
|
+
if (!this.getNodeExpanded(node) && (children?.length || childrenCount)) {
|
|
1331
|
+
collapsed.push(node);
|
|
1332
|
+
}
|
|
1333
|
+
if (children) {
|
|
1334
|
+
traverse(children);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
traverse(this.nodes());
|
|
1339
|
+
return collapsed;
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Check if a node is expanded
|
|
1343
|
+
* @param nodeId - The ID of the node
|
|
1344
|
+
* @returns true if node is expanded, false otherwise
|
|
1345
|
+
*/
|
|
1346
|
+
isNodeExpanded(nodeId) {
|
|
1347
|
+
const node = this.findNode(nodeId);
|
|
1348
|
+
return node ? this.getNodeExpanded(node) : false;
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Check if a node is selected
|
|
1352
|
+
* @param nodeId - The ID of the node
|
|
1353
|
+
* @returns true if node is selected, false otherwise
|
|
1354
|
+
*/
|
|
1355
|
+
isNodeSelected(nodeId) {
|
|
1356
|
+
const node = this.findNode(nodeId);
|
|
1357
|
+
return node ? this.getNodeSelected(node) : false;
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* Check if a node has children
|
|
1361
|
+
* @param nodeId - The ID of the node
|
|
1362
|
+
* @returns true if node has children, false otherwise
|
|
1363
|
+
*/
|
|
1364
|
+
hasChildren(nodeId) {
|
|
1365
|
+
const node = this.findNode(nodeId);
|
|
1366
|
+
return this.treeService.hasChildren(node ?? {}, this.childrenField());
|
|
1367
|
+
}
|
|
530
1368
|
/**
|
|
531
1369
|
* Get template context for a node
|
|
532
1370
|
*/
|
|
533
1371
|
getTemplateContext(node, level = 0) {
|
|
1372
|
+
const children = this.getNodeChildren(node);
|
|
1373
|
+
const childrenCount = this.getNodeChildrenCount(node);
|
|
534
1374
|
return {
|
|
535
1375
|
$implicit: node,
|
|
536
1376
|
node,
|
|
537
1377
|
level,
|
|
538
|
-
expanded: node
|
|
539
|
-
childrenCount:
|
|
540
|
-
loading: node
|
|
1378
|
+
expanded: this.getNodeExpanded(node),
|
|
1379
|
+
childrenCount: childrenCount ?? children?.length ?? 0,
|
|
1380
|
+
loading: this.getNodeLoading(node),
|
|
541
1381
|
};
|
|
542
1382
|
}
|
|
543
1383
|
/**
|
|
@@ -553,7 +1393,8 @@ class AXTreeViewComponent {
|
|
|
553
1393
|
* Check if node should show expand toggle
|
|
554
1394
|
*/
|
|
555
1395
|
shouldShowExpandToggle(node) {
|
|
556
|
-
return this.treeService.hasChildren(node
|
|
1396
|
+
return (this.treeService.hasChildren(node, this.childrenField()) ||
|
|
1397
|
+
this.treeService.canLazyLoad(node, this.isLazyDataSource(), this.childrenCountField(), this.childrenField()));
|
|
557
1398
|
}
|
|
558
1399
|
/**
|
|
559
1400
|
* Check if checkboxes should be shown (only for multiple mode)
|
|
@@ -561,11 +1402,50 @@ class AXTreeViewComponent {
|
|
|
561
1402
|
shouldShowCheckbox() {
|
|
562
1403
|
return this.selectMode() === 'multiple' && this.showCheckbox();
|
|
563
1404
|
}
|
|
1405
|
+
/**
|
|
1406
|
+
* Check if a node is a leaf (has no children)
|
|
1407
|
+
* A node is a leaf if it has no loaded children AND no childrenCount (or childrenCount is 0)
|
|
1408
|
+
*/
|
|
1409
|
+
isLeafNode(node) {
|
|
1410
|
+
const hasChildren = this.treeService.hasChildren(node, this.childrenField());
|
|
1411
|
+
const childrenCount = this.getNodeChildrenCount(node);
|
|
1412
|
+
const hasChildrenCount = childrenCount && childrenCount > 0;
|
|
1413
|
+
const canLazyLoad = this.treeService.canLazyLoad(node, this.isLazyDataSource(), this.childrenCountField(), this.childrenField());
|
|
1414
|
+
// A node is a leaf if:
|
|
1415
|
+
// 1. It has no loaded children
|
|
1416
|
+
// 2. AND it has no childrenCount (or childrenCount is 0)
|
|
1417
|
+
// 3. AND it cannot be lazy loaded
|
|
1418
|
+
return !hasChildren && !hasChildrenCount && !canLazyLoad;
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Check if a node can be selected (considering selectMode and isLeafOnlyMode)
|
|
1422
|
+
*/
|
|
1423
|
+
canSelectNode(node) {
|
|
1424
|
+
if (this.selectMode() === 'none') {
|
|
1425
|
+
return false;
|
|
1426
|
+
}
|
|
1427
|
+
if (this.isLeafOnlyMode()) {
|
|
1428
|
+
return this.isLeafNode(node);
|
|
1429
|
+
}
|
|
1430
|
+
return true;
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Check if checkbox should be shown for a specific node
|
|
1434
|
+
*/
|
|
1435
|
+
shouldShowCheckboxForNode(node) {
|
|
1436
|
+
if (!this.shouldShowCheckbox()) {
|
|
1437
|
+
return false;
|
|
1438
|
+
}
|
|
1439
|
+
if (this.isLeafOnlyMode()) {
|
|
1440
|
+
return this.isLeafNode(node);
|
|
1441
|
+
}
|
|
1442
|
+
return true;
|
|
1443
|
+
}
|
|
564
1444
|
/**
|
|
565
1445
|
* Generate unique list ID for each node
|
|
566
1446
|
*/
|
|
567
1447
|
getListId(node) {
|
|
568
|
-
return this.treeService.getListId(node);
|
|
1448
|
+
return this.treeService.getListId(node, this.idField());
|
|
569
1449
|
}
|
|
570
1450
|
/**
|
|
571
1451
|
* Check if a node is currently focused
|
|
@@ -586,30 +1466,50 @@ class AXTreeViewComponent {
|
|
|
586
1466
|
if (!this.shouldShowExpandToggle(node)) {
|
|
587
1467
|
return null;
|
|
588
1468
|
}
|
|
589
|
-
return node
|
|
1469
|
+
return this.getNodeExpanded(node) ? 'true' : 'false';
|
|
590
1470
|
}
|
|
591
1471
|
/**
|
|
592
1472
|
* Get ARIA selected state for a node
|
|
593
1473
|
*/
|
|
594
1474
|
getNodeAriaSelected(node) {
|
|
1475
|
+
if (this.selectMode() === 'none') {
|
|
1476
|
+
return null;
|
|
1477
|
+
}
|
|
1478
|
+
const selected = this.getNodeSelected(node);
|
|
595
1479
|
if (this.selectMode() === 'single') {
|
|
596
|
-
return
|
|
1480
|
+
return selected ? 'true' : 'false';
|
|
1481
|
+
}
|
|
1482
|
+
if (this.selectMode() === 'multiple') {
|
|
1483
|
+
return selected ? 'true' : 'false';
|
|
597
1484
|
}
|
|
598
1485
|
return null;
|
|
599
1486
|
}
|
|
1487
|
+
/**
|
|
1488
|
+
* Emit selection change event with all selected nodes
|
|
1489
|
+
*/
|
|
1490
|
+
emitSelectionChange() {
|
|
1491
|
+
const selectedNodes = this.getSelectedNodes();
|
|
1492
|
+
this.onSelectionChange.emit({
|
|
1493
|
+
component: this,
|
|
1494
|
+
selectedNodes,
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
600
1497
|
// ==================== Event Handlers ====================
|
|
601
1498
|
/**
|
|
602
|
-
* Handle node click - for single selection mode or multiple mode
|
|
1499
|
+
* Handle node click - for single selection mode or multiple mode when showCheckbox is false
|
|
603
1500
|
*/
|
|
604
1501
|
onNodeClick(node, event) {
|
|
605
|
-
if (node
|
|
1502
|
+
if (this.getNodeDisabled(node))
|
|
1503
|
+
return;
|
|
1504
|
+
if (this.selectMode() === 'none')
|
|
1505
|
+
return;
|
|
1506
|
+
if (!this.canSelectNode(node))
|
|
606
1507
|
return;
|
|
607
1508
|
const mode = this.selectMode();
|
|
608
|
-
const shouldCheckOnClick = this.checkOnClick();
|
|
609
1509
|
if (mode === 'single') {
|
|
610
1510
|
this.handleSingleSelection(node, event);
|
|
611
1511
|
}
|
|
612
|
-
else if (mode === 'multiple' &&
|
|
1512
|
+
else if (mode === 'multiple' && !this.showCheckbox()) {
|
|
613
1513
|
this.handleMultipleSelection(node, event);
|
|
614
1514
|
}
|
|
615
1515
|
}
|
|
@@ -617,19 +1517,19 @@ class AXTreeViewComponent {
|
|
|
617
1517
|
* Toggle node expansion state with lazy loading support
|
|
618
1518
|
*/
|
|
619
1519
|
async toggleNode(node, event) {
|
|
620
|
-
if (node
|
|
1520
|
+
if (this.getNodeDisabled(node))
|
|
621
1521
|
return;
|
|
622
1522
|
if (this.isEvent(event) && typeof event.stopPropagation === 'function') {
|
|
623
1523
|
event.stopPropagation();
|
|
624
1524
|
}
|
|
625
|
-
const hasChildren = this.treeService.hasChildren(node);
|
|
626
|
-
const canLazyLoad = this.treeService.canLazyLoad(node, this.isLazyDataSource());
|
|
1525
|
+
const hasChildren = this.treeService.hasChildren(node, this.childrenField());
|
|
1526
|
+
const canLazyLoad = this.treeService.canLazyLoad(node, this.isLazyDataSource(), this.childrenCountField(), this.childrenField());
|
|
627
1527
|
if (hasChildren || canLazyLoad) {
|
|
628
|
-
const willExpand = !node
|
|
1528
|
+
const willExpand = !this.getNodeExpanded(node);
|
|
629
1529
|
if (willExpand && canLazyLoad) {
|
|
630
1530
|
await this.loadNodeChildren(node);
|
|
631
1531
|
}
|
|
632
|
-
node
|
|
1532
|
+
this.setNodeExpanded(node, willExpand);
|
|
633
1533
|
this.refreshNodes();
|
|
634
1534
|
this.onNodeToggle.emit({ component: this, node, nativeEvent: event });
|
|
635
1535
|
}
|
|
@@ -640,17 +1540,22 @@ class AXTreeViewComponent {
|
|
|
640
1540
|
toggleSelection(node, event) {
|
|
641
1541
|
if (!event.isUserInteraction)
|
|
642
1542
|
return;
|
|
1543
|
+
if (this.selectMode() === 'none')
|
|
1544
|
+
return;
|
|
1545
|
+
if (!this.canSelectNode(node))
|
|
1546
|
+
return;
|
|
643
1547
|
const mode = this.selectMode();
|
|
644
1548
|
if (mode !== 'multiple')
|
|
645
1549
|
return;
|
|
646
1550
|
const newValue = event.value === null ? true : event.value;
|
|
647
|
-
node
|
|
648
|
-
node
|
|
649
|
-
|
|
650
|
-
|
|
1551
|
+
this.setNodeSelected(node, newValue);
|
|
1552
|
+
this.setNodeIndeterminate(node, false);
|
|
1553
|
+
const children = this.getNodeChildren(node);
|
|
1554
|
+
if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
|
|
1555
|
+
this.treeService.selectAllChildren(children, newValue, this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
651
1556
|
}
|
|
652
|
-
if (this.
|
|
653
|
-
this.treeService.updateParentStates(this.nodes(), node, this.
|
|
1557
|
+
if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
|
|
1558
|
+
this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
|
|
654
1559
|
}
|
|
655
1560
|
this.refreshNodes();
|
|
656
1561
|
this.onNodeSelect.emit({
|
|
@@ -658,12 +1563,13 @@ class AXTreeViewComponent {
|
|
|
658
1563
|
node,
|
|
659
1564
|
isUserInteraction: event.isUserInteraction,
|
|
660
1565
|
});
|
|
1566
|
+
this.emitSelectionChange();
|
|
661
1567
|
}
|
|
662
1568
|
/**
|
|
663
1569
|
* Handle drop events for tree nodes
|
|
664
1570
|
*/
|
|
665
1571
|
onDrop(event, parentNode) {
|
|
666
|
-
const targetArray = parentNode
|
|
1572
|
+
const targetArray = parentNode ? (this.getNodeChildren(parentNode) ?? []) : this.nodes();
|
|
667
1573
|
const isReordering = event.previousContainer === event.container;
|
|
668
1574
|
if (isReordering) {
|
|
669
1575
|
this.handleReorder(event, targetArray, parentNode);
|
|
@@ -684,14 +1590,18 @@ class AXTreeViewComponent {
|
|
|
684
1590
|
if (!sourceArray)
|
|
685
1591
|
return;
|
|
686
1592
|
const movedNode = sourceArray[event.previousIndex];
|
|
687
|
-
if (!this.treeService.isValidDropTarget(movedNode, targetNode))
|
|
1593
|
+
if (!this.treeService.isValidDropTarget(movedNode, targetNode, this.idField(), this.childrenField()))
|
|
688
1594
|
return;
|
|
689
1595
|
if (!this.emitBeforeDropEvent(movedNode, sourceListId, targetNode, event.previousIndex, 0))
|
|
690
1596
|
return;
|
|
691
|
-
|
|
1597
|
+
let targetChildren = this.getNodeChildren(targetNode);
|
|
1598
|
+
if (!targetChildren) {
|
|
1599
|
+
targetChildren = [];
|
|
1600
|
+
this.setNodeChildren(targetNode, targetChildren);
|
|
1601
|
+
}
|
|
692
1602
|
sourceArray.splice(event.previousIndex, 1);
|
|
693
|
-
|
|
694
|
-
targetNode
|
|
1603
|
+
targetChildren.unshift(movedNode);
|
|
1604
|
+
this.setNodeExpanded(targetNode, true);
|
|
695
1605
|
this.emitDropEvents(movedNode, this.findParentByListId(sourceListId), targetNode, event.previousIndex, 0, false);
|
|
696
1606
|
this.refreshNodes();
|
|
697
1607
|
}
|
|
@@ -706,18 +1616,25 @@ class AXTreeViewComponent {
|
|
|
706
1616
|
*/
|
|
707
1617
|
onTreeFocus(event) {
|
|
708
1618
|
if (event.target === event.currentTarget) {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
if
|
|
1619
|
+
const flatList = this.treeService.buildFlatNodeList(this.nodes(), this.hiddenField(), this.disabledField(), this.expandedField(), this.childrenField());
|
|
1620
|
+
if (flatList.length > 0) {
|
|
1621
|
+
const focusedId = this.focusedNodeId();
|
|
1622
|
+
if (focusedId) {
|
|
1623
|
+
// Check if the focused node still exists and is not hidden
|
|
1624
|
+
const focusedNode = this.treeService.findNodeById(this.nodes(), focusedId, this.idField());
|
|
1625
|
+
if (focusedNode && this.getNodeHidden(focusedNode) !== true) {
|
|
714
1626
|
this.focusNodeById(focusedId);
|
|
715
1627
|
}
|
|
716
1628
|
else {
|
|
717
|
-
|
|
1629
|
+
// Focused node no longer exists, focus first node
|
|
1630
|
+
this.focusNodeById(this.getNodeId(flatList[0].node));
|
|
718
1631
|
}
|
|
719
1632
|
}
|
|
720
|
-
|
|
1633
|
+
else {
|
|
1634
|
+
// No node is focused, focus first node
|
|
1635
|
+
this.focusNodeById(this.getNodeId(flatList[0].node));
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
721
1638
|
}
|
|
722
1639
|
}
|
|
723
1640
|
/**
|
|
@@ -732,11 +1649,13 @@ class AXTreeViewComponent {
|
|
|
732
1649
|
* Handle keyboard navigation
|
|
733
1650
|
*/
|
|
734
1651
|
handleKeyDown(event) {
|
|
735
|
-
const flatList = this.treeService.buildFlatNodeList(this.nodes());
|
|
1652
|
+
const flatList = this.treeService.buildFlatNodeList(this.nodes(), this.hiddenField(), this.disabledField(), this.expandedField(), this.childrenField());
|
|
736
1653
|
if (flatList.length === 0)
|
|
737
1654
|
return;
|
|
738
1655
|
const currentFocused = this.getFocusedNode();
|
|
739
|
-
let currentIndex = currentFocused
|
|
1656
|
+
let currentIndex = currentFocused
|
|
1657
|
+
? flatList.findIndex((item) => this.getNodeId(item.node) === this.getNodeId(currentFocused))
|
|
1658
|
+
: -1;
|
|
740
1659
|
if (currentIndex === -1 && event.target === event.currentTarget) {
|
|
741
1660
|
currentIndex = 0;
|
|
742
1661
|
}
|
|
@@ -749,7 +1668,7 @@ class AXTreeViewComponent {
|
|
|
749
1668
|
if (navigationResult.targetIndex !== null &&
|
|
750
1669
|
navigationResult.targetIndex >= 0 &&
|
|
751
1670
|
navigationResult.targetIndex < flatList.length) {
|
|
752
|
-
this.focusNodeById(flatList[navigationResult.targetIndex].node
|
|
1671
|
+
this.focusNodeById(this.getNodeId(flatList[navigationResult.targetIndex].node));
|
|
753
1672
|
}
|
|
754
1673
|
}
|
|
755
1674
|
}
|
|
@@ -772,32 +1691,34 @@ class AXTreeViewComponent {
|
|
|
772
1691
|
* Load children for a node using lazy loading
|
|
773
1692
|
*/
|
|
774
1693
|
async loadNodeChildren(node) {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
if (this.treeService.hasChildren(node) || !node.childrenCount || node.childrenCount === 0) {
|
|
1694
|
+
const nodeId = this.getNodeId(node);
|
|
1695
|
+
if (!this.isLazyDataSource() || this.loadingNodes().has(nodeId))
|
|
778
1696
|
return;
|
|
1697
|
+
if (this.treeService.hasChildren(node, this.childrenField())) {
|
|
1698
|
+
const childrenCount = this.getNodeChildrenCount(node);
|
|
1699
|
+
if (!childrenCount || childrenCount === 0) {
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
779
1702
|
}
|
|
780
1703
|
const ds = this.datasource();
|
|
781
1704
|
if (typeof ds !== 'function')
|
|
782
1705
|
return;
|
|
783
1706
|
try {
|
|
784
|
-
|
|
785
|
-
this.loadingNodes.update((set) => new Set(set).add(node.id));
|
|
1707
|
+
this.loadingNodes.update((set) => new Set(set).add(nodeId));
|
|
786
1708
|
this.refreshNodes();
|
|
787
|
-
const result = ds(
|
|
1709
|
+
const result = ds(nodeId);
|
|
788
1710
|
const children = result instanceof Promise ? await result : result;
|
|
789
|
-
node
|
|
790
|
-
node
|
|
1711
|
+
this.setNodeChildren(node, children);
|
|
1712
|
+
this.setNodeChildrenCount(node, children.length);
|
|
791
1713
|
}
|
|
792
1714
|
catch (error) {
|
|
793
1715
|
this.handleError('Failed to load children', error);
|
|
794
|
-
node
|
|
1716
|
+
this.setNodeChildrenCount(node, 0);
|
|
795
1717
|
}
|
|
796
1718
|
finally {
|
|
797
|
-
node.loading = false;
|
|
798
1719
|
this.loadingNodes.update((set) => {
|
|
799
1720
|
const newSet = new Set(set);
|
|
800
|
-
newSet.delete(
|
|
1721
|
+
newSet.delete(nodeId);
|
|
801
1722
|
return newSet;
|
|
802
1723
|
});
|
|
803
1724
|
this.refreshNodes();
|
|
@@ -805,9 +1726,13 @@ class AXTreeViewComponent {
|
|
|
805
1726
|
}
|
|
806
1727
|
/**
|
|
807
1728
|
* Internal method to refresh nodes signal and sync back to datasource if it's an array
|
|
1729
|
+
* Creates new array references for all nested children to ensure reactivity
|
|
808
1730
|
*/
|
|
809
1731
|
refreshNodes() {
|
|
810
1732
|
const currentNodes = this.nodes();
|
|
1733
|
+
// Create new array references for all nested children to ensure reactivity
|
|
1734
|
+
// This ensures Angular's change detection picks up changes even with callback datasource
|
|
1735
|
+
this.ensureNewArrayReferences(currentNodes);
|
|
811
1736
|
this.nodes.set([...currentNodes]);
|
|
812
1737
|
if (!this.isLazyDataSource() && !this.isUpdatingFromDatasource) {
|
|
813
1738
|
this.isUpdatingFromDatasource = true;
|
|
@@ -817,13 +1742,28 @@ class AXTreeViewComponent {
|
|
|
817
1742
|
}, 0);
|
|
818
1743
|
}
|
|
819
1744
|
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Recursively ensure all children arrays have new references to trigger change detection
|
|
1747
|
+
* Mutates the tree structure by replacing children arrays with new array references
|
|
1748
|
+
*/
|
|
1749
|
+
ensureNewArrayReferences(nodes) {
|
|
1750
|
+
for (const node of nodes) {
|
|
1751
|
+
const children = this.getNodeChildren(node);
|
|
1752
|
+
if (children && children.length > 0) {
|
|
1753
|
+
// Create new array reference for children
|
|
1754
|
+
this.setNodeChildren(node, [...children]);
|
|
1755
|
+
// Recursively process nested children
|
|
1756
|
+
this.ensureNewArrayReferences(children);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
820
1760
|
/**
|
|
821
1761
|
* Handle single selection mode
|
|
822
1762
|
*/
|
|
823
1763
|
handleSingleSelection(node, event) {
|
|
824
|
-
this.treeService.deselectAllNodes(this.nodes());
|
|
825
|
-
node
|
|
826
|
-
node
|
|
1764
|
+
this.treeService.deselectAllNodes(this.nodes(), this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
1765
|
+
this.setNodeSelected(node, true);
|
|
1766
|
+
this.setNodeIndeterminate(node, false);
|
|
827
1767
|
this.refreshNodes();
|
|
828
1768
|
this.onNodeSelect.emit({
|
|
829
1769
|
component: this,
|
|
@@ -831,19 +1771,21 @@ class AXTreeViewComponent {
|
|
|
831
1771
|
nativeEvent: event,
|
|
832
1772
|
isUserInteraction: true,
|
|
833
1773
|
});
|
|
1774
|
+
this.emitSelectionChange();
|
|
834
1775
|
}
|
|
835
1776
|
/**
|
|
836
|
-
* Handle multiple selection mode
|
|
1777
|
+
* Handle multiple selection mode when showCheckbox is false (click to toggle)
|
|
837
1778
|
*/
|
|
838
1779
|
handleMultipleSelection(node, event) {
|
|
839
|
-
const newValue = !node
|
|
840
|
-
node
|
|
841
|
-
node
|
|
842
|
-
|
|
843
|
-
|
|
1780
|
+
const newValue = !this.getNodeSelected(node);
|
|
1781
|
+
this.setNodeSelected(node, newValue);
|
|
1782
|
+
this.setNodeIndeterminate(node, false);
|
|
1783
|
+
const children = this.getNodeChildren(node);
|
|
1784
|
+
if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
|
|
1785
|
+
this.treeService.selectAllChildren(children, newValue, this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
844
1786
|
}
|
|
845
|
-
if (this.
|
|
846
|
-
this.treeService.updateParentStates(this.nodes(), node, this.
|
|
1787
|
+
if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
|
|
1788
|
+
this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
|
|
847
1789
|
}
|
|
848
1790
|
this.refreshNodes();
|
|
849
1791
|
this.onNodeSelect.emit({
|
|
@@ -852,30 +1794,33 @@ class AXTreeViewComponent {
|
|
|
852
1794
|
nativeEvent: event,
|
|
853
1795
|
isUserInteraction: true,
|
|
854
1796
|
});
|
|
1797
|
+
this.emitSelectionChange();
|
|
855
1798
|
}
|
|
856
1799
|
/**
|
|
857
1800
|
* Get array reference by drop list ID
|
|
858
1801
|
*/
|
|
859
1802
|
getArrayByListId(listId) {
|
|
860
|
-
return this.treeService.getArrayByListId(this.nodes(), listId);
|
|
1803
|
+
return this.treeService.getArrayByListId(this.nodes(), listId, this.idField(), this.childrenField());
|
|
861
1804
|
}
|
|
862
1805
|
/**
|
|
863
1806
|
* Find parent node by list ID
|
|
864
1807
|
*/
|
|
865
1808
|
findParentByListId(listId) {
|
|
866
|
-
return this.treeService.findParentByListId(this.nodes(), listId);
|
|
1809
|
+
return this.treeService.findParentByListId(this.nodes(), listId, this.idField());
|
|
867
1810
|
}
|
|
868
1811
|
/**
|
|
869
|
-
* Check if move operation is allowed based on
|
|
1812
|
+
* Check if move operation is allowed based on dragBehavior
|
|
870
1813
|
*/
|
|
871
1814
|
canMoveToParent() {
|
|
872
|
-
|
|
1815
|
+
const behavior = this.dragBehavior();
|
|
1816
|
+
return behavior !== 'none' && behavior !== 'order-only';
|
|
873
1817
|
}
|
|
874
1818
|
/**
|
|
875
|
-
* Check if reorder operation is allowed based on
|
|
1819
|
+
* Check if reorder operation is allowed based on dragBehavior
|
|
876
1820
|
*/
|
|
877
1821
|
canReorder() {
|
|
878
|
-
|
|
1822
|
+
const behavior = this.dragBehavior();
|
|
1823
|
+
return behavior !== 'none' && behavior !== 'move';
|
|
879
1824
|
}
|
|
880
1825
|
/**
|
|
881
1826
|
* Handle reordering within the same list */
|
|
@@ -948,7 +1893,7 @@ class AXTreeViewComponent {
|
|
|
948
1893
|
const focusedId = this.focusedNodeId();
|
|
949
1894
|
if (!focusedId)
|
|
950
1895
|
return null;
|
|
951
|
-
return this.treeService.findNodeById(this.nodes(), focusedId);
|
|
1896
|
+
return this.treeService.findNodeById(this.nodes(), focusedId, this.idField());
|
|
952
1897
|
}
|
|
953
1898
|
/**
|
|
954
1899
|
* Set focus to a node by ID
|
|
@@ -992,14 +1937,14 @@ class AXTreeViewComponent {
|
|
|
992
1937
|
break;
|
|
993
1938
|
case 'ArrowLeft':
|
|
994
1939
|
if (currentFocused) {
|
|
995
|
-
if (currentFocused
|
|
1940
|
+
if (this.getNodeExpanded(currentFocused) && this.shouldShowExpandToggle(currentFocused)) {
|
|
996
1941
|
this.toggleNode(currentFocused, event);
|
|
997
1942
|
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
998
1943
|
}
|
|
999
1944
|
else {
|
|
1000
1945
|
const currentItem = flatList[currentIndex];
|
|
1001
1946
|
if (currentItem?.parent) {
|
|
1002
|
-
targetIndex = flatList.findIndex((item) => item.node
|
|
1947
|
+
targetIndex = flatList.findIndex((item) => this.getNodeId(item.node) === this.getNodeId(currentItem.parent));
|
|
1003
1948
|
}
|
|
1004
1949
|
else {
|
|
1005
1950
|
shouldPreventDefault = false;
|
|
@@ -1012,22 +1957,21 @@ class AXTreeViewComponent {
|
|
|
1012
1957
|
break;
|
|
1013
1958
|
case 'ArrowRight':
|
|
1014
1959
|
if (currentFocused) {
|
|
1015
|
-
if (!currentFocused
|
|
1960
|
+
if (!this.getNodeExpanded(currentFocused) && this.shouldShowExpandToggle(currentFocused)) {
|
|
1016
1961
|
this.toggleNode(currentFocused, event);
|
|
1017
1962
|
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1018
1963
|
}
|
|
1019
|
-
else if (currentFocused
|
|
1020
|
-
this.treeService.hasChildren(currentFocused)
|
|
1021
|
-
currentFocused
|
|
1022
|
-
|
|
1023
|
-
if (children.length > 0) {
|
|
1964
|
+
else if (this.getNodeExpanded(currentFocused) &&
|
|
1965
|
+
this.treeService.hasChildren(currentFocused, this.childrenField())) {
|
|
1966
|
+
const children = this.getNodeChildren(currentFocused);
|
|
1967
|
+
if (children && children.length > 0) {
|
|
1024
1968
|
const firstChild = children[0];
|
|
1025
|
-
targetIndex = flatList.findIndex((item) => item.node
|
|
1969
|
+
targetIndex = flatList.findIndex((item) => this.getNodeId(item.node) === this.getNodeId(firstChild));
|
|
1026
1970
|
if (targetIndex === -1) {
|
|
1027
|
-
const updatedFlatList = this.treeService.buildFlatNodeList(this.nodes());
|
|
1028
|
-
targetIndex = updatedFlatList.findIndex((item) => item.node
|
|
1971
|
+
const updatedFlatList = this.treeService.buildFlatNodeList(this.nodes(), this.hiddenField(), this.disabledField(), this.expandedField(), this.childrenField());
|
|
1972
|
+
targetIndex = updatedFlatList.findIndex((item) => this.getNodeId(item.node) === this.getNodeId(firstChild));
|
|
1029
1973
|
if (targetIndex >= 0 && targetIndex < updatedFlatList.length) {
|
|
1030
|
-
this.focusNodeById(updatedFlatList[targetIndex].node
|
|
1974
|
+
this.focusNodeById(this.getNodeId(updatedFlatList[targetIndex].node));
|
|
1031
1975
|
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1032
1976
|
}
|
|
1033
1977
|
}
|
|
@@ -1052,7 +1996,7 @@ class AXTreeViewComponent {
|
|
|
1052
1996
|
break;
|
|
1053
1997
|
case ' ':
|
|
1054
1998
|
case 'Space':
|
|
1055
|
-
if (currentFocused && this.selectMode()
|
|
1999
|
+
if (currentFocused && this.selectMode() !== 'none' && this.canSelectNode(currentFocused)) {
|
|
1056
2000
|
event.preventDefault();
|
|
1057
2001
|
this.handleSpaceKeySelection(currentFocused, event);
|
|
1058
2002
|
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
@@ -1060,7 +2004,7 @@ class AXTreeViewComponent {
|
|
|
1060
2004
|
shouldPreventDefault = false;
|
|
1061
2005
|
break;
|
|
1062
2006
|
case 'Enter':
|
|
1063
|
-
if (currentFocused) {
|
|
2007
|
+
if (currentFocused && this.canSelectNode(currentFocused)) {
|
|
1064
2008
|
event.preventDefault();
|
|
1065
2009
|
this.handleEnterKeySelection(currentFocused, event);
|
|
1066
2010
|
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
@@ -1069,7 +2013,7 @@ class AXTreeViewComponent {
|
|
|
1069
2013
|
break;
|
|
1070
2014
|
default:
|
|
1071
2015
|
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
|
|
1072
|
-
if (currentFocused && this.selectMode() === 'multiple') {
|
|
2016
|
+
if (currentFocused && this.selectMode() === 'multiple' && this.canSelectNode(currentFocused)) {
|
|
1073
2017
|
event.preventDefault();
|
|
1074
2018
|
this.handleCtrlEnterSelection(currentFocused, event);
|
|
1075
2019
|
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
@@ -1083,16 +2027,31 @@ class AXTreeViewComponent {
|
|
|
1083
2027
|
}
|
|
1084
2028
|
/**
|
|
1085
2029
|
* Handle Space key selection
|
|
2030
|
+
* In single mode: replaces selection (deselects all, selects this node)
|
|
2031
|
+
* In multiple mode: toggles selection
|
|
1086
2032
|
*/
|
|
1087
2033
|
handleSpaceKeySelection(node, event) {
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
if (
|
|
1092
|
-
|
|
2034
|
+
if (!this.canSelectNode(node))
|
|
2035
|
+
return;
|
|
2036
|
+
const mode = this.selectMode();
|
|
2037
|
+
if (mode === 'single') {
|
|
2038
|
+
// Single mode: replace selection
|
|
2039
|
+
this.treeService.deselectAllNodes(this.nodes(), this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
2040
|
+
this.setNodeSelected(node, true);
|
|
2041
|
+
this.setNodeIndeterminate(node, false);
|
|
1093
2042
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
2043
|
+
else {
|
|
2044
|
+
// Multiple mode: toggle selection
|
|
2045
|
+
const newValue = !this.getNodeSelected(node);
|
|
2046
|
+
this.setNodeSelected(node, newValue);
|
|
2047
|
+
this.setNodeIndeterminate(node, false);
|
|
2048
|
+
const children = this.getNodeChildren(node);
|
|
2049
|
+
if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
|
|
2050
|
+
this.treeService.selectAllChildren(children, newValue, this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
2051
|
+
}
|
|
2052
|
+
if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
|
|
2053
|
+
this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
|
|
2054
|
+
}
|
|
1096
2055
|
}
|
|
1097
2056
|
this.refreshNodes();
|
|
1098
2057
|
this.onNodeSelect.emit({
|
|
@@ -1101,21 +2060,30 @@ class AXTreeViewComponent {
|
|
|
1101
2060
|
nativeEvent: event,
|
|
1102
2061
|
isUserInteraction: true,
|
|
1103
2062
|
});
|
|
2063
|
+
this.emitSelectionChange();
|
|
1104
2064
|
}
|
|
1105
2065
|
/**
|
|
1106
2066
|
* Handle Enter key selection
|
|
2067
|
+
* In single mode: replaces selection (deselects all, selects this node)
|
|
2068
|
+
* In multiple mode: replaces selection (deselects all, selects this node and respects selectionBehavior)
|
|
1107
2069
|
*/
|
|
1108
2070
|
handleEnterKeySelection(node, event) {
|
|
2071
|
+
if (!this.canSelectNode(node))
|
|
2072
|
+
return;
|
|
1109
2073
|
const mode = this.selectMode();
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
node
|
|
2074
|
+
// Enter key always replaces selection (deselects all first)
|
|
2075
|
+
this.treeService.deselectAllNodes(this.nodes(), this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
2076
|
+
this.setNodeSelected(node, true);
|
|
2077
|
+
this.setNodeIndeterminate(node, false);
|
|
1113
2078
|
if (mode === 'multiple') {
|
|
1114
|
-
|
|
1115
|
-
|
|
2079
|
+
const children = this.getNodeChildren(node);
|
|
2080
|
+
// Respect selectionBehavior: cascade to children if enabled
|
|
2081
|
+
if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
|
|
2082
|
+
this.treeService.selectAllChildren(children, true, this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
1116
2083
|
}
|
|
1117
|
-
if
|
|
1118
|
-
|
|
2084
|
+
// Respect selectionBehavior: update parent states if intermediate mode
|
|
2085
|
+
if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
|
|
2086
|
+
this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
|
|
1119
2087
|
}
|
|
1120
2088
|
}
|
|
1121
2089
|
this.refreshNodes();
|
|
@@ -1125,19 +2093,23 @@ class AXTreeViewComponent {
|
|
|
1125
2093
|
nativeEvent: event,
|
|
1126
2094
|
isUserInteraction: true,
|
|
1127
2095
|
});
|
|
2096
|
+
this.emitSelectionChange();
|
|
1128
2097
|
}
|
|
1129
2098
|
/**
|
|
1130
2099
|
* Handle Ctrl/Cmd + Enter key selection
|
|
1131
2100
|
*/
|
|
1132
2101
|
handleCtrlEnterSelection(node, event) {
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
2102
|
+
if (!this.canSelectNode(node))
|
|
2103
|
+
return;
|
|
2104
|
+
const newValue = !this.getNodeSelected(node);
|
|
2105
|
+
this.setNodeSelected(node, newValue);
|
|
2106
|
+
this.setNodeIndeterminate(node, false);
|
|
2107
|
+
const children = this.getNodeChildren(node);
|
|
2108
|
+
if (this.cascadesToChildren() && children && !this.isLeafOnlyMode()) {
|
|
2109
|
+
this.treeService.selectAllChildren(children, newValue, this.selectedField(), this.indeterminateField(), this.childrenField());
|
|
1138
2110
|
}
|
|
1139
|
-
if (this.
|
|
1140
|
-
this.treeService.updateParentStates(this.nodes(), node, this.
|
|
2111
|
+
if (this.hasIntermediateState() && !this.isLeafOnlyMode()) {
|
|
2112
|
+
this.treeService.updateParentStates(this.nodes(), node, this.hasIntermediateState(), this.idField(), this.childrenField(), this.selectedField(), this.indeterminateField());
|
|
1141
2113
|
}
|
|
1142
2114
|
this.refreshNodes();
|
|
1143
2115
|
this.onNodeSelect.emit({
|
|
@@ -1146,6 +2118,7 @@ class AXTreeViewComponent {
|
|
|
1146
2118
|
nativeEvent: event,
|
|
1147
2119
|
isUserInteraction: true,
|
|
1148
2120
|
});
|
|
2121
|
+
this.emitSelectionChange();
|
|
1149
2122
|
}
|
|
1150
2123
|
/**
|
|
1151
2124
|
* Type guard to check if value is an Event
|
|
@@ -1165,7 +2138,7 @@ class AXTreeViewComponent {
|
|
|
1165
2138
|
}
|
|
1166
2139
|
}
|
|
1167
2140
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1168
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: AXTreeViewComponent, isStandalone: true, selector: "ax-tree-view", inputs: { datasource: { classPropertyName: "datasource", publicName: "datasource", isSignal: true, isRequired: true, transformFunction: null }, selectMode: { classPropertyName: "selectMode", publicName: "selectMode", isSignal: true, isRequired: false, transformFunction: null }, showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, checkChildrenOnSelect: { classPropertyName: "checkChildrenOnSelect", publicName: "checkChildrenOnSelect", isSignal: true, isRequired: false, transformFunction: null }, intermediateState: { classPropertyName: "intermediateState", publicName: "intermediateState", isSignal: true, isRequired: false, transformFunction: null }, checkOnClick: { classPropertyName: "checkOnClick", publicName: "checkOnClick", isSignal: true, isRequired: false, transformFunction: null }, dragMode: { classPropertyName: "dragMode", publicName: "dragMode", isSignal: true, isRequired: false, transformFunction: null }, dragOperationType: { classPropertyName: "dragOperationType", publicName: "dragOperationType", isSignal: true, isRequired: false, transformFunction: null }, showIcons: { classPropertyName: "showIcons", publicName: "showIcons", isSignal: true, isRequired: false, transformFunction: null }, showChildrenBadge: { classPropertyName: "showChildrenBadge", publicName: "showChildrenBadge", isSignal: true, isRequired: false, transformFunction: null }, expandedIcon: { classPropertyName: "expandedIcon", publicName: "expandedIcon", isSignal: true, isRequired: false, transformFunction: null }, collapsedIcon: { classPropertyName: "collapsedIcon", publicName: "collapsedIcon", isSignal: true, isRequired: false, transformFunction: null }, indentSize: { classPropertyName: "indentSize", publicName: "indentSize", isSignal: true, isRequired: false, transformFunction: null }, nodeHeight: { classPropertyName: "nodeHeight", publicName: "nodeHeight", isSignal: true, isRequired: false, transformFunction: null }, look: { classPropertyName: "look", publicName: "look", isSignal: true, isRequired: false, transformFunction: null }, itemTemplate: { classPropertyName: "itemTemplate", publicName: "itemTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { datasource: "datasourceChange", onBeforeDrop: "onBeforeDrop", onNodeToggle: "onNodeToggle", onNodeSelect: "onNodeSelect", onOrderChange: "onOrderChange", onMoveChange: "onMoveChange", onItemsChange: "onItemsChange" }, host: { attributes: { "role": "tree", "tabindex": "0" }, listeners: { "keydown": "handleKeyDown($event)", "focus": "onTreeFocus($event)", "blur": "onTreeBlur($event)" }, properties: { "class.ax-tree-view-default": "look() === 'default'", "class.ax-tree-view-card": "look() === 'card'", "class.ax-tree-view-with-line": "look() === 'with-line'", "class.ax-tree-view-rtl": "isRtl", "style.--ax-tree-view-indent-size": "indentSize() + 'px'", "style.--ax-tree-view-line-offset": "(indentSize() / 2) + 'px'", "attr.aria-label": "\"Tree navigation\"" }, classAttribute: "ax-tree-view" }, providers: [AXTreeViewService], ngImport: i0, template: "<!-- Root drop list -->\n<div\n axFocusTrap\n [axDropList]=\"dragMode() !== 'none'\"\n [sortingDisabled]=\"false\"\n [id]=\"getListId()\"\n [attr.data-node-id]=\"null\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDrop($event)\"\n class=\"ax-tree-view-drop-list\"\n [class.ax-tree-view-card]=\"look() === 'card'\"\n [class.ax-tree-view-with-lines]=\"look() === 'with-line'\"\n [class.ax-tree-view-compact]=\"nodeHeight() === 'compact'\"\n [class.ax-tree-view-comfortable]=\"nodeHeight() === 'comfortable'\"\n role=\"group\"\n>\n @for (node of nodes(); track node.id) {\n @if (node.visible !== false) {\n <div\n [axDrag]=\"dragMode() !== 'none'\"\n [dragDisabled]=\"node.disabled\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-selected]=\"node.selected\"\n [class.ax-tree-view-node-disabled]=\"node.disabled\"\n [class.ax-tree-view-node-loading]=\"node.loading\"\n role=\"treeitem\"\n [attr.aria-level]=\"getNodeAriaLevel(0)\"\n [attr.aria-expanded]=\"getNodeAriaExpanded(node)\"\n [attr.aria-selected]=\"getNodeAriaSelected(node)\"\n [attr.aria-disabled]=\"node.disabled ? 'true' : null\"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [axDropList]=\"dragMode() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + node.id\"\n [attr.data-node-id]=\"node.id\"\n [attr.data-tree-node-id]=\"node.id\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"\n (selectMode() === 'single' || (selectMode() === 'multiple' && checkOnClick())) && onNodeClick(node, $event)\n \"\n (focus)=\"onNodeFocus(node.id)\"\n [tabindex]=\"isNodeFocused(node.id) ? 0 : -1\"\n >\n @if (dragMode() === 'handler') {\n <span class=\"ax-tree-view-drag-handle\" axDragHandle title=\"Drag to reorder\"> \u22EE\u22EE </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"node.expanded\"\n [disabled]=\"node.disabled || node.loading\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (node.loading) {\n <ax-icon>\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-loading-spinner\"></i>\n </ax-icon>\n } @else {\n <ax-icon>\n <i\n [class]=\"node.expanded ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n @if (itemTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"itemTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, 0)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckbox()) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"node.indeterminate ? null : node.selected || false\"\n [indeterminate]=\"node.indeterminate || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && node.icon) {\n <i [class]=\"node.icon\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span class=\"ax-tree-view-node-label\">{{ node.label }}</span>\n @if (showChildrenBadge() && (node.childrenCount || (node.children && node.children.length > 0))) {\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(node.childrenCount ?? node.children?.length ?? 0).toString()\"\n ></ax-badge>\n }\n }\n </div>\n </div>\n @if (node.expanded && node.children && node.children.length > 0) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: node.children, parent: node, level: 1 }\"\n ></ng-container>\n </div>\n }\n }\n }\n</div>\n\n<!-- Recursive children template -->\n<ng-template #childrenList let-children=\"children\" let-parent=\"parent\" let-level=\"level\">\n <div\n [axDropList]=\"dragMode() !== 'none'\"\n [id]=\"getListId(parent)\"\n [attr.data-node-id]=\"parent.id\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDrop($event, parent)\"\n class=\"ax-tree-view-drop-list\"\n role=\"group\"\n >\n @for (node of children; track node.id) {\n @if (node.visible !== false) {\n <div\n [axDrag]=\"dragMode() !== 'none'\"\n [dragDisabled]=\"node.disabled\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-selected]=\"node.selected\"\n [class.ax-tree-view-node-disabled]=\"node.disabled\"\n [class.ax-tree-view-node-loading]=\"node.loading\"\n [class.ax-tree-view-node-focused]=\"isNodeFocused(node.id)\"\n role=\"treeitem\"\n [attr.aria-level]=\"getNodeAriaLevel(level)\"\n [attr.aria-expanded]=\"getNodeAriaExpanded(node)\"\n [attr.aria-selected]=\"getNodeAriaSelected(node)\"\n [attr.aria-disabled]=\"node.disabled ? 'true' : null\"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [style.padding-inline-start.px]=\"getNodePaddingInline(level)\"\n [axDropList]=\"dragMode() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + node.id\"\n [attr.data-node-id]=\"node.id\"\n [attr.data-tree-node-id]=\"node.id\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"\n (selectMode() === 'single' || (selectMode() === 'multiple' && checkOnClick())) &&\n onNodeClick(node, $event)\n \"\n (focus)=\"onNodeFocus(node.id)\"\n (blur)=\"focusedNodeId.set(null)\"\n [tabindex]=\"isNodeFocused(node.id) ? 0 : -1\"\n >\n @if (dragMode() === 'handler') {\n <span class=\"ax-tree-view-drag-handle\" axDragHandle title=\"Drag to reorder\"> \u22EE\u22EE </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"node.expanded\"\n [disabled]=\"node.disabled || node.loading\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (node.loading) {\n <ax-icon>\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-loading-spinner\"></i>\n </ax-icon>\n } @else {\n <ax-icon>\n <i\n [class]=\"node.expanded ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n\n @if (itemTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"itemTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, level)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckbox()) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"node.indeterminate ? null : node.selected || false\"\n [indeterminate]=\"node.indeterminate || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && node.icon) {\n <i [class]=\"node.icon\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span class=\"ax-tree-view-node-label\">{{ node.label }}</span>\n @if (showChildrenBadge() && (node.childrenCount || (node.children && node.children.length > 0))) {\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(node.childrenCount ?? node.children?.length ?? 0).toString()\"\n ></ax-badge>\n }\n }\n </div>\n </div>\n @if (node.expanded && node.children && node.children.length > 0) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: node.children, parent: node, level: level + 1 }\"\n ></ng-container>\n </div>\n }\n }\n }\n </div>\n</ng-template>\n", styles: [".ax-tree-view{display:block;width:100%;--ax-comp-tree-view-indent-size: 12px;--ax-comp-tree-view-node-hover-bg: rgba(var(--ax-sys-color-on-lightest-surface), .04);--ax-comp-tree-view-node-selected-bg: rgba(var(--ax-sys-color-primary-500), .12);--ax-comp-tree-view-node-border-radius: 6px;--ax-comp-tree-view-node-margin: .25rem;--ax-comp-tree-view-line-color: rgba(var(--ax-sys-color-on-lightest-surface), .15);--ax-comp-tree-view-drag-preview-opacity: .9;--ax-comp-tree-view-drag-placeholder-bg: rgba(var(--ax-sys-color-on-lightest-surface), .02);--ax-comp-tree-view-drop-active-bg: rgba(var(--ax-sys-color-primary-500), .08);--ax-comp-tree-view-drop-active-outline: rgba(var(--ax-sys-color-primary-500), .3)}.ax-tree-view-drop-list{min-height:2rem}.ax-tree-view-compact .ax-tree-view-node-content{padding:.25rem .5rem;gap:.375rem;font-size:.8125rem}.ax-tree-view-comfortable .ax-tree-view-node-content{padding:.75rem .625rem;gap:.625rem;font-size:.9375rem}.ax-tree-view-node{position:relative;margin:var(--ax-comp-tree-view-node-margin) 0;border-radius:var(--ax-comp-tree-view-node-border-radius);border:1px solid transparent;cursor:move}.ax-tree-view-node:hover:not(.ax-dragging){background:var(--ax-comp-tree-view-node-hover-bg)}.ax-tree-view-node.ax-tree-view-node-selected{background:var(--ax-comp-tree-view-node-selected-bg);border-color:currentColor}.ax-tree-view-node.ax-dragging{opacity:var(--ax-comp-tree-view-drag-placeholder-opacity);cursor:grabbing!important}.ax-tree-view-node.ax-drag-placeholder{background:var(--ax-comp-tree-view-drag-placeholder-bg)}.ax-drag-preview{opacity:var(--ax-comp-tree-view-drag-preview-opacity)!important;box-shadow:0 4px 12px rgba(var(--ax-sys-color-on-lightest-surface),.2)!important;cursor:grabbing!important;border:2px dashed currentColor!important}.ax-tree-view-node-content.ax-drop-list-sorting-active{background:var(--ax-comp-tree-view-drop-active-bg);border-radius:var(--ax-comp-tree-view-node-border-radius);outline:2px dashed var(--ax-comp-tree-view-drop-active-outline);outline-offset:-2px}.ax-tree-view-node-content{display:flex;align-items:center;gap:.5rem;padding:.5rem;cursor:pointer;-webkit-user-select:none;user-select:none;outline:none;border:1px solid transparent;border-radius:var(--ax-comp-tree-view-node-border-radius)}.ax-tree-view-node-content:focus{outline:none}.ax-tree-view-node-content:focus-visible{outline:2px solid rgba(var(--ax-sys-color-primary-500),.8);outline-offset:2px;border-radius:var(--ax-comp-tree-view-node-border-radius)}.ax-tree-view-drag-handle{cursor:grab;opacity:.6;padding:.25rem}.ax-tree-view-drag-handle:hover{opacity:1}.ax-tree-view-drag-handle:active{cursor:grabbing}.ax-tree-view-expand-toggle{background:none;border:none;cursor:pointer;padding:.25rem;min-width:1.5rem;height:1.5rem}.ax-tree-view-expand-toggle:not(.ax-tree-view-has-children){opacity:0;pointer-events:none}.ax-tree-view-toggle-icon{font-size:.75rem}.ax-tree-view-node-icon{font-size:1.125rem;flex-shrink:0}.ax-tree-view-node-label{flex:1;font-size:.875rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ax-tree-view-children{padding-inline-start:var(--ax-tree-view-indent-size, var(--ax-comp-tree-view-indent-size))}.ax-tree-view-node-disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.ax-tree-view-node-loading{opacity:.7}.ax-tree-view-card .ax-tree-view-node{border:1px solid rgba(var(--ax-sys-color-border-lightest-surface),1);margin:.5rem 0}.ax-tree-view-card .ax-tree-view-node-content{padding:1rem}.ax-tree-view-with-lines .ax-tree-view-children{position:relative;padding-inline-start:var(--ax-tree-view-indent-size, var(--ax-comp-tree-view-indent-size))}.ax-tree-view-with-lines .ax-tree-view-children:before{content:\"\";position:absolute;inset-inline-start:var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2));top:0;height:calc(100% - .875rem);width:1px;background:var(--ax-tree-view-line-color, var(--ax-comp-tree-view-line-color))}.ax-tree-view-with-lines .ax-tree-view-node{position:relative}.ax-tree-view-with-lines .ax-tree-view-node:before{content:\"\";position:absolute;inset-inline-start:calc(-1 * var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2)));top:60%;width:var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2));height:1px;background:var(--ax-tree-view-line-color, var(--ax-comp-tree-view-line-color))}.ax-tree-view-with-lines>.ax-tree-view-drop-list>.ax-tree-view-node:before,.ax-tree-view-with-lines>.ax-tree-view-node:before{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: AXDragDirective, selector: "[axDrag]", inputs: ["axDrag", "dragData", "dragDisabled", "dragTransition", "dragElementClone", "dropZoneGroup", "dragStartDelay", "dragResetOnDblClick", "dragLockAxis", "dragClonedTemplate", "dragCursor", "dragBoundary", "dragTransitionDuration"], outputs: ["dragPositionChanged"] }, { kind: "directive", type: AXDragHandleDirective, selector: "[axDragHandle]" }, { kind: "directive", type: AXDropListDirective, selector: "[axDropList]", inputs: ["axDropList", "sortingDisabled", "dropListGroup", "dropListOrientation"], outputs: ["dropListDropped"], exportAs: ["axDropList"] }, { kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: AXCheckBoxComponent, selector: "ax-check-box", inputs: ["disabled", "tabIndex", "readonly", "color", "value", "name", "id", "isLoading", "indeterminate"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged"] }, { kind: "component", type: AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
2141
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: AXTreeViewComponent, isStandalone: true, selector: "ax-tree-view", inputs: { datasource: { classPropertyName: "datasource", publicName: "datasource", isSignal: true, isRequired: true, transformFunction: null }, selectMode: { classPropertyName: "selectMode", publicName: "selectMode", isSignal: true, isRequired: false, transformFunction: null }, showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, selectionBehavior: { classPropertyName: "selectionBehavior", publicName: "selectionBehavior", isSignal: true, isRequired: false, transformFunction: null }, dragArea: { classPropertyName: "dragArea", publicName: "dragArea", isSignal: true, isRequired: false, transformFunction: null }, dragBehavior: { classPropertyName: "dragBehavior", publicName: "dragBehavior", isSignal: true, isRequired: false, transformFunction: null }, showIcons: { classPropertyName: "showIcons", publicName: "showIcons", isSignal: true, isRequired: false, transformFunction: null }, showChildrenBadge: { classPropertyName: "showChildrenBadge", publicName: "showChildrenBadge", isSignal: true, isRequired: false, transformFunction: null }, expandedIcon: { classPropertyName: "expandedIcon", publicName: "expandedIcon", isSignal: true, isRequired: false, transformFunction: null }, collapsedIcon: { classPropertyName: "collapsedIcon", publicName: "collapsedIcon", isSignal: true, isRequired: false, transformFunction: null }, indentSize: { classPropertyName: "indentSize", publicName: "indentSize", isSignal: true, isRequired: false, transformFunction: null }, look: { classPropertyName: "look", publicName: "look", isSignal: true, isRequired: false, transformFunction: null }, nodeTemplate: { classPropertyName: "nodeTemplate", publicName: "nodeTemplate", isSignal: true, isRequired: false, transformFunction: null }, idField: { classPropertyName: "idField", publicName: "idField", isSignal: true, isRequired: false, transformFunction: null }, titleField: { classPropertyName: "titleField", publicName: "titleField", isSignal: true, isRequired: false, transformFunction: null }, tooltipField: { classPropertyName: "tooltipField", publicName: "tooltipField", isSignal: true, isRequired: false, transformFunction: null }, iconField: { classPropertyName: "iconField", publicName: "iconField", isSignal: true, isRequired: false, transformFunction: null }, expandedField: { classPropertyName: "expandedField", publicName: "expandedField", isSignal: true, isRequired: false, transformFunction: null }, selectedField: { classPropertyName: "selectedField", publicName: "selectedField", isSignal: true, isRequired: false, transformFunction: null }, indeterminateField: { classPropertyName: "indeterminateField", publicName: "indeterminateField", isSignal: true, isRequired: false, transformFunction: null }, disabledField: { classPropertyName: "disabledField", publicName: "disabledField", isSignal: true, isRequired: false, transformFunction: null }, hiddenField: { classPropertyName: "hiddenField", publicName: "hiddenField", isSignal: true, isRequired: false, transformFunction: null }, childrenField: { classPropertyName: "childrenField", publicName: "childrenField", isSignal: true, isRequired: false, transformFunction: null }, childrenCountField: { classPropertyName: "childrenCountField", publicName: "childrenCountField", isSignal: true, isRequired: false, transformFunction: null }, dataField: { classPropertyName: "dataField", publicName: "dataField", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { datasource: "datasourceChange", onBeforeDrop: "onBeforeDrop", onNodeToggle: "onNodeToggle", onNodeSelect: "onNodeSelect", onSelectionChange: "onSelectionChange", onOrderChange: "onOrderChange", onMoveChange: "onMoveChange", onItemsChange: "onItemsChange" }, host: { attributes: { "role": "tree", "tabindex": "0" }, listeners: { "keydown": "handleKeyDown($event)", "focus": "onTreeFocus($event)", "blur": "onTreeBlur($event)" }, properties: { "class.ax-tree-view-default": "look() === 'default'", "class.ax-tree-view-card": "look() === 'card'", "class.ax-tree-view-with-line": "look() === 'with-line'", "class.ax-tree-view-rtl": "isRtl", "style.--ax-tree-view-indent-size": "indentSize() + 'px'", "style.--ax-tree-view-line-offset": "(indentSize() / 2) + 'px'", "attr.aria-label": "\"Tree navigation\"" }, classAttribute: "ax-tree-view" }, providers: [AXTreeViewService], ngImport: i0, template: "<!-- Root drop list -->\n<div\n axFocusTrap\n [axDropList]=\"dragBehavior() !== 'none'\"\n [sortingDisabled]=\"false\"\n [id]=\"getListId()\"\n [attr.data-node-id]=\"null\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDrop($event)\"\n class=\"ax-tree-view-drop-list\"\n [class.ax-tree-view-card]=\"look() === 'card'\"\n [class.ax-tree-view-with-lines]=\"look() === 'with-line'\"\n role=\"group\"\n>\n @for (node of nodes(); track getNodeId(node)) {\n @if (getNodeHidden(node) !== true) {\n <div\n [axDrag]=\"dragBehavior() !== 'none'\"\n [dragDisabled]=\"getNodeDisabled(node)\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-selected]=\"getNodeSelected(node)\"\n [class.ax-tree-view-node-disabled]=\"getNodeDisabled(node)\"\n [class.ax-tree-view-node-loading]=\"getNodeLoading(node)\"\n role=\"treeitem\"\n [attr.aria-level]=\"getNodeAriaLevel(0)\"\n [attr.aria-expanded]=\"getNodeAriaExpanded(node)\"\n [attr.aria-selected]=\"getNodeAriaSelected(node)\"\n [attr.aria-disabled]=\"getNodeDisabled(node) || (isLeafOnlyMode() && !isLeafNode(node)) ? 'true' : null\"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [axDropList]=\"dragBehavior() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + getNodeId(node)\"\n [attr.data-node-id]=\"getNodeId(node)\"\n [attr.data-tree-node-id]=\"getNodeId(node)\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"\n (selectMode() === 'single' || (selectMode() === 'multiple' && !showCheckbox())) && onNodeClick(node, $event)\n \"\n (focus)=\"onNodeFocus(getNodeId(node))\"\n [tabindex]=\"isNodeFocused(getNodeId(node)) ? 0 : -1\"\n >\n @if (dragArea() === 'handler') {\n <span class=\"ax-tree-view-drag-handle\" axDragHandle title=\"Drag to reorder\"> \u22EE\u22EE </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"getNodeExpanded(node)\"\n [disabled]=\"getNodeDisabled(node) || getNodeLoading(node)\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (getNodeLoading(node)) {\n <ax-icon>\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-loading-spinner\"></i>\n </ax-icon>\n } @else {\n <ax-icon>\n <i\n [class]=\"getNodeExpanded(node) ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n @if (nodeTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, 0)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckboxForNode(node)) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"getNodeIndeterminate(node) ? null : getNodeSelected(node) || false\"\n [indeterminate]=\"getNodeIndeterminate(node) || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && getNodeIcon(node)) {\n <i [class]=\"getNodeIcon(node)\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span\n class=\"ax-tree-view-node-label\"\n [axTooltip]=\"getNodeTooltip(node) || ''\"\n [axTooltipDisabled]=\"!getNodeTooltip(node)\"\n [axTooltipPlacement]=\"'top-start'\"\n >\n {{ getNodeTitle(node) }}\n </span>\n @if (\n showChildrenBadge() &&\n (getNodeChildrenCount(node) || (getNodeChildren(node) && getNodeChildren(node)!.length > 0))\n ) {\n <span class=\"ax-tree-view-node-badge\">\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(getNodeChildrenCount(node) ?? getNodeChildren(node)?.length ?? 0).toString()\"\n ></ax-badge>\n </span>\n }\n }\n </div>\n </div>\n @if (getNodeExpanded(node) && getNodeChildren(node) && getNodeChildren(node)!.length > 0) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: getNodeChildren(node), parent: node, level: 1 }\"\n ></ng-container>\n </div>\n }\n }\n }\n</div>\n\n<!-- Recursive children template -->\n<ng-template #childrenList let-children=\"children\" let-parent=\"parent\" let-level=\"level\">\n <div\n [axDropList]=\"dragBehavior() !== 'none'\"\n [id]=\"getListId(parent)\"\n [attr.data-node-id]=\"getNodeId(parent)\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDrop($event, parent)\"\n class=\"ax-tree-view-drop-list\"\n role=\"group\"\n >\n @for (node of children; track getNodeId(node)) {\n @if (getNodeHidden(node) !== true) {\n <div\n [axDrag]=\"dragBehavior() !== 'none'\"\n [dragDisabled]=\"getNodeDisabled(node)\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-selected]=\"getNodeSelected(node)\"\n [class.ax-tree-view-node-disabled]=\"getNodeDisabled(node)\"\n [class.ax-tree-view-node-loading]=\"getNodeLoading(node)\"\n [class.ax-tree-view-node-focused]=\"isNodeFocused(getNodeId(node))\"\n role=\"treeitem\"\n [attr.aria-level]=\"getNodeAriaLevel(level)\"\n [attr.aria-expanded]=\"getNodeAriaExpanded(node)\"\n [attr.aria-selected]=\"getNodeAriaSelected(node)\"\n [attr.aria-disabled]=\"getNodeDisabled(node) ? 'true' : null\"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [axDropList]=\"dragBehavior() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + getNodeId(node)\"\n [attr.data-node-id]=\"getNodeId(node)\"\n [attr.data-tree-node-id]=\"getNodeId(node)\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"\n (selectMode() === 'single' || (selectMode() === 'multiple' && !showCheckbox())) &&\n onNodeClick(node, $event)\n \"\n (focus)=\"onNodeFocus(getNodeId(node))\"\n (blur)=\"focusedNodeId.set(null)\"\n [tabindex]=\"isNodeFocused(getNodeId(node)) ? 0 : -1\"\n >\n @if (dragArea() === 'handler') {\n <span class=\"ax-tree-view-drag-handle\" axDragHandle title=\"Drag to reorder\"> \u22EE\u22EE </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"getNodeExpanded(node)\"\n [disabled]=\"getNodeDisabled(node) || getNodeLoading(node)\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (getNodeLoading(node)) {\n <ax-icon>\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-loading-spinner\"></i>\n </ax-icon>\n } @else {\n <ax-icon>\n <i\n [class]=\"getNodeExpanded(node) ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n\n @if (nodeTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, level)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckboxForNode(node)) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"getNodeIndeterminate(node) ? null : getNodeSelected(node) || false\"\n [indeterminate]=\"getNodeIndeterminate(node) || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && getNodeIcon(node)) {\n <i [class]=\"getNodeIcon(node)\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span\n class=\"ax-tree-view-node-label\"\n [axTooltip]=\"getNodeTooltip(node) || ''\"\n [axTooltipDisabled]=\"!getNodeTooltip(node)\"\n [axTooltipPlacement]=\"'top-start'\"\n >\n {{ getNodeTitle(node) }}\n </span>\n @if (\n showChildrenBadge() &&\n (getNodeChildrenCount(node) || (getNodeChildren(node) && getNodeChildren(node)!.length > 0))\n ) {\n <span class=\"ax-tree-view-node-badge\">\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(getNodeChildrenCount(node) ?? getNodeChildren(node)?.length ?? 0).toString()\"\n ></ax-badge>\n </span>\n }\n }\n </div>\n </div>\n @if (getNodeExpanded(node) && getNodeChildren(node) && getNodeChildren(node)!.length > 0) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: getNodeChildren(node), parent: node, level: level + 1 }\"\n ></ng-container>\n </div>\n }\n }\n }\n </div>\n</ng-template>\n", styles: [".ax-tree-view{display:block;width:100%;--ax-comp-tree-view-indent-size: 12px;--ax-comp-tree-view-node-hover-bg: rgba(var(--ax-sys-color-on-lightest-surface), .04);--ax-comp-tree-view-node-selected-bg: rgba(var(--ax-sys-color-primary-500), .12);--ax-comp-tree-view-node-border-radius: 6px;--ax-comp-tree-view-node-margin: .25rem;--ax-comp-tree-view-line-color: rgba(var(--ax-sys-color-on-lightest-surface), .15);--ax-comp-tree-view-drag-preview-opacity: .9;--ax-comp-tree-view-drag-placeholder-bg: rgba(var(--ax-sys-color-on-lightest-surface), .02);--ax-comp-tree-view-drop-active-bg: rgba(var(--ax-sys-color-primary-500), .08);--ax-comp-tree-view-drop-active-outline: rgba(var(--ax-sys-color-primary-500), .3);--ax-comp-tree-view-content-padding: 0;--ax-comp-tree-view-content-gap: .5rem;--ax-comp-tree-view-drop-list-min-height: 2rem;--ax-comp-tree-view-drag-handle-padding: .25rem;--ax-comp-tree-view-badge-padding: .25rem;--ax-comp-tree-view-expand-toggle-padding: .25rem;--ax-comp-tree-view-outline-offset: 2px;--ax-comp-tree-view-outline-offset-negative: -2px}.ax-tree-view-drop-list{min-height:var(--ax-comp-tree-view-drop-list-min-height)}.ax-tree-view-node{position:relative;margin:var(--ax-comp-tree-view-node-margin) 0;border-radius:var(--ax-comp-tree-view-node-border-radius);cursor:move}.ax-tree-view-node:hover:not(.ax-dragging){background:var(--ax-comp-tree-view-node-hover-bg)}.ax-tree-view-node.ax-tree-view-node-selected{background:var(--ax-comp-tree-view-node-selected-bg)}.ax-tree-view-node.ax-dragging{opacity:var(--ax-comp-tree-view-drag-placeholder-opacity);cursor:grabbing!important}.ax-tree-view-node.ax-drag-placeholder{background:var(--ax-comp-tree-view-drag-placeholder-bg)}.ax-drag-preview{opacity:var(--ax-comp-tree-view-drag-preview-opacity)!important;box-shadow:0 4px 12px rgba(var(--ax-sys-color-on-lightest-surface),.2)!important;cursor:grabbing!important;border:2px dashed currentColor!important}.ax-tree-view-node-content.ax-drop-list-sorting-active{background:var(--ax-comp-tree-view-drop-active-bg);border-radius:var(--ax-comp-tree-view-node-border-radius);outline:2px dashed var(--ax-comp-tree-view-drop-active-outline);outline-offset:var(--ax-comp-tree-view-outline-offset-negative)}.ax-tree-view-node-content{display:flex;align-items:center;gap:var(--ax-comp-tree-view-content-gap);padding:var(--ax-comp-tree-view-content-padding);cursor:pointer;-webkit-user-select:none;user-select:none;outline:none;border-radius:var(--ax-comp-tree-view-node-border-radius)}.ax-tree-view-node-content:focus{outline:none}.ax-tree-view-node-content:focus-visible{outline:2px solid rgba(var(--ax-sys-color-primary-500),.8);outline-offset:var(--ax-comp-tree-view-outline-offset);border-radius:var(--ax-comp-tree-view-node-border-radius)}.ax-tree-view-drag-handle{cursor:grab;opacity:.6;padding:var(--ax-comp-tree-view-drag-handle-padding);padding-inline-start:calc(var(--ax-comp-tree-view-drag-handle-padding) * 2)}.ax-tree-view-drag-handle:hover{opacity:1}.ax-tree-view-drag-handle:active{cursor:grabbing}.ax-tree-view-expand-toggle{background:none;border:none;cursor:pointer;padding:var(--ax-comp-tree-view-expand-toggle-padding);min-width:1.5rem;height:1.5rem}.ax-tree-view-expand-toggle:not(.ax-tree-view-has-children){opacity:0;pointer-events:none}.ax-tree-view-toggle-icon{font-size:.75rem}.ax-tree-view-node-icon{font-size:1.125rem;flex-shrink:0}.ax-tree-view-node-label{flex:1;font-size:.875rem;line-height:1rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ax-tree-view-node-badge{padding:var(--ax-comp-tree-view-badge-padding);padding-inline-end:calc(var(--ax-comp-tree-view-badge-padding) * 1.5)}.ax-tree-view-children{padding-inline-start:var(--ax-tree-view-indent-size, var(--ax-comp-tree-view-indent-size))}.ax-tree-view-node-disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.ax-tree-view-node-loading{opacity:.7}.ax-tree-view-card .ax-tree-view-node{border:1px solid rgba(var(--ax-sys-color-border-lightest-surface),1)}.ax-tree-view-card .ax-tree-view-node.ax-tree-view-node-selected{border:1px solid rgba(var(--ax-sys-color-border-surface),1)}.ax-tree-view-with-lines .ax-tree-view-children{position:relative;padding-inline-start:var(--ax-tree-view-indent-size, var(--ax-comp-tree-view-indent-size))}.ax-tree-view-with-lines .ax-tree-view-children:before{content:\"\";position:absolute;inset-inline-start:var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2));top:0;height:calc(100% - .875rem);width:1px;background:var(--ax-tree-view-line-color, var(--ax-comp-tree-view-line-color))}.ax-tree-view-with-lines .ax-tree-view-node{position:relative}.ax-tree-view-with-lines .ax-tree-view-node:before{content:\"\";position:absolute;inset-inline-start:calc(-1 * var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2)));top:60%;width:var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2));height:1px;background:var(--ax-tree-view-line-color, var(--ax-comp-tree-view-line-color))}.ax-tree-view-with-lines>.ax-tree-view-drop-list>.ax-tree-view-node:before,.ax-tree-view-with-lines>.ax-tree-view-node:before{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: AXDragDirective, selector: "[axDrag]", inputs: ["axDrag", "dragData", "dragDisabled", "dragTransition", "dragElementClone", "dropZoneGroup", "dragStartDelay", "dragResetOnDblClick", "dragLockAxis", "dragClonedTemplate", "dragCursor", "dragBoundary", "dragTransitionDuration"], outputs: ["dragPositionChanged"] }, { kind: "directive", type: AXDragHandleDirective, selector: "[axDragHandle]" }, { kind: "directive", type: AXDropListDirective, selector: "[axDropList]", inputs: ["axDropList", "sortingDisabled", "dropListGroup", "dropListOrientation"], outputs: ["dropListDropped"], exportAs: ["axDropList"] }, { kind: "directive", type: AXFocusTrapDirective, selector: "[axFocusTrap]" }, { kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: AXCheckBoxComponent, selector: "ax-check-box", inputs: ["disabled", "tabIndex", "readonly", "color", "value", "name", "id", "isLoading", "indeterminate"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged"] }, { kind: "component", type: AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "component", type: AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "directive", type: AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
1169
2142
|
}
|
|
1170
2143
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewComponent, decorators: [{
|
|
1171
2144
|
type: Component,
|
|
@@ -1175,27 +2148,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
|
|
|
1175
2148
|
AXDragDirective,
|
|
1176
2149
|
AXDragHandleDirective,
|
|
1177
2150
|
AXDropListDirective,
|
|
2151
|
+
AXFocusTrapDirective,
|
|
1178
2152
|
NgTemplateOutlet,
|
|
1179
2153
|
AXButtonComponent,
|
|
1180
2154
|
AXCheckBoxComponent,
|
|
1181
2155
|
AXBadgeComponent,
|
|
1182
2156
|
AXDecoratorIconComponent,
|
|
2157
|
+
AXTooltipDirective,
|
|
1183
2158
|
], host: {
|
|
1184
2159
|
class: 'ax-tree-view',
|
|
2160
|
+
role: 'tree',
|
|
2161
|
+
tabindex: '0',
|
|
1185
2162
|
'[class.ax-tree-view-default]': "look() === 'default'",
|
|
1186
2163
|
'[class.ax-tree-view-card]': "look() === 'card'",
|
|
1187
2164
|
'[class.ax-tree-view-with-line]': "look() === 'with-line'",
|
|
1188
2165
|
'[class.ax-tree-view-rtl]': 'isRtl',
|
|
1189
2166
|
'[style.--ax-tree-view-indent-size]': "indentSize() + 'px'",
|
|
1190
2167
|
'[style.--ax-tree-view-line-offset]': "(indentSize() / 2) + 'px'",
|
|
1191
|
-
role: 'tree',
|
|
1192
2168
|
'[attr.aria-label]': '"Tree navigation"',
|
|
1193
2169
|
'(keydown)': 'handleKeyDown($event)',
|
|
1194
2170
|
'(focus)': 'onTreeFocus($event)',
|
|
1195
2171
|
'(blur)': 'onTreeBlur($event)',
|
|
1196
|
-
tabindex: '0',
|
|
1197
|
-
}, template: "<!-- Root drop list -->\n<div\n axFocusTrap\n [axDropList]=\"dragMode() !== 'none'\"\n [sortingDisabled]=\"false\"\n [id]=\"getListId()\"\n [attr.data-node-id]=\"null\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDrop($event)\"\n class=\"ax-tree-view-drop-list\"\n [class.ax-tree-view-card]=\"look() === 'card'\"\n [class.ax-tree-view-with-lines]=\"look() === 'with-line'\"\n [class.ax-tree-view-compact]=\"nodeHeight() === 'compact'\"\n [class.ax-tree-view-comfortable]=\"nodeHeight() === 'comfortable'\"\n role=\"group\"\n>\n @for (node of nodes(); track node.id) {\n @if (node.visible !== false) {\n <div\n [axDrag]=\"dragMode() !== 'none'\"\n [dragDisabled]=\"node.disabled\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-selected]=\"node.selected\"\n [class.ax-tree-view-node-disabled]=\"node.disabled\"\n [class.ax-tree-view-node-loading]=\"node.loading\"\n role=\"treeitem\"\n [attr.aria-level]=\"getNodeAriaLevel(0)\"\n [attr.aria-expanded]=\"getNodeAriaExpanded(node)\"\n [attr.aria-selected]=\"getNodeAriaSelected(node)\"\n [attr.aria-disabled]=\"node.disabled ? 'true' : null\"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [axDropList]=\"dragMode() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + node.id\"\n [attr.data-node-id]=\"node.id\"\n [attr.data-tree-node-id]=\"node.id\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"\n (selectMode() === 'single' || (selectMode() === 'multiple' && checkOnClick())) && onNodeClick(node, $event)\n \"\n (focus)=\"onNodeFocus(node.id)\"\n [tabindex]=\"isNodeFocused(node.id) ? 0 : -1\"\n >\n @if (dragMode() === 'handler') {\n <span class=\"ax-tree-view-drag-handle\" axDragHandle title=\"Drag to reorder\"> \u22EE\u22EE </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"node.expanded\"\n [disabled]=\"node.disabled || node.loading\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (node.loading) {\n <ax-icon>\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-loading-spinner\"></i>\n </ax-icon>\n } @else {\n <ax-icon>\n <i\n [class]=\"node.expanded ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n @if (itemTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"itemTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, 0)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckbox()) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"node.indeterminate ? null : node.selected || false\"\n [indeterminate]=\"node.indeterminate || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && node.icon) {\n <i [class]=\"node.icon\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span class=\"ax-tree-view-node-label\">{{ node.label }}</span>\n @if (showChildrenBadge() && (node.childrenCount || (node.children && node.children.length > 0))) {\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(node.childrenCount ?? node.children?.length ?? 0).toString()\"\n ></ax-badge>\n }\n }\n </div>\n </div>\n @if (node.expanded && node.children && node.children.length > 0) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: node.children, parent: node, level: 1 }\"\n ></ng-container>\n </div>\n }\n }\n }\n</div>\n\n<!-- Recursive children template -->\n<ng-template #childrenList let-children=\"children\" let-parent=\"parent\" let-level=\"level\">\n <div\n [axDropList]=\"dragMode() !== 'none'\"\n [id]=\"getListId(parent)\"\n [attr.data-node-id]=\"parent.id\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDrop($event, parent)\"\n class=\"ax-tree-view-drop-list\"\n role=\"group\"\n >\n @for (node of children; track node.id) {\n @if (node.visible !== false) {\n <div\n [axDrag]=\"dragMode() !== 'none'\"\n [dragDisabled]=\"node.disabled\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-selected]=\"node.selected\"\n [class.ax-tree-view-node-disabled]=\"node.disabled\"\n [class.ax-tree-view-node-loading]=\"node.loading\"\n [class.ax-tree-view-node-focused]=\"isNodeFocused(node.id)\"\n role=\"treeitem\"\n [attr.aria-level]=\"getNodeAriaLevel(level)\"\n [attr.aria-expanded]=\"getNodeAriaExpanded(node)\"\n [attr.aria-selected]=\"getNodeAriaSelected(node)\"\n [attr.aria-disabled]=\"node.disabled ? 'true' : null\"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [style.padding-inline-start.px]=\"getNodePaddingInline(level)\"\n [axDropList]=\"dragMode() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + node.id\"\n [attr.data-node-id]=\"node.id\"\n [attr.data-tree-node-id]=\"node.id\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"\n (selectMode() === 'single' || (selectMode() === 'multiple' && checkOnClick())) &&\n onNodeClick(node, $event)\n \"\n (focus)=\"onNodeFocus(node.id)\"\n (blur)=\"focusedNodeId.set(null)\"\n [tabindex]=\"isNodeFocused(node.id) ? 0 : -1\"\n >\n @if (dragMode() === 'handler') {\n <span class=\"ax-tree-view-drag-handle\" axDragHandle title=\"Drag to reorder\"> \u22EE\u22EE </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"node.expanded\"\n [disabled]=\"node.disabled || node.loading\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (node.loading) {\n <ax-icon>\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-loading-spinner\"></i>\n </ax-icon>\n } @else {\n <ax-icon>\n <i\n [class]=\"node.expanded ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n\n @if (itemTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"itemTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, level)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckbox()) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"node.indeterminate ? null : node.selected || false\"\n [indeterminate]=\"node.indeterminate || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && node.icon) {\n <i [class]=\"node.icon\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span class=\"ax-tree-view-node-label\">{{ node.label }}</span>\n @if (showChildrenBadge() && (node.childrenCount || (node.children && node.children.length > 0))) {\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(node.childrenCount ?? node.children?.length ?? 0).toString()\"\n ></ax-badge>\n }\n }\n </div>\n </div>\n @if (node.expanded && node.children && node.children.length > 0) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: node.children, parent: node, level: level + 1 }\"\n ></ng-container>\n </div>\n }\n }\n }\n </div>\n</ng-template>\n", styles: [".ax-tree-view{display:block;width:100%;--ax-comp-tree-view-indent-size: 12px;--ax-comp-tree-view-node-hover-bg: rgba(var(--ax-sys-color-on-lightest-surface), .04);--ax-comp-tree-view-node-selected-bg: rgba(var(--ax-sys-color-primary-500), .12);--ax-comp-tree-view-node-border-radius: 6px;--ax-comp-tree-view-node-margin: .25rem;--ax-comp-tree-view-line-color: rgba(var(--ax-sys-color-on-lightest-surface), .15);--ax-comp-tree-view-drag-preview-opacity: .9;--ax-comp-tree-view-drag-placeholder-bg: rgba(var(--ax-sys-color-on-lightest-surface), .02);--ax-comp-tree-view-drop-active-bg: rgba(var(--ax-sys-color-primary-500), .08);--ax-comp-tree-view-drop-active-outline: rgba(var(--ax-sys-color-primary-500), .3)}.ax-tree-view-drop-list{min-height:2rem}.ax-tree-view-compact .ax-tree-view-node-content{padding:.25rem .5rem;gap:.375rem;font-size:.8125rem}.ax-tree-view-comfortable .ax-tree-view-node-content{padding:.75rem .625rem;gap:.625rem;font-size:.9375rem}.ax-tree-view-node{position:relative;margin:var(--ax-comp-tree-view-node-margin) 0;border-radius:var(--ax-comp-tree-view-node-border-radius);border:1px solid transparent;cursor:move}.ax-tree-view-node:hover:not(.ax-dragging){background:var(--ax-comp-tree-view-node-hover-bg)}.ax-tree-view-node.ax-tree-view-node-selected{background:var(--ax-comp-tree-view-node-selected-bg);border-color:currentColor}.ax-tree-view-node.ax-dragging{opacity:var(--ax-comp-tree-view-drag-placeholder-opacity);cursor:grabbing!important}.ax-tree-view-node.ax-drag-placeholder{background:var(--ax-comp-tree-view-drag-placeholder-bg)}.ax-drag-preview{opacity:var(--ax-comp-tree-view-drag-preview-opacity)!important;box-shadow:0 4px 12px rgba(var(--ax-sys-color-on-lightest-surface),.2)!important;cursor:grabbing!important;border:2px dashed currentColor!important}.ax-tree-view-node-content.ax-drop-list-sorting-active{background:var(--ax-comp-tree-view-drop-active-bg);border-radius:var(--ax-comp-tree-view-node-border-radius);outline:2px dashed var(--ax-comp-tree-view-drop-active-outline);outline-offset:-2px}.ax-tree-view-node-content{display:flex;align-items:center;gap:.5rem;padding:.5rem;cursor:pointer;-webkit-user-select:none;user-select:none;outline:none;border:1px solid transparent;border-radius:var(--ax-comp-tree-view-node-border-radius)}.ax-tree-view-node-content:focus{outline:none}.ax-tree-view-node-content:focus-visible{outline:2px solid rgba(var(--ax-sys-color-primary-500),.8);outline-offset:2px;border-radius:var(--ax-comp-tree-view-node-border-radius)}.ax-tree-view-drag-handle{cursor:grab;opacity:.6;padding:.25rem}.ax-tree-view-drag-handle:hover{opacity:1}.ax-tree-view-drag-handle:active{cursor:grabbing}.ax-tree-view-expand-toggle{background:none;border:none;cursor:pointer;padding:.25rem;min-width:1.5rem;height:1.5rem}.ax-tree-view-expand-toggle:not(.ax-tree-view-has-children){opacity:0;pointer-events:none}.ax-tree-view-toggle-icon{font-size:.75rem}.ax-tree-view-node-icon{font-size:1.125rem;flex-shrink:0}.ax-tree-view-node-label{flex:1;font-size:.875rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ax-tree-view-children{padding-inline-start:var(--ax-tree-view-indent-size, var(--ax-comp-tree-view-indent-size))}.ax-tree-view-node-disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.ax-tree-view-node-loading{opacity:.7}.ax-tree-view-card .ax-tree-view-node{border:1px solid rgba(var(--ax-sys-color-border-lightest-surface),1);margin:.5rem 0}.ax-tree-view-card .ax-tree-view-node-content{padding:1rem}.ax-tree-view-with-lines .ax-tree-view-children{position:relative;padding-inline-start:var(--ax-tree-view-indent-size, var(--ax-comp-tree-view-indent-size))}.ax-tree-view-with-lines .ax-tree-view-children:before{content:\"\";position:absolute;inset-inline-start:var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2));top:0;height:calc(100% - .875rem);width:1px;background:var(--ax-tree-view-line-color, var(--ax-comp-tree-view-line-color))}.ax-tree-view-with-lines .ax-tree-view-node{position:relative}.ax-tree-view-with-lines .ax-tree-view-node:before{content:\"\";position:absolute;inset-inline-start:calc(-1 * var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2)));top:60%;width:var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2));height:1px;background:var(--ax-tree-view-line-color, var(--ax-comp-tree-view-line-color))}.ax-tree-view-with-lines>.ax-tree-view-drop-list>.ax-tree-view-node:before,.ax-tree-view-with-lines>.ax-tree-view-node:before{display:none}\n"] }]
|
|
1198
|
-
}], propDecorators: { datasource: [{ type: i0.Input, args: [{ isSignal: true, alias: "datasource", required: true }] }, { type: i0.Output, args: ["datasourceChange"] }], selectMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectMode", required: false }] }], showCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheckbox", required: false }] }], checkChildrenOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkChildrenOnSelect", required: false }] }], intermediateState: [{ type: i0.Input, args: [{ isSignal: true, alias: "intermediateState", required: false }] }], checkOnClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkOnClick", required: false }] }], dragMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragMode", required: false }] }], dragOperationType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragOperationType", required: false }] }], showIcons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcons", required: false }] }], showChildrenBadge: [{ type: i0.Input, args: [{ isSignal: true, alias: "showChildrenBadge", required: false }] }], expandedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedIcon", required: false }] }], collapsedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedIcon", required: false }] }], indentSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "indentSize", required: false }] }], nodeHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeHeight", required: false }] }], look: [{ type: i0.Input, args: [{ isSignal: true, alias: "look", required: false }] }], itemTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemTemplate", required: false }] }], onBeforeDrop: [{ type: i0.Output, args: ["onBeforeDrop"] }], onNodeToggle: [{ type: i0.Output, args: ["onNodeToggle"] }], onNodeSelect: [{ type: i0.Output, args: ["onNodeSelect"] }], onOrderChange: [{ type: i0.Output, args: ["onOrderChange"] }], onMoveChange: [{ type: i0.Output, args: ["onMoveChange"] }], onItemsChange: [{ type: i0.Output, args: ["onItemsChange"] }] } });
|
|
2172
|
+
}, template: "<!-- Root drop list -->\n<div\n axFocusTrap\n [axDropList]=\"dragBehavior() !== 'none'\"\n [sortingDisabled]=\"false\"\n [id]=\"getListId()\"\n [attr.data-node-id]=\"null\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDrop($event)\"\n class=\"ax-tree-view-drop-list\"\n [class.ax-tree-view-card]=\"look() === 'card'\"\n [class.ax-tree-view-with-lines]=\"look() === 'with-line'\"\n role=\"group\"\n>\n @for (node of nodes(); track getNodeId(node)) {\n @if (getNodeHidden(node) !== true) {\n <div\n [axDrag]=\"dragBehavior() !== 'none'\"\n [dragDisabled]=\"getNodeDisabled(node)\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-selected]=\"getNodeSelected(node)\"\n [class.ax-tree-view-node-disabled]=\"getNodeDisabled(node)\"\n [class.ax-tree-view-node-loading]=\"getNodeLoading(node)\"\n role=\"treeitem\"\n [attr.aria-level]=\"getNodeAriaLevel(0)\"\n [attr.aria-expanded]=\"getNodeAriaExpanded(node)\"\n [attr.aria-selected]=\"getNodeAriaSelected(node)\"\n [attr.aria-disabled]=\"getNodeDisabled(node) || (isLeafOnlyMode() && !isLeafNode(node)) ? 'true' : null\"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [axDropList]=\"dragBehavior() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + getNodeId(node)\"\n [attr.data-node-id]=\"getNodeId(node)\"\n [attr.data-tree-node-id]=\"getNodeId(node)\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"\n (selectMode() === 'single' || (selectMode() === 'multiple' && !showCheckbox())) && onNodeClick(node, $event)\n \"\n (focus)=\"onNodeFocus(getNodeId(node))\"\n [tabindex]=\"isNodeFocused(getNodeId(node)) ? 0 : -1\"\n >\n @if (dragArea() === 'handler') {\n <span class=\"ax-tree-view-drag-handle\" axDragHandle title=\"Drag to reorder\"> \u22EE\u22EE </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"getNodeExpanded(node)\"\n [disabled]=\"getNodeDisabled(node) || getNodeLoading(node)\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (getNodeLoading(node)) {\n <ax-icon>\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-loading-spinner\"></i>\n </ax-icon>\n } @else {\n <ax-icon>\n <i\n [class]=\"getNodeExpanded(node) ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n @if (nodeTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, 0)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckboxForNode(node)) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"getNodeIndeterminate(node) ? null : getNodeSelected(node) || false\"\n [indeterminate]=\"getNodeIndeterminate(node) || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && getNodeIcon(node)) {\n <i [class]=\"getNodeIcon(node)\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span\n class=\"ax-tree-view-node-label\"\n [axTooltip]=\"getNodeTooltip(node) || ''\"\n [axTooltipDisabled]=\"!getNodeTooltip(node)\"\n [axTooltipPlacement]=\"'top-start'\"\n >\n {{ getNodeTitle(node) }}\n </span>\n @if (\n showChildrenBadge() &&\n (getNodeChildrenCount(node) || (getNodeChildren(node) && getNodeChildren(node)!.length > 0))\n ) {\n <span class=\"ax-tree-view-node-badge\">\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(getNodeChildrenCount(node) ?? getNodeChildren(node)?.length ?? 0).toString()\"\n ></ax-badge>\n </span>\n }\n }\n </div>\n </div>\n @if (getNodeExpanded(node) && getNodeChildren(node) && getNodeChildren(node)!.length > 0) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: getNodeChildren(node), parent: node, level: 1 }\"\n ></ng-container>\n </div>\n }\n }\n }\n</div>\n\n<!-- Recursive children template -->\n<ng-template #childrenList let-children=\"children\" let-parent=\"parent\" let-level=\"level\">\n <div\n [axDropList]=\"dragBehavior() !== 'none'\"\n [id]=\"getListId(parent)\"\n [attr.data-node-id]=\"getNodeId(parent)\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDrop($event, parent)\"\n class=\"ax-tree-view-drop-list\"\n role=\"group\"\n >\n @for (node of children; track getNodeId(node)) {\n @if (getNodeHidden(node) !== true) {\n <div\n [axDrag]=\"dragBehavior() !== 'none'\"\n [dragDisabled]=\"getNodeDisabled(node)\"\n [dragData]=\"node\"\n class=\"ax-tree-view-node\"\n [class.ax-tree-view-node-selected]=\"getNodeSelected(node)\"\n [class.ax-tree-view-node-disabled]=\"getNodeDisabled(node)\"\n [class.ax-tree-view-node-loading]=\"getNodeLoading(node)\"\n [class.ax-tree-view-node-focused]=\"isNodeFocused(getNodeId(node))\"\n role=\"treeitem\"\n [attr.aria-level]=\"getNodeAriaLevel(level)\"\n [attr.aria-expanded]=\"getNodeAriaExpanded(node)\"\n [attr.aria-selected]=\"getNodeAriaSelected(node)\"\n [attr.aria-disabled]=\"getNodeDisabled(node) ? 'true' : null\"\n >\n <div\n class=\"ax-tree-view-node-content\"\n [axDropList]=\"dragBehavior() !== 'none'\"\n [id]=\"'ax-tree-view-node-drop-' + getNodeId(node)\"\n [attr.data-node-id]=\"getNodeId(node)\"\n [attr.data-tree-node-id]=\"getNodeId(node)\"\n [attr.data-drop-type]=\"'onto-node'\"\n dropListGroup=\"ax-tree-view-nodes\"\n (dropListDropped)=\"onDropOntoNode($event, node)\"\n (click)=\"\n (selectMode() === 'single' || (selectMode() === 'multiple' && !showCheckbox())) &&\n onNodeClick(node, $event)\n \"\n (focus)=\"onNodeFocus(getNodeId(node))\"\n (blur)=\"focusedNodeId.set(null)\"\n [tabindex]=\"isNodeFocused(getNodeId(node)) ? 0 : -1\"\n >\n @if (dragArea() === 'handler') {\n <span class=\"ax-tree-view-drag-handle\" axDragHandle title=\"Drag to reorder\"> \u22EE\u22EE </span>\n }\n <ax-button\n class=\"ax-tree-view-expand-toggle ax-sm\"\n (onClick)=\"toggleNode(node, $any($event))\"\n [class.ax-tree-view-has-children]=\"shouldShowExpandToggle(node)\"\n [class.ax-tree-view-expanded]=\"getNodeExpanded(node)\"\n [disabled]=\"getNodeDisabled(node) || getNodeLoading(node)\"\n [style.visibility]=\"shouldShowExpandToggle(node) ? 'visible' : 'hidden'\"\n >\n @if (getNodeLoading(node)) {\n <ax-icon>\n <i class=\"fa-solid fa-spinner fa-spin ax-tree-view-loading-spinner\"></i>\n </ax-icon>\n } @else {\n <ax-icon>\n <i\n [class]=\"getNodeExpanded(node) ? directionExpandedIcon() : directionCollapsedIcon()\"\n class=\"ax-tree-view-toggle-icon\"\n ></i>\n </ax-icon>\n }\n </ax-button>\n\n @if (nodeTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate()!\"\n [ngTemplateOutletContext]=\"getTemplateContext(node, level)\"\n ></ng-container>\n } @else {\n @if (shouldShowCheckboxForNode(node)) {\n <ax-check-box\n class=\"ax-tree-view-checkbox\"\n [ngModel]=\"getNodeIndeterminate(node) ? null : getNodeSelected(node) || false\"\n [indeterminate]=\"getNodeIndeterminate(node) || false\"\n (onValueChanged)=\"toggleSelection(node, $event)\"\n ></ax-check-box>\n }\n @if (showIcons() && getNodeIcon(node)) {\n <i [class]=\"getNodeIcon(node)\" class=\"ax-tree-view-node-icon\"></i>\n }\n <span\n class=\"ax-tree-view-node-label\"\n [axTooltip]=\"getNodeTooltip(node) || ''\"\n [axTooltipDisabled]=\"!getNodeTooltip(node)\"\n [axTooltipPlacement]=\"'top-start'\"\n >\n {{ getNodeTitle(node) }}\n </span>\n @if (\n showChildrenBadge() &&\n (getNodeChildrenCount(node) || (getNodeChildren(node) && getNodeChildren(node)!.length > 0))\n ) {\n <span class=\"ax-tree-view-node-badge\">\n <ax-badge\n class=\"ax-tree-view-children-badge\"\n [text]=\"(getNodeChildrenCount(node) ?? getNodeChildren(node)?.length ?? 0).toString()\"\n ></ax-badge>\n </span>\n }\n }\n </div>\n </div>\n @if (getNodeExpanded(node) && getNodeChildren(node) && getNodeChildren(node)!.length > 0) {\n <div class=\"ax-tree-view-children\" role=\"group\">\n <ng-container\n [ngTemplateOutlet]=\"childrenList\"\n [ngTemplateOutletContext]=\"{ children: getNodeChildren(node), parent: node, level: level + 1 }\"\n ></ng-container>\n </div>\n }\n }\n }\n </div>\n</ng-template>\n", styles: [".ax-tree-view{display:block;width:100%;--ax-comp-tree-view-indent-size: 12px;--ax-comp-tree-view-node-hover-bg: rgba(var(--ax-sys-color-on-lightest-surface), .04);--ax-comp-tree-view-node-selected-bg: rgba(var(--ax-sys-color-primary-500), .12);--ax-comp-tree-view-node-border-radius: 6px;--ax-comp-tree-view-node-margin: .25rem;--ax-comp-tree-view-line-color: rgba(var(--ax-sys-color-on-lightest-surface), .15);--ax-comp-tree-view-drag-preview-opacity: .9;--ax-comp-tree-view-drag-placeholder-bg: rgba(var(--ax-sys-color-on-lightest-surface), .02);--ax-comp-tree-view-drop-active-bg: rgba(var(--ax-sys-color-primary-500), .08);--ax-comp-tree-view-drop-active-outline: rgba(var(--ax-sys-color-primary-500), .3);--ax-comp-tree-view-content-padding: 0;--ax-comp-tree-view-content-gap: .5rem;--ax-comp-tree-view-drop-list-min-height: 2rem;--ax-comp-tree-view-drag-handle-padding: .25rem;--ax-comp-tree-view-badge-padding: .25rem;--ax-comp-tree-view-expand-toggle-padding: .25rem;--ax-comp-tree-view-outline-offset: 2px;--ax-comp-tree-view-outline-offset-negative: -2px}.ax-tree-view-drop-list{min-height:var(--ax-comp-tree-view-drop-list-min-height)}.ax-tree-view-node{position:relative;margin:var(--ax-comp-tree-view-node-margin) 0;border-radius:var(--ax-comp-tree-view-node-border-radius);cursor:move}.ax-tree-view-node:hover:not(.ax-dragging){background:var(--ax-comp-tree-view-node-hover-bg)}.ax-tree-view-node.ax-tree-view-node-selected{background:var(--ax-comp-tree-view-node-selected-bg)}.ax-tree-view-node.ax-dragging{opacity:var(--ax-comp-tree-view-drag-placeholder-opacity);cursor:grabbing!important}.ax-tree-view-node.ax-drag-placeholder{background:var(--ax-comp-tree-view-drag-placeholder-bg)}.ax-drag-preview{opacity:var(--ax-comp-tree-view-drag-preview-opacity)!important;box-shadow:0 4px 12px rgba(var(--ax-sys-color-on-lightest-surface),.2)!important;cursor:grabbing!important;border:2px dashed currentColor!important}.ax-tree-view-node-content.ax-drop-list-sorting-active{background:var(--ax-comp-tree-view-drop-active-bg);border-radius:var(--ax-comp-tree-view-node-border-radius);outline:2px dashed var(--ax-comp-tree-view-drop-active-outline);outline-offset:var(--ax-comp-tree-view-outline-offset-negative)}.ax-tree-view-node-content{display:flex;align-items:center;gap:var(--ax-comp-tree-view-content-gap);padding:var(--ax-comp-tree-view-content-padding);cursor:pointer;-webkit-user-select:none;user-select:none;outline:none;border-radius:var(--ax-comp-tree-view-node-border-radius)}.ax-tree-view-node-content:focus{outline:none}.ax-tree-view-node-content:focus-visible{outline:2px solid rgba(var(--ax-sys-color-primary-500),.8);outline-offset:var(--ax-comp-tree-view-outline-offset);border-radius:var(--ax-comp-tree-view-node-border-radius)}.ax-tree-view-drag-handle{cursor:grab;opacity:.6;padding:var(--ax-comp-tree-view-drag-handle-padding);padding-inline-start:calc(var(--ax-comp-tree-view-drag-handle-padding) * 2)}.ax-tree-view-drag-handle:hover{opacity:1}.ax-tree-view-drag-handle:active{cursor:grabbing}.ax-tree-view-expand-toggle{background:none;border:none;cursor:pointer;padding:var(--ax-comp-tree-view-expand-toggle-padding);min-width:1.5rem;height:1.5rem}.ax-tree-view-expand-toggle:not(.ax-tree-view-has-children){opacity:0;pointer-events:none}.ax-tree-view-toggle-icon{font-size:.75rem}.ax-tree-view-node-icon{font-size:1.125rem;flex-shrink:0}.ax-tree-view-node-label{flex:1;font-size:.875rem;line-height:1rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ax-tree-view-node-badge{padding:var(--ax-comp-tree-view-badge-padding);padding-inline-end:calc(var(--ax-comp-tree-view-badge-padding) * 1.5)}.ax-tree-view-children{padding-inline-start:var(--ax-tree-view-indent-size, var(--ax-comp-tree-view-indent-size))}.ax-tree-view-node-disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.ax-tree-view-node-loading{opacity:.7}.ax-tree-view-card .ax-tree-view-node{border:1px solid rgba(var(--ax-sys-color-border-lightest-surface),1)}.ax-tree-view-card .ax-tree-view-node.ax-tree-view-node-selected{border:1px solid rgba(var(--ax-sys-color-border-surface),1)}.ax-tree-view-with-lines .ax-tree-view-children{position:relative;padding-inline-start:var(--ax-tree-view-indent-size, var(--ax-comp-tree-view-indent-size))}.ax-tree-view-with-lines .ax-tree-view-children:before{content:\"\";position:absolute;inset-inline-start:var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2));top:0;height:calc(100% - .875rem);width:1px;background:var(--ax-tree-view-line-color, var(--ax-comp-tree-view-line-color))}.ax-tree-view-with-lines .ax-tree-view-node{position:relative}.ax-tree-view-with-lines .ax-tree-view-node:before{content:\"\";position:absolute;inset-inline-start:calc(-1 * var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2)));top:60%;width:var(--ax-tree-view-line-offset, calc(var(--ax-comp-tree-view-indent-size) / 2));height:1px;background:var(--ax-tree-view-line-color, var(--ax-comp-tree-view-line-color))}.ax-tree-view-with-lines>.ax-tree-view-drop-list>.ax-tree-view-node:before,.ax-tree-view-with-lines>.ax-tree-view-node:before{display:none}\n"] }]
|
|
2173
|
+
}], propDecorators: { datasource: [{ type: i0.Input, args: [{ isSignal: true, alias: "datasource", required: true }] }, { type: i0.Output, args: ["datasourceChange"] }], selectMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectMode", required: false }] }], showCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheckbox", required: false }] }], selectionBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionBehavior", required: false }] }], dragArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragArea", required: false }] }], dragBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragBehavior", required: false }] }], showIcons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcons", required: false }] }], showChildrenBadge: [{ type: i0.Input, args: [{ isSignal: true, alias: "showChildrenBadge", required: false }] }], expandedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedIcon", required: false }] }], collapsedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedIcon", required: false }] }], indentSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "indentSize", required: false }] }], look: [{ type: i0.Input, args: [{ isSignal: true, alias: "look", required: false }] }], nodeTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeTemplate", required: false }] }], idField: [{ type: i0.Input, args: [{ isSignal: true, alias: "idField", required: false }] }], titleField: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleField", required: false }] }], tooltipField: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipField", required: false }] }], iconField: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconField", required: false }] }], expandedField: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedField", required: false }] }], selectedField: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedField", required: false }] }], indeterminateField: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminateField", required: false }] }], disabledField: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledField", required: false }] }], hiddenField: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenField", required: false }] }], childrenField: [{ type: i0.Input, args: [{ isSignal: true, alias: "childrenField", required: false }] }], childrenCountField: [{ type: i0.Input, args: [{ isSignal: true, alias: "childrenCountField", required: false }] }], dataField: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataField", required: false }] }], onBeforeDrop: [{ type: i0.Output, args: ["onBeforeDrop"] }], onNodeToggle: [{ type: i0.Output, args: ["onNodeToggle"] }], onNodeSelect: [{ type: i0.Output, args: ["onNodeSelect"] }], onSelectionChange: [{ type: i0.Output, args: ["onSelectionChange"] }], onOrderChange: [{ type: i0.Output, args: ["onOrderChange"] }], onMoveChange: [{ type: i0.Output, args: ["onMoveChange"] }], onItemsChange: [{ type: i0.Output, args: ["onItemsChange"] }] } });
|
|
1199
2174
|
|
|
1200
2175
|
class AXTreeViewModule {
|
|
1201
2176
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|