license import { AnimationEvent } from '@angular/animations' ; import { CdkAccordionItem } from '@angular/cdk/accordion' ; import { coerceBooleanProperty } from '@angular/cdk/coercion' ; import { UniqueSelectionDispatcher } from '@angular/cdk/collections' ; import { TemplatePortal } from '@angular/cdk/portal' ; import { AfterContentInit , ChangeDetectionStrategy , ChangeDetectorRef , Component , ContentChild , Directive , EventEmitter , ElementRef , Input , Inject , OnChanges , OnDestroy , Optional , Output , SimpleChanges , SkipSelf , ViewContainerRef , ViewEncapsulation , ViewChild , InjectionToken , } from '@angular/core' ; import { DOCUMENT } from '@angular/common' ; import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations' ; import { Subject } from 'rxjs' ; import { filter , startWith , take , distinctUntilChanged } from 'rxjs/operators' ; import { matExpansionAnimations } from './expansion-animations' ; import { MatExpansionPanelContent } from './expansion-panel-content' ; import { MAT_ACCORDION , MatAccordionBase } from './accordion-base' ; /** MatExpansionPanel's states. */ export type MatExpansionPanelState = 'expanded' | 'collapsed' ; /** Counter for generating unique element ids. */ let uniqueId = 0 ; /**
 * Object that can be used to override the default options
 * for all of the expansion panels in a module.
 */ export interface MatExpansionPanelDefaultOptions { /** Height of the header while the panel is expanded. */ expandedHeight : string ; /** Height of the header while the panel is collapsed. */ collapsedHeight : string ; /** Whether the toggle indicator should be hidden. */ hideToggle : boolean ; } /**
 * Injection token that can be used to configure the defalt
 * options for the expansion panel component.
 */ export const MAT_EXPANSION_PANEL_DEFAULT_OPTIONS = new InjectionToken < MatExpansionPanelDefaultOptions > ( 'MAT_EXPANSION_PANEL_DEFAULT_OPTIONS' ) ; /**
 * `<mat-expansion-panel>`
 *
 * This component can be used as a single element to show expandable content, or as one of
 * multiple children of an element with the MatAccordion directive attached.
 */ @ Component ( { moduleId : module . id , styleUrls : [ './expansion-panel.css' ] , selector : 'mat-expansion-panel' , exportAs : 'matExpansionPanel' , templateUrl : './expansion-panel.html' , encapsulation : ViewEncapsulation . None , changeDetection : ChangeDetectionStrategy . OnPush , inputs : [ 'disabled' , 'expanded' ] , outputs : [ 'opened' , 'closed' , 'expandedChange' ] , animations : [ matExpansionAnimations . bodyExpansion ] , providers : [ { provide : MAT_ACCORDION , useValue : undefined } , ] , host : { 'class' : 'mat-expansion-panel' , '[class.mat-expanded]' : 'expanded' , '[class._mat-animation-noopable]' : '_animationMode === "NoopAnimations"' , '[class.mat-expansion-panel-spacing]' : '_hasSpacing()' , } } ) export class MatExpansionPanel extends CdkAccordionItem implements AfterContentInit , OnChanges , OnDestroy { private _document : Document | undefined ; /** Whether the toggle indicator should be hidden. */ @ Input ( ) get hideToggle ( ) : boolean { return this . _hideToggle || ( this . accordion && this . accordion . hideToggle ) ; } set hideToggle ( value ) { this . _hideToggle = coerceBooleanProperty ( value ) ; } private _hideToggle = false ; /** An event emitted after the body's expansion animation happens. */ @ Output ( ) afterExpand = new EventEmitter < void > ( ) ; /** An event emitted after the body's collapse animation happens. */ @ Output ( ) afterCollapse = new EventEmitter < void > ( ) ; /** Stream that emits for changes in `@Input` properties. */ readonly _inputChanges = new Subject < SimpleChanges > ( ) ; /** Optionally defined accordion the expansion panel belongs to. */ accordion : MatAccordionBase ; /** Content that will be rendered lazily. */ @ ContentChild ( MatExpansionPanelContent ) _lazyContent : MatExpansionPanelContent ; /** Element containing the panel's user-provided content. */ @ ViewChild ( 'body' ) _body : ElementRef < HTMLElement > ; /** Portal holding the user's content. */ _portal : TemplatePortal ; /** ID for the associated header element. Used for a11y labelling. */ _headerId = `mat-expansion-panel-header-${ uniqueId ++ }` ; /** Stream of body animation done events. */ _bodyAnimationDone = new Subject < AnimationEvent > ( ) ; constructor ( @ Optional ( ) @ SkipSelf ( ) @ Inject ( MAT_ACCORDION ) accordion , _changeDetectorRef , _uniqueSelectionDispatcher , private _viewContainerRef , @ Inject ( DOCUMENT ) _document ? , @ Optional ( ) @ Inject ( ANIMATION_MODULE_TYPE ) public _animationMode ? , @ Inject ( MAT_EXPANSION_PANEL_DEFAULT_OPTIONS ) @ Optional ( ) defaultOptions ? ) { super ( accordion , _changeDetectorRef , _uniqueSelectionDispatcher ) ; this . accordion = accordion ; this . _document = _document ; this . _bodyAnimationDone . pipe ( distinctUntilChanged ( ( x , y ) => { return x . fromState === y . fromState && x . toState === y . toState ; } ) ) . subscribe ( event => { if ( event . fromState !== 'void' ) { if ( event . toState === 'expanded' ) { this . afterExpand . emit ( ) ; } else if ( event . toState === 'collapsed' ) { this . afterCollapse . emit ( ) ; } } } ) ; if ( defaultOptions ) { this . hideToggle = defaultOptions . hideToggle ; } } /** Determines whether the expansion panel should have spacing between it and its siblings. */ _hasSpacing ( ) { if ( this . accordion ) { return ( this . expanded ? this . accordion . displayMode : this . _getExpandedState ( ) ) === 'default' ; } return false ; } /** Gets the expanded state string. */ _getExpandedState ( ) { return this . expanded ? 'expanded' : 'collapsed' ; } ngAfterContentInit ( ) { if ( this . _lazyContent ) { this . opened . pipe ( startWith < void > ( null ! ) , filter ( ( ) => this . expanded && ! this . _portal ) , take ( 1 ) ) . subscribe ( ( ) => { this . _portal = new TemplatePortal ( this . _lazyContent . _template , this . _viewContainerRef ) ; } ) ; } } ngOnChanges ( changes ) { this . _inputChanges . next ( changes ) ; } ngOnDestroy ( ) { super . ngOnDestroy ( ) ; this . _bodyAnimationDone . complete ( ) ; this . _inputChanges . complete ( ) ; } /** Checks whether the expansion panel's content contains the currently-focused element. */ _containsFocus ( ) { if ( this . _body && this . _document ) { const focusedElement = this . _document . activeElement ; const bodyElement = this . _body . nativeElement ; return focusedElement === bodyElement || bodyElement . contains ( focusedElement ) ; } return false ; } } @ Directive ( { selector : 'mat-action-row' , host : { class : 'mat-action-row' } } ) export class MatExpansionPanelActionRow { }