@memberjunction/ng-trees 0.0.1 → 3.1.0

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.
@@ -0,0 +1,1012 @@
1
+ /**
2
+ * Tree Dropdown Component for @memberjunction/ng-trees
3
+ *
4
+ * A searchable dropdown with tree selection. Features:
5
+ * - Smart positioning (auto-flips above/below based on available space)
6
+ * - Portal rendering to avoid clipping
7
+ * - Type-ahead search with highlighting
8
+ * - Keyboard navigation
9
+ * - Single and multi-select modes
10
+ */
11
+ import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
12
+ import { BeforeSearchEventArgs, AfterSearchEventArgs, BeforeDropdownOpenEventArgs, AfterDropdownOpenEventArgs, BeforeDropdownCloseEventArgs, AfterDropdownCloseEventArgs } from '../events/tree-events';
13
+ import { Subject } from 'rxjs';
14
+ import { debounceTime, takeUntil } from 'rxjs/operators';
15
+ import { Metadata, CompositeKey } from '@memberjunction/core';
16
+ import * as i0 from "@angular/core";
17
+ import * as i1 from "@angular/common";
18
+ import * as i2 from "../tree/tree.component";
19
+ const _c0 = ["triggerElement"];
20
+ const _c1 = ["searchInput"];
21
+ const _c2 = ["dropdownPanel"];
22
+ const _c3 = ["treeComponent"];
23
+ const _c4 = () => ({});
24
+ function TreeDropdownComponent_span_4_Template(rf, ctx) { if (rf & 1) {
25
+ i0.ɵɵelementStart(0, "span", 15);
26
+ i0.ɵɵelement(1, "i");
27
+ i0.ɵɵelementEnd();
28
+ } if (rf & 2) {
29
+ const ctx_r1 = i0.ɵɵnextContext();
30
+ i0.ɵɵstyleProp("color", ctx_r1.getDisplayColor());
31
+ i0.ɵɵadvance();
32
+ i0.ɵɵclassMap(ctx_r1.getDisplayIcon());
33
+ } }
34
+ function TreeDropdownComponent_span_7_Template(rf, ctx) { if (rf & 1) {
35
+ i0.ɵɵelementStart(0, "span", 16);
36
+ i0.ɵɵelement(1, "i", 17);
37
+ i0.ɵɵelementEnd();
38
+ } }
39
+ function TreeDropdownComponent_button_9_Template(rf, ctx) { if (rf & 1) {
40
+ const _r3 = i0.ɵɵgetCurrentView();
41
+ i0.ɵɵelementStart(0, "button", 18);
42
+ i0.ɵɵlistener("click", function TreeDropdownComponent_button_9_Template_button_click_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Clear($event)); });
43
+ i0.ɵɵelement(1, "i", 19);
44
+ i0.ɵɵelementEnd();
45
+ } }
46
+ function TreeDropdownComponent_div_12_div_2_button_5_Template(rf, ctx) { if (rf & 1) {
47
+ const _r6 = i0.ɵɵgetCurrentView();
48
+ i0.ɵɵelementStart(0, "button", 29);
49
+ i0.ɵɵlistener("click", function TreeDropdownComponent_div_12_div_2_button_5_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.onClearSearch()); });
50
+ i0.ɵɵelement(1, "i", 19);
51
+ i0.ɵɵelementEnd();
52
+ } }
53
+ function TreeDropdownComponent_div_12_div_2_Template(rf, ctx) { if (rf & 1) {
54
+ const _r5 = i0.ɵɵgetCurrentView();
55
+ i0.ɵɵelementStart(0, "div", 24)(1, "div", 25);
56
+ i0.ɵɵelement(2, "i", 26);
57
+ i0.ɵɵelementStart(3, "input", 27, 3);
58
+ i0.ɵɵlistener("input", function TreeDropdownComponent_div_12_div_2_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onSearchInput($event)); })("keydown", function TreeDropdownComponent_div_12_div_2_Template_input_keydown_3_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onSearchKeyDown($event)); });
59
+ i0.ɵɵelementEnd();
60
+ i0.ɵɵtemplate(5, TreeDropdownComponent_div_12_div_2_button_5_Template, 2, 0, "button", 28);
61
+ i0.ɵɵelementEnd()();
62
+ } if (rf & 2) {
63
+ const ctx_r1 = i0.ɵɵnextContext(2);
64
+ i0.ɵɵadvance(3);
65
+ i0.ɵɵproperty("placeholder", ctx_r1.SearchConfig.Placeholder || "Type to search...")("value", ctx_r1.SearchText);
66
+ i0.ɵɵadvance(2);
67
+ i0.ɵɵproperty("ngIf", ctx_r1.SearchText);
68
+ } }
69
+ function TreeDropdownComponent_div_12_Template(rf, ctx) { if (rf & 1) {
70
+ const _r4 = i0.ɵɵgetCurrentView();
71
+ i0.ɵɵelementStart(0, "div", 20, 1);
72
+ i0.ɵɵtemplate(2, TreeDropdownComponent_div_12_div_2_Template, 6, 3, "div", 21);
73
+ i0.ɵɵelementStart(3, "div", 22)(4, "mj-tree", 23, 2);
74
+ i0.ɵɵlistener("SelectionChange", function TreeDropdownComponent_div_12_Template_mj_tree_SelectionChange_4_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onTreeSelectionChange($event)); })("BeforeNodeSelect", function TreeDropdownComponent_div_12_Template_mj_tree_BeforeNodeSelect_4_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onTreeBeforeNodeSelect($event)); })("AfterNodeSelect", function TreeDropdownComponent_div_12_Template_mj_tree_AfterNodeSelect_4_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onTreeAfterNodeSelect($event)); })("BeforeDataLoad", function TreeDropdownComponent_div_12_Template_mj_tree_BeforeDataLoad_4_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onTreeBeforeDataLoad($event)); })("AfterDataLoad", function TreeDropdownComponent_div_12_Template_mj_tree_AfterDataLoad_4_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onTreeAfterDataLoad($event)); });
75
+ i0.ɵɵelementEnd()()();
76
+ } if (rf & 2) {
77
+ const ctx_r1 = i0.ɵɵnextContext();
78
+ i0.ɵɵclassMap(ctx_r1.StyleConfig.DropdownClass || "");
79
+ i0.ɵɵclassProp("tree-dropdown-panel--open", ctx_r1.IsOpen)("tree-dropdown-panel--above", ctx_r1.Position == null ? null : ctx_r1.Position.renderAbove)("tree-dropdown-panel--below", !(ctx_r1.Position == null ? null : ctx_r1.Position.renderAbove));
80
+ i0.ɵɵproperty("ngStyle", ctx_r1.IsOpen ? ctx_r1.getDropdownStyles() : i0.ɵɵpureFunction0(21, _c4));
81
+ i0.ɵɵadvance(2);
82
+ i0.ɵɵproperty("ngIf", ctx_r1.EnableSearch);
83
+ i0.ɵɵadvance(2);
84
+ i0.ɵɵproperty("BranchConfig", ctx_r1.BranchConfig)("LeafConfig", ctx_r1.LeafConfig)("SelectionMode", ctx_r1.SelectionMode)("SelectableTypes", ctx_r1.SelectableTypes)("SelectedIDs", ctx_r1.getSelectedIDsArray())("AutoLoad", ctx_r1.AutoLoad)("ShowIcons", true)("ShowExpandCollapseAll", false)("AnimateExpandCollapse", true)("EmptyMessage", ctx_r1.SearchText ? "No matches found" : "No items available")("EmptyIcon", ctx_r1.SearchText ? "fa-solid fa-search" : "fa-solid fa-folder-open");
85
+ } }
86
+ export class TreeDropdownComponent {
87
+ cdr;
88
+ renderer;
89
+ // ========================================
90
+ // Tree Configuration (passed to inner tree)
91
+ // ========================================
92
+ /** Branch (category) entity configuration - REQUIRED */
93
+ BranchConfig;
94
+ /** Optional leaf entity configuration */
95
+ LeafConfig;
96
+ /** Selection mode: 'single' or 'multiple' */
97
+ SelectionMode = 'single';
98
+ /** What types can be selected: 'branch', 'leaf', or 'both' */
99
+ SelectableTypes = 'both';
100
+ // ========================================
101
+ // Value Inputs
102
+ // ========================================
103
+ /** Current selected value (CompositeKey for single, CompositeKeys for multiple) */
104
+ _value = null;
105
+ /**
106
+ * The selected value as a CompositeKey (single select) or array of CompositeKeys (multi-select).
107
+ * CompositeKey supports both simple single-field primary keys and composite primary keys.
108
+ *
109
+ * @example Single select with simple ID:
110
+ * ```typescript
111
+ * dropdown.Value = CompositeKey.FromID('some-guid');
112
+ * ```
113
+ *
114
+ * @example Single select with composite key:
115
+ * ```typescript
116
+ * dropdown.Value = new CompositeKey([
117
+ * { FieldName: 'Field1', Value: 'value1' },
118
+ * { FieldName: 'Field2', Value: 'value2' }
119
+ * ]);
120
+ * ```
121
+ *
122
+ * @example Multi-select:
123
+ * ```typescript
124
+ * dropdown.Value = [
125
+ * CompositeKey.FromID('guid1'),
126
+ * CompositeKey.FromID('guid2')
127
+ * ];
128
+ * ```
129
+ */
130
+ set Value(val) {
131
+ if (!CompositeKey.EqualsEx(val, this._value)) {
132
+ this._value = val;
133
+ // If tree is loaded, sync selection immediately
134
+ if (this.IsLoaded && this.treeComponent) {
135
+ this.syncValueToSelection();
136
+ }
137
+ else {
138
+ // Tree not loaded yet - fetch display text directly via Metadata
139
+ this.fetchDisplayTextForValue(val);
140
+ }
141
+ }
142
+ }
143
+ get Value() {
144
+ return this._value;
145
+ }
146
+ /** Cached display text for showing in trigger before tree loads */
147
+ _pendingDisplayText = null;
148
+ // ========================================
149
+ // Dropdown-specific Inputs
150
+ // ========================================
151
+ /** Placeholder text when nothing selected */
152
+ Placeholder = 'Select...';
153
+ /** Enable search filtering */
154
+ EnableSearch = true;
155
+ /** Search configuration */
156
+ SearchConfig = {};
157
+ /** Dropdown configuration */
158
+ DropdownConfig = {};
159
+ /** Style configuration */
160
+ StyleConfig = {};
161
+ /** Show clear button */
162
+ Clearable = true;
163
+ /** Disabled state */
164
+ Disabled = false;
165
+ /** Show node icons in display */
166
+ ShowIconInDisplay = true;
167
+ /** Auto-load data on init */
168
+ AutoLoad = true;
169
+ /** Show loading in trigger */
170
+ ShowLoadingInTrigger = true;
171
+ // ========================================
172
+ // Event Outputs
173
+ // ========================================
174
+ /** Emitted when value changes */
175
+ ValueChange = new EventEmitter();
176
+ /** Emitted with full node(s) when selection changes */
177
+ SelectionChange = new EventEmitter();
178
+ // Bubble up tree events
179
+ BeforeNodeSelect = new EventEmitter();
180
+ AfterNodeSelect = new EventEmitter();
181
+ BeforeSearch = new EventEmitter();
182
+ AfterSearch = new EventEmitter();
183
+ BeforeDropdownOpen = new EventEmitter();
184
+ AfterDropdownOpen = new EventEmitter();
185
+ BeforeDropdownClose = new EventEmitter();
186
+ AfterDropdownClose = new EventEmitter();
187
+ BeforeDataLoad = new EventEmitter();
188
+ AfterDataLoad = new EventEmitter();
189
+ // ========================================
190
+ // ViewChild References
191
+ // ========================================
192
+ triggerElement;
193
+ searchInput;
194
+ dropdownPanel;
195
+ treeComponent;
196
+ // ========================================
197
+ // State
198
+ // ========================================
199
+ /** Is dropdown open */
200
+ IsOpen = false;
201
+ /** Dropdown position */
202
+ Position = null;
203
+ /** Search text */
204
+ SearchText = '';
205
+ /** Selected nodes (for display) */
206
+ SelectedNodes = [];
207
+ /** Is tree loading */
208
+ IsLoading = false;
209
+ /** Has data loaded */
210
+ IsLoaded = false;
211
+ /** Dropdown portal element */
212
+ dropdownPortal = null;
213
+ /** Search debounce subject */
214
+ searchSubject = new Subject();
215
+ /** Destroy subject */
216
+ destroy$ = new Subject();
217
+ /** Click outside listener */
218
+ clickOutsideListener = null;
219
+ /** Scroll listener */
220
+ scrollListener = null;
221
+ /** Resize listener */
222
+ resizeListener = null;
223
+ /** Pending load promise resolvers */
224
+ _loadResolvers = [];
225
+ // ========================================
226
+ // Constructor
227
+ // ========================================
228
+ constructor(cdr, renderer) {
229
+ this.cdr = cdr;
230
+ this.renderer = renderer;
231
+ }
232
+ // ========================================
233
+ // Lifecycle
234
+ // ========================================
235
+ ngOnInit() {
236
+ // Setup search debounce
237
+ const debounceMs = this.SearchConfig.DebounceMs ?? 200;
238
+ this.searchSubject.pipe(debounceTime(debounceMs), takeUntil(this.destroy$)).subscribe(text => {
239
+ this.performSearch(text);
240
+ });
241
+ }
242
+ ngAfterViewInit() {
243
+ // Create portal element
244
+ this.createDropdownPortal();
245
+ }
246
+ // Note: Value changes are handled by the getter/setter pattern
247
+ // No ngOnChanges needed for that property
248
+ ngOnDestroy() {
249
+ this.destroy$.next();
250
+ this.destroy$.complete();
251
+ this.removeDropdownPortal();
252
+ this.removeEventListeners();
253
+ }
254
+ // ========================================
255
+ // Public Methods
256
+ // ========================================
257
+ /**
258
+ * Returns a promise that resolves when the tree data has finished loading.
259
+ * If data is already loaded, the promise resolves immediately.
260
+ *
261
+ * Use this method when you need to perform operations that depend on the tree
262
+ * being fully loaded, such as programmatically selecting nodes or accessing
263
+ * the tree structure.
264
+ *
265
+ * Note: For setting initial values, you typically don't need this method -
266
+ * just set the `Value` input and the component will automatically display
267
+ * the correct text by looking up the record name via Metadata.
268
+ *
269
+ * @returns A promise that resolves when the tree data is loaded
270
+ * @example
271
+ * ```typescript
272
+ * // Wait for tree to load before accessing tree structure
273
+ * await treeDropdown.WaitForDataLoad();
274
+ * const nodes = treeDropdown.treeComponent.Nodes;
275
+ * ```
276
+ */
277
+ WaitForDataLoad() {
278
+ if (this.IsLoaded) {
279
+ return Promise.resolve();
280
+ }
281
+ return new Promise((resolve) => {
282
+ this._loadResolvers.push(resolve);
283
+ });
284
+ }
285
+ /**
286
+ * Open the dropdown
287
+ */
288
+ Open() {
289
+ if (this.Disabled || this.IsOpen) {
290
+ return;
291
+ }
292
+ // Fire before event
293
+ const beforeEvent = new BeforeDropdownOpenEventArgs(this);
294
+ this.BeforeDropdownOpen.emit(beforeEvent);
295
+ if (beforeEvent.Cancel) {
296
+ return;
297
+ }
298
+ this.IsOpen = true;
299
+ this.calculatePosition();
300
+ this.attachEventListeners();
301
+ // Focus search input after opening
302
+ setTimeout(() => {
303
+ if (this.EnableSearch && this.searchInput) {
304
+ this.searchInput.nativeElement.focus();
305
+ }
306
+ }, 50);
307
+ // Fire after event
308
+ const afterEvent = new AfterDropdownOpenEventArgs(this, this.Position?.renderAbove ? 'above' : 'below');
309
+ this.AfterDropdownOpen.emit(afterEvent);
310
+ this.cdr.detectChanges();
311
+ }
312
+ /**
313
+ * Close the dropdown
314
+ */
315
+ Close(reason = 'programmatic') {
316
+ if (!this.IsOpen) {
317
+ return;
318
+ }
319
+ // Fire before event
320
+ const beforeEvent = new BeforeDropdownCloseEventArgs(this, reason);
321
+ this.BeforeDropdownClose.emit(beforeEvent);
322
+ if (beforeEvent.Cancel) {
323
+ return;
324
+ }
325
+ this.IsOpen = false;
326
+ this.SearchText = '';
327
+ this.clearSearch();
328
+ this.removeEventListeners();
329
+ // Fire after event
330
+ const afterEvent = new AfterDropdownCloseEventArgs(this, reason);
331
+ this.AfterDropdownClose.emit(afterEvent);
332
+ this.cdr.detectChanges();
333
+ }
334
+ /**
335
+ * Toggle dropdown
336
+ */
337
+ Toggle() {
338
+ if (this.IsOpen) {
339
+ this.Close('programmatic');
340
+ }
341
+ else {
342
+ this.Open();
343
+ }
344
+ }
345
+ /**
346
+ * Clear selection
347
+ */
348
+ Clear(event) {
349
+ if (event) {
350
+ event.stopPropagation();
351
+ }
352
+ this.SelectedNodes = [];
353
+ this._value = null;
354
+ this._pendingDisplayText = null;
355
+ if (this.treeComponent) {
356
+ this.treeComponent.ClearSelection();
357
+ }
358
+ this.ValueChange.emit(this._value);
359
+ this.SelectionChange.emit(null);
360
+ this.cdr.detectChanges();
361
+ }
362
+ /**
363
+ * Refresh tree data
364
+ */
365
+ async Refresh() {
366
+ if (this.treeComponent) {
367
+ await this.treeComponent.Refresh();
368
+ }
369
+ }
370
+ // ========================================
371
+ // Template Event Handlers
372
+ // ========================================
373
+ /**
374
+ * Handle trigger click
375
+ */
376
+ onTriggerClick() {
377
+ if (!this.Disabled) {
378
+ this.Toggle();
379
+ }
380
+ }
381
+ /**
382
+ * Handle trigger keydown
383
+ */
384
+ onTriggerKeyDown(event) {
385
+ if (this.Disabled) {
386
+ return;
387
+ }
388
+ switch (event.key) {
389
+ case 'Enter':
390
+ case ' ':
391
+ case 'ArrowDown':
392
+ event.preventDefault();
393
+ this.Open();
394
+ break;
395
+ case 'Escape':
396
+ if (this.IsOpen) {
397
+ event.preventDefault();
398
+ this.Close('escape');
399
+ }
400
+ break;
401
+ }
402
+ }
403
+ /**
404
+ * Handle search input
405
+ */
406
+ onSearchInput(event) {
407
+ const value = event.target.value;
408
+ this.SearchText = value;
409
+ this.searchSubject.next(value);
410
+ }
411
+ /**
412
+ * Handle search keydown
413
+ */
414
+ onSearchKeyDown(event) {
415
+ switch (event.key) {
416
+ case 'Escape':
417
+ event.preventDefault();
418
+ event.stopPropagation();
419
+ this.Close('escape');
420
+ break;
421
+ case 'ArrowDown':
422
+ event.preventDefault();
423
+ // Focus first tree node
424
+ if (this.treeComponent) {
425
+ const visibleNodes = this.getVisibleNodesInOrder(this.treeComponent.Nodes);
426
+ if (visibleNodes.length > 0) {
427
+ this.treeComponent.FocusedNode = visibleNodes[0];
428
+ this.cdr.detectChanges();
429
+ }
430
+ }
431
+ break;
432
+ }
433
+ }
434
+ /**
435
+ * Handle clear search
436
+ */
437
+ onClearSearch() {
438
+ this.SearchText = '';
439
+ this.clearSearch();
440
+ if (this.searchInput) {
441
+ this.searchInput.nativeElement.focus();
442
+ }
443
+ }
444
+ /**
445
+ * Handle tree selection change
446
+ */
447
+ onTreeSelectionChange(nodes) {
448
+ this.SelectedNodes = nodes;
449
+ // Clear pending display text since we now have real nodes
450
+ this._pendingDisplayText = null;
451
+ // Update value - convert node IDs to CompositeKeys
452
+ if (this.SelectionMode === 'single') {
453
+ this._value = nodes.length > 0 ? CompositeKey.FromID(nodes[0].ID) : null;
454
+ this.ValueChange.emit(this._value);
455
+ this.SelectionChange.emit(nodes.length > 0 ? nodes[0] : null);
456
+ // Close on selection in single mode (unless disabled)
457
+ // Only close if user actually selected something (not on empty selection from sync)
458
+ if (this.DropdownConfig.CloseOnSelect !== false && nodes.length > 0) {
459
+ this.Close('selection');
460
+ }
461
+ }
462
+ else {
463
+ this._value = nodes.map(n => CompositeKey.FromID(n.ID));
464
+ this.ValueChange.emit(this._value);
465
+ this.SelectionChange.emit(nodes.length > 0 ? nodes : null);
466
+ }
467
+ this.cdr.detectChanges();
468
+ }
469
+ /**
470
+ * Handle tree data load events
471
+ */
472
+ onTreeBeforeDataLoad(event) {
473
+ this.IsLoading = true;
474
+ this.BeforeDataLoad.emit(event);
475
+ this.cdr.detectChanges();
476
+ }
477
+ onTreeAfterDataLoad(event) {
478
+ this.IsLoading = false;
479
+ this.IsLoaded = true;
480
+ // Resolve all pending WaitForDataLoad() promises
481
+ const resolvers = this._loadResolvers;
482
+ this._loadResolvers = [];
483
+ for (const resolve of resolvers) {
484
+ resolve();
485
+ }
486
+ // Sync selection after load - defer to next microtask to ensure ViewChild is resolved
487
+ Promise.resolve().then(() => {
488
+ if (this.treeComponent) {
489
+ }
490
+ this.syncValueToSelection();
491
+ this.cdr.detectChanges();
492
+ });
493
+ this.AfterDataLoad.emit(event);
494
+ this.cdr.detectChanges();
495
+ }
496
+ /**
497
+ * Bubble tree events
498
+ */
499
+ onTreeBeforeNodeSelect(event) {
500
+ this.BeforeNodeSelect.emit(event);
501
+ }
502
+ onTreeAfterNodeSelect(event) {
503
+ this.AfterNodeSelect.emit(event);
504
+ }
505
+ // ========================================
506
+ // Private Methods
507
+ // ========================================
508
+ /**
509
+ * Create dropdown portal element (attached to body)
510
+ */
511
+ createDropdownPortal() {
512
+ this.dropdownPortal = this.renderer.createElement('div');
513
+ this.renderer.addClass(this.dropdownPortal, 'mj-tree-dropdown-portal');
514
+ this.renderer.setStyle(this.dropdownPortal, 'position', 'fixed');
515
+ this.renderer.setStyle(this.dropdownPortal, 'z-index', '10000');
516
+ this.renderer.setStyle(this.dropdownPortal, 'display', 'none');
517
+ this.renderer.appendChild(document.body, this.dropdownPortal);
518
+ }
519
+ /**
520
+ * Remove dropdown portal
521
+ */
522
+ removeDropdownPortal() {
523
+ if (this.dropdownPortal && this.dropdownPortal.parentNode) {
524
+ this.dropdownPortal.parentNode.removeChild(this.dropdownPortal);
525
+ this.dropdownPortal = null;
526
+ }
527
+ }
528
+ /**
529
+ * Calculate dropdown position
530
+ */
531
+ calculatePosition() {
532
+ if (!this.triggerElement) {
533
+ return;
534
+ }
535
+ const triggerRect = this.triggerElement.nativeElement.getBoundingClientRect();
536
+ const viewportHeight = window.innerHeight;
537
+ const viewportWidth = window.innerWidth;
538
+ // Parse max height from config
539
+ const maxHeightConfig = this.DropdownConfig.MaxHeight || '300px';
540
+ const maxHeightPx = parseInt(maxHeightConfig, 10) || 300;
541
+ // Calculate available space
542
+ const spaceBelow = viewportHeight - triggerRect.bottom - 8; // 8px margin
543
+ const spaceAbove = triggerRect.top - 8;
544
+ // Determine if we should render above or below
545
+ let renderAbove = false;
546
+ let maxHeight = maxHeightPx;
547
+ if (this.DropdownConfig.Position === 'above') {
548
+ renderAbove = true;
549
+ maxHeight = Math.min(maxHeightPx, spaceAbove);
550
+ }
551
+ else if (this.DropdownConfig.Position === 'below') {
552
+ renderAbove = false;
553
+ maxHeight = Math.min(maxHeightPx, spaceBelow);
554
+ }
555
+ else {
556
+ // Auto position
557
+ if (spaceBelow < maxHeightPx && spaceAbove > spaceBelow) {
558
+ renderAbove = true;
559
+ maxHeight = Math.min(maxHeightPx, spaceAbove);
560
+ }
561
+ else {
562
+ renderAbove = false;
563
+ maxHeight = Math.min(maxHeightPx, spaceBelow);
564
+ }
565
+ }
566
+ // Calculate width
567
+ const minWidth = this.DropdownConfig.MinWidth
568
+ ? parseInt(this.DropdownConfig.MinWidth, 10)
569
+ : triggerRect.width;
570
+ const width = Math.max(minWidth, triggerRect.width);
571
+ // Ensure dropdown doesn't go off screen horizontally
572
+ let left = triggerRect.left;
573
+ if (left + width > viewportWidth - 8) {
574
+ left = viewportWidth - width - 8;
575
+ }
576
+ if (left < 8) {
577
+ left = 8;
578
+ }
579
+ // Calculate top position
580
+ let top;
581
+ if (renderAbove) {
582
+ top = triggerRect.top - maxHeight - 4;
583
+ }
584
+ else {
585
+ top = triggerRect.bottom + 4;
586
+ }
587
+ this.Position = {
588
+ top,
589
+ left,
590
+ width,
591
+ maxHeight,
592
+ renderAbove
593
+ };
594
+ }
595
+ /**
596
+ * Attach event listeners for click outside, scroll, resize
597
+ */
598
+ attachEventListeners() {
599
+ // Click outside - defer with a small timeout to:
600
+ // 1. Allow the opening click event to complete
601
+ // 2. Ensure the dropdown panel DOM element is fully rendered
602
+ // 3. Allow Angular change detection to complete
603
+ if (this.DropdownConfig.CloseOnOutsideClick !== false) {
604
+ setTimeout(() => {
605
+ // Only attach if still open (could have been closed in the meantime)
606
+ if (!this.IsOpen) {
607
+ return;
608
+ }
609
+ this.clickOutsideListener = this.renderer.listen('document', 'click', (event) => {
610
+ const target = event.target;
611
+ // Double check we're still open
612
+ if (!this.IsOpen) {
613
+ return;
614
+ }
615
+ const isInsideTrigger = this.triggerElement?.nativeElement?.contains(target);
616
+ // Check if click is inside the dropdown panel (rendered inline, not in portal)
617
+ const isInsideDropdown = this.dropdownPanel?.nativeElement?.contains(target);
618
+ if (!isInsideTrigger && !isInsideDropdown) {
619
+ this.Close('outsideClick');
620
+ }
621
+ else {
622
+ }
623
+ });
624
+ }, 100); // 100ms delay to ensure DOM is stable
625
+ }
626
+ else {
627
+ }
628
+ // Escape key
629
+ if (this.DropdownConfig.CloseOnEscape !== false) {
630
+ document.addEventListener('keydown', this.handleEscapeKey);
631
+ }
632
+ // Scroll - reposition dropdown
633
+ this.scrollListener = this.renderer.listen('window', 'scroll', () => {
634
+ if (this.IsOpen) {
635
+ this.calculatePosition();
636
+ this.updateDropdownPortalPosition();
637
+ }
638
+ });
639
+ // Resize - reposition dropdown
640
+ this.resizeListener = this.renderer.listen('window', 'resize', () => {
641
+ if (this.IsOpen) {
642
+ this.calculatePosition();
643
+ this.updateDropdownPortalPosition();
644
+ }
645
+ });
646
+ }
647
+ /**
648
+ * Remove event listeners
649
+ */
650
+ removeEventListeners() {
651
+ if (this.clickOutsideListener) {
652
+ this.clickOutsideListener();
653
+ this.clickOutsideListener = null;
654
+ }
655
+ document.removeEventListener('keydown', this.handleEscapeKey);
656
+ if (this.scrollListener) {
657
+ this.scrollListener();
658
+ this.scrollListener = null;
659
+ }
660
+ if (this.resizeListener) {
661
+ this.resizeListener();
662
+ this.resizeListener = null;
663
+ }
664
+ }
665
+ /**
666
+ * Handle escape key
667
+ */
668
+ handleEscapeKey = (event) => {
669
+ if (event.key === 'Escape' && this.IsOpen) {
670
+ this.Close('escape');
671
+ }
672
+ };
673
+ /**
674
+ * Update dropdown portal position
675
+ */
676
+ updateDropdownPortalPosition() {
677
+ if (!this.dropdownPortal || !this.Position) {
678
+ return;
679
+ }
680
+ this.renderer.setStyle(this.dropdownPortal, 'top', `${this.Position.top}px`);
681
+ this.renderer.setStyle(this.dropdownPortal, 'left', `${this.Position.left}px`);
682
+ this.renderer.setStyle(this.dropdownPortal, 'width', `${this.Position.width}px`);
683
+ }
684
+ /**
685
+ * Perform search on tree
686
+ */
687
+ performSearch(text) {
688
+ // Fire before event
689
+ const beforeEvent = new BeforeSearchEventArgs(this, text);
690
+ this.BeforeSearch.emit(beforeEvent);
691
+ if (beforeEvent.Cancel) {
692
+ return;
693
+ }
694
+ const searchText = beforeEvent.ModifiedSearchText ?? text;
695
+ if (!this.treeComponent) {
696
+ return;
697
+ }
698
+ const matchedNodes = this.treeComponent.FilterNodes(searchText, {
699
+ caseSensitive: this.SearchConfig.CaseSensitive,
700
+ searchBranches: this.SearchConfig.SearchBranches ?? true,
701
+ searchLeaves: this.SearchConfig.SearchLeaves ?? true,
702
+ searchDescription: this.SearchConfig.SearchDescription
703
+ });
704
+ // Auto-expand to show matches
705
+ if (this.SearchConfig.AutoExpandMatches !== false && searchText.trim()) {
706
+ for (const node of matchedNodes) {
707
+ this.treeComponent.ExpandToNode(node.ID);
708
+ }
709
+ }
710
+ // Fire after event
711
+ const afterEvent = new AfterSearchEventArgs(this, searchText, matchedNodes);
712
+ this.AfterSearch.emit(afterEvent);
713
+ this.cdr.detectChanges();
714
+ }
715
+ /**
716
+ * Clear search filter
717
+ */
718
+ clearSearch() {
719
+ if (this.treeComponent) {
720
+ this.treeComponent.FilterNodes('', {});
721
+ this.cdr.detectChanges();
722
+ }
723
+ }
724
+ /**
725
+ * Get all visible nodes in tree order (for keyboard navigation)
726
+ */
727
+ getVisibleNodesInOrder(nodes) {
728
+ const result = [];
729
+ this.collectVisibleNodesRecursive(nodes, result);
730
+ return result;
731
+ }
732
+ collectVisibleNodesRecursive(nodes, result) {
733
+ for (const node of nodes) {
734
+ if (node.Visible) {
735
+ result.push(node);
736
+ if (node.Expanded && node.Type === 'branch') {
737
+ this.collectVisibleNodesRecursive(node.Children, result);
738
+ }
739
+ }
740
+ }
741
+ }
742
+ /**
743
+ * Sync value to tree selection
744
+ */
745
+ syncValueToSelection() {
746
+ if (!this.treeComponent || !this.IsLoaded) {
747
+ return;
748
+ }
749
+ // Convert CompositeKey(s) to string IDs for tree selection
750
+ const ids = this.getSelectedIDsArray();
751
+ // Pass emitChange=false to avoid emitting SelectionChange during sync
752
+ // This prevents unnecessary events and parent component confusion
753
+ this.treeComponent.SelectNodes(ids, false);
754
+ // Use try-catch as defensive measure since tree component may not be fully ready
755
+ try {
756
+ this.SelectedNodes = this.treeComponent.GetSelectedNodes() || [];
757
+ }
758
+ catch {
759
+ this.SelectedNodes = [];
760
+ }
761
+ // Clear pending display text since we now have real nodes
762
+ this._pendingDisplayText = null;
763
+ this.cdr.detectChanges();
764
+ }
765
+ /**
766
+ * Fetch display text for value before tree loads using Metadata.GetEntityRecordName
767
+ */
768
+ async fetchDisplayTextForValue(val) {
769
+ if (!val || !this.LeafConfig) {
770
+ this._pendingDisplayText = null;
771
+ this.cdr.detectChanges();
772
+ return;
773
+ }
774
+ try {
775
+ const md = new Metadata();
776
+ const entityName = this.LeafConfig.EntityName;
777
+ if (Array.isArray(val)) {
778
+ // Multiple selection
779
+ if (val.length === 0) {
780
+ this._pendingDisplayText = null;
781
+ }
782
+ else if (val.length === 1) {
783
+ this._pendingDisplayText = await md.GetEntityRecordName(entityName, val[0]);
784
+ }
785
+ else {
786
+ this._pendingDisplayText = `${val.length} items selected`;
787
+ }
788
+ }
789
+ else {
790
+ // Single selection
791
+ this._pendingDisplayText = await md.GetEntityRecordName(entityName, val);
792
+ }
793
+ }
794
+ catch (error) {
795
+ console.warn('[TreeDropdown] Failed to fetch display text:', error);
796
+ this._pendingDisplayText = null;
797
+ }
798
+ this.cdr.detectChanges();
799
+ }
800
+ // ========================================
801
+ // Template Helpers
802
+ // ========================================
803
+ /**
804
+ * Get display text for selected value(s)
805
+ */
806
+ getDisplayText() {
807
+ // If we have selected nodes from the tree, use those
808
+ if (this.SelectedNodes.length > 0) {
809
+ if (this.SelectionMode === 'single') {
810
+ return this.SelectedNodes[0].Label;
811
+ }
812
+ // Multiple selection
813
+ if (this.SelectedNodes.length === 1) {
814
+ return this.SelectedNodes[0].Label;
815
+ }
816
+ return `${this.SelectedNodes.length} items selected`;
817
+ }
818
+ // If tree not loaded but we have pending display text from Metadata lookup
819
+ if (this._pendingDisplayText) {
820
+ return this._pendingDisplayText;
821
+ }
822
+ return '';
823
+ }
824
+ /**
825
+ * Get display icon for single selection
826
+ */
827
+ getDisplayIcon() {
828
+ if (!this.ShowIconInDisplay || this.SelectedNodes.length !== 1) {
829
+ return null;
830
+ }
831
+ return this.SelectedNodes[0].Icon;
832
+ }
833
+ /**
834
+ * Get display color for single selection
835
+ */
836
+ getDisplayColor() {
837
+ if (this.SelectedNodes.length !== 1) {
838
+ return null;
839
+ }
840
+ return this.SelectedNodes[0].Color || null;
841
+ }
842
+ /**
843
+ * Check if has selection
844
+ */
845
+ hasSelection() {
846
+ return this.SelectedNodes.length > 0 || this._pendingDisplayText != null;
847
+ }
848
+ /**
849
+ * Get dropdown panel styles
850
+ */
851
+ getDropdownStyles() {
852
+ if (!this.Position) {
853
+ return {};
854
+ }
855
+ return {
856
+ top: `${this.Position.top}px`,
857
+ left: `${this.Position.left}px`,
858
+ width: `${this.Position.width}px`,
859
+ maxHeight: `${this.Position.maxHeight}px`
860
+ };
861
+ }
862
+ /**
863
+ * Get trigger classes
864
+ */
865
+ getTriggerClasses() {
866
+ return {
867
+ 'tree-dropdown-trigger': true,
868
+ 'tree-dropdown-trigger--open': this.IsOpen,
869
+ 'tree-dropdown-trigger--disabled': this.Disabled,
870
+ 'tree-dropdown-trigger--has-value': this.hasSelection(),
871
+ 'tree-dropdown-trigger--loading': this.IsLoading && this.ShowLoadingInTrigger
872
+ };
873
+ }
874
+ /**
875
+ * Get selected IDs as a string array for passing to tree component.
876
+ * Extracts the first key value from each CompositeKey (typically the ID field).
877
+ */
878
+ getSelectedIDsArray() {
879
+ if (!this._value) {
880
+ return [];
881
+ }
882
+ const keys = Array.isArray(this._value) ? this._value : [this._value];
883
+ return keys.map(key => {
884
+ // Get the first value from the composite key (usually the ID)
885
+ const firstValue = key.GetValueByIndex(0);
886
+ return firstValue != null ? String(firstValue) : '';
887
+ }).filter(id => id !== '');
888
+ }
889
+ static ɵfac = function TreeDropdownComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || TreeDropdownComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.Renderer2)); };
890
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TreeDropdownComponent, selectors: [["mj-tree-dropdown"]], viewQuery: function TreeDropdownComponent_Query(rf, ctx) { if (rf & 1) {
891
+ i0.ɵɵviewQuery(_c0, 5);
892
+ i0.ɵɵviewQuery(_c1, 5);
893
+ i0.ɵɵviewQuery(_c2, 5);
894
+ i0.ɵɵviewQuery(_c3, 5);
895
+ } if (rf & 2) {
896
+ let _t;
897
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.triggerElement = _t.first);
898
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.searchInput = _t.first);
899
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.dropdownPanel = _t.first);
900
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.treeComponent = _t.first);
901
+ } }, inputs: { BranchConfig: "BranchConfig", LeafConfig: "LeafConfig", SelectionMode: "SelectionMode", SelectableTypes: "SelectableTypes", Value: "Value", Placeholder: "Placeholder", EnableSearch: "EnableSearch", SearchConfig: "SearchConfig", DropdownConfig: "DropdownConfig", StyleConfig: "StyleConfig", Clearable: "Clearable", Disabled: "Disabled", ShowIconInDisplay: "ShowIconInDisplay", AutoLoad: "AutoLoad", ShowLoadingInTrigger: "ShowLoadingInTrigger" }, outputs: { ValueChange: "ValueChange", SelectionChange: "SelectionChange", BeforeNodeSelect: "BeforeNodeSelect", AfterNodeSelect: "AfterNodeSelect", BeforeSearch: "BeforeSearch", AfterSearch: "AfterSearch", BeforeDropdownOpen: "BeforeDropdownOpen", AfterDropdownOpen: "AfterDropdownOpen", BeforeDropdownClose: "BeforeDropdownClose", AfterDropdownClose: "AfterDropdownClose", BeforeDataLoad: "BeforeDataLoad", AfterDataLoad: "AfterDataLoad" }, decls: 13, vars: 16, consts: [["triggerElement", ""], ["dropdownPanel", ""], ["treeComponent", ""], ["searchInput", ""], [1, "tree-dropdown"], ["tabindex", "0", "role", "combobox", 3, "click", "keydown", "ngClass"], [1, "tree-dropdown-trigger__content"], ["class", "tree-dropdown-trigger__icon", 3, "color", 4, "ngIf"], [1, "tree-dropdown-trigger__text"], ["class", "tree-dropdown-trigger__loading", 4, "ngIf"], [1, "tree-dropdown-trigger__actions"], ["type", "button", "class", "tree-dropdown-trigger__clear", "title", "Clear selection", "tabindex", "-1", 3, "click", 4, "ngIf"], [1, "tree-dropdown-trigger__chevron"], [1, "fa-solid", 3, "ngClass"], ["class", "tree-dropdown-panel", "role", "tree", 3, "tree-dropdown-panel--open", "tree-dropdown-panel--above", "tree-dropdown-panel--below", "class", "ngStyle", 4, "ngIf"], [1, "tree-dropdown-trigger__icon"], [1, "tree-dropdown-trigger__loading"], [1, "fa-solid", "fa-spinner", "fa-spin"], ["type", "button", "title", "Clear selection", "tabindex", "-1", 1, "tree-dropdown-trigger__clear", 3, "click"], [1, "fa-solid", "fa-times"], ["role", "tree", 1, "tree-dropdown-panel", 3, "ngStyle"], ["class", "tree-dropdown-search", 4, "ngIf"], [1, "tree-dropdown-tree"], [3, "SelectionChange", "BeforeNodeSelect", "AfterNodeSelect", "BeforeDataLoad", "AfterDataLoad", "BranchConfig", "LeafConfig", "SelectionMode", "SelectableTypes", "SelectedIDs", "AutoLoad", "ShowIcons", "ShowExpandCollapseAll", "AnimateExpandCollapse", "EmptyMessage", "EmptyIcon"], [1, "tree-dropdown-search"], [1, "tree-dropdown-search__input-wrapper"], [1, "fa-solid", "fa-search", "tree-dropdown-search__icon"], ["type", "text", "autocomplete", "off", "spellcheck", "false", 1, "tree-dropdown-search__input", 3, "input", "keydown", "placeholder", "value"], ["type", "button", "class", "tree-dropdown-search__clear", "tabindex", "-1", 3, "click", 4, "ngIf"], ["type", "button", "tabindex", "-1", 1, "tree-dropdown-search__clear", 3, "click"]], template: function TreeDropdownComponent_Template(rf, ctx) { if (rf & 1) {
902
+ const _r1 = i0.ɵɵgetCurrentView();
903
+ i0.ɵɵelementStart(0, "div", 4)(1, "div", 5, 0);
904
+ i0.ɵɵlistener("click", function TreeDropdownComponent_Template_div_click_1_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onTriggerClick()); })("keydown", function TreeDropdownComponent_Template_div_keydown_1_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onTriggerKeyDown($event)); });
905
+ i0.ɵɵelementStart(3, "div", 6);
906
+ i0.ɵɵtemplate(4, TreeDropdownComponent_span_4_Template, 2, 4, "span", 7);
907
+ i0.ɵɵelementStart(5, "span", 8);
908
+ i0.ɵɵtext(6);
909
+ i0.ɵɵelementEnd();
910
+ i0.ɵɵtemplate(7, TreeDropdownComponent_span_7_Template, 2, 0, "span", 9);
911
+ i0.ɵɵelementEnd();
912
+ i0.ɵɵelementStart(8, "div", 10);
913
+ i0.ɵɵtemplate(9, TreeDropdownComponent_button_9_Template, 2, 0, "button", 11);
914
+ i0.ɵɵelementStart(10, "span", 12);
915
+ i0.ɵɵelement(11, "i", 13);
916
+ i0.ɵɵelementEnd()()();
917
+ i0.ɵɵtemplate(12, TreeDropdownComponent_div_12_Template, 6, 22, "div", 14);
918
+ i0.ɵɵelementEnd();
919
+ } if (rf & 2) {
920
+ i0.ɵɵclassProp("tree-dropdown--disabled", ctx.Disabled);
921
+ i0.ɵɵadvance();
922
+ i0.ɵɵclassMap(ctx.StyleConfig.SearchInputClass || "");
923
+ i0.ɵɵproperty("ngClass", ctx.getTriggerClasses());
924
+ i0.ɵɵattribute("aria-expanded", ctx.IsOpen)("aria-haspopup", "tree")("aria-disabled", ctx.Disabled);
925
+ i0.ɵɵadvance(3);
926
+ i0.ɵɵproperty("ngIf", ctx.getDisplayIcon());
927
+ i0.ɵɵadvance();
928
+ i0.ɵɵclassProp("tree-dropdown-trigger__text--placeholder", !ctx.hasSelection());
929
+ i0.ɵɵadvance();
930
+ i0.ɵɵtextInterpolate1(" ", ctx.hasSelection() ? ctx.getDisplayText() : ctx.Placeholder, " ");
931
+ i0.ɵɵadvance();
932
+ i0.ɵɵproperty("ngIf", ctx.IsLoading && ctx.ShowLoadingInTrigger);
933
+ i0.ɵɵadvance(2);
934
+ i0.ɵɵproperty("ngIf", ctx.Clearable && ctx.hasSelection() && !ctx.Disabled);
935
+ i0.ɵɵadvance(2);
936
+ i0.ɵɵproperty("ngClass", ctx.IsOpen ? "fa-chevron-up" : "fa-chevron-down");
937
+ i0.ɵɵadvance();
938
+ i0.ɵɵproperty("ngIf", ctx.IsOpen);
939
+ } }, dependencies: [i1.NgClass, i1.NgIf, i1.NgStyle, i2.TreeComponent], styles: ["\n\n\n\n\n\n\n\n\n\n\n\n\n[_nghost-%COMP%] {\n \n\n --dropdown-bg: #ffffff;\n --dropdown-border-color: #d0d0d0;\n --dropdown-border-hover: #999999;\n --dropdown-border-focus: #2196f3;\n --dropdown-text-color: #333333;\n --dropdown-text-placeholder: #999999;\n --dropdown-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);\n\n \n\n --dropdown-trigger-bg: #ffffff;\n --dropdown-trigger-height: 40px;\n --dropdown-trigger-padding: 8px 12px;\n --dropdown-trigger-radius: 8px;\n\n \n\n --dropdown-search-bg: #f5f5f5;\n --dropdown-search-height: 36px;\n\n \n\n --dropdown-animation-duration: 150ms;\n --dropdown-animation-easing: ease-out;\n\n display: block;\n position: relative;\n width: 100%;\n}\n\n\n\n\n\n\n.tree-dropdown[_ngcontent-%COMP%] {\n position: relative;\n width: 100%;\n}\n\n.tree-dropdown--disabled[_ngcontent-%COMP%] {\n opacity: 0.6;\n pointer-events: none;\n}\n\n\n\n\n\n\n.tree-dropdown-trigger[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n min-height: var(--dropdown-trigger-height);\n padding: var(--dropdown-trigger-padding);\n background: var(--dropdown-trigger-bg);\n border: 1px solid var(--dropdown-border-color);\n border-radius: var(--dropdown-trigger-radius);\n cursor: pointer;\n outline: none;\n transition: all var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n}\n\n.tree-dropdown-trigger[_ngcontent-%COMP%]:hover {\n border-color: var(--dropdown-border-hover);\n}\n\n.tree-dropdown-trigger[_ngcontent-%COMP%]:focus, \n.tree-dropdown-trigger--open[_ngcontent-%COMP%] {\n border-color: var(--dropdown-border-focus);\n box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.15);\n}\n\n.tree-dropdown-trigger--disabled[_ngcontent-%COMP%] {\n cursor: not-allowed;\n background: #f5f5f5;\n}\n\n.tree-dropdown-trigger__content[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n overflow: hidden;\n}\n\n.tree-dropdown-trigger__icon[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n flex-shrink: 0;\n}\n\n.tree-dropdown-trigger__text[_ngcontent-%COMP%] {\n flex: 1;\n font-size: 14px;\n color: var(--dropdown-text-color);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tree-dropdown-trigger__text--placeholder[_ngcontent-%COMP%] {\n color: var(--dropdown-text-placeholder);\n}\n\n.tree-dropdown-trigger__loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n color: var(--dropdown-border-focus);\n font-size: 12px;\n}\n\n.tree-dropdown-trigger__actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-shrink: 0;\n}\n\n.tree-dropdown-trigger__clear[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: #e0e0e0;\n color: #666;\n cursor: pointer;\n font-size: 10px;\n transition: all var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n}\n\n.tree-dropdown-trigger__clear[_ngcontent-%COMP%]:hover {\n background: #d0d0d0;\n color: #333;\n}\n\n.tree-dropdown-trigger__chevron[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n color: var(--dropdown-text-placeholder);\n font-size: 12px;\n transition: transform var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n}\n\n.tree-dropdown-trigger--open[_ngcontent-%COMP%] .tree-dropdown-trigger__chevron[_ngcontent-%COMP%] {\n transform: rotate(180deg);\n}\n\n\n\n\n\n\n.tree-dropdown-panel[_ngcontent-%COMP%] {\n position: fixed;\n z-index: 10000;\n display: flex;\n flex-direction: column;\n background: var(--dropdown-bg);\n border: 1px solid var(--dropdown-border-color);\n border-radius: 8px;\n box-shadow: var(--dropdown-shadow);\n overflow: hidden;\n animation: _ngcontent-%COMP%_dropdown-open var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n \n\n box-sizing: border-box;\n}\n\n.tree-dropdown-panel--above[_ngcontent-%COMP%] {\n transform-origin: bottom center;\n}\n\n.tree-dropdown-panel--below[_ngcontent-%COMP%] {\n transform-origin: top center;\n}\n\n@keyframes _ngcontent-%COMP%_dropdown-open {\n from {\n opacity: 0;\n transform: scaleY(0.95);\n }\n to {\n opacity: 1;\n transform: scaleY(1);\n }\n}\n\n\n\n\n\n\n.tree-dropdown-search[_ngcontent-%COMP%] {\n padding: 8px;\n border-bottom: 1px solid #e8e8e8;\n background: var(--dropdown-search-bg);\n}\n\n.tree-dropdown-search__input-wrapper[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n height: var(--dropdown-search-height);\n padding: 0 10px;\n background: var(--dropdown-bg);\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n transition: all var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n}\n\n.tree-dropdown-search__input-wrapper[_ngcontent-%COMP%]:focus-within {\n border-color: var(--dropdown-border-focus);\n box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.1);\n}\n\n.tree-dropdown-search__icon[_ngcontent-%COMP%] {\n color: #999;\n font-size: 12px;\n flex-shrink: 0;\n}\n\n.tree-dropdown-search__input[_ngcontent-%COMP%] {\n flex: 1;\n border: none;\n background: transparent;\n font-size: 14px;\n color: var(--dropdown-text-color);\n outline: none;\n min-width: 0;\n}\n\n.tree-dropdown-search__input[_ngcontent-%COMP%]::placeholder {\n color: var(--dropdown-text-placeholder);\n}\n\n.tree-dropdown-search__clear[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: #e0e0e0;\n color: #666;\n cursor: pointer;\n font-size: 9px;\n flex-shrink: 0;\n transition: all var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n}\n\n.tree-dropdown-search__clear[_ngcontent-%COMP%]:hover {\n background: #d0d0d0;\n color: #333;\n}\n\n\n\n\n\n\n.tree-dropdown-tree[_ngcontent-%COMP%] {\n flex: 1;\n overflow: auto;\n min-height: 0;\n max-height: 100%;\n}\n\n.tree-dropdown-tree[_ngcontent-%COMP%] mj-tree[_ngcontent-%COMP%] {\n display: block;\n height: auto;\n max-height: 100%;\n}\n\n\n\n.tree-dropdown-tree[_ngcontent-%COMP%] .tree-container {\n border-radius: 0;\n height: auto;\n max-height: none;\n}\n\n.tree-dropdown-tree[_ngcontent-%COMP%] .tree-content {\n padding: 4px 0;\n}\n\n.tree-dropdown-tree[_ngcontent-%COMP%] .tree-node {\n padding: 6px 12px;\n min-height: 32px;\n}\n\n.tree-dropdown-tree[_ngcontent-%COMP%] .tree-empty {\n padding: 24px 16px;\n}\n\n.tree-dropdown-tree[_ngcontent-%COMP%] .tree-loading {\n padding: 24px 16px;\n}\n\n\n\n\n\n\n[_nghost-%COMP%] .mj-tree-dropdown-portal {\n pointer-events: none;\n}\n\n[_nghost-%COMP%] .mj-tree-dropdown-portal > * {\n pointer-events: auto;\n}\n\n\n\n\n\n\n.tree-dropdown-pills[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n padding: 4px;\n}\n\n.tree-dropdown-pill[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: #e3f2fd;\n border-radius: 12px;\n font-size: 12px;\n color: #1976d2;\n}\n\n.tree-dropdown-pill__remove[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n height: 14px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: #1976d2;\n cursor: pointer;\n font-size: 8px;\n transition: background var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n}\n\n.tree-dropdown-pill__remove[_ngcontent-%COMP%]:hover {\n background: rgba(25, 118, 210, 0.2);\n}\n\n\n\n\n\n\n@media (max-width: 480px) {\n [_nghost-%COMP%] {\n --dropdown-trigger-height: 44px;\n }\n\n .tree-dropdown-trigger__text[_ngcontent-%COMP%] {\n font-size: 15px;\n }\n\n .tree-dropdown-search__input[_ngcontent-%COMP%] {\n font-size: 16px; \n\n }\n}"] });
940
+ }
941
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TreeDropdownComponent, [{
942
+ type: Component,
943
+ args: [{ selector: 'mj-tree-dropdown', template: "<!-- Tree Dropdown Component Template -->\n<div class=\"tree-dropdown\" [class.tree-dropdown--disabled]=\"Disabled\">\n\n <!-- Trigger Button -->\n <div\n #triggerElement\n [ngClass]=\"getTriggerClasses()\"\n [class]=\"StyleConfig.SearchInputClass || ''\"\n tabindex=\"0\"\n role=\"combobox\"\n [attr.aria-expanded]=\"IsOpen\"\n [attr.aria-haspopup]=\"'tree'\"\n [attr.aria-disabled]=\"Disabled\"\n (click)=\"onTriggerClick()\"\n (keydown)=\"onTriggerKeyDown($event)\">\n\n <!-- Selected Value Display -->\n <div class=\"tree-dropdown-trigger__content\">\n <!-- Icon -->\n <span\n class=\"tree-dropdown-trigger__icon\"\n *ngIf=\"getDisplayIcon()\"\n [style.color]=\"getDisplayColor()\">\n <i [class]=\"getDisplayIcon()\"></i>\n </span>\n\n <!-- Text -->\n <span\n class=\"tree-dropdown-trigger__text\"\n [class.tree-dropdown-trigger__text--placeholder]=\"!hasSelection()\">\n {{ hasSelection() ? getDisplayText() : Placeholder }}\n </span>\n\n <!-- Loading Spinner -->\n <span class=\"tree-dropdown-trigger__loading\" *ngIf=\"IsLoading && ShowLoadingInTrigger\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </span>\n </div>\n\n <!-- Actions -->\n <div class=\"tree-dropdown-trigger__actions\">\n <!-- Clear Button -->\n <button\n type=\"button\"\n class=\"tree-dropdown-trigger__clear\"\n *ngIf=\"Clearable && hasSelection() && !Disabled\"\n (click)=\"Clear($event)\"\n title=\"Clear selection\"\n tabindex=\"-1\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n\n <!-- Chevron -->\n <span class=\"tree-dropdown-trigger__chevron\">\n <i\n class=\"fa-solid\"\n [ngClass]=\"IsOpen ? 'fa-chevron-up' : 'fa-chevron-down'\">\n </i>\n </span>\n </div>\n </div>\n\n <!-- Dropdown Panel (rendered in place, will be moved to portal when open) -->\n <div\n #dropdownPanel\n class=\"tree-dropdown-panel\"\n [class.tree-dropdown-panel--open]=\"IsOpen\"\n [class.tree-dropdown-panel--above]=\"Position?.renderAbove\"\n [class.tree-dropdown-panel--below]=\"!Position?.renderAbove\"\n [class]=\"StyleConfig.DropdownClass || ''\"\n [ngStyle]=\"IsOpen ? getDropdownStyles() : {}\"\n *ngIf=\"IsOpen\"\n role=\"tree\">\n\n <!-- Search Input -->\n <div class=\"tree-dropdown-search\" *ngIf=\"EnableSearch\">\n <div class=\"tree-dropdown-search__input-wrapper\">\n <i class=\"fa-solid fa-search tree-dropdown-search__icon\"></i>\n <input\n #searchInput\n type=\"text\"\n class=\"tree-dropdown-search__input\"\n [placeholder]=\"SearchConfig.Placeholder || 'Type to search...'\"\n [value]=\"SearchText\"\n (input)=\"onSearchInput($event)\"\n (keydown)=\"onSearchKeyDown($event)\"\n autocomplete=\"off\"\n spellcheck=\"false\">\n <button\n type=\"button\"\n class=\"tree-dropdown-search__clear\"\n *ngIf=\"SearchText\"\n (click)=\"onClearSearch()\"\n tabindex=\"-1\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n </div>\n\n <!-- Tree -->\n <div class=\"tree-dropdown-tree\">\n <mj-tree\n #treeComponent\n [BranchConfig]=\"BranchConfig\"\n [LeafConfig]=\"LeafConfig\"\n [SelectionMode]=\"SelectionMode\"\n [SelectableTypes]=\"SelectableTypes\"\n [SelectedIDs]=\"getSelectedIDsArray()\"\n [AutoLoad]=\"AutoLoad\"\n [ShowIcons]=\"true\"\n [ShowExpandCollapseAll]=\"false\"\n [AnimateExpandCollapse]=\"true\"\n [EmptyMessage]=\"SearchText ? 'No matches found' : 'No items available'\"\n [EmptyIcon]=\"SearchText ? 'fa-solid fa-search' : 'fa-solid fa-folder-open'\"\n (SelectionChange)=\"onTreeSelectionChange($event)\"\n (BeforeNodeSelect)=\"onTreeBeforeNodeSelect($event)\"\n (AfterNodeSelect)=\"onTreeAfterNodeSelect($event)\"\n (BeforeDataLoad)=\"onTreeBeforeDataLoad($event)\"\n (AfterDataLoad)=\"onTreeAfterDataLoad($event)\">\n </mj-tree>\n </div>\n </div>\n</div>\n", styles: ["/**\n * Tree Dropdown Component Styles\n *\n * Uses CSS custom properties for easy theming.\n * Containers can override these variables to customize appearance.\n */\n\n/* ========================================\n CSS Variables (Theming)\n ======================================== */\n\n:host {\n /* Colors */\n --dropdown-bg: #ffffff;\n --dropdown-border-color: #d0d0d0;\n --dropdown-border-hover: #999999;\n --dropdown-border-focus: #2196f3;\n --dropdown-text-color: #333333;\n --dropdown-text-placeholder: #999999;\n --dropdown-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);\n\n /* Trigger */\n --dropdown-trigger-bg: #ffffff;\n --dropdown-trigger-height: 40px;\n --dropdown-trigger-padding: 8px 12px;\n --dropdown-trigger-radius: 8px;\n\n /* Search */\n --dropdown-search-bg: #f5f5f5;\n --dropdown-search-height: 36px;\n\n /* Animation */\n --dropdown-animation-duration: 150ms;\n --dropdown-animation-easing: ease-out;\n\n display: block;\n position: relative;\n width: 100%;\n}\n\n/* ========================================\n Container\n ======================================== */\n\n.tree-dropdown {\n position: relative;\n width: 100%;\n}\n\n.tree-dropdown--disabled {\n opacity: 0.6;\n pointer-events: none;\n}\n\n/* ========================================\n Trigger\n ======================================== */\n\n.tree-dropdown-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n min-height: var(--dropdown-trigger-height);\n padding: var(--dropdown-trigger-padding);\n background: var(--dropdown-trigger-bg);\n border: 1px solid var(--dropdown-border-color);\n border-radius: var(--dropdown-trigger-radius);\n cursor: pointer;\n outline: none;\n transition: all var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n}\n\n.tree-dropdown-trigger:hover {\n border-color: var(--dropdown-border-hover);\n}\n\n.tree-dropdown-trigger:focus,\n.tree-dropdown-trigger--open {\n border-color: var(--dropdown-border-focus);\n box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.15);\n}\n\n.tree-dropdown-trigger--disabled {\n cursor: not-allowed;\n background: #f5f5f5;\n}\n\n.tree-dropdown-trigger__content {\n flex: 1;\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n overflow: hidden;\n}\n\n.tree-dropdown-trigger__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n flex-shrink: 0;\n}\n\n.tree-dropdown-trigger__text {\n flex: 1;\n font-size: 14px;\n color: var(--dropdown-text-color);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tree-dropdown-trigger__text--placeholder {\n color: var(--dropdown-text-placeholder);\n}\n\n.tree-dropdown-trigger__loading {\n display: flex;\n align-items: center;\n color: var(--dropdown-border-focus);\n font-size: 12px;\n}\n\n.tree-dropdown-trigger__actions {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-shrink: 0;\n}\n\n.tree-dropdown-trigger__clear {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: #e0e0e0;\n color: #666;\n cursor: pointer;\n font-size: 10px;\n transition: all var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n}\n\n.tree-dropdown-trigger__clear:hover {\n background: #d0d0d0;\n color: #333;\n}\n\n.tree-dropdown-trigger__chevron {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n color: var(--dropdown-text-placeholder);\n font-size: 12px;\n transition: transform var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n}\n\n.tree-dropdown-trigger--open .tree-dropdown-trigger__chevron {\n transform: rotate(180deg);\n}\n\n/* ========================================\n Dropdown Panel\n ======================================== */\n\n.tree-dropdown-panel {\n position: fixed;\n z-index: 10000;\n display: flex;\n flex-direction: column;\n background: var(--dropdown-bg);\n border: 1px solid var(--dropdown-border-color);\n border-radius: 8px;\n box-shadow: var(--dropdown-shadow);\n overflow: hidden;\n animation: dropdown-open var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n /* Ensure the panel respects its maxHeight and allows child scrolling */\n box-sizing: border-box;\n}\n\n.tree-dropdown-panel--above {\n transform-origin: bottom center;\n}\n\n.tree-dropdown-panel--below {\n transform-origin: top center;\n}\n\n@keyframes dropdown-open {\n from {\n opacity: 0;\n transform: scaleY(0.95);\n }\n to {\n opacity: 1;\n transform: scaleY(1);\n }\n}\n\n/* ========================================\n Search\n ======================================== */\n\n.tree-dropdown-search {\n padding: 8px;\n border-bottom: 1px solid #e8e8e8;\n background: var(--dropdown-search-bg);\n}\n\n.tree-dropdown-search__input-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n height: var(--dropdown-search-height);\n padding: 0 10px;\n background: var(--dropdown-bg);\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n transition: all var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n}\n\n.tree-dropdown-search__input-wrapper:focus-within {\n border-color: var(--dropdown-border-focus);\n box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.1);\n}\n\n.tree-dropdown-search__icon {\n color: #999;\n font-size: 12px;\n flex-shrink: 0;\n}\n\n.tree-dropdown-search__input {\n flex: 1;\n border: none;\n background: transparent;\n font-size: 14px;\n color: var(--dropdown-text-color);\n outline: none;\n min-width: 0;\n}\n\n.tree-dropdown-search__input::placeholder {\n color: var(--dropdown-text-placeholder);\n}\n\n.tree-dropdown-search__clear {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: #e0e0e0;\n color: #666;\n cursor: pointer;\n font-size: 9px;\n flex-shrink: 0;\n transition: all var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n}\n\n.tree-dropdown-search__clear:hover {\n background: #d0d0d0;\n color: #333;\n}\n\n/* ========================================\n Tree Container\n ======================================== */\n\n.tree-dropdown-tree {\n flex: 1;\n overflow: auto;\n min-height: 0;\n max-height: 100%;\n}\n\n.tree-dropdown-tree mj-tree {\n display: block;\n height: auto;\n max-height: 100%;\n}\n\n/* Override tree styles for dropdown context */\n.tree-dropdown-tree ::ng-deep .tree-container {\n border-radius: 0;\n height: auto;\n max-height: none;\n}\n\n.tree-dropdown-tree ::ng-deep .tree-content {\n padding: 4px 0;\n}\n\n.tree-dropdown-tree ::ng-deep .tree-node {\n padding: 6px 12px;\n min-height: 32px;\n}\n\n.tree-dropdown-tree ::ng-deep .tree-empty {\n padding: 24px 16px;\n}\n\n.tree-dropdown-tree ::ng-deep .tree-loading {\n padding: 24px 16px;\n}\n\n/* ========================================\n Portal Container (in body)\n ======================================== */\n\n:host ::ng-deep .mj-tree-dropdown-portal {\n pointer-events: none;\n}\n\n:host ::ng-deep .mj-tree-dropdown-portal > * {\n pointer-events: auto;\n}\n\n/* ========================================\n Multiple Selection Pills\n ======================================== */\n\n.tree-dropdown-pills {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n padding: 4px;\n}\n\n.tree-dropdown-pill {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n background: #e3f2fd;\n border-radius: 12px;\n font-size: 12px;\n color: #1976d2;\n}\n\n.tree-dropdown-pill__remove {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 14px;\n height: 14px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: #1976d2;\n cursor: pointer;\n font-size: 8px;\n transition: background var(--dropdown-animation-duration) var(--dropdown-animation-easing);\n}\n\n.tree-dropdown-pill__remove:hover {\n background: rgba(25, 118, 210, 0.2);\n}\n\n/* ========================================\n Responsive\n ======================================== */\n\n@media (max-width: 480px) {\n :host {\n --dropdown-trigger-height: 44px;\n }\n\n .tree-dropdown-trigger__text {\n font-size: 15px;\n }\n\n .tree-dropdown-search__input {\n font-size: 16px; /* Prevents zoom on iOS */\n }\n}\n"] }]
944
+ }], () => [{ type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }], { BranchConfig: [{
945
+ type: Input
946
+ }], LeafConfig: [{
947
+ type: Input
948
+ }], SelectionMode: [{
949
+ type: Input
950
+ }], SelectableTypes: [{
951
+ type: Input
952
+ }], Value: [{
953
+ type: Input
954
+ }], Placeholder: [{
955
+ type: Input
956
+ }], EnableSearch: [{
957
+ type: Input
958
+ }], SearchConfig: [{
959
+ type: Input
960
+ }], DropdownConfig: [{
961
+ type: Input
962
+ }], StyleConfig: [{
963
+ type: Input
964
+ }], Clearable: [{
965
+ type: Input
966
+ }], Disabled: [{
967
+ type: Input
968
+ }], ShowIconInDisplay: [{
969
+ type: Input
970
+ }], AutoLoad: [{
971
+ type: Input
972
+ }], ShowLoadingInTrigger: [{
973
+ type: Input
974
+ }], ValueChange: [{
975
+ type: Output
976
+ }], SelectionChange: [{
977
+ type: Output
978
+ }], BeforeNodeSelect: [{
979
+ type: Output
980
+ }], AfterNodeSelect: [{
981
+ type: Output
982
+ }], BeforeSearch: [{
983
+ type: Output
984
+ }], AfterSearch: [{
985
+ type: Output
986
+ }], BeforeDropdownOpen: [{
987
+ type: Output
988
+ }], AfterDropdownOpen: [{
989
+ type: Output
990
+ }], BeforeDropdownClose: [{
991
+ type: Output
992
+ }], AfterDropdownClose: [{
993
+ type: Output
994
+ }], BeforeDataLoad: [{
995
+ type: Output
996
+ }], AfterDataLoad: [{
997
+ type: Output
998
+ }], triggerElement: [{
999
+ type: ViewChild,
1000
+ args: ['triggerElement']
1001
+ }], searchInput: [{
1002
+ type: ViewChild,
1003
+ args: ['searchInput']
1004
+ }], dropdownPanel: [{
1005
+ type: ViewChild,
1006
+ args: ['dropdownPanel']
1007
+ }], treeComponent: [{
1008
+ type: ViewChild,
1009
+ args: ['treeComponent']
1010
+ }] }); })();
1011
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TreeDropdownComponent, { className: "TreeDropdownComponent", filePath: "src/lib/tree-dropdown/tree-dropdown.component.ts", lineNumber: 68 }); })();
1012
+ //# sourceMappingURL=tree-dropdown.component.js.map