import { LiveAnnouncer } from '@angular/cdk/a11y' ; import { AfterViewInit , ChangeDetectionStrategy , ChangeDetectorRef , Component , OnDestroy , Optional } from '@angular/core' ; import { merge , Subject } from 'rxjs' ; import { delay , filter , takeUntil } from 'rxjs/operators' ; import { FocusIndicatorOriginService } from '../../../directives/accessibility/index' ; import { DateRangeOptions } from '../../date-range-picker/date-range-picker.directive' ; import { DateRangePicker , DateRangeService } from '../../date-range-picker/date-range.service' ; import { DatePickerHeaderEvent , DateTimePickerService } from '../date-time-picker.service' ; import { compareDays , isDateAfter , isDateBefore } from '../date-time-picker.utils' ; import { DayViewItem , DayViewService } from './day-view.service' ; @ Component ( { selector : 'ux-date-time-picker-day-view' , templateUrl : './day-view.component.html' , providers : [ DayViewService ] , changeDetection : ChangeDetectionStrategy . OnPush } ) export class DayViewComponent implements AfterViewInit , OnDestroy { /** Determine if we are in range selection mode */ get _isRangeMode ( ) : boolean { return ! ! this . _rangeOptions ; } /** Determine if this picker is the start picker */ get _isRangeStart ( ) : boolean { return this . _isRangeMode && this . _rangeOptions . picker === DateRangePicker . Start ; } /** Determine if this picker is the end picker */ get _isRangeEnd ( ) : boolean { return this . _isRangeMode && this . _rangeOptions . picker === DateRangePicker . End ; } get _rangeStart ( ) : Date | null { return this . _isRangeMode && this . _rangeService ? this . _rangeService . start : null ; } get _rangeEnd ( ) : Date | null { return this . _isRangeMode && this . _rangeService ? this . _rangeService . end : null ; } private _onDestroy = new Subject < void > ( ) ; constructor ( public datePicker , public dayService , private _changeDetector , private _focusOrigin , private _liveAnnouncer , @ Optional ( ) private _rangeService , @ Optional ( ) private _rangeOptions ) { datePicker . headerEvent$ . pipe ( takeUntil ( this . _onDestroy ) ) . subscribe ( event => event === DatePickerHeaderEvent . Next ? this . next ( ) : this . previous ( ) ) ; if ( _rangeService ) { merge ( _rangeService . onRangeChange , _rangeService . onHoverChange ) . pipe ( takeUntil ( this . _onDestroy ) ) . subscribe ( ( ) => _changeDetector . detectChanges ( ) ) ; _rangeService . onStartChange . pipe ( takeUntil ( this . _onDestroy ) , filter ( date => ! ! date && this . _isRangeEnd && this . datePicker . initialised ) , delay ( 0 ) ) . subscribe ( date => this . onRangeChange ( date ) ) ; _rangeService . onEndChange . pipe ( takeUntil ( this . _onDestroy ) , filter ( date => ! ! date && this . _isRangeStart && this . datePicker . initialised ) , delay ( 0 ) ) . subscribe ( date => this . onRangeChange ( date ) ) ; _rangeService . onClear . pipe ( takeUntil ( this . _onDestroy ) ) . subscribe ( ( ) => this . datePicker . selected$ . next ( null ) ) ; } } ngAfterViewInit ( ) { merge ( this . datePicker . min$ , this . datePicker . max$ ) . pipe ( takeUntil ( this . _onDestroy ) ) . subscribe ( ( ) => this . _changeDetector . detectChanges ( ) ) ; if ( ! this . datePicker . initialised && this . _rangeStart && ! this . _rangeEnd && this . _isRangeEnd ) { this . onRangeChange ( this . _rangeStart ) ; } if ( ! this . datePicker . initialised && this . _rangeEnd && ! this . _rangeStart && this . _isRangeStart ) { this . onRangeChange ( this . _rangeEnd ) ; } } ngOnDestroy ( ) { this . _onDestroy . next ( ) ; this . _onDestroy . complete ( ) ; } /**
     * Navigate to the previous page of dates
     */ previous ( ) { this . datePicker . setViewportMonth ( this . datePicker . month$ . value - 1 ) ; } /**
     * Navigate to the next page of dates
     */ next ( ) { this . datePicker . setViewportMonth ( this . datePicker . month$ . value + 1 ) ; } param date select ( date ) { if ( this . _isRangeMode && ! this . _rangeStart && ! this . _rangeEnd ) { this . _rangeService . clear ( ) ; } if ( this . _isRangeMode && this . _isRangeStart && this . _rangeStart && compareDays ( this . _rangeStart , date ) ) { this . _rangeService . setStartDate ( null ) ; this . datePicker . selected$ . next ( null ) ; return ; } if ( this . _isRangeMode && this . _isRangeEnd && this . _rangeEnd && compareDays ( this . _rangeEnd , date ) ) { this . _rangeService . setEndDate ( null ) ; this . datePicker . selected$ . next ( null ) ; return ; } if ( this . _isRangeMode ) { if ( this . _isRangeStart && ! this . _rangeService . showTime ) { this . datePicker . setDate ( date . getDate ( ) , date . getMonth ( ) , date . getFullYear ( ) , 0 , 0 , 0 ) ; } else if ( this . _isRangeEnd && ! this . _rangeService . showTime ) { this . datePicker . setDate ( date . getDate ( ) , date . getMonth ( ) , date . getFullYear ( ) , 23 , 59 , 59 ) ; } else { this . datePicker . setDate ( date . getDate ( ) , date . getMonth ( ) , date . getFullYear ( ) , this . datePicker . hours , this . datePicker . minutes , this . datePicker . seconds ) ; } } else { this . datePicker . setDate ( date . getDate ( ) , date . getMonth ( ) , date . getFullYear ( ) ) ; } this . dayService . setFocus ( date . getDate ( ) , date . getMonth ( ) , date . getFullYear ( ) ) ; if ( this . _isRangeMode && this . _isRangeStart && this . _rangeStart && this . _rangeEnd ) { if ( this . _rangeStart . getTime ( ) > this . _rangeEnd . getTime ( ) ) { this . _rangeService . setEndDate ( null ) ; } } if ( this . _isRangeMode && this . _isRangeEnd && this . _rangeStart && this . _rangeEnd ) { if ( this . _rangeEnd . getTime ( ) < this . _rangeStart . getTime ( ) ) { this . _rangeService . setStartDate ( null ) ; } } } trackWeekByFn ( index ) { return index ; } trackDayByFn ( _index , item ) { return `${ item . day } ${ item . month } ${ item . year }` ; } focusDate ( item , dayOffset ) { const target = new Date ( item . date . setDate ( item . date . getDate ( ) + dayOffset ) ) ; this . _focusOrigin . setOrigin ( 'keyboard' ) ; this . dayService . setFocus ( target . getDate ( ) , target . getMonth ( ) , target . getFullYear ( ) ) ; } getTabbable ( item ) { const focused = this . dayService . focused$ . value ; const grid = this . dayService . grid$ . value ; const month = this . datePicker . month$ . value ; if ( focused ) { const isFocusedDayVisible = ! ! grid . find ( row => ! ! row . find ( _item => _item . day === focused . day && _item . month === focused . month && _item . year === focused . year && _item . month === month ) ) ; if ( isFocusedDayVisible ) { return focused . day === item . day && focused . month === item . month && focused . year === item . year ; } } const isSelectedDayVisible = ! ! grid . find ( row => ! ! row . find ( day => day . isActive ) ) ; if ( isSelectedDayVisible ) { return item . isActive ; } for ( const row of grid ) { for ( const column of row ) { if ( column === item && column . month === month && ! this . getDisabled ( column . date ) ) { return true ; } } } return false ; } getDisabled ( date ) { if ( this . _isRangeMode ) { if ( this . _isRangeStart && ! this . _rangeStart && this . _rangeEnd && isDateAfter ( date , this . _rangeEnd ) ) { return true ; } if ( this . _isRangeEnd && ! this . _rangeEnd && this . _rangeStart && isDateBefore ( date , this . _rangeStart ) ) { return true ; } } if ( this . datePicker . min$ . value && isDateBefore ( date , this . datePicker . min$ . value ) ) { return true ; } if ( this . datePicker . max$ . value && isDateAfter ( date , this . datePicker . max$ . value ) ) { return true ; } return false ; } isRangeStartDate ( date ) { return this . _isRangeMode && this . _rangeStart && compareDays ( date , this . _rangeStart ) ; } isRangeEndDate ( date ) { return this . _isRangeMode && this . _rangeEnd && compareDays ( date , this . _rangeEnd ) ; } isWithinRange ( date ) { return this . _isRangeMode && this . _rangeStart && isDateAfter ( date , this . _rangeStart ) && isDateBefore ( date , this . _rangeEnd ) ; } isDateHovered ( date ) { if ( ! this . _isRangeMode || ! this . _rangeService . hover || this . _rangeStart && this . _rangeEnd ) { return ; } return this . _rangeStart && isDateAfter ( date , this . _rangeStart ) && isDateBefore ( date , this . _rangeService . hover , true ) || this . _rangeEnd && isDateBefore ( date , this . _rangeEnd ) && isDateAfter ( date , this . _rangeService . hover , true ) ; } isItemActive ( date , isActive ) { if ( ! this . _isRangeMode ) { return isActive ; } return this . _isRangeStart && this . _rangeStart && compareDays ( this . _rangeStart , date ) || this . _isRangeEnd && this . _rangeEnd && compareDays ( this . _rangeEnd , date ) ; } onRangeMouseEnter ( date ) { if ( this . _isRangeMode ) { this . _rangeService . setDateMouseEnter ( date ) ; } } onRangeMouseLeave ( date ) { if ( this . _isRangeMode ) { this . _rangeService . setDateMouseLeave ( date ) ; } } /** Announce the date when we focus on a date */ announceRangeMode ( ) { if ( this . _isRangeMode ) { this . _liveAnnouncer . announce ( this . _isRangeStart ? this . _rangeService . startPickerAriaLabel : this . _rangeService . endPickerAriaLabel ) ; } } /** Determine if we should focus a date */ shouldFocus ( item ) { if ( ! this . datePicker . initialised && this . _isRangeEnd || this . _rangeService && this . _rangeService . isChangingTime ) { return false ; } const { day , month , year } = this . dayService . focused$ . value ; return day === item . day && month === item . month && year === item . year && item . isCurrentMonth ; } /** Update the viewport when the range changes to ensure focus is present on a valid item */ private onRangeChange ( date ) { if ( ! ( this . _isRangeStart && ! this . _rangeStart || this . _isRangeEnd && ! this . _rangeEnd ) ) { return ; } const currentDate = new Date ( this . datePicker . year$ . value , this . datePicker . month$ . value ) ; const startShouldUpdate = this . _isRangeStart && ! this . _rangeStart && ( currentDate . getFullYear ( ) > date . getFullYear ( ) || currentDate . getFullYear ( ) === date . getFullYear ( ) && currentDate . getMonth ( ) > date . getMonth ( ) ) ; const endShouldUpdate = this . _isRangeEnd && ! this . _rangeEnd && ( currentDate . getFullYear ( ) < date . getFullYear ( ) || currentDate . getFullYear ( ) === date . getFullYear ( ) && currentDate . getMonth ( ) < date . getMonth ( ) ) ; if ( startShouldUpdate || endShouldUpdate ) { this . datePicker . setViewportMonth ( date . getMonth ( ) ) ; this . datePicker . setViewportYear ( date . getFullYear ( ) ) ; this . dayService . setFocus ( date . getDate ( ) , date . getMonth ( ) , date . getFullYear ( ) ) ; this . _changeDetector . detectChanges ( ) ; } } }