import { ChangeDetectionStrategy , ChangeDetectorRef , Component , EventEmitter , forwardRef , Input , Output , ViewEncapsulation } from '@angular/core' ; import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ; export const TIME_PICKER_VALUE_ACCESSOR = { provide : NG_VALUE_ACCESSOR , useExisting : forwardRef ( ( ) => TimePickerComponent ) , multi : true } ; @ Component ( { selector : 'ux-time-picker' , templateUrl : './time-picker.component.html' , encapsulation : ViewEncapsulation . None , changeDetection : ChangeDetectionStrategy . OnPush , providers : [ TIME_PICKER_VALUE_ACCESSOR ] , host : { 'aria-label' : 'Time Picker' } } ) export class TimePickerComponent implements ControlValueAccessor { /** Whether the arrow keys can be used to increment or decrement the selected time component. */ @ Input ( ) arrowkeys : boolean = true ; /** Whether the mouse scroll wheel can be used to increment or decrement the selected time component. */ @ Input ( ) mousewheel : boolean = true ; /** Whether the control is disabled. */ @ Input ( ) disabled : boolean = false ; /** Whether the control is readonly. */ @ Input ( ) readOnly : boolean = false ; /** Whether to show the meridian (AM/PM) selector. If this is false, the 24-hour clock will be used. */ @ Input ( ) showMeridian : boolean = false ; /** Whether to show the hour selector. */ @ Input ( ) showHours : boolean = true ; /** Whether to show the minute selector. */ @ Input ( ) showMinutes : boolean = true ; /** Whether to show the second selector. */ @ Input ( ) showSeconds : boolean = false ; /** Whether to show increment and decrement buttons in the time picker. */ @ Input ( ) showSpinners : boolean = true ; /** The number of hours to increment or decrement by when using the spinner buttons, arrow keys, or mouse scroll wheel. */ @ Input ( ) hourStep : number = 1 ; /** The number of minutes to increment or decrement by when using the spinner buttons, arrow keys, or mouse scroll wheel. */ @ Input ( ) minuteStep : number = 1 ; /** The number of seconds to increment or decrement by when using the spinner buttons, arrow keys, or mouse scroll wheel. */ @ Input ( ) secondStep : number = 1 ; /** The minimum value that the component will allow. */ @ Input ( ) min : Date ; /** The maximum value that the component will allow. */ @ Input ( ) max : Date ; /** An array containing the labels to show in the meridian selector. */ @ Input ( ) meridians : string [ ] = [ 'AM' , 'PM' ] ; /** The value to display. */ @ Input ( ) set value ( value ) { this . _value = new Date ( value ) ; this . valueChange . emit ( this . _value ) ; this . onChangeCallback ( this . _value ) ; this . onTouchedCallback ( ) ; } get value ( ) : Date { return new Date ( this . _value ) ; } get _meridian ( ) : string { return this . _value . getHours ( ) < 12 ? this . meridians [ 0 ] : this . meridians [ 1 ] ; } get _valid ( ) : boolean { return this . checkValidity ( this . _value ) ; } /** Emitted when the `value` changes. */ @ Output ( ) valueChange = new EventEmitter < Date > ( ) ; /** Emitted when the validity of the control changes. */ @ Output ( ) isValid = new EventEmitter < boolean > ( ) ; constructor ( private readonly _changeDetector ) { } onTouchedCallback : ( ) => void = ( ) => { } ; onChangeCallback : ( _ : Date ) => void = ( ) => { } ; private _value = new Date ( ) ; private _isValid : boolean = true ; writeValue ( value ) { this . value = value ; this . _changeDetector . markForCheck ( ) ; } registerOnChange ( fn ) { this . onChangeCallback = fn ; } registerOnTouched ( fn ) { this . onTouchedCallback = fn ; } setDisabledState ( isDisabled ) { this . disabled = isDisabled ; this . _changeDetector . markForCheck ( ) ; } getMeridianTime ( hour ) { return hour > 12 ? hour - 12 : hour ; } setHour ( hour ) { const date = this . value ; date . setHours ( hour ? hour : 0 ) ; this . value = date ; } setMinute ( minute ) { const date = this . value ; date . setMinutes ( minute ? minute : 0 ) ; this . value = date ; } setSeconds ( seconds ) { const date = this . value ; date . setSeconds ( seconds ? seconds : 0 ) ; this . value = date ; } incrementHour ( arrowkey = false ) { if ( this . disabled || arrowkey && ! this . arrowkeys ) { return ; } this . setHour ( this . value . getHours ( ) + this . hourStep ) ; } decrementHour ( arrowkey = false ) { if ( this . disabled || arrowkey && ! this . arrowkeys ) { return ; } this . setHour ( this . value . getHours ( ) - this . hourStep ) ; } incrementMinute ( arrowkey = false ) { if ( this . disabled || arrowkey && ! this . arrowkeys ) { return ; } this . setMinute ( this . value . getMinutes ( ) + this . minuteStep ) ; } decrementMinute ( arrowkey = false ) { if ( this . disabled || arrowkey && ! this . arrowkeys ) { return ; } this . setMinute ( this . value . getMinutes ( ) - this . minuteStep ) ; } incrementSecond ( arrowkey = false ) { if ( this . disabled || arrowkey && ! this . arrowkeys ) { return ; } this . setSeconds ( this . value . getSeconds ( ) + this . secondStep ) ; } decrementSecond ( arrowkey = false ) { if ( this . disabled || arrowkey && ! this . arrowkeys ) { return ; } this . setSeconds ( this . value . getSeconds ( ) - this . secondStep ) ; } selectMeridian ( meridian ) { const hour = this . value . getHours ( ) ; if ( meridian === this . meridians [ 0 ] ) { if ( hour >= 12 ) { this . setHour ( hour - 12 ) ; } } if ( meridian === this . meridians [ 1 ] ) { if ( hour < 12 ) { this . setHour ( hour + 12 ) ; } } } checkValidity ( date ) { let valid = true ; const min = this . normalizeDate ( this . min , date ) ; const max = this . normalizeDate ( this . max , date ) ; if ( ( min && date . getTime ( ) < min . getTime ( ) ) || ( max && date . getTime ( ) > max . getTime ( ) ) ) { valid = false ; } if ( valid !== this . _isValid ) { this . _isValid = valid ; this . isValid . emit ( valid ) ; } return valid ; } hourChange ( value ) { if ( value && typeof value === 'string' && value . trim ( ) === '' ) { return ; } let hour = typeof value === 'number' ? value : parseInt ( value ) ; if ( ! isNaN ( hour ) ) { if ( hour < 0 ) { hour = 0 ; } if ( hour > ( this . showMeridian ? 12 : 23 ) ) { hour = this . showMeridian ? 12 : 23 ; } } const currentHour = this . value . getHours ( ) ; if ( hour === currentHour ) { return ; } hour = isNaN ( hour ) ? currentHour : hour ; if ( this . showMeridian ) { if ( this . _meridian === this . meridians [ 0 ] ) { if ( hour >= 12 ) { hour -= 12 ; } } if ( this . _meridian === this . meridians [ 1 ] ) { if ( hour < 12 ) { hour += 12 ; } } } this . setHour ( hour ) ; } minuteChange ( value ) { let minute = typeof value === 'number' ? value : parseInt ( value ) ; const currentMinute = this . value . getMinutes ( ) ; if ( minute === currentMinute ) { return ; } if ( ! isNaN ( minute ) ) { if ( minute < 0 ) { minute = 59 ; } if ( minute > 59 ) { minute = 0 ; } } this . setMinute ( isNaN ( minute ) ? currentMinute : minute ) ; } secondChange ( value ) { let second = typeof value === 'number' ? value : parseInt ( value ) ; const currentSecond = this . value . getSeconds ( ) ; if ( second === currentSecond ) { return ; } if ( ! isNaN ( second ) ) { if ( second < 0 ) { second = 0 ; } if ( second > 59 ) { second = 59 ; } } this . setSeconds ( isNaN ( second ) ? currentSecond : second ) ; } /** Normalise a date's year/month/date components. */ private normalizeDate ( date , reference ) { if ( ! date ) { return null ; } const normalized = new Date ( date ) ; normalized . setFullYear ( reference . getFullYear ( ) ) ; normalized . setMonth ( reference . getMonth ( ) ) ; normalized . setDate ( reference . getDate ( ) ) ; return normalized ; } }