license import { DateService } from './date.service' ; import { batch , range , } from './helpers' ; import { CalendarDateInfo , CalendarDateOptions , CalendarRange , } from '../type' ; const DEFAULT_DATE_OPTIONS = { bounding : false , holiday : false , range : false , } ; export type DateRange < D > = CalendarDateInfo < D > [ ] ; export type DateBatch < D > = DateRange < D > [ ] ; export class CalendarDataService < D > { constructor ( protected dateService ) { } public createDayPickerData = ( date , dateRange ? ) : DateBatch < D > => { const weeks = this . createDates ( date , DEFAULT_DATE_OPTIONS , dateRange ) ; return this . withBoundingMonths ( weeks , date ) ; } ; public createMonthPickerData = ( date , rows , columns ) : DateBatch < D > => { const yearStart = this . dateService . getYearStart ( date ) ; const monthRange = range ( rows * columns , ( index ) : CalendarDateInfo < D > => { const monthDate = this . dateService . addMonth ( yearStart , index ) ; return { date : monthDate , ... DEFAULT_DATE_OPTIONS } ; } ) ; return batch ( monthRange , rows ) ; } ; public createYearPickerData = ( date , rows , columns ) : DateBatch < D > => { const yearStart = this . dateService . getYearStart ( date ) ; const yearRange = range ( rows * columns , ( index ) : CalendarDateInfo < D > => { const yearDate = this . dateService . addYear ( yearStart , index ) ; return { date : yearDate , ... DEFAULT_DATE_OPTIONS } ; } ) ; return batch ( yearRange , rows ) ; } ; public createDayPickerPagerData = ( startDate , endDate ) : DateRange < D > => { const numberOfDayPickers = this . getNumberOfMonths ( startDate , endDate ) + 1 ; return range ( numberOfDayPickers , ( index ) : CalendarDateInfo < D > => { const monthDate = this . dateService . addMonth ( startDate , index ) ; return { date : monthDate , ... DEFAULT_DATE_OPTIONS } ; } ) ; } ; public createYearPickerPagerData = ( startDate , endDate , rows , columns ) : DateRange < D > => { const numberOfYears = this . getNumberOfYears ( startDate , endDate ) + 1 ; const numberOfYearPickers = Math . max ( Math . ceil ( numberOfYears / ( rows * columns ) ) , 1 ) ; return range ( numberOfYearPickers , ( index ) : CalendarDateInfo < D > => { const yearDate = this . dateService . addYear ( startDate , index * rows * columns ) ; return { date : yearDate , ... DEFAULT_DATE_OPTIONS } ; } ) ; } ; public getNumberOfMonths = ( lhs , rhs ) : number => { const numberOfYears = this . getNumberOfYears ( lhs , rhs ) ; const numberOfMonths = this . dateService . getMonth ( rhs ) - this . dateService . getMonth ( lhs ) ; return numberOfMonths + numberOfYears * DateService . MONTHS_IN_YEAR ; } ; public getNumberOfYears = ( lhs , rhs ) : number => { return this . dateService . getYear ( rhs ) - this . dateService . getYear ( lhs ) ; } ; private createDates ( activeMonth , options , dateRange ? ) { let days = this . createDateRangeForMonth ( activeMonth , options ) ; if ( dateRange ) { days = this . withRangedDates ( days , dateRange ) ; } const startOfWeekDayDiff = this . getStartOfWeekDayDiff ( activeMonth ) ; return batch ( days , DateService . DAYS_IN_WEEK , startOfWeekDayDiff ) ; } private withRangedDates ( days , calendarRange ) { if ( calendarRange . startDate && ! calendarRange . endDate ) { return this . withRangedStartDates ( days , calendarRange . startDate ) ; } if ( calendarRange . startDate && calendarRange . endDate ) { return this . withRangedStartEndDates ( days , calendarRange . startDate , calendarRange . endDate ) ; } return days ; } private withRangedStartDates ( days , startDate ) { return days . map ( ( day ) : CalendarDateInfo < D > => { const isSameStartDate = this . dateService . compareDatesSafe ( day . date , startDate ) === 0 ; return isSameStartDate ? { ... day , range : true } : day ; } ) ; } private withRangedStartEndDates ( days , startDate , endDate ) { return days . map ( ( day ) : CalendarDateInfo < D > => { const isSameStartDate = this . dateService . compareDatesSafe ( day . date , startDate ) === 0 ; const isSameEndDate = this . dateService . compareDatesSafe ( day . date , endDate ) === 0 ; if ( isSameStartDate || isSameEndDate ) { return { ... day , range : true } ; } else { const isInRange = this . dateService . isBetween ( day . date , startDate , endDate ) ; return { ... day , range : isInRange } ; } } ) ; } private withBoundingMonths ( weeks , activeMonth ) { let withBoundingMonths = weeks ; if ( this . isShouldAddPrevBoundingMonth ( withBoundingMonths ) ) { withBoundingMonths = this . addPrevBoundingMonth ( withBoundingMonths , activeMonth ) ; } if ( this . isShouldAddNextBoundingMonth ( withBoundingMonths ) ) { withBoundingMonths = this . addNextBoundingMonth ( withBoundingMonths , activeMonth ) ; } return withBoundingMonths ; } private addPrevBoundingMonth ( weeks , activeMonth ) { const firstWeek = weeks . shift ( ) ; const numberOfBoundingDates = DateService . DAYS_IN_WEEK - firstWeek . length ; firstWeek . unshift ( ... this . createPrevBoundingDays ( activeMonth , numberOfBoundingDates ) ) ; return [ firstWeek , ... weeks ] ; } private addNextBoundingMonth ( weeks , activeMonth ) { const lastWeek = weeks . pop ( ) ; const numberOfBoundingDates = DateService . DAYS_IN_WEEK - lastWeek . length ; lastWeek . push ( ... this . createNextBoundingDays ( activeMonth , numberOfBoundingDates ) ) ; return [ ... weeks , lastWeek ] ; } private createPrevBoundingDays ( activeMonth , numberOfBoundingDates ) { const month = this . dateService . addMonth ( activeMonth , - 1 ) ; const daysInMonth = this . dateService . getNumberOfDaysInMonth ( month ) ; return this . createDateRangeForMonth ( month , DEFAULT_DATE_OPTIONS ) . slice ( daysInMonth - numberOfBoundingDates ) . map ( ( date ) : CalendarDateInfo < D > => { return { ... date , bounding : true } ; } ) ; } private createNextBoundingDays ( activeMonth , numberOfBoundingDates ) { const month = this . dateService . addMonth ( activeMonth , 1 ) ; return this . createDateRangeForMonth ( month , DEFAULT_DATE_OPTIONS ) . slice ( 0 , numberOfBoundingDates ) . map ( ( date ) : CalendarDateInfo < D > => { return { ... date , bounding : true } ; } ) ; } private getStartOfWeekDayDiff ( date ) { const startOfMonth = this . dateService . getMonthStart ( date ) ; return this . getWeekStartDiff ( startOfMonth ) ; } private getWeekStartDiff ( date ) { return ( DateService . DAYS_IN_WEEK - this . dateService . getFirstDayOfWeek ( ) + this . dateService . getDayOfWeek ( date ) ) % DateService . DAYS_IN_WEEK ; } private isShouldAddPrevBoundingMonth ( weeks ) { return weeks [ 0 ] . length < DateService . DAYS_IN_WEEK ; } private isShouldAddNextBoundingMonth ( weeks ) { return weeks [ weeks . length - 1 ] . length < DateService . DAYS_IN_WEEK ; } private createDateRangeForMonth ( monthDate , options ) { const daysInMonth = this . dateService . getNumberOfDaysInMonth ( monthDate ) ; return range ( daysInMonth , ( i ) : CalendarDateInfo < D > => { const year = this . dateService . getYear ( monthDate ) ; const month = this . dateService . getMonth ( monthDate ) ; const date = this . dateService . createDate ( year , month , i + 1 ) ; return { date , ... options } ; } ) ; } }