import { Component , forwardRef , OnInit , ViewEncapsulation , Input , Output , EventEmitter , AfterViewInit , OnChanges } from '@angular/core' ; import { InputCoreComponent } from '@sq-ui/ng-sq-common' ; import { NG_VALUE_ACCESSOR } from '@angular/forms' ; import { CalendarDay , InCalendarPicker } from '../interfaces/calendar-entities' ; import { CalendarPeriodRelativityEnum } from '../enums/calendar-period-relativity.enum' ; import { DateRange } from '../interfaces/date-range' ; import { CalendarPeriodTypeEnum } from '../enums/calendar-period-type.enum' ; import { DateObjectType } from '../enums/date-object-type.enum' ; import { TimepickerConfig } from '../interfaces/timepicker-config' ; import { List } from 'immutable' ; import { CalendarManagerService } from '../calendar-manager.service' ; import * as momentNs from 'moment' ; const moment = momentNs ; const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR = { provide : NG_VALUE_ACCESSOR , useExisting : forwardRef ( ( ) => DatetimePickerComponent ) , multi : true } ; @ Component ( { selector : 'sq-datetime-picker' , templateUrl : './datetime-picker.component.html' , styleUrls : [ './datetime-picker.component.scss' ] , encapsulation : ViewEncapsulation . None , providers : [ CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR ] } ) export class DatetimePickerComponent extends InputCoreComponent implements OnInit , AfterViewInit , OnChanges { @ Input ( ) locale = 'en' ; @ Input ( ) maxDate : momentNs . Moment | Date ; @ Input ( ) minDate : momentNs . Moment | Date ; @ Input ( ) isMultipleSelect = false ; @ Input ( ) format : string ; @ Input ( ) isTimepickerEnabled = false ; @ Input ( ) dateObjectType : string = DateObjectType . Moment ; @ Input ( ) timepickerConfig : TimepickerConfig ; @ Output ( ) dateSelectionChange : EventEmitter < momentNs . Moment | Date > = new EventEmitter < momentNs . Moment | Date > ( ) ; weekdays : string [ ] ; months : InCalendarPicker [ ] ; yearsList : InCalendarPicker [ ] ; calendar : Array < CalendarDay [ ] > ; currentMonth : momentNs . Moment ; isMonthsPickerEnabled = false ; isYearsPickerEnabled = false ; time : momentNs . Moment ; calendarPeriodRelativity = CalendarPeriodRelativityEnum ; period : CalendarPeriodTypeEnum = CalendarPeriodTypeEnum . Month ; private selectedDates : List < momentNs . Moment > = List < momentNs . Moment > ( ) ; private parsedSelectedDates : any ; constructor ( private calendarManager ) { super ( ) ; } ngOnInit ( ) { moment . locale ( this . locale ) ; this . calendarManager . setLocale ( this . locale ) ; const now = moment ( ) . hours ( 0 ) . minutes ( 0 ) . locale ( this . locale ) ; this . selectedDates = List ( [ now . clone ( ) ] ) ; this . weekdays = this . calendarManager . getWeekdays ( ) ; this . calendar = this . getMonthCalendar ( now . clone ( ) ) ; this . initializeAuthorValuesIfAny ( ) ; } ngAfterViewInit ( ) { setTimeout ( ( ) => { this . setValueResult ( ) ; } ) ; } ngOnChanges ( changesObj ) { if ( changesObj . timepickerConfig && changesObj . timepickerConfig . currentValue ) { this . setValueResult ( ) ; } } onDateClick ( date ) { switch ( date . relativityToCurrentMonth ) { case CalendarPeriodRelativityEnum . After : this . select ( date ) ; this . next ( ) ; break ; case CalendarPeriodRelativityEnum . Before : this . select ( date ) ; this . previous ( ) ; break ; default : this . select ( date ) ; break ; } } select ( date ) { const month = this . currentMonth . clone ( ) ; if ( date . relativityToCurrentMonth === CalendarPeriodRelativityEnum . Before ) { month . subtract ( 1 , 'month' ) ; } if ( date . relativityToCurrentMonth === CalendarPeriodRelativityEnum . After ) { month . add ( 1 , 'month' ) ; } this . markDateAsSelected ( date ) ; this . dateSelectionChange . emit ( this . value ) ; } next ( ) { if ( this . period === CalendarPeriodTypeEnum . Month ) { const nextMonth = this . currentMonth . add ( 1 , 'month' ) ; this . calendar = this . getMonthCalendar ( nextMonth ) ; } if ( this . period === CalendarPeriodTypeEnum . Year ) { const dateRange = { minDate : moment ( this . minDate ) , maxDate : moment ( this . maxDate ) } ; this . yearsList = this . calendarManager . generateYearPickerCollection ( null , 19 , dateRange ) ; } } previous ( ) { if ( this . period === CalendarPeriodTypeEnum . Month ) { const previousMonth = this . currentMonth . subtract ( 1 , 'month' ) ; this . calendar = this . getMonthCalendar ( previousMonth ) ; } if ( this . period === CalendarPeriodTypeEnum . Year ) { const dateRange = { minDate : moment ( this . minDate ) , maxDate : moment ( this . maxDate ) } ; this . yearsList = this . calendarManager . generateYearPickerCollection ( null , - 19 , dateRange ) ; } } getMonthCalendar ( startPeriod ) { const selectedDates = this . selectedDates . toArray ( ) ; const dateRange = { minDate : this . minDate , maxDate : this . maxDate } ; this . currentMonth = startPeriod . clone ( ) ; return this . calendarManager . generateCalendarForMonth ( startPeriod , this . currentMonth , selectedDates , dateRange ) ; } showMonthsPicker ( year = this . currentMonth . year ( ) ) { this . deselectAll ( ) ; this . isYearsPickerEnabled = false ; this . isMonthsPickerEnabled = true ; this . currentMonth . year ( year ) ; const dateRange = { minDate : this . minDate , maxDate : this . maxDate } ; this . period = CalendarPeriodTypeEnum . Month ; this . months = this . calendarManager . generateMonthPickerCollection ( year , dateRange ) ; } showYearsPicker ( ) { this . deselectAll ( ) ; this . isMonthsPickerEnabled = false ; this . isYearsPickerEnabled = true ; const dateRange = { minDate : this . minDate , maxDate : this . maxDate } ; this . period = CalendarPeriodTypeEnum . Year ; this . yearsList = this . calendarManager . generateYearPickerCollection ( this . currentMonth , 19 , dateRange ) ; } selectMonth ( month ) { this . calendar = this . getMonthCalendar ( month . momentObj ) ; this . isMonthsPickerEnabled = false ; } selectYear ( year ) { this . showMonthsPicker ( year . momentObj . year ( ) ) ; } onTimeChange ( ) { this . setValueResult ( ) ; this . dateSelectionChange . emit ( this . value ) ; } private initializeAuthorValuesIfAny ( ) { const subscription = this . _modelToViewChange . subscribe ( ( newValue ) => { if ( this . selectedDates . size === 1 && this . selectedDates . get ( 0 ) . isSame ( moment ( ) , 'day' ) ) { if ( newValue ) { this . deselectAll ( ) ; if ( Array . isArray ( newValue ) ) { newValue . forEach ( ( date ) => { const convertedDate = this . calendarManager . findADateFromCalendar ( moment ( date ) , this . calendar ) ; this . markDateAsSelected ( convertedDate ) ; } ) ; } else { const calendarDay = this . calendarManager . findADateFromCalendar ( moment ( newValue ) , this . calendar ) ; this . markDateAsSelected ( calendarDay ) ; } } } subscription . unsubscribe ( ) ; } ) ; } private markDateAsSelected ( date ) { const selectedMomentObj = moment ( date . momentObj ) ; const selectedIndex = this . calendarManager . getSelectedItemIndex ( selectedMomentObj , this . selectedDates . toArray ( ) ) ; if ( this . isMultipleSelect ) { if ( selectedIndex > - 1 ) { date . isSelected = false ; this . selectedDates = this . selectedDates . remove ( selectedIndex ) ; } else { this . selectedDates = this . selectedDates . push ( selectedMomentObj ) ; date . isSelected = true ; } } else { const previousDate = this . calendarManager . findADateFromCalendar ( this . selectedDates . get ( 0 ) , this . calendar ) ; if ( previousDate ) { previousDate . isSelected = false ; } this . selectedDates = this . selectedDates . clear ( ) ; this . selectedDates = this . selectedDates . push ( selectedMomentObj ) ; date . isSelected = true ; } this . setValueResult ( ) ; } private deselectAll ( ) { this . selectedDates . toArray ( ) . forEach ( ( selectedDate ) => { const calendarDay = this . calendarManager . findADateFromCalendar ( selectedDate , this . calendar ) ; if ( calendarDay ) { calendarDay . isSelected = false ; } } ) ; this . selectedDates = List ( [ ] ) ; this . setValueResult ( ) ; } private setValueResult ( ) { this . parsedSelectedDates = this . selectedDates . toArray ( ) ; if ( this . parsedSelectedDates . length > 0 ) { this . setValueTimeIfNeeded ( ) ; this . sortValueIfNeeded ( ) ; this . toValueDateObjectTypeIfNeeded ( ) ; this . toValueFormatIfNeeded ( ) ; } if ( this . isMultipleSelect ) { this . value = this . parsedSelectedDates ; } else { this . value = this . parsedSelectedDates [ 0 ] ; } } private toValueDateObjectTypeIfNeeded ( ) { if ( ! this . format ) { switch ( this . dateObjectType ) { case DateObjectType . Date : this . parsedSelectedDates = this . parsedSelectedDates . map ( ( momentObj ) => { return momentObj . toDate ( ) ; } ) ; break ; case DateObjectType . Unix : this . parsedSelectedDates = this . parsedSelectedDates . map ( ( momentObj ) => { return momentObj . toDate ( ) . getTime ( ) ; } ) ; break ; } } } private toValueFormatIfNeeded ( ) { if ( this . format ) { const formattedDates = this . parsedSelectedDates . map ( ( date ) => { return moment ( date ) . format ( this . format ) ; } ) ; this . parsedSelectedDates = formattedDates ; } } private setValueTimeIfNeeded ( ) { if ( this . isTimepickerEnabled && this . time ) { const datesWithTime = this . parsedSelectedDates . map ( ( momentObj ) => { return momentObj . hours ( this . time . hours ( ) ) . minutes ( this . time . minutes ( ) ) ; } ) ; this . parsedSelectedDates = datesWithTime ; } } private sortValueIfNeeded ( ) { if ( this . isMultipleSelect ) { const sortedDates = this . calendarManager . sortDatesAsc ( this . parsedSelectedDates ) ; this . parsedSelectedDates = sortedDates ; } } }