@eduardsson/ngx-daterange
Version:
Date-Range Selector for Angular
536 lines (527 loc) • 36.8 kB
JavaScript
import { EventEmitter, Component, ChangeDetectionStrategy, ViewEncapsulation, Input, Output, HostListener, Pipe, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormGroup, FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import * as momentNs from 'moment';
import { extendMoment } from 'moment-range';
const moment$1 = momentNs;
const { range } = extendMoment(moment$1);
class CalendarComponent {
constructor() {
this.singleCalendar = false;
this.dateChanged = new EventEmitter();
this.monthChanged = new EventEmitter();
this.yearChanged = new EventEmitter();
}
get monthText() {
return moment$1.monthsShort()[this.month];
}
ngOnChanges() {
this.createCalendarGridData();
}
getWeekDays() {
return moment$1.weekdaysShort(true);
}
getWeekNumbers(monthRange) {
const weekNumbers = [];
const weeks = Array.from(monthRange.by("weeks"));
for (let i = 0; i < weeks.length; i++) {
const week = weeks[i];
if (i < 6) {
weekNumbers.push(week.week());
}
else {
break;
}
}
return weekNumbers;
}
getWeeksRange(weeks) {
const weeksRange = [];
for (let i = 0; i < weeks.length; i++) {
const week = weeks[i];
let firstWeekDay = moment$1([this.year, this.month])
.week(week)
.startOf("week");
let lastWeekDay = moment$1([this.year, this.month])
.week(week)
.endOf("week");
if (i > 0 && week < weeks[i - 1]) {
firstWeekDay.add(1, "year");
lastWeekDay.add(1, "year");
}
weeksRange.push(range(firstWeekDay.week(week).startOf("week"), lastWeekDay.week(week).endOf("week")));
}
return weeksRange;
}
createCalendarGridData() {
const firstDay = moment$1([this.year, this.month]).startOf("month");
const endDay = moment$1([this.year, this.month])
.endOf("month")
.endOf("week");
const monthRange = range(firstDay, endDay);
const weeksRange = this.getWeeksRange(this.getWeekNumbers(monthRange));
const weekList = [];
weeksRange.map(week => {
const daysList = [];
Array.from(week.by("days")).forEach((day) => {
if (day.isSame(this.minDate, "date")) {
day = this.minDate;
}
else if (day.isSame(this.maxDate, "date")) {
day = this.maxDate;
}
daysList.push(day);
});
weekList.push(daysList);
});
this.weekList = weekList;
}
isDisabled(day) {
return (day.isBefore(this.minDate) ||
day.isAfter(this.maxDate) ||
(day.isBefore(this.selectedFromDate) && !this.isLeft));
}
isDateAvailable(day) {
if (this.isLeft) {
return (day.isSameOrBefore(this.selectedToDate, "date") &&
!day.isSameOrBefore(this.minDate, "date"));
}
return (day.isSameOrAfter(this.selectedFromDate, "date") &&
!day.isSameOrAfter(this.maxDate, "date"));
}
isSelectedDate(day) {
const date = this.isLeft ? this.selectedFromDate : this.selectedToDate;
return date && day.get("month") === this.month && day.isSame(date, "date");
}
isDateInRange(day) {
if (this.isLeft) {
if (!this.selectedToDate) {
return (this.selectedFromDate &&
day.get("month") === this.month &&
day.isSameOrAfter(this.selectedFromDate, "date"));
}
}
if (this.selectedFromDate) {
return (this.selectedToDate &&
day.get("month") === this.month &&
day.isSameOrBefore(this.selectedToDate, "date") &&
day.isSameOrAfter(this.selectedFromDate, "date"));
}
}
isDifferentMonth(day) {
return day.get("month") !== this.month;
}
dateSelected(event, data) {
this.dateChanged.emit({
day: data.day,
isLeft: this.isLeft
});
event.stopPropagation();
}
monthSelected(event, data) {
this.monthChanged.emit({
value: data.value,
isLeft: this.isLeft
});
event.stopPropagation();
}
yearSelected(event, data) {
this.yearChanged.emit({
value: data.value,
isLeft: this.isLeft
});
event.stopPropagation();
}
}
CalendarComponent.decorators = [
{ type: Component, args: [{
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
selector: "calendar",
template: "<div class=\"row\">\n <div class=\"col text-center\">\n <div\n class=\"d-flex align-items-center calendar-pagination {{\n icons === 'material' ? 'material' : ''\n }}\"\n >\n <div>\n <button\n type=\"button\"\n class=\"btn btn-link btn-previous-year\"\n (click)=\"yearSelected($event, { value: -1 })\"\n >\n <i *ngIf=\"icons === 'material'\" class=\"material-icons\">first_page</i>\n <i\n *ngIf=\"icons === 'font-awesome'\"\n class=\"fas fa-angle-double-left\"\n ></i>\n <img\n *ngIf=\"icons === 'default'\"\n src=\"data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA0MDcuNDM2IDQwNy40MzYiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQwNy40MzYgNDA3LjQzNjsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSIxNnB4IiBoZWlnaHQ9IjE2cHgiPgo8Zz4KCTxwb2x5Z29uIHBvaW50cz0iMjY2LjQ1MiwyMS4xNzggMjQ1LjIwNCwwIDQyLjE0OSwyMDMuNzE4IDI0NS4yMDQsNDA3LjQzNiAyNjYuNDUyLDM4Ni4yNTggODQuNTA3LDIwMy43MTggICIgZmlsbD0iIzAwMDAwMCIvPgoJPHBvbHlnb24gcG9pbnRzPSIzNjUuMjg2LDIxLjE3OCAzNDQuMDM4LDAgMTQwLjk4MywyMDMuNzE4IDM0NC4wMzgsNDA3LjQzNiAzNjUuMjg2LDM4Ni4yNTggMTgzLjM0MSwyMDMuNzE4ICAiIGZpbGw9IiMwMDAwMDAiLz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K\"\n />\n </button>\n </div>\n <div>\n <button\n type=\"button\"\n class=\"btn btn-link btn-previous-month\"\n (click)=\"monthSelected($event, { value: -1 })\"\n >\n <i *ngIf=\"icons === 'material'\" class=\"material-icons\"\n >chevron_left</i\n >\n <i *ngIf=\"icons === 'font-awesome'\" class=\"fas fa-angle-left\"></i>\n <img\n *ngIf=\"icons === 'default'\"\n src=\"data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTYuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjE2cHgiIGhlaWdodD0iMTZweCIgdmlld0JveD0iMCAwIDQ0NC41MzEgNDQ0LjUzMSIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNDQ0LjUzMSA0NDQuNTMxOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgoJPHBhdGggZD0iTTIxMy4xMywyMjIuNDA5TDM1MS44OCw4My42NTNjNy4wNS03LjA0MywxMC41NjctMTUuNjU3LDEwLjU2Ny0yNS44NDFjMC0xMC4xODMtMy41MTgtMTguNzkzLTEwLjU2Ny0yNS44MzUgICBsLTIxLjQwOS0yMS40MTZDMzIzLjQzMiwzLjUyMSwzMTQuODE3LDAsMzA0LjYzNywwcy0xOC43OTEsMy41MjEtMjUuODQxLDEwLjU2MUw5Mi42NDksMTk2LjQyNSAgIGMtNy4wNDQsNy4wNDMtMTAuNTY2LDE1LjY1Ni0xMC41NjYsMjUuODQxczMuNTIxLDE4Ljc5MSwxMC41NjYsMjUuODM3bDE4Ni4xNDYsMTg1Ljg2NGM3LjA1LDcuMDQzLDE1LjY2LDEwLjU2NCwyNS44NDEsMTAuNTY0ICAgczE4Ljc5NS0zLjUyMSwyNS44MzQtMTAuNTY0bDIxLjQwOS0yMS40MTJjNy4wNS03LjAzOSwxMC41NjctMTUuNjA0LDEwLjU2Ny0yNS42OTdjMC0xMC4wODUtMy41MTgtMTguNzQ2LTEwLjU2Ny0yNS45NzggICBMMjEzLjEzLDIyMi40MDl6IiBmaWxsPSIjMDAwMDAwIi8+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==\"\n />\n </button>\n </div>\n <div class=\"flex-grow-1 month-year\">{{ monthText }} {{ year }}</div>\n <div>\n <button\n type=\"button\"\n class=\"btn btn-link btn-next-month\"\n (click)=\"monthSelected($event, { value: 1 })\"\n >\n <i *ngIf=\"icons === 'material'\" class=\"material-icons\"\n >chevron_right</i\n >\n <i *ngIf=\"icons === 'font-awesome'\" class=\"fas fa-angle-right\"></i>\n <img\n *ngIf=\"icons === 'default'\"\n src=\"data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTYuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjE2cHgiIGhlaWdodD0iMTZweCIgdmlld0JveD0iMCAwIDQ0NC44MTkgNDQ0LjgxOSIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNDQ0LjgxOSA0NDQuODE5OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgoJPHBhdGggZD0iTTM1Mi4wMjUsMTk2LjcxMkwxNjUuODg0LDEwLjg0OEMxNTkuMDI5LDMuNjE1LDE1MC40NjksMCwxNDAuMTg3LDBjLTEwLjI4MiwwLTE4Ljg0MiwzLjYxOS0yNS42OTcsMTAuODQ4TDkyLjc5MiwzMi4yNjQgICBjLTcuMDQ0LDcuMDQzLTEwLjU2NiwxNS42MDQtMTAuNTY2LDI1LjY5MmMwLDkuODk3LDMuNTIxLDE4LjU2LDEwLjU2NiwyNS45ODFsMTM4Ljc1MywxMzguNDczTDkyLjc4NiwzNjEuMTY4ICAgYy03LjA0Miw3LjA0My0xMC41NjQsMTUuNjA0LTEwLjU2NCwyNS42OTNjMCw5Ljg5NiwzLjUyMSwxOC41NjIsMTAuNTY0LDI1Ljk4bDIxLjcsMjEuNDEzICAgYzcuMDQzLDcuMDQzLDE1LjYxMiwxMC41NjQsMjUuNjk3LDEwLjU2NGMxMC4wODksMCwxOC42NTYtMy41MjEsMjUuNjk3LTEwLjU2NGwxODYuMTQ1LTE4NS44NjQgICBjNy4wNDYtNy40MjMsMTAuNTcxLTE2LjA4NCwxMC41NzEtMjUuOTgxQzM2Mi41OTcsMjEyLjMyMSwzNTkuMDcxLDIwMy43NTUsMzUyLjAyNSwxOTYuNzEyeiIgZmlsbD0iIzAwMDAwMCIvPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=\"\n />\n </button>\n </div>\n <div>\n <button\n type=\"button\"\n class=\"btn btn-link btn-next-year\"\n (click)=\"yearSelected($event, { value: 1 })\"\n >\n <i *ngIf=\"icons === 'material'\" class=\"material-icons\">last_page</i>\n <i\n *ngIf=\"icons === 'font-awesome'\"\n class=\"fas fa-angle-double-right\"\n ></i>\n <img\n *ngIf=\"icons === 'default'\"\n src=\"data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA0MDcuNDM2IDQwNy40MzYiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQwNy40MzYgNDA3LjQzNjsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSIxNnB4IiBoZWlnaHQ9IjE2cHgiPgo8Zz4KCTxwb2x5Z29uIHBvaW50cz0iMTYyLjIzMSwwIDE0MC45ODMsMjEuMTc4IDMyMi45MjksMjAzLjcxOCAxNDAuOTgzLDM4Ni4yNTggMTYyLjIzMSw0MDcuNDM2IDM2NS4yODYsMjAzLjcxOCAgIiBmaWxsPSIjMDAwMDAwIi8+Cgk8cG9seWdvbiBwb2ludHM9IjYzLjM5NywwIDQyLjE0OSwyMS4xNzggMjI0LjA5NSwyMDMuNzE4IDQyLjE0OSwzODYuMjU4IDYzLjM5Nyw0MDcuNDM2IDI2Ni40NTIsMjAzLjcxOCAgIiBmaWxsPSIjMDAwMDAwIi8+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==\"\n />\n </button>\n </div>\n </div>\n </div>\n</div>\n\n<div class=\"row\">\n <div class=\"col\">\n <table class=\"table table-borderless mb-0\">\n <thead>\n <tr class=\"week-days\">\n <th *ngFor=\"let day of getWeekDays()\">{{ day }}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let week of weekList; let i = index\">\n <td\n *ngFor=\"let day of weekList[i]\"\n (click)=\"dateSelected($event, { day: day })\"\n [ngClass]=\"{\n selected: isSelectedDate(day),\n 'in-selected-range': isDateInRange(day),\n disabled: isDisabled(day),\n 'different-month': isDifferentMonth(day)\n }\"\n >\n {{ day.format(\"D\") }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n</div>\n",
styles: [".calendar.calendar-single .table td.in-selected-range{background-color:transparent}.calendar .calendar-pagination{font-size:16px;margin:.5rem -15px 0}.calendar .calendar-pagination.material .btn.btn-previous-year{padding-right:3px}.calendar .calendar-pagination.material .btn.btn-previous-month{padding-left:0}.calendar .calendar-pagination.material .btn.btn-next-month{padding-right:0}.calendar .calendar-pagination.material .btn.btn-next-year{padding-left:3px}.calendar .calendar-pagination .month-year{font-weight:600}.calendar .week-days{text-transform:uppercase}.calendar .table th,.calendar .table td{border-radius:0;color:#333;font-size:12px;height:36px;line-height:1;padding:0;text-align:center;vertical-align:middle}.calendar .table th{font-weight:400}.calendar .table td{border-radius:5px;cursor:pointer;font-weight:900;width:32px;max-width:32px}.calendar .table td:not(.disabled):not(.selected):hover{background-color:#eee;color:#333}.calendar .table td.selected{background-color:#003d79;border-radius:5px;color:#fff;font-size:12px}.calendar .table td.selected.in-selected-range{background-color:#003d79;border-radius:5px}.calendar .table td.selected:hover{background-color:#003d79}.calendar .table td.in-selected-range{background-color:#dfefff;border-radius:0;font-size:12px}.calendar .table td.disabled{background-color:transparent;color:#666;cursor:not-allowed;font-weight:400}.calendar .table td.different-month{color:#888;font-weight:500}.calendar .table td.different-month.disabled{color:#ccc}\n"]
},] }
];
CalendarComponent.propDecorators = {
month: [{ type: Input }],
year: [{ type: Input }],
selectedFromDate: [{ type: Input }],
selectedToDate: [{ type: Input }],
isLeft: [{ type: Input }],
format: [{ type: Input }],
minDate: [{ type: Input }],
maxDate: [{ type: Input }],
singleCalendar: [{ type: Input }],
icons: [{ type: Input }],
dateChanged: [{ type: Output }],
monthChanged: [{ type: Output }],
yearChanged: [{ type: Output }]
};
const defaultDateFormat = 'YYYY-MM-DD';
const defaultTimeFormat = 'HH:mm';
const defaultDateRangePickerOptions = {
autoApply: true,
clickOutsideAllowed: true,
disabled: false,
icons: 'default',
format: defaultDateFormat,
labelText: 'Date Range',
maxDate: null,
minDate: null,
position: 'left',
preDefinedRanges: [],
singleCalendar: false,
};
const moment = momentNs;
let instanceCount = 0;
class DateRangePickerComponent {
constructor() {
this.options = defaultDateRangePickerOptions;
this.controlName = "dateRange";
this.instanceId = null;
this.fromDate = null;
this.toDate = null;
this.rangeSelected = new EventEmitter();
this.range = "";
this.showCalendars = false;
if (!this.instanceId) {
// assign auto-id
this.instanceId = `dateRangePicker-${instanceCount++}`;
}
}
get enableApplyButton() {
return (!this.options.autoApply &&
!this.options.singleCalendar &&
this.fromDate !== null &&
this.toDate !== null);
}
handleClick(event) {
const target = event.target;
// close the DatePicker if clicking outside is not allowed
if (!this.options.clickOutsideAllowed) {
const containerElementClassRoot = "dateRangePicker";
let targetPathClassNames = [""];
if (event["path"]) {
targetPathClassNames = event["path"].map(obj => obj.className || "");
}
else {
try {
targetPathClassNames = event
.composedPath()
.map(item => item.className || "");
}
catch (error) {
console.warn("Edge sux");
}
}
const targetExistsInPath = targetPathClassNames.some(className => {
if (typeof className === "string") {
return className && className.includes(containerElementClassRoot);
}
return false;
});
if (!targetExistsInPath) {
this.toggleCalendarVisibility(false);
}
}
// Close the DatePicker if the target input was clicked
if (target.id === this.instanceId) {
this.toggleCalendarVisibility(!this.showCalendars);
}
}
ngOnInit() {
// ensure dates in options are valid
this.validateOptionDates();
// ensure input dates are within the min/max dates in options
this.validateInputDates();
if (this.options.preDefinedRanges &&
this.options.preDefinedRanges.length > 0) {
this.defaultRanges = this.validateAndAssignPredefinedRanges(this.options.preDefinedRanges);
}
// assign values not present in options with default values
const optionsKeys = Object.keys(this.options);
const defaultValuesKeys = Object.keys(defaultDateRangePickerOptions);
defaultValuesKeys.forEach((key) => {
if (!optionsKeys.includes(key)) {
this.options[key] = defaultDateRangePickerOptions[key];
}
});
// update calendar grid
this.updateCalendar();
// create parent form group if it doesn't exist
if (!this.parentFormGroup) {
this.parentFormGroup = new FormGroup({});
}
// add form control to parent form group
const value = this.formatRangeAsString();
const control = new FormControl(value, this.options.validators);
if (this.options.disabled) {
control.disable();
}
this.parentFormGroup.addControl(this.controlName, control);
// set value of control
this.setRange();
}
validateInputDates() {
if (this.fromDate &&
this.options.minDate &&
this.fromDate.isBefore(this.options.minDate, "date")) {
throw new RangeError("@Input fromDate is before the specified minDate in options");
}
if (this.toDate &&
this.options.maxDate &&
this.toDate.isAfter(this.options.maxDate, "date")) {
throw new RangeError("@Input toDate is after the specified maxDate in options");
}
}
validateOptionDates() {
// validate maxDate isn't before minDate or vice versa
if (this.options) {
if (this.options.minDate && this.options.maxDate) {
if (this.options.minDate.isAfter(this.options.maxDate, "date")) {
throw new RangeError("minDate specified in options is after the maxDate");
}
else if (this.options.maxDate.isBefore(this.options.minDate, "date")) {
throw new RangeError("maxDate specified in options is before the minDate");
}
}
}
}
// assists CSS to fix small positioning bug with From:/To: date text
checkChrome() {
return window["chrome"] ? "is-chrome" : "";
}
toggleCalendarVisibility(value) {
this.showCalendars = value !== null ? value : !this.showCalendars;
}
setFromToMonthYear(fromDate, toDate) {
const tempFromDate = fromDate || this.fromDate || this.options.startingFromDate || moment();
const tempToDate = toDate || this.toDate || this.options.startingToDate || moment();
this.fromMonth = tempFromDate.get("month");
this.fromYear = tempFromDate.get("year");
this.toMonth = tempToDate.get("month");
this.toYear = tempToDate.get("year");
}
updateCalendar() {
// get month and year to show calendar
this.setFromToMonthYear();
this.setRange();
}
// update from/to based on selection
dateChanged(changedData) {
const value = changedData.day;
const isLeft = changedData.isLeft;
if (isLeft) {
this.fromDate = value;
if (this.fromDate.isAfter(this.toDate, "date")) {
this.toDate = this.fromDate.clone();
}
}
else {
this.toDate = value;
if (this.toDate.isBefore(this.fromDate, "date")) {
this.fromDate = this.toDate.clone();
}
}
this.setFromToMonthYear(this.fromDate, this.toDate);
if (this.isAutoApply() &&
(this.options.singleCalendar || !isLeft) &&
this.fromDate) {
this.toggleCalendarVisibility(false);
this.setRange();
this.emitRangeSelected();
}
}
emitRangeSelected(data) {
if (!data) {
data = this.options.singleCalendar
? { start: this.fromDate }
: { start: this.fromDate, end: this.toDate };
}
this.rangeSelected.emit(data);
}
getMoment(value) {
return moment(value, this.options.format);
}
formatRangeAsString() {
let range = "";
if (this.options.singleCalendar && this.fromDate) {
range = this.fromDate.format(this.options.format);
}
else if (!this.options.singleCalendar && this.fromDate && this.toDate) {
range = `${this.fromDate.format(this.options.format)} - ${this.toDate.format(this.options.format)}`;
}
return range;
}
setRange() {
this.range = this.formatRangeAsString();
if (this.parentFormGroup) {
const control = this.parentFormGroup.get(this.controlName);
if (control) {
control.setValue(this.range);
control.updateValueAndValidity();
}
}
}
setDateFromInput(event, isLeft = false) {
const target = event.target;
try {
if (target.value) {
const day = this.getMoment(target.value);
if (!day.isBefore(this.options.minDate) &&
!day.isAfter(this.options.maxDate)) {
if (isLeft && !this.fromDate) {
this.fromDate = day;
}
if (!isLeft && !this.toDate) {
this.toDate = day;
}
this.dateChanged({
day,
isLeft
});
this.setFromToMonthYear(this.fromDate, this.toDate);
}
else {
// assume nothing - reset values
this.fromDate = null;
this.toDate = null;
target.value = "";
target.focus();
}
}
this.emitRangeSelected();
}
catch (e) {
console.error(e);
}
}
monthChanged(data) {
let temp;
if (data.isLeft) {
temp = moment([this.fromYear, this.fromMonth]).add(data.value, "months");
this.fromMonth = temp.get("month");
this.fromYear = temp.get("year");
}
else {
temp = moment([this.toYear, this.toMonth]).add(data.value, "months");
this.toMonth = temp.get("month");
this.toYear = temp.get("year");
}
}
yearChanged(data) {
let temp;
if (data.isLeft) {
temp = moment([this.fromYear, this.fromMonth]).add(data.value, "year");
this.fromMonth = temp.get("month");
this.fromYear = temp.get("year");
}
else {
temp = moment([this.toYear, this.toMonth]).add(data.value, "year");
this.toMonth = temp.get("month");
this.toYear = temp.get("year");
}
}
close(event) {
this.toggleCalendarVisibility(false);
event.stopPropagation();
}
reset(event) {
this.fromDate = null;
this.toDate = null;
this.setRange();
event.stopPropagation();
}
apply(event) {
this.toggleCalendarVisibility(false);
this.setRange();
this.emitRangeSelected();
event.stopPropagation();
}
applyPredefinedRange(event, definedDateRange) {
// adjust to/from month/year so calendar months and years match range
this.setFromToMonthYear(definedDateRange.value.start, definedDateRange.value.end);
this.fromDate = definedDateRange.value.start;
this.toDate = definedDateRange.value.end;
if (this.options.autoApply) {
this.apply(event);
}
}
validateAndAssignPredefinedRanges(ranges) {
return ranges.filter(range => {
if (range.value.start.isAfter(range.value.end, "date")) {
throw new RangeError(`Pre-defined range "${range.name}" start date cannot be after the end date for the range.`);
}
if (this.options.minDate &&
range.value.start.isBefore(this.options.minDate)) {
throw new RangeError(`Pre-defined range "${range.name}" start date is before the specified minDate in your options.`);
}
if (this.options.maxDate &&
range.value.end.isAfter(this.options.maxDate)) {
throw new RangeError(`Pre-defined range "${range.name}" end date is after the specified maxDate in your options.`);
}
// add range to ranges
return true;
});
}
isAutoApply() {
if (this.options.singleCalendar) {
return true;
}
else {
return this.options.autoApply;
}
}
}
DateRangePickerComponent.decorators = [
{ type: Component, args: [{
encapsulation: ViewEncapsulation.None,
selector: "date-range-picker",
template: "<div class=\"dateRangePicker-wrapper\" *ngIf=\"parentFormGroup\">\n <div #content>\n <ng-content></ng-content>\n </div>\n <div class=\"form-group\" [formGroup]=\"parentFormGroup\">\n <ng-container *ngIf=\"content.childNodes.length === 0\">\n <label [attr.for]=\"instanceId\">{{ options.labelText }}</label>\n <input [attr.id]=\"instanceId\" class=\"form-control\" type=\"text\" [formControlName]=\"controlName\">\n </ng-container>\n\n <div [ngClass]=\"{'dateRangePicker': true, 'invisible': !showCalendars, 'dateRangePicker-single': options.singleCalendar, 'is-chrome': checkChrome(), 'open-left': options.position === 'left', 'open-right': options.position === 'right' }\">\n <div class=\"container-fluid\">\n <div class=\"row\">\n <div class=\"col\">\n\n <div class=\"row form-inputs mb-0\" *ngIf=\"!options.singleCalendar\">\n\n <div class=\"col\">\n <div class=\"\" *ngIf=\"options.icons !== 'default'\">\n <div [ngClass]=\"{ 'd-flex align-items-center input-with-icon': true, 'material': options.icons === 'material'}\">\n <i *ngIf=\"options.icons === 'material'\" class=\"material-icons\">event</i>\n <i *ngIf=\"options.icons === 'font-awesome'\" class=\"far fa-calendar-alt\"></i>\n <span *ngIf=\"fromDate\">From: </span>\n <input class=\"form-control\" [ngModel]=\"fromDate | formatMomentDate: options.format\" (blur)=\"setDateFromInput($event, true)\" type=\"text\" name=\"daterangepicker_start\" [ngModelOptions]=\"{ standalone: true }\" placeholder=\"Select From Date\" />\n </div>\n </div>\n\n <input *ngIf=\"options.icons === 'default'\" class=\"form-control\" [ngModel]=\"fromDate | formatMomentDate: options.format\" (blur)=\"setDateFromInput($event, true)\" type=\"text\" name=\"daterangepicker_start\" [ngModelOptions]=\"{standalone: true}\" placeholder=\"Select From Date\" />\n </div>\n\n <div class=\"col\">\n <div class=\"\" *ngIf=\"options.icons !== 'default'\">\n <div [ngClass]=\"{ 'd-flex align-items-center input-with-icon': true, 'material': options.icons === 'material'}\">\n <i *ngIf=\"options.icons === 'material'\" class=\"material-icons\">event</i>\n <i *ngIf=\"options.icons === 'font-awesome'\" class=\"far fa-calendar-alt\"></i>\n <span *ngIf=\"toDate\">To: </span>\n <input class=\"form-control\" [ngModel]=\"toDate | formatMomentDate: options.format\" (blur)=\"setDateFromInput($event)\" type=\"text\" name=\"daterangepicker_end\" [ngModelOptions]=\"{ standalone: true }\" placeholder=\"Select To Date\" />\n </div>\n </div>\n\n <input *ngIf=\"options.icons === 'default'\" class=\"form-control\" [ngModel]=\"toDate | formatMomentDate: options.format\" (blur)=\"setDateFromInput($event)\" name=\"daterangepicker_end\" [ngModelOptions]=\"{standalone: true}\" />\n </div>\n\n </div>\n\n <div class=\"row row-calendars\">\n <div class=\"col\">\n <calendar [ngClass]=\"{ 'calendar': true, 'calendar-single': options.singleCalendar }\" [isLeft]=\"true\" [month]=\"fromMonth\" [year]=\"fromYear\" (monthChanged)=monthChanged($event)\n (yearChanged)=\"yearChanged($event)\" (dateChanged)=\"dateChanged($event)\" [format]=\"options.format\" [selectedFromDate]=\"fromDate\"\n [selectedToDate]=\"toDate\" [minDate]=\"options.minDate\" [maxDate]=\"options.maxDate\" [singleCalendar]=\"options.singleCalendar\" [icons]=\"options.icons\"></calendar>\n </div>\n\n <div class=\"col\" *ngIf=\"!options.singleCalendar\">\n <calendar class=\"calendar\" [month]=\"toMonth\" [year]=\"toYear\" [format]=\"options.format\" (dateChanged)=\"dateChanged($event)\" (monthChanged)=\"monthChanged($event)\" (yearChanged)=yearChanged($event) [selectedFromDate]=\"fromDate\" [selectedToDate]=\"toDate\" [minDate]=\"options.minDate\" [maxDate]=\"options.maxDate\" [icons]=\"options.icons\"></calendar>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"row text-center my-3\" *ngIf=\"!options.singleCalendar\">\n <div class=\"col\">\n <div class=\"row mb-2\" *ngIf=\"options.preDefinedRanges && options.preDefinedRanges.length > 0\">\n <div class=\"col\">\n <button type=\"button\" *ngFor=\"let range of options.preDefinedRanges\" class=\"btn btn-link\" (click)=\"applyPredefinedRange($event, range)\">{{ range.name }}</button>\n </div>\n </div>\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\" (click)=\"close($event)\">Close</button>\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm ml-3\" (click)=\"reset($event)\" *ngIf=\"this.range\">Reset</button>\n <button type=\"button\" class=\"btn btn-primary btn-sm ml-3\" [disabled]=\"!enableApplyButton\" (click)=\"apply($event)\" *ngIf=\"!isAutoApply()\">Apply</button>\n </div>\n </div>\n\n </div>\n </div>\n\n </div>\n</div>\n",
styles: [".dateRangePicker-wrapper{position:relative;border:none}.dateRangePicker-wrapper .dateRangePicker{background:#fff;border-radius:9px;box-shadow:0 0 10px #818b9080;font-family:\"Roboto\",Helvetica,Arial,sans-serif;overflow:visible;position:absolute;top:88px;z-index:3000}.dateRangePicker-wrapper .dateRangePicker.dateRangePicker-single{min-width:300px}@media screen and (min-width: 576px){.dateRangePicker-wrapper .dateRangePicker{min-width:600px}}.dateRangePicker-wrapper .dateRangePicker:before,.dateRangePicker-wrapper .dateRangePicker:after{content:\"\";height:0px;width:0px;border-bottom:10px solid #6E777C;border-left:10px solid transparent;border-right:10px solid transparent;position:absolute;top:-10px}.dateRangePicker-wrapper .dateRangePicker.open-left{left:0}.dateRangePicker-wrapper .dateRangePicker.open-left:before{left:10px}.dateRangePicker-wrapper .dateRangePicker.open-left:after{display:none}.dateRangePicker-wrapper .dateRangePicker.open-right{right:0}.dateRangePicker-wrapper .dateRangePicker.open-right:before{display:none}.dateRangePicker-wrapper .dateRangePicker.open-right:after{right:10px}.dateRangePicker-wrapper .dateRangePicker.is-chrome .form-inputs .input-with-icon span{position:relative;top:1px}.dateRangePicker-wrapper .dateRangePicker .btn,.dateRangePicker-wrapper .dateRangePicker .btn.btn-link{min-width:auto;width:auto}.dateRangePicker-wrapper .dateRangePicker .form-inputs{background-color:#6e777c;border-top-left-radius:8px;border-top-right-radius:8px;color:#fff;font-size:16px}.dateRangePicker-wrapper .dateRangePicker .form-inputs>.col{padding:.5rem 15px;min-width:270px}.dateRangePicker-wrapper .dateRangePicker .form-inputs .input-with-icon{display:flex;align-content:flex-start;align-items:center!important}.dateRangePicker-wrapper .dateRangePicker .form-inputs .input-with-icon i{color:#fff;margin-right:.5rem}.dateRangePicker-wrapper .dateRangePicker .form-inputs .input-with-icon i.material-icons{top:0}.dateRangePicker-wrapper .dateRangePicker .form-inputs .input-with-icon span{line-height:1}.dateRangePicker-wrapper .dateRangePicker .form-inputs .form-control{background:none;border:0 none;color:#fff;display:inline-block;padding:0;width:150px}.dateRangePicker-wrapper .dateRangePicker .form-inputs .form-control::-moz-placeholder{color:#fff}.dateRangePicker-wrapper .dateRangePicker .form-inputs .form-control::placeholder{color:#fff}.dateRangePicker-wrapper .dateRangePicker .form-inputs>div:nth-child(2),.dateRangePicker-wrapper .dateRangePicker .row-calendars>div:nth-child(2){border-left:solid 2px #C6D1D6}.dateRangePicker-wrapper i.material-icons{position:relative;top:2px}\n"]
},] }
];
DateRangePickerComponent.ctorParameters = () => [];
DateRangePickerComponent.propDecorators = {
options: [{ type: Input }],
controlName: [{ type: Input }],
instanceId: [{ type: Input }],
parentFormGroup: [{ type: Input }],
fromDate: [{ type: Input }],
toDate: [{ type: Input }],
rangeSelected: [{ type: Output }],
handleClick: [{ type: HostListener, args: ["document:click", ["$event"],] }]
};
class FormatMomentDatePipe {
transform(value, format) {
return value ? value.format(format) : '';
}
}
FormatMomentDatePipe.decorators = [
{ type: Pipe, args: [{
name: 'formatMomentDate'
},] }
];
const declarations = [
CalendarComponent,
DateRangePickerComponent,
FormatMomentDatePipe,
];
class NgxDateRangeModule {
}
NgxDateRangeModule.decorators = [
{ type: NgModule, args: [{
declarations: [...declarations],
exports: [...declarations],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
]
},] }
];
/**
* Generated bundle index. Do not edit.
*/
export { CalendarComponent, DateRangePickerComponent, NgxDateRangeModule, FormatMomentDatePipe as ɵa };
//# sourceMappingURL=eduardsson-ngx-daterange.js.map