Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 1x 90x 90x 90x 90x 90x 90x 65x 7x 7x 58x 7x 58x 953x 953x 953x 133x | /**
* Copyright (c) Siemens 2016 - 2025
* SPDX-License-Identifier: MIT
*/
import { A11yModule, CdkTrapFocus } from '@angular/cdk/a11y';
import {
ChangeDetectionStrategy,
Component,
HostBinding,
HostListener,
inject,
viewChild,
DOCUMENT
} from '@angular/core';
import { SiHeaderDropdownTriggerDirective } from './si-header-dropdown-trigger.directive';
import { SI_HEADER_DROPDOWN_OPTIONS } from './si-header.model';
/**
* Wrapper component for {@link SiHeaderDropdownItemComponent}.
* Must only be opened using an {@link SiHeaderDropdownTriggerDirective}.
*/
@Component({
selector: 'si-header-dropdown',
imports: [A11yModule],
templateUrl: './si-header-dropdown.component.html',
styles: ':host.sub-menu {min-inline-size: 200px}',
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
class: 'dropdown-menu position-static',
role: 'group',
'[id]': 'trigger.ariaControls',
'[attr.aria-labelledby]': 'trigger.id'
}
})
export class SiHeaderDropdownComponent {
/** @internal */
protected trigger = inject(SiHeaderDropdownTriggerDirective);
private readonly focusTrap = viewChild.required(CdkTrapFocus);
private previousElement: Element | null = null;
private readonly document = inject(DOCUMENT);
private readonly options = inject(SI_HEADER_DROPDOWN_OPTIONS, { optional: true });
constructor() {
// The autoFocus feature of the focus trap is not enough, as this component is not newly created when opened in mobile (inline).
// But we still need autofocus in desktop mode, as the close event is never executed (component is destroyed before).
this.trigger.openChange.subscribe(change => {
if (!this.trigger.isOverlay && this.trapFocus && change) {
this.previousElement = this.document.activeElement;
this.focusTrap().focusTrap.focusFirstTabbableElementWhenReady();
} else {
if (
this.previousElement &&
'focus' in this.previousElement &&
typeof this.previousElement.focus === 'function'
) {
this.previousElement.focus();
}
this.previousElement = null;
}
});
}
/** @internal */
@HostBinding('class.show')
protected get show(): boolean {
return this.trigger.isOpen;
}
/** @internal */
@HostBinding('class.header-dropdown-overlay') protected get overlay(): boolean {
return this.trigger.isOverlay;
}
/** @internal */
@HostBinding('class.sub-menu') protected get submenu(): boolean {
return this.trigger.level > 1;
}
/** @internal */
protected get trapFocus(): boolean {
return (
this.trigger.isOverlay ||
(!this.options?.disableRootFocusTrapForInlineMode && this.trigger.level === 1)
);
}
/** @internal */
@HostListener('keydown.escape')
protected escape(): void {
this.trigger?.close();
}
}
|