import { FocusableOption , FocusMonitor , FocusOrigin } from '@angular/cdk/a11y' ; import { BooleanInput , coerceBooleanProperty } from '@angular/cdk/coercion' ; import { AfterViewInit , Attribute , ChangeDetectionStrategy , ChangeDetectorRef , Component , Directive , ElementRef , EventEmitter , forwardRef , Inject , Input , OnDestroy , Optional , Output , ViewChild , ViewEncapsulation , } from '@angular/core' ; import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ; import { CanDisable , HasTabIndex , mixinDisabled , mixinTabIndex } from '@sbb-esta/angular/core' ; import { SbbCheckboxDefaultOptions , SBB_CHECKBOX_DEFAULT_OPTIONS , SBB_CHECKBOX_DEFAULT_OPTIONS_FACTORY , } from './checkbox-config' ; let nextUniqueId = 0 ; const defaults = SBB_CHECKBOX_DEFAULT_OPTIONS_FACTORY ( ) ; docs-private export const SBB_CHECKBOX_CONTROL_VALUE_ACCESSOR = { provide : NG_VALUE_ACCESSOR , useExisting : forwardRef ( ( ) => SbbCheckbox ) , multi : true , } ; /** Change event object emitted by SbbCheckbox. */ export class SbbCheckboxChange { /** The source SbbCheckbox of the event. */ source : SbbCheckbox ; /** The new `checked` value of the checkbox. */ checked : boolean ; } const _SbbCheckboxMixinBase = mixinTabIndex ( mixinDisabled ( class { } ) ) ; /** Base class with all of the `SbbCheckbox` functionality. */ @ Directive ( ) export class _SbbCheckboxBase extends _SbbCheckboxMixinBase implements ControlValueAccessor , AfterViewInit , OnDestroy , CanDisable , HasTabIndex , FocusableOption { /**
     * Attached to the aria-label attribute of the host element. In most cases, aria-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 ; /** The 'aria-describedby' attribute is read after the element's label and field type. */ @ Input ( 'aria-describedby' ) ariaDescribedby : string ; private _uniqueId : string = `sbb-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 < SbbCheckboxChange > = new EventEmitter < SbbCheckboxChange > ( ) ; /** 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 > ; docs-private _onTouched : ( ) => any = ( ) => { } ; private _controlValueAccessorChangeFn : ( value : any ) => void = ( ) => { } ; constructor ( private _elementRef , protected _changeDetectorRef , private _focusMonitor , @ Attribute ( 'tabindex' ) tabIndex , @ Optional ( ) @ Inject ( SBB_CHECKBOX_DEFAULT_OPTIONS ) private _options ? ) { super ( ) ; this . _options = this . _options || defaults ; this . tabIndex = parseInt ( tabIndex , 10 ) || 0 ; } ngAfterViewInit ( ) { this . _focusMonitor . monitor ( this . _elementRef , true ) . subscribe ( ( focusOrigin ) => { if ( ! focusOrigin ) { Promise . resolve ( ) . then ( ( ) => { this . _onTouched ( ) ; this . _changeDetectorRef . markForCheck ( ) ; } ) ; } } ) ; this . _syncIndeterminate ( this . _indeterminate ) ; } 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 ( ) override get disabled ( ) : boolean { return this . _disabled ; } override 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 = coerceBooleanProperty ( value ) ; if ( changed ) { this . indeterminateChange . emit ( this . _indeterminate ) ; } this . _syncIndeterminate ( this . _indeterminate ) ; } private _indeterminate : boolean = false ; /** 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 ( ) { if ( this . checked ) { return 'true' ; } return this . indeterminate ? 'mixed' : 'false' ; } protected _emitChangeEvent ( ) { const event = new SbbCheckboxChange ( ) ; 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 ; } /**
     * Event handler for checkbox input element.
     * Toggles checked state if element is not disabled.
     * Do not toggle on (change) event since IE doesn't fire change event when
     *   indeterminate checkbox is clicked.
     */ _onInputClick ( event ) { const clickAction = this . _options ?. clickAction ; event . stopPropagation ( ) ; if ( ! this . disabled && clickAction !== 'noop' ) { if ( this . indeterminate && clickAction !== 'check' ) { Promise . resolve ( ) . then ( ( ) => { this . _indeterminate = false ; this . indeterminateChange . emit ( this . _indeterminate ) ; } ) ; } this . toggle ( ) ; this . _emitChangeEvent ( ) ; } else if ( ! this . disabled && clickAction === 'noop' ) { this . _inputElement . nativeElement . checked = this . checked ; this . _inputElement . nativeElement . indeterminate = this . indeterminate ; } } /** Focuses the checkbox. */ focus ( origin ? , options ? ) { if ( origin ) { this . _focusMonitor . focusVia ( this . _inputElement , origin , options ) ; } else { this . _inputElement . nativeElement . focus ( options ) ; } } _onInteractionEvent ( event ) { event . stopPropagation ( ) ; } /**
     * Syncs the indeterminate value with the checkbox DOM node.
     *
     * We sync `indeterminate` directly on the DOM node, because in Ivy the check for whether a
     * property is supported on an element boils down to `if (propName in element)`. Domino's
     * HTMLInputElement doesn't have an `indeterminate` property so Ivy will warn during
     * server-side rendering.
     */ private _syncIndeterminate ( value ) { const nativeCheckbox = this . _inputElement ; if ( nativeCheckbox ) { nativeCheckbox . nativeElement . indeterminate = value ; } } } /**
 * A SBB design checkbox component. Supports all of the functionality of an HTML5 checkbox,
 * and exposes a similar API. A SbbCheckbox 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.
 */ @ Component ( { selector : 'sbb-checkbox' , templateUrl : './checkbox.html' , exportAs : 'sbbCheckbox' , host : { class : 'sbb-checkbox sbb-selection-item' , '[id]' : 'id' , '[attr.tabindex]' : 'null' , '[class.sbb-selection-indeterminate]' : 'indeterminate' , '[class.sbb-selection-checked]' : 'checked' , '[class.sbb-selection-disabled]' : 'disabled' , } , providers : [ SBB_CHECKBOX_CONTROL_VALUE_ACCESSOR ] , inputs : [ 'tabIndex' ] , encapsulation : ViewEncapsulation . None , changeDetection : ChangeDetectionStrategy . OnPush , } ) export class SbbCheckbox extends _SbbCheckboxBase { }