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