concepto-user-controls 0.0.8 → 0.0.10
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/esm2022/lib/concepto-input/concepto-input.component.mjs +20 -0
- package/esm2022/lib/concepto-tree/components/tree-node/tree-node.component.mjs +301 -0
- package/esm2022/lib/concepto-tree/components/tree-node-checkbox/tree-node-checkbox.component.mjs +90 -0
- package/esm2022/lib/concepto-tree/components/tree-node-content/tree-node-content.component.mjs +65 -0
- package/esm2022/lib/concepto-tree/components/tree-node-expander/tree-node-expander.component.mjs +74 -0
- package/esm2022/lib/concepto-tree/components/tree-root/tree-root.component.mjs +230 -0
- package/esm2022/lib/concepto-tree/components/tree-viewport/tree-viewport.component.mjs +216 -0
- package/esm2022/lib/concepto-tree/concepto-tree.component.mjs +214 -13
- package/esm2022/lib/concepto-tree/core/models/tree-events.model.mjs +2 -0
- package/esm2022/lib/concepto-tree/core/models/tree-node.model.mjs +102 -0
- package/esm2022/lib/concepto-tree/core/models/tree-options.model.mjs +24 -0
- package/esm2022/lib/concepto-tree/core/models/tree.model.mjs +313 -0
- package/esm2022/lib/concepto-tree/core/services/tree-drag-drop.service.mjs +27 -0
- package/esm2022/lib/concepto-tree/directives/tree-drag.directive.mjs +69 -0
- package/esm2022/lib/concepto-tree/directives/tree-drop.directive.mjs +124 -0
- package/esm2022/lib/concepto-tree/directives/tree-node-template.directive.mjs +19 -0
- package/esm2022/public-api.mjs +2 -1
- package/fesm2022/concepto-user-controls.mjs +1837 -13
- package/fesm2022/concepto-user-controls.mjs.map +1 -1
- package/lib/concepto-input/concepto-input.component.d.ts +8 -0
- package/lib/concepto-tree/components/tree-node/tree-node.component.d.ts +19 -0
- package/lib/concepto-tree/components/tree-node-checkbox/tree-node-checkbox.component.d.ts +17 -0
- package/lib/concepto-tree/components/tree-node-content/tree-node-content.component.d.ts +18 -0
- package/lib/concepto-tree/components/tree-node-expander/tree-node-expander.component.d.ts +12 -0
- package/lib/concepto-tree/components/tree-root/tree-root.component.d.ts +35 -0
- package/lib/concepto-tree/components/tree-viewport/tree-viewport.component.d.ts +33 -0
- package/lib/concepto-tree/concepto-tree.component.d.ts +32 -1
- package/lib/concepto-tree/core/models/tree-events.model.d.ts +13 -0
- package/lib/concepto-tree/core/models/tree-node.model.d.ts +39 -0
- package/lib/concepto-tree/core/models/tree-options.model.d.ts +28 -0
- package/lib/concepto-tree/core/models/tree.model.d.ts +54 -0
- package/lib/concepto-tree/core/services/tree-drag-drop.service.d.ts +11 -0
- package/lib/concepto-tree/directives/tree-drag.directive.d.ts +16 -0
- package/lib/concepto-tree/directives/tree-drop.directive.d.ts +25 -0
- package/lib/concepto-tree/directives/tree-node-template.directive.d.ts +8 -0
- package/package.json +1 -1
- package/public-api.d.ts +1 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// lib/components/tree-root/tree-root.component.ts
|
|
2
|
+
import { Component, Input, Output, EventEmitter, HostListener, signal, contentChild, TemplateRef, ChangeDetectionStrategy, } from '@angular/core';
|
|
3
|
+
import { CommonModule } from '@angular/common';
|
|
4
|
+
import { TreeModel } from '../../core/models/tree.model';
|
|
5
|
+
import { DEFAULT_TREE_OPTIONS } from '../../core/models/tree-options.model';
|
|
6
|
+
import { TreeNodeComponent } from '../tree-node/tree-node.component';
|
|
7
|
+
import { TreeViewportComponent } from '../tree-viewport/tree-viewport.component';
|
|
8
|
+
import { TreeNodeTemplateDirective } from '../../directives/tree-node-template.directive';
|
|
9
|
+
import * as i0 from "@angular/core";
|
|
10
|
+
export class TreeRootComponent {
|
|
11
|
+
nodes = [];
|
|
12
|
+
options = {};
|
|
13
|
+
initialized = new EventEmitter();
|
|
14
|
+
toggleExpanded = new EventEmitter();
|
|
15
|
+
activate = new EventEmitter();
|
|
16
|
+
deactivate = new EventEmitter();
|
|
17
|
+
select = new EventEmitter();
|
|
18
|
+
deselect = new EventEmitter();
|
|
19
|
+
focus = new EventEmitter();
|
|
20
|
+
blur = new EventEmitter();
|
|
21
|
+
moveNode = new EventEmitter();
|
|
22
|
+
loadChildren = new EventEmitter();
|
|
23
|
+
treeEvent = new EventEmitter();
|
|
24
|
+
// Content queries
|
|
25
|
+
nodeTemplate = contentChild(TreeNodeTemplateDirective, {
|
|
26
|
+
read: TemplateRef
|
|
27
|
+
});
|
|
28
|
+
treeModel;
|
|
29
|
+
mergedOptions;
|
|
30
|
+
isFocused = signal(false);
|
|
31
|
+
eventsSubscription;
|
|
32
|
+
ngOnInit() {
|
|
33
|
+
this.initializeTreeModel();
|
|
34
|
+
}
|
|
35
|
+
ngOnChanges(changes) {
|
|
36
|
+
if (changes['options'] && !changes['options'].firstChange) {
|
|
37
|
+
this.initializeTreeModel();
|
|
38
|
+
}
|
|
39
|
+
if (changes['nodes'] && !changes['nodes'].firstChange) {
|
|
40
|
+
this.treeModel.setData(this.nodes);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
initializeTreeModel() {
|
|
44
|
+
// Unsubscribe from previous subscription if exists
|
|
45
|
+
this.eventsSubscription?.unsubscribe();
|
|
46
|
+
this.mergedOptions = {
|
|
47
|
+
...DEFAULT_TREE_OPTIONS,
|
|
48
|
+
...this.options,
|
|
49
|
+
};
|
|
50
|
+
this.treeModel = new TreeModel(this.mergedOptions);
|
|
51
|
+
this.treeModel.setData(this.nodes);
|
|
52
|
+
// Subscribe to tree events
|
|
53
|
+
this.eventsSubscription = this.treeModel.events$.subscribe(event => {
|
|
54
|
+
this.handleTreeEvent(event);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
ngOnDestroy() {
|
|
58
|
+
// Clean up subscription
|
|
59
|
+
this.eventsSubscription?.unsubscribe();
|
|
60
|
+
}
|
|
61
|
+
handleTreeEvent(event) {
|
|
62
|
+
this.treeEvent.emit(event);
|
|
63
|
+
switch (event.type) {
|
|
64
|
+
case 'initialized':
|
|
65
|
+
this.initialized.emit(event);
|
|
66
|
+
break;
|
|
67
|
+
case 'expand':
|
|
68
|
+
case 'collapse':
|
|
69
|
+
this.toggleExpanded.emit(event);
|
|
70
|
+
break;
|
|
71
|
+
case 'activate':
|
|
72
|
+
this.activate.emit(event);
|
|
73
|
+
break;
|
|
74
|
+
case 'deactivate':
|
|
75
|
+
this.deactivate.emit(event);
|
|
76
|
+
break;
|
|
77
|
+
case 'select':
|
|
78
|
+
this.select.emit(event);
|
|
79
|
+
break;
|
|
80
|
+
case 'deselect':
|
|
81
|
+
this.deselect.emit(event);
|
|
82
|
+
break;
|
|
83
|
+
case 'focus':
|
|
84
|
+
this.focus.emit(event);
|
|
85
|
+
break;
|
|
86
|
+
case 'blur':
|
|
87
|
+
this.blur.emit(event);
|
|
88
|
+
break;
|
|
89
|
+
case 'moveNode':
|
|
90
|
+
this.moveNode.emit(event);
|
|
91
|
+
break;
|
|
92
|
+
case 'loadChildren':
|
|
93
|
+
this.loadChildren.emit(event);
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Keyboard navigation
|
|
98
|
+
onKeydown(event) {
|
|
99
|
+
if (!this.isFocused())
|
|
100
|
+
return;
|
|
101
|
+
const key = event.key;
|
|
102
|
+
const isRtl = this.options.rtl;
|
|
103
|
+
switch (key) {
|
|
104
|
+
case 'ArrowDown':
|
|
105
|
+
this.treeModel.focusNextNode();
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
break;
|
|
108
|
+
case 'ArrowUp':
|
|
109
|
+
this.treeModel.focusPreviousNode();
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
break;
|
|
112
|
+
case 'ArrowRight':
|
|
113
|
+
isRtl ? this.treeModel.focusDrillUp() : this.treeModel.focusDrillDown();
|
|
114
|
+
event.preventDefault();
|
|
115
|
+
break;
|
|
116
|
+
case 'ArrowLeft':
|
|
117
|
+
isRtl ? this.treeModel.focusDrillDown() : this.treeModel.focusDrillUp();
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
break;
|
|
120
|
+
case 'Enter':
|
|
121
|
+
case ' ':
|
|
122
|
+
const focused = this.treeModel.focusedNode();
|
|
123
|
+
if (focused) {
|
|
124
|
+
this.treeModel.activateNode(focused);
|
|
125
|
+
}
|
|
126
|
+
event.preventDefault();
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
onFocus() {
|
|
131
|
+
this.isFocused.set(true);
|
|
132
|
+
}
|
|
133
|
+
onBlur() {
|
|
134
|
+
this.isFocused.set(false);
|
|
135
|
+
}
|
|
136
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TreeRootComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
137
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: TreeRootComponent, isStandalone: true, selector: "tree-root", inputs: { nodes: "nodes", options: "options" }, outputs: { initialized: "initialized", toggleExpanded: "toggleExpanded", activate: "activate", deactivate: "deactivate", select: "select", deselect: "deselect", focus: "focus", blur: "blur", moveNode: "moveNode", loadChildren: "loadChildren", treeEvent: "treeEvent" }, host: { listeners: { "keydown": "onKeydown($event)", "focus": "onFocus()", "blur": "onBlur()" }, properties: { "class.tree-focused": "isFocused()" } }, queries: [{ propertyName: "nodeTemplate", first: true, predicate: TreeNodeTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
138
|
+
<div
|
|
139
|
+
class="tree-container"
|
|
140
|
+
[class.tree-rtl]="mergedOptions?.rtl"
|
|
141
|
+
tabindex="0">
|
|
142
|
+
|
|
143
|
+
@if (mergedOptions?.useVirtualScroll) {
|
|
144
|
+
<tree-viewport
|
|
145
|
+
[treeModel]="treeModel"
|
|
146
|
+
[options]="mergedOptions"
|
|
147
|
+
[nodeTemplate]="nodeTemplate()">
|
|
148
|
+
</tree-viewport>
|
|
149
|
+
} @else {
|
|
150
|
+
@for (root of treeModel.roots(); track root.id) {
|
|
151
|
+
<tree-node
|
|
152
|
+
[node]="root"
|
|
153
|
+
[treeModel]="treeModel"
|
|
154
|
+
[options]="mergedOptions"
|
|
155
|
+
[nodeTemplate]="nodeTemplate()">
|
|
156
|
+
</tree-node>
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
</div>
|
|
160
|
+
`, isInline: true, styles: [".tree-container{width:100%;height:100%;outline:none}.tree-rtl{direction:rtl}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: TreeNodeComponent, selector: "tree-node", inputs: ["node", "treeModel", "options", "nodeTemplate"] }, { kind: "component", type: TreeViewportComponent, selector: "tree-viewport", inputs: ["treeModel", "options", "nodeTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
161
|
+
}
|
|
162
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TreeRootComponent, decorators: [{
|
|
163
|
+
type: Component,
|
|
164
|
+
args: [{ selector: 'tree-root', standalone: true, imports: [
|
|
165
|
+
CommonModule,
|
|
166
|
+
TreeNodeComponent,
|
|
167
|
+
TreeViewportComponent,
|
|
168
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
169
|
+
<div
|
|
170
|
+
class="tree-container"
|
|
171
|
+
[class.tree-rtl]="mergedOptions?.rtl"
|
|
172
|
+
tabindex="0">
|
|
173
|
+
|
|
174
|
+
@if (mergedOptions?.useVirtualScroll) {
|
|
175
|
+
<tree-viewport
|
|
176
|
+
[treeModel]="treeModel"
|
|
177
|
+
[options]="mergedOptions"
|
|
178
|
+
[nodeTemplate]="nodeTemplate()">
|
|
179
|
+
</tree-viewport>
|
|
180
|
+
} @else {
|
|
181
|
+
@for (root of treeModel.roots(); track root.id) {
|
|
182
|
+
<tree-node
|
|
183
|
+
[node]="root"
|
|
184
|
+
[treeModel]="treeModel"
|
|
185
|
+
[options]="mergedOptions"
|
|
186
|
+
[nodeTemplate]="nodeTemplate()">
|
|
187
|
+
</tree-node>
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
</div>
|
|
191
|
+
`, host: {
|
|
192
|
+
'[class.tree-focused]': 'isFocused()',
|
|
193
|
+
}, styles: [".tree-container{width:100%;height:100%;outline:none}.tree-rtl{direction:rtl}\n"] }]
|
|
194
|
+
}], propDecorators: { nodes: [{
|
|
195
|
+
type: Input
|
|
196
|
+
}], options: [{
|
|
197
|
+
type: Input
|
|
198
|
+
}], initialized: [{
|
|
199
|
+
type: Output
|
|
200
|
+
}], toggleExpanded: [{
|
|
201
|
+
type: Output
|
|
202
|
+
}], activate: [{
|
|
203
|
+
type: Output
|
|
204
|
+
}], deactivate: [{
|
|
205
|
+
type: Output
|
|
206
|
+
}], select: [{
|
|
207
|
+
type: Output
|
|
208
|
+
}], deselect: [{
|
|
209
|
+
type: Output
|
|
210
|
+
}], focus: [{
|
|
211
|
+
type: Output
|
|
212
|
+
}], blur: [{
|
|
213
|
+
type: Output
|
|
214
|
+
}], moveNode: [{
|
|
215
|
+
type: Output
|
|
216
|
+
}], loadChildren: [{
|
|
217
|
+
type: Output
|
|
218
|
+
}], treeEvent: [{
|
|
219
|
+
type: Output
|
|
220
|
+
}], onKeydown: [{
|
|
221
|
+
type: HostListener,
|
|
222
|
+
args: ['keydown', ['$event']]
|
|
223
|
+
}], onFocus: [{
|
|
224
|
+
type: HostListener,
|
|
225
|
+
args: ['focus']
|
|
226
|
+
}], onBlur: [{
|
|
227
|
+
type: HostListener,
|
|
228
|
+
args: ['blur']
|
|
229
|
+
}] } });
|
|
230
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJlZS1yb290LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2NvbmNlcHRvLXVzZXItY29udHJvbHMvc3JjL2xpYi9jb25jZXB0by10cmVlL2NvbXBvbmVudHMvdHJlZS1yb290L3RyZWUtcm9vdC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsa0RBQWtEO0FBQ2xELE9BQU8sRUFDTCxTQUFTLEVBQ1QsS0FBSyxFQUNMLE1BQU0sRUFDTixZQUFZLEVBS1osWUFBWSxFQUNaLE1BQU0sRUFDTixZQUFZLEVBQ1osV0FBVyxFQUNYLHVCQUF1QixHQUN4QixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFL0MsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ3pELE9BQU8sRUFBZSxvQkFBb0IsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBRXpGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDBDQUEwQyxDQUFDO0FBQ2pGLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLCtDQUErQyxDQUFDOztBQWtEMUYsTUFBTSxPQUFPLGlCQUFpQjtJQUNuQixLQUFLLEdBQVUsRUFBRSxDQUFDO0lBQ2xCLE9BQU8sR0FBeUIsRUFBRSxDQUFDO0lBRWxDLFdBQVcsR0FBRyxJQUFJLFlBQVksRUFBYSxDQUFDO0lBQzVDLGNBQWMsR0FBRyxJQUFJLFlBQVksRUFBYSxDQUFDO0lBQy9DLFFBQVEsR0FBRyxJQUFJLFlBQVksRUFBYSxDQUFDO0lBQ3pDLFVBQVUsR0FBRyxJQUFJLFlBQVksRUFBYSxDQUFDO0lBQzNDLE1BQU0sR0FBRyxJQUFJLFlBQVksRUFBYSxDQUFDO0lBQ3ZDLFFBQVEsR0FBRyxJQUFJLFlB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c0IsRUFBRSxhQUFhO3FCQUN0Qzs4QkFHUSxLQUFLO3NCQUFiLEtBQUs7Z0JBQ0csT0FBTztzQkFBZixLQUFLO2dCQUVJLFdBQVc7c0JBQXBCLE1BQU07Z0JBQ0csY0FBYztzQkFBdkIsTUFBTTtnQkFDRyxRQUFRO3NCQUFqQixNQUFNO2dCQUNHLFVBQVU7c0JBQW5CLE1BQU07Z0JBQ0csTUFBTTtzQkFBZixNQUFNO2dCQUNHLFFBQVE7c0JBQWpCLE1BQU07Z0JBQ0csS0FBSztzQkFBZCxNQUFNO2dCQUNHLElBQUk7c0JBQWIsTUFBTTtnQkFDRyxRQUFRO3NCQUFqQixNQUFNO2dCQUNHLFlBQVk7c0JBQXJCLE1BQU07Z0JBQ0csU0FBUztzQkFBbEIsTUFBTTtnQkEwRlAsU0FBUztzQkFEUixZQUFZO3VCQUFDLFNBQVMsRUFBRSxDQUFDLFFBQVEsQ0FBQztnQkFvQ25DLE9BQU87c0JBRE4sWUFBWTt1QkFBQyxPQUFPO2dCQU1yQixNQUFNO3NCQURMLFlBQVk7dUJBQUMsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbIi8vIGxpYi9jb21wb25lbnRzL3RyZWUtcm9vdC90cmVlLXJvb3QuY29tcG9uZW50LnRzXHJcbmltcG9ydCB7XHJcbiAgQ29tcG9uZW50LFxyXG4gIElucHV0LFxyXG4gIE91dHB1dCxcclxuICBFdmVudEVtaXR0ZXIsXHJcbiAgT25Jbml0LFxyXG4gIE9uQ2hhbmdlcyxcclxuICBPbkRlc3Ryb3ksXHJcbiAgU2ltcGxlQ2hhbmdlcyxcclxuICBIb3N0TGlzdGVuZXIsXHJcbiAgc2lnbmFsLFxyXG4gIGNvbnRlbnRDaGlsZCxcclxuICBUZW1wbGF0ZVJlZixcclxuICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcclxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcclxuaW1wb3J0IHsgU3Vic2NyaXB0aW9uIH0gZnJvbSAncnhqcyc7XHJcbmltcG9ydCB7IFRyZWVNb2RlbCB9IGZyb20gJy4uLy4uL2NvcmUvbW9kZWxzL3RyZWUubW9kZWwnO1xyXG5pbXBvcnQgeyBUcmVlT3B0aW9ucywgREVGQVVMVF9UUkVFX09QVElPTlMgfSBmcm9tICcuLi8uLi9jb3JlL21vZGVscy90cmVlLW9wdGlvbnMubW9kZWwnO1xyXG5pbXBvcnQgeyBUcmVlRXZlbnQgfSBmcm9tICcuLi8uLi9jb3JlL21vZGVscy90cmVlLWV2ZW50cy5tb2RlbCc7XHJcbmltcG9ydCB7IFRyZWVOb2RlQ29tcG9uZW50IH0gZnJvbSAnLi4vdHJlZS1ub2RlL3RyZWUtbm9kZS5jb21wb25lbnQnO1xyXG5pbXBvcnQgeyBUcmVlVmlld3BvcnRDb21wb25lbnQgfSBmcm9tICcuLi90cmVlLXZpZXdwb3J0L3RyZWUtdmlld3BvcnQuY29tcG9uZW50JztcclxuaW1wb3J0IHsgVHJlZU5vZGVUZW1wbGF0ZURpcmVjdGl2ZSB9IGZyb20gJy4uLy4uL2RpcmVjdGl2ZXMvdHJlZS1ub2RlLXRlbXBsYXRlLmRpcmVjdGl2ZSc7XHJcblxyXG5AQ29tcG9uZW50KHtcclxuICBzZWxlY3RvcjogJ3RyZWUtcm9vdCcsXHJcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcclxuICBpbXBvcnRzOiBbXHJcbiAgICBDb21tb25Nb2R1bGUsXHJcbiAgICBUcmVlTm9kZUNvbXBvbmVudCxcclxuICAgIFRyZWVWaWV3cG9ydENvbXBvbmVudCxcclxuICBdLFxyXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxyXG4gIHRlbXBsYXRlOiBgXHJcbiAgICA8ZGl2XHJcbiAgICAgIGNsYXNzPVwidHJlZS1jb250YWluZXJcIlxyXG4gICAgICBbY2xhc3MudHJlZS1ydGxdPVwibWVyZ2VkT3B0aW9ucz8ucnRsXCJcclxuICAgICAgdGFiaW5kZXg9XCIwXCI+XHJcblxyXG4gICAgICBAaWYgKG1lcmdlZE9wdGlvbnM/LnVzZVZpcnR1YWxTY3JvbGwpIHtcclxuICAgICAgICA8dHJlZS12aWV3cG9ydFxyXG4gICAgICAgICAgW3RyZWVNb2RlbF09XCJ0cmVlTW9kZWxcIlxyXG4gICAgICAgICAgW29wdGlvbnNdPVwibWVyZ2VkT3B0aW9uc1wiXHJcbiAgICAgICAgICBbbm9kZVRlbXBsYXRlXT1cIm5vZGVUZW1wbGF0ZSgpXCI+XHJcbiAgICAgICAgPC90cmVlLXZpZXdwb3J0PlxyXG4gICAgICB9IEBlbHNlIHtcclxuICAgICAgICBAZm9yIChyb290IG9mIHRyZWVNb2RlbC5yb290cygpOyB0cmFjayByb290LmlkKSB7XHJcbiAgICAgICAgICA8dHJlZS1ub2RlXHJcbiAgICAgICAgICAgIFtub2RlXT1cInJvb3RcIlxyXG4gICAgICAgICAgICBbdHJlZU1vZGVsXT1cInRyZWVNb2RlbFwiXHJcbiAgICAgICAgICAgIFtvcHRpb25zXT1cIm1lcmdlZE9wdGlvbnNcIlxyXG4gICAgICAgICAgICBbbm9kZVRlbXBsYXRlXT1cIm5vZGVUZW1wbGF0ZSgpXCI+XHJcbiAgICAgICAgICA8L3RyZWUtbm9kZT5cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIDwvZGl2PlxyXG4gIGAsXHJcbiAgc3R5bGVzOiBbYFxyXG4gICAgLnRyZWUtY29udGFpbmVyIHtcclxuICAgICAgd2lkdGg6IDEwMCU7XHJcbiAgICAgIGhlaWdodDogMTAwJTtcclxuICAgICAgb3V0bGluZTogbm9uZTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgLnRyZWUtcnRsIHtcclxuICAgICAgZGlyZWN0aW9uOiBydGw7XHJcbiAgICB9XHJcbiAgYF0sXHJcbiAgaG9zdDoge1xyXG4gICAgJ1tjbGFzcy50cmVlLWZvY3VzZWRdJzogJ2lzRm9jdXNlZCgpJyxcclxuICB9LFxyXG59KVxyXG5leHBvcnQgY2xhc3MgVHJlZVJvb3RDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uQ2hhbmdlcywgT25EZXN0cm95IHtcclxuICBASW5wdXQoKSBub2RlczogYW55W10gPSBbXTtcclxuICBASW5wdXQoKSBvcHRpb25zOiBQYXJ0aWFsPFRyZWVPcHRpb25zPiA9IHt9O1xyXG4gIFxyXG4gIEBPdXRwdXQoKSBpbml0aWFsaXplZCA9IG5ldyBFdmVudEVtaXR0ZXI8VHJlZUV2ZW50PigpO1xyXG4gIEBPdXRwdXQoKSB0b2dnbGVFeHBhbmRlZCA9IG5ldyBFdmVudEVtaXR0ZXI8VHJlZUV2ZW50PigpO1xyXG4gIEBPdXRwdXQoKSBhY3RpdmF0ZSA9IG5ldyBFdmVudEVtaXR0ZXI8VHJlZUV2ZW50PigpO1xyXG4gIEBPdXRwdXQoKSBkZWFjdGl2YXRlID0gbmV3IEV2ZW50RW1pdHRlcjxUcmVlRXZlbnQ+KCk7XHJcbiAgQE91dHB1dCgpIHNlbGVjdCA9IG5ldyBFdmVudEVtaXR0ZXI8VHJlZUV2ZW50PigpO1xyXG4gIEBPdXRwdXQoKSBkZXNlbGVjdCA9IG5ldyBFdmVudEVtaXR0ZXI8VHJlZUV2ZW50PigpO1xyXG4gIEBPdXRwdXQoKSBmb2N1cyA9IG5ldyBFdmVudEVtaXR0ZXI8VHJlZUV2ZW50PigpO1xyXG4gIEBPdXRwdXQoKSBibHVyID0gbmV3IEV2ZW50RW1pdHRlcjxUcmVlRXZlbnQ+KCk7XHJcbiAgQE91dHB1dCgpIG1vdmVOb2RlID0gbmV3IEV2ZW50RW1pdHRlcjxUcmVlRXZlbnQ+KCk7XHJcbiAgQE91dHB1dCgpIGxvYWRDaGlsZHJlbiA9IG5ldyBFdmVudEVtaXR0ZXI8VHJlZUV2ZW50PigpO1xyXG4gIEBPdXRwdXQoKSB0cmVlRXZlbnQgPSBuZXcgRXZlbnRFbWl0dGVyPFRyZWVFdmVudD4oKTtcclxuICBcclxuICAvLyBDb250ZW50IHF1ZXJpZXNcclxuICByZWFkb25seSBub2RlVGVtcGxhdGUgPSBjb250ZW50Q2hpbGQoVHJlZU5vZGVUZW1wbGF0ZURpcmVjdGl2ZSwge1xyXG4gICAgcmVhZDogVGVtcGxhdGVSZWZcclxuICB9KTtcclxuICBcclxuICB0cmVlTW9kZWwhOiBUcmVlTW9kZWw7XHJcbiAgbWVyZ2VkT3B0aW9ucyE6IFRyZWVPcHRpb25zO1xyXG4gIGlzRm9jdXNlZCA9IHNpZ25hbChmYWxzZSk7XHJcblxyXG4gIHByaXZhdGUgZXZlbnRzU3Vic2NyaXB0aW9uPzogU3Vic2NyaXB0aW9uO1xyXG4gIFxyXG4gIG5nT25Jbml0KCk6IHZvaWQge1xyXG4gICAgdGhpcy5pbml0aWFsaXplVHJlZU1vZGVsKCk7XHJcbiAgfVxyXG4gIFxyXG4gIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcclxuICAgIGlmIChjaGFuZ2VzWydvcHRpb25zJ10gJiYgIWNoYW5nZXNbJ29wdGlvbnMnXS5maXJzdENoYW5nZSkge1xyXG4gICAgICB0aGlzLmluaXRpYWxpemVUcmVlTW9kZWwoKTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgaWYgKGNoYW5nZXNbJ25vZGVzJ10gJiYgIWNoYW5nZXNbJ25vZGVzJ10uZmlyc3RDaGFuZ2UpIHtcclxuICAgICAgdGhpcy50cmVlTW9kZWwuc2V0RGF0YSh0aGlzLm5vZGVzKTtcclxuICAgIH1cclxuICB9XHJcbiAgXHJcbiAgcHJpdmF0ZSBpbml0aWFsaXplVHJlZU1vZGVsKCk6IHZvaWQge1xyXG4gICAgLy8gVW5zdWJzY3JpYmUgZnJvbSBwcmV2aW91cyBzdWJzY3JpcHRpb24gaWYgZXhpc3RzXHJcbiAgICB0aGlzLmV2ZW50c1N1YnNjcmlwdGlvbj8udW5zdWJzY3JpYmUoKTtcclxuXHJcbiAgICB0aGlzLm1lcmdlZE9wdGlvbnMgPSB7XHJcbiAgICAgIC4uLkRFRkFVTFRfVFJFRV9PUFRJT05TLFxyXG4gICAgICAuLi50aGlzLm9wdGlvbnMsXHJcbiAgICB9O1xyXG5cclxuICAgIHRoaXMudHJlZU1vZGVsID0gbmV3IFRyZWVNb2RlbCh0aGlzLm1lcmdlZE9wdGlvbnMpO1xyXG4gICAgdGhpcy50cmVlTW9kZWwuc2V0RGF0YSh0aGlzLm5vZGVzKTtcclxuXHJcbiAgICAvLyBTdWJzY3JpYmUgdG8gdHJlZSBldmVudHNcclxuICAgIHRoaXMuZXZlbnRzU3Vic2NyaXB0aW9uID0gdGhpcy50cmVlTW9kZWwuZXZlbnRzJC5zdWJzY3JpYmUoZXZlbnQgPT4ge1xyXG4gICAgICB0aGlzLmhhbmRsZVRyZWVFdmVudChldmVudCk7XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xyXG4gICAgLy8gQ2xlYW4gdXAgc3Vic2NyaXB0aW9uXHJcbiAgICB0aGlzLmV2ZW50c1N1YnNjcmlwdGlvbj8udW5zdWJzY3JpYmUoKTtcclxuICB9XHJcbiAgXHJcbiAgcHJpdmF0ZSBoYW5kbGVUcmVlRXZlbnQoZXZlbnQ6IFRyZWVFdmVudCk6IHZvaWQge1xyXG4gICAgdGhpcy50cmVlRXZlbnQuZW1pdChldmVudCk7XHJcbiAgICBcclxuICAgIHN3aXRjaCAoZXZlbnQudHlwZSkge1xyXG4gICAgICBjYXNlICdpbml0aWFsaXplZCc6XHJcbiAgICAgICAgdGhpcy5pbml0aWFsaXplZC5lbWl0KGV2ZW50KTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAnZXhwYW5kJzpcclxuICAgICAgY2FzZSAnY29sbGFwc2UnOlxyXG4gICAgICAgIHRoaXMudG9nZ2xlRXhwYW5kZWQuZW1pdChldmVudCk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ2FjdGl2YXRlJzpcclxuICAgICAgICB0aGlzLmFjdGl2YXRlLmVtaXQoZXZlbnQpO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBjYXNlICdkZWFjdGl2YXRlJzpcclxuICAgICAgICB0aGlzLmRlYWN0aXZhdGUuZW1pdChldmVudCk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3NlbGVjdCc6XHJcbiAgICAgICAgdGhpcy5zZWxlY3QuZW1pdChldmVudCk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ2Rlc2VsZWN0JzpcclxuICAgICAgICB0aGlzLmRlc2VsZWN0LmVtaXQoZXZlbnQpO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBjYXNlICdmb2N1cyc6XHJcbiAgICAgICAgdGhpcy5mb2N1cy5lbWl0KGV2ZW50KTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAnYmx1cic6XHJcbiAgICAgICAgdGhpcy5ibHVyLmVtaXQoZXZlbnQpO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBjYXNlICdtb3ZlTm9kZSc6XHJcbiAgICAgICAgdGhpcy5tb3ZlTm9kZS5lbWl0KGV2ZW50KTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAnbG9hZENoaWxkcmVuJzpcclxuICAgICAgICB0aGlzLmxvYWRDaGlsZHJlbi5lbWl0KGV2ZW50KTtcclxuICAgICAgICBicmVhaztcclxuICAgIH1cclxuICB9XHJcbiAgXHJcbiAgLy8gS2V5Ym9hcmQgbmF2aWdhdGlvblxyXG4gIEBIb3N0TGlzdGVuZXIoJ2tleWRvd24nLCBbJyRldmVudCddKVxyXG4gIG9uS2V5ZG93bihldmVudDogS2V5Ym9hcmRFdmVudCk6IHZvaWQge1xyXG4gICAgaWYgKCF0aGlzLmlzRm9jdXNlZCgpKSByZXR1cm47XHJcbiAgICBcclxuICAgIGNvbnN0IGtleSA9IGV2ZW50LmtleTtcclxuICAgIGNvbnN0IGlzUnRsID0gdGhpcy5vcHRpb25zLnJ0bDtcclxuICAgIFxyXG4gICAgc3dpdGNoIChrZXkpIHtcclxuICAgICAgY2FzZSAnQXJyb3dEb3duJzpcclxuICAgICAgICB0aGlzLnRyZWVNb2RlbC5mb2N1c05leHROb2RlKCk7XHJcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAnQXJyb3dVcCc6XHJcbiAgICAgICAgdGhpcy50cmVlTW9kZWwuZm9jdXNQcmV2aW91c05vZGUoKTtcclxuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBjYXNlICdBcnJvd1JpZ2h0JzpcclxuICAgICAgICBpc1J0bCA/IHRoaXMudHJlZU1vZGVsLmZvY3VzRHJpbGxVcCgpIDogdGhpcy50cmVlTW9kZWwuZm9jdXNEcmlsbERvd24oKTtcclxuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBjYXNlICdBcnJvd0xlZnQnOlxyXG4gICAgICAgIGlzUnRsID8gdGhpcy50cmVlTW9kZWwuZm9jdXNEcmlsbERvd24oKSA6IHRoaXMudHJlZU1vZGVsLmZvY3VzRHJpbGxVcCgpO1xyXG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ0VudGVyJzpcclxuICAgICAgY2FzZSAnICc6XHJcbiAgICAgICAgY29uc3QgZm9jdXNlZCA9IHRoaXMudHJlZU1vZGVsLmZvY3VzZWROb2RlKCk7XHJcbiAgICAgICAgaWYgKGZvY3VzZWQpIHtcclxuICAgICAgICAgIHRoaXMudHJlZU1vZGVsLmFjdGl2YXRlTm9kZShmb2N1c2VkKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuICAgICAgICBicmVhaztcclxuICAgIH1cclxuICB9XHJcbiAgXHJcbiAgQEhvc3RMaXN0ZW5lcignZm9jdXMnKVxyXG4gIG9uRm9jdXMoKTogdm9pZCB7XHJcbiAgICB0aGlzLmlzRm9jdXNlZC5zZXQodHJ1ZSk7XHJcbiAgfVxyXG4gIFxyXG4gIEBIb3N0TGlzdGVuZXIoJ2JsdXInKVxyXG4gIG9uQmx1cigpOiB2b2lkIHtcclxuICAgIHRoaXMuaXNGb2N1c2VkLnNldChmYWxzZSk7XHJcbiAgfVxyXG59Il19
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// lib/components/tree-viewport/tree-viewport.component.ts
|
|
2
|
+
import { Component, Input, ViewChild, ChangeDetectionStrategy, signal, computed, effect, } from '@angular/core';
|
|
3
|
+
import { CommonModule } from '@angular/common';
|
|
4
|
+
import { TreeNodeComponent } from '../tree-node/tree-node.component';
|
|
5
|
+
import { fromEvent, Subject, takeUntil } from 'rxjs';
|
|
6
|
+
import { debounceTime } from 'rxjs/operators';
|
|
7
|
+
import * as i0 from "@angular/core";
|
|
8
|
+
export class TreeViewportComponent {
|
|
9
|
+
treeModel;
|
|
10
|
+
options;
|
|
11
|
+
nodeTemplate;
|
|
12
|
+
scrollContainer;
|
|
13
|
+
destroy$ = new Subject();
|
|
14
|
+
scrollTop = signal(0);
|
|
15
|
+
viewportHeight = signal(0);
|
|
16
|
+
flattenedNodes = computed(() => {
|
|
17
|
+
return this.treeModel.flattenedNodes();
|
|
18
|
+
});
|
|
19
|
+
totalHeight = computed(() => {
|
|
20
|
+
const nodes = this.flattenedNodes();
|
|
21
|
+
const nodeHeight = this.options.nodeHeight || 22;
|
|
22
|
+
if (typeof nodeHeight === 'number') {
|
|
23
|
+
return nodes.length * nodeHeight;
|
|
24
|
+
}
|
|
25
|
+
return nodes.reduce((sum, node) => sum + nodeHeight(node), 0);
|
|
26
|
+
});
|
|
27
|
+
visibleRange = computed(() => {
|
|
28
|
+
const scrollTop = this.scrollTop();
|
|
29
|
+
const viewportHeight = this.viewportHeight();
|
|
30
|
+
const nodes = this.flattenedNodes();
|
|
31
|
+
const nodeHeight = this.options.nodeHeight || 22;
|
|
32
|
+
const bufferAmount = this.options.bufferAmount || 5;
|
|
33
|
+
let startIndex = 0;
|
|
34
|
+
let accumulatedHeight = 0;
|
|
35
|
+
// Find start index
|
|
36
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
37
|
+
const height = typeof nodeHeight === 'number'
|
|
38
|
+
? nodeHeight
|
|
39
|
+
: nodeHeight(nodes[i]);
|
|
40
|
+
if (accumulatedHeight + height > scrollTop) {
|
|
41
|
+
startIndex = Math.max(0, i - bufferAmount);
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
accumulatedHeight += height;
|
|
45
|
+
}
|
|
46
|
+
// Find end index
|
|
47
|
+
let endIndex = startIndex;
|
|
48
|
+
accumulatedHeight = 0;
|
|
49
|
+
for (let i = startIndex; i < nodes.length; i++) {
|
|
50
|
+
const height = typeof nodeHeight === 'number'
|
|
51
|
+
? nodeHeight
|
|
52
|
+
: nodeHeight(nodes[i]);
|
|
53
|
+
accumulatedHeight += height;
|
|
54
|
+
if (accumulatedHeight > viewportHeight + (bufferAmount * (typeof nodeHeight === 'number' ? nodeHeight : 22))) {
|
|
55
|
+
endIndex = i;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (endIndex === startIndex) {
|
|
60
|
+
endIndex = nodes.length;
|
|
61
|
+
}
|
|
62
|
+
return { startIndex, endIndex };
|
|
63
|
+
});
|
|
64
|
+
visibleNodes = computed(() => {
|
|
65
|
+
const range = this.visibleRange();
|
|
66
|
+
const nodes = this.flattenedNodes();
|
|
67
|
+
return nodes.slice(range.startIndex, range.endIndex);
|
|
68
|
+
});
|
|
69
|
+
paddingTop = computed(() => {
|
|
70
|
+
const range = this.visibleRange();
|
|
71
|
+
const nodes = this.flattenedNodes();
|
|
72
|
+
const nodeHeight = this.options.nodeHeight || 22;
|
|
73
|
+
let height = 0;
|
|
74
|
+
for (let i = 0; i < range.startIndex; i++) {
|
|
75
|
+
height += typeof nodeHeight === 'number'
|
|
76
|
+
? nodeHeight
|
|
77
|
+
: nodeHeight(nodes[i]);
|
|
78
|
+
}
|
|
79
|
+
return height;
|
|
80
|
+
});
|
|
81
|
+
paddingBottom = computed(() => {
|
|
82
|
+
const range = this.visibleRange();
|
|
83
|
+
const nodes = this.flattenedNodes();
|
|
84
|
+
const nodeHeight = this.options.nodeHeight || 22;
|
|
85
|
+
const totalHeight = this.totalHeight();
|
|
86
|
+
let visibleHeight = this.paddingTop();
|
|
87
|
+
for (let i = range.startIndex; i < range.endIndex; i++) {
|
|
88
|
+
visibleHeight += typeof nodeHeight === 'number'
|
|
89
|
+
? nodeHeight
|
|
90
|
+
: nodeHeight(nodes[i]);
|
|
91
|
+
}
|
|
92
|
+
return Math.max(0, totalHeight - visibleHeight);
|
|
93
|
+
});
|
|
94
|
+
constructor() {
|
|
95
|
+
// Update viewport when tree changes
|
|
96
|
+
effect(() => {
|
|
97
|
+
this.flattenedNodes(); // Subscribe to changes
|
|
98
|
+
this.updateViewport();
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
ngOnInit() {
|
|
102
|
+
this.setupScrollListener();
|
|
103
|
+
this.updateViewportHeight();
|
|
104
|
+
}
|
|
105
|
+
ngOnDestroy() {
|
|
106
|
+
this.destroy$.next();
|
|
107
|
+
this.destroy$.complete();
|
|
108
|
+
}
|
|
109
|
+
setupScrollListener() {
|
|
110
|
+
fromEvent(this.scrollContainer.nativeElement, 'scroll')
|
|
111
|
+
.pipe(debounceTime(10), takeUntil(this.destroy$))
|
|
112
|
+
.subscribe(() => {
|
|
113
|
+
this.onScroll();
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
updateViewportHeight() {
|
|
117
|
+
const height = this.scrollContainer.nativeElement.clientHeight;
|
|
118
|
+
this.viewportHeight.set(height);
|
|
119
|
+
}
|
|
120
|
+
onScroll() {
|
|
121
|
+
const scrollTop = this.scrollContainer.nativeElement.scrollTop;
|
|
122
|
+
this.scrollTop.set(scrollTop);
|
|
123
|
+
}
|
|
124
|
+
updateViewport() {
|
|
125
|
+
// Force recalculation
|
|
126
|
+
this.scrollTop.set(this.scrollContainer.nativeElement.scrollTop);
|
|
127
|
+
}
|
|
128
|
+
scrollToNode(node) {
|
|
129
|
+
const nodes = this.flattenedNodes();
|
|
130
|
+
const index = nodes.findIndex(n => n.id === node.id);
|
|
131
|
+
if (index === -1)
|
|
132
|
+
return;
|
|
133
|
+
const nodeHeight = this.options.nodeHeight || 22;
|
|
134
|
+
let targetScrollTop = 0;
|
|
135
|
+
for (let i = 0; i < index; i++) {
|
|
136
|
+
targetScrollTop += typeof nodeHeight === 'number'
|
|
137
|
+
? nodeHeight
|
|
138
|
+
: nodeHeight(nodes[i]);
|
|
139
|
+
}
|
|
140
|
+
this.scrollContainer.nativeElement.scrollTop = targetScrollTop;
|
|
141
|
+
}
|
|
142
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TreeViewportComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
143
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: TreeViewportComponent, isStandalone: true, selector: "tree-viewport", inputs: { treeModel: "treeModel", options: "options", nodeTemplate: "nodeTemplate" }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true, static: true }], ngImport: i0, template: `
|
|
144
|
+
<div
|
|
145
|
+
#scrollContainer
|
|
146
|
+
class="tree-viewport-container"
|
|
147
|
+
[style.height.px]="options.scrollContainerHeight || 400"
|
|
148
|
+
(scroll)="onScroll()">
|
|
149
|
+
|
|
150
|
+
<div class="tree-viewport-content"
|
|
151
|
+
[style.height.px]="totalHeight()">
|
|
152
|
+
|
|
153
|
+
<div class="tree-viewport-padding-top"
|
|
154
|
+
[style.height.px]="paddingTop()">
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
@for (node of visibleNodes(); track node.id) {
|
|
158
|
+
<tree-node
|
|
159
|
+
[node]="node"
|
|
160
|
+
[treeModel]="treeModel"
|
|
161
|
+
[options]="options"
|
|
162
|
+
[nodeTemplate]="nodeTemplate">
|
|
163
|
+
</tree-node>
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
<div class="tree-viewport-padding-bottom"
|
|
167
|
+
[style.height.px]="paddingBottom()">
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
`, isInline: true, styles: [".tree-viewport-container{overflow-y:auto;overflow-x:hidden;position:relative}.tree-viewport-content{position:relative;width:100%}.tree-viewport-padding-top,.tree-viewport-padding-bottom{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: TreeNodeComponent, selector: "tree-node", inputs: ["node", "treeModel", "options", "nodeTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
172
|
+
}
|
|
173
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TreeViewportComponent, decorators: [{
|
|
174
|
+
type: Component,
|
|
175
|
+
args: [{ selector: 'tree-viewport', standalone: true, imports: [CommonModule, TreeNodeComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
176
|
+
<div
|
|
177
|
+
#scrollContainer
|
|
178
|
+
class="tree-viewport-container"
|
|
179
|
+
[style.height.px]="options.scrollContainerHeight || 400"
|
|
180
|
+
(scroll)="onScroll()">
|
|
181
|
+
|
|
182
|
+
<div class="tree-viewport-content"
|
|
183
|
+
[style.height.px]="totalHeight()">
|
|
184
|
+
|
|
185
|
+
<div class="tree-viewport-padding-top"
|
|
186
|
+
[style.height.px]="paddingTop()">
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
@for (node of visibleNodes(); track node.id) {
|
|
190
|
+
<tree-node
|
|
191
|
+
[node]="node"
|
|
192
|
+
[treeModel]="treeModel"
|
|
193
|
+
[options]="options"
|
|
194
|
+
[nodeTemplate]="nodeTemplate">
|
|
195
|
+
</tree-node>
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
<div class="tree-viewport-padding-bottom"
|
|
199
|
+
[style.height.px]="paddingBottom()">
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
`, styles: [".tree-viewport-container{overflow-y:auto;overflow-x:hidden;position:relative}.tree-viewport-content{position:relative;width:100%}.tree-viewport-padding-top,.tree-viewport-padding-bottom{width:100%}\n"] }]
|
|
204
|
+
}], ctorParameters: () => [], propDecorators: { treeModel: [{
|
|
205
|
+
type: Input,
|
|
206
|
+
args: [{ required: true }]
|
|
207
|
+
}], options: [{
|
|
208
|
+
type: Input,
|
|
209
|
+
args: [{ required: true }]
|
|
210
|
+
}], nodeTemplate: [{
|
|
211
|
+
type: Input
|
|
212
|
+
}], scrollContainer: [{
|
|
213
|
+
type: ViewChild,
|
|
214
|
+
args: ['scrollContainer', { static: true }]
|
|
215
|
+
}] } });
|
|
216
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJlZS12aWV3cG9ydC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9jb25jZXB0by11c2VyLWNvbnRyb2xzL3NyYy9saWIvY29uY2VwdG8tdHJlZS9jb21wb25lbnRzL3RyZWUtdmlld3BvcnQvdHJlZS12aWV3cG9ydC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMERBQTBEO0FBQzFELE9BQU8sRUFDTCxTQUFTLEVBQ1QsS0FBSyxFQUdMLFNBQVMsRUFFVCx1QkFBdUIsRUFDdkIsTUFBTSxFQUNOLFFBQVEsRUFFUixNQUFNLEdBQ1AsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBSS9DLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNyRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7O0FBc0Q5QyxNQUFNLE9BQU8scUJBQXFCO0lBQ0wsU0FBUyxDQUFhO0lBQ3RCLE9BQU8sQ0FBZTtJQUN4QyxZQUFZLENBQW9CO0lBR3pDLGVBQWUsQ0FBMkI7SUFFbEMsUUFBUSxHQUFHLElBQUksT0FBTyxFQUFRLENBQUM7SUFFOUIsU0FBUyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN0QixjQUFjLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRTNCLGNBQWMsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQ3RDLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsQ0FBQztJQUN6QyxDQUFDLENBQUMsQ0FBQztJQUVNLFdBQVcsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQ25DLE1BQU0sS0FBSyxH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Q0FBQztJQUNqRSxDQUFDO3dHQTdLVSxxQkFBcUI7NEZBQXJCLHFCQUFxQixpU0EvQ3RCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBNEJULGdSQTlCUyxZQUFZLCtCQUFFLGlCQUFpQjs7NEZBaUQ5QixxQkFBcUI7a0JBcERqQyxTQUFTOytCQUNFLGVBQWUsY0FDYixJQUFJLFdBQ1AsQ0FBQyxZQUFZLEVBQUUsaUJBQWlCLENBQUMsbUJBQ3pCLHVCQUF1QixDQUFDLE1BQU0sWUFDckM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E0QlQ7d0RBb0IwQixTQUFTO3NCQUFuQyxLQUFLO3VCQUFDLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRTtnQkFDRSxPQUFPO3NCQUFqQyxLQUFLO3VCQUFDLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRTtnQkFDaEIsWUFBWTtzQkFBcEIsS0FBSztnQkFHTixlQUFlO3NCQURkLFNBQVM7dUJBQUMsaUJBQWlCLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiLy8gbGliL2NvbXBvbmVudHMvdHJlZS12aWV3cG9ydC90cmVlLXZpZXdwb3J0LmNvbXBvbmVudC50c1xyXG5pbXBvcnQge1xyXG4gIENvbXBvbmVudCxcclxuICBJbnB1dCxcclxuICBPbkluaXQsXHJcbiAgT25EZXN0cm95LFxyXG4gIFZpZXdDaGlsZCxcclxuICBFbGVtZW50UmVmLFxyXG4gIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxyXG4gIHNpZ25hbCxcclxuICBjb21wdXRlZCxcclxuICBUZW1wbGF0ZVJlZixcclxuICBlZmZlY3QsXHJcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XHJcbmltcG9ydCB7IFRyZWVNb2RlbCB9IGZyb20gJy4uLy4uL2NvcmUvbW9kZWxzL3RyZWUubW9kZWwnO1xyXG5pbXBvcnQgeyBUcmVlTm9kZSB9IGZyb20gJy4uLy4uL2NvcmUvbW9kZWxzL3RyZWUtbm9kZS5tb2RlbCc7XHJcbmltcG9ydCB7IFRyZWVPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29yZS9tb2RlbHMvdHJlZS1vcHRpb25zLm1vZGVsJztcclxuaW1wb3J0IHsgVHJlZU5vZGVDb21wb25lbnQgfSBmcm9tICcuLi90cmVlLW5vZGUvdHJlZS1ub2RlLmNvbXBvbmVudCc7XHJcbmltcG9ydCB7IGZyb21FdmVudCwgU3ViamVjdCwgdGFrZVVudGlsIH0gZnJvbSAncnhqcyc7XHJcbmltcG9ydCB7IGRlYm91bmNlVGltZSB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcclxuXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAndHJlZS12aWV3cG9ydCcsXHJcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcclxuICBpbXBvcnRzOiBbQ29tbW9uTW9kdWxlLCBUcmVlTm9kZUNvbXBvbmVudF0sXHJcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXHJcbiAgdGVtcGxhdGU6IGBcclxuICAgIDxkaXYgXHJcbiAgICAgICNzY3JvbGxDb250YWluZXJcclxuICAgICAgY2xhc3M9XCJ0cmVlLXZpZXdwb3J0LWNvbnRhaW5lclwiXHJcbiAgICAgIFtzdHlsZS5oZWlnaHQucHhdPVwib3B0aW9ucy5zY3JvbGxDb250YWluZXJIZWlnaHQgfHwgNDAwXCJcclxuICAgICAgKHNjcm9sbCk9XCJvblNjcm9sbCgpXCI+XHJcbiAgICAgIFxyXG4gICAgICA8ZGl2IGNsYXNzPVwidHJlZS12aWV3cG9ydC1jb250ZW50XCJcclxuICAgICAgICAgICBbc3R5bGUuaGVpZ2h0LnB4XT1cInRvdGFsSGVpZ2h0KClcIj5cclxuICAgICAgICBcclxuICAgICAgICA8ZGl2IGNsYXNzPVwidHJlZS12aWV3cG9ydC1wYWRkaW5nLXRvcFwiXHJcbiAgICAgICAgICAgICBbc3R5bGUuaGVpZ2h0LnB4XT1cInBhZGRpbmdUb3AoKVwiPlxyXG4gICAgICAgIDwvZGl2PlxyXG4gICAgICAgIFxyXG4gICAgICAgIEBmb3IgKG5vZGUgb2YgdmlzaWJsZU5vZGVzKCk7IHRyYWNrIG5vZGUuaWQpIHtcclxuICAgICAgICAgIDx0cmVlLW5vZGVcclxuICAgICAgICAgICAgW25vZGVdPVwibm9kZVwiXHJcbiAgICAgICAgICAgIFt0cmVlTW9kZWxdPVwidHJlZU1vZGVsXCJcclxuICAgICAgICAgICAgW29wdGlvbnNdPVwib3B0aW9uc1wiXHJcbiAgICAgICAgICAgIFtub2RlVGVtcGxhdGVdPVwibm9kZVRlbXBsYXRlXCI+XHJcbiAgICAgICAgICA8L3RyZWUtbm9kZT5cclxuICAgICAgICB9XHJcbiAgICAgICAgXHJcbiAgICAgICAgPGRpdiBjbGFzcz1cInRyZWUtdmlld3BvcnQtcGFkZGluZy1ib3R0b21cIlxyXG4gICAgICAgICAgICAgW3N0eWxlLmhlaWdodC5weF09XCJwYWRkaW5nQm90dG9tKClcIj5cclxuICAgICAgICA8L2Rpdj5cclxuICAgICAgPC9kaXY+XHJcbiAgICA8L2Rpdj5cclxuICBgLFxyXG4gIHN0eWxlczogW2BcclxuICAgIC50cmVlLXZpZXdwb3J0LWNvbnRhaW5lciB7XHJcbiAgICAgIG92ZXJmbG93LXk6IGF1dG87XHJcbiAgICAgIG92ZXJmbG93LXg6IGhpZGRlbjtcclxuICAgICAgcG9zaXRpb246IHJlbGF0aXZlO1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICAudHJlZS12aWV3cG9ydC1jb250ZW50IHtcclxuICAgICAgcG9zaXRpb246IHJlbGF0aXZlO1xyXG4gICAgICB3aWR0aDogMTAwJTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgLnRyZWUtdmlld3BvcnQtcGFkZGluZy10b3AsXHJcbiAgICAudHJlZS12aWV3cG9ydC1wYWRkaW5nLWJvdHRvbSB7XHJcbiAgICAgIHdpZHRoOiAxMDAlO1xyXG4gICAgfVxyXG4gIGBdLFxyXG59KVxyXG5leHBvcnQgY2xhc3MgVHJlZVZpZXdwb3J0Q29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3kge1xyXG4gIEBJbnB1dCh7IHJlcXVpcmVkOiB0cnVlIH0pIHRyZWVNb2RlbCE6IFRyZWVNb2RlbDtcclxuICBASW5wdXQoeyByZXF1aXJlZDogdHJ1ZSB9KSBvcHRpb25zITogVHJlZU9wdGlvbnM7XHJcbiAgQElucHV0KCkgbm9kZVRlbXBsYXRlPzogVGVtcGxhdGVSZWY8YW55PjtcclxuICBcclxuICBAVmlld0NoaWxkKCdzY3JvbGxDb250YWluZXInLCB7IHN0YXRpYzogdHJ1ZSB9KSBcclxuICBzY3JvbGxDb250YWluZXIhOiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PjtcclxuICBcclxuICBwcml2YXRlIGRlc3Ryb3kkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcclxuICBcclxuICByZWFkb25seSBzY3JvbGxUb3AgPSBzaWduYWwoMCk7XHJcbiAgcmVhZG9ubHkgdmlld3BvcnRIZWlnaHQgPSBzaWduYWwoMCk7XHJcbiAgXHJcbiAgcmVhZG9ubHkgZmxhdHRlbmVkTm9kZXMgPSBjb21wdXRlZCgoKSA9PiB7XHJcbiAgICByZXR1cm4gdGhpcy50cmVlTW9kZWwuZmxhdHRlbmVkTm9kZXMoKTtcclxuICB9KTtcclxuICBcclxuICByZWFkb25seSB0b3RhbEhlaWdodCA9IGNvbXB1dGVkKCgpID0+IHtcclxuICAgIGNvbnN0IG5vZGVzID0gdGhpcy5mbGF0dGVuZWROb2RlcygpO1xyXG4gICAgY29uc3Qgbm9kZUhlaWdodCA9IHRoaXMub3B0aW9ucy5ub2RlSGVpZ2h0IHx8IDIyO1xyXG4gICAgXHJcbiAgICBpZiAodHlwZW9mIG5vZGVIZWlnaHQgPT09ICdudW1iZXInKSB7XHJcbiAgICAgIHJldHVybiBub2Rlcy5sZW5ndGggKiBub2RlSGVpZ2h0O1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICByZXR1cm4gbm9kZXMucmVkdWNlKChzdW0sIG5vZGUpID0+IHN1bSArIG5vZGVIZWlnaHQobm9kZSksIDApO1xyXG4gIH0pO1xyXG4gIFxyXG4gIHJlYWRvbmx5IHZpc2libGVSYW5nZSA9IGNvbXB1dGVkKCgpID0+IHtcclxuICAgIGNvbnN0IHNjcm9sbFRvcCA9IHRoaXMuc2Nyb2xsVG9wKCk7XHJcbiAgICBjb25zdCB2aWV3cG9ydEhlaWdodCA9IHRoaXMudmlld3BvcnRIZWlnaHQoKTtcclxuICAgIGNvbnN0IG5vZGVzID0gdGhpcy5mbGF0dGVuZWROb2RlcygpO1xyXG4gICAgY29uc3Qgbm9kZUhlaWdodCA9IHRoaXMub3B0aW9ucy5ub2RlSGVpZ2h0IHx8IDIyO1xyXG4gICAgY29uc3QgYnVmZmVyQW1vdW50ID0gdGhpcy5vcHRpb25zLmJ1ZmZlckFtb3VudCB8fCA1O1xyXG4gICAgXHJcbiAgICBsZXQgc3RhcnRJbmRleCA9IDA7XHJcbiAgICBsZXQgYWNjdW11bGF0ZWRIZWlnaHQgPSAwO1xyXG4gICAgXHJcbiAgICAvLyBGaW5kIHN0YXJ0IGluZGV4XHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5vZGVzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgIGNvbnN0IGhlaWdodCA9IHR5cGVvZiBub2RlSGVpZ2h0ID09PSAnbnVtYmVyJyBcclxuICAgICAgICA/IG5vZGVIZWlnaHQgXHJcbiAgICAgICAgOiBub2RlSGVpZ2h0KG5vZGVzW2ldKTtcclxuICAgICAgXHJcbiAgICAgIGlmIChhY2N1bXVsYXRlZEhlaWdodCArIGhlaWdodCA+IHNjcm9sbFRvcCkge1xyXG4gICAgICAgIHN0YXJ0SW5kZXggPSBNYXRoLm1heCgwLCBpIC0gYnVmZmVyQW1vdW50KTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgfVxyXG4gICAgICBhY2N1bXVsYXRlZEhlaWdodCArPSBoZWlnaHQ7XHJcbiAgICB9XHJcbiAgICBcclxuICAgIC8vIEZpbmQgZW5kIGluZGV4XHJcbiAgICBsZXQgZW5kSW5kZXggPSBzdGFydEluZGV4O1xyXG4gICAgYWNjdW11bGF0ZWRIZWlnaHQgPSAwO1xyXG4gICAgXHJcbiAgICBmb3IgKGxldCBpID0gc3RhcnRJbmRleDsgaSA8IG5vZGVzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgIGNvbnN0IGhlaWdodCA9IHR5cGVvZiBub2RlSGVpZ2h0ID09PSAnbnVtYmVyJ1xyXG4gICAgICAgID8gbm9kZUhlaWdodFxyXG4gICAgICAgIDogbm9kZUhlaWdodChub2Rlc1tpXSk7XHJcbiAgICAgIFxyXG4gICAgICBhY2N1bXVsYXRlZEhlaWdodCArPSBoZWlnaHQ7XHJcbiAgICAgIFxyXG4gICAgICBpZiAoYWNjdW11bGF0ZWRIZWlnaHQgPiB2aWV3cG9ydEhlaWdodCArIChidWZmZXJBbW91bnQgKiAodHlwZW9mIG5vZGVIZWlnaHQgPT09ICdudW1iZXInID8gbm9kZUhlaWdodCA6IDIyKSkpIHtcclxuICAgICAgICBlbmRJbmRleCA9IGk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIFxyXG4gICAgaWYgKGVuZEluZGV4ID09PSBzdGFydEluZGV4KSB7XHJcbiAgICAgIGVuZEluZGV4ID0gbm9kZXMubGVuZ3RoO1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICByZXR1cm4geyBzdGFydEluZGV4LCBlbmRJbmRleCB9O1xyXG4gIH0pO1xyXG4gIFxyXG4gIHJlYWRvbmx5IHZpc2libGVOb2RlcyA9IGNvbXB1dGVkKCgpID0+IHtcclxuICAgIGNvbnN0IHJhbmdlID0gdGhpcy52aXNpYmxlUmFuZ2UoKTtcclxuICAgIGNvbnN0IG5vZGVzID0gdGhpcy5mbGF0dGVuZWROb2RlcygpO1xyXG4gICAgcmV0dXJuIG5vZGVzLnNsaWNlKHJhbmdlLnN0YXJ0SW5kZXgsIHJhbmdlLmVuZEluZGV4KTtcclxuICB9KTtcclxuICBcclxuICByZWFkb25seSBwYWRkaW5nVG9wID0gY29tcHV0ZWQoKCkgPT4ge1xyXG4gICAgY29uc3QgcmFuZ2UgPSB0aGlzLnZpc2libGVSYW5nZSgpO1xyXG4gICAgY29uc3Qgbm9kZXMgPSB0aGlzLmZsYXR0ZW5lZE5vZGVzKCk7XHJcbiAgICBjb25zdCBub2RlSGVpZ2h0ID0gdGhpcy5vcHRpb25zLm5vZGVIZWlnaHQgfHwgMjI7XHJcbiAgICBcclxuICAgIGxldCBoZWlnaHQgPSAwO1xyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCByYW5nZS5zdGFydEluZGV4OyBpKyspIHtcclxuICAgICAgaGVpZ2h0ICs9IHR5cGVvZiBub2RlSGVpZ2h0ID09PSAnbnVtYmVyJyBcclxuICAgICAgICA/IG5vZGVIZWlnaHQgXHJcbiAgICAgICAgOiBub2RlSGVpZ2h0KG5vZGVzW2ldKTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgcmV0dXJuIGhlaWdodDtcclxuICB9KTtcclxuICBcclxuICByZWFkb25seSBwYWRkaW5nQm90dG9tID0gY29tcHV0ZWQoKCkgPT4ge1xyXG4gICAgY29uc3QgcmFuZ2UgPSB0aGlzLnZpc2libGVSYW5nZSgpO1xyXG4gICAgY29uc3Qgbm9kZXMgPSB0aGlzLmZsYXR0ZW5lZE5vZGVzKCk7XHJcbiAgICBjb25zdCBub2RlSGVpZ2h0ID0gdGhpcy5vcHRpb25zLm5vZGVIZWlnaHQgfHwgMjI7XHJcbiAgICBjb25zdCB0b3RhbEhlaWdodCA9IHRoaXMudG90YWxIZWlnaHQoKTtcclxuICAgIFxyXG4gICAgbGV0IHZpc2libGVIZWlnaHQgPSB0aGlzLnBhZGRpbmdUb3AoKTtcclxuICAgIFxyXG4gICAgZm9yIChsZXQgaSA9IHJhbmdlLnN0YXJ0SW5kZXg7IGkgPCByYW5nZS5lbmRJbmRleDsgaSsrKSB7XHJcbiAgICAgIHZpc2libGVIZWlnaHQgKz0gdHlwZW9mIG5vZGVIZWlnaHQgPT09ICdudW1iZXInXHJcbiAgICAgICAgPyBub2RlSGVpZ2h0XHJcbiAgICAgICAgOiBub2RlSGVpZ2h0KG5vZGVzW2ldKTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgcmV0dXJuIE1hdGgubWF4KDAsIHRvdGFsSGVpZ2h0IC0gdmlzaWJsZUhlaWdodCk7XHJcbiAgfSk7XHJcbiAgXHJcbiAgY29uc3RydWN0b3IoKSB7XHJcbiAgICAvLyBVcGRhdGUgdmlld3BvcnQgd2hlbiB0cmVlIGNoYW5nZXNcclxuICAgIGVmZmVjdCgoKSA9PiB7XHJcbiAgICAgIHRoaXMuZmxhdHRlbmVkTm9kZXMoKTsgLy8gU3Vic2NyaWJlIHRvIGNoYW5nZXNcclxuICAgICAgdGhpcy51cGRhdGVWaWV3cG9ydCgpO1xyXG4gICAgfSk7XHJcbiAgfVxyXG4gIFxyXG4gIG5nT25Jbml0KCk6IHZvaWQge1xyXG4gICAgdGhpcy5zZXR1cFNjcm9sbExpc3RlbmVyKCk7XHJcbiAgICB0aGlzLnVwZGF0ZVZpZXdwb3J0SGVpZ2h0KCk7XHJcbiAgfVxyXG4gIFxyXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xyXG4gICAgdGhpcy5kZXN0cm95JC5uZXh0KCk7XHJcbiAgICB0aGlzLmRlc3Ryb3kkLmNvbXBsZXRlKCk7XHJcbiAgfVxyXG4gIFxyXG4gIHByaXZhdGUgc2V0dXBTY3JvbGxMaXN0ZW5lcigpOiB2b2lkIHtcclxuICAgIGZyb21FdmVudCh0aGlzLnNjcm9sbENvbnRhaW5lci5uYXRpdmVFbGVtZW50LCAnc2Nyb2xsJylcclxuICAgICAgLnBpcGUoXHJcbiAgICAgICAgZGVib3VuY2VUaW1lKDEwKSxcclxuICAgICAgICB0YWtlVW50aWwodGhpcy5kZXN0cm95JClcclxuICAgICAgKVxyXG4gICAgICAuc3Vic2NyaWJlKCgpID0+IHtcclxuICAgICAgICB0aGlzLm9uU2Nyb2xsKCk7XHJcbiAgICAgIH0pO1xyXG4gIH1cclxuICBcclxuICBwcml2YXRlIHVwZGF0ZVZpZXdwb3J0SGVpZ2h0KCk6IHZvaWQge1xyXG4gICAgY29uc3QgaGVpZ2h0ID0gdGhpcy5zY3JvbGxDb250YWluZXIubmF0aXZlRWxlbWVudC5jbGllbnRIZWlnaHQ7XHJcbiAgICB0aGlzLnZpZXdwb3J0SGVpZ2h0LnNldChoZWlnaHQpO1xyXG4gIH1cclxuICBcclxuICBvblNjcm9sbCgpOiB2b2lkIHtcclxuICAgIGNvbnN0IHNjcm9sbFRvcCA9IHRoaXMuc2Nyb2xsQ29udGFpbmVyLm5hdGl2ZUVsZW1lbnQuc2Nyb2xsVG9wO1xyXG4gICAgdGhpcy5zY3JvbGxUb3Auc2V0KHNjcm9sbFRvcCk7XHJcbiAgfVxyXG4gIFxyXG4gIHByaXZhdGUgdXBkYXRlVmlld3BvcnQoKTogdm9pZCB7XHJcbiAgICAvLyBGb3JjZSByZWNhbGN1bGF0aW9uXHJcbiAgICB0aGlzLnNjcm9sbFRvcC5zZXQodGhpcy5zY3JvbGxDb250YWluZXIubmF0aXZlRWxlbWVudC5zY3JvbGxUb3ApO1xyXG4gIH1cclxuICBcclxuICBzY3JvbGxUb05vZGUobm9kZTogVHJlZU5vZGUpOiB2b2lkIHtcclxuICAgIGNvbnN0IG5vZGVzID0gdGhpcy5mbGF0dGVuZWROb2RlcygpO1xyXG4gICAgY29uc3QgaW5kZXggPSBub2Rlcy5maW5kSW5kZXgobiA9PiBuLmlkID09PSBub2RlLmlkKTtcclxuICAgIFxyXG4gICAgaWYgKGluZGV4ID09PSAtMSkgcmV0dXJuO1xyXG4gICAgXHJcbiAgICBjb25zdCBub2RlSGVpZ2h0ID0gdGhpcy5vcHRpb25zLm5vZGVIZWlnaHQgfHwgMjI7XHJcbiAgICBsZXQgdGFyZ2V0U2Nyb2xsVG9wID0gMDtcclxuICAgIFxyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBpbmRleDsgaSsrKSB7XHJcbiAgICAgIHRhcmdldFNjcm9sbFRvcCArPSB0eXBlb2Ygbm9kZUhlaWdodCA9PT0gJ251bWJlcidcclxuICAgICAgICA/IG5vZGVIZWlnaHRcclxuICAgICAgICA6IG5vZGVIZWlnaHQobm9kZXNbaV0pO1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICB0aGlzLnNjcm9sbENvbnRhaW5lci5uYXRpdmVFbGVtZW50LnNjcm9sbFRvcCA9IHRhcmdldFNjcm9sbFRvcDtcclxuICB9XHJcbn0iXX0=
|