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