@acorex/components 21.0.0-next.36 → 21.0.0-next.38
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/fesm2022/acorex-components-file-explorer.mjs +8 -8
- package/fesm2022/acorex-components-file-explorer.mjs.map +1 -1
- package/fesm2022/acorex-components-tree-view-legacy.mjs +511 -0
- package/fesm2022/acorex-components-tree-view-legacy.mjs.map +1 -0
- package/fesm2022/acorex-components-tree-view.mjs +1123 -397
- package/fesm2022/acorex-components-tree-view.mjs.map +1 -1
- package/file-explorer/index.d.ts +6 -6
- package/package.json +6 -6
- package/tree-view/index.d.ts +499 -168
- package/tree-view-legacy/README.md +3 -0
- package/tree-view-legacy/index.d.ts +184 -0
- package/fesm2022/acorex-components-tree2.mjs +0 -730
- package/fesm2022/acorex-components-tree2.mjs.map +0 -1
- package/tree2/README.md +0 -3
- package/tree2/index.d.ts +0 -346
|
@@ -1,505 +1,1231 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { moveItemInArray, transferArrayItem, AXDragDirective, AXDragHandleDirective, AXDropListDirective } from '@acorex/cdk/drag-drop';
|
|
2
|
+
import { AXFocusTrapDirective } from '@acorex/cdk/focus-trap';
|
|
3
|
+
import { AXBadgeComponent } from '@acorex/components/badge';
|
|
4
|
+
import { AXButtonComponent } from '@acorex/components/button';
|
|
5
|
+
import { AXCheckBoxComponent } from '@acorex/components/check-box';
|
|
6
|
+
import { AXDecoratorIconComponent } from '@acorex/components/decorators';
|
|
4
7
|
import { AXPlatform } from '@acorex/core/platform';
|
|
5
|
-
import { AXTranslatorPipe } from '@acorex/core/translation';
|
|
6
8
|
import * as i1 from '@angular/common';
|
|
7
|
-
import { CommonModule, NgTemplateOutlet
|
|
9
|
+
import { CommonModule, NgTemplateOutlet } from '@angular/common';
|
|
8
10
|
import * as i0 from '@angular/core';
|
|
9
|
-
import { inject,
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import * as i1$1 from '@angular/forms';
|
|
11
|
+
import { Injectable, inject, DestroyRef, model, input, output, signal, computed, effect, afterNextRender, ViewEncapsulation, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
|
|
12
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
13
|
+
import * as i2 from '@angular/forms';
|
|
13
14
|
import { FormsModule } from '@angular/forms';
|
|
14
|
-
import {
|
|
15
|
+
import { map } from 'rxjs/operators';
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (this.isExpanded()) {
|
|
38
|
-
return `${baseClasses} ax-icon-chevron-down`;
|
|
17
|
+
/**
|
|
18
|
+
* Service for tree node operations
|
|
19
|
+
* Handles all business logic for tree node manipulation
|
|
20
|
+
*/
|
|
21
|
+
class AXTreeViewService {
|
|
22
|
+
// ==================== Constants ====================
|
|
23
|
+
static { this.ROOT_LIST_ID = 'ax-tree-view-root-list'; }
|
|
24
|
+
static { this.NODE_DROP_PREFIX = 'ax-tree-view-node-drop-'; }
|
|
25
|
+
static { this.LIST_PREFIX = 'ax-tree-view-list-'; }
|
|
26
|
+
// ==================== Node Finding & Traversal ====================
|
|
27
|
+
/**
|
|
28
|
+
* Find a node by ID in the tree
|
|
29
|
+
*/
|
|
30
|
+
findNodeById(nodes, id) {
|
|
31
|
+
for (const node of nodes) {
|
|
32
|
+
if (node.id === id)
|
|
33
|
+
return node;
|
|
34
|
+
if (node.children) {
|
|
35
|
+
const found = this.findNodeById(node.children, id);
|
|
36
|
+
if (found)
|
|
37
|
+
return found;
|
|
39
38
|
}
|
|
40
|
-
return this.platformService.isRtl()
|
|
41
|
-
? `${baseClasses} ax-icon-chevron-left`
|
|
42
|
-
: `${baseClasses} ax-icon-chevron-right`;
|
|
43
|
-
}, ...(ngDevMode ? [{ debugName: "arrowIcon" }] : []));
|
|
44
|
-
}
|
|
45
|
-
handleArrowNodeClick() {
|
|
46
|
-
if (this.item()[this.treeView.disableField()] || this.isLoading() || this.treeView.expandOn() === 'dbClick') {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
this.isExpanded.set(!this.isExpanded());
|
|
50
|
-
if (this.treeView.itemsPromise && this.isExpanded() && !this.item()[this.treeView.childrenField()]?.length) {
|
|
51
|
-
this.treeView.fetchData(this.item());
|
|
52
|
-
this.treeView.setNodeLoading(this.item()[this.treeView.valueField()], true);
|
|
53
39
|
}
|
|
54
|
-
|
|
40
|
+
return null;
|
|
55
41
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Find parent node of a given node
|
|
44
|
+
*/
|
|
45
|
+
findParentNode(nodes, targetNode) {
|
|
46
|
+
for (const node of nodes) {
|
|
47
|
+
if (node.children?.some((child) => child.id === targetNode.id)) {
|
|
48
|
+
return node;
|
|
49
|
+
}
|
|
50
|
+
if (node.children) {
|
|
51
|
+
const found = this.findParentNode(node.children, targetNode);
|
|
52
|
+
if (found)
|
|
53
|
+
return found;
|
|
54
|
+
}
|
|
59
55
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Check if targetNode is a descendant of ancestorNode (or the same node)
|
|
60
|
+
* Prevents circular references by checking if target exists in ancestor's children tree
|
|
61
|
+
*/
|
|
62
|
+
isValidDropTarget(movedNode, targetNode) {
|
|
63
|
+
if (movedNode.id === targetNode.id || this.isNodeDescendantOf(targetNode, movedNode)) {
|
|
64
|
+
return false;
|
|
63
65
|
}
|
|
64
|
-
|
|
66
|
+
return true;
|
|
65
67
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Check if targetNode is a descendant of ancestorNode
|
|
70
|
+
*/
|
|
71
|
+
isNodeDescendantOf(targetNode, ancestorNode) {
|
|
72
|
+
if (targetNode.id === ancestorNode.id) {
|
|
73
|
+
return true;
|
|
69
74
|
}
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
if (!ancestorNode.children || ancestorNode.children.length === 0) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
for (const child of ancestorNode.children) {
|
|
79
|
+
if (child.id === targetNode.id) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (this.isNodeDescendantOf(targetNode, child)) {
|
|
83
|
+
return true;
|
|
75
84
|
}
|
|
76
85
|
}
|
|
77
|
-
|
|
78
|
-
this.treeView.onCollapsedChanged.emit({ component: this, data: this.item(), nativeElement: this.nativeElement });
|
|
86
|
+
return false;
|
|
79
87
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
this.items = input(...(ngDevMode ? [undefined, { debugName: "items" }] : []));
|
|
93
|
-
this.showCheckbox = input(true, ...(ngDevMode ? [{ debugName: "showCheckbox" }] : []));
|
|
94
|
-
this.hasCheckboxField = input('hasCheckbox', ...(ngDevMode ? [{ debugName: "hasCheckboxField" }] : []));
|
|
95
|
-
this.selectionMode = input('single', ...(ngDevMode ? [{ debugName: "selectionMode" }] : []));
|
|
96
|
-
this.selectionBehavior = input(...(ngDevMode ? [undefined, { debugName: "selectionBehavior" }] : []));
|
|
97
|
-
this.selectionScope = input('all', ...(ngDevMode ? [{ debugName: "selectionScope" }] : []));
|
|
98
|
-
this.focusNodeEnabled = input(true, ...(ngDevMode ? [{ debugName: "focusNodeEnabled" }] : []));
|
|
99
|
-
this.valueField = input('id', ...(ngDevMode ? [{ debugName: "valueField" }] : []));
|
|
100
|
-
this.textField = input('text', ...(ngDevMode ? [{ debugName: "textField" }] : []));
|
|
101
|
-
this.visibleField = input('visible', ...(ngDevMode ? [{ debugName: "visibleField" }] : []));
|
|
102
|
-
this.disableField = input('disabled', ...(ngDevMode ? [{ debugName: "disableField" }] : []));
|
|
103
|
-
this.hasChildField = input('hasChild', ...(ngDevMode ? [{ debugName: "hasChildField" }] : []));
|
|
104
|
-
this.selectedField = input('selected', ...(ngDevMode ? [{ debugName: "selectedField" }] : []));
|
|
105
|
-
this.expandedField = input('expanded', ...(ngDevMode ? [{ debugName: "expandedField" }] : []));
|
|
106
|
-
this.tooltipField = input('tooltip', ...(ngDevMode ? [{ debugName: "tooltipField" }] : []));
|
|
107
|
-
this.childrenField = input('children', ...(ngDevMode ? [{ debugName: "childrenField" }] : []));
|
|
108
|
-
this.activeField = input('active', ...(ngDevMode ? [{ debugName: "activeField" }] : []));
|
|
109
|
-
this.indeterminateField = input('indeterminate', ...(ngDevMode ? [{ debugName: "indeterminateField" }] : []));
|
|
110
|
-
this.parentField = input('parentId', ...(ngDevMode ? [{ debugName: "parentField" }] : []));
|
|
111
|
-
this.iconField = input('icon', ...(ngDevMode ? [{ debugName: "iconField" }] : []));
|
|
112
|
-
this.toggleIcons = input(...(ngDevMode ? [undefined, { debugName: "toggleIcons" }] : []));
|
|
113
|
-
this.look = input('defult', ...(ngDevMode ? [{ debugName: "look" }] : []));
|
|
114
|
-
this.showEmptyNodeMassage = input(false, ...(ngDevMode ? [{ debugName: "showEmptyNodeMassage" }] : []));
|
|
115
|
-
this.onSelectionChanged = output();
|
|
116
|
-
this.onItemSelectedChanged = output();
|
|
117
|
-
this.onNodeClick = output();
|
|
118
|
-
this.onCollapsedChanged = output();
|
|
119
|
-
this.onNodedbClick = output();
|
|
120
|
-
this.executorChanges = signal(null, ...(ngDevMode ? [{ debugName: "executorChanges" }] : []));
|
|
121
|
-
this.platformService = inject(AXPlatform);
|
|
122
|
-
this.#effect = effect(() => {
|
|
123
|
-
const itemsInput = this.items();
|
|
124
|
-
if (typeof itemsInput === 'function') {
|
|
125
|
-
const result = itemsInput();
|
|
126
|
-
if (result instanceof Promise) {
|
|
127
|
-
this.itemsPromise = (options) => itemsInput(options);
|
|
128
|
-
this.fetchData();
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
this.itemsSignal.set(result);
|
|
132
|
-
this.itemsPromise = null;
|
|
88
|
+
/**
|
|
89
|
+
* Build a flat list of all visible focusable nodes
|
|
90
|
+
*/
|
|
91
|
+
buildFlatNodeList(nodes) {
|
|
92
|
+
const flatList = [];
|
|
93
|
+
const traverse = (nodeList, level, parent) => {
|
|
94
|
+
for (const node of nodeList) {
|
|
95
|
+
if (node.visible !== false && !node.disabled) {
|
|
96
|
+
flatList.push({ node, level, parent });
|
|
97
|
+
if (node.expanded && node.children) {
|
|
98
|
+
traverse(node.children, level + 1, node);
|
|
99
|
+
}
|
|
133
100
|
}
|
|
134
101
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
}, ...(ngDevMode ? [{ debugName: "#effect" }] : []));
|
|
140
|
-
this.expandOn = input('defult', ...(ngDevMode ? [{ debugName: "expandOn" }] : []));
|
|
141
|
-
this.loadingState = signal({}, ...(ngDevMode ? [{ debugName: "loadingState" }] : []));
|
|
102
|
+
};
|
|
103
|
+
traverse(nodes, 0);
|
|
104
|
+
return flatList;
|
|
142
105
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
106
|
+
// ==================== Node State Checks ====================
|
|
107
|
+
/**
|
|
108
|
+
* Check if node has children
|
|
109
|
+
*/
|
|
110
|
+
hasChildren(node) {
|
|
111
|
+
return Boolean(node.children?.length);
|
|
146
112
|
}
|
|
147
|
-
|
|
148
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Check if node can be lazy loaded
|
|
115
|
+
*/
|
|
116
|
+
canLazyLoad(node, isLazyDataSource) {
|
|
117
|
+
return isLazyDataSource && Boolean(node.childrenCount && node.childrenCount > 0 && !this.hasChildren(node));
|
|
149
118
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (event.value !== null) {
|
|
161
|
-
switch (this.selectionBehavior()) {
|
|
162
|
-
case 'autoExpand':
|
|
163
|
-
if (event.value) {
|
|
164
|
-
if (this.itemsPromise && item[this.hasChildField()] && !item?.[this.childrenField()]?.length) {
|
|
165
|
-
this.setNodeLoading(item[this.valueField()], true);
|
|
166
|
-
this.fetchData(item);
|
|
167
|
-
}
|
|
168
|
-
this.toggleExpand(item);
|
|
169
|
-
}
|
|
170
|
-
break;
|
|
171
|
-
case 'cascade':
|
|
172
|
-
this.expandAndToggleSelection(item, event.value);
|
|
173
|
-
break;
|
|
174
|
-
case 'indeterminate':
|
|
175
|
-
if (item?.[this.childrenField()]?.length) {
|
|
176
|
-
this.applySelectionToChildren(item, event.value, item[this.valueField()]);
|
|
177
|
-
}
|
|
178
|
-
this.updateParentSelection(item, event.value);
|
|
179
|
-
break;
|
|
180
|
-
default:
|
|
181
|
-
break;
|
|
182
|
-
}
|
|
119
|
+
// ==================== Selection Management ====================
|
|
120
|
+
/**
|
|
121
|
+
* Recursively select/deselect all children
|
|
122
|
+
*/
|
|
123
|
+
selectAllChildren(children, selected) {
|
|
124
|
+
for (const child of children) {
|
|
125
|
+
child.selected = selected;
|
|
126
|
+
child.indeterminate = false;
|
|
127
|
+
if (child.children) {
|
|
128
|
+
this.selectAllChildren(child.children, selected);
|
|
183
129
|
}
|
|
184
|
-
this.onItemSelectedChanged.emit({
|
|
185
|
-
component: this,
|
|
186
|
-
data: item,
|
|
187
|
-
nativeElement: this.nativeElement,
|
|
188
|
-
});
|
|
189
|
-
const result = this.findSelectedNodes(this.itemsSignal());
|
|
190
|
-
this.onSelectionChanged.emit({
|
|
191
|
-
component: this,
|
|
192
|
-
data: result,
|
|
193
|
-
nativeElement: this.nativeElement,
|
|
194
|
-
});
|
|
195
130
|
}
|
|
196
131
|
}
|
|
197
132
|
/**
|
|
198
|
-
*
|
|
199
|
-
* auto expand
|
|
200
|
-
*
|
|
133
|
+
* Get selection state of children
|
|
201
134
|
*/
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
135
|
+
getChildrenSelectionState(children) {
|
|
136
|
+
let selectedCount = 0;
|
|
137
|
+
let indeterminateCount = 0;
|
|
138
|
+
for (const child of children) {
|
|
139
|
+
if (child.selected && !child.indeterminate) {
|
|
140
|
+
selectedCount++;
|
|
141
|
+
}
|
|
142
|
+
if (child.indeterminate) {
|
|
143
|
+
indeterminateCount++;
|
|
144
|
+
}
|
|
205
145
|
}
|
|
146
|
+
return {
|
|
147
|
+
allSelected: selectedCount === children.length,
|
|
148
|
+
someSelected: selectedCount > 0 || indeterminateCount > 0,
|
|
149
|
+
};
|
|
206
150
|
}
|
|
207
151
|
/**
|
|
208
|
-
*
|
|
209
|
-
* expand and change value parent change
|
|
210
|
-
*
|
|
152
|
+
* Update parent node states based on children selection (with intermediate state support)
|
|
211
153
|
*/
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
154
|
+
updateParentStates(nodes, changedNode, intermediateState) {
|
|
155
|
+
const parent = this.findParentNode(nodes, changedNode);
|
|
156
|
+
if (!parent || !parent.children)
|
|
157
|
+
return;
|
|
158
|
+
const { allSelected, someSelected } = this.getChildrenSelectionState(parent.children);
|
|
159
|
+
if (allSelected) {
|
|
160
|
+
parent.selected = true;
|
|
161
|
+
parent.indeterminate = false;
|
|
216
162
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
163
|
+
else if (someSelected) {
|
|
164
|
+
if (intermediateState) {
|
|
165
|
+
parent.selected = true;
|
|
166
|
+
parent.indeterminate = true;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
parent.selected = false;
|
|
170
|
+
parent.indeterminate = false;
|
|
171
|
+
}
|
|
220
172
|
}
|
|
173
|
+
else {
|
|
174
|
+
parent.selected = false;
|
|
175
|
+
parent.indeterminate = false;
|
|
176
|
+
}
|
|
177
|
+
this.updateParentStates(nodes, parent, intermediateState);
|
|
221
178
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
this.
|
|
179
|
+
/**
|
|
180
|
+
* Recursively deselect all nodes
|
|
181
|
+
*/
|
|
182
|
+
deselectAllNodes(nodes) {
|
|
183
|
+
for (const node of nodes) {
|
|
184
|
+
node.selected = false;
|
|
185
|
+
node.indeterminate = false;
|
|
186
|
+
if (node.children) {
|
|
187
|
+
this.deselectAllNodes(node.children);
|
|
231
188
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Recursively set selection state for all nodes
|
|
193
|
+
*/
|
|
194
|
+
setAllSelection(nodes, selected) {
|
|
195
|
+
for (const node of nodes) {
|
|
196
|
+
node.selected = selected;
|
|
197
|
+
node.indeterminate = false;
|
|
198
|
+
if (node.children) {
|
|
199
|
+
this.setAllSelection(node.children, selected);
|
|
236
200
|
}
|
|
237
|
-
}
|
|
201
|
+
}
|
|
238
202
|
}
|
|
239
203
|
/**
|
|
240
|
-
*
|
|
241
|
-
* indeterminate logic
|
|
242
|
-
*
|
|
204
|
+
* Recursively count selected nodes
|
|
243
205
|
*/
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
parent[this.selectedField()] = false;
|
|
252
|
-
parent[this.indeterminateField()] = null;
|
|
206
|
+
countSelected(nodes) {
|
|
207
|
+
let count = 0;
|
|
208
|
+
for (const node of nodes) {
|
|
209
|
+
if (node.selected)
|
|
210
|
+
count++;
|
|
211
|
+
if (node.children) {
|
|
212
|
+
count += this.countSelected(node.children);
|
|
253
213
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
214
|
+
}
|
|
215
|
+
return count;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Recursively collect selected nodes
|
|
219
|
+
*/
|
|
220
|
+
collectSelected(nodes, result) {
|
|
221
|
+
for (const node of nodes) {
|
|
222
|
+
if (node.selected)
|
|
223
|
+
result.push(node);
|
|
224
|
+
if (node.children) {
|
|
225
|
+
this.collectSelected(node.children, result);
|
|
257
226
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Recursively remove selected nodes
|
|
231
|
+
*/
|
|
232
|
+
removeSelected(nodes) {
|
|
233
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
234
|
+
if (nodes[i].selected && !nodes[i].indeterminate) {
|
|
235
|
+
nodes.splice(i, 1);
|
|
261
236
|
}
|
|
262
|
-
else {
|
|
263
|
-
|
|
264
|
-
parent[this.selectedField()] = true;
|
|
237
|
+
else if (nodes[i].children) {
|
|
238
|
+
this.removeSelected(nodes[i].children ?? []);
|
|
265
239
|
}
|
|
266
|
-
parent = this.findParent(parent, this.itemsSignal());
|
|
267
240
|
}
|
|
268
241
|
}
|
|
269
|
-
|
|
242
|
+
/**
|
|
243
|
+
* Recursively update all parent states in the tree (used after deletion)
|
|
244
|
+
*/
|
|
245
|
+
updateAllParentStates(nodes, intermediateState) {
|
|
270
246
|
for (const node of nodes) {
|
|
271
|
-
if (node
|
|
272
|
-
|
|
247
|
+
if (node.children && node.children.length > 0) {
|
|
248
|
+
this.updateAllParentStates(node.children, intermediateState);
|
|
249
|
+
const { allSelected, someSelected } = this.getChildrenSelectionState(node.children);
|
|
250
|
+
if (allSelected) {
|
|
251
|
+
node.selected = true;
|
|
252
|
+
node.indeterminate = false;
|
|
253
|
+
}
|
|
254
|
+
else if (someSelected) {
|
|
255
|
+
if (intermediateState) {
|
|
256
|
+
node.selected = true;
|
|
257
|
+
node.indeterminate = true;
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
node.selected = false;
|
|
261
|
+
node.indeterminate = false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
node.selected = false;
|
|
266
|
+
node.indeterminate = false;
|
|
267
|
+
}
|
|
273
268
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// ==================== Expansion Management ====================
|
|
272
|
+
/**
|
|
273
|
+
* Recursively set expanded state (with lazy loading)
|
|
274
|
+
*/
|
|
275
|
+
async setExpandedState(nodes, expanded, isLazyDataSource, loadNodeChildren) {
|
|
276
|
+
for (const node of nodes) {
|
|
277
|
+
const hasChildren = this.hasChildren(node);
|
|
278
|
+
const canLazyLoad = this.canLazyLoad(node, isLazyDataSource);
|
|
279
|
+
if (hasChildren || canLazyLoad) {
|
|
280
|
+
if (expanded && canLazyLoad) {
|
|
281
|
+
await loadNodeChildren(node);
|
|
282
|
+
}
|
|
283
|
+
node.expanded = expanded;
|
|
284
|
+
if (node.children) {
|
|
285
|
+
await this.setExpandedState(node.children, expanded, isLazyDataSource, loadNodeChildren);
|
|
286
|
+
}
|
|
278
287
|
}
|
|
279
288
|
}
|
|
280
|
-
|
|
289
|
+
}
|
|
290
|
+
// ==================== Drag & Drop Helpers ====================
|
|
291
|
+
/**
|
|
292
|
+
* Get array reference by drop list ID
|
|
293
|
+
*/
|
|
294
|
+
getArrayByListId(nodes, listId) {
|
|
295
|
+
if (listId === AXTreeViewService.ROOT_LIST_ID) {
|
|
296
|
+
return nodes;
|
|
297
|
+
}
|
|
298
|
+
if (listId.startsWith(AXTreeViewService.NODE_DROP_PREFIX)) {
|
|
299
|
+
const nodeId = listId.replace(AXTreeViewService.NODE_DROP_PREFIX, '');
|
|
300
|
+
const node = this.findNodeById(nodes, nodeId);
|
|
301
|
+
return node ? [node] : null;
|
|
302
|
+
}
|
|
303
|
+
const nodeId = listId.replace(AXTreeViewService.LIST_PREFIX, '');
|
|
304
|
+
const node = this.findNodeById(nodes, nodeId);
|
|
305
|
+
return node?.children ?? null;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Find parent node by list ID
|
|
309
|
+
*/
|
|
310
|
+
findParentByListId(nodes, listId) {
|
|
311
|
+
if (listId === AXTreeViewService.ROOT_LIST_ID) {
|
|
312
|
+
return undefined;
|
|
313
|
+
}
|
|
314
|
+
const prefix = listId.startsWith(AXTreeViewService.NODE_DROP_PREFIX)
|
|
315
|
+
? AXTreeViewService.NODE_DROP_PREFIX
|
|
316
|
+
: AXTreeViewService.LIST_PREFIX;
|
|
317
|
+
const nodeId = listId.replace(prefix, '');
|
|
318
|
+
return this.findNodeById(nodes, nodeId) ?? undefined;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Generate unique list ID for each node
|
|
322
|
+
*/
|
|
323
|
+
getListId(node) {
|
|
324
|
+
return node ? `${AXTreeViewService.LIST_PREFIX}${node.id}` : AXTreeViewService.ROOT_LIST_ID;
|
|
281
325
|
}
|
|
282
326
|
/**
|
|
283
|
-
*
|
|
284
|
-
* find node selected true for emit Selections
|
|
285
|
-
*
|
|
327
|
+
* Get root list ID constant
|
|
286
328
|
*/
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
329
|
+
getRootListId() {
|
|
330
|
+
return AXTreeViewService.ROOT_LIST_ID;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Get node drop prefix constant
|
|
334
|
+
*/
|
|
335
|
+
getNodeDropPrefix() {
|
|
336
|
+
return AXTreeViewService.NODE_DROP_PREFIX;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Get list prefix constant
|
|
340
|
+
*/
|
|
341
|
+
getListPrefix() {
|
|
342
|
+
return AXTreeViewService.LIST_PREFIX;
|
|
343
|
+
}
|
|
344
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
345
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewService }); }
|
|
346
|
+
}
|
|
347
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewService, decorators: [{
|
|
348
|
+
type: Injectable
|
|
349
|
+
}] });
|
|
350
|
+
|
|
351
|
+
class AXTreeViewComponent {
|
|
352
|
+
constructor() {
|
|
353
|
+
// ==================== Dependencies ====================
|
|
354
|
+
this.treeService = inject(AXTreeViewService);
|
|
355
|
+
this.platformService = inject(AXPlatform);
|
|
356
|
+
this.destroyRef = inject(DestroyRef);
|
|
357
|
+
// ==================== Inputs ====================
|
|
358
|
+
/** Tree data source - can be static array or lazy loading function */
|
|
359
|
+
this.datasource = model.required(...(ngDevMode ? [{ debugName: "datasource" }] : []));
|
|
360
|
+
/** Selection mode: 'single' (click to select) or 'multiple' (checkbox selection) */
|
|
361
|
+
this.selectMode = input('multiple', ...(ngDevMode ? [{ debugName: "selectMode" }] : []));
|
|
362
|
+
/** Whether to show checkboxes for selection (only applies to multiple mode) */
|
|
363
|
+
this.showCheckbox = input(true, ...(ngDevMode ? [{ debugName: "showCheckbox" }] : []));
|
|
364
|
+
/** When true, selecting a parent also selects all loaded children (only for multiple mode) */
|
|
365
|
+
this.checkChildrenOnSelect = input(true, ...(ngDevMode ? [{ debugName: "checkChildrenOnSelect" }] : []));
|
|
366
|
+
/** When true, selecting a child makes parents indeterminate (only for multiple mode) */
|
|
367
|
+
this.intermediateState = input(true, ...(ngDevMode ? [{ debugName: "intermediateState" }] : []));
|
|
368
|
+
/** When true, clicking on a node toggles its selection (works for both single and multiple modes) */
|
|
369
|
+
this.checkOnClick = input(false, ...(ngDevMode ? [{ debugName: "checkOnClick" }] : []));
|
|
370
|
+
/** Drag and drop mode: 'none' (disabled), 'handler' (drag handle), 'item' (entire item) */
|
|
371
|
+
this.dragMode = input('handler', ...(ngDevMode ? [{ debugName: "dragMode" }] : []));
|
|
372
|
+
/** Drag operation type: 'order-only' (reorder only), 'move' (move between parents), 'both' (allow both) */
|
|
373
|
+
this.dragOperationType = input('both', ...(ngDevMode ? [{ debugName: "dragOperationType" }] : []));
|
|
374
|
+
/** Whether to show icons */
|
|
375
|
+
this.showIcons = input(true, ...(ngDevMode ? [{ debugName: "showIcons" }] : []));
|
|
376
|
+
/** Whether to show children count badge */
|
|
377
|
+
this.showChildrenBadge = input(true, ...(ngDevMode ? [{ debugName: "showChildrenBadge" }] : []));
|
|
378
|
+
/** Custom icon for expanded nodes */
|
|
379
|
+
this.expandedIcon = input('fa-solid fa-chevron-down', ...(ngDevMode ? [{ debugName: "expandedIcon" }] : []));
|
|
380
|
+
/** Custom icon for collapsed nodes */
|
|
381
|
+
this.collapsedIcon = input('fa-solid fa-chevron-right', ...(ngDevMode ? [{ debugName: "collapsedIcon" }] : []));
|
|
382
|
+
/** Indent size in pixels for each level */
|
|
383
|
+
this.indentSize = input(12, ...(ngDevMode ? [{ debugName: "indentSize" }] : []));
|
|
384
|
+
/** Node height in pixels */
|
|
385
|
+
this.nodeHeight = input('normal', ...(ngDevMode ? [{ debugName: "nodeHeight" }] : []));
|
|
386
|
+
/** Visual style variant */
|
|
387
|
+
this.look = input('default', ...(ngDevMode ? [{ debugName: "look" }] : []));
|
|
388
|
+
/** Custom template for tree items */
|
|
389
|
+
this.itemTemplate = input(...(ngDevMode ? [undefined, { debugName: "itemTemplate" }] : []));
|
|
390
|
+
// ==================== Outputs ====================
|
|
391
|
+
/** Emitted before a drop operation - set canceled to true to prevent drop */
|
|
392
|
+
this.onBeforeDrop = output();
|
|
393
|
+
/** Emitted when a node is toggled (expanded/collapsed) */
|
|
394
|
+
this.onNodeToggle = output();
|
|
395
|
+
/** Emitted when a node is selected/deselected */
|
|
396
|
+
this.onNodeSelect = output();
|
|
397
|
+
/** Emitted when nodes are reordered within the same parent */
|
|
398
|
+
this.onOrderChange = output();
|
|
399
|
+
/** Emitted when a node is moved to a different parent */
|
|
400
|
+
this.onMoveChange = output();
|
|
401
|
+
/** Emitted for any item change (order or move) */
|
|
402
|
+
this.onItemsChange = output();
|
|
403
|
+
// ==================== Internal State ====================
|
|
404
|
+
/** Internal signal for tree nodes */
|
|
405
|
+
this.nodes = signal([], ...(ngDevMode ? [{ debugName: "nodes" }] : []));
|
|
406
|
+
/** Internal signal for tracking loading state */
|
|
407
|
+
this.loadingNodes = signal(new Set(), ...(ngDevMode ? [{ debugName: "loadingNodes" }] : []));
|
|
408
|
+
/** Currently focused node ID for keyboard navigation */
|
|
409
|
+
this.focusedNodeId = signal(null, ...(ngDevMode ? [{ debugName: "focusedNodeId" }] : []));
|
|
410
|
+
/** RTL detection signal */
|
|
411
|
+
this.isRtl = signal(this.platformService.isRtl(), ...(ngDevMode ? [{ debugName: "isRtl" }] : []));
|
|
412
|
+
/** Computed chevron icons that flip for RTL */
|
|
413
|
+
this.directionExpandedIcon = computed(() => this.expandedIcon(), ...(ngDevMode ? [{ debugName: "directionExpandedIcon" }] : []));
|
|
414
|
+
this.directionCollapsedIcon = computed(() => {
|
|
415
|
+
const isRtlDirection = this.isRtl();
|
|
416
|
+
const defaultIcon = this.collapsedIcon();
|
|
417
|
+
if (isRtlDirection && defaultIcon === 'fa-solid fa-chevron-right') {
|
|
418
|
+
return 'fa-solid fa-chevron-left';
|
|
419
|
+
}
|
|
420
|
+
if (!isRtlDirection && defaultIcon === 'fa-solid fa-chevron-left') {
|
|
421
|
+
return 'fa-solid fa-chevron-right';
|
|
422
|
+
}
|
|
423
|
+
return defaultIcon;
|
|
424
|
+
}, ...(ngDevMode ? [{ debugName: "directionCollapsedIcon" }] : []));
|
|
425
|
+
/** Flag to prevent infinite loops when syncing datasource */
|
|
426
|
+
this.isUpdatingFromDatasource = false;
|
|
427
|
+
/** Computed to check if datasource is a function */
|
|
428
|
+
this.isLazyDataSource = computed(() => typeof this.datasource() === 'function', ...(ngDevMode ? [{ debugName: "isLazyDataSource" }] : []));
|
|
429
|
+
// ==================== Effects ====================
|
|
430
|
+
/** Effect to handle datasource changes */
|
|
431
|
+
this.#datasourceEffect = effect(() => {
|
|
432
|
+
if (this.isUpdatingFromDatasource)
|
|
433
|
+
return;
|
|
434
|
+
const ds = this.datasource();
|
|
435
|
+
if (Array.isArray(ds)) {
|
|
436
|
+
this.nodes.set([...ds]);
|
|
292
437
|
}
|
|
293
|
-
if (
|
|
294
|
-
|
|
438
|
+
else if (typeof ds === 'function') {
|
|
439
|
+
this.loadRootItems(ds).catch((error) => {
|
|
440
|
+
this.handleError('Failed to load root items', error);
|
|
441
|
+
});
|
|
295
442
|
}
|
|
443
|
+
}, ...(ngDevMode ? [{ debugName: "#datasourceEffect" }] : []));
|
|
444
|
+
/** Initialize direction change listener */
|
|
445
|
+
this.#initDirectionListener = afterNextRender(() => {
|
|
446
|
+
this.platformService.directionChange
|
|
447
|
+
.pipe(map((event) => event.data === 'rtl'), takeUntilDestroyed(this.destroyRef))
|
|
448
|
+
.subscribe((isRtl) => this.isRtl.set(isRtl));
|
|
296
449
|
});
|
|
297
|
-
return selectedNodes;
|
|
298
450
|
}
|
|
451
|
+
// ==================== Effects ====================
|
|
452
|
+
/** Effect to handle datasource changes */
|
|
453
|
+
#datasourceEffect;
|
|
454
|
+
/** Initialize direction change listener */
|
|
455
|
+
#initDirectionListener;
|
|
456
|
+
// ==================== Public API ====================
|
|
299
457
|
/**
|
|
300
|
-
*
|
|
301
|
-
* find for emit Selections single mode
|
|
302
|
-
*
|
|
458
|
+
* Expand all nodes in the tree (with lazy loading support)
|
|
303
459
|
*/
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (child?.[this.childrenField()]?.length) {
|
|
308
|
-
this.handleUnSelectNode(child[this.childrenField()]);
|
|
309
|
-
}
|
|
310
|
-
});
|
|
460
|
+
async expandAll() {
|
|
461
|
+
await this.treeService.setExpandedState(this.nodes(), true, this.isLazyDataSource(), (node) => this.loadNodeChildren(node));
|
|
462
|
+
this.refreshNodes();
|
|
311
463
|
}
|
|
312
464
|
/**
|
|
313
|
-
*
|
|
314
|
-
* lazy load logic
|
|
315
|
-
*
|
|
465
|
+
* Collapse all nodes in the tree
|
|
316
466
|
*/
|
|
317
|
-
|
|
318
|
-
this.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
467
|
+
collapseAll() {
|
|
468
|
+
this.treeService.setExpandedState(this.nodes(), false, this.isLazyDataSource(), (node) => this.loadNodeChildren(node));
|
|
469
|
+
this.refreshNodes();
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Get count of selected nodes
|
|
473
|
+
*/
|
|
474
|
+
getSelectedCount() {
|
|
475
|
+
return this.treeService.countSelected(this.nodes());
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Check if any nodes are selected
|
|
479
|
+
*/
|
|
480
|
+
hasSelection() {
|
|
481
|
+
return this.getSelectedCount() > 0;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Get all selected nodes
|
|
485
|
+
*/
|
|
486
|
+
getSelectedNodes() {
|
|
487
|
+
const selected = [];
|
|
488
|
+
this.treeService.collectSelected(this.nodes(), selected);
|
|
489
|
+
return selected;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Delete selected nodes from the tree
|
|
493
|
+
*/
|
|
494
|
+
deleteSelected() {
|
|
495
|
+
this.treeService.removeSelected(this.nodes());
|
|
496
|
+
this.treeService.updateAllParentStates(this.nodes(), this.intermediateState());
|
|
497
|
+
this.refreshNodes();
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Select all nodes in the tree
|
|
501
|
+
*/
|
|
502
|
+
selectAll() {
|
|
503
|
+
this.treeService.setAllSelection(this.nodes(), true);
|
|
504
|
+
this.refreshNodes();
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Deselect all nodes in the tree
|
|
508
|
+
*/
|
|
509
|
+
deselectAll() {
|
|
510
|
+
this.treeService.setAllSelection(this.nodes(), false);
|
|
511
|
+
this.refreshNodes();
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Find a node by ID in the tree
|
|
515
|
+
*/
|
|
516
|
+
findNode(id) {
|
|
517
|
+
return this.treeService.findNodeById(this.nodes(), id);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Refresh the tree to trigger change detection
|
|
521
|
+
*/
|
|
522
|
+
refresh() {
|
|
523
|
+
this.refreshNodes();
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Check if a node is currently loading
|
|
527
|
+
*/
|
|
528
|
+
isNodeLoading(nodeId) {
|
|
529
|
+
return this.loadingNodes().has(nodeId);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Get template context for a node
|
|
533
|
+
*/
|
|
534
|
+
getTemplateContext(node, level = 0) {
|
|
535
|
+
return {
|
|
536
|
+
$implicit: node,
|
|
537
|
+
node,
|
|
538
|
+
level,
|
|
539
|
+
expanded: node.expanded ?? false,
|
|
540
|
+
childrenCount: node.childrenCount ?? node.children?.length ?? 0,
|
|
541
|
+
loading: node.loading ?? false,
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Calculate padding-inline for a node based on its level
|
|
546
|
+
*/
|
|
547
|
+
getNodePaddingInline(level) {
|
|
548
|
+
const indent = this.indentSize();
|
|
549
|
+
const currentLook = this.look();
|
|
550
|
+
const multiplier = currentLook === 'with-line' ? 1 / 3 : 1;
|
|
551
|
+
return level * indent * multiplier;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Check if node should show expand toggle
|
|
555
|
+
*/
|
|
556
|
+
shouldShowExpandToggle(node) {
|
|
557
|
+
return this.treeService.hasChildren(node) || this.treeService.canLazyLoad(node, this.isLazyDataSource());
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Check if checkboxes should be shown (only for multiple mode)
|
|
561
|
+
*/
|
|
562
|
+
shouldShowCheckbox() {
|
|
563
|
+
return this.selectMode() === 'multiple' && this.showCheckbox();
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Generate unique list ID for each node
|
|
567
|
+
*/
|
|
568
|
+
getListId(node) {
|
|
569
|
+
return this.treeService.getListId(node);
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Check if a node is currently focused
|
|
573
|
+
*/
|
|
574
|
+
isNodeFocused(nodeId) {
|
|
575
|
+
return this.focusedNodeId() === nodeId;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Get ARIA level for a node
|
|
579
|
+
*/
|
|
580
|
+
getNodeAriaLevel(level) {
|
|
581
|
+
return level + 1;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Get ARIA expanded state for a node
|
|
585
|
+
*/
|
|
586
|
+
getNodeAriaExpanded(node) {
|
|
587
|
+
if (!this.shouldShowExpandToggle(node)) {
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
return node.expanded ? 'true' : 'false';
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Get ARIA selected state for a node
|
|
594
|
+
*/
|
|
595
|
+
getNodeAriaSelected(node) {
|
|
596
|
+
if (this.selectMode() === 'single') {
|
|
597
|
+
return node.selected ? 'true' : 'false';
|
|
598
|
+
}
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
// ==================== Event Handlers ====================
|
|
602
|
+
/**
|
|
603
|
+
* Handle node click - for single selection mode or multiple mode with checkOnClick enabled
|
|
604
|
+
*/
|
|
605
|
+
onNodeClick(node, event) {
|
|
606
|
+
if (node.disabled)
|
|
607
|
+
return;
|
|
608
|
+
const mode = this.selectMode();
|
|
609
|
+
const shouldCheckOnClick = this.checkOnClick();
|
|
610
|
+
if (mode === 'single') {
|
|
611
|
+
this.handleSingleSelection(node, event);
|
|
612
|
+
}
|
|
613
|
+
else if (mode === 'multiple' && shouldCheckOnClick) {
|
|
614
|
+
this.handleMultipleSelection(node, event);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Toggle node expansion state with lazy loading support
|
|
619
|
+
*/
|
|
620
|
+
async toggleNode(node, event) {
|
|
621
|
+
if (node.disabled)
|
|
622
|
+
return;
|
|
623
|
+
if (this.isEvent(event) && typeof event.stopPropagation === 'function') {
|
|
624
|
+
event.stopPropagation();
|
|
625
|
+
}
|
|
626
|
+
const hasChildren = this.treeService.hasChildren(node);
|
|
627
|
+
const canLazyLoad = this.treeService.canLazyLoad(node, this.isLazyDataSource());
|
|
628
|
+
if (hasChildren || canLazyLoad) {
|
|
629
|
+
const willExpand = !node.expanded;
|
|
630
|
+
if (willExpand && canLazyLoad) {
|
|
631
|
+
await this.loadNodeChildren(node);
|
|
331
632
|
}
|
|
332
|
-
|
|
333
|
-
.
|
|
334
|
-
this.
|
|
633
|
+
node.expanded = willExpand;
|
|
634
|
+
this.refreshNodes();
|
|
635
|
+
this.onNodeToggle.emit({ component: this, node, nativeEvent: event });
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Toggle node selection state with indeterminate support (for multiple mode)
|
|
640
|
+
*/
|
|
641
|
+
toggleSelection(node, event) {
|
|
642
|
+
if (!event.isUserInteraction)
|
|
643
|
+
return;
|
|
644
|
+
const mode = this.selectMode();
|
|
645
|
+
if (mode !== 'multiple')
|
|
646
|
+
return;
|
|
647
|
+
const newValue = event.value === null ? true : event.value;
|
|
648
|
+
node.selected = newValue;
|
|
649
|
+
node.indeterminate = false;
|
|
650
|
+
if (this.checkChildrenOnSelect() && node.children) {
|
|
651
|
+
this.treeService.selectAllChildren(node.children, newValue);
|
|
652
|
+
}
|
|
653
|
+
if (this.intermediateState()) {
|
|
654
|
+
this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
|
|
655
|
+
}
|
|
656
|
+
this.refreshNodes();
|
|
657
|
+
this.onNodeSelect.emit({
|
|
658
|
+
component: this,
|
|
659
|
+
node,
|
|
660
|
+
isUserInteraction: event.isUserInteraction,
|
|
335
661
|
});
|
|
336
662
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
663
|
+
/**
|
|
664
|
+
* Handle drop events for tree nodes
|
|
665
|
+
*/
|
|
666
|
+
onDrop(event, parentNode) {
|
|
667
|
+
const targetArray = parentNode?.children ?? this.nodes();
|
|
668
|
+
const isReordering = event.previousContainer === event.container;
|
|
669
|
+
if (isReordering) {
|
|
670
|
+
this.handleReorder(event, targetArray, parentNode);
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
this.handleMove(event, targetArray, parentNode);
|
|
674
|
+
}
|
|
675
|
+
this.refreshNodes();
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Handle drop events when dropping directly onto a node (to make it a child)
|
|
679
|
+
*/
|
|
680
|
+
onDropOntoNode(event, targetNode) {
|
|
681
|
+
if (!this.canMoveToParent())
|
|
682
|
+
return;
|
|
683
|
+
const sourceListId = event.previousContainer.element.id;
|
|
684
|
+
const sourceArray = this.getArrayByListId(sourceListId);
|
|
685
|
+
if (!sourceArray)
|
|
686
|
+
return;
|
|
687
|
+
const movedNode = sourceArray[event.previousIndex];
|
|
688
|
+
if (!this.treeService.isValidDropTarget(movedNode, targetNode))
|
|
689
|
+
return;
|
|
690
|
+
if (!this.emitBeforeDropEvent(movedNode, sourceListId, targetNode, event.previousIndex, 0))
|
|
691
|
+
return;
|
|
692
|
+
targetNode.children ??= [];
|
|
693
|
+
sourceArray.splice(event.previousIndex, 1);
|
|
694
|
+
targetNode.children.unshift(movedNode);
|
|
695
|
+
targetNode.expanded = true;
|
|
696
|
+
this.emitDropEvents(movedNode, this.findParentByListId(sourceListId), targetNode, event.previousIndex, 0, false);
|
|
697
|
+
this.refreshNodes();
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Handle node focus event
|
|
701
|
+
*/
|
|
702
|
+
onNodeFocus(nodeId) {
|
|
703
|
+
this.focusedNodeId.set(nodeId);
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Handle tree container focus - focus first node if none is focused
|
|
707
|
+
*/
|
|
708
|
+
onTreeFocus(event) {
|
|
709
|
+
if (event.target === event.currentTarget) {
|
|
710
|
+
afterNextRender(() => {
|
|
711
|
+
const flatList = this.treeService.buildFlatNodeList(this.nodes());
|
|
712
|
+
if (flatList.length > 0) {
|
|
713
|
+
const focusedId = this.focusedNodeId();
|
|
714
|
+
if (focusedId) {
|
|
715
|
+
this.focusNodeById(focusedId);
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
this.focusNodeById(flatList[0].node.id);
|
|
343
719
|
}
|
|
344
|
-
element[this.childrenField()] = _children;
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
if (element?.[this.childrenField()])
|
|
348
|
-
this.findNode(parentId, _children, element[this.childrenField()]);
|
|
349
720
|
}
|
|
350
721
|
});
|
|
351
722
|
}
|
|
352
723
|
}
|
|
353
724
|
/**
|
|
354
|
-
*
|
|
355
|
-
|
|
356
|
-
|
|
725
|
+
* Handle tree container blur
|
|
726
|
+
*/
|
|
727
|
+
onTreeBlur(event) {
|
|
728
|
+
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) {
|
|
729
|
+
this.focusedNodeId.set(null);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Handle keyboard navigation
|
|
734
|
+
*/
|
|
735
|
+
handleKeyDown(event) {
|
|
736
|
+
const flatList = this.treeService.buildFlatNodeList(this.nodes());
|
|
737
|
+
if (flatList.length === 0)
|
|
738
|
+
return;
|
|
739
|
+
const currentFocused = this.getFocusedNode();
|
|
740
|
+
let currentIndex = currentFocused ? flatList.findIndex((item) => item.node.id === currentFocused.id) : -1;
|
|
741
|
+
if (currentIndex === -1 && event.target === event.currentTarget) {
|
|
742
|
+
currentIndex = 0;
|
|
743
|
+
}
|
|
744
|
+
const navigationResult = this.handleNavigationKey(event, flatList, currentIndex, currentFocused);
|
|
745
|
+
if (navigationResult.handled) {
|
|
746
|
+
if (navigationResult.shouldPreventDefault) {
|
|
747
|
+
event.preventDefault();
|
|
748
|
+
event.stopPropagation();
|
|
749
|
+
}
|
|
750
|
+
if (navigationResult.targetIndex !== null &&
|
|
751
|
+
navigationResult.targetIndex >= 0 &&
|
|
752
|
+
navigationResult.targetIndex < flatList.length) {
|
|
753
|
+
this.focusNodeById(flatList[navigationResult.targetIndex].node.id);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
// ==================== Private Methods ====================
|
|
758
|
+
/**
|
|
759
|
+
* Load root items when datasource is a function
|
|
760
|
+
*/
|
|
761
|
+
async loadRootItems(loadFn) {
|
|
762
|
+
try {
|
|
763
|
+
const result = loadFn();
|
|
764
|
+
const rootNodes = result instanceof Promise ? await result : result;
|
|
765
|
+
this.nodes.set(rootNodes);
|
|
766
|
+
}
|
|
767
|
+
catch (error) {
|
|
768
|
+
this.handleError('Failed to load root items', error);
|
|
769
|
+
this.nodes.set([]);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Load children for a node using lazy loading
|
|
774
|
+
*/
|
|
775
|
+
async loadNodeChildren(node) {
|
|
776
|
+
if (!this.isLazyDataSource() || node.loading)
|
|
777
|
+
return;
|
|
778
|
+
if (this.treeService.hasChildren(node) || !node.childrenCount || node.childrenCount === 0) {
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
const ds = this.datasource();
|
|
782
|
+
if (typeof ds !== 'function')
|
|
783
|
+
return;
|
|
784
|
+
try {
|
|
785
|
+
node.loading = true;
|
|
786
|
+
this.loadingNodes.update((set) => new Set(set).add(node.id));
|
|
787
|
+
this.refreshNodes();
|
|
788
|
+
const result = ds(node.id);
|
|
789
|
+
const children = result instanceof Promise ? await result : result;
|
|
790
|
+
node.children = children;
|
|
791
|
+
node.childrenCount = children.length;
|
|
792
|
+
}
|
|
793
|
+
catch (error) {
|
|
794
|
+
this.handleError('Failed to load children', error);
|
|
795
|
+
node.childrenCount = 0;
|
|
796
|
+
}
|
|
797
|
+
finally {
|
|
798
|
+
node.loading = false;
|
|
799
|
+
this.loadingNodes.update((set) => {
|
|
800
|
+
const newSet = new Set(set);
|
|
801
|
+
newSet.delete(node.id);
|
|
802
|
+
return newSet;
|
|
803
|
+
});
|
|
804
|
+
this.refreshNodes();
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Internal method to refresh nodes signal and sync back to datasource if it's an array
|
|
809
|
+
* Creates new array references for all nested children to ensure reactivity
|
|
357
810
|
*/
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
811
|
+
refreshNodes() {
|
|
812
|
+
const currentNodes = this.nodes();
|
|
813
|
+
// Create new array references for all nested children to ensure reactivity
|
|
814
|
+
// This ensures Angular's change detection picks up changes even with callback datasource
|
|
815
|
+
this.ensureNewArrayReferences(currentNodes);
|
|
816
|
+
this.nodes.set([...currentNodes]);
|
|
817
|
+
if (!this.isLazyDataSource() && !this.isUpdatingFromDatasource) {
|
|
818
|
+
this.isUpdatingFromDatasource = true;
|
|
819
|
+
this.datasource.set([...currentNodes]);
|
|
820
|
+
setTimeout(() => {
|
|
821
|
+
this.isUpdatingFromDatasource = false;
|
|
822
|
+
}, 0);
|
|
363
823
|
}
|
|
364
|
-
this.onCollapsedChanged.emit({ component: this, data: node.data, nativeElement: this.nativeElement });
|
|
365
824
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
825
|
+
/**
|
|
826
|
+
* Recursively ensure all children arrays have new references to trigger change detection
|
|
827
|
+
* Mutates the tree structure by replacing children arrays with new array references
|
|
828
|
+
*/
|
|
829
|
+
ensureNewArrayReferences(nodes) {
|
|
830
|
+
for (const node of nodes) {
|
|
831
|
+
if (node.children && node.children.length > 0) {
|
|
832
|
+
// Create new array reference for children
|
|
833
|
+
node.children = [...node.children];
|
|
834
|
+
// Recursively process nested children
|
|
835
|
+
this.ensureNewArrayReferences(node.children);
|
|
371
836
|
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Handle single selection mode
|
|
841
|
+
*/
|
|
842
|
+
handleSingleSelection(node, event) {
|
|
843
|
+
this.treeService.deselectAllNodes(this.nodes());
|
|
844
|
+
node.selected = true;
|
|
845
|
+
node.indeterminate = false;
|
|
846
|
+
this.refreshNodes();
|
|
847
|
+
this.onNodeSelect.emit({
|
|
848
|
+
component: this,
|
|
849
|
+
node,
|
|
850
|
+
nativeEvent: event,
|
|
851
|
+
isUserInteraction: true,
|
|
372
852
|
});
|
|
373
853
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
854
|
+
/**
|
|
855
|
+
* Handle multiple selection mode with checkOnClick
|
|
856
|
+
*/
|
|
857
|
+
handleMultipleSelection(node, event) {
|
|
858
|
+
const newValue = !node.selected;
|
|
859
|
+
node.selected = newValue;
|
|
860
|
+
node.indeterminate = false;
|
|
861
|
+
if (this.checkChildrenOnSelect() && node.children) {
|
|
862
|
+
this.treeService.selectAllChildren(node.children, newValue);
|
|
863
|
+
}
|
|
864
|
+
if (this.intermediateState()) {
|
|
865
|
+
this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
|
|
866
|
+
}
|
|
867
|
+
this.refreshNodes();
|
|
868
|
+
this.onNodeSelect.emit({
|
|
869
|
+
component: this,
|
|
870
|
+
node,
|
|
871
|
+
nativeEvent: event,
|
|
872
|
+
isUserInteraction: true,
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Get array reference by drop list ID
|
|
877
|
+
*/
|
|
878
|
+
getArrayByListId(listId) {
|
|
879
|
+
return this.treeService.getArrayByListId(this.nodes(), listId);
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Find parent node by list ID
|
|
883
|
+
*/
|
|
884
|
+
findParentByListId(listId) {
|
|
885
|
+
return this.treeService.findParentByListId(this.nodes(), listId);
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Check if move operation is allowed based on dragOperationType
|
|
889
|
+
*/
|
|
890
|
+
canMoveToParent() {
|
|
891
|
+
return this.dragOperationType() !== 'order-only';
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Check if reorder operation is allowed based on dragOperationType
|
|
895
|
+
*/
|
|
896
|
+
canReorder() {
|
|
897
|
+
return this.dragOperationType() !== 'move';
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Handle reordering within the same list */
|
|
901
|
+
handleReorder(event, targetArray, parentNode) {
|
|
902
|
+
if (!this.canReorder())
|
|
903
|
+
return;
|
|
904
|
+
const movedNode = targetArray[event.previousIndex];
|
|
905
|
+
moveItemInArray(targetArray, event.previousIndex, event.currentIndex);
|
|
906
|
+
this.emitDropEvents(movedNode, parentNode, parentNode, event.previousIndex, event.currentIndex, true);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Handle moving between different lists
|
|
910
|
+
*/
|
|
911
|
+
handleMove(event, targetArray, parentNode) {
|
|
912
|
+
if (!this.canMoveToParent())
|
|
913
|
+
return;
|
|
914
|
+
const sourceListId = event.previousContainer.element.id;
|
|
915
|
+
const sourceArray = this.getArrayByListId(sourceListId);
|
|
916
|
+
if (!sourceArray)
|
|
917
|
+
return;
|
|
918
|
+
const movedNode = sourceArray[event.previousIndex];
|
|
919
|
+
if (parentNode && !this.treeService.isValidDropTarget(movedNode, parentNode))
|
|
920
|
+
return;
|
|
921
|
+
if (!this.emitBeforeDropEvent(movedNode, sourceListId, parentNode, event.previousIndex, event.currentIndex)) {
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
transferArrayItem(sourceArray, targetArray, event.previousIndex, event.currentIndex);
|
|
925
|
+
this.emitDropEvents(movedNode, this.findParentByListId(sourceListId), parentNode, event.previousIndex, event.currentIndex, false);
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Emit beforeDrop event and return whether to continue
|
|
929
|
+
*/
|
|
930
|
+
emitBeforeDropEvent(movedNode, sourceListId, currentParent, previousIndex, currentIndex) {
|
|
931
|
+
const beforeDropEvent = {
|
|
932
|
+
component: this,
|
|
933
|
+
movedNode,
|
|
934
|
+
previousParent: this.findParentByListId(sourceListId),
|
|
935
|
+
currentParent,
|
|
936
|
+
previousIndex,
|
|
937
|
+
currentIndex,
|
|
938
|
+
canceled: false,
|
|
939
|
+
};
|
|
940
|
+
this.onBeforeDrop.emit(beforeDropEvent);
|
|
941
|
+
return !beforeDropEvent.canceled;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Emit drop events based on operation type
|
|
945
|
+
*/
|
|
946
|
+
emitDropEvents(node, previousParent, currentParent, previousIndex, currentIndex, isReorder) {
|
|
947
|
+
const dropEvent = {
|
|
948
|
+
component: this,
|
|
949
|
+
node,
|
|
950
|
+
previousParent,
|
|
951
|
+
currentParent,
|
|
952
|
+
previousIndex,
|
|
953
|
+
currentIndex,
|
|
954
|
+
};
|
|
955
|
+
if (isReorder) {
|
|
956
|
+
this.onOrderChange.emit(dropEvent);
|
|
957
|
+
}
|
|
958
|
+
else {
|
|
959
|
+
this.onMoveChange.emit(dropEvent);
|
|
960
|
+
}
|
|
961
|
+
this.onItemsChange.emit(dropEvent);
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Get the currently focused node
|
|
965
|
+
*/
|
|
966
|
+
getFocusedNode() {
|
|
967
|
+
const focusedId = this.focusedNodeId();
|
|
968
|
+
if (!focusedId)
|
|
969
|
+
return null;
|
|
970
|
+
return this.treeService.findNodeById(this.nodes(), focusedId);
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Set focus to a node by ID
|
|
974
|
+
*/
|
|
975
|
+
focusNodeById(nodeId) {
|
|
976
|
+
this.focusedNodeId.set(nodeId);
|
|
977
|
+
setTimeout(() => {
|
|
978
|
+
const element = document.querySelector(`[data-tree-node-id="${nodeId}"]`);
|
|
979
|
+
if (element) {
|
|
980
|
+
element.focus();
|
|
981
|
+
element.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
982
|
+
}
|
|
983
|
+
}, 0);
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Handle keyboard navigation keys
|
|
987
|
+
*/
|
|
988
|
+
handleNavigationKey(event, flatList, currentIndex, currentFocused) {
|
|
989
|
+
let targetIndex = currentIndex;
|
|
990
|
+
let shouldPreventDefault = true;
|
|
991
|
+
let handled = true;
|
|
992
|
+
switch (event.key) {
|
|
993
|
+
case 'ArrowUp':
|
|
994
|
+
if (currentIndex > 0) {
|
|
995
|
+
targetIndex = currentIndex - 1;
|
|
996
|
+
}
|
|
997
|
+
else {
|
|
998
|
+
shouldPreventDefault = false;
|
|
999
|
+
}
|
|
387
1000
|
break;
|
|
388
|
-
case '
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
1001
|
+
case 'ArrowDown':
|
|
1002
|
+
if (currentIndex < flatList.length - 1) {
|
|
1003
|
+
targetIndex = currentIndex + 1;
|
|
1004
|
+
}
|
|
1005
|
+
else if (currentIndex === -1) {
|
|
1006
|
+
targetIndex = 0;
|
|
1007
|
+
}
|
|
1008
|
+
else {
|
|
1009
|
+
shouldPreventDefault = false;
|
|
392
1010
|
}
|
|
393
1011
|
break;
|
|
394
|
-
case '
|
|
395
|
-
|
|
1012
|
+
case 'ArrowLeft':
|
|
1013
|
+
if (currentFocused) {
|
|
1014
|
+
if (currentFocused.expanded && this.shouldShowExpandToggle(currentFocused)) {
|
|
1015
|
+
this.toggleNode(currentFocused, event);
|
|
1016
|
+
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1017
|
+
}
|
|
1018
|
+
else {
|
|
1019
|
+
const currentItem = flatList[currentIndex];
|
|
1020
|
+
if (currentItem?.parent) {
|
|
1021
|
+
targetIndex = flatList.findIndex((item) => item.node.id === currentItem.parent.id);
|
|
1022
|
+
}
|
|
1023
|
+
else {
|
|
1024
|
+
shouldPreventDefault = false;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
else {
|
|
1029
|
+
shouldPreventDefault = false;
|
|
1030
|
+
}
|
|
396
1031
|
break;
|
|
397
|
-
case '
|
|
398
|
-
|
|
1032
|
+
case 'ArrowRight':
|
|
1033
|
+
if (currentFocused) {
|
|
1034
|
+
if (!currentFocused.expanded && this.shouldShowExpandToggle(currentFocused)) {
|
|
1035
|
+
this.toggleNode(currentFocused, event);
|
|
1036
|
+
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1037
|
+
}
|
|
1038
|
+
else if (currentFocused.expanded &&
|
|
1039
|
+
this.treeService.hasChildren(currentFocused) &&
|
|
1040
|
+
currentFocused.children) {
|
|
1041
|
+
const children = currentFocused.children;
|
|
1042
|
+
if (children.length > 0) {
|
|
1043
|
+
const firstChild = children[0];
|
|
1044
|
+
targetIndex = flatList.findIndex((item) => item.node.id === firstChild.id);
|
|
1045
|
+
if (targetIndex === -1) {
|
|
1046
|
+
const updatedFlatList = this.treeService.buildFlatNodeList(this.nodes());
|
|
1047
|
+
targetIndex = updatedFlatList.findIndex((item) => item.node.id === firstChild.id);
|
|
1048
|
+
if (targetIndex >= 0 && targetIndex < updatedFlatList.length) {
|
|
1049
|
+
this.focusNodeById(updatedFlatList[targetIndex].node.id);
|
|
1050
|
+
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
else {
|
|
1055
|
+
shouldPreventDefault = false;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
shouldPreventDefault = false;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
else {
|
|
1063
|
+
shouldPreventDefault = false;
|
|
1064
|
+
}
|
|
1065
|
+
break;
|
|
1066
|
+
case 'Home':
|
|
1067
|
+
targetIndex = 0;
|
|
1068
|
+
break;
|
|
1069
|
+
case 'End':
|
|
1070
|
+
targetIndex = flatList.length - 1;
|
|
1071
|
+
break;
|
|
1072
|
+
case ' ':
|
|
1073
|
+
case 'Space':
|
|
1074
|
+
if (currentFocused && this.selectMode() === 'multiple') {
|
|
1075
|
+
event.preventDefault();
|
|
1076
|
+
this.handleSpaceKeySelection(currentFocused, event);
|
|
1077
|
+
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1078
|
+
}
|
|
1079
|
+
shouldPreventDefault = false;
|
|
1080
|
+
break;
|
|
1081
|
+
case 'Enter':
|
|
1082
|
+
if (currentFocused) {
|
|
1083
|
+
event.preventDefault();
|
|
1084
|
+
this.handleEnterKeySelection(currentFocused, event);
|
|
1085
|
+
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1086
|
+
}
|
|
1087
|
+
shouldPreventDefault = false;
|
|
399
1088
|
break;
|
|
400
1089
|
default:
|
|
1090
|
+
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
|
|
1091
|
+
if (currentFocused && this.selectMode() === 'multiple') {
|
|
1092
|
+
event.preventDefault();
|
|
1093
|
+
this.handleCtrlEnterSelection(currentFocused, event);
|
|
1094
|
+
return { handled: true, shouldPreventDefault: true, targetIndex: null };
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
handled = false;
|
|
1098
|
+
shouldPreventDefault = false;
|
|
401
1099
|
break;
|
|
402
1100
|
}
|
|
403
|
-
|
|
404
|
-
*
|
|
405
|
-
* for detect changes treeviewitem
|
|
406
|
-
*
|
|
407
|
-
*/
|
|
408
|
-
this.executorChanges.set(operation);
|
|
1101
|
+
return { handled, shouldPreventDefault, targetIndex };
|
|
409
1102
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
1103
|
+
/**
|
|
1104
|
+
* Handle Space key selection
|
|
1105
|
+
*/
|
|
1106
|
+
handleSpaceKeySelection(node, event) {
|
|
1107
|
+
const newValue = !node.selected;
|
|
1108
|
+
node.selected = newValue;
|
|
1109
|
+
node.indeterminate = false;
|
|
1110
|
+
if (this.checkChildrenOnSelect() && node.children) {
|
|
1111
|
+
this.treeService.selectAllChildren(node.children, newValue);
|
|
413
1112
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
const nodesToExpand = [];
|
|
417
|
-
for (const valueField of valueFields) {
|
|
418
|
-
const foundNodes = this.findNodesByValueField(this.itemsSignal(), valueField);
|
|
419
|
-
nodesToExpand.push(...foundNodes);
|
|
1113
|
+
if (this.intermediateState()) {
|
|
1114
|
+
this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
|
|
420
1115
|
}
|
|
421
|
-
|
|
1116
|
+
this.refreshNodes();
|
|
1117
|
+
this.onNodeSelect.emit({
|
|
1118
|
+
component: this,
|
|
1119
|
+
node,
|
|
1120
|
+
nativeEvent: event,
|
|
1121
|
+
isUserInteraction: true,
|
|
1122
|
+
});
|
|
422
1123
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
1124
|
+
/**
|
|
1125
|
+
* Handle Enter key selection
|
|
1126
|
+
*/
|
|
1127
|
+
handleEnterKeySelection(node, event) {
|
|
1128
|
+
const mode = this.selectMode();
|
|
1129
|
+
this.treeService.deselectAllNodes(this.nodes());
|
|
1130
|
+
node.selected = true;
|
|
1131
|
+
node.indeterminate = false;
|
|
1132
|
+
if (mode === 'multiple') {
|
|
1133
|
+
if (this.checkChildrenOnSelect() && node.children) {
|
|
1134
|
+
this.treeService.selectAllChildren(node.children, true);
|
|
428
1135
|
}
|
|
429
|
-
if (
|
|
430
|
-
|
|
1136
|
+
if (this.intermediateState()) {
|
|
1137
|
+
this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
|
|
431
1138
|
}
|
|
432
1139
|
}
|
|
433
|
-
|
|
1140
|
+
this.refreshNodes();
|
|
1141
|
+
this.onNodeSelect.emit({
|
|
1142
|
+
component: this,
|
|
1143
|
+
node,
|
|
1144
|
+
nativeEvent: event,
|
|
1145
|
+
isUserInteraction: true,
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Handle Ctrl/Cmd + Enter key selection
|
|
1150
|
+
*/
|
|
1151
|
+
handleCtrlEnterSelection(node, event) {
|
|
1152
|
+
const newValue = !node.selected;
|
|
1153
|
+
node.selected = newValue;
|
|
1154
|
+
node.indeterminate = false;
|
|
1155
|
+
if (this.checkChildrenOnSelect() && node.children) {
|
|
1156
|
+
this.treeService.selectAllChildren(node.children, newValue);
|
|
1157
|
+
}
|
|
1158
|
+
if (this.intermediateState()) {
|
|
1159
|
+
this.treeService.updateParentStates(this.nodes(), node, this.intermediateState());
|
|
1160
|
+
}
|
|
1161
|
+
this.refreshNodes();
|
|
1162
|
+
this.onNodeSelect.emit({
|
|
1163
|
+
component: this,
|
|
1164
|
+
node,
|
|
1165
|
+
nativeEvent: event,
|
|
1166
|
+
isUserInteraction: true,
|
|
1167
|
+
});
|
|
434
1168
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
1169
|
+
/**
|
|
1170
|
+
* Type guard to check if value is an Event
|
|
1171
|
+
*/
|
|
1172
|
+
isEvent(value) {
|
|
1173
|
+
return value instanceof Event;
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Handle errors consistently
|
|
1177
|
+
*/
|
|
1178
|
+
handleError(message, error) {
|
|
1179
|
+
if (error instanceof Error) {
|
|
1180
|
+
console.error(`${message}:`, error.message);
|
|
440
1181
|
}
|
|
441
|
-
|
|
442
|
-
|
|
1182
|
+
else {
|
|
1183
|
+
console.error(`${message}:`, error);
|
|
443
1184
|
}
|
|
444
1185
|
}
|
|
445
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewComponent, deps:
|
|
446
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: AXTreeViewComponent, isStandalone: true, selector: "ax-tree-view", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, hasCheckboxField: { classPropertyName: "hasCheckboxField", publicName: "hasCheckboxField", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selectionBehavior: { classPropertyName: "selectionBehavior", publicName: "selectionBehavior", isSignal: true, isRequired: false, transformFunction: null }, selectionScope: { classPropertyName: "selectionScope", publicName: "selectionScope", isSignal: true, isRequired: false, transformFunction: null }, focusNodeEnabled: { classPropertyName: "focusNodeEnabled", publicName: "focusNodeEnabled", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, visibleField: { classPropertyName: "visibleField", publicName: "visibleField", isSignal: true, isRequired: false, transformFunction: null }, disableField: { classPropertyName: "disableField", publicName: "disableField", isSignal: true, isRequired: false, transformFunction: null }, hasChildField: { classPropertyName: "hasChildField", publicName: "hasChildField", isSignal: true, isRequired: false, transformFunction: null }, selectedField: { classPropertyName: "selectedField", publicName: "selectedField", isSignal: true, isRequired: false, transformFunction: null }, expandedField: { classPropertyName: "expandedField", publicName: "expandedField", isSignal: true, isRequired: false, transformFunction: null }, tooltipField: { classPropertyName: "tooltipField", publicName: "tooltipField", isSignal: true, isRequired: false, transformFunction: null }, childrenField: { classPropertyName: "childrenField", publicName: "childrenField", isSignal: true, isRequired: false, transformFunction: null }, activeField: { classPropertyName: "activeField", publicName: "activeField", isSignal: true, isRequired: false, transformFunction: null }, indeterminateField: { classPropertyName: "indeterminateField", publicName: "indeterminateField", isSignal: true, isRequired: false, transformFunction: null }, parentField: { classPropertyName: "parentField", publicName: "parentField", isSignal: true, isRequired: false, transformFunction: null }, iconField: { classPropertyName: "iconField", publicName: "iconField", isSignal: true, isRequired: false, transformFunction: null }, toggleIcons: { classPropertyName: "toggleIcons", publicName: "toggleIcons", isSignal: true, isRequired: false, transformFunction: null }, look: { classPropertyName: "look", publicName: "look", isSignal: true, isRequired: false, transformFunction: null }, showEmptyNodeMassage: { classPropertyName: "showEmptyNodeMassage", publicName: "showEmptyNodeMassage", isSignal: true, isRequired: false, transformFunction: null }, itemTemplate: { classPropertyName: "itemTemplate", publicName: "itemTemplate", isSignal: false, isRequired: false, transformFunction: null }, emptyTemplate: { classPropertyName: "emptyTemplate", publicName: "emptyTemplate", isSignal: false, isRequired: false, transformFunction: null }, expandOn: { classPropertyName: "expandOn", publicName: "expandOn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelectionChanged: "onSelectionChanged", onItemSelectedChanged: "onItemSelectedChanged", onNodeClick: "onNodeClick", onCollapsedChanged: "onCollapsedChanged", onNodedbClick: "onNodedbClick" }, host: { properties: { "class": "this.__hostClass" } }, providers: [
|
|
447
|
-
{ provide: AXComponent, useExisting: AXTreeViewComponent },
|
|
448
|
-
{ provide: AXTreeViewBase, useExisting: AXTreeViewComponent },
|
|
449
|
-
], usesInheritance: true, ngImport: i0, template: "@if (resolvedItems?.length) {\n @for (node of resolvedItems; track $index) {\n <ng-container [ngTemplateOutlet]=\"recursion\" [ngTemplateOutletContext]=\"{ $implicit: node }\"></ng-container>\n }\n} @else {\n <ng-container [ngTemplateOutlet]=\"emptyTemplate || empty\"></ng-container>\n}\n\n<ng-template #recursion let-item>\n @if (item[visibleField()] !== false) {\n <ax-tree-view-item\n [item]=\"item\"\n [isExpanded]=\"item[expandedField()]\"\n [(isActive)]=\"item[activeField()]\"\n [isLoading]=\"isNodeLoading(item[valueField()])\"\n [executorChanges]=\"executorChanges()\"\n >\n @if (\n (showCheckbox() && selectionScope() === 'all' && item[hasCheckboxField()] !== false) ||\n (showCheckbox() &&\n selectionScope() === 'parent' &&\n item[childrenField()]?.length &&\n item[hasCheckboxField()]) !== false ||\n (showCheckbox() &&\n selectionScope() === 'children' &&\n !item[childrenField()]?.length &&\n item[hasCheckboxField()] !== false)\n ) {\n <ax-check-box\n [disabled]=\"item[disableField()]\"\n [indeterminate]=\"item[indeterminateField()]\"\n [(ngModel)]=\"item[selectedField()]\"\n (onValueChanged)=\"handleNodeSelectionClick($event, item)\"\n ></ax-check-box>\n }\n @if (item[iconField()]) {\n <ax-prefix>\n <ax-icon [icon]=\"item[iconField()]\"></ax-icon>\n </ax-prefix>\n }\n @if (item[textField()]) {\n <ax-text>{{ item[textField()] }}</ax-text>\n }\n\n @for (child of item?.[childrenField()]; track $index) {\n <ng-container [ngTemplateOutlet]=\"recursion\" [ngTemplateOutletContext]=\"{ $implicit: child }\"></ng-container>\n }\n </ax-tree-view-item>\n }\n</ng-template>\n\n<ng-template #empty>\n {{ '@acorex:common.general.no-result-found' | translate | async }}\n</ng-template>\n", styles: ["ax-tree-view{--ax-comp-tree-view-arrow-size: .875rem;--ax-comp-tree-view-text-size: .875rem;--ax-comp-tree-view-active-bg-color: var(--ax-sys-color-primary-surface);--ax-comp-tree-view-active-text-color: var(--ax-sys-color-on-primary-surface);--ax-comp-tree-view-hover-bg-color: var(--ax-sys-color-dark-surface);--ax-comp-tree-view-indicator-size: 2px}ax-tree-view:has(>ax-tree-view-item i) ax-tree-view-item .ax-tree-view-container:not(:has(i)){padding-inline-start:1.5rem}ax-tree-view.ax-look-with-line ax-tree-view-item{position:relative}ax-tree-view.ax-look-with-line ax-tree-view-item:before{content:\"\";position:absolute;top:0;inset-inline-start:.625rem;width:1px;height:100%;background-color:#ccc}ax-tree-view.ax-look-with-line ax-tree-view-item .ax-tree-view-container{padding-inline-start:1.25rem}ax-tree-view.ax-look-with-line ax-tree-view-item .ax-tree-view-container .ax-tree-view-icon-container{padding:.25rem}ax-tree-view ax-tree-view-item ax-check-box{margin-inline-start:.5rem}ax-tree-view ax-tree-view-item .ax-tree-view-container{display:flex;align-items:center;margin-bottom:.125rem}ax-tree-view ax-tree-view-item .ax-tree-view-container ax-text{font-size:var(--ax-comp-tree-view-text-size)}ax-tree-view ax-tree-view-item .ax-tree-view-container{cursor:pointer}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-checkbox-end-side{display:none!important}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-arrow{font-size:var(--ax-comp-tree-view-arrow-size)!important}ax-tree-view ax-tree-view-item .ax-tree-view-container ax-suffix:empty{display:none}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-icon-container{width:1.5rem;height:1.5rem;display:flex;align-items:center;justify-content:center}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items{display:flex;align-items:center}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items .ax-tree-view-items-prefix{display:flex;align-items:center;gap:.5rem;padding:.25rem .5rem;border-radius:.25rem;overflow-x:auto;margin-inline-start:.25rem}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items .ax-tree-view-items-prefix.ax-noselect-tree-view{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items .ax-tree-view-items-prefix.ax-state-tree-view-active{background-color:rgba(var(--ax-comp-tree-view-active-bg-color));color:rgba(var(--ax-comp-tree-view-active-text-color))}ax-tree-view ax-tree-view-item .ax-tree-view-container .ax-tree-view-items .ax-tree-view-items-prefix:hover:not(.ax-state-tree-view-active){background-color:rgba(var(--ax-comp-tree-view-hover-bg-color))}ax-tree-view ax-tree-view-item .ax-tree-view-child{padding-inline-start:1rem}ax-tree-view ax-tree-view-item .ax-tree-view-child .ax-tree-view-container:not(:has(.ax-tree-view-icon-container i)){padding-inline-start:1.5rem}ax-tree-view ax-tree-view-item .ax-tree-view-child.ax-tree-view-empty-child{padding-inline-start:2rem}ax-tree-view ax-tree-view-item .ax-state-disabled{cursor:not-allowed!important;opacity:.5!important}@keyframes fadeSlideIn{0%{opacity:0;max-height:0}to{opacity:1;max-height:50px}}@keyframes fadeSlideOut{0%{opacity:1;max-height:50px}to{opacity:0;max-height:0}}ax-tree-view ax-tree-view-item .ax-fade-slide-in{animation:fadeSlideIn var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}ax-tree-view ax-tree-view-item .ax-fade-slide-out{animation:fadeSlideOut var(--ax-sys-transition-duration) var(--ax-sys-transition-timing-function)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: AXTreeViewItemComponent, selector: "ax-tree-view-item", inputs: ["item", "isExpanded", "isActive", "isLoading", "executorChanges"], outputs: ["isExpandedChange", "isActiveChange"] }, { kind: "component", type: AXCheckBoxComponent, selector: "ax-check-box", inputs: ["disabled", "tabIndex", "readonly", "color", "value", "name", "id", "isLoading", "indeterminate"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "component", type: AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
1186
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1187
|
+
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 }); }
|
|
450
1188
|
}
|
|
451
1189
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewComponent, decorators: [{
|
|
452
1190
|
type: Component,
|
|
453
|
-
args: [{ selector: 'ax-tree-view', encapsulation: ViewEncapsulation.None,
|
|
1191
|
+
args: [{ selector: 'ax-tree-view', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, providers: [AXTreeViewService], imports: [
|
|
1192
|
+
CommonModule,
|
|
1193
|
+
FormsModule,
|
|
1194
|
+
AXDragDirective,
|
|
1195
|
+
AXDragHandleDirective,
|
|
1196
|
+
AXDropListDirective,
|
|
1197
|
+
AXFocusTrapDirective,
|
|
454
1198
|
NgTemplateOutlet,
|
|
455
|
-
|
|
1199
|
+
AXButtonComponent,
|
|
456
1200
|
AXCheckBoxComponent,
|
|
457
|
-
|
|
458
|
-
AXDecoratorGenericComponent,
|
|
1201
|
+
AXBadgeComponent,
|
|
459
1202
|
AXDecoratorIconComponent,
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
1203
|
+
], host: {
|
|
1204
|
+
class: 'ax-tree-view',
|
|
1205
|
+
'[class.ax-tree-view-default]': "look() === 'default'",
|
|
1206
|
+
'[class.ax-tree-view-card]': "look() === 'card'",
|
|
1207
|
+
'[class.ax-tree-view-with-line]': "look() === 'with-line'",
|
|
1208
|
+
'[class.ax-tree-view-rtl]': 'isRtl',
|
|
1209
|
+
'[style.--ax-tree-view-indent-size]': "indentSize() + 'px'",
|
|
1210
|
+
'[style.--ax-tree-view-line-offset]': "(indentSize() / 2) + 'px'",
|
|
1211
|
+
role: 'tree',
|
|
1212
|
+
'[attr.aria-label]': '"Tree navigation"',
|
|
1213
|
+
'(keydown)': 'handleKeyDown($event)',
|
|
1214
|
+
'(focus)': 'onTreeFocus($event)',
|
|
1215
|
+
'(blur)': 'onTreeBlur($event)',
|
|
1216
|
+
tabindex: '0',
|
|
1217
|
+
}, 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"] }]
|
|
1218
|
+
}], 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"] }] } });
|
|
474
1219
|
|
|
475
|
-
const COMPONENT = [AXTreeViewComponent, AXTreeViewItemComponent];
|
|
476
|
-
const MODULES = [
|
|
477
|
-
CommonModule,
|
|
478
|
-
AXCommonModule,
|
|
479
|
-
AXDecoratorModule,
|
|
480
|
-
AXCheckBoxModule,
|
|
481
|
-
AXFormModule,
|
|
482
|
-
FormsModule,
|
|
483
|
-
AXTooltipModule,
|
|
484
|
-
AXLoadingModule,
|
|
485
|
-
];
|
|
486
1220
|
class AXTreeViewModule {
|
|
487
1221
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
488
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewModule, imports: [AXTreeViewComponent
|
|
489
|
-
|
|
490
|
-
AXDecoratorModule,
|
|
491
|
-
AXCheckBoxModule,
|
|
492
|
-
AXFormModule,
|
|
493
|
-
FormsModule,
|
|
494
|
-
AXTooltipModule,
|
|
495
|
-
AXLoadingModule], exports: [AXTreeViewComponent, AXTreeViewItemComponent] }); }
|
|
496
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewModule, imports: [COMPONENT, MODULES] }); }
|
|
1222
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewModule, imports: [AXTreeViewComponent] }); }
|
|
1223
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewModule, imports: [AXTreeViewComponent] }); }
|
|
497
1224
|
}
|
|
498
1225
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AXTreeViewModule, decorators: [{
|
|
499
1226
|
type: NgModule,
|
|
500
1227
|
args: [{
|
|
501
|
-
imports: [
|
|
502
|
-
exports: [...COMPONENT],
|
|
1228
|
+
imports: [AXTreeViewComponent],
|
|
503
1229
|
}]
|
|
504
1230
|
}] });
|
|
505
1231
|
|
|
@@ -507,5 +1233,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
|
|
|
507
1233
|
* Generated bundle index. Do not edit.
|
|
508
1234
|
*/
|
|
509
1235
|
|
|
510
|
-
export {
|
|
1236
|
+
export { AXTreeViewComponent, AXTreeViewModule, AXTreeViewService };
|
|
511
1237
|
//# sourceMappingURL=acorex-components-tree-view.mjs.map
|