import { LiveAnnouncer } from '@angular/cdk/a11y' ; import { AfterViewInit , ChangeDetectionStrategy , ChangeDetectorRef , Component , OnDestroy , Optional } from '@angular/core' ; import { merge , Subject } from 'rxjs' ; import { takeUntil } from 'rxjs/operators' ; 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 { isDateAfter , isDateBefore } from '../date-time-picker.utils' ; import { FocusedMonthItem , MonthViewItem , MonthViewService } from './month-view.service' ; @ Component ( { selector : 'ux-date-time-picker-month-view' , templateUrl : './month-view.component.html' , providers : [ MonthViewService ] , changeDetection : ChangeDetectionStrategy . OnPush } ) export class MonthViewComponent 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 ; } get _minMonth ( ) : Date | null { return this . _datePicker . min$ . value ? new Date ( this . _datePicker . min$ . value . getFullYear ( ) , this . _datePicker . min$ . value . getMonth ( ) ) : null ; } get _maxMonth ( ) : Date | null { return this . _datePicker . max$ . value ? new Date ( this . _datePicker . max$ . value . getFullYear ( ) , this . _datePicker . max$ . value . getMonth ( ) ) : null ; } private _onDestroy = new Subject < void > ( ) ; constructor ( private _datePicker , public monthService , private _liveAnnouncer , private _changeDetector , @ Optional ( ) private _rangeService , @ Optional ( ) private _rangeOptions ) { _datePicker . headerEvent$ . pipe ( takeUntil ( this . _onDestroy ) ) . subscribe ( event => event === DatePickerHeaderEvent . Next ? this . next ( ) : this . previous ( ) ) ; if ( this . _rangeService ) { this . _rangeService . onRangeChange . pipe ( takeUntil ( this . _onDestroy ) ) . subscribe ( ( ) => _changeDetector . detectChanges ( ) ) ; } if ( this . monthService . focused$ . value ) { if ( this . getDisabled ( this . monthService . focused$ . value ) ) { for ( const row of this . monthService . grid$ . value ) { for ( const column of row ) { if ( ! this . getDisabled ( column ) ) { this . monthService . setFocus ( column . month , column . year ) ; return ; } } } } } } ngAfterViewInit ( ) { merge ( this . _datePicker . min$ , this . _datePicker . max$ ) . pipe ( takeUntil ( this . _onDestroy ) ) . subscribe ( ( ) => this . _changeDetector . detectChanges ( ) ) ; } ngOnDestroy ( ) { this . _onDestroy . next ( ) ; this . _onDestroy . complete ( ) ; } /** Get the disabled state of a month */ getDisabled ( item ) { const date = new Date ( item . year , item . month ) ; if ( this . _isRangeMode ) { if ( this . _isRangeStart && ! this . _rangeStart && this . _rangeEnd && isDateAfter ( date , new Date ( this . _rangeEnd . getFullYear ( ) , this . _rangeEnd . getMonth ( ) ) ) ) { return true ; } if ( this . _isRangeEnd && ! this . _rangeEnd && this . _rangeStart && isDateBefore ( date , new Date ( this . _rangeStart . getFullYear ( ) , this . _rangeStart . getMonth ( ) ) ) ) { return true ; } } if ( this . _minMonth && isDateBefore ( date , this . _minMonth ) ) { return true ; } if ( this . _maxMonth && isDateAfter ( date , this . _maxMonth ) ) { return true ; } return false ; } /**
     * Go to the previous year
     */ previous ( ) { this . _datePicker . setViewportYear ( this . _datePicker . year$ . value - 1 ) ; } /**
     * Go to the next year
     */ next ( ) { this . _datePicker . setViewportYear ( this . _datePicker . year$ . value + 1 ) ; } param month select ( month ) { this . _datePicker . setViewportMonth ( month ) ; this . _datePicker . goToChildMode ( ) ; } focusMonth ( item , monthOffset ) { let targetMonth = item . month + monthOffset ; let targetYear = item . year ; if ( targetMonth < 0 ) { targetMonth += 12 ; targetYear -= 1 ; } if ( targetMonth >= 12 ) { targetMonth -= 12 ; targetYear += 1 ; } this . monthService . setFocus ( targetMonth , targetYear ) ; } trackRowByFn ( index ) { return index ; } trackMonthByFn ( _index , item ) { return `${ item . month } ${ item . year }` ; } getTabbable ( item ) { const focused = this . monthService . focused$ . value ; const grid = this . monthService . grid$ . value ; if ( focused ) { const isFocusedMonthVisible = ! ! grid . find ( row => ! ! row . find ( _item => _item . month === focused . month && _item . year === focused . year ) ) ; if ( isFocusedMonthVisible ) { return focused . month === item . month && focused . year === item . year ; } } const isSelectedMonthVisible = ! ! grid . find ( row => ! ! row . find ( month => month . isActiveMonth ) ) ; if ( isSelectedMonthVisible ) { return item . isActiveMonth ; } for ( const row of grid ) { for ( const column of row ) { if ( ! this . getDisabled ( column ) ) { return item === column ; } } } return false ; } /** Announce the date when we focus on a date */ announceRangeMode ( ) { if ( this . _isRangeMode ) { this . _liveAnnouncer . announce ( this . _isRangeStart ? this . _rangeService . startPickerAriaLabel : this . _rangeService . endPickerAriaLabel ) ; } } shouldFocus ( item ) { const focused = this . monthService . focused$ . value ; if ( focused ) { return focused . month === item . month && focused . year === item . year ; } } }