/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import { Directive, Input, ContentChildren, QueryList, AfterContentInit, OnDestroy, booleanAttribute, } from '@angular/core'; import {CdkAccordion} from '@angular/cdk/accordion'; import {FocusKeyManager} from '@angular/cdk/a11y'; import {startWith} from 'rxjs/operators'; import { MAT_ACCORDION, MatAccordionBase, MatAccordionDisplayMode, MatAccordionTogglePosition, } from './accordion-base'; import {MatExpansionPanelHeader} from './expansion-panel-header'; /** * Directive for a Material Design Accordion. */ @Directive({ selector: 'mat-accordion', exportAs: 'matAccordion', providers: [ { provide: MAT_ACCORDION, useExisting: MatAccordion, }, ], host: { class: 'mat-accordion', // Class binding which is only used by the test harness as there is no other // way for the harness to detect if multiple panel support is enabled. '[class.mat-accordion-multi]': 'this.multi', }, }) export class MatAccordion extends CdkAccordion implements MatAccordionBase, AfterContentInit, OnDestroy { private _keyManager: FocusKeyManager; /** Headers belonging to this accordion. */ private _ownHeaders = new QueryList(); /** All headers inside the accordion. Includes headers inside nested accordions. */ @ContentChildren(MatExpansionPanelHeader, {descendants: true}) _headers: QueryList; /** Whether the expansion indicator should be hidden. */ @Input({transform: booleanAttribute}) hideToggle: boolean = false; /** * Display mode used for all expansion panels in the accordion. Currently two display * modes exist: * default - a gutter-like spacing is placed around any expanded panel, placing the expanded * panel at a different elevation from the rest of the accordion. * flat - no spacing is placed around expanded panels, showing all panels at the same * elevation. */ @Input() displayMode: MatAccordionDisplayMode = 'default'; /** The position of the expansion indicator. */ @Input() togglePosition: MatAccordionTogglePosition = 'after'; ngAfterContentInit() { this._headers.changes .pipe(startWith(this._headers)) .subscribe((headers: QueryList) => { this._ownHeaders.reset(headers.filter(header => header.panel.accordion === this)); this._ownHeaders.notifyOnChanges(); }); this._keyManager = new FocusKeyManager(this._ownHeaders).withWrap().withHomeAndEnd(); } /** Handles keyboard events coming in from the panel headers. */ _handleHeaderKeydown(event: KeyboardEvent) { this._keyManager.onKeydown(event); } _handleHeaderFocus(header: MatExpansionPanelHeader) { this._keyManager.updateActiveItem(header); } override ngOnDestroy() { super.ngOnDestroy(); this._keyManager?.destroy(); this._ownHeaders.destroy(); } }