license import { FocusMonitor } from '@angular/cdk/a11y' ; import { coerceBooleanProperty } from '@angular/cdk/coercion' ; import { Attribute , ChangeDetectionStrategy , ChangeDetectorRef , Component , ElementRef , EventEmitter , forwardRef , Inject , Input , NgZone , OnDestroy , Optional , Output , ViewChild , ViewEncapsulation , AfterViewChecked , } from '@angular/core' ; import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ; import { CanColor , CanColorCtor , CanDisable , CanDisableCtor , CanDisableRipple , CanDisableRippleCtor , HasTabIndex , HasTabIndexCtor , MatRipple , mixinColor , mixinDisabled , mixinDisableRipple , mixinTabIndex , } from '@angular/material/core' ; import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations' ; import { MAT_CHECKBOX_CLICK_ACTION , MatCheckboxClickAction } from './checkbox-config' ; let nextUniqueId = 0 ; let calculatedRippleRadius = 0 ; docs-private export const MAT_CHECKBOX_CONTROL_VALUE_ACCESSOR = { provide : NG_VALUE_ACCESSOR , useExisting : forwardRef ( ( ) => MatCheckbox ) , multi : true } ; docs-private export enum TransitionCheckState { /** The initial state of the component before any user interaction. */ Init , /** The state representing the component when it's becoming checked. */ Checked , /** The state representing the component when it's becoming unchecked. */ Unchecked , /** The state representing the component when it's becoming indeterminate. */ Indeterminate } /** Change event object emitted by MatCheckbox. */ export class MatCheckboxChange { /** The source MatCheckbox of the event. */ source : MatCheckbox ; /** The new `checked` value of the checkbox. */ checked : boolean ; } docs-private export class MatCheckboxBase { constructor ( public _elementRef ) { } } export const _MatCheckboxMixinBase = mixinTabIndex ( mixinColor ( mixinDisableRipple ( mixinDisabled ( MatCheckboxBase ) ) , 'accent' ) ) ; /**
 * A material design checkbox component. Supports all of the functionality of an HTML5 checkbox,
 * and exposes a similar API. A MatCheckbox can be either checked, unchecked, indeterminate, or
 * disabled. Note that all additional accessibility attributes are taken care of by the component,
 * so there is no need to provide them yourself. However, if you want to omit a label and still
 * have the checkbox be accessible, you may supply an [aria-label] input.
 * See: https://material.io/design/components/selection-controls.html
 */ @ Component ( { moduleId : module . id , selector : 'mat-checkbox' , templateUrl : 'checkbox.html' , styleUrls : [ 'checkbox.css' ] , exportAs : 'matCheckbox' , host : { 'class' : 'mat-checkbox' , '[id]' : 'id' , '[attr.tabindex]' : 'null' , '[class.mat-checkbox-indeterminate]' : 'indeterminate' , '[class.mat-checkbox-checked]' : 'checked' , '[class.mat-checkbox-disabled]' : 'disabled' , '[class.mat-checkbox-label-before]' : 'labelPosition == "before"' , '[class._mat-animation-noopable]' : `_animationMode === 'NoopAnimations'` , } , providers : [ MAT_CHECKBOX_CONTROL_VALUE_ACCESSOR ] , inputs : [ 'disableRipple' , 'color' , 'tabIndex' ] , encapsulation : ViewEncapsulation . None , changeDetection : ChangeDetectionStrategy . OnPush } ) export class MatCheckbox extends _MatCheckboxMixinBase implements ControlValueAccessor , AfterViewChecked , OnDestroy , CanColor , CanDisable , HasTabIndex , CanDisableRipple { /**
     * Attached to the aria-label attribute of the host element. In most cases, arial-labelledby will
     * take precedence so this may be omitted.
     */ @ Input ( 'aria-label' ) ariaLabel : string = '' ; /**
     * Users can specify the `aria-labelledby` attribute which will be forwarded to the input element
     */ @ Input ( 'aria-labelledby' ) ariaLabelledby : string | null = null ; private _uniqueId : string = `mat-checkbox-${ ++ nextUniqueId }` ; /** A unique id for the checkbox input. If none is supplied, it will be auto-generated. */ @ Input ( ) id : string = this . _uniqueId ; /** Returns the unique id for the visual hidden input. */ get inputId ( ) : string { return `${ this . id || this . _uniqueId }-input` ; } /** Whether the checkbox is required. */ @ Input ( ) get required ( ) : boolean { return this . _required ; } set required ( value ) { this . _required = coerceBooleanProperty ( value ) ; } private _required : boolean ; /** Whether the label should appear after or before the checkbox. Defaults to 'after' */ @ Input ( ) labelPosition : 'before' | 'after' = 'after' ; /** Name value will be applied to the input element if present */ @ Input ( ) name : string | null = null ; /** Event emitted when the checkbox's `checked` value changes. */ @ Output ( ) readonly change : EventEmitter < MatCheckboxChange > = new EventEmitter < MatCheckboxChange > ( ) ; /** Event emitted when the checkbox's `indeterminate` value changes. */ @ Output ( ) readonly indeterminateChange : EventEmitter < boolean > = new EventEmitter < boolean > ( ) ; /** The value attribute of the native input element */ @ Input ( ) value : string ; /** The native `<input type="checkbox">` element */ @ ViewChild ( 'input' ) _inputElement : ElementRef < HTMLInputElement > ; /** Reference to the ripple instance of the checkbox. */ @ ViewChild ( MatRipple ) ripple : MatRipple ; docs-private _onTouched : ( ) => any = ( ) => { } ; private _currentAnimationClass : string = '' ; private _currentCheckState : TransitionCheckState = TransitionCheckState . Init ; private _controlValueAccessorChangeFn : ( value : any ) => void = ( ) => { } ; constructor ( elementRef , private _changeDetectorRef , private _focusMonitor , private _ngZone , @ Attribute ( 'tabindex' ) tabIndex , @ Optional ( ) @ Inject ( MAT_CHECKBOX_CLICK_ACTION ) private _clickAction , @ Optional ( ) @ Inject ( ANIMATION_MODULE_TYPE ) public _animationMode ? ) { super ( elementRef ) ; this . tabIndex = parseInt ( tabIndex ) || 0 ; this . _focusMonitor . monitor ( elementRef , true ) . subscribe ( focusOrigin => { if ( ! focusOrigin ) { Promise . resolve ( ) . then ( ( ) => this . _onTouched ( ) ) ; } } ) ; } ngAfterViewChecked ( ) { this . _calculateRippleRadius ( ) ; } ngOnDestroy ( ) { this . _focusMonitor . stopMonitoring ( this . _elementRef ) ; } /**
     * Whether the checkbox is checked.
     */ @ Input ( ) get checked ( ) : boolean { return this . _checked ; } set checked ( value ) { if ( value != this . checked ) { this . _checked = value ; this . _changeDetectorRef . markForCheck ( ) ; } } private _checked : boolean = false ; /**
     * Whether the checkbox is disabled. This fully overrides the implementation provided by
     * mixinDisabled, but the mixin is still required because mixinTabIndex requires it.
     */ @ Input ( ) get disabled ( ) { return this . _disabled ; } set disabled ( value ) { const newValue = coerceBooleanProperty ( value ) ; if ( newValue !== this . disabled ) { this . _disabled = newValue ; this . _changeDetectorRef . markForCheck ( ) ; } } private _disabled : boolean = false ; /**
     * Whether the checkbox is indeterminate. This is also known as "mixed" mode and can be used to
     * represent a checkbox with three states, e.g. a checkbox that represents a nested list of
     * checkable items. Note that whenever checkbox is manually clicked, indeterminate is immediately
     * set to false.
     */ @ Input ( ) get indeterminate ( ) : boolean { return this . _indeterminate ; } set indeterminate ( value ) { const changed = value != this . _indeterminate ; this . _indeterminate = value ; if ( changed ) { if ( this . _indeterminate ) { this . _transitionCheckState ( TransitionCheckState . Indeterminate ) ; } else { this . _transitionCheckState ( this . checked ? TransitionCheckState . Checked : TransitionCheckState . Unchecked ) ; } this . indeterminateChange . emit ( this . _indeterminate ) ; } } private _indeterminate : boolean = false ; _isRippleDisabled ( ) { return this . disableRipple || this . disabled ; } /** Method being called whenever the label text changes. */ _onLabelTextChange ( ) { this . _changeDetectorRef . detectChanges ( ) ; } writeValue ( value ) { this . checked = ! ! value ; } registerOnChange ( fn ) { this . _controlValueAccessorChangeFn = fn ; } registerOnTouched ( fn ) { this . _onTouched = fn ; } setDisabledState ( isDisabled ) { this . disabled = isDisabled ; } _getAriaChecked ( ) { return this . checked ? 'true' : ( this . indeterminate ? 'mixed' : 'false' ) ; } private _transitionCheckState ( newState ) { let oldState = this . _currentCheckState ; let element = this . _elementRef . nativeElement ; if ( oldState === newState ) { return ; } if ( this . _currentAnimationClass . length > 0 ) { element . classList . remove ( this . _currentAnimationClass ) ; } this . _currentAnimationClass = this . _getAnimationClassForCheckStateTransition ( oldState , newState ) ; this . _currentCheckState = newState ; if ( this . _currentAnimationClass . length > 0 ) { element . classList . add ( this . _currentAnimationClass ) ; const animationClass = this . _currentAnimationClass ; this . _ngZone . runOutsideAngular ( ( ) => { setTimeout ( ( ) => { element . classList . remove ( animationClass ) ; } , 1000 ) ; } ) ; } } private _emitChangeEvent ( ) { const event = new MatCheckboxChange ( ) ; event . source = this ; event . checked = this . checked ; this . _controlValueAccessorChangeFn ( this . checked ) ; this . change . emit ( event ) ; } /** Toggles the `checked` state of the checkbox. */ toggle ( ) { this . checked = ! this . checked ; } param event _onInputClick ( event ) { event . stopPropagation ( ) ; if ( ! this . disabled && this . _clickAction !== 'noop' ) { if ( this . indeterminate && this . _clickAction !== 'check' ) { Promise . resolve ( ) . then ( ( ) => { this . _indeterminate = false ; this . indeterminateChange . emit ( this . _indeterminate ) ; } ) ; } this . toggle ( ) ; this . _transitionCheckState ( this . _checked ? TransitionCheckState . Checked : TransitionCheckState . Unchecked ) ; this . _emitChangeEvent ( ) ; } else if ( ! this . disabled && this . _clickAction === 'noop' ) { this . _inputElement . nativeElement . checked = this . checked ; this . _inputElement . nativeElement . indeterminate = this . indeterminate ; } } /** Focuses the checkbox. */ focus ( ) { this . _focusMonitor . focusVia ( this . _inputElement , 'keyboard' ) ; } _onInteractionEvent ( event ) { event . stopPropagation ( ) ; } private _getAnimationClassForCheckStateTransition ( oldState , newState ) { if ( this . _animationMode === 'NoopAnimations' ) { return '' ; } let animSuffix = '' ; switch ( oldState ) { case TransitionCheckState . Init : if ( newState === TransitionCheckState . Checked ) { animSuffix = 'unchecked-checked' ; } else if ( newState == TransitionCheckState . Indeterminate ) { animSuffix = 'unchecked-indeterminate' ; } else { return '' ; } break ; case TransitionCheckState . Unchecked : animSuffix = newState === TransitionCheckState . Checked ? 'unchecked-checked' : 'unchecked-indeterminate' ; break ; case TransitionCheckState . Checked : animSuffix = newState === TransitionCheckState . Unchecked ? 'checked-unchecked' : 'checked-indeterminate' ; break ; case TransitionCheckState . Indeterminate : animSuffix = newState === TransitionCheckState . Checked ? 'indeterminate-checked' : 'indeterminate-unchecked' ; break ; } return `mat-checkbox-anim-${ animSuffix }` ; } /**
     * Calculate the radius for the ripple based on the ripple elements width.  Only calculated once
     * for the application.
     */ private _calculateRippleRadius ( ) { if ( ! calculatedRippleRadius ) { const rippleWidth = this . _elementRef . nativeElement . querySelector ( '.mat-checkbox-ripple' ) . clientWidth || 0 ; calculatedRippleRadius = rippleWidth / 2 ; } this . ripple . radius = calculatedRippleRadius ; } }