import { Component , ElementRef , ViewEncapsulation , ViewChild , ChangeDetectorRef , OnDestroy } from "@angular/core" ; import { IMyDate , IMyMonth , IMyCalendarDay , IMyCalendarMonth , IMyCalendarYear , IMyWeek , IMyOptions , IMySelectorPosition } from "./interfaces/index" ; import { UtilService } from "./services/ngx-my-date-picker.util.service" ; import { KeyCode } from "./enums/key-code.enum" ; import { MonthId } from "./enums/month-id.enum" ; declare var require ; const myDpStyles = require ( "./ngx-my-date-picker.component.css" ) ; const myDpTpl = require ( "./ngx-my-date-picker.component.html" ) ; @ Component ( { selector : "ngx-my-date-picker" , styles : [ myDpStyles ] , template : myDpTpl , providers : [ UtilService ] , encapsulation : ViewEncapsulation . None } ) export class NgxMyDatePicker implements OnDestroy { @ ViewChild ( "selectorEl" ) selectorEl : any ; opts : IMyOptions ; visibleMonth : IMyMonth = { monthTxt : "" , monthNbr : 0 , year : 0 } ; selectedMonth : IMyMonth = { monthTxt : "" , monthNbr : 0 , year : 0 } ; selectedDate : IMyDate = { year : 0 , month : 0 , day : 0 } ; weekDays : Array < string > = [ ] ; dates : Array < IMyWeek > = [ ] ; months : Array < Array < IMyCalendarMonth > > = [ ] ; years : Array < Array < IMyCalendarYear > > = [ ] ; disableTodayBtn : boolean = false ; dayIdx : number = 0 ; weekDayOpts : Array < string > = [ "su" , "mo" , "tu" , "we" , "th" , "fr" , "sa" ] ; selectMonth : boolean = false ; selectYear : boolean = false ; dateChanged : Function ; calendarViewChanged : Function ; closedByEsc : Function ; selectorPos : IMySelectorPosition = null ; prevMonthDisabled : boolean = false ; nextMonthDisabled : boolean = false ; prevYearDisabled : boolean = false ; nextYearDisabled : boolean = false ; prevYearsDisabled : boolean = false ; nextYearsDisabled : boolean = false ; prevMonthId : number = MonthId . prev ; currMonthId : number = MonthId . curr ; nextMonthId : number = MonthId . next ; constructor ( public elem , private cdr , private utilService ) { } ngOnDestroy ( ) { this . removeListener ( ) ; } onClickListener = ( evt ) => this . onClick ( evt ) ; addListener ( ) { this . elem . nativeElement . addEventListener ( "click" , this . onClickListener ) ; } removeListener ( ) { this . elem . nativeElement . removeEventListener ( "click" , this . onClickListener ) ; } onClick ( evt ) { if ( ( this . opts . monthSelector || this . opts . yearSelector ) && evt . target ) { this . resetMonthYearSelect ( ) ; } } initialize ( opts , defaultMonth , selectorPos , inputValue , dc , cvc , cbe ) { this . opts = opts ; this . selectorPos = selectorPos ; this . weekDays . length = 0 ; this . addListener ( ) ; this . isTodayDisabled ( ) ; this . dayIdx = this . weekDayOpts . indexOf ( this . opts . firstDayOfWeek ) ; if ( this . dayIdx !== - 1 ) { let idx = this . dayIdx ; for ( let i = 0 ; i < this . weekDayOpts . length ; i ++ ) { this . weekDays . push ( this . opts . dayLabels [ this . weekDayOpts [ idx ] ] ) ; idx = this . weekDayOpts [ idx ] === "sa" ? 0 : idx + 1 ; } } let date = this . utilService . isDateValid ( inputValue , this . opts . dateFormat , this . opts . minYear , this . opts . maxYear , this . opts . disableUntil , this . opts . disableSince , this . opts . disableWeekends , this . opts . disableDates , this . opts . disableDateRanges , this . opts . disableWeekdays , this . opts . monthLabels , this . opts . enableDates ) ; if ( date . day !== 0 && date . month !== 0 && date . year !== 0 ) { this . selectedDate = date ; } else { if ( defaultMonth !== null && defaultMonth !== undefined && defaultMonth !== "" ) { this . selectedMonth = this . utilService . parseDefaultMonth ( defaultMonth ) ; } } this . dateChanged = dc ; this . calendarViewChanged = cvc ; this . closedByEsc = cbe ; this . setVisibleMonth ( ) ; } setCalendarView ( date ) { this . selectedDate = date ; this . setVisibleMonth ( ) ; } resetMonthYearSelect ( ) { this . selectMonth = false ; this . selectYear = false ; } onSelectMonthClicked ( event ) { event . stopPropagation ( ) ; this . selectMonth = ! this . selectMonth ; this . selectYear = false ; this . cdr . detectChanges ( ) ; if ( this . selectMonth ) { let today = this . getToday ( ) ; this . months . length = 0 ; for ( let i = 1 ; i <= 12 ; i += 3 ) { let row = [ ] ; for ( let j = i ; j < i + 3 ; j ++ ) { let disabled = this . utilService . isMonthDisabledByDisableUntil ( { year : this . visibleMonth . year , month : j , day : this . daysInMonth ( j , this . visibleMonth . year ) } , this . opts . disableUntil ) || this . utilService . isMonthDisabledByDisableSince ( { year : this . visibleMonth . year , month : j , day : 1 } , this . opts . disableSince ) ; row . push ( { nbr : j , name : this . opts . monthLabels [ j ] , currMonth : j === today . month && this . visibleMonth . year === today . year , selected : j === this . visibleMonth . monthNbr , disabled : disabled } ) ; } this . months . push ( row ) ; } } } onMonthCellClicked ( cell ) { let mc = cell . nbr !== this . visibleMonth . monthNbr ; this . visibleMonth = { monthTxt : this . opts . monthLabels [ cell . nbr ] , monthNbr : cell . nbr , year : this . visibleMonth . year } ; this . generateCalendar ( cell . nbr , this . visibleMonth . year , mc ) ; this . selectMonth = false ; this . selectorEl . nativeElement . focus ( ) ; } onMonthCellKeyDown ( event , cell ) { if ( ( event . keyCode === KeyCode . enter || event . keyCode === KeyCode . space ) && ! cell . disabled ) { event . preventDefault ( ) ; this . onMonthCellClicked ( cell ) ; } } onSelectYearClicked ( event ) { event . stopPropagation ( ) ; this . selectYear = ! this . selectYear ; this . selectMonth = false ; this . cdr . detectChanges ( ) ; if ( this . selectYear ) { this . generateYears ( this . visibleMonth . year ) ; } } onYearCellClicked ( cell ) { let yc = cell . year !== this . visibleMonth . year ; this . visibleMonth = { monthTxt : this . visibleMonth . monthTxt , monthNbr : this . visibleMonth . monthNbr , year : cell . year } ; this . generateCalendar ( this . visibleMonth . monthNbr , cell . year , yc ) ; this . selectYear = false ; this . selectorEl . nativeElement . focus ( ) ; } onPrevYears ( event , year ) { event . stopPropagation ( ) ; this . generateYears ( year - 25 ) ; } onNextYears ( event , year ) { event . stopPropagation ( ) ; this . generateYears ( year + 25 ) ; } generateYears ( year ) { this . years . length = 0 ; let today = this . getToday ( ) ; for ( let i = year ; i <= 20 + year ; i += 5 ) { let row = [ ] ; for ( let j = i ; j < i + 5 ; j ++ ) { let disabled = this . utilService . isMonthDisabledByDisableUntil ( { year : j , month : this . visibleMonth . monthNbr , day : this . daysInMonth ( this . visibleMonth . monthNbr , j ) } , this . opts . disableUntil ) || this . utilService . isMonthDisabledByDisableSince ( { year : j , month : this . visibleMonth . monthNbr , day : 1 } , this . opts . disableSince ) ; let minMax = j < this . opts . minYear || j > this . opts . maxYear ; row . push ( { year : j , currYear : j === today . year , selected : j === this . visibleMonth . year , disabled : disabled || minMax } ) ; } this . years . push ( row ) ; } this . prevYearsDisabled = this . years [ 0 ] [ 0 ] . year <= this . opts . minYear || this . utilService . isMonthDisabledByDisableUntil ( { year : this . years [ 0 ] [ 0 ] . year - 1 , month : this . visibleMonth . monthNbr , day : this . daysInMonth ( this . visibleMonth . monthNbr , this . years [ 0 ] [ 0 ] . year - 1 ) } , this . opts . disableUntil ) ; this . nextYearsDisabled = this . years [ 4 ] [ 4 ] . year >= this . opts . maxYear || this . utilService . isMonthDisabledByDisableSince ( { year : this . years [ 4 ] [ 4 ] . year + 1 , month : this . visibleMonth . monthNbr , day : 1 } , this . opts . disableSince ) ; } onYearCellKeyDown ( event , cell ) { if ( ( event . keyCode === KeyCode . enter || event . keyCode === KeyCode . space ) && ! cell . disabled ) { event . preventDefault ( ) ; this . onYearCellClicked ( cell ) ; } } isTodayDisabled ( ) { this . disableTodayBtn = this . utilService . isDisabledDate ( this . getToday ( ) , this . opts . minYear , this . opts . maxYear , this . opts . disableUntil , this . opts . disableSince , this . opts . disableWeekends , this . opts . disableDates , this . opts . disableDateRanges , this . opts . disableWeekdays , this . opts . enableDates ) ; } setVisibleMonth ( ) { let y = 0 , m = 0 ; if ( this . selectedDate . year === 0 && this . selectedDate . month === 0 && this . selectedDate . day === 0 ) { if ( this . selectedMonth . year === 0 && this . selectedMonth . monthNbr === 0 ) { let today = this . getToday ( ) ; y = today . year ; m = today . month ; } else { y = this . selectedMonth . year ; m = this . selectedMonth . monthNbr ; } } else { y = this . selectedDate . year ; m = this . selectedDate . month ; } this . visibleMonth = { monthTxt : this . opts . monthLabels [ m ] , monthNbr : m , year : y } ; this . generateCalendar ( m , y , true ) ; } onPrevMonth ( ) { let d = this . getDate ( this . visibleMonth . year , this . visibleMonth . monthNbr , 1 ) ; d . setMonth ( d . getMonth ( ) - 1 ) ; let y = d . getFullYear ( ) ; let m = d . getMonth ( ) + 1 ; this . visibleMonth = { monthTxt : this . opts . monthLabels [ m ] , monthNbr : m , year : y } ; this . generateCalendar ( m , y , true ) ; } onNextMonth ( ) { let d = this . getDate ( this . visibleMonth . year , this . visibleMonth . monthNbr , 1 ) ; d . setMonth ( d . getMonth ( ) + 1 ) ; let y = d . getFullYear ( ) ; let m = d . getMonth ( ) + 1 ; this . visibleMonth = { monthTxt : this . opts . monthLabels [ m ] , monthNbr : m , year : y } ; this . generateCalendar ( m , y , true ) ; } onPrevYear ( ) { this . visibleMonth . year -- ; this . generateCalendar ( this . visibleMonth . monthNbr , this . visibleMonth . year , true ) ; } onNextYear ( ) { this . visibleMonth . year ++ ; this . generateCalendar ( this . visibleMonth . monthNbr , this . visibleMonth . year , true ) ; } onCloseSelector ( event ) { if ( event . keyCode === KeyCode . esc ) { this . closedByEsc ( ) ; } } onTodayClicked ( ) { let today = this . getToday ( ) ; this . selectDate ( today ) ; if ( ! this . opts . closeSelectorOnDateSelect ) { this . setVisibleMonth ( ) ; } } onCellClicked ( cell ) { if ( cell . cmo === this . prevMonthId ) { this . onPrevMonth ( ) ; if ( ! this . opts . allowSelectionOnlyInCurrentMonth ) { this . selectDate ( cell . dateObj ) ; } } else if ( cell . cmo === this . currMonthId ) { this . selectDate ( cell . dateObj ) ; } else if ( cell . cmo === this . nextMonthId ) { this . onNextMonth ( ) ; if ( ! this . opts . allowSelectionOnlyInCurrentMonth ) { this . selectDate ( cell . dateObj ) ; } } this . resetMonthYearSelect ( ) ; } onCellKeyDown ( event , cell ) { if ( ( event . keyCode === KeyCode . enter || event . keyCode === KeyCode . space ) && ! cell . disabled ) { event . preventDefault ( ) ; this . onCellClicked ( cell ) ; } } selectDate ( date ) { this . selectedDate = date ; this . dateChanged ( this . utilService . getDateModel ( date , this . opts . dateFormat , this . opts . monthLabels ) , this . opts . closeSelectorOnDateSelect ) ; } monthStartIdx ( y , m ) { let d = new Date ( ) ; d . setDate ( 1 ) ; d . setMonth ( m - 1 ) ; d . setFullYear ( y ) ; let idx = d . getDay ( ) + this . sundayIdx ( ) ; return idx >= 7 ? idx - 7 : idx ; } daysInMonth ( m , y ) { return new Date ( y , m , 0 ) . getDate ( ) ; } daysInPrevMonth ( m , y ) { let d = this . getDate ( y , m , 1 ) ; d . setMonth ( d . getMonth ( ) - 1 ) ; return this . daysInMonth ( d . getMonth ( ) + 1 , d . getFullYear ( ) ) ; } isCurrDay ( d , m , y , cmo , today ) { return d === today . day && m === today . month && y === today . year && cmo === this . currMonthId ; } getToday ( ) { let date = new Date ( ) ; return { year : date . getFullYear ( ) , month : date . getMonth ( ) + 1 , day : date . getDate ( ) } ; } getDayNumber ( date ) { let d = this . getDate ( date . year , date . month , date . day ) ; return d . getDay ( ) ; } getWeekday ( date ) { return this . weekDayOpts [ this . getDayNumber ( date ) ] ; } getDate ( year , month , day ) { return new Date ( year , month - 1 , day , 0 , 0 , 0 , 0 ) ; } sundayIdx ( ) { return this . dayIdx > 0 ? 7 - this . dayIdx : 0 ; } generateCalendar ( m , y , notifyChange ) { this . dates . length = 0 ; let today = this . getToday ( ) ; let monthStart = this . monthStartIdx ( y , m ) ; let dInThisM = this . daysInMonth ( m , y ) ; let dInPrevM = this . daysInPrevMonth ( m , y ) ; let dayNbr = 1 ; let cmo = this . prevMonthId ; for ( let i = 1 ; i < 7 ; i ++ ) { let week = [ ] ; if ( i === 1 ) { let pm = dInPrevM - monthStart + 1 ; for ( let j = pm ; j <= dInPrevM ; j ++ ) { let date = { year : m === 1 ? y - 1 : y , month : m === 1 ? 12 : m - 1 , day : j } ; week . push ( { dateObj : date , cmo : cmo , currDay : this . isCurrDay ( j , m , y , cmo , today ) , disabled : this . utilService . isDisabledDate ( date , this . opts . minYear , this . opts . maxYear , this . opts . disableUntil , this . opts . disableSince , this . opts . disableWeekends , this . opts . disableDates , this . opts . disableDateRanges , this . opts . disableWeekdays , this . opts . enableDates ) , markedDate : this . utilService . isMarkedDate ( date , this . opts . markDates , this . opts . markWeekends ) , highlight : this . utilService . isHighlightedDate ( date , this . opts . sunHighlight , this . opts . satHighlight , this . opts . highlightDates ) } ) ; } cmo = this . currMonthId ; let daysLeft = 7 - week . length ; for ( let j = 0 ; j < daysLeft ; j ++ ) { let date = { year : y , month : m , day : dayNbr } ; week . push ( { dateObj : date , cmo : cmo , currDay : this . isCurrDay ( dayNbr , m , y , cmo , today ) , disabled : this . utilService . isDisabledDate ( date , this . opts . minYear , this . opts . maxYear , this . opts . disableUntil , this . opts . disableSince , this . opts . disableWeekends , this . opts . disableDates , this . opts . disableDateRanges , this . opts . disableWeekdays , this . opts . enableDates ) , markedDate : this . utilService . isMarkedDate ( date , this . opts . markDates , this . opts . markWeekends ) , highlight : this . utilService . isHighlightedDate ( date , this . opts . sunHighlight , this . opts . satHighlight , this . opts . highlightDates ) } ) ; dayNbr ++ ; } } else { for ( let j = 1 ; j < 8 ; j ++ ) { if ( dayNbr > dInThisM ) { dayNbr = 1 ; cmo = this . nextMonthId ; } let date = { year : cmo === this . nextMonthId && m === 12 ? y + 1 : y , month : cmo === this . currMonthId ? m : cmo === this . nextMonthId && m < 12 ? m + 1 : 1 , day : dayNbr } ; week . push ( { dateObj : date , cmo : cmo , currDay : this . isCurrDay ( dayNbr , m , y , cmo , today ) , disabled : this . utilService . isDisabledDate ( date , this . opts . minYear , this . opts . maxYear , this . opts . disableUntil , this . opts . disableSince , this . opts . disableWeekends , this . opts . disableDates , this . opts . disableDateRanges , this . opts . disableWeekdays , this . opts . enableDates ) , markedDate : this . utilService . isMarkedDate ( date , this . opts . markDates , this . opts . markWeekends ) , highlight : this . utilService . isHighlightedDate ( date , this . opts . sunHighlight , this . opts . satHighlight , this . opts . highlightDates ) } ) ; dayNbr ++ ; } } let weekNbr = this . opts . showWeekNumbers && this . opts . firstDayOfWeek === "mo" ? this . utilService . getWeekNumber ( week [ 0 ] . dateObj ) : 0 ; this . dates . push ( { week : week , weekNbr : weekNbr } ) ; } this . setHeaderBtnDisabledState ( m , y ) ; if ( notifyChange ) { this . calendarViewChanged ( { year : y , month : m , first : { number : 1 , weekday : this . getWeekday ( { year : y , month : m , day : 1 } ) } , last : { number : dInThisM , weekday : this . getWeekday ( { year : y , month : m , day : dInThisM } ) } } ) ; } } setHeaderBtnDisabledState ( m , y ) { let dpm = false ; let dpy = false ; let dnm = false ; let dny = false ; if ( this . opts . disableHeaderButtons ) { dpm = this . utilService . isMonthDisabledByDisableUntil ( { year : m === 1 ? y - 1 : y , month : m === 1 ? 12 : m - 1 , day : this . daysInMonth ( m === 1 ? 12 : m - 1 , m === 1 ? y - 1 : y ) } , this . opts . disableUntil ) ; dpy = this . utilService . isMonthDisabledByDisableUntil ( { year : y - 1 , month : m , day : this . daysInMonth ( m , y - 1 ) } , this . opts . disableUntil ) ; dnm = this . utilService . isMonthDisabledByDisableSince ( { year : m === 12 ? y + 1 : y , month : m === 12 ? 1 : m + 1 , day : 1 } , this . opts . disableSince ) ; dny = this . utilService . isMonthDisabledByDisableSince ( { year : y + 1 , month : m , day : 1 } , this . opts . disableSince ) ; } this . prevMonthDisabled = m === 1 && y === this . opts . minYear || dpm ; this . prevYearDisabled = y - 1 < this . opts . minYear || dpy ; this . nextMonthDisabled = m === 12 && y === this . opts . maxYear || dnm ; this . nextYearDisabled = y + 1 > this . opts . maxYear || dny ; } }