UNPKG

mat-daterangepicker

Version:

Angular material date picker extension to support range selection.

993 lines (985 loc) 56.7 kB
import { __extends, __decorate, __param, __awaiter, __generator } from 'tslib'; import { EventEmitter, ReflectiveInjector, Optional, Inject, ChangeDetectorRef, Injector, Input, Output, Component, ViewEncapsulation, ChangeDetectionStrategy, ViewChild, ViewChildren, InjectionToken, NgZone, ViewContainerRef, forwardRef, ElementRef, Directive, ComponentFactoryResolver, NgModule } from '@angular/core'; import { DOCUMENT, CommonModule } from '@angular/common'; import { A11yModule } from '@angular/cdk/a11y'; import { Overlay, OverlayModule } from '@angular/cdk/overlay'; import { ComponentPortal, PortalModule } from '@angular/cdk/portal'; import { DateAdapter, MAT_DATE_FORMATS, MatNativeDateModule } from '@angular/material/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { MatCalendar, MatDatepickerIntl, matDatepickerAnimations, MatDatepickerContent, MAT_DATEPICKER_SCROLL_STRATEGY, MatDatepicker, MatDatepickerInputEvent, MatDatepickerInput, MatMonthView, MatCalendarBody, MatDatepickerToggle, MatDatepickerModule } from '@angular/material/datepicker'; import { BehaviorSubject, Subject, Subscription } from 'rxjs'; import { Directionality } from '@angular/cdk/bidi'; import { take } from 'rxjs/operators'; import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms'; import { MatFormField } from '@angular/material/form-field'; import { MAT_INPUT_VALUE_ACCESSOR } from '@angular/material/input'; /* RANGE: Most of the code is redundant and hera as inheritance boilerplate. The relevant code is marked with a comment. */ var MatDaterangeCalendar = /** @class */ (function (_super) { __extends(MatDaterangeCalendar, _super); // THIS SECTION IS JUST TO PROVIDE MatCalendar as SgCalendar to MatCalendarHeader function MatDaterangeCalendar(_intl, _dateAdapter, _dateFormats, changeDetectorRef, drInjector) { var _this = _super.call(this, _intl, _dateAdapter, _dateFormats, changeDetectorRef) || this; _this.drInjector = drInjector; _this.selectedRangeEndChange = new EventEmitter(); return _this; } MatDaterangeCalendar.prototype.ngAfterContentInit = function () { _super.prototype.ngAfterContentInit.call(this); var injector = ReflectiveInjector.resolveAndCreate([{ provide: MatCalendar, useValue: this }], this.drInjector); this._calendarHeaderPortal.injector = injector; var activeDate = this.startAt || this['_dateAdapter'].today(); this.activeDate = this.view === 0 ? activeDate : this.getSecondViewActiveDate(activeDate); // Assign to the private property since we don't want to move focus on init. // this._currentView = this.startView; }; Object.defineProperty(MatDaterangeCalendar.prototype, "selectedRangeEnd", { get: function () { return this._selectedRangeEnd; }, set: function (value) { this._selectedRangeEnd = this._getValidDateOrNull(this._dateAdapter.deserialize(value)); }, enumerable: true, configurable: true }); MatDaterangeCalendar.prototype._dateSelectedRangeEnd = function (date) { if (!this._dateAdapter.sameDate(date, this.selectedRangeEnd)) { this.selectedRangeEndChange.emit(date); } }; MatDaterangeCalendar.prototype.getSecondViewActiveDate = function (activeDate) { var date = this['_dateAdapter'].getDate(activeDate); var month = this['_dateAdapter'].getMonth(activeDate); var year = this['_dateAdapter'].getYear(activeDate); var y, m; if (month === 11) { m = 0; y = year + 1; } else { m = month + 1, y = year; } var D = this['_dateAdapter'].createDate(y, m, date); return D; }; MatDaterangeCalendar.ctorParameters = function () { return [ { type: MatDatepickerIntl }, { type: DateAdapter, decorators: [{ type: Optional }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [MAT_DATE_FORMATS,] }] }, { type: ChangeDetectorRef }, { type: Injector } ]; }; __decorate([ Input() ], MatDaterangeCalendar.prototype, "view", void 0); __decorate([ Input() ], MatDaterangeCalendar.prototype, "setHoverCells", void 0); __decorate([ Input() ], MatDaterangeCalendar.prototype, "clearBody", void 0); __decorate([ Input() ], MatDaterangeCalendar.prototype, "range", void 0); __decorate([ Input() ], MatDaterangeCalendar.prototype, "selectedRangeEnd", null); __decorate([ Output() ], MatDaterangeCalendar.prototype, "selectedRangeEndChange", void 0); MatDaterangeCalendar = __decorate([ Component({ selector: 'mat-daterange-calendar', template: "<!-- \n\tRANGE: Most of the code is redundant and hera as inheritance boilerplate.\n\tThe relevant code is marked with a comment.\n\tADDED LOC ~3\n-->\n<ng-template [cdkPortalOutlet]=\"_calendarHeaderPortal\"></ng-template>\n\n<div class=\"mat-calendar-content\" [ngSwitch]=\"currentView\" cdkMonitorSubtreeFocus tabindex=\"-1\">\n\n\t<mat-daterange-month-view\n\t\t*ngSwitchCase=\"'month'\"\n\t\t[view]=\"view\"\n\t\t[activeDate]=\"activeDate\"\n\t\t[selected]=\"selected\"\n\t\t[dateFilter]=\"dateFilter\"\n\t\t[maxDate]=\"maxDate\"\n\t\t[minDate]=\"minDate\"\n\t\t(selectedChange)=\"_dateSelected($event)\"\n\t\t(_userSelection)=\"_userSelected()\"\n\n\t\t[range]=\"range\"\n\t\t[setHoverCells]=\"setHoverCells\"\n\t\t[clearBody]=\"clearBody\"\n\t\t[selectedRangeEnd]=\"selectedRangeEnd\"\n\t\t(selectedRangeEndChange)=\"_dateSelectedRangeEnd($event)\">\n\t\t<!-- THE 3 LINES ABOVE ARE THE ONLY ADDITION -->\n\t</mat-daterange-month-view>\n\n\t<mat-year-view\n\t\t*ngSwitchCase=\"'year'\"\n\t\t[activeDate]=\"activeDate\"\n\t\t[selected]=\"selected\"\n\t\t[dateFilter]=\"dateFilter\"\n\t\t[maxDate]=\"maxDate\"\n\t\t[minDate]=\"minDate\"\n\t\t(monthSelected)=\"_monthSelectedInYearView($event)\"\n\t\t(selectedChange)=\"_goToDateInView($event, 'month')\">\n\t</mat-year-view>\n\n\t<mat-multi-year-view\n\t\t*ngSwitchCase=\"'multi-year'\"\n\t\t[activeDate]=\"activeDate\"\n\t\t[selected]=\"selected\"\n\t\t[dateFilter]=\"dateFilter\"\n\t\t[maxDate]=\"maxDate\"\n\t\t[minDate]=\"minDate\"\n\t\t(yearSelected)=\"_yearSelectedInMultiYearView($event)\"\n\t\t(selectedChange)=\"_goToDateInView($event, 'year')\">\n\t</mat-multi-year-view>\n</div>\n\n", host: { 'class': 'mat-calendar', }, exportAs: 'matDaterangeCalendar', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush }), __param(1, Optional()), __param(2, Optional()), __param(2, Inject(MAT_DATE_FORMATS)) ], MatDaterangeCalendar); return MatDaterangeCalendar; }(MatCalendar)); /* RANGE: Most of the code is redundant and hera as inheritance boilerplate. The relevant code is marked with a comment. */ var MatDaterangepickerContent = /** @class */ (function (_super) { __extends(MatDaterangepickerContent, _super); function MatDaterangepickerContent() { var _this = _super !== null && _super.apply(this, arguments) || this; _this._onApplyRange = new EventEmitter(); _this.setHoverCells = new BehaviorSubject(null); _this.clearBody = new BehaviorSubject(null); return _this; } Object.defineProperty(MatDaterangepickerContent.prototype, "drDatepicker", { get: function () { return this.datepicker; }, enumerable: true, configurable: true }); MatDaterangepickerContent.prototype.ngOnInit = function () { }; MatDaterangepickerContent.prototype.ngAfterViewInit = function () { }; // THIS IS THE ONLY ADDITION MatDaterangepickerContent.prototype.onUserSelection = function (view) { this.drDatepicker._userSelection(); }; MatDaterangepickerContent.prototype.applyRange = function () { if (this.drDatepicker.applyButton) { this.drDatepicker.applyRange(); } }; MatDaterangepickerContent.prototype.clearRange = function () { this.drDatepicker.clearRange(); this.clearBody.next(true); this.datepicker._selected = null; this.drDatepicker._selectedRangeEnd = null; }; MatDaterangepickerContent.prototype.setCustomRange = function (range) { if (!range.startDate || !range.endDate) return; this.datepicker._selected = range.startDate; this.drDatepicker._selectedRangeEnd = range.endDate; this.datepicker._selectedChanged.next(range.startDate); this.drDatepicker._selectedChangedRangeEnd.next(range.endDate); this.setActiveDate(range.startDate, range.endDate); }; MatDaterangepickerContent.prototype.isSameRange = function (range) { if (!this.datepicker._selected || !this.drDatepicker._selectedRangeEnd || !range.startDate || !range.endDate) return; return this.drDatepicker._drDateAdapter.compareDate(range.startDate, this.datepicker._selected) == 0 && this.drDatepicker._drDateAdapter.compareDate(range.endDate, this.drDatepicker._selectedRangeEnd) == 0; }; MatDaterangepickerContent.prototype.isSomeRange = function () { var _this = this; if (!this.datepicker._selected || !this.drDatepicker._selectedRangeEnd) return false; return this.drDatepicker.customRanges.some(function (range) { return _this.isSameRange(range); }); }; MatDaterangepickerContent.prototype.getWidthDateRangePicker = function () { if (this.drDatepicker.dualView) { if (this.drDatepicker.showCustomRanges && this.drDatepicker.customRanges.length) { return '655px'; } return '600px'; } if (this.drDatepicker.showCustomRanges && this.drDatepicker.customRanges.length) { return '450px'; } }; MatDaterangepickerContent.prototype.setActiveDate = function (from, to) { var fromMonth = this.drDatepicker._drDateAdapter.getMonth(from); var toMonth = this.drDatepicker._drDateAdapter.getMonth(to); var toYear = this.drDatepicker._drDateAdapter.getYear(to); var toDate = this.drDatepicker._drDateAdapter.getDate(to); var y, m; if (toMonth === 11) { m = 0; y = toYear + 1; } else { m = toMonth + 1, y = toYear; } var endDate = this.drDatepicker._drDateAdapter.createDate(y, m, 1); this.calendars.forEach(function (calendar, i) { if (i === 0) { calendar.activeDate = from; } else { if (fromMonth !== toMonth) { // if start month & end month are differents calendar.activeDate = to; } else { calendar.activeDate = endDate; } } }); }; __decorate([ ViewChild(MatDaterangeCalendar, { static: true }) ], MatDaterangepickerContent.prototype, "_calendar", void 0); __decorate([ ViewChildren(MatDaterangeCalendar) ], MatDaterangepickerContent.prototype, "calendars", void 0); MatDaterangepickerContent = __decorate([ Component({ selector: 'mat-daterangepicker-content', template: "<div>\n\t<!-- \n\t\tRANGE: Most of the code is redundant and hera as inheritance boilerplate.\n\t\tThe relevant code is marked with a comment.\n\t\tADDED LOC ~3\n\t-->\n\t<div class=\"mat-daterange-calendars\" [ngStyle]=\"{'width': getWidthDateRangePicker()}\">\n\t\t<div *ngIf=\"drDatepicker.showCustomRanges && drDatepicker.customRanges.length\" class=\"mat-daterange-custom-ranges\">\n\t\t\t<button *ngFor=\"let range of drDatepicker.customRanges\" mat-flat-button [color]=\"isSameRange(range) ? 'primary' : ''\" (click)=\"setCustomRange(range)\" class=\"mat-daterange-calendar-button-action\">{{range.name}}</button>\n\t\t\t<button mat-flat-button [color]=\"!isSomeRange() ? 'primary' : ''\" class=\"mat-daterange-custom-range mat-daterange-calendar-button-action\">Custom Range</button>\n\t\t</div>\n\t\t<mat-daterange-calendar *ngFor=\"let view of drDatepicker._getViews()\" #Calendar cdkTrapFocus\n\t\t\t\t\t[id]=\"datepicker.id\"\n\t\t\t\t\t[ngClass]=\"datepicker.panelClass\"\n\t\t\t\t\tclass=\"mat-daterange-calendar\"\n\t\t\t\t\t[view]=\"view\"\n\t\t\t\t\t[setHoverCells]=\"setHoverCells\"\n\t\t\t\t\t[clearBody]=\"clearBody\"\n\t\t\t\t\t\n\t\t\t\t\t[startAt]=\"datepicker.startAt\"\n\t\t\t\t\t[startView]=\"datepicker.startView\"\n\t\t\t\t\t[minDate]=\"datepicker._minDate\"\n\t\t\t\t\t[maxDate]=\"datepicker._maxDate\"\n\t\t\t\t\t[dateFilter]=\"datepicker._dateFilter\"\n\t\t\t\t\t[headerComponent]=\"datepicker.calendarHeaderComponent\"\n\t\t\t\t\t[selected]=\"datepicker._selected\"\n\t\t\t\t\t[@fadeInCalendar]=\"'enter'\"\n\t\t\t\t\t(selectedChange)=\"datepicker.select($event)\"\n\t\t\t\t\t(yearSelected)=\"datepicker._selectYear($event)\"\n\t\t\t\t\t(monthSelected)=\"datepicker._selectMonth($event)\"\n\n\t\t\t\t\t[range]=\"!!drDatepicker._datepickerInputRangeEnd\"\n\t\t\t\t\t[selectedRangeEnd]=\"drDatepicker._selectedRangeEnd\"\n\t\t\t\t\t(selectedRangeEndChange)=\"drDatepicker.selectRangeEnd($event)\"\n\t\t\t\t\t(_userSelection)=\"onUserSelection(view)\">\n\t\t\t\t\t<!-- \n\t\t\t\t\t\t\tTHE 3 LINES ABOVE ARE THE ONLY ADDITION\n\t\t\t\t\t\t\tThe _userSelection will pass logic handling to the datepicker component\n\t\t\t\t\t\t\tinstead of closing the dialog/popup, this is because range logic is handled there.\n\n\t\t\t\t\t\t\tThe other attributes are passing on the additional date (Range end) bindings\n\t\t\t\t\t\t\tand a flag that sets range on/off based on the input range.\n\t\t\t\t\t\t\tThis means that an *ngIf on that input by the user can toggle range on/off without\n\t\t\t\t\t\t\ta special flag.\n\t\t\t\t\t-->\n\n\t\t</mat-daterange-calendar>\n\t</div>\n\n\t<div *ngIf=\"drDatepicker.applyButton\" class=\"mat-daterange-calendar-actions\">\n\t\t<button mat-button color=\"primary\" (click)=\"clearRange()\">Clear</button>&nbsp;&nbsp;\n\t\t<button mat-flat-button color=\"primary\" (click)=\"applyRange()\">Apply</button>\n\t</div>\n</div>", host: { 'class': 'mat-datepicker-content', '[@transformPanel]': '"enter"', '[class.mat-datepicker-content-touch]': 'datepicker.touchUi', '[class.mat-datepicker-content-above]': '_isAbove', }, animations: [ matDatepickerAnimations.transformPanel, matDatepickerAnimations.fadeInCalendar, ], exportAs: 'matDaterangepickerContent', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, inputs: ['color'], styles: [".mat-daterange-calendar-body-in-range{background-color:rgba(63,81,181,.15)}.mat-daterange-calendar-body-in-range.mat-daterange-calendar-body-range-start{border-radius:100% 0 0 100%}.mat-daterange-calendar-body-in-range.mat-daterange-calendar-body-range-end{border-radius:0 100% 100% 0}.mat-daterange-calendars{display:flex}.mat-daterange-calendar{flex-grow:1}.mat-daterange-calendar-actions{padding:15px;text-align:right}.mat-daterange-custom-ranges{display:flex;flex-flow:column;width:130px;padding:8px}.mat-daterange-custom-ranges button{text-align:left;font-size:12px;line-height:32px;font-weight:400!important}.mat-daterange-calendar-button-action{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}"] }) ], MatDaterangepickerContent); return MatDaterangepickerContent; }(MatDatepickerContent)); var thisMonthRange = function () { var y = new Date().getFullYear(); var m = new Date().getMonth(); var yearFrom, monthFrom, yearTo, monthTo; yearFrom = y; monthFrom = m; yearTo = y; monthTo = m + 1; if (m === 11) { yearTo = y + 1; monthTo = 0; } return { name: 'This Month', startDate: new Date(yearFrom, monthFrom, 1), endDate: new Date(yearTo, monthTo, 0) }; }; var ɵ0 = thisMonthRange; var lastMonthRange = function () { var y = new Date().getFullYear(); var m = new Date().getMonth(); var yearFrom, monthFrom, yearTo, monthTo; yearFrom = y; monthFrom = m - 1; yearTo = y; monthTo = m; if (m === 0) { yearFrom = y - 1; monthFrom = 11; } return { name: 'Last Month', startDate: new Date(yearFrom, monthFrom, 1), endDate: new Date(yearTo, monthTo, 0) }; }; var ɵ1 = lastMonthRange; var lastThreeMonthsRange = function () { var y = new Date().getFullYear(); var m = new Date().getMonth(); var yearFrom, monthFrom, yearTo, monthTo; yearFrom = y; monthFrom = m - 3; // current month - number desired yearTo = y; monthTo = m; if (monthFrom < 0) { yearFrom = y - 1; monthFrom = 11 + 1 + monthFrom; } return { name: 'Last 3 Months', startDate: new Date(yearFrom, monthFrom, 1), endDate: new Date(yearTo, monthTo, 0) }; }; var ɵ2 = lastThreeMonthsRange; var DEFAULT_DATES_RANGES = [ { name: 'Today', startDate: new Date(), endDate: new Date() }, { name: 'Yesterday', startDate: new Date(new Date().setDate(new Date().getDate() - 1)), endDate: new Date(new Date().setDate(new Date().getDate() - 1)) }, { name: 'Last 7 Days', startDate: new Date(new Date().setDate(new Date().getDate() - 6)), endDate: new Date() }, { name: 'Last 30 Days', startDate: new Date(new Date().setDate(new Date().getDate() - 29)), endDate: new Date() }, thisMonthRange(), lastMonthRange(), lastThreeMonthsRange(), ]; /* RANGE: Most of the code is redundant and hera as inheritance boilerplate. The added/edited code adds logic for handling 1 or 2 inputs, i.e. range. When we have 2 inputs it's a range, otherwise its a single value date selection. The main logic is in `selectRangeEnd()` which handles range/non-range and inner range states. (this could probably be more orgenaized with proper states for clarity) Instead of "_userSelection" closing the popup/dialog from the calendar-content, the logic is moved here into _userSelection() which will close/keep based on range logic. */ var MAT_DEFAULT_DATES_RANGES = new InjectionToken('Custom Ranges'); var ɵ0$1 = []; var MatDaterangepicker = /** @class */ (function (_super) { __extends(MatDaterangepicker, _super); function MatDaterangepicker(_drDialog, _drOverlay, _drNgZone, _drViewContainerRef, _drScrollStrategy, _drDateAdapter, _drDir, _drDocument, _customRanges) { var _this = _super.call(this, _drDialog = Object.create(_drDialog), _drOverlay, _drNgZone, _drViewContainerRef, _drScrollStrategy, _drDateAdapter, _drDir, _drDocument) || this; _this._drOverlay = _drOverlay; _this._drNgZone = _drNgZone; _this._drViewContainerRef = _drViewContainerRef; _this._drScrollStrategy = _drScrollStrategy; _this._drDateAdapter = _drDateAdapter; _this._drDir = _drDir; _this._drDocument = _drDocument; _this._customRanges = _customRanges; _this.applyButton = true; _this.dualView = true; _this.showCustomRanges = false; _this.apply = new EventEmitter(); _this.customRanges = []; _this._validSelectedRangeEnd = null; /** Emits new selected date when selected date changes. */ _this._selectedChangedRangeEnd = new Subject(); _this._clearRangeEnd = new Subject(); _this._range = false; _this._inputRangeEndSubscription = Subscription.EMPTY; /* This is a monkey patch workaround to support a new component for dialog/popup. Because everything is freaking private in material, why would somone use protected anyway. */ _this._drDialog = _drDialog; _this._drDialog.open = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (typeof args[0].createEmbeddedView !== 'function') { args[0] = MatDaterangepickerContent; } return MatDialog.prototype.open.apply(_drDialog, args); }; // Object.defineProperty(this, '_calendarPortal', { // get: function () { return this.__calendarPortal; }, // set: function (value) { // this.__calendarPortal = value; // if (value) { // value.component = MatDaterangepickerContent; // } // } // }) _this['_openAsPopup'] = function () { var portal = new ComponentPortal(MatDaterangepickerContent, _this._drViewContainerRef); _this['_destroyPopup'](); _this['_createPopup'](); _this['_popupComponentRef'] = _this['_popupRef'].attach(portal); _this.forwardContentValues(_this['_popupComponentRef'].instance); // Update the position once the calendar has rendered. _this._drNgZone.onStable.asObservable().pipe(take(1)).subscribe(function () { _this['_popupRef'].updatePosition(); }); }; return _this; } Object.defineProperty(MatDaterangepicker.prototype, "_selectedRangeEnd", { /** The currently selected date. */ get: function () { return this._validSelectedRangeEnd; }, set: function (value) { this._validSelectedRangeEnd = value; }, enumerable: true, configurable: true }); MatDaterangepicker.prototype.ngOnInit = function () { if (this.customRanges) { this._setCustomRanges(); } }; MatDaterangepicker.prototype.ngAfterViewInit = function () { }; MatDaterangepicker.prototype.forwardContentValues = function (instance) { instance.datepicker = this; instance.color = this.color; }; MatDaterangepicker.prototype.ngOnDestroy = function () { _super.prototype.ngOnDestroy.call(this); this._inputRangeEndSubscription.unsubscribe(); }; MatDaterangepicker.prototype.selectRangeEnd = function (date) { if (!this._range) { _super.prototype.select.call(this, date); } else { if (!date) { if (this._selected && this._selectedRangeEnd) { _super.prototype.select.call(this, null); this._selectRangeEnd(null); } else if (this._selected) { _super.prototype.select.call(this, null); } else if (this._selectedRangeEnd) { this._selectRangeEnd(null); } } else if (!this._selected || this._selectedRangeEnd) { if (this._selectedRangeEnd) { this._selectRangeEnd(null); } _super.prototype.select.call(this, date); } else { if (date < this._selected) { var swap = this._selected; _super.prototype.select.call(this, date); this._selectRangeEnd(swap); } else if (date >= this._selected) { // notice date === this._selected is not skipped this._selectRangeEnd(date); } } } }; MatDaterangepicker.prototype._userSelection = function () { if (this.applyButton) return; if (!this._range || (this._selected && this._selectedRangeEnd)) { this.close(); } }; MatDaterangepicker.prototype.applyRange = function () { this.close(); this.apply.emit(); }; MatDaterangepicker.prototype.clearRange = function () { this._datepickerInput.value = null; this._clearRangeEnd.next(); }; MatDaterangepicker.prototype._getViews = function () { if (this.dualView) { return [0, 1]; } return [0]; }; MatDaterangepicker.prototype._registerInputRangeEnd = function (input) { var _this = this; if (this._datepickerInputRangeEnd) { throw Error('A MatDatepicker can only be associated with a single range end input.'); } this._datepickerInputRangeEnd = input; this._range = !!input; this._inputRangeEndSubscription.unsubscribe(); if (this._range) { this._inputRangeEndSubscription = this._datepickerInputRangeEnd._valueChange.subscribe(function (value) { return _this._selectedRangeEnd = value; }); } }; MatDaterangepicker.prototype._unregisterInputRangeEnd = function (input) { if (this._datepickerInputRangeEnd) { if (!input || input === this._datepickerInputRangeEnd) { this._datepickerInputRangeEnd = undefined; this._range = false; this._inputRangeEndSubscription.unsubscribe(); } } }; MatDaterangepicker.prototype._selectRangeEnd = function (date) { var oldValue = this._selectedRangeEnd; this._selectedRangeEnd = date; if (!this._drDateAdapter.sameDate(oldValue, this._selectedRangeEnd)) { this._selectedChangedRangeEnd.next(this._selectedRangeEnd); } }; MatDaterangepicker.prototype._setCustomRanges = function () { var _this = this; if (this._customRanges.length) { this.customRanges = this._customRanges; } else { this.customRanges = DEFAULT_DATES_RANGES.map(function (range) { return { name: range.name, startDate: range.startDate ? _this._drDateAdapter.createDate(range.startDate.getFullYear(), range.startDate.getMonth(), range.startDate.getDate()) : null, endDate: range.endDate ? _this._drDateAdapter.createDate(range.endDate.getFullYear(), range.endDate.getMonth(), range.endDate.getDate()) : null }; }); } }; MatDaterangepicker.ctorParameters = function () { return [ { type: MatDialog }, { type: Overlay }, { type: NgZone }, { type: ViewContainerRef }, { type: undefined, decorators: [{ type: Inject, args: [MAT_DATEPICKER_SCROLL_STRATEGY,] }] }, { type: DateAdapter, decorators: [{ type: Optional }] }, { type: Directionality, decorators: [{ type: Optional }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT,] }] }, { type: Array, decorators: [{ type: Inject, args: [MAT_DEFAULT_DATES_RANGES,] }] } ]; }; __decorate([ Input() ], MatDaterangepicker.prototype, "applyButton", void 0); __decorate([ Input() ], MatDaterangepicker.prototype, "dualView", void 0); __decorate([ Input() ], MatDaterangepicker.prototype, "showCustomRanges", void 0); __decorate([ Output() ], MatDaterangepicker.prototype, "apply", void 0); MatDaterangepicker = __decorate([ Component({ selector: 'mat-daterangepicker', template: '', exportAs: 'matDaterangepicker', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, providers: [ { provide: MAT_DEFAULT_DATES_RANGES, useValue: ɵ0$1 }, ] }), __param(4, Inject(MAT_DATEPICKER_SCROLL_STRATEGY)), __param(5, Optional()), __param(6, Optional()), __param(7, Optional()), __param(7, Inject(DOCUMENT)), __param(8, Inject(MAT_DEFAULT_DATES_RANGES)) ], MatDaterangepicker); return MatDaterangepicker; }(MatDatepicker)); /* RANGE: Most of the code is redundant and hera as inheritance boilerplate. The relevant code adds simple logic for a 2nd input, combined with minimal changes in the datepicker component, the work is done there so in native implementation this should have minimal changes, perhaps using dedicated input without min/max/filter etc... which are only set on the 1st input */ var SG_DATEPICKER_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(function () { return MatDaterangepickerInputEnd; }), multi: true }; var SG_DATEPICKER_VALIDATORS = { provide: NG_VALIDATORS, useExisting: forwardRef(function () { return MatDaterangepickerInputEnd; }), multi: true }; var MatDaterangepickerInputEnd = /** @class */ (function (_super) { __extends(MatDaterangepickerInputEnd, _super); function MatDaterangepickerInputEnd(_drElementRef, _drDateAdapter, _drDateFormats, _drFormField) { var _this = _super.call(this, _drElementRef, _drDateAdapter, _drDateFormats, _drFormField) || this; _this._drElementRef = _drElementRef; _this._drDateAdapter = _drDateAdapter; _this._drDateFormats = _drDateFormats; _this._drFormField = _drFormField; _this._matDatepickerSubscription = Subscription.EMPTY; _this._drCvaOnChange = function () { }; _this._drDatepickerClearSubscription = Subscription.EMPTY; return _this; } MatDaterangepickerInputEnd_1 = MatDaterangepickerInputEnd; Object.defineProperty(MatDaterangepickerInputEnd.prototype, "matDaterangepickerEnd", { set: function (value) { if (this._datepicker !== value) { this.unregister(); this._datepicker = value; value._registerInputRangeEnd(this); } }, enumerable: true, configurable: true }); Object.defineProperty(MatDaterangepickerInputEnd.prototype, "_matDaterangepickerEnd", { get: function () { return this._datepicker; }, enumerable: true, configurable: true }); MatDaterangepickerInputEnd.prototype.ngAfterViewInit = function () { // Object.defineProperty(this, 'value', { // set: function (value: D | null) { // if(!this._matDaterangepickerEnd.applyButton){ // console.log('end '+value) // return; // } // value = this._dateAdapter.deserialize(value); // this._lastValueValid = !value || this._dateAdapter.isValid(value); // value = this._getValidDateOrNull(value); // const oldDate = this.value; // this['_assignValue'](value); // this['_formatValue'](value); // if (!this._dateAdapter.sameDate(oldDate, value)) { // this._valueChange.emit(value); // } // } // }) }; MatDaterangepickerInputEnd.prototype.ngAfterContentInit = function () { var _this = this; if (this._datepicker) { this._matDatepickerSubscription = this._matDaterangepickerEnd._selectedChangedRangeEnd.subscribe(function (selected) { _this.value = selected; _this._drCvaOnChange(selected); _this._onTouched(); _this.dateInput.emit(new MatDatepickerInputEvent(_this, _this._drElementRef.nativeElement)); _this.dateChange.emit(new MatDatepickerInputEvent(_this, _this._drElementRef.nativeElement)); }); //runs every clear range end this._drDatepickerClearSubscription = this._matDaterangepickerEnd._clearRangeEnd.subscribe(function () { _this.value = null; _this._drCvaOnChange(null); }); } }; MatDaterangepickerInputEnd.prototype.ngOnDestroy = function () { _super.prototype.ngOnDestroy.call(this); this.unregister(); this._matDatepickerSubscription.unsubscribe(); this._drDatepickerClearSubscription.unsubscribe(); }; // Implemented as part of ControlValueAccessor. MatDaterangepickerInputEnd.prototype.registerOnChange = function (fn) { this._drCvaOnChange = fn; _super.prototype.registerOnChange.call(this, fn); }; MatDaterangepickerInputEnd.prototype.unregister = function () { if (this._matDaterangepickerEnd) { this._matDaterangepickerEnd._unregisterInputRangeEnd(this); } }; var MatDaterangepickerInputEnd_1; MatDaterangepickerInputEnd.ctorParameters = function () { return [ { type: ElementRef }, { type: DateAdapter, decorators: [{ type: Optional }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [MAT_DATE_FORMATS,] }] }, { type: MatFormField, decorators: [{ type: Optional }] } ]; }; __decorate([ Input() ], MatDaterangepickerInputEnd.prototype, "matDaterangepickerEnd", null); MatDaterangepickerInputEnd = MatDaterangepickerInputEnd_1 = __decorate([ Directive({ selector: '[matDaterangepickerEnd]', providers: [ SG_DATEPICKER_VALUE_ACCESSOR, SG_DATEPICKER_VALIDATORS, { provide: MAT_INPUT_VALUE_ACCESSOR, useExisting: MatDaterangepickerInputEnd_1 }, ], host: { '[attr.aria-haspopup]': 'true', '[attr.aria-owns]': '(_datepicker?.opened && _datepicker.id) || null', '[attr.min]': 'min ? _dateAdapter.toIso8601(min) : null', '[attr.max]': 'max ? _dateAdapter.toIso8601(max) : null', '[disabled]': 'disabled', '(input)': '_onInput($event.target.value)', '(change)': '_onChange()', '(blur)': '_onBlur()', '(keydown)': '_onKeydown($event)', }, exportAs: 'MatDaterangepickerInputEnd', }), __param(1, Optional()), __param(2, Optional()), __param(2, Inject(MAT_DATE_FORMATS)), __param(3, Optional()) ], MatDaterangepickerInputEnd); return MatDaterangepickerInputEnd; }(MatDatepickerInput)); /* RANGE: Most of the code is redundant and hera as inheritance boilerplate. The added/edited code adds logic for handling range / non range mode. Additionaly, in range mode it will apply logic to mark the dates within the active month that are within the range. There are 3 range states for a month view, set by the combination of "_rangable" and "_allInRange": - Not rangable (single value datepicker OR active month not in range) - _rangable = False - Rangable but not all (active month is partially in range) - _rangable = True - All In Range (active month is in range, start/end dates before/after this month) - _rangable = True & _allInRange = True */ var MatDaterangeMonthView = /** @class */ (function (_super) { __extends(MatDaterangeMonthView, _super); function MatDaterangeMonthView() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.selectedRangeEndChange = new EventEmitter(); _this._createDate = function (date) { var selectedYear = _this._dateAdapter.getYear(_this.activeDate); var selectedMonth = _this._dateAdapter.getMonth(_this.activeDate); return new Date(selectedYear, selectedMonth, date); }; return _this; } Object.defineProperty(MatDaterangeMonthView.prototype, "selectedRangeEnd", { get: function () { return this._selectedRangeEnd; }, set: function (value) { this._selectedRangeEnd = this._getValidDateOrNull(this._dateAdapter.deserialize(value)); this._selectedRangeEndDate = this._getDateInCurrentMonth(this._selectedRangeEnd); }, enumerable: true, configurable: true }); Object.defineProperty(MatDaterangeMonthView.prototype, "_allInRange", { get: function () { if (!this.range || !this.activeDate || !this.selectedRangeEnd || !this.selected) { return false; } else { var daysInMonth = this._dateAdapter.getNumDaysInMonth(this.activeDate); var startDate = this.createDate(1); var endDate = this.createDate(daysInMonth); return this._dateAdapter.compareDate(this.selected, startDate) < 0 && this._dateAdapter.compareDate(this.selectedRangeEnd, endDate) > 0; } }, enumerable: true, configurable: true }); Object.defineProperty(MatDaterangeMonthView.prototype, "_rangable", { get: function () { if (!this.range || !this._weeks) { return false; } else { var daysInMonth = this._dateAdapter.getNumDaysInMonth(this.activeDate); var startDate = this.createDate(1); var endDate = this.createDate(daysInMonth); if (this.selectedRangeEnd < startDate) { return false; } if (this.selected > endDate) { return false; } return true; } }, enumerable: true, configurable: true }); MatDaterangeMonthView.prototype._init = function () { this._selectedRangeEndDate = this._getDateInCurrentMonth(this.selectedRangeEnd); _super.prototype._init.call(this); }; MatDaterangeMonthView.prototype._dateSelected = function (date) { return __awaiter(this, void 0, void 0, function () { var selectedDate, year, month, swap; return __generator(this, function (_a) { if (!this.range) { _super.prototype._dateSelected.call(this, date); } else { if (!this.selected || this.selectedRangeEnd) { if (this._selectedRangeEnd) { this._dateSelectedRangeEnd(null); } _super.prototype._dateSelected.call(this, date); } else { if (date === null) { this._dateSelectedRangeEnd(null); } else { selectedDate = this.createDate(date); if (selectedDate < this.selected) { year = this._dateAdapter.getYear(this.selected); month = this._dateAdapter.getMonth(this.selected); swap = this._dateAdapter.getDate(this.selected); _super.prototype._dateSelected.call(this, date); this._dateSelectedRangeEnd(swap, year, month); } else { this._dateSelectedRangeEnd(date); } } } } return [2 /*return*/]; }); }); }; MatDaterangeMonthView.prototype._dateSelectedRangeEnd = function (date, year, month) { if (date === null) { this.selectedRangeEndChange.emit(null); } else if (this._selectedRangeEndDate != date) { var selectedDate = void 0; if (year && month) { selectedDate = this._dateAdapter.createDate(year, month, date); } else { selectedDate = this.createDate(date); } this.selectedRangeEndChange.emit(selectedDate); this._userSelection.emit(); } }; MatDaterangeMonthView.prototype.createDate = function (date) { var selectedYear = this._dateAdapter.getYear(this.activeDate); var selectedMonth = this._dateAdapter.getMonth(this.activeDate); return this._dateAdapter.createDate(selectedYear, selectedMonth, date); }; MatDaterangeMonthView.prototype.getSelectedDate = function () { if (!this.selected) return null; var selectedYear = this._dateAdapter.getYear(this.selected); var selectedMonth = this._dateAdapter.getMonth(this.selected); var selectedDate = this._dateAdapter.getDate(this.selected); return new Date(selectedYear, selectedMonth, selectedDate); }; MatDaterangeMonthView.prototype.getSelectedRangeEndDate = function () { if (!this.selectedRangeEnd) return null; var selectedYear = this._dateAdapter.getYear(this.selectedRangeEnd); var selectedMonth = this._dateAdapter.getMonth(this.selectedRangeEnd); var selectedDate = this._dateAdapter.getDate(this.selectedRangeEnd); return new Date(selectedYear, selectedMonth, selectedDate); }; __decorate([ Input() ], MatDaterangeMonthView.prototype, "range", void 0); __decorate([ Input() ], MatDaterangeMonthView.prototype, "view", void 0); __decorate([ Input() ], MatDaterangeMonthView.prototype, "setHoverCells", void 0); __decorate([ Input() ], MatDaterangeMonthView.prototype, "clearBody", void 0); __decorate([ Input() ], MatDaterangeMonthView.prototype, "selectedRangeEnd", null); __decorate([ Output() ], MatDaterangeMonthView.prototype, "selectedRangeEndChange", void 0); MatDaterangeMonthView = __decorate([ Component({ selector: 'mat-daterange-month-view', template: "<!-- \n RANGE: Most of the code is redundant and hera as inheritance boilerplate.\n \n The 3 attributes in the middle are the additional bindings to the template.\n They notify the body:\n - Are we in range mode ([range])\n - Are all the dates in range ([allInRange])\n this happens when the start date is before the current month and the end is after it.\n - The value marking the end of the range ([selectedRangeEndValue])\n\n ADDED LOC ~3\n -->\n\n<table class=\"mat-calendar-table\">\n <thead class=\"mat-calendar-table-header\">\n <tr><th *ngFor=\"let day of _weekdays\" [attr.aria-label]=\"day.long\">{{day.narrow}}</th></tr>\n <tr><th class=\"mat-calendar-table-header-divider\" colspan=\"7\" aria-hidden=\"true\"></th></tr>\n </thead>\n <tbody mat-daterange-calendar-body\n [view]=\"view\"\n [label]=\"_monthLabel\"\n [rows]=\"_weeks\"\n [todayValue]=\"_todayDate\"\n [selectedValue]=\"_selectedDate\"\n\n [selectedRangeEndValue]=\"_selectedRangeEndDate\"\n [allInRange]=\"_allInRange\"\n [range]=\"_rangable\"\n [createDate]=\"_createDate\"\n [dateAdapter]=\"_dateAdapter\"\n [selected]=\"getSelectedDate()\"\n [selectedRangeEnd]=\"getSelectedRangeEndDate()\"\n [setHoverCells]=\"setHoverCells\"\n [clearBody]=\"clearBody\"\n [labelMinRequiredCells]=\"3\"\n [activeCell]=\"_dateAdapter.getDate(activeDate) - 1\"\n (selectedValueChange)=\"_dateSelected($event)\" \n (keydown)=\"_handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n", exportAs: 'matMonthView', encapsulation: ViewEncapsulation.None, preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush }) ], MatDaterangeMonthView); return MatDaterangeMonthView; }(MatMonthView)); /* RANGE: Most of the code is redundant and hera as inheritance boilerplate. The relevant code is marked with a comment. */ var MatDaterangeCalendarBody = /** @class */ (function (_super) { __extends(MatDaterangeCalendarBody, _super); function MatDaterangeCalendarBody() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.hoverSubscription = Subscription.EMPTY; _this.clearBodySubscription = Subscription.EMPTY; return _this; } MatDaterangeCalendarBody.prototype.ngOnInit = function () { var _this = this; this.hoverSubscription = this.setHoverCells.subscribe(function (tmpDate) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { if (!tmpDate || !this.selected || this.selectedRangeEnd || (this.selected && this.selectedRangeEnd)) return [2 /*return*/]; this._doHoverCells(tmpDate); return [2 /*return*/]; }); }); }); this.clearBodySubscription = this.clearBody.subscribe(function (clear) { if (clear) { _this._doHoverCells(null, clear); } }); }; MatDaterangeCalendarBody.prototype._cellClass = function (date) { if (!this.selected || !this.selectedRangeEnd) return; var startDate = this.selected.getTime(); var endDate = this.selectedRangeEnd.getTime(); var cellDate = this.createDate(date).getTime(); return { 'mat-daterange-calendar-body-in-range': cellDate >= startDate && cellDate <= endDate && startDate !== endDate, 'mat-daterange-calendar-body-range-start': cellDate === startDate && startDate !== endDate, 'mat-daterange-calendar-body-range-end': cellDate === endDate && startDate !== endDate }; }; MatDaterangeCalendarBody.prototype._inRange = function (date) { // if (this.range) { // if (this.allInRange) { // return true; // } // if (this.selectedValue && !this.selectedRangeEndValue) { // return date >= this.selectedValue; // } // if (this.selectedRangeEndValue && !this.selectedValue) { // return date <= this.selectedRangeEndValue; // } // return date >= this.selectedValue && date <= this.selectedRangeEndValue; // } }; MatDaterangeCalendarBody.prototype._onHoverCell = function (tmpDate) { if (this.selectedRangeEndValue) return; this.setHoverCells.next(this.createDate(tmpDate)); }; MatDaterangeCalendarBody.prototype._doHoverCells = function (tmpDate, clear) { var _this = this; var _a; var startDate = (_a = this.selected) === null || _a === void 0 ? void 0 : _a.getTime(); var endTempDate = tmpDate === null || tmpDate === void 0 ? void 0 : tmpDate.getTime(); this.rows.map(function (row, r) { var rowElm = _this.rowsElms.toArray()[r]; var hasLabel = false; row.map(function (cell, c) { var cellElm = rowElm.nativeElement.cells[hasLabel ? c + 1 : c]; if (cellElm.classList.contains('mat-calendar-body-label')) { hasLabel = true; cellElm = rowElm.nativeElement.cells[c + 1]; } cellElm.classList.remove('mat-daterange-calendar-body-in-range'); cellElm.classList.remove('mat-daterange-calendar-body-range-end'); cellElm.classList.remove('mat-daterange-calendar-body-range-start'); if (clear) return; var inRange = _this._inRangeTemp(cell.value, startDate, endTempDate, cellElm); }); }); }; MatDaterangeCalendarBody.prototype._inRangeTemp = function (date, startDate, endTempDate, cellElm) { var cellDate = this.createDate(date).getTime(); if (endTempDate >= startDate) { if (endTempDate !== startDate) { if (cellDate === startDate) { cellElm.classList.add('mat-daterange-calendar-body-in-range'); cellElm.classList.add('mat-daterange-calendar-body-range-start'); } if (cellDate === endTempDate) { cellElm.classList.add('mat-daterange-calendar-body-in-range'); cellElm.classList.add('mat-daterange-calendar-body-range-end'); } } if (cellDate > startDate && cellDate < endTempDate) { cellElm.classList.add('mat-daterange-calendar-body-in-range'); return true; } return false; } else { if (endTempDate !== startDate) { if (cellDate === startDate) { cellElm.classList.add('mat-daterange-calendar-body-in-range'); cellElm.classList.add('mat-daterange-calendar-body-range-end'); } if (cellDate === endTempDate) { cellElm.classList.add('mat-daterange-calendar-body-in-range'); cellElm.classList.add('mat-daterange-calendar-body-range-start'); } } if (cellDate < startDate && cellDate > endTempDate) { cellElm.classList.add('mat-daterange-calendar-body-in-range'); return true; } return false; } }; MatDaterangeCalendarBody.prototype.ngOnDestroy = function () { this.hoverSubscription.unsubscribe(); this.clearBodySubscription.unsubscribe(); }; __decorate([ Input() ], MatDaterangeCalendarBody.prototype, "range", void 0); __decorate([ Input() ], MatDaterangeCalendarBody.prototype, "allInRange", void 0); __decorate([ Input()