import { AfterViewInit , ChangeDetectionStrategy , ChangeDetectorRef , Component , ElementRef , EventEmitter , forwardRef , Input , Optional , OnDestroy , Output , Provider , ViewChild , ViewEncapsulation } from '@angular/core' ; import { NG_VALUE_ACCESSOR , ControlValueAccessor } from '@angular/forms' ; import { supportsPassiveEventListeners } from '@angular/cdk/platform' ; import { coerceBooleanProperty } from '@angular/cdk/coercion' ; import { MDCSwitchFoundation , MDCSwitchAdapter } from '@material/switch' ; import { MDCRippleAdapter , MDCRippleFoundation } from '@material/ripple' ; import { matches } from '@angular-mdc/web/dom' ; import { MDCComponent } from '@angular-mdc/web/base' ; import { MdcRipple , MDCRippleCapableSurface } from '@angular-mdc/web/ripple' ; import { MdcFormField , MdcFormFieldControl } from '@angular-mdc/web/form-field' ; export const MDC_SWITCH_CONTROL_VALUE_ACCESSOR = { provide : NG_VALUE_ACCESSOR , useExisting : forwardRef ( ( ) => MdcSwitch ) , multi : true } ; /** Change event object emitted by MdcSwitch. */ export class MdcSwitchChange { constructor ( /** The source MdcSwitch of the event. */ public source , /** The new `checked` value of the switch. */ public checked ) { } } let nextUniqueId = 0 ; @ Component ( { selector : 'mdc-switch' , host : { '[id]' : 'id' , 'class' : 'mdc-switch' , '[class.mdc-switch--checked]' : 'checked' , '[class.mdc-switch--disabled]' : 'disabled' , '(focus)' : '_inputElement.nativeElement.focus()' } , templateUrl : 'switch.html' , changeDetection : ChangeDetectionStrategy . OnPush , encapsulation : ViewEncapsulation . None , providers : [ MDC_SWITCH_CONTROL_VALUE_ACCESSOR , { provide : MdcFormFieldControl , useExisting : MdcSwitch } , MdcRipple ] } ) export class MdcSwitch extends MDCComponent < MDCSwitchFoundation > implements MdcFormFieldControl < any > , AfterViewInit , ControlValueAccessor , OnDestroy , MDCRippleCapableSurface { private _uniqueId : string = `mdc-switch-${ ++ nextUniqueId }` ; private _initialized : boolean = false ; _root ! : Element ; @ Input ( ) id : string = this . _uniqueId ; @ Input ( ) name : string | null = null ; @ Input ( ) tabIndex : number = 0 ; /** The value attribute of the native input element */ @ Input ( ) value : string | null = null ; @ Input ( ) get checked ( ) : boolean { return this . _checked ; } set checked ( value ) { if ( this . disabled ) { return ; } this . _checked = coerceBooleanProperty ( value ) ; this . _changeDetectorRef . markForCheck ( ) ; } private _checked : boolean = false ; @ Input ( ) get disabled ( ) : boolean { return this . _disabled ; } set disabled ( value ) { this . setDisabledState ( value ) ; } private _disabled : boolean = false ; @ Input ( ) get required ( ) : boolean { return this . _required ; } set required ( value ) { this . _required = coerceBooleanProperty ( value ) ; } private _required : boolean = false ; /** Used to set the aria-label attribute on the underlying input element. */ @ Input ( 'aria-label' ) ariaLabel : string | null = null ; /** Used to set the aria-labelledby attribute on the underlying input element. */ @ Input ( 'aria-labelledby' ) ariaLabelledby : string | null = null ; @ Output ( ) readonly change : EventEmitter < MdcSwitchChange > = new EventEmitter < MdcSwitchChange > ( ) ; @ ViewChild ( 'input' , { static : true } ) _inputElement ! : ElementRef < HTMLInputElement > ; @ ViewChild ( 'thumbUnderlay' , { static : false } ) thumbUnderlay ! : ElementRef < HTMLElement > ; /** View to model callback called when value changes */ private _onChange = ( _ ) => { } ; /** View to model callback called when control has been touched */ private _onTouched = ( ) => { } ; get inputId ( ) : string { return `${ this . id || this . _uniqueId }-input` ; } getDefaultFoundation ( ) { if ( ! this . _initialized ) { return undefined ; } const adapter = { addClass : ( className ) => this . _getHostElement ( ) . classList . add ( className ) , removeClass : ( className ) => this . _getHostElement ( ) . classList . remove ( className ) , setNativeControlChecked : ( checked ) => this . _getInputElement ( ) . checked = checked , setNativeControlDisabled : ( disabled ) => this . _getInputElement ( ) . disabled = disabled , setNativeControlAttr : ( attr , value ) => this . _getInputElement ( ) . setAttribute ( attr , value ) } ; return new MDCSwitchFoundation ( adapter ) ; } constructor ( private _changeDetectorRef , public ripple , public elementRef , @ Optional ( ) private _parentFormField ) { super ( elementRef ) ; this . _root = this . elementRef . nativeElement ; if ( this . _parentFormField ) { _parentFormField . elementRef . nativeElement . classList . add ( 'mdc-form-field' ) ; } } ngAfterViewInit ( ) { this . _initialized = true ; this . ripple = this . _createRipple ( ) ; this . ripple . init ( ) ; this . _asyncBuildFoundation ( ) . then ( ( ) => { this . _foundation . init ( ) ; this . setDisabledState ( this . _inputElement . nativeElement . disabled ) ; } ) ; } ngOnDestroy ( ) { this . ripple . destroy ( ) ; this . destroy ( ) ; } async _asyncBuildFoundation ( ) { this . _foundation = this . getDefaultFoundation ( ) ; } onChange ( evt ) { evt . stopPropagation ( ) ; this . _foundation . handleChange ( evt ) ; this . _checked = this . _inputElement . nativeElement . checked ; this . _foundation . setChecked ( this . _checked ) ; this . _emitChangeEvent ( ) ; this . _changeDetectorRef . markForCheck ( ) ; } onInputClick ( evt ) { evt . stopPropagation ( ) ; } onBlur ( ) { this . _onTouched ( ) ; } writeValue ( value ) { this . checked = ! ! value ; } registerOnChange ( fn ) { this . _onChange = fn ; } registerOnTouched ( fn ) { this . _onTouched = fn ; } /** Toggles the checked state of the switch. */ toggle ( ) { this . checked = ! this . checked ; this . _onChange ( this . checked ) ; } setDisabledState ( disabled ) { const newValue = coerceBooleanProperty ( disabled ) ; if ( newValue !== this . _disabled ) { this . _disabled = newValue ; this . _foundation ?. setDisabled ( newValue ) ; this . _changeDetectorRef . markForCheck ( ) ; } } focus ( ) { this . _inputElement . nativeElement . focus ( ) ; } private _createRipple ( ) { const rippleSurface = this . thumbUnderlay . nativeElement as HTMLElement ; const adapter = { ... MdcRipple . createAdapter ( this ) , addClass : ( className ) => rippleSurface . classList . add ( className ) , computeBoundingRect : ( ) => rippleSurface . getBoundingClientRect ( ) , deregisterInteractionHandler : ( evtType , handler ) => this . _inputElement . nativeElement . removeEventListener ( evtType , handler , supportsPassiveEventListeners ( ) ) , isSurfaceActive : ( ) => matches ( this . _inputElement . nativeElement , ':active' ) , isUnbounded : ( ) => true , registerInteractionHandler : ( evtType , handler ) => this . _inputElement . nativeElement . addEventListener ( evtType , handler , supportsPassiveEventListeners ( ) ) , removeClass : ( className ) => rippleSurface . classList . remove ( className ) , updateCssVariable : ( varName , value ) => rippleSurface . style . setProperty ( varName , value ) } ; return new MdcRipple ( this . elementRef , new MDCRippleFoundation ( adapter ) ) ; } /**
     * Emits a change event on the `change` output. Also notifies the FormControl about the change.
     */ private _emitChangeEvent ( ) { this . _onChange ( this . checked ) ; this . change . emit ( new MdcSwitchChange ( this , this . checked ) ) ; } /** Retrieves the DOM element of the component input. */ private _getInputElement ( ) { return this . _inputElement . nativeElement ; } /** Retrieves the DOM element of the component host. */ private _getHostElement ( ) { return this . elementRef . nativeElement ; } }