UNPKG

ng-daterangepicker-ext

Version:
234 lines (201 loc) 7.58 kB
import { DateObj, NgDateRangePickerHelper } from '../shared/ng-daterangerpicker.helper'; import { Component, ElementRef, EventEmitter, Injectable, Input, OnInit, Output, ViewChild } from '@angular/core'; import { NgDateRangePickerOptions } from '../shared/ng-daterangepicker-options'; import * as dateFns from 'date-fns'; export interface IDay { date: Date; day: number; weekday: number; today: boolean; firstMonthDay: boolean; lastMonthDay: boolean; visible: boolean; from: boolean; to: boolean; isWithinRange: boolean; } @Component({ selector: 'ng-daterange-calendar', templateUrl: 'ng-daterange-calendar.component.html', styleUrls: ['ng-daterange-calendar.component.sass'] }) @Injectable() export class NgDateRangeCalendarComponent implements OnInit { @Input() options: NgDateRangePickerOptions; @Input() target: Element | false; @Output() onDateFrom: EventEmitter<Date> = new EventEmitter<Date>(); @Output() onDateTo: EventEmitter<Date> = new EventEmitter<Date>(); @Output() onValueChange: EventEmitter<string> = new EventEmitter<string>(); @Input() opened: false | 'from' | 'to'; @Input() currentRange: 'tm' | 'lm' | 'lw' | 'tw' | 'ty' | 'ly'; @ViewChild('hours') private hoursEl: ElementRef; @ViewChild('minutes') private minutesEl: ElementRef; @ViewChild('seconds') private secondsEl: ElementRef; _dateFrom: Date; get dateFrom(): Date { return this._dateFrom; }; set dateFrom(value: Date) { this._dateFrom = value; this.onDateFrom.emit(this.dateFrom); } _dateTo: Date; get dateTo(): Date { return this._dateTo; }; set dateTo(value: Date) { this._dateTo = value; this.onDateTo.emit(this.dateTo); } private coords: ClientRect; date: Date; dayNames: string[]; days: IDay[]; timerOpened: boolean; constructor(public element: ElementRef, private helper: NgDateRangePickerHelper) { } ngOnInit(): void { this.date = dateFns.startOfDay(new Date()); this.currentRange = this.options.range; this.initNames(); this.showAt(); this.generateCalendar(); } initNames(): void { this.dayNames = this.options.dayNames; } showAt(): void { let top: number = 0; if (this.target) { this.coords = this.target.getBoundingClientRect(); top = this.coords.top + 30; } else { this.coords = { top: 0, left: 0, bottom: 0, right: 0 } as ClientRect; top = this.coords.top + 75; } let el: HTMLElement = this.element.nativeElement; el.style.top = Math.floor(top) + 'px'; el.style.left = Math.floor(this.coords.left) + 'px'; el.style.position = 'absolute'; } generateCalendar(): void { this.days = []; let start: Date = dateFns.startOfMonth(this.date); let end: Date = dateFns.endOfMonth(this.date); let days: IDay[] = dateFns.eachDay(start, end).map(d => { return { date: d, day: dateFns.getDate(d), weekday: dateFns.getDay(d), today: dateFns.isToday(d), firstMonthDay: dateFns.isFirstDayOfMonth(d), lastMonthDay: dateFns.isLastDayOfMonth(d), visible: true, from: dateFns.isSameDay(this.dateFrom, d), to: dateFns.isSameDay(this.dateTo, d), isWithinRange: dateFns.isWithinRange(d, this.dateFrom, this.dateTo) }; }); let prevMonthDayNum = dateFns.getDay(start) - 1; let prevMonthDays: IDay[] = []; if (prevMonthDayNum > 0) { prevMonthDays = Array.from(Array(prevMonthDayNum).keys()).map(i => { let d = dateFns.subDays(start, prevMonthDayNum - i); return { date: d, day: dateFns.getDate(d), weekday: dateFns.getDay(d), firstMonthDay: dateFns.isFirstDayOfMonth(d), lastMonthDay: dateFns.isLastDayOfMonth(d), today: false, visible: false, from: false, to: false, isWithinRange: false }; }); } this.days = prevMonthDays.concat(days); } selectDate(e: MouseEvent, index: number): void { e.preventDefault(); let selectedDate: Date = this.days[index].date; if ((this.opened === 'from' && dateFns.isAfter(selectedDate, this.dateTo)) || (this.opened === 'to' && dateFns.isBefore(selectedDate, this.dateFrom))) { return; } if (this.opened === 'from') { this.dateFrom = selectedDate; this.opened = 'to'; } else if (this.opened === 'to') { this.dateTo = selectedDate; this.opened = 'from'; } this.generateCalendar(); this.emitValue(); } prevMonth(): void { this.date = dateFns.subMonths(this.date, 1); this.generateCalendar(); } nextMonth(): void { this.date = dateFns.addMonths(this.date, 1); this.generateCalendar(); } selectRange(range: 'tm' | 'lm' | 'lw' | 'tw' | 'ty' | 'ly'): void { this.currentRange = range; let dates: DateObj = this.helper.selectRange(this.options, range); this.dateFrom = dates.dateFrom; this.dateTo = dates.dateTo; this.generateCalendar(); this.emitValue(); } closeCalendar(e: MouseEvent): void { this.opened = false; } formatTime(value: Date) { return dateFns.format(value, this.options.timeFormat); } toggleTimer(): void { this.timerOpened = !this.timerOpened; } getTimeUnitValue(unitFormat: 'H' | 'm' | 's'): string { return dateFns.format(this.opened === 'from' ? this.dateFrom : this.dateTo, unitFormat); } formatTimeForTitle(hours: number, minutes: number, seconds: number): string { let f = (v: number): string | number => { return v < 10 ? '0' + v : v; }; return `${f(hours)}:${f(minutes)}${this.showSeconds() ? ':' + f(seconds) : ''}`; } onTimeChange(): void { if (this.opened === 'from') { this.setTime(this.dateFrom); } else { this.setTime(this.dateTo); } } setTime(dateObj: Date): void { dateObj.setHours(this.hoursEl.nativeElement.value); dateObj.setMinutes(this.minutesEl.nativeElement.value); dateObj.setSeconds(this.showSeconds() ? this.secondsEl.nativeElement.value : 0); } showSeconds(): boolean { return this.options.timeFormat.includes(':s'); // also covers :ss } emitValue(): void { this.onValueChange .emit(`${dateFns.format(this.dateFrom, this.options.outputFormat)}-${dateFns.format(this.dateTo, this.options.outputFormat)}`); } getCSSClasses(): string { return `calendar theme-${this.options.theme}${!!this.opened ? ' is-opened' : ''}${this.opened === 'to' ? ' is-to' : ''}`; } }