@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.
@@ -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.id === id)
33
+ if (node[idField] === id)
32
34
  return node;
33
- if (node.children) {
34
- const found = this.findNodeById(node.children, id);
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
- if (node.children?.some((child) => child.id === targetNode.id)) {
50
+ const children = node[childrenField];
51
+ if (children?.some((child) => child[idField] === targetId)) {
47
52
  return node;
48
53
  }
49
- if (node.children) {
50
- const found = this.findParentNode(node.children, targetNode);
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
- if (movedNode.id === targetNode.id || this.isNodeDescendantOf(targetNode, movedNode)) {
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
- if (targetNode.id === ancestorNode.id) {
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
- if (!ancestorNode.children || ancestorNode.children.length === 0) {
83
+ const children = ancestorNode[childrenField];
84
+ if (!children || children.length === 0) {
75
85
  return false;
76
86
  }
77
- for (const child of ancestorNode.children) {
78
- if (child.id === targetNode.id) {
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
- if (node.visible !== false && !node.disabled) {
105
+ const hidden = node[hiddenField];
106
+ const disabled = node[disabledField];
107
+ if (hidden !== true && !disabled) {
95
108
  flatList.push({ node, level, parent });
96
- if (node.expanded && node.children) {
97
- traverse(node.children, level + 1, node);
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
- return Boolean(node.children?.length);
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
- return isLazyDataSource && Boolean(node.childrenCount && node.childrenCount > 0 && !this.hasChildren(node));
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.selected = selected;
125
- child.indeterminate = false;
126
- if (child.children) {
127
- this.selectAllChildren(child.children, selected);
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
- if (child.selected && !child.indeterminate) {
156
+ const selected = child[selectedField];
157
+ const indeterminate = child[indeterminateField];
158
+ if (selected && !indeterminate) {
139
159
  selectedCount++;
140
160
  }
141
- if (child.indeterminate) {
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
- if (!parent || !parent.children)
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(parent.children);
178
+ const { allSelected, someSelected } = this.getChildrenSelectionState(parentChildren, selectedField, indeterminateField);
158
179
  if (allSelected) {
159
- parent.selected = true;
160
- parent.indeterminate = false;
180
+ parent[selectedField] = true;
181
+ parent[indeterminateField] = false;
161
182
  }
162
183
  else if (someSelected) {
163
184
  if (intermediateState) {
164
- parent.selected = true;
165
- parent.indeterminate = true;
185
+ parent[selectedField] = true;
186
+ parent[indeterminateField] = true;
166
187
  }
167
188
  else {
168
- parent.selected = false;
169
- parent.indeterminate = false;
189
+ parent[selectedField] = false;
190
+ parent[indeterminateField] = false;
170
191
  }
171
192
  }
172
193
  else {
173
- parent.selected = false;
174
- parent.indeterminate = false;
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.selected = false;
184
- node.indeterminate = false;
185
- if (node.children) {
186
- this.deselectAllNodes(node.children);
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.selected = selected;
196
- node.indeterminate = false;
197
- if (node.children) {
198
- this.setAllSelection(node.children, selected);
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.selected)
231
+ if (node[selectedField])
209
232
  count++;
210
- if (node.children) {
211
- count += this.countSelected(node.children);
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.selected)
245
+ if (node[selectedField])
222
246
  result.push(node);
223
- if (node.children) {
224
- this.collectSelected(node.children, result);
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
- if (nodes[i].selected && !nodes[i].indeterminate) {
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 if (nodes[i].children) {
237
- this.removeSelected(nodes[i].children ?? []);
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
- if (node.children && node.children.length > 0) {
247
- this.updateAllParentStates(node.children, intermediateState);
248
- const { allSelected, someSelected } = this.getChildrenSelectionState(node.children);
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.selected = true;
251
- node.indeterminate = false;
282
+ node[selectedField] = true;
283
+ node[indeterminateField] = false;
252
284
  }
253
285
  else if (someSelected) {
254
286
  if (intermediateState) {
255
- node.selected = true;
256
- node.indeterminate = true;
287
+ node[selectedField] = true;
288
+ node[indeterminateField] = true;
257
289
  }
258
290
  else {
259
- node.selected = false;
260
- node.indeterminate = false;
291
+ node[selectedField] = false;
292
+ node[indeterminateField] = false;
261
293
  }
262
294
  }
263
295
  else {
264
- node.selected = false;
265
- node.indeterminate = false;
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.expanded = expanded;
283
- if (node.children) {
284
- await this.setExpandedState(node.children, expanded, isLazyDataSource, loadNodeChildren);
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
- return node?.children ?? null;
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
- return node ? `${AXTreeViewService.LIST_PREFIX}${node.id}` : AXTreeViewService.ROOT_LIST_ID;
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('multiple', ...(ngDevMode ? [{ debugName: "selectMode" }] : []));
361
- /** Whether to show checkboxes for selection (only applies to multiple mode) */
362
- this.showCheckbox = input(true, ...(ngDevMode ? [{ debugName: "showCheckbox" }] : []));
363
- /** When true, selecting a parent also selects all loaded children (only for multiple mode) */
364
- this.checkChildrenOnSelect = input(true, ...(ngDevMode ? [{ debugName: "checkChildrenOnSelect" }] : []));
365
- /** When true, selecting a child makes parents indeterminate (only for multiple mode) */
366
- this.intermediateState = input(true, ...(ngDevMode ? [{ debugName: "intermediateState" }] : []));
367
- /** When true, clicking on a node toggles its selection (works for both single and multiple modes) */
368
- this.checkOnClick = input(false, ...(ngDevMode ? [{ debugName: "checkOnClick" }] : []));
369
- /** Drag and drop mode: 'none' (disabled), 'handler' (drag handle), 'item' (entire item) */
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(12, ...(ngDevMode ? [{ debugName: "indentSize" }] : []));
383
- /** Node height in pixels */
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.itemTemplate = input(...(ngDevMode ? [undefined, { debugName: "itemTemplate" }] : []));
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
- this.loadRootItems(ds).catch((error) => {
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
- this.treeService.updateAllParentStates(this.nodes(), this.intermediateState());
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
- this.treeService.setAllSelection(this.nodes(), true);
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.expanded ?? false,
539
- childrenCount: node.childrenCount ?? node.children?.length ?? 0,
540
- loading: node.loading ?? false,
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) || this.treeService.canLazyLoad(node, this.isLazyDataSource());
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.expanded ? 'true' : 'false';
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 node.selected ? 'true' : 'false';
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 with checkOnClick enabled
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.disabled)
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' && shouldCheckOnClick) {
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.disabled)
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.expanded;
1528
+ const willExpand = !this.getNodeExpanded(node);
629
1529
  if (willExpand && canLazyLoad) {
630
1530
  await this.loadNodeChildren(node);
631
1531
  }
632
- node.expanded = willExpand;
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.selected = newValue;
648
- node.indeterminate = false;
649
- if (this.checkChildrenOnSelect() && node.children) {
650
- this.treeService.selectAllChildren(node.children, newValue);
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.intermediateState()) {
653
- this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
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?.children ?? this.nodes();
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
- targetNode.children ??= [];
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
- targetNode.children.unshift(movedNode);
694
- targetNode.expanded = true;
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
- afterNextRender(() => {
710
- const flatList = this.treeService.buildFlatNodeList(this.nodes());
711
- if (flatList.length > 0) {
712
- const focusedId = this.focusedNodeId();
713
- if (focusedId) {
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
- this.focusNodeById(flatList[0].node.id);
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 ? flatList.findIndex((item) => item.node.id === currentFocused.id) : -1;
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.id);
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
- if (!this.isLazyDataSource() || node.loading)
776
- return;
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
- node.loading = true;
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(node.id);
1709
+ const result = ds(nodeId);
788
1710
  const children = result instanceof Promise ? await result : result;
789
- node.children = children;
790
- node.childrenCount = children.length;
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.childrenCount = 0;
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(node.id);
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.selected = true;
826
- node.indeterminate = false;
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 with checkOnClick
1777
+ * Handle multiple selection mode when showCheckbox is false (click to toggle)
837
1778
  */
838
1779
  handleMultipleSelection(node, event) {
839
- const newValue = !node.selected;
840
- node.selected = newValue;
841
- node.indeterminate = false;
842
- if (this.checkChildrenOnSelect() && node.children) {
843
- this.treeService.selectAllChildren(node.children, newValue);
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.intermediateState()) {
846
- this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
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 dragOperationType
1812
+ * Check if move operation is allowed based on dragBehavior
870
1813
  */
871
1814
  canMoveToParent() {
872
- return this.dragOperationType() !== 'order-only';
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 dragOperationType
1819
+ * Check if reorder operation is allowed based on dragBehavior
876
1820
  */
877
1821
  canReorder() {
878
- return this.dragOperationType() !== 'move';
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.expanded && this.shouldShowExpandToggle(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.id === currentItem.parent.id);
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.expanded && this.shouldShowExpandToggle(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.expanded &&
1020
- this.treeService.hasChildren(currentFocused) &&
1021
- currentFocused.children) {
1022
- const children = currentFocused.children;
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.id === firstChild.id);
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.id === firstChild.id);
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.id);
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() === 'multiple') {
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
- const newValue = !node.selected;
1089
- node.selected = newValue;
1090
- node.indeterminate = false;
1091
- if (this.checkChildrenOnSelect() && node.children) {
1092
- this.treeService.selectAllChildren(node.children, newValue);
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
- if (this.intermediateState()) {
1095
- this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
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
- this.treeService.deselectAllNodes(this.nodes());
1111
- node.selected = true;
1112
- node.indeterminate = false;
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
- if (this.checkChildrenOnSelect() && node.children) {
1115
- this.treeService.selectAllChildren(node.children, true);
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 (this.intermediateState()) {
1118
- this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
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
- const newValue = !node.selected;
1134
- node.selected = newValue;
1135
- node.indeterminate = false;
1136
- if (this.checkChildrenOnSelect() && node.children) {
1137
- this.treeService.selectAllChildren(node.children, newValue);
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.intermediateState()) {
1140
- this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
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 }); }