@memberjunction/ng-trees 5.24.0 → 5.26.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.
@@ -189,7 +189,9 @@ export declare class TreeDropdownComponent implements OnInit, OnDestroy, AfterVi
189
189
  */
190
190
  onSearchInput(event: Event): void;
191
191
  /**
192
- * Handle search keydown
192
+ * Handle search keydown — provides full keyboard navigation while search input retains focus.
193
+ * The tree's own keyboard handler doesn't fire because the search input has DOM focus,
194
+ * so all navigation is handled here by manipulating the tree's FocusedNode visual state.
193
195
  */
194
196
  onSearchKeyDown(event: KeyboardEvent): void;
195
197
  /**
@@ -210,6 +212,38 @@ export declare class TreeDropdownComponent implements OnInit, OnDestroy, AfterVi
210
212
  */
211
213
  onTreeBeforeNodeSelect(event: BeforeNodeSelectEventArgs): void;
212
214
  onTreeAfterNodeSelect(event: AfterNodeSelectEventArgs): void;
215
+ /**
216
+ * Focus the search input after the dropdown opens.
217
+ * Deferred to the next macrotask so that:
218
+ * 1. Angular has fully resolved @ViewChild queries for the @if block
219
+ * 2. The browser's click event (on the trigger) has completed and won't steal focus back
220
+ * Falls back to direct DOM querySelector if the ViewChild isn't resolved.
221
+ */
222
+ private focusSearchInput;
223
+ /**
224
+ * Navigate tree focus up/down/first/last while search input retains DOM focus
225
+ */
226
+ private navigateTree;
227
+ /**
228
+ * Select the currently focused tree node (Enter key)
229
+ */
230
+ private selectFocusedNode;
231
+ /**
232
+ * ArrowRight: expand focused branch or descend to first child
233
+ */
234
+ private expandOrDescendFocusedNode;
235
+ /**
236
+ * ArrowLeft: collapse focused branch or ascend to parent
237
+ */
238
+ private collapseOrAscendFocusedNode;
239
+ /**
240
+ * Toggle expand/collapse of a branch node
241
+ */
242
+ private toggleBranchExpansion;
243
+ /**
244
+ * Scroll the currently focused tree node into view within the dropdown panel
245
+ */
246
+ private scrollFocusedNodeIntoView;
213
247
  /**
214
248
  * Create dropdown portal element (attached to body)
215
249
  */
@@ -1 +1 @@
1
- {"version":3,"file":"tree-dropdown.component.d.ts","sourceRoot":"","sources":["../../../src/lib/tree-dropdown/tree-dropdown.component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tree-dropdown.component.d.ts","sourceRoot":"","sources":["../../../src/lib/tree-dropdown/tree-dropdown.component.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,7 @@ import { BeforeSearchEventArgs, AfterSearchEventArgs, BeforeDropdownOpenEventArg
13
13
  import { Subject } from 'rxjs';
14
14
  import { debounceTime, takeUntil } from 'rxjs/operators';
15
15
  import { Metadata, CompositeKey } from '@memberjunction/core';
16
+ import { UUIDsEqual } from '@memberjunction/global';
16
17
  import * as i0 from "@angular/core";
17
18
  import * as i1 from "@angular/common";
18
19
  import * as i2 from "../tree/tree.component";
@@ -298,25 +299,13 @@ export class TreeDropdownComponent {
298
299
  this.IsOpen = true;
299
300
  this.calculatePosition();
300
301
  this.attachEventListeners();
301
- // Focus search input after opening use microtask + fallback to ensure it works
302
- // even when Angular change detection hasn't yet rendered the @if block
303
- Promise.resolve().then(() => {
304
- if (this.EnableSearch && this.searchInput) {
305
- this.searchInput.nativeElement.focus();
306
- }
307
- else {
308
- // Fallback: the @if block may not have rendered yet, retry after a frame
309
- setTimeout(() => {
310
- if (this.EnableSearch && this.searchInput) {
311
- this.searchInput.nativeElement.focus();
312
- }
313
- }, 100);
314
- }
315
- });
302
+ // Trigger change detection so the @if (IsOpen) block renders the dropdown panel
303
+ this.cdr.detectChanges();
304
+ // Focus search input after rendering
305
+ this.focusSearchInput();
316
306
  // Fire after event
317
307
  const afterEvent = new AfterDropdownOpenEventArgs(this, this.Position?.renderAbove ? 'above' : 'below');
318
308
  this.AfterDropdownOpen.emit(afterEvent);
319
- this.cdr.detectChanges();
320
309
  }
321
310
  /**
322
311
  * Close the dropdown
@@ -418,7 +407,9 @@ export class TreeDropdownComponent {
418
407
  this.searchSubject.next(value);
419
408
  }
420
409
  /**
421
- * Handle search keydown
410
+ * Handle search keydown — provides full keyboard navigation while search input retains focus.
411
+ * The tree's own keyboard handler doesn't fire because the search input has DOM focus,
412
+ * so all navigation is handled here by manipulating the tree's FocusedNode visual state.
422
413
  */
423
414
  onSearchKeyDown(event) {
424
415
  switch (event.key) {
@@ -429,14 +420,31 @@ export class TreeDropdownComponent {
429
420
  break;
430
421
  case 'ArrowDown':
431
422
  event.preventDefault();
432
- // Focus first tree node — guard against null/empty state
433
- if (this.treeComponent?.Nodes?.length) {
434
- const visibleNodes = this.getVisibleNodesInOrder(this.treeComponent.Nodes);
435
- if (visibleNodes.length > 0) {
436
- this.treeComponent.FocusedNode = visibleNodes[0];
437
- this.cdr.detectChanges();
438
- }
439
- }
423
+ this.navigateTree('down');
424
+ break;
425
+ case 'ArrowUp':
426
+ event.preventDefault();
427
+ this.navigateTree('up');
428
+ break;
429
+ case 'ArrowRight':
430
+ event.preventDefault();
431
+ this.expandOrDescendFocusedNode();
432
+ break;
433
+ case 'ArrowLeft':
434
+ event.preventDefault();
435
+ this.collapseOrAscendFocusedNode();
436
+ break;
437
+ case 'Enter':
438
+ event.preventDefault();
439
+ this.selectFocusedNode();
440
+ break;
441
+ case 'Home':
442
+ event.preventDefault();
443
+ this.navigateTree('first');
444
+ break;
445
+ case 'End':
446
+ event.preventDefault();
447
+ this.navigateTree('last');
440
448
  break;
441
449
  }
442
450
  }
@@ -512,6 +520,172 @@ export class TreeDropdownComponent {
512
520
  this.AfterNodeSelect.emit(event);
513
521
  }
514
522
  // ========================================
523
+ // Keyboard Navigation Helpers
524
+ // ========================================
525
+ /**
526
+ * Focus the search input after the dropdown opens.
527
+ * Deferred to the next macrotask so that:
528
+ * 1. Angular has fully resolved @ViewChild queries for the @if block
529
+ * 2. The browser's click event (on the trigger) has completed and won't steal focus back
530
+ * Falls back to direct DOM querySelector if the ViewChild isn't resolved.
531
+ */
532
+ focusSearchInput() {
533
+ if (!this.EnableSearch)
534
+ return;
535
+ const tryFocus = (retriesLeft) => {
536
+ // Try ViewChild first
537
+ if (this.searchInput?.nativeElement) {
538
+ this.searchInput.nativeElement.focus();
539
+ return;
540
+ }
541
+ // Fallback: query DOM directly (ViewChild may not resolve for @if blocks)
542
+ const panel = this.dropdownPanel?.nativeElement;
543
+ if (panel) {
544
+ const input = panel.querySelector('.tree-dropdown-search__input');
545
+ if (input) {
546
+ input.focus();
547
+ return;
548
+ }
549
+ }
550
+ // Retry on next frame
551
+ if (retriesLeft > 0) {
552
+ requestAnimationFrame(() => tryFocus(retriesLeft - 1));
553
+ }
554
+ };
555
+ // Defer to next macrotask so the click event on the trigger completes first
556
+ // and Angular finishes resolving ViewChild queries for the new @if block
557
+ setTimeout(() => tryFocus(5), 0);
558
+ }
559
+ /**
560
+ * Navigate tree focus up/down/first/last while search input retains DOM focus
561
+ */
562
+ navigateTree(direction) {
563
+ if (!this.treeComponent?.Nodes?.length)
564
+ return;
565
+ const visibleNodes = this.getVisibleNodesInOrder(this.treeComponent.Nodes);
566
+ if (visibleNodes.length === 0)
567
+ return;
568
+ const currentIndex = this.treeComponent.FocusedNode
569
+ ? visibleNodes.indexOf(this.treeComponent.FocusedNode)
570
+ : -1;
571
+ let targetNode = null;
572
+ switch (direction) {
573
+ case 'down':
574
+ targetNode = currentIndex === -1
575
+ ? visibleNodes[0]
576
+ : currentIndex < visibleNodes.length - 1
577
+ ? visibleNodes[currentIndex + 1]
578
+ : null;
579
+ break;
580
+ case 'up':
581
+ targetNode = currentIndex === -1
582
+ ? visibleNodes[visibleNodes.length - 1]
583
+ : currentIndex > 0
584
+ ? visibleNodes[currentIndex - 1]
585
+ : null;
586
+ break;
587
+ case 'first':
588
+ targetNode = visibleNodes[0];
589
+ break;
590
+ case 'last':
591
+ targetNode = visibleNodes[visibleNodes.length - 1];
592
+ break;
593
+ }
594
+ if (targetNode) {
595
+ this.treeComponent.FocusedNode = targetNode;
596
+ this.cdr.detectChanges();
597
+ this.scrollFocusedNodeIntoView();
598
+ }
599
+ }
600
+ /**
601
+ * Select the currently focused tree node (Enter key)
602
+ */
603
+ selectFocusedNode() {
604
+ const focusedNode = this.treeComponent?.FocusedNode;
605
+ if (!focusedNode)
606
+ return;
607
+ // Check if the node type is selectable per config
608
+ const isSelectable = this.SelectableTypes === 'both'
609
+ || (this.SelectableTypes === 'leaf' && focusedNode.Type === 'leaf')
610
+ || (this.SelectableTypes === 'branch' && focusedNode.Type === 'branch');
611
+ if (isSelectable) {
612
+ this.treeComponent.SelectNodes([focusedNode.ID], true);
613
+ }
614
+ else if (focusedNode.Type === 'branch') {
615
+ // Non-selectable branch: toggle expand/collapse
616
+ this.toggleBranchExpansion(focusedNode);
617
+ }
618
+ }
619
+ /**
620
+ * ArrowRight: expand focused branch or descend to first child
621
+ */
622
+ expandOrDescendFocusedNode() {
623
+ const node = this.treeComponent?.FocusedNode;
624
+ if (!node || node.Type !== 'branch')
625
+ return;
626
+ if (!node.Expanded && node.Children?.length) {
627
+ this.treeComponent.ExpandToNode(node.Children[0].ID);
628
+ this.cdr.detectChanges();
629
+ }
630
+ else if (node.Expanded && node.Children?.length) {
631
+ // Already expanded: move focus to first visible child
632
+ const firstVisible = node.Children.find(c => c.Visible);
633
+ if (firstVisible) {
634
+ this.treeComponent.FocusedNode = firstVisible;
635
+ this.cdr.detectChanges();
636
+ this.scrollFocusedNodeIntoView();
637
+ }
638
+ }
639
+ }
640
+ /**
641
+ * ArrowLeft: collapse focused branch or ascend to parent
642
+ */
643
+ collapseOrAscendFocusedNode() {
644
+ const node = this.treeComponent?.FocusedNode;
645
+ if (!node)
646
+ return;
647
+ if (node.Type === 'branch' && node.Expanded) {
648
+ // Collapse the branch
649
+ this.toggleBranchExpansion(node);
650
+ }
651
+ else if (node.ParentID) {
652
+ // Move to parent
653
+ const allNodes = this.getVisibleNodesInOrder(this.treeComponent.Nodes);
654
+ const parent = allNodes.find(n => UUIDsEqual(n.ID, node.ParentID));
655
+ if (parent) {
656
+ this.treeComponent.FocusedNode = parent;
657
+ this.cdr.detectChanges();
658
+ this.scrollFocusedNodeIntoView();
659
+ }
660
+ }
661
+ }
662
+ /**
663
+ * Toggle expand/collapse of a branch node
664
+ */
665
+ toggleBranchExpansion(node) {
666
+ if (node.Expanded) {
667
+ node.Expanded = false;
668
+ }
669
+ else {
670
+ this.treeComponent.ExpandToNode(node.ID);
671
+ }
672
+ this.cdr.detectChanges();
673
+ }
674
+ /**
675
+ * Scroll the currently focused tree node into view within the dropdown panel
676
+ */
677
+ scrollFocusedNodeIntoView() {
678
+ requestAnimationFrame(() => {
679
+ const panel = this.dropdownPanel?.nativeElement;
680
+ if (!panel)
681
+ return;
682
+ const focused = panel.querySelector('.tree-node-focused, .tree-node--focused, [data-focused="true"]');
683
+ if (focused) {
684
+ focused.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
685
+ }
686
+ });
687
+ }
688
+ // ========================================
515
689
  // Private Methods
516
690
  // ========================================
517
691
  /**
@@ -1019,5 +1193,5 @@ export class TreeDropdownComponent {
1019
1193
  type: ViewChild,
1020
1194
  args: ['treeComponent']
1021
1195
  }] }); })();
1022
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TreeDropdownComponent, { className: "TreeDropdownComponent", filePath: "src/lib/tree-dropdown/tree-dropdown.component.ts", lineNumber: 69 }); })();
1196
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TreeDropdownComponent, { className: "TreeDropdownComponent", filePath: "src/lib/tree-dropdown/tree-dropdown.component.ts", lineNumber: 70 }); })();
1023
1197
  //# sourceMappingURL=tree-dropdown.component.js.map