license import { FocusMonitor } from '@angular/cdk/a11y' ; import { coerceBooleanProperty } from '@angular/cdk/coercion' ; import { ChangeDetectionStrategy , ChangeDetectorRef , Component , ElementRef , EventEmitter , Inject , Input , NgZone , OnDestroy , Optional , Output , ViewChild , ViewEncapsulation , } from '@angular/core' ; import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ; import { CHECKBOX_CLICK_ACTION , CheckboxClickAction } from './checkbox-config' ; let uniqueId = 0 ; /** Change event object emitted by Checkbox. */ export class CheckboxChange { /** The source Checkbox of the event. */ source : CheckboxComponent ; /** The new `checked` value of the checkbox. */ checked : boolean ; } /**
 * 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 ( { selector : 'gd-checkbox' , exportAs : 'gdCheckbox' , templateUrl : './checkbox.component.html' , styleUrls : [ './checkbox.component.scss' ] , encapsulation : ViewEncapsulation . None , changeDetection : ChangeDetectionStrategy . OnPush , providers : [ { provide : NG_VALUE_ACCESSOR , useExisting : CheckboxComponent , multi : true , } , ] , host : { 'class' : 'Checkbox' , '[class.Checkbox--checked]' : 'checked' , '[class.Checkbox--indeterminate]' : 'indeterminate' , '[class.Checkbox--disabled]' : 'disabled' , '[class.Checkbox--fixedWidth]' : 'fixedWidth' , '[id]' : 'id' , '[attr.tabindex]' : 'null' , } , } ) export class CheckboxComponent implements ControlValueAccessor , OnDestroy { /** Name value will be applied to the input element if present */ @ Input ( ) name : string | null = null ; /** The value attribute of the native input element */ @ Input ( ) value : string ; /** Event emitted when the checkbox's `checked` value changes. */ @ Output ( ) readonly change = new EventEmitter < CheckboxChange > ( ) ; /** Event emitted when the checkbox's `indeterminate` value changes. */ @ Output ( ) readonly indeterminateChange = new EventEmitter < boolean > ( ) ; /** The native `<input type="checkbox">` element */ @ ViewChild ( 'input' ) _inputElement : ElementRef < HTMLInputElement > ; /**
     * 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 = `gd-checkbox-${ uniqueId ++ }` ; /** A unique id for the checkbox input. If none is supplied, it will be auto-generated. */ @ Input ( ) id : string = this . _uniqueId ; /** Determine to use fixed size of checkbox. */ @ Input ( ) fixedWidth : boolean = false ; constructor ( public _elementRef , private changeDetectorRef , private focusMonitor , private ngZone , @ Optional ( ) @ Inject ( CHECKBOX_CLICK_ACTION ) private _clickAction ) { this . focusMonitor . monitor ( this . _elementRef . nativeElement , true ) . subscribe ( ( focusOrigin ) => { if ( ! focusOrigin ) { Promise . resolve ( ) . then ( ( ) => this . _onTouched ( ) ) ; } } ) ; } private _checked : boolean = false ; /**
     * 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 _disabled : 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 _indeterminate : 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 ) { this . indeterminateChange . emit ( this . _indeterminate ) ; } } get inputId ( ) : string { return `${ this . id || this . _uniqueId }-input` ; } docs-private _onTouched : ( ) => any = ( ) => { } ; ngOnDestroy ( ) { this . focusMonitor . stopMonitoring ( this . _elementRef . nativeElement ) ; } writeValue ( value ) { this . checked = ! ! value ; } registerOnChange ( fn ) { this . _controlValueAccessorChangeFn = fn ; } registerOnTouched ( fn ) { this . _onTouched = fn ; } setDisabledState ( isDisabled ) { this . disabled = isDisabled ; } /** Toggles the `checked` state of the checkbox. */ toggle ( ) { this . checked = ! this . checked ; } _getAriaChecked ( ) { return this . checked ? 'true' : ( this . indeterminate ? 'mixed' : 'false' ) ; } /** Method being called whenever the label text changes. */ _onLabelTextChange ( ) { this . changeDetectorRef . detectChanges ( ) ; } 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 . _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 . nativeElement , 'keyboard' ) ; } _onInteractionEvent ( event ) { event . stopPropagation ( ) ; } private _controlValueAccessorChangeFn : ( value : any ) => void = ( ) => { } ; private _emitChangeEvent ( ) { const event = new CheckboxChange ( ) ; event . source = this ; event . checked = this . checked ; this . _controlValueAccessorChangeFn ( this . checked ) ; this . change . emit ( event ) ; } }