license import { coerceBooleanProperty } from '@angular/cdk/coercion' ; import { ENTER , SPACE , hasModifierKey } from '@angular/cdk/keycodes' ; import { AfterViewChecked , ChangeDetectionStrategy , ChangeDetectorRef , Component , ElementRef , EventEmitter , Inject , InjectionToken , Input , OnDestroy , Optional , Output , QueryList , ViewEncapsulation , } from '@angular/core' ; import { Subject } from 'rxjs' ; import { MatOptgroup } from './optgroup' ; /**
 * Option IDs need to be unique across components, so this counter exists outside of
 * the component definition.
 */ let _uniqueIdCounter = 0 ; /** Event object emitted by MatOption when selected or deselected. */ export class MatOptionSelectionChange { constructor ( /** Reference to the option that emitted the event. */ public source , /** Whether the change in the option's value was a result of a user action. */ public isUserInput = false ) { } } docs-private export interface MatOptionParentComponent { disableRipple ? : boolean ; multiple ? : boolean ; } /**
 * Injection token used to provide the parent component to options.
 */ export const MAT_OPTION_PARENT_COMPONENT = new InjectionToken < MatOptionParentComponent > ( 'MAT_OPTION_PARENT_COMPONENT' ) ; /**
 * Single option inside of a `<mat-select>` element.
 */ @ Component ( { moduleId : module . id , selector : 'mat-option' , exportAs : 'matOption' , host : { 'role' : 'option' , '[attr.tabindex]' : '_getTabIndex()' , '[class.mat-selected]' : 'selected' , '[class.mat-option-multiple]' : 'multiple' , '[class.mat-active]' : 'active' , '[id]' : 'id' , '[attr.aria-selected]' : 'selected.toString()' , '[attr.aria-disabled]' : 'disabled.toString()' , '[class.mat-option-disabled]' : 'disabled' , '(click)' : '_selectViaInteraction()' , '(keydown)' : '_handleKeydown($event)' , 'class' : 'mat-option' , } , styleUrls : [ 'option.css' ] , templateUrl : 'option.html' , encapsulation : ViewEncapsulation . None , changeDetection : ChangeDetectionStrategy . OnPush , } ) export class MatOption implements AfterViewChecked , OnDestroy { private _selected = false ; private _active = false ; private _disabled = false ; private _mostRecentViewValue = '' ; /** Whether the wrapping component is in multiple selection mode. */ get multiple ( ) { return this . _parent && this . _parent . multiple ; } /** Whether or not the option is currently selected. */ get selected ( ) : boolean { return this . _selected ; } /** The form value of the option. */ @ Input ( ) value : any ; /** The unique ID of the option. */ @ Input ( ) id : string = `mat-option-${ _uniqueIdCounter ++ }` ; /** Whether the option is disabled. */ @ Input ( ) get disabled ( ) { return ( this . group && this . group . disabled ) || this . _disabled ; } set disabled ( value ) { this . _disabled = coerceBooleanProperty ( value ) ; } /** Whether ripples for the option are disabled. */ get disableRipple ( ) { return this . _parent && this . _parent . disableRipple ; } /** Event emitted when the option is selected or deselected. */ @ Output ( ) readonly onSelectionChange = new EventEmitter < MatOptionSelectionChange > ( ) ; /** Emits when the state of the option changes and any parents have to be notified. */ readonly _stateChanges = new Subject < void > ( ) ; constructor ( private _element , private _changeDetectorRef , @ Optional ( ) @ Inject ( MAT_OPTION_PARENT_COMPONENT ) private _parent , @ Optional ( ) readonly group ) { } /**
     * Whether or not the option is currently active and ready to be selected.
     * An active option displays styles as if it is focused, but the
     * focus is actually retained somewhere else. This comes in handy
     * for components like autocomplete where focus must remain on the input.
     */ get active ( ) : boolean { return this . _active ; } /**
     * The displayed value of the option. It is necessary to show the selected option in the
     * select's trigger.
     */ get viewValue ( ) : string { return ( this . _getHostElement ( ) . textContent || '' ) . trim ( ) ; } /** Selects the option. */ select ( ) { if ( ! this . _selected ) { this . _selected = true ; this . _changeDetectorRef . markForCheck ( ) ; this . _emitSelectionChangeEvent ( ) ; } } /** Deselects the option. */ deselect ( ) { if ( this . _selected ) { this . _selected = false ; this . _changeDetectorRef . markForCheck ( ) ; this . _emitSelectionChangeEvent ( ) ; } } /** Sets focus onto this option. */ focus ( ) { const element = this . _getHostElement ( ) ; if ( typeof element . focus === 'function' ) { element . focus ( ) ; } } /**
     * This method sets display styles on the option to make it appear
     * active. This is used by the ActiveDescendantKeyManager so key
     * events will display the proper options as active on arrow key events.
     */ setActiveStyles ( ) { if ( ! this . _active ) { this . _active = true ; this . _changeDetectorRef . markForCheck ( ) ; } } /**
     * This method removes display styles on the option that made it appear
     * active. This is used by the ActiveDescendantKeyManager so key
     * events will display the proper options as active on arrow key events.
     */ setInactiveStyles ( ) { if ( this . _active ) { this . _active = false ; this . _changeDetectorRef . markForCheck ( ) ; } } /** Gets the label to be used when determining whether the option should be focused. */ getLabel ( ) { return this . viewValue ; } /** Ensures the option is selected when activated from the keyboard. */ _handleKeydown ( event ) { if ( ( event . keyCode === ENTER || event . keyCode === SPACE ) && ! hasModifierKey ( event ) ) { this . _selectViaInteraction ( ) ; event . preventDefault ( ) ; } } /**
     * `Selects the option while indicating the selection came from the user. Used to
     * determine if the select's view -> model callback should be invoked.`
     */ _selectViaInteraction ( ) { if ( ! this . disabled ) { this . _selected = this . multiple ? ! this . _selected : true ; this . _changeDetectorRef . markForCheck ( ) ; this . _emitSelectionChangeEvent ( true ) ; } } /** Returns the correct tabindex for the option depending on disabled state. */ _getTabIndex ( ) { return this . disabled ? '-1' : '0' ; } /** Gets the host DOM element. */ _getHostElement ( ) { return this . _element . nativeElement ; } ngAfterViewChecked ( ) { if ( this . _selected ) { const viewValue = this . viewValue ; if ( viewValue !== this . _mostRecentViewValue ) { this . _mostRecentViewValue = viewValue ; this . _stateChanges . next ( ) ; } } } ngOnDestroy ( ) { this . _stateChanges . complete ( ) ; } /** Emits the selection change event. */ private _emitSelectionChangeEvent ( isUserInput = false ) { this . onSelectionChange . emit ( new MatOptionSelectionChange ( this , isUserInput ) ) ; } } param optionIndex param options param optionGroups docs-private export function _countGroupLabelsBeforeOption ( optionIndex , options , optionGroups ) { if ( optionGroups . length ) { let optionsArray = options . toArray ( ) ; let groups = optionGroups . toArray ( ) ; let groupCounter = 0 ; for ( let i = 0 ; i < optionIndex + 1 ; i ++ ) { if ( optionsArray [ i ] . group && optionsArray [ i ] . group === groups [ groupCounter ] ) { groupCounter ++ ; } } return groupCounter ; } return 0 ; } param optionIndex param optionHeight param currentScrollPosition param panelHeight docs-private export function _getOptionScrollPosition ( optionIndex , optionHeight , currentScrollPosition , panelHeight ) { const optionOffset = optionIndex * optionHeight ; if ( optionOffset < currentScrollPosition ) { return optionOffset ; } if ( optionOffset + optionHeight > currentScrollPosition + panelHeight ) { return Math . max ( 0 , optionOffset - panelHeight + optionHeight ) ; } return currentScrollPosition ; }