UNPKG

257 kBJavaScriptView Raw
1import * as i5 from '@angular/cdk/a11y';
2import { A11yModule } from '@angular/cdk/a11y';
3import * as i9 from '@angular/cdk/overlay';
4import { Overlay, FlexibleConnectedPositionStrategy, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
5import * as i6 from '@angular/cdk/portal';
6import { ComponentPortal, TemplatePortal, PortalModule } from '@angular/cdk/portal';
7import * as i1 from '@angular/common';
8import { CommonModule } from '@angular/common';
9import * as i0 from '@angular/core';
10import { Injectable, EventEmitter, Component, ViewEncapsulation, ChangeDetectionStrategy, Input, Output, Optional, SkipSelf, InjectionToken, Inject, ViewChild, forwardRef, Directive, Attribute, ContentChild, InjectFlags, Self, TemplateRef, NgModule } from '@angular/core';
11import * as i3 from '@angular/material/button';
12import { MatButtonModule } from '@angular/material/button';
13import { CdkScrollableModule } from '@angular/cdk/scrolling';
14import * as i1$1 from '@angular/material/core';
15import { DateAdapter, MAT_DATE_FORMATS, mixinColor, mixinErrorState, MatCommonModule } from '@angular/material/core';
16import { Subject, Subscription, merge, of } from 'rxjs';
17import { ESCAPE, hasModifierKey, SPACE, ENTER, PAGE_DOWN, PAGE_UP, END, HOME, DOWN_ARROW, UP_ARROW, RIGHT_ARROW, LEFT_ARROW, BACKSPACE } from '@angular/cdk/keycodes';
18import * as i2 from '@angular/cdk/bidi';
19import { take, startWith, filter } from 'rxjs/operators';
20import { coerceBooleanProperty, coerceStringArray } from '@angular/cdk/coercion';
21import { _getFocusedElementPierceShadowDom } from '@angular/cdk/platform';
22import { trigger, transition, animate, keyframes, style, state } from '@angular/animations';
23import * as i2$2 from '@angular/forms';
24import { NG_VALUE_ACCESSOR, NG_VALIDATORS, Validators, NgControl } from '@angular/forms';
25import * as i2$1 from '@angular/material/form-field';
26import { MAT_FORM_FIELD, MatFormFieldControl } from '@angular/material/form-field';
27import { MAT_INPUT_VALUE_ACCESSOR } from '@angular/material/input';
28
29/**
30 * @license
31 * Copyright Google LLC All Rights Reserved.
32 *
33 * Use of this source code is governed by an MIT-style license that can be
34 * found in the LICENSE file at https://angular.io/license
35 */
36/** @docs-private */
37function createMissingDateImplError(provider) {
38 return Error(`MatDatepicker: No provider found for ${provider}. You must import one of the following ` +
39 `modules at your application root: MatNativeDateModule, MatMomentDateModule, or provide a ` +
40 `custom implementation.`);
41}
42
43/**
44 * @license
45 * Copyright Google LLC All Rights Reserved.
46 *
47 * Use of this source code is governed by an MIT-style license that can be
48 * found in the LICENSE file at https://angular.io/license
49 */
50/** Datepicker data that requires internationalization. */
51class MatDatepickerIntl {
52 constructor() {
53 /**
54 * Stream that emits whenever the labels here are changed. Use this to notify
55 * components if the labels have changed after initialization.
56 */
57 this.changes = new Subject();
58 /** A label for the calendar popup (used by screen readers). */
59 this.calendarLabel = 'Calendar';
60 /** A label for the button used to open the calendar popup (used by screen readers). */
61 this.openCalendarLabel = 'Open calendar';
62 /** Label for the button used to close the calendar popup. */
63 this.closeCalendarLabel = 'Close calendar';
64 /** A label for the previous month button (used by screen readers). */
65 this.prevMonthLabel = 'Previous month';
66 /** A label for the next month button (used by screen readers). */
67 this.nextMonthLabel = 'Next month';
68 /** A label for the previous year button (used by screen readers). */
69 this.prevYearLabel = 'Previous year';
70 /** A label for the next year button (used by screen readers). */
71 this.nextYearLabel = 'Next year';
72 /** A label for the previous multi-year button (used by screen readers). */
73 this.prevMultiYearLabel = 'Previous 24 years';
74 /** A label for the next multi-year button (used by screen readers). */
75 this.nextMultiYearLabel = 'Next 24 years';
76 /** A label for the 'switch to month view' button (used by screen readers). */
77 this.switchToMonthViewLabel = 'Choose date';
78 /** A label for the 'switch to year view' button (used by screen readers). */
79 this.switchToMultiYearViewLabel = 'Choose month and year';
80 }
81 /** Formats a range of years. */
82 formatYearRange(start, end) {
83 return `${start} \u2013 ${end}`;
84 }
85}
86MatDatepickerIntl.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerIntl, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
87MatDatepickerIntl.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerIntl, providedIn: 'root' });
88i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerIntl, decorators: [{
89 type: Injectable,
90 args: [{ providedIn: 'root' }]
91 }] });
92
93/**
94 * @license
95 * Copyright Google LLC All Rights Reserved.
96 *
97 * Use of this source code is governed by an MIT-style license that can be
98 * found in the LICENSE file at https://angular.io/license
99 */
100/**
101 * An internal class that represents the data corresponding to a single calendar cell.
102 * @docs-private
103 */
104class MatCalendarCell {
105 constructor(value, displayValue, ariaLabel, enabled, cssClasses = {}, compareValue = value, rawValue) {
106 this.value = value;
107 this.displayValue = displayValue;
108 this.ariaLabel = ariaLabel;
109 this.enabled = enabled;
110 this.cssClasses = cssClasses;
111 this.compareValue = compareValue;
112 this.rawValue = rawValue;
113 }
114}
115/**
116 * An internal component used to display calendar data in a table.
117 * @docs-private
118 */
119class MatCalendarBody {
120 constructor(_elementRef, _ngZone) {
121 this._elementRef = _elementRef;
122 this._ngZone = _ngZone;
123 /**
124 * Used to focus the active cell after change detection has run.
125 */
126 this._focusActiveCellAfterViewChecked = false;
127 /** The number of columns in the table. */
128 this.numCols = 7;
129 /** The cell number of the active cell in the table. */
130 this.activeCell = 0;
131 /** Whether a range is being selected. */
132 this.isRange = false;
133 /**
134 * The aspect ratio (width / height) to use for the cells in the table. This aspect ratio will be
135 * maintained even as the table resizes.
136 */
137 this.cellAspectRatio = 1;
138 /** Start of the preview range. */
139 this.previewStart = null;
140 /** End of the preview range. */
141 this.previewEnd = null;
142 /** Emits when a new value is selected. */
143 this.selectedValueChange = new EventEmitter();
144 /** Emits when the preview has changed as a result of a user action. */
145 this.previewChange = new EventEmitter();
146 this.activeDateChange = new EventEmitter();
147 /**
148 * Event handler for when the user enters an element
149 * inside the calendar body (e.g. by hovering in or focus).
150 */
151 this._enterHandler = (event) => {
152 if (this._skipNextFocus && event.type === 'focus') {
153 this._skipNextFocus = false;
154 return;
155 }
156 // We only need to hit the zone when we're selecting a range.
157 if (event.target && this.isRange) {
158 const cell = this._getCellFromElement(event.target);
159 if (cell) {
160 this._ngZone.run(() => this.previewChange.emit({ value: cell.enabled ? cell : null, event }));
161 }
162 }
163 };
164 /**
165 * Event handler for when the user's pointer leaves an element
166 * inside the calendar body (e.g. by hovering out or blurring).
167 */
168 this._leaveHandler = (event) => {
169 // We only need to hit the zone when we're selecting a range.
170 if (this.previewEnd !== null && this.isRange) {
171 // Only reset the preview end value when leaving cells. This looks better, because
172 // we have a gap between the cells and the rows and we don't want to remove the
173 // range just for it to show up again when the user moves a few pixels to the side.
174 if (event.target && this._getCellFromElement(event.target)) {
175 this._ngZone.run(() => this.previewChange.emit({ value: null, event }));
176 }
177 }
178 };
179 _ngZone.runOutsideAngular(() => {
180 const element = _elementRef.nativeElement;
181 element.addEventListener('mouseenter', this._enterHandler, true);
182 element.addEventListener('focus', this._enterHandler, true);
183 element.addEventListener('mouseleave', this._leaveHandler, true);
184 element.addEventListener('blur', this._leaveHandler, true);
185 });
186 }
187 ngAfterViewChecked() {
188 if (this._focusActiveCellAfterViewChecked) {
189 this._focusActiveCell();
190 this._focusActiveCellAfterViewChecked = false;
191 }
192 }
193 /** Called when a cell is clicked. */
194 _cellClicked(cell, event) {
195 if (cell.enabled) {
196 this.selectedValueChange.emit({ value: cell.value, event });
197 }
198 }
199 _emitActiveDateChange(cell, event) {
200 if (cell.enabled) {
201 this.activeDateChange.emit({ value: cell.value, event });
202 }
203 }
204 /** Returns whether a cell should be marked as selected. */
205 _isSelected(value) {
206 return this.startValue === value || this.endValue === value;
207 }
208 ngOnChanges(changes) {
209 const columnChanges = changes['numCols'];
210 const { rows, numCols } = this;
211 if (changes['rows'] || columnChanges) {
212 this._firstRowOffset = rows && rows.length && rows[0].length ? numCols - rows[0].length : 0;
213 }
214 if (changes['cellAspectRatio'] || columnChanges || !this._cellPadding) {
215 this._cellPadding = `${(50 * this.cellAspectRatio) / numCols}%`;
216 }
217 if (columnChanges || !this._cellWidth) {
218 this._cellWidth = `${100 / numCols}%`;
219 }
220 }
221 ngOnDestroy() {
222 const element = this._elementRef.nativeElement;
223 element.removeEventListener('mouseenter', this._enterHandler, true);
224 element.removeEventListener('focus', this._enterHandler, true);
225 element.removeEventListener('mouseleave', this._leaveHandler, true);
226 element.removeEventListener('blur', this._leaveHandler, true);
227 }
228 /** Returns whether a cell is active. */
229 _isActiveCell(rowIndex, colIndex) {
230 let cellNumber = rowIndex * this.numCols + colIndex;
231 // Account for the fact that the first row may not have as many cells.
232 if (rowIndex) {
233 cellNumber -= this._firstRowOffset;
234 }
235 return cellNumber == this.activeCell;
236 }
237 /**
238 * Focuses the active cell after the microtask queue is empty.
239 *
240 * Adding a 0ms setTimeout seems to fix Voiceover losing focus when pressing PageUp/PageDown
241 * (issue #24330).
242 *
243 * Determined a 0ms by gradually increasing duration from 0 and testing two use cases with screen
244 * reader enabled:
245 *
246 * 1. Pressing PageUp/PageDown repeatedly with pausing between each key press.
247 * 2. Pressing and holding the PageDown key with repeated keys enabled.
248 *
249 * Test 1 worked roughly 95-99% of the time with 0ms and got a little bit better as the duration
250 * increased. Test 2 got slightly better until the duration was long enough to interfere with
251 * repeated keys. If the repeated key speed was faster than the timeout duration, then pressing
252 * and holding pagedown caused the entire page to scroll.
253 *
254 * Since repeated key speed can verify across machines, determined that any duration could
255 * potentially interfere with repeated keys. 0ms would be best because it almost entirely
256 * eliminates the focus being lost in Voiceover (#24330) without causing unintended side effects.
257 * Adding delay also complicates writing tests.
258 */
259 _focusActiveCell(movePreview = true) {
260 this._ngZone.runOutsideAngular(() => {
261 this._ngZone.onStable.pipe(take(1)).subscribe(() => {
262 setTimeout(() => {
263 const activeCell = this._elementRef.nativeElement.querySelector('.mat-calendar-body-active');
264 if (activeCell) {
265 if (!movePreview) {
266 this._skipNextFocus = true;
267 }
268 activeCell.focus();
269 }
270 });
271 });
272 });
273 }
274 /** Focuses the active cell after change detection has run and the microtask queue is empty. */
275 _scheduleFocusActiveCellAfterViewChecked() {
276 this._focusActiveCellAfterViewChecked = true;
277 }
278 /** Gets whether a value is the start of the main range. */
279 _isRangeStart(value) {
280 return isStart(value, this.startValue, this.endValue);
281 }
282 /** Gets whether a value is the end of the main range. */
283 _isRangeEnd(value) {
284 return isEnd(value, this.startValue, this.endValue);
285 }
286 /** Gets whether a value is within the currently-selected range. */
287 _isInRange(value) {
288 return isInRange(value, this.startValue, this.endValue, this.isRange);
289 }
290 /** Gets whether a value is the start of the comparison range. */
291 _isComparisonStart(value) {
292 return isStart(value, this.comparisonStart, this.comparisonEnd);
293 }
294 /** Whether the cell is a start bridge cell between the main and comparison ranges. */
295 _isComparisonBridgeStart(value, rowIndex, colIndex) {
296 if (!this._isComparisonStart(value) || this._isRangeStart(value) || !this._isInRange(value)) {
297 return false;
298 }
299 let previousCell = this.rows[rowIndex][colIndex - 1];
300 if (!previousCell) {
301 const previousRow = this.rows[rowIndex - 1];
302 previousCell = previousRow && previousRow[previousRow.length - 1];
303 }
304 return previousCell && !this._isRangeEnd(previousCell.compareValue);
305 }
306 /** Whether the cell is an end bridge cell between the main and comparison ranges. */
307 _isComparisonBridgeEnd(value, rowIndex, colIndex) {
308 if (!this._isComparisonEnd(value) || this._isRangeEnd(value) || !this._isInRange(value)) {
309 return false;
310 }
311 let nextCell = this.rows[rowIndex][colIndex + 1];
312 if (!nextCell) {
313 const nextRow = this.rows[rowIndex + 1];
314 nextCell = nextRow && nextRow[0];
315 }
316 return nextCell && !this._isRangeStart(nextCell.compareValue);
317 }
318 /** Gets whether a value is the end of the comparison range. */
319 _isComparisonEnd(value) {
320 return isEnd(value, this.comparisonStart, this.comparisonEnd);
321 }
322 /** Gets whether a value is within the current comparison range. */
323 _isInComparisonRange(value) {
324 return isInRange(value, this.comparisonStart, this.comparisonEnd, this.isRange);
325 }
326 /**
327 * Gets whether a value is the same as the start and end of the comparison range.
328 * For context, the functions that we use to determine whether something is the start/end of
329 * a range don't allow for the start and end to be on the same day, because we'd have to use
330 * much more specific CSS selectors to style them correctly in all scenarios. This is fine for
331 * the regular range, because when it happens, the selected styles take over and still show where
332 * the range would've been, however we don't have these selected styles for a comparison range.
333 * This function is used to apply a class that serves the same purpose as the one for selected
334 * dates, but it only applies in the context of a comparison range.
335 */
336 _isComparisonIdentical(value) {
337 // Note that we don't need to null check the start/end
338 // here, because the `value` will always be defined.
339 return this.comparisonStart === this.comparisonEnd && value === this.comparisonStart;
340 }
341 /** Gets whether a value is the start of the preview range. */
342 _isPreviewStart(value) {
343 return isStart(value, this.previewStart, this.previewEnd);
344 }
345 /** Gets whether a value is the end of the preview range. */
346 _isPreviewEnd(value) {
347 return isEnd(value, this.previewStart, this.previewEnd);
348 }
349 /** Gets whether a value is inside the preview range. */
350 _isInPreview(value) {
351 return isInRange(value, this.previewStart, this.previewEnd, this.isRange);
352 }
353 /** Finds the MatCalendarCell that corresponds to a DOM node. */
354 _getCellFromElement(element) {
355 let cell;
356 if (isTableCell(element)) {
357 cell = element;
358 }
359 else if (isTableCell(element.parentNode)) {
360 cell = element.parentNode;
361 }
362 if (cell) {
363 const row = cell.getAttribute('data-mat-row');
364 const col = cell.getAttribute('data-mat-col');
365 if (row && col) {
366 return this.rows[parseInt(row)][parseInt(col)];
367 }
368 }
369 return null;
370 }
371}
372MatCalendarBody.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatCalendarBody, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
373MatCalendarBody.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatCalendarBody, selector: "[mat-calendar-body]", inputs: { label: "label", rows: "rows", todayValue: "todayValue", startValue: "startValue", endValue: "endValue", labelMinRequiredCells: "labelMinRequiredCells", numCols: "numCols", activeCell: "activeCell", isRange: "isRange", cellAspectRatio: "cellAspectRatio", comparisonStart: "comparisonStart", comparisonEnd: "comparisonEnd", previewStart: "previewStart", previewEnd: "previewEnd" }, outputs: { selectedValueChange: "selectedValueChange", previewChange: "previewChange", activeDateChange: "activeDateChange" }, host: { classAttribute: "mat-calendar-body" }, exportAs: ["matCalendarBody"], usesOnChanges: true, ngImport: i0, template: "<!--\n If there's not enough space in the first row, create a separate label row. We mark this row as\n aria-hidden because we don't want it to be read out as one of the weeks in the month.\n-->\n<tr *ngIf=\"_firstRowOffset < labelMinRequiredCells\" aria-hidden=\"true\">\n <td class=\"mat-calendar-body-label\"\n [attr.colspan]=\"numCols\"\n [style.paddingTop]=\"_cellPadding\"\n [style.paddingBottom]=\"_cellPadding\">\n {{label}}\n </td>\n</tr>\n\n<!-- Create the first row separately so we can include a special spacer cell. -->\n<tr *ngFor=\"let row of rows; let rowIndex = index\" role=\"row\">\n <!--\n This cell is purely decorative, but we can't put `aria-hidden` or `role=\"presentation\"` on it,\n because it throws off the week days for the rest of the row on NVDA. The aspect ratio of the\n table cells is maintained by setting the top and bottom padding as a percentage of the width\n (a variant of the trick described here: https://www.w3schools.com/howto/howto_css_aspect_ratio.asp).\n -->\n <td *ngIf=\"rowIndex === 0 && _firstRowOffset\"\n class=\"mat-calendar-body-label\"\n [attr.colspan]=\"_firstRowOffset\"\n [style.paddingTop]=\"_cellPadding\"\n [style.paddingBottom]=\"_cellPadding\">\n {{_firstRowOffset >= labelMinRequiredCells ? label : ''}}\n </td>\n <!--\n Each gridcell in the calendar contains a button, which signals to assistive technology that the\n cell is interactable, as well as the selection state via `aria-pressed`. See #23476 for\n background.\n -->\n <td\n *ngFor=\"let item of row; let colIndex = index\"\n role=\"gridcell\"\n class=\"mat-calendar-body-cell-container\"\n [style.width]=\"_cellWidth\"\n [style.paddingTop]=\"_cellPadding\"\n [style.paddingBottom]=\"_cellPadding\"\n [attr.data-mat-row]=\"rowIndex\"\n [attr.data-mat-col]=\"colIndex\"\n >\n <button\n type=\"button\"\n class=\"mat-calendar-body-cell\"\n [ngClass]=\"item.cssClasses\"\n [tabindex]=\"_isActiveCell(rowIndex, colIndex) ? 0 : -1\"\n [class.mat-calendar-body-disabled]=\"!item.enabled\"\n [class.mat-calendar-body-active]=\"_isActiveCell(rowIndex, colIndex)\"\n [class.mat-calendar-body-range-start]=\"_isRangeStart(item.compareValue)\"\n [class.mat-calendar-body-range-end]=\"_isRangeEnd(item.compareValue)\"\n [class.mat-calendar-body-in-range]=\"_isInRange(item.compareValue)\"\n [class.mat-calendar-body-comparison-bridge-start]=\"_isComparisonBridgeStart(item.compareValue, rowIndex, colIndex)\"\n [class.mat-calendar-body-comparison-bridge-end]=\"_isComparisonBridgeEnd(item.compareValue, rowIndex, colIndex)\"\n [class.mat-calendar-body-comparison-start]=\"_isComparisonStart(item.compareValue)\"\n [class.mat-calendar-body-comparison-end]=\"_isComparisonEnd(item.compareValue)\"\n [class.mat-calendar-body-in-comparison-range]=\"_isInComparisonRange(item.compareValue)\"\n [class.mat-calendar-body-preview-start]=\"_isPreviewStart(item.compareValue)\"\n [class.mat-calendar-body-preview-end]=\"_isPreviewEnd(item.compareValue)\"\n [class.mat-calendar-body-in-preview]=\"_isInPreview(item.compareValue)\"\n [attr.aria-label]=\"item.ariaLabel\"\n [attr.aria-disabled]=\"!item.enabled || null\"\n [attr.aria-pressed]=\"_isSelected(item.compareValue)\"\n [attr.aria-current]=\"todayValue === item.compareValue ? 'date' : null\"\n (click)=\"_cellClicked(item, $event)\"\n (focus)=\"_emitActiveDateChange(item, $event)\">\n <div class=\"mat-calendar-body-cell-content mat-focus-indicator\"\n [class.mat-calendar-body-selected]=\"_isSelected(item.compareValue)\"\n [class.mat-calendar-body-comparison-identical]=\"_isComparisonIdentical(item.compareValue)\"\n [class.mat-calendar-body-today]=\"todayValue === item.compareValue\">\n {{item.displayValue}}\n </div>\n <div class=\"mat-calendar-body-cell-preview\" aria-hidden=\"true\"></div>\n </button>\n </td>\n</tr>\n", styles: [".mat-calendar-body{min-width:224px}.mat-calendar-body-label{height:0;line-height:0;text-align:left;padding-left:4.7142857143%;padding-right:4.7142857143%}.mat-calendar-body-cell-container{position:relative;height:0;line-height:0}.mat-calendar-body-cell{-webkit-user-select:none;user-select:none;cursor:pointer;outline:none;border:none;-webkit-tap-highlight-color:rgba(0,0,0,0);position:absolute;top:0;left:0;width:100%;height:100%;background:none;text-align:center;outline:none;font-family:inherit;margin:0}.mat-calendar-body-cell::-moz-focus-inner{border:0}.mat-calendar-body-cell::before,.mat-calendar-body-cell::after,.mat-calendar-body-cell-preview{content:\"\";position:absolute;top:5%;left:0;z-index:0;box-sizing:border-box;height:90%;width:100%}.mat-calendar-body-range-start:not(.mat-calendar-body-in-comparison-range)::before,.mat-calendar-body-range-start::after,.mat-calendar-body-comparison-start:not(.mat-calendar-body-comparison-bridge-start)::before,.mat-calendar-body-comparison-start::after,.mat-calendar-body-preview-start .mat-calendar-body-cell-preview{left:5%;width:95%;border-top-left-radius:999px;border-bottom-left-radius:999px}[dir=rtl] .mat-calendar-body-range-start:not(.mat-calendar-body-in-comparison-range)::before,[dir=rtl] .mat-calendar-body-range-start::after,[dir=rtl] .mat-calendar-body-comparison-start:not(.mat-calendar-body-comparison-bridge-start)::before,[dir=rtl] .mat-calendar-body-comparison-start::after,[dir=rtl] .mat-calendar-body-preview-start .mat-calendar-body-cell-preview{left:0;border-radius:0;border-top-right-radius:999px;border-bottom-right-radius:999px}.mat-calendar-body-range-end:not(.mat-calendar-body-in-comparison-range)::before,.mat-calendar-body-range-end::after,.mat-calendar-body-comparison-end:not(.mat-calendar-body-comparison-bridge-end)::before,.mat-calendar-body-comparison-end::after,.mat-calendar-body-preview-end .mat-calendar-body-cell-preview{width:95%;border-top-right-radius:999px;border-bottom-right-radius:999px}[dir=rtl] .mat-calendar-body-range-end:not(.mat-calendar-body-in-comparison-range)::before,[dir=rtl] .mat-calendar-body-range-end::after,[dir=rtl] .mat-calendar-body-comparison-end:not(.mat-calendar-body-comparison-bridge-end)::before,[dir=rtl] .mat-calendar-body-comparison-end::after,[dir=rtl] .mat-calendar-body-preview-end .mat-calendar-body-cell-preview{left:5%;border-radius:0;border-top-left-radius:999px;border-bottom-left-radius:999px}[dir=rtl] .mat-calendar-body-comparison-bridge-start.mat-calendar-body-range-end::after,[dir=rtl] .mat-calendar-body-comparison-bridge-end.mat-calendar-body-range-start::after{width:95%;border-top-right-radius:999px;border-bottom-right-radius:999px}.mat-calendar-body-comparison-start.mat-calendar-body-range-end::after,[dir=rtl] .mat-calendar-body-comparison-start.mat-calendar-body-range-end::after,.mat-calendar-body-comparison-end.mat-calendar-body-range-start::after,[dir=rtl] .mat-calendar-body-comparison-end.mat-calendar-body-range-start::after{width:90%}.mat-calendar-body-in-preview .mat-calendar-body-cell-preview{border-top:dashed 1px;border-bottom:dashed 1px}.mat-calendar-body-preview-start .mat-calendar-body-cell-preview{border-left:dashed 1px}[dir=rtl] .mat-calendar-body-preview-start .mat-calendar-body-cell-preview{border-left:0;border-right:dashed 1px}.mat-calendar-body-preview-end .mat-calendar-body-cell-preview{border-right:dashed 1px}[dir=rtl] .mat-calendar-body-preview-end .mat-calendar-body-cell-preview{border-right:0;border-left:dashed 1px}.mat-calendar-body-disabled{cursor:default}.cdk-high-contrast-active .mat-calendar-body-disabled{opacity:.5}.mat-calendar-body-cell-content{top:5%;left:5%;z-index:1;display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:90%;height:90%;line-height:1;border-width:1px;border-style:solid;border-radius:999px}.mat-calendar-body-cell-content.mat-focus-indicator{position:absolute}.cdk-high-contrast-active .mat-calendar-body-cell-content{border:none}.cdk-high-contrast-active .mat-datepicker-popup:not(:empty),.cdk-high-contrast-active .mat-calendar-body-cell:not(.mat-calendar-body-in-range) .mat-calendar-body-selected{outline:solid 1px}.cdk-high-contrast-active .mat-calendar-body-today{outline:dotted 1px}.cdk-high-contrast-active .cdk-keyboard-focused .mat-calendar-body-active>.mat-calendar-body-cell-content,.cdk-high-contrast-active .cdk-program-focused .mat-calendar-body-active>.mat-calendar-body-cell-content{outline:dotted 2px}.cdk-high-contrast-active .cdk-keyboard-focused .mat-calendar-body-active>.mat-calendar-body-cell-content.mat-calendar-body-selected,.cdk-high-contrast-active .cdk-program-focused .mat-calendar-body-active>.mat-calendar-body-cell-content.mat-calendar-body-selected{outline:solid 3px}.cdk-high-contrast-active .mat-calendar-body-cell::before,.cdk-high-contrast-active .mat-calendar-body-cell::after,.cdk-high-contrast-active .mat-calendar-body-selected{background:none}.cdk-high-contrast-active .mat-calendar-body-in-range::before,.cdk-high-contrast-active .mat-calendar-body-comparison-bridge-start::before,.cdk-high-contrast-active .mat-calendar-body-comparison-bridge-end::before{border-top:solid 1px;border-bottom:solid 1px}.cdk-high-contrast-active .mat-calendar-body-range-start::before{border-left:solid 1px}[dir=rtl] .cdk-high-contrast-active .mat-calendar-body-range-start::before{border-left:0;border-right:solid 1px}.cdk-high-contrast-active .mat-calendar-body-range-end::before{border-right:solid 1px}[dir=rtl] .cdk-high-contrast-active .mat-calendar-body-range-end::before{border-right:0;border-left:solid 1px}.cdk-high-contrast-active .mat-calendar-body-in-comparison-range::before{border-top:dashed 1px;border-bottom:dashed 1px}.cdk-high-contrast-active .mat-calendar-body-comparison-start::before{border-left:dashed 1px}[dir=rtl] .cdk-high-contrast-active .mat-calendar-body-comparison-start::before{border-left:0;border-right:dashed 1px}.cdk-high-contrast-active .mat-calendar-body-comparison-end::before{border-right:dashed 1px}[dir=rtl] .cdk-high-contrast-active .mat-calendar-body-comparison-end::before{border-right:0;border-left:dashed 1px}[dir=rtl] .mat-calendar-body-label{text-align:right}"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
374i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatCalendarBody, decorators: [{
375 type: Component,
376 args: [{ selector: '[mat-calendar-body]', host: {
377 'class': 'mat-calendar-body',
378 }, exportAs: 'matCalendarBody', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n If there's not enough space in the first row, create a separate label row. We mark this row as\n aria-hidden because we don't want it to be read out as one of the weeks in the month.\n-->\n<tr *ngIf=\"_firstRowOffset < labelMinRequiredCells\" aria-hidden=\"true\">\n <td class=\"mat-calendar-body-label\"\n [attr.colspan]=\"numCols\"\n [style.paddingTop]=\"_cellPadding\"\n [style.paddingBottom]=\"_cellPadding\">\n {{label}}\n </td>\n</tr>\n\n<!-- Create the first row separately so we can include a special spacer cell. -->\n<tr *ngFor=\"let row of rows; let rowIndex = index\" role=\"row\">\n <!--\n This cell is purely decorative, but we can't put `aria-hidden` or `role=\"presentation\"` on it,\n because it throws off the week days for the rest of the row on NVDA. The aspect ratio of the\n table cells is maintained by setting the top and bottom padding as a percentage of the width\n (a variant of the trick described here: https://www.w3schools.com/howto/howto_css_aspect_ratio.asp).\n -->\n <td *ngIf=\"rowIndex === 0 && _firstRowOffset\"\n class=\"mat-calendar-body-label\"\n [attr.colspan]=\"_firstRowOffset\"\n [style.paddingTop]=\"_cellPadding\"\n [style.paddingBottom]=\"_cellPadding\">\n {{_firstRowOffset >= labelMinRequiredCells ? label : ''}}\n </td>\n <!--\n Each gridcell in the calendar contains a button, which signals to assistive technology that the\n cell is interactable, as well as the selection state via `aria-pressed`. See #23476 for\n background.\n -->\n <td\n *ngFor=\"let item of row; let colIndex = index\"\n role=\"gridcell\"\n class=\"mat-calendar-body-cell-container\"\n [style.width]=\"_cellWidth\"\n [style.paddingTop]=\"_cellPadding\"\n [style.paddingBottom]=\"_cellPadding\"\n [attr.data-mat-row]=\"rowIndex\"\n [attr.data-mat-col]=\"colIndex\"\n >\n <button\n type=\"button\"\n class=\"mat-calendar-body-cell\"\n [ngClass]=\"item.cssClasses\"\n [tabindex]=\"_isActiveCell(rowIndex, colIndex) ? 0 : -1\"\n [class.mat-calendar-body-disabled]=\"!item.enabled\"\n [class.mat-calendar-body-active]=\"_isActiveCell(rowIndex, colIndex)\"\n [class.mat-calendar-body-range-start]=\"_isRangeStart(item.compareValue)\"\n [class.mat-calendar-body-range-end]=\"_isRangeEnd(item.compareValue)\"\n [class.mat-calendar-body-in-range]=\"_isInRange(item.compareValue)\"\n [class.mat-calendar-body-comparison-bridge-start]=\"_isComparisonBridgeStart(item.compareValue, rowIndex, colIndex)\"\n [class.mat-calendar-body-comparison-bridge-end]=\"_isComparisonBridgeEnd(item.compareValue, rowIndex, colIndex)\"\n [class.mat-calendar-body-comparison-start]=\"_isComparisonStart(item.compareValue)\"\n [class.mat-calendar-body-comparison-end]=\"_isComparisonEnd(item.compareValue)\"\n [class.mat-calendar-body-in-comparison-range]=\"_isInComparisonRange(item.compareValue)\"\n [class.mat-calendar-body-preview-start]=\"_isPreviewStart(item.compareValue)\"\n [class.mat-calendar-body-preview-end]=\"_isPreviewEnd(item.compareValue)\"\n [class.mat-calendar-body-in-preview]=\"_isInPreview(item.compareValue)\"\n [attr.aria-label]=\"item.ariaLabel\"\n [attr.aria-disabled]=\"!item.enabled || null\"\n [attr.aria-pressed]=\"_isSelected(item.compareValue)\"\n [attr.aria-current]=\"todayValue === item.compareValue ? 'date' : null\"\n (click)=\"_cellClicked(item, $event)\"\n (focus)=\"_emitActiveDateChange(item, $event)\">\n <div class=\"mat-calendar-body-cell-content mat-focus-indicator\"\n [class.mat-calendar-body-selected]=\"_isSelected(item.compareValue)\"\n [class.mat-calendar-body-comparison-identical]=\"_isComparisonIdentical(item.compareValue)\"\n [class.mat-calendar-body-today]=\"todayValue === item.compareValue\">\n {{item.displayValue}}\n </div>\n <div class=\"mat-calendar-body-cell-preview\" aria-hidden=\"true\"></div>\n </button>\n </td>\n</tr>\n", styles: [".mat-calendar-body{min-width:224px}.mat-calendar-body-label{height:0;line-height:0;text-align:left;padding-left:4.7142857143%;padding-right:4.7142857143%}.mat-calendar-body-cell-container{position:relative;height:0;line-height:0}.mat-calendar-body-cell{-webkit-user-select:none;user-select:none;cursor:pointer;outline:none;border:none;-webkit-tap-highlight-color:rgba(0,0,0,0);position:absolute;top:0;left:0;width:100%;height:100%;background:none;text-align:center;outline:none;font-family:inherit;margin:0}.mat-calendar-body-cell::-moz-focus-inner{border:0}.mat-calendar-body-cell::before,.mat-calendar-body-cell::after,.mat-calendar-body-cell-preview{content:\"\";position:absolute;top:5%;left:0;z-index:0;box-sizing:border-box;height:90%;width:100%}.mat-calendar-body-range-start:not(.mat-calendar-body-in-comparison-range)::before,.mat-calendar-body-range-start::after,.mat-calendar-body-comparison-start:not(.mat-calendar-body-comparison-bridge-start)::before,.mat-calendar-body-comparison-start::after,.mat-calendar-body-preview-start .mat-calendar-body-cell-preview{left:5%;width:95%;border-top-left-radius:999px;border-bottom-left-radius:999px}[dir=rtl] .mat-calendar-body-range-start:not(.mat-calendar-body-in-comparison-range)::before,[dir=rtl] .mat-calendar-body-range-start::after,[dir=rtl] .mat-calendar-body-comparison-start:not(.mat-calendar-body-comparison-bridge-start)::before,[dir=rtl] .mat-calendar-body-comparison-start::after,[dir=rtl] .mat-calendar-body-preview-start .mat-calendar-body-cell-preview{left:0;border-radius:0;border-top-right-radius:999px;border-bottom-right-radius:999px}.mat-calendar-body-range-end:not(.mat-calendar-body-in-comparison-range)::before,.mat-calendar-body-range-end::after,.mat-calendar-body-comparison-end:not(.mat-calendar-body-comparison-bridge-end)::before,.mat-calendar-body-comparison-end::after,.mat-calendar-body-preview-end .mat-calendar-body-cell-preview{width:95%;border-top-right-radius:999px;border-bottom-right-radius:999px}[dir=rtl] .mat-calendar-body-range-end:not(.mat-calendar-body-in-comparison-range)::before,[dir=rtl] .mat-calendar-body-range-end::after,[dir=rtl] .mat-calendar-body-comparison-end:not(.mat-calendar-body-comparison-bridge-end)::before,[dir=rtl] .mat-calendar-body-comparison-end::after,[dir=rtl] .mat-calendar-body-preview-end .mat-calendar-body-cell-preview{left:5%;border-radius:0;border-top-left-radius:999px;border-bottom-left-radius:999px}[dir=rtl] .mat-calendar-body-comparison-bridge-start.mat-calendar-body-range-end::after,[dir=rtl] .mat-calendar-body-comparison-bridge-end.mat-calendar-body-range-start::after{width:95%;border-top-right-radius:999px;border-bottom-right-radius:999px}.mat-calendar-body-comparison-start.mat-calendar-body-range-end::after,[dir=rtl] .mat-calendar-body-comparison-start.mat-calendar-body-range-end::after,.mat-calendar-body-comparison-end.mat-calendar-body-range-start::after,[dir=rtl] .mat-calendar-body-comparison-end.mat-calendar-body-range-start::after{width:90%}.mat-calendar-body-in-preview .mat-calendar-body-cell-preview{border-top:dashed 1px;border-bottom:dashed 1px}.mat-calendar-body-preview-start .mat-calendar-body-cell-preview{border-left:dashed 1px}[dir=rtl] .mat-calendar-body-preview-start .mat-calendar-body-cell-preview{border-left:0;border-right:dashed 1px}.mat-calendar-body-preview-end .mat-calendar-body-cell-preview{border-right:dashed 1px}[dir=rtl] .mat-calendar-body-preview-end .mat-calendar-body-cell-preview{border-right:0;border-left:dashed 1px}.mat-calendar-body-disabled{cursor:default}.cdk-high-contrast-active .mat-calendar-body-disabled{opacity:.5}.mat-calendar-body-cell-content{top:5%;left:5%;z-index:1;display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:90%;height:90%;line-height:1;border-width:1px;border-style:solid;border-radius:999px}.mat-calendar-body-cell-content.mat-focus-indicator{position:absolute}.cdk-high-contrast-active .mat-calendar-body-cell-content{border:none}.cdk-high-contrast-active .mat-datepicker-popup:not(:empty),.cdk-high-contrast-active .mat-calendar-body-cell:not(.mat-calendar-body-in-range) .mat-calendar-body-selected{outline:solid 1px}.cdk-high-contrast-active .mat-calendar-body-today{outline:dotted 1px}.cdk-high-contrast-active .cdk-keyboard-focused .mat-calendar-body-active>.mat-calendar-body-cell-content,.cdk-high-contrast-active .cdk-program-focused .mat-calendar-body-active>.mat-calendar-body-cell-content{outline:dotted 2px}.cdk-high-contrast-active .cdk-keyboard-focused .mat-calendar-body-active>.mat-calendar-body-cell-content.mat-calendar-body-selected,.cdk-high-contrast-active .cdk-program-focused .mat-calendar-body-active>.mat-calendar-body-cell-content.mat-calendar-body-selected{outline:solid 3px}.cdk-high-contrast-active .mat-calendar-body-cell::before,.cdk-high-contrast-active .mat-calendar-body-cell::after,.cdk-high-contrast-active .mat-calendar-body-selected{background:none}.cdk-high-contrast-active .mat-calendar-body-in-range::before,.cdk-high-contrast-active .mat-calendar-body-comparison-bridge-start::before,.cdk-high-contrast-active .mat-calendar-body-comparison-bridge-end::before{border-top:solid 1px;border-bottom:solid 1px}.cdk-high-contrast-active .mat-calendar-body-range-start::before{border-left:solid 1px}[dir=rtl] .cdk-high-contrast-active .mat-calendar-body-range-start::before{border-left:0;border-right:solid 1px}.cdk-high-contrast-active .mat-calendar-body-range-end::before{border-right:solid 1px}[dir=rtl] .cdk-high-contrast-active .mat-calendar-body-range-end::before{border-right:0;border-left:solid 1px}.cdk-high-contrast-active .mat-calendar-body-in-comparison-range::before{border-top:dashed 1px;border-bottom:dashed 1px}.cdk-high-contrast-active .mat-calendar-body-comparison-start::before{border-left:dashed 1px}[dir=rtl] .cdk-high-contrast-active .mat-calendar-body-comparison-start::before{border-left:0;border-right:dashed 1px}.cdk-high-contrast-active .mat-calendar-body-comparison-end::before{border-right:dashed 1px}[dir=rtl] .cdk-high-contrast-active .mat-calendar-body-comparison-end::before{border-right:0;border-left:dashed 1px}[dir=rtl] .mat-calendar-body-label{text-align:right}"] }]
379 }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { label: [{
380 type: Input
381 }], rows: [{
382 type: Input
383 }], todayValue: [{
384 type: Input
385 }], startValue: [{
386 type: Input
387 }], endValue: [{
388 type: Input
389 }], labelMinRequiredCells: [{
390 type: Input
391 }], numCols: [{
392 type: Input
393 }], activeCell: [{
394 type: Input
395 }], isRange: [{
396 type: Input
397 }], cellAspectRatio: [{
398 type: Input
399 }], comparisonStart: [{
400 type: Input
401 }], comparisonEnd: [{
402 type: Input
403 }], previewStart: [{
404 type: Input
405 }], previewEnd: [{
406 type: Input
407 }], selectedValueChange: [{
408 type: Output
409 }], previewChange: [{
410 type: Output
411 }], activeDateChange: [{
412 type: Output
413 }] } });
414/** Checks whether a node is a table cell element. */
415function isTableCell(node) {
416 return node.nodeName === 'TD';
417}
418/** Checks whether a value is the start of a range. */
419function isStart(value, start, end) {
420 return end !== null && start !== end && value < end && value === start;
421}
422/** Checks whether a value is the end of a range. */
423function isEnd(value, start, end) {
424 return start !== null && start !== end && value >= start && value === end;
425}
426/** Checks whether a value is inside of a range. */
427function isInRange(value, start, end, rangeEnabled) {
428 return (rangeEnabled &&
429 start !== null &&
430 end !== null &&
431 start !== end &&
432 value >= start &&
433 value <= end);
434}
435
436/**
437 * @license
438 * Copyright Google LLC All Rights Reserved.
439 *
440 * Use of this source code is governed by an MIT-style license that can be
441 * found in the LICENSE file at https://angular.io/license
442 */
443/** A class representing a range of dates. */
444class DateRange {
445 constructor(
446 /** The start date of the range. */
447 start,
448 /** The end date of the range. */
449 end) {
450 this.start = start;
451 this.end = end;
452 }
453}
454/**
455 * A selection model containing a date selection.
456 * @docs-private
457 */
458class MatDateSelectionModel {
459 constructor(
460 /** The current selection. */
461 selection, _adapter) {
462 this.selection = selection;
463 this._adapter = _adapter;
464 this._selectionChanged = new Subject();
465 /** Emits when the selection has changed. */
466 this.selectionChanged = this._selectionChanged;
467 this.selection = selection;
468 }
469 /**
470 * Updates the current selection in the model.
471 * @param value New selection that should be assigned.
472 * @param source Object that triggered the selection change.
473 */
474 updateSelection(value, source) {
475 const oldValue = this.selection;
476 this.selection = value;
477 this._selectionChanged.next({ selection: value, source, oldValue });
478 }
479 ngOnDestroy() {
480 this._selectionChanged.complete();
481 }
482 _isValidDateInstance(date) {
483 return this._adapter.isDateInstance(date) && this._adapter.isValid(date);
484 }
485}
486MatDateSelectionModel.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDateSelectionModel, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
487MatDateSelectionModel.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDateSelectionModel });
488i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDateSelectionModel, decorators: [{
489 type: Injectable
490 }], ctorParameters: function () { return [{ type: undefined }, { type: i1$1.DateAdapter }]; } });
491/**
492 * A selection model that contains a single date.
493 * @docs-private
494 */
495class MatSingleDateSelectionModel extends MatDateSelectionModel {
496 constructor(adapter) {
497 super(null, adapter);
498 }
499 /**
500 * Adds a date to the current selection. In the case of a single date selection, the added date
501 * simply overwrites the previous selection
502 */
503 add(date) {
504 super.updateSelection(date, this);
505 }
506 /** Checks whether the current selection is valid. */
507 isValid() {
508 return this.selection != null && this._isValidDateInstance(this.selection);
509 }
510 /**
511 * Checks whether the current selection is complete. In the case of a single date selection, this
512 * is true if the current selection is not null.
513 */
514 isComplete() {
515 return this.selection != null;
516 }
517 /** Clones the selection model. */
518 clone() {
519 const clone = new MatSingleDateSelectionModel(this._adapter);
520 clone.updateSelection(this.selection, this);
521 return clone;
522 }
523}
524MatSingleDateSelectionModel.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatSingleDateSelectionModel, deps: [{ token: i1$1.DateAdapter }], target: i0.ɵɵFactoryTarget.Injectable });
525MatSingleDateSelectionModel.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatSingleDateSelectionModel });
526i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatSingleDateSelectionModel, decorators: [{
527 type: Injectable
528 }], ctorParameters: function () { return [{ type: i1$1.DateAdapter }]; } });
529/**
530 * A selection model that contains a date range.
531 * @docs-private
532 */
533class MatRangeDateSelectionModel extends MatDateSelectionModel {
534 constructor(adapter) {
535 super(new DateRange(null, null), adapter);
536 }
537 /**
538 * Adds a date to the current selection. In the case of a date range selection, the added date
539 * fills in the next `null` value in the range. If both the start and the end already have a date,
540 * the selection is reset so that the given date is the new `start` and the `end` is null.
541 */
542 add(date) {
543 let { start, end } = this.selection;
544 if (start == null) {
545 start = date;
546 }
547 else if (end == null) {
548 end = date;
549 }
550 else {
551 start = date;
552 end = null;
553 }
554 super.updateSelection(new DateRange(start, end), this);
555 }
556 /** Checks whether the current selection is valid. */
557 isValid() {
558 const { start, end } = this.selection;
559 // Empty ranges are valid.
560 if (start == null && end == null) {
561 return true;
562 }
563 // Complete ranges are only valid if both dates are valid and the start is before the end.
564 if (start != null && end != null) {
565 return (this._isValidDateInstance(start) &&
566 this._isValidDateInstance(end) &&
567 this._adapter.compareDate(start, end) <= 0);
568 }
569 // Partial ranges are valid if the start/end is valid.
570 return ((start == null || this._isValidDateInstance(start)) &&
571 (end == null || this._isValidDateInstance(end)));
572 }
573 /**
574 * Checks whether the current selection is complete. In the case of a date range selection, this
575 * is true if the current selection has a non-null `start` and `end`.
576 */
577 isComplete() {
578 return this.selection.start != null && this.selection.end != null;
579 }
580 /** Clones the selection model. */
581 clone() {
582 const clone = new MatRangeDateSelectionModel(this._adapter);
583 clone.updateSelection(this.selection, this);
584 return clone;
585 }
586}
587MatRangeDateSelectionModel.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatRangeDateSelectionModel, deps: [{ token: i1$1.DateAdapter }], target: i0.ɵɵFactoryTarget.Injectable });
588MatRangeDateSelectionModel.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatRangeDateSelectionModel });
589i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatRangeDateSelectionModel, decorators: [{
590 type: Injectable
591 }], ctorParameters: function () { return [{ type: i1$1.DateAdapter }]; } });
592/** @docs-private */
593function MAT_SINGLE_DATE_SELECTION_MODEL_FACTORY(parent, adapter) {
594 return parent || new MatSingleDateSelectionModel(adapter);
595}
596/**
597 * Used to provide a single selection model to a component.
598 * @docs-private
599 */
600const MAT_SINGLE_DATE_SELECTION_MODEL_PROVIDER = {
601 provide: MatDateSelectionModel,
602 deps: [[new Optional(), new SkipSelf(), MatDateSelectionModel], DateAdapter],
603 useFactory: MAT_SINGLE_DATE_SELECTION_MODEL_FACTORY,
604};
605/** @docs-private */
606function MAT_RANGE_DATE_SELECTION_MODEL_FACTORY(parent, adapter) {
607 return parent || new MatRangeDateSelectionModel(adapter);
608}
609/**
610 * Used to provide a range selection model to a component.
611 * @docs-private
612 */
613const MAT_RANGE_DATE_SELECTION_MODEL_PROVIDER = {
614 provide: MatDateSelectionModel,
615 deps: [[new Optional(), new SkipSelf(), MatDateSelectionModel], DateAdapter],
616 useFactory: MAT_RANGE_DATE_SELECTION_MODEL_FACTORY,
617};
618
619/**
620 * @license
621 * Copyright Google LLC All Rights Reserved.
622 *
623 * Use of this source code is governed by an MIT-style license that can be
624 * found in the LICENSE file at https://angular.io/license
625 */
626/** Injection token used to customize the date range selection behavior. */
627const MAT_DATE_RANGE_SELECTION_STRATEGY = new InjectionToken('MAT_DATE_RANGE_SELECTION_STRATEGY');
628/** Provides the default date range selection behavior. */
629class DefaultMatCalendarRangeStrategy {
630 constructor(_dateAdapter) {
631 this._dateAdapter = _dateAdapter;
632 }
633 selectionFinished(date, currentRange) {
634 let { start, end } = currentRange;
635 if (start == null) {
636 start = date;
637 }
638 else if (end == null && date && this._dateAdapter.compareDate(date, start) >= 0) {
639 end = date;
640 }
641 else {
642 start = date;
643 end = null;
644 }
645 return new DateRange(start, end);
646 }
647 createPreview(activeDate, currentRange) {
648 let start = null;
649 let end = null;
650 if (currentRange.start && !currentRange.end && activeDate) {
651 start = currentRange.start;
652 end = activeDate;
653 }
654 return new DateRange(start, end);
655 }
656}
657DefaultMatCalendarRangeStrategy.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DefaultMatCalendarRangeStrategy, deps: [{ token: i1$1.DateAdapter }], target: i0.ɵɵFactoryTarget.Injectable });
658DefaultMatCalendarRangeStrategy.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DefaultMatCalendarRangeStrategy });
659i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DefaultMatCalendarRangeStrategy, decorators: [{
660 type: Injectable
661 }], ctorParameters: function () { return [{ type: i1$1.DateAdapter }]; } });
662/** @docs-private */
663function MAT_CALENDAR_RANGE_STRATEGY_PROVIDER_FACTORY(parent, adapter) {
664 return parent || new DefaultMatCalendarRangeStrategy(adapter);
665}
666/** @docs-private */
667const MAT_CALENDAR_RANGE_STRATEGY_PROVIDER = {
668 provide: MAT_DATE_RANGE_SELECTION_STRATEGY,
669 deps: [[new Optional(), new SkipSelf(), MAT_DATE_RANGE_SELECTION_STRATEGY], DateAdapter],
670 useFactory: MAT_CALENDAR_RANGE_STRATEGY_PROVIDER_FACTORY,
671};
672
673/**
674 * @license
675 * Copyright Google LLC All Rights Reserved.
676 *
677 * Use of this source code is governed by an MIT-style license that can be
678 * found in the LICENSE file at https://angular.io/license
679 */
680const DAYS_PER_WEEK = 7;
681/**
682 * An internal component used to display a single month in the datepicker.
683 * @docs-private
684 */
685class MatMonthView {
686 constructor(_changeDetectorRef, _dateFormats, _dateAdapter, _dir, _rangeStrategy) {
687 this._changeDetectorRef = _changeDetectorRef;
688 this._dateFormats = _dateFormats;
689 this._dateAdapter = _dateAdapter;
690 this._dir = _dir;
691 this._rangeStrategy = _rangeStrategy;
692 this._rerenderSubscription = Subscription.EMPTY;
693 /** Emits when a new date is selected. */
694 this.selectedChange = new EventEmitter();
695 /** Emits when any date is selected. */
696 this._userSelection = new EventEmitter();
697 /** Emits when any date is activated. */
698 this.activeDateChange = new EventEmitter();
699 if (typeof ngDevMode === 'undefined' || ngDevMode) {
700 if (!this._dateAdapter) {
701 throw createMissingDateImplError('DateAdapter');
702 }
703 if (!this._dateFormats) {
704 throw createMissingDateImplError('MAT_DATE_FORMATS');
705 }
706 }
707 this._activeDate = this._dateAdapter.today();
708 }
709 /**
710 * The date to display in this month view (everything other than the month and year is ignored).
711 */
712 get activeDate() {
713 return this._activeDate;
714 }
715 set activeDate(value) {
716 const oldActiveDate = this._activeDate;
717 const validDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)) ||
718 this._dateAdapter.today();
719 this._activeDate = this._dateAdapter.clampDate(validDate, this.minDate, this.maxDate);
720 if (!this._hasSameMonthAndYear(oldActiveDate, this._activeDate)) {
721 this._init();
722 }
723 }
724 /** The currently selected date. */
725 get selected() {
726 return this._selected;
727 }
728 set selected(value) {
729 if (value instanceof DateRange) {
730 this._selected = value;
731 }
732 else {
733 this._selected = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
734 }
735 this._setRanges(this._selected);
736 }
737 /** The minimum selectable date. */
738 get minDate() {
739 return this._minDate;
740 }
741 set minDate(value) {
742 this._minDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
743 }
744 /** The maximum selectable date. */
745 get maxDate() {
746 return this._maxDate;
747 }
748 set maxDate(value) {
749 this._maxDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
750 }
751 ngAfterContentInit() {
752 this._rerenderSubscription = this._dateAdapter.localeChanges
753 .pipe(startWith(null))
754 .subscribe(() => this._init());
755 }
756 ngOnChanges(changes) {
757 const comparisonChange = changes['comparisonStart'] || changes['comparisonEnd'];
758 if (comparisonChange && !comparisonChange.firstChange) {
759 this._setRanges(this.selected);
760 }
761 }
762 ngOnDestroy() {
763 this._rerenderSubscription.unsubscribe();
764 }
765 /** Handles when a new date is selected. */
766 _dateSelected(event) {
767 const date = event.value;
768 const selectedDate = this._getDateFromDayOfMonth(date);
769 let rangeStartDate;
770 let rangeEndDate;
771 if (this._selected instanceof DateRange) {
772 rangeStartDate = this._getDateInCurrentMonth(this._selected.start);
773 rangeEndDate = this._getDateInCurrentMonth(this._selected.end);
774 }
775 else {
776 rangeStartDate = rangeEndDate = this._getDateInCurrentMonth(this._selected);
777 }
778 if (rangeStartDate !== date || rangeEndDate !== date) {
779 this.selectedChange.emit(selectedDate);
780 }
781 this._userSelection.emit({ value: selectedDate, event: event.event });
782 this._previewStart = this._previewEnd = null;
783 this._changeDetectorRef.markForCheck();
784 }
785 /**
786 * Takes the index of a calendar body cell wrapped in in an event as argument. For the date that
787 * corresponds to the given cell, set `activeDate` to that date and fire `activeDateChange` with
788 * that date.
789 *
790 * This fucntion is used to match each component's model of the active date with the calendar
791 * body cell that was focused. It updates its value of `activeDate` synchronously and updates the
792 * parent's value asynchonously via the `activeDateChange` event. The child component receives an
793 * updated value asynchronously via the `activeCell` Input.
794 */
795 _updateActiveDate(event) {
796 const month = event.value;
797 const oldActiveDate = this._activeDate;
798 this.activeDate = this._getDateFromDayOfMonth(month);
799 if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {
800 this.activeDateChange.emit(this._activeDate);
801 }
802 }
803 /** Handles keydown events on the calendar body when calendar is in month view. */
804 _handleCalendarBodyKeydown(event) {
805 // TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent
806 // disabled ones from being selected. This may not be ideal, we should look into whether
807 // navigation should skip over disabled dates, and if so, how to implement that efficiently.
808 const oldActiveDate = this._activeDate;
809 const isRtl = this._isRtl();
810 switch (event.keyCode) {
811 case LEFT_ARROW:
812 this.activeDate = this._dateAdapter.addCalendarDays(this._activeDate, isRtl ? 1 : -1);
813 break;
814 case RIGHT_ARROW:
815 this.activeDate = this._dateAdapter.addCalendarDays(this._activeDate, isRtl ? -1 : 1);
816 break;
817 case UP_ARROW:
818 this.activeDate = this._dateAdapter.addCalendarDays(this._activeDate, -7);
819 break;
820 case DOWN_ARROW:
821 this.activeDate = this._dateAdapter.addCalendarDays(this._activeDate, 7);
822 break;
823 case HOME:
824 this.activeDate = this._dateAdapter.addCalendarDays(this._activeDate, 1 - this._dateAdapter.getDate(this._activeDate));
825 break;
826 case END:
827 this.activeDate = this._dateAdapter.addCalendarDays(this._activeDate, this._dateAdapter.getNumDaysInMonth(this._activeDate) -
828 this._dateAdapter.getDate(this._activeDate));
829 break;
830 case PAGE_UP:
831 this.activeDate = event.altKey
832 ? this._dateAdapter.addCalendarYears(this._activeDate, -1)
833 : this._dateAdapter.addCalendarMonths(this._activeDate, -1);
834 break;
835 case PAGE_DOWN:
836 this.activeDate = event.altKey
837 ? this._dateAdapter.addCalendarYears(this._activeDate, 1)
838 : this._dateAdapter.addCalendarMonths(this._activeDate, 1);
839 break;
840 case ENTER:
841 case SPACE:
842 this._selectionKeyPressed = true;
843 if (this._canSelect(this._activeDate)) {
844 // Prevent unexpected default actions such as form submission.
845 // Note that we only prevent the default action here while the selection happens in
846 // `keyup` below. We can't do the selection here, because it can cause the calendar to
847 // reopen if focus is restored immediately. We also can't call `preventDefault` on `keyup`
848 // because it's too late (see #23305).
849 event.preventDefault();
850 }
851 return;
852 case ESCAPE:
853 // Abort the current range selection if the user presses escape mid-selection.
854 if (this._previewEnd != null && !hasModifierKey(event)) {
855 this._previewStart = this._previewEnd = null;
856 this.selectedChange.emit(null);
857 this._userSelection.emit({ value: null, event });
858 event.preventDefault();
859 event.stopPropagation(); // Prevents the overlay from closing.
860 }
861 return;
862 default:
863 // Don't prevent default or focus active cell on keys that we don't explicitly handle.
864 return;
865 }
866 if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {
867 this.activeDateChange.emit(this.activeDate);
868 this._focusActiveCellAfterViewChecked();
869 }
870 // Prevent unexpected default actions such as form submission.
871 event.preventDefault();
872 }
873 /** Handles keyup events on the calendar body when calendar is in month view. */
874 _handleCalendarBodyKeyup(event) {
875 if (event.keyCode === SPACE || event.keyCode === ENTER) {
876 if (this._selectionKeyPressed && this._canSelect(this._activeDate)) {
877 this._dateSelected({ value: this._dateAdapter.getDate(this._activeDate), event });
878 }
879 this._selectionKeyPressed = false;
880 }
881 }
882 /** Initializes this month view. */
883 _init() {
884 this._setRanges(this.selected);
885 this._todayDate = this._getCellCompareValue(this._dateAdapter.today());
886 this._monthLabel = this._dateFormats.display.monthLabel
887 ? this._dateAdapter.format(this.activeDate, this._dateFormats.display.monthLabel)
888 : this._dateAdapter
889 .getMonthNames('short')[this._dateAdapter.getMonth(this.activeDate)].toLocaleUpperCase();
890 let firstOfMonth = this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), this._dateAdapter.getMonth(this.activeDate), 1);
891 this._firstWeekOffset =
892 (DAYS_PER_WEEK +
893 this._dateAdapter.getDayOfWeek(firstOfMonth) -
894 this._dateAdapter.getFirstDayOfWeek()) %
895 DAYS_PER_WEEK;
896 this._initWeekdays();
897 this._createWeekCells();
898 this._changeDetectorRef.markForCheck();
899 }
900 /** Focuses the active cell after the microtask queue is empty. */
901 _focusActiveCell(movePreview) {
902 this._matCalendarBody._focusActiveCell(movePreview);
903 }
904 /** Focuses the active cell after change detection has run and the microtask queue is empty. */
905 _focusActiveCellAfterViewChecked() {
906 this._matCalendarBody._scheduleFocusActiveCellAfterViewChecked();
907 }
908 /** Called when the user has activated a new cell and the preview needs to be updated. */
909 _previewChanged({ event, value: cell }) {
910 if (this._rangeStrategy) {
911 // We can assume that this will be a range, because preview
912 // events aren't fired for single date selections.
913 const value = cell ? cell.rawValue : null;
914 const previewRange = this._rangeStrategy.createPreview(value, this.selected, event);
915 this._previewStart = this._getCellCompareValue(previewRange.start);
916 this._previewEnd = this._getCellCompareValue(previewRange.end);
917 // Note that here we need to use `detectChanges`, rather than `markForCheck`, because
918 // the way `_focusActiveCell` is set up at the moment makes it fire at the wrong time
919 // when navigating one month back using the keyboard which will cause this handler
920 // to throw a "changed after checked" error when updating the preview state.
921 this._changeDetectorRef.detectChanges();
922 }
923 }
924 /**
925 * Takes a day of the month and returns a new date in the same month and year as the currently
926 * active date. The returned date will have the same day of the month as the argument date.
927 */
928 _getDateFromDayOfMonth(dayOfMonth) {
929 return this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), this._dateAdapter.getMonth(this.activeDate), dayOfMonth);
930 }
931 /** Initializes the weekdays. */
932 _initWeekdays() {
933 const firstDayOfWeek = this._dateAdapter.getFirstDayOfWeek();
934 const narrowWeekdays = this._dateAdapter.getDayOfWeekNames('narrow');
935 const longWeekdays = this._dateAdapter.getDayOfWeekNames('long');
936 // Rotate the labels for days of the week based on the configured first day of the week.
937 let weekdays = longWeekdays.map((long, i) => {
938 return { long, narrow: narrowWeekdays[i] };
939 });
940 this._weekdays = weekdays.slice(firstDayOfWeek).concat(weekdays.slice(0, firstDayOfWeek));
941 }
942 /** Creates MatCalendarCells for the dates in this month. */
943 _createWeekCells() {
944 const daysInMonth = this._dateAdapter.getNumDaysInMonth(this.activeDate);
945 const dateNames = this._dateAdapter.getDateNames();
946 this._weeks = [[]];
947 for (let i = 0, cell = this._firstWeekOffset; i < daysInMonth; i++, cell++) {
948 if (cell == DAYS_PER_WEEK) {
949 this._weeks.push([]);
950 cell = 0;
951 }
952 const date = this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), this._dateAdapter.getMonth(this.activeDate), i + 1);
953 const enabled = this._shouldEnableDate(date);
954 const ariaLabel = this._dateAdapter.format(date, this._dateFormats.display.dateA11yLabel);
955 const cellClasses = this.dateClass ? this.dateClass(date, 'month') : undefined;
956 this._weeks[this._weeks.length - 1].push(new MatCalendarCell(i + 1, dateNames[i], ariaLabel, enabled, cellClasses, this._getCellCompareValue(date), date));
957 }
958 }
959 /** Date filter for the month */
960 _shouldEnableDate(date) {
961 return (!!date &&
962 (!this.minDate || this._dateAdapter.compareDate(date, this.minDate) >= 0) &&
963 (!this.maxDate || this._dateAdapter.compareDate(date, this.maxDate) <= 0) &&
964 (!this.dateFilter || this.dateFilter(date)));
965 }
966 /**
967 * Gets the date in this month that the given Date falls on.
968 * Returns null if the given Date is in another month.
969 */
970 _getDateInCurrentMonth(date) {
971 return date && this._hasSameMonthAndYear(date, this.activeDate)
972 ? this._dateAdapter.getDate(date)
973 : null;
974 }
975 /** Checks whether the 2 dates are non-null and fall within the same month of the same year. */
976 _hasSameMonthAndYear(d1, d2) {
977 return !!(d1 &&
978 d2 &&
979 this._dateAdapter.getMonth(d1) == this._dateAdapter.getMonth(d2) &&
980 this._dateAdapter.getYear(d1) == this._dateAdapter.getYear(d2));
981 }
982 /** Gets the value that will be used to one cell to another. */
983 _getCellCompareValue(date) {
984 if (date) {
985 // We use the time since the Unix epoch to compare dates in this view, rather than the
986 // cell values, because we need to support ranges that span across multiple months/years.
987 const year = this._dateAdapter.getYear(date);
988 const month = this._dateAdapter.getMonth(date);
989 const day = this._dateAdapter.getDate(date);
990 return new Date(year, month, day).getTime();
991 }
992 return null;
993 }
994 /** Determines whether the user has the RTL layout direction. */
995 _isRtl() {
996 return this._dir && this._dir.value === 'rtl';
997 }
998 /** Sets the current range based on a model value. */
999 _setRanges(selectedValue) {
1000 if (selectedValue instanceof DateRange) {
1001 this._rangeStart = this._getCellCompareValue(selectedValue.start);
1002 this._rangeEnd = this._getCellCompareValue(selectedValue.end);
1003 this._isRange = true;
1004 }
1005 else {
1006 this._rangeStart = this._rangeEnd = this._getCellCompareValue(selectedValue);
1007 this._isRange = false;
1008 }
1009 this._comparisonRangeStart = this._getCellCompareValue(this.comparisonStart);
1010 this._comparisonRangeEnd = this._getCellCompareValue(this.comparisonEnd);
1011 }
1012 /** Gets whether a date can be selected in the month view. */
1013 _canSelect(date) {
1014 return !this.dateFilter || this.dateFilter(date);
1015 }
1016}
1017MatMonthView.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatMonthView, deps: [{ token: i0.ChangeDetectorRef }, { token: MAT_DATE_FORMATS, optional: true }, { token: i1$1.DateAdapter, optional: true }, { token: i2.Directionality, optional: true }, { token: MAT_DATE_RANGE_SELECTION_STRATEGY, optional: true }], target: i0.ɵɵFactoryTarget.Component });
1018MatMonthView.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatMonthView, selector: "mat-month-view", inputs: { activeDate: "activeDate", selected: "selected", minDate: "minDate", maxDate: "maxDate", dateFilter: "dateFilter", dateClass: "dateClass", comparisonStart: "comparisonStart", comparisonEnd: "comparisonEnd" }, outputs: { selectedChange: "selectedChange", _userSelection: "_userSelection", activeDateChange: "activeDateChange" }, viewQueries: [{ propertyName: "_matCalendarBody", first: true, predicate: MatCalendarBody, descendants: true }], exportAs: ["matMonthView"], usesOnChanges: true, ngImport: i0, template: "<table class=\"mat-calendar-table\" role=\"grid\">\n <thead class=\"mat-calendar-table-header\">\n <tr>\n <th scope=\"col\" *ngFor=\"let day of _weekdays\">\n <span class=\"cdk-visually-hidden\">{{day.long}}</span>\n <span aria-hidden=\"true\">{{day.narrow}}</span>\n </th>\n </tr>\n <tr><th aria-hidden=\"true\" class=\"mat-calendar-table-header-divider\" colspan=\"7\"></th></tr>\n </thead>\n <tbody mat-calendar-body\n [label]=\"_monthLabel\"\n [rows]=\"_weeks\"\n [todayValue]=\"_todayDate!\"\n [startValue]=\"_rangeStart!\"\n [endValue]=\"_rangeEnd!\"\n [comparisonStart]=\"_comparisonRangeStart\"\n [comparisonEnd]=\"_comparisonRangeEnd\"\n [previewStart]=\"_previewStart\"\n [previewEnd]=\"_previewEnd\"\n [isRange]=\"_isRange\"\n [labelMinRequiredCells]=\"3\"\n [activeCell]=\"_dateAdapter.getDate(activeDate) - 1\"\n (selectedValueChange)=\"_dateSelected($event)\"\n (activeDateChange)=\"_updateActiveDate($event)\"\n (previewChange)=\"_previewChanged($event)\"\n (keyup)=\"_handleCalendarBodyKeyup($event)\"\n (keydown)=\"_handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n", dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: MatCalendarBody, selector: "[mat-calendar-body]", inputs: ["label", "rows", "todayValue", "startValue", "endValue", "labelMinRequiredCells", "numCols", "activeCell", "isRange", "cellAspectRatio", "comparisonStart", "comparisonEnd", "previewStart", "previewEnd"], outputs: ["selectedValueChange", "previewChange", "activeDateChange"], exportAs: ["matCalendarBody"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1019i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatMonthView, decorators: [{
1020 type: Component,
1021 args: [{ selector: 'mat-month-view', exportAs: 'matMonthView', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<table class=\"mat-calendar-table\" role=\"grid\">\n <thead class=\"mat-calendar-table-header\">\n <tr>\n <th scope=\"col\" *ngFor=\"let day of _weekdays\">\n <span class=\"cdk-visually-hidden\">{{day.long}}</span>\n <span aria-hidden=\"true\">{{day.narrow}}</span>\n </th>\n </tr>\n <tr><th aria-hidden=\"true\" class=\"mat-calendar-table-header-divider\" colspan=\"7\"></th></tr>\n </thead>\n <tbody mat-calendar-body\n [label]=\"_monthLabel\"\n [rows]=\"_weeks\"\n [todayValue]=\"_todayDate!\"\n [startValue]=\"_rangeStart!\"\n [endValue]=\"_rangeEnd!\"\n [comparisonStart]=\"_comparisonRangeStart\"\n [comparisonEnd]=\"_comparisonRangeEnd\"\n [previewStart]=\"_previewStart\"\n [previewEnd]=\"_previewEnd\"\n [isRange]=\"_isRange\"\n [labelMinRequiredCells]=\"3\"\n [activeCell]=\"_dateAdapter.getDate(activeDate) - 1\"\n (selectedValueChange)=\"_dateSelected($event)\"\n (activeDateChange)=\"_updateActiveDate($event)\"\n (previewChange)=\"_previewChanged($event)\"\n (keyup)=\"_handleCalendarBodyKeyup($event)\"\n (keydown)=\"_handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n" }]
1022 }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
1023 type: Optional
1024 }, {
1025 type: Inject,
1026 args: [MAT_DATE_FORMATS]
1027 }] }, { type: i1$1.DateAdapter, decorators: [{
1028 type: Optional
1029 }] }, { type: i2.Directionality, decorators: [{
1030 type: Optional
1031 }] }, { type: undefined, decorators: [{
1032 type: Inject,
1033 args: [MAT_DATE_RANGE_SELECTION_STRATEGY]
1034 }, {
1035 type: Optional
1036 }] }]; }, propDecorators: { activeDate: [{
1037 type: Input
1038 }], selected: [{
1039 type: Input
1040 }], minDate: [{
1041 type: Input
1042 }], maxDate: [{
1043 type: Input
1044 }], dateFilter: [{
1045 type: Input
1046 }], dateClass: [{
1047 type: Input
1048 }], comparisonStart: [{
1049 type: Input
1050 }], comparisonEnd: [{
1051 type: Input
1052 }], selectedChange: [{
1053 type: Output
1054 }], _userSelection: [{
1055 type: Output
1056 }], activeDateChange: [{
1057 type: Output
1058 }], _matCalendarBody: [{
1059 type: ViewChild,
1060 args: [MatCalendarBody]
1061 }] } });
1062
1063/**
1064 * @license
1065 * Copyright Google LLC All Rights Reserved.
1066 *
1067 * Use of this source code is governed by an MIT-style license that can be
1068 * found in the LICENSE file at https://angular.io/license
1069 */
1070const yearsPerPage = 24;
1071const yearsPerRow = 4;
1072/**
1073 * An internal component used to display a year selector in the datepicker.
1074 * @docs-private
1075 */
1076class MatMultiYearView {
1077 constructor(_changeDetectorRef, _dateAdapter, _dir) {
1078 this._changeDetectorRef = _changeDetectorRef;
1079 this._dateAdapter = _dateAdapter;
1080 this._dir = _dir;
1081 this._rerenderSubscription = Subscription.EMPTY;
1082 /** Emits when a new year is selected. */
1083 this.selectedChange = new EventEmitter();
1084 /** Emits the selected year. This doesn't imply a change on the selected date */
1085 this.yearSelected = new EventEmitter();
1086 /** Emits when any date is activated. */
1087 this.activeDateChange = new EventEmitter();
1088 if (!this._dateAdapter && (typeof ngDevMode === 'undefined' || ngDevMode)) {
1089 throw createMissingDateImplError('DateAdapter');
1090 }
1091 this._activeDate = this._dateAdapter.today();
1092 }
1093 /** The date to display in this multi-year view (everything other than the year is ignored). */
1094 get activeDate() {
1095 return this._activeDate;
1096 }
1097 set activeDate(value) {
1098 let oldActiveDate = this._activeDate;
1099 const validDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)) ||
1100 this._dateAdapter.today();
1101 this._activeDate = this._dateAdapter.clampDate(validDate, this.minDate, this.maxDate);
1102 if (!isSameMultiYearView(this._dateAdapter, oldActiveDate, this._activeDate, this.minDate, this.maxDate)) {
1103 this._init();
1104 }
1105 }
1106 /** The currently selected date. */
1107 get selected() {
1108 return this._selected;
1109 }
1110 set selected(value) {
1111 if (value instanceof DateRange) {
1112 this._selected = value;
1113 }
1114 else {
1115 this._selected = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
1116 }
1117 this._setSelectedYear(value);
1118 }
1119 /** The minimum selectable date. */
1120 get minDate() {
1121 return this._minDate;
1122 }
1123 set minDate(value) {
1124 this._minDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
1125 }
1126 /** The maximum selectable date. */
1127 get maxDate() {
1128 return this._maxDate;
1129 }
1130 set maxDate(value) {
1131 this._maxDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
1132 }
1133 ngAfterContentInit() {
1134 this._rerenderSubscription = this._dateAdapter.localeChanges
1135 .pipe(startWith(null))
1136 .subscribe(() => this._init());
1137 }
1138 ngOnDestroy() {
1139 this._rerenderSubscription.unsubscribe();
1140 }
1141 /** Initializes this multi-year view. */
1142 _init() {
1143 this._todayYear = this._dateAdapter.getYear(this._dateAdapter.today());
1144 // We want a range years such that we maximize the number of
1145 // enabled dates visible at once. This prevents issues where the minimum year
1146 // is the last item of a page OR the maximum year is the first item of a page.
1147 // The offset from the active year to the "slot" for the starting year is the
1148 // *actual* first rendered year in the multi-year view.
1149 const activeYear = this._dateAdapter.getYear(this._activeDate);
1150 const minYearOfPage = activeYear - getActiveOffset(this._dateAdapter, this.activeDate, this.minDate, this.maxDate);
1151 this._years = [];
1152 for (let i = 0, row = []; i < yearsPerPage; i++) {
1153 row.push(minYearOfPage + i);
1154 if (row.length == yearsPerRow) {
1155 this._years.push(row.map(year => this._createCellForYear(year)));
1156 row = [];
1157 }
1158 }
1159 this._changeDetectorRef.markForCheck();
1160 }
1161 /** Handles when a new year is selected. */
1162 _yearSelected(event) {
1163 const year = event.value;
1164 const selectedYear = this._dateAdapter.createDate(year, 0, 1);
1165 const selectedDate = this._getDateFromYear(year);
1166 this.yearSelected.emit(selectedYear);
1167 this.selectedChange.emit(selectedDate);
1168 }
1169 /**
1170 * Takes the index of a calendar body cell wrapped in in an event as argument. For the date that
1171 * corresponds to the given cell, set `activeDate` to that date and fire `activeDateChange` with
1172 * that date.
1173 *
1174 * This fucntion is used to match each component's model of the active date with the calendar
1175 * body cell that was focused. It updates its value of `activeDate` synchronously and updates the
1176 * parent's value asynchonously via the `activeDateChange` event. The child component receives an
1177 * updated value asynchronously via the `activeCell` Input.
1178 */
1179 _updateActiveDate(event) {
1180 const year = event.value;
1181 const oldActiveDate = this._activeDate;
1182 this.activeDate = this._getDateFromYear(year);
1183 if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {
1184 this.activeDateChange.emit(this.activeDate);
1185 }
1186 }
1187 /** Handles keydown events on the calendar body when calendar is in multi-year view. */
1188 _handleCalendarBodyKeydown(event) {
1189 const oldActiveDate = this._activeDate;
1190 const isRtl = this._isRtl();
1191 switch (event.keyCode) {
1192 case LEFT_ARROW:
1193 this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, isRtl ? 1 : -1);
1194 break;
1195 case RIGHT_ARROW:
1196 this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, isRtl ? -1 : 1);
1197 break;
1198 case UP_ARROW:
1199 this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, -yearsPerRow);
1200 break;
1201 case DOWN_ARROW:
1202 this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, yearsPerRow);
1203 break;
1204 case HOME:
1205 this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, -getActiveOffset(this._dateAdapter, this.activeDate, this.minDate, this.maxDate));
1206 break;
1207 case END:
1208 this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, yearsPerPage -
1209 getActiveOffset(this._dateAdapter, this.activeDate, this.minDate, this.maxDate) -
1210 1);
1211 break;
1212 case PAGE_UP:
1213 this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, event.altKey ? -yearsPerPage * 10 : -yearsPerPage);
1214 break;
1215 case PAGE_DOWN:
1216 this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, event.altKey ? yearsPerPage * 10 : yearsPerPage);
1217 break;
1218 case ENTER:
1219 case SPACE:
1220 // Note that we only prevent the default action here while the selection happens in
1221 // `keyup` below. We can't do the selection here, because it can cause the calendar to
1222 // reopen if focus is restored immediately. We also can't call `preventDefault` on `keyup`
1223 // because it's too late (see #23305).
1224 this._selectionKeyPressed = true;
1225 break;
1226 default:
1227 // Don't prevent default or focus active cell on keys that we don't explicitly handle.
1228 return;
1229 }
1230 if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {
1231 this.activeDateChange.emit(this.activeDate);
1232 }
1233 this._focusActiveCellAfterViewChecked();
1234 // Prevent unexpected default actions such as form submission.
1235 event.preventDefault();
1236 }
1237 /** Handles keyup events on the calendar body when calendar is in multi-year view. */
1238 _handleCalendarBodyKeyup(event) {
1239 if (event.keyCode === SPACE || event.keyCode === ENTER) {
1240 if (this._selectionKeyPressed) {
1241 this._yearSelected({ value: this._dateAdapter.getYear(this._activeDate), event });
1242 }
1243 this._selectionKeyPressed = false;
1244 }
1245 }
1246 _getActiveCell() {
1247 return getActiveOffset(this._dateAdapter, this.activeDate, this.minDate, this.maxDate);
1248 }
1249 /** Focuses the active cell after the microtask queue is empty. */
1250 _focusActiveCell() {
1251 this._matCalendarBody._focusActiveCell();
1252 }
1253 /** Focuses the active cell after change detection has run and the microtask queue is empty. */
1254 _focusActiveCellAfterViewChecked() {
1255 this._matCalendarBody._scheduleFocusActiveCellAfterViewChecked();
1256 }
1257 /**
1258 * Takes a year and returns a new date on the same day and month as the currently active date
1259 * The returned date will have the same year as the argument date.
1260 */
1261 _getDateFromYear(year) {
1262 const activeMonth = this._dateAdapter.getMonth(this.activeDate);
1263 const daysInMonth = this._dateAdapter.getNumDaysInMonth(this._dateAdapter.createDate(year, activeMonth, 1));
1264 const normalizedDate = this._dateAdapter.createDate(year, activeMonth, Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth));
1265 return normalizedDate;
1266 }
1267 /** Creates an MatCalendarCell for the given year. */
1268 _createCellForYear(year) {
1269 const date = this._dateAdapter.createDate(year, 0, 1);
1270 const yearName = this._dateAdapter.getYearName(date);
1271 const cellClasses = this.dateClass ? this.dateClass(date, 'multi-year') : undefined;
1272 return new MatCalendarCell(year, yearName, yearName, this._shouldEnableYear(year), cellClasses);
1273 }
1274 /** Whether the given year is enabled. */
1275 _shouldEnableYear(year) {
1276 // disable if the year is greater than maxDate lower than minDate
1277 if (year === undefined ||
1278 year === null ||
1279 (this.maxDate && year > this._dateAdapter.getYear(this.maxDate)) ||
1280 (this.minDate && year < this._dateAdapter.getYear(this.minDate))) {
1281 return false;
1282 }
1283 // enable if it reaches here and there's no filter defined
1284 if (!this.dateFilter) {
1285 return true;
1286 }
1287 const firstOfYear = this._dateAdapter.createDate(year, 0, 1);
1288 // If any date in the year is enabled count the year as enabled.
1289 for (let date = firstOfYear; this._dateAdapter.getYear(date) == year; date = this._dateAdapter.addCalendarDays(date, 1)) {
1290 if (this.dateFilter(date)) {
1291 return true;
1292 }
1293 }
1294 return false;
1295 }
1296 /** Determines whether the user has the RTL layout direction. */
1297 _isRtl() {
1298 return this._dir && this._dir.value === 'rtl';
1299 }
1300 /** Sets the currently-highlighted year based on a model value. */
1301 _setSelectedYear(value) {
1302 this._selectedYear = null;
1303 if (value instanceof DateRange) {
1304 const displayValue = value.start || value.end;
1305 if (displayValue) {
1306 this._selectedYear = this._dateAdapter.getYear(displayValue);
1307 }
1308 }
1309 else if (value) {
1310 this._selectedYear = this._dateAdapter.getYear(value);
1311 }
1312 }
1313}
1314MatMultiYearView.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatMultiYearView, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$1.DateAdapter, optional: true }, { token: i2.Directionality, optional: true }], target: i0.ɵɵFactoryTarget.Component });
1315MatMultiYearView.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatMultiYearView, selector: "mat-multi-year-view", inputs: { activeDate: "activeDate", selected: "selected", minDate: "minDate", maxDate: "maxDate", dateFilter: "dateFilter", dateClass: "dateClass" }, outputs: { selectedChange: "selectedChange", yearSelected: "yearSelected", activeDateChange: "activeDateChange" }, viewQueries: [{ propertyName: "_matCalendarBody", first: true, predicate: MatCalendarBody, descendants: true }], exportAs: ["matMultiYearView"], ngImport: i0, template: "<table class=\"mat-calendar-table\" role=\"grid\">\n <thead aria-hidden=\"true\" class=\"mat-calendar-table-header\">\n <tr><th class=\"mat-calendar-table-header-divider\" colspan=\"4\"></th></tr>\n </thead>\n <tbody mat-calendar-body\n [rows]=\"_years\"\n [todayValue]=\"_todayYear\"\n [startValue]=\"_selectedYear!\"\n [endValue]=\"_selectedYear!\"\n [numCols]=\"4\"\n [cellAspectRatio]=\"4 / 7\"\n [activeCell]=\"_getActiveCell()\"\n (selectedValueChange)=\"_yearSelected($event)\"\n (activeDateChange)=\"_updateActiveDate($event)\"\n (keyup)=\"_handleCalendarBodyKeyup($event)\"\n (keydown)=\"_handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n", dependencies: [{ kind: "component", type: MatCalendarBody, selector: "[mat-calendar-body]", inputs: ["label", "rows", "todayValue", "startValue", "endValue", "labelMinRequiredCells", "numCols", "activeCell", "isRange", "cellAspectRatio", "comparisonStart", "comparisonEnd", "previewStart", "previewEnd"], outputs: ["selectedValueChange", "previewChange", "activeDateChange"], exportAs: ["matCalendarBody"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1316i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatMultiYearView, decorators: [{
1317 type: Component,
1318 args: [{ selector: 'mat-multi-year-view', exportAs: 'matMultiYearView', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<table class=\"mat-calendar-table\" role=\"grid\">\n <thead aria-hidden=\"true\" class=\"mat-calendar-table-header\">\n <tr><th class=\"mat-calendar-table-header-divider\" colspan=\"4\"></th></tr>\n </thead>\n <tbody mat-calendar-body\n [rows]=\"_years\"\n [todayValue]=\"_todayYear\"\n [startValue]=\"_selectedYear!\"\n [endValue]=\"_selectedYear!\"\n [numCols]=\"4\"\n [cellAspectRatio]=\"4 / 7\"\n [activeCell]=\"_getActiveCell()\"\n (selectedValueChange)=\"_yearSelected($event)\"\n (activeDateChange)=\"_updateActiveDate($event)\"\n (keyup)=\"_handleCalendarBodyKeyup($event)\"\n (keydown)=\"_handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n" }]
1319 }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i1$1.DateAdapter, decorators: [{
1320 type: Optional
1321 }] }, { type: i2.Directionality, decorators: [{
1322 type: Optional
1323 }] }]; }, propDecorators: { activeDate: [{
1324 type: Input
1325 }], selected: [{
1326 type: Input
1327 }], minDate: [{
1328 type: Input
1329 }], maxDate: [{
1330 type: Input
1331 }], dateFilter: [{
1332 type: Input
1333 }], dateClass: [{
1334 type: Input
1335 }], selectedChange: [{
1336 type: Output
1337 }], yearSelected: [{
1338 type: Output
1339 }], activeDateChange: [{
1340 type: Output
1341 }], _matCalendarBody: [{
1342 type: ViewChild,
1343 args: [MatCalendarBody]
1344 }] } });
1345function isSameMultiYearView(dateAdapter, date1, date2, minDate, maxDate) {
1346 const year1 = dateAdapter.getYear(date1);
1347 const year2 = dateAdapter.getYear(date2);
1348 const startingYear = getStartingYear(dateAdapter, minDate, maxDate);
1349 return (Math.floor((year1 - startingYear) / yearsPerPage) ===
1350 Math.floor((year2 - startingYear) / yearsPerPage));
1351}
1352/**
1353 * When the multi-year view is first opened, the active year will be in view.
1354 * So we compute how many years are between the active year and the *slot* where our
1355 * "startingYear" will render when paged into view.
1356 */
1357function getActiveOffset(dateAdapter, activeDate, minDate, maxDate) {
1358 const activeYear = dateAdapter.getYear(activeDate);
1359 return euclideanModulo(activeYear - getStartingYear(dateAdapter, minDate, maxDate), yearsPerPage);
1360}
1361/**
1362 * We pick a "starting" year such that either the maximum year would be at the end
1363 * or the minimum year would be at the beginning of a page.
1364 */
1365function getStartingYear(dateAdapter, minDate, maxDate) {
1366 let startingYear = 0;
1367 if (maxDate) {
1368 const maxYear = dateAdapter.getYear(maxDate);
1369 startingYear = maxYear - yearsPerPage + 1;
1370 }
1371 else if (minDate) {
1372 startingYear = dateAdapter.getYear(minDate);
1373 }
1374 return startingYear;
1375}
1376/** Gets remainder that is non-negative, even if first number is negative */
1377function euclideanModulo(a, b) {
1378 return ((a % b) + b) % b;
1379}
1380
1381/**
1382 * @license
1383 * Copyright Google LLC All Rights Reserved.
1384 *
1385 * Use of this source code is governed by an MIT-style license that can be
1386 * found in the LICENSE file at https://angular.io/license
1387 */
1388/**
1389 * An internal component used to display a single year in the datepicker.
1390 * @docs-private
1391 */
1392class MatYearView {
1393 constructor(_changeDetectorRef, _dateFormats, _dateAdapter, _dir) {
1394 this._changeDetectorRef = _changeDetectorRef;
1395 this._dateFormats = _dateFormats;
1396 this._dateAdapter = _dateAdapter;
1397 this._dir = _dir;
1398 this._rerenderSubscription = Subscription.EMPTY;
1399 /** Emits when a new month is selected. */
1400 this.selectedChange = new EventEmitter();
1401 /** Emits the selected month. This doesn't imply a change on the selected date */
1402 this.monthSelected = new EventEmitter();
1403 /** Emits when any date is activated. */
1404 this.activeDateChange = new EventEmitter();
1405 if (typeof ngDevMode === 'undefined' || ngDevMode) {
1406 if (!this._dateAdapter) {
1407 throw createMissingDateImplError('DateAdapter');
1408 }
1409 if (!this._dateFormats) {
1410 throw createMissingDateImplError('MAT_DATE_FORMATS');
1411 }
1412 }
1413 this._activeDate = this._dateAdapter.today();
1414 }
1415 /** The date to display in this year view (everything other than the year is ignored). */
1416 get activeDate() {
1417 return this._activeDate;
1418 }
1419 set activeDate(value) {
1420 let oldActiveDate = this._activeDate;
1421 const validDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)) ||
1422 this._dateAdapter.today();
1423 this._activeDate = this._dateAdapter.clampDate(validDate, this.minDate, this.maxDate);
1424 if (this._dateAdapter.getYear(oldActiveDate) !== this._dateAdapter.getYear(this._activeDate)) {
1425 this._init();
1426 }
1427 }
1428 /** The currently selected date. */
1429 get selected() {
1430 return this._selected;
1431 }
1432 set selected(value) {
1433 if (value instanceof DateRange) {
1434 this._selected = value;
1435 }
1436 else {
1437 this._selected = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
1438 }
1439 this._setSelectedMonth(value);
1440 }
1441 /** The minimum selectable date. */
1442 get minDate() {
1443 return this._minDate;
1444 }
1445 set minDate(value) {
1446 this._minDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
1447 }
1448 /** The maximum selectable date. */
1449 get maxDate() {
1450 return this._maxDate;
1451 }
1452 set maxDate(value) {
1453 this._maxDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
1454 }
1455 ngAfterContentInit() {
1456 this._rerenderSubscription = this._dateAdapter.localeChanges
1457 .pipe(startWith(null))
1458 .subscribe(() => this._init());
1459 }
1460 ngOnDestroy() {
1461 this._rerenderSubscription.unsubscribe();
1462 }
1463 /** Handles when a new month is selected. */
1464 _monthSelected(event) {
1465 const month = event.value;
1466 const selectedMonth = this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), month, 1);
1467 this.monthSelected.emit(selectedMonth);
1468 const selectedDate = this._getDateFromMonth(month);
1469 this.selectedChange.emit(selectedDate);
1470 }
1471 /**
1472 * Takes the index of a calendar body cell wrapped in in an event as argument. For the date that
1473 * corresponds to the given cell, set `activeDate` to that date and fire `activeDateChange` with
1474 * that date.
1475 *
1476 * This fucntion is used to match each component's model of the active date with the calendar
1477 * body cell that was focused. It updates its value of `activeDate` synchronously and updates the
1478 * parent's value asynchonously via the `activeDateChange` event. The child component receives an
1479 * updated value asynchronously via the `activeCell` Input.
1480 */
1481 _updateActiveDate(event) {
1482 const month = event.value;
1483 const oldActiveDate = this._activeDate;
1484 this.activeDate = this._getDateFromMonth(month);
1485 if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {
1486 this.activeDateChange.emit(this.activeDate);
1487 }
1488 }
1489 /** Handles keydown events on the calendar body when calendar is in year view. */
1490 _handleCalendarBodyKeydown(event) {
1491 // TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent
1492 // disabled ones from being selected. This may not be ideal, we should look into whether
1493 // navigation should skip over disabled dates, and if so, how to implement that efficiently.
1494 const oldActiveDate = this._activeDate;
1495 const isRtl = this._isRtl();
1496 switch (event.keyCode) {
1497 case LEFT_ARROW:
1498 this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, isRtl ? 1 : -1);
1499 break;
1500 case RIGHT_ARROW:
1501 this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, isRtl ? -1 : 1);
1502 break;
1503 case UP_ARROW:
1504 this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, -4);
1505 break;
1506 case DOWN_ARROW:
1507 this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, 4);
1508 break;
1509 case HOME:
1510 this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, -this._dateAdapter.getMonth(this._activeDate));
1511 break;
1512 case END:
1513 this.activeDate = this._dateAdapter.addCalendarMonths(this._activeDate, 11 - this._dateAdapter.getMonth(this._activeDate));
1514 break;
1515 case PAGE_UP:
1516 this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, event.altKey ? -10 : -1);
1517 break;
1518 case PAGE_DOWN:
1519 this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate, event.altKey ? 10 : 1);
1520 break;
1521 case ENTER:
1522 case SPACE:
1523 // Note that we only prevent the default action here while the selection happens in
1524 // `keyup` below. We can't do the selection here, because it can cause the calendar to
1525 // reopen if focus is restored immediately. We also can't call `preventDefault` on `keyup`
1526 // because it's too late (see #23305).
1527 this._selectionKeyPressed = true;
1528 break;
1529 default:
1530 // Don't prevent default or focus active cell on keys that we don't explicitly handle.
1531 return;
1532 }
1533 if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {
1534 this.activeDateChange.emit(this.activeDate);
1535 this._focusActiveCellAfterViewChecked();
1536 }
1537 // Prevent unexpected default actions such as form submission.
1538 event.preventDefault();
1539 }
1540 /** Handles keyup events on the calendar body when calendar is in year view. */
1541 _handleCalendarBodyKeyup(event) {
1542 if (event.keyCode === SPACE || event.keyCode === ENTER) {
1543 if (this._selectionKeyPressed) {
1544 this._monthSelected({ value: this._dateAdapter.getMonth(this._activeDate), event });
1545 }
1546 this._selectionKeyPressed = false;
1547 }
1548 }
1549 /** Initializes this year view. */
1550 _init() {
1551 this._setSelectedMonth(this.selected);
1552 this._todayMonth = this._getMonthInCurrentYear(this._dateAdapter.today());
1553 this._yearLabel = this._dateAdapter.getYearName(this.activeDate);
1554 let monthNames = this._dateAdapter.getMonthNames('short');
1555 // First row of months only contains 5 elements so we can fit the year label on the same row.
1556 this._months = [
1557 [0, 1, 2, 3],
1558 [4, 5, 6, 7],
1559 [8, 9, 10, 11],
1560 ].map(row => row.map(month => this._createCellForMonth(month, monthNames[month])));
1561 this._changeDetectorRef.markForCheck();
1562 }
1563 /** Focuses the active cell after the microtask queue is empty. */
1564 _focusActiveCell() {
1565 this._matCalendarBody._focusActiveCell();
1566 }
1567 /** Schedules the matCalendarBody to focus the active cell after change detection has run */
1568 _focusActiveCellAfterViewChecked() {
1569 this._matCalendarBody._scheduleFocusActiveCellAfterViewChecked();
1570 }
1571 /**
1572 * Gets the month in this year that the given Date falls on.
1573 * Returns null if the given Date is in another year.
1574 */
1575 _getMonthInCurrentYear(date) {
1576 return date && this._dateAdapter.getYear(date) == this._dateAdapter.getYear(this.activeDate)
1577 ? this._dateAdapter.getMonth(date)
1578 : null;
1579 }
1580 /**
1581 * Takes a month and returns a new date in the same day and year as the currently active date.
1582 * The returned date will have the same month as the argument date.
1583 */
1584 _getDateFromMonth(month) {
1585 const normalizedDate = this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), month, 1);
1586 const daysInMonth = this._dateAdapter.getNumDaysInMonth(normalizedDate);
1587 return this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), month, Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth));
1588 }
1589 /** Creates an MatCalendarCell for the given month. */
1590 _createCellForMonth(month, monthName) {
1591 const date = this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), month, 1);
1592 const ariaLabel = this._dateAdapter.format(date, this._dateFormats.display.monthYearA11yLabel);
1593 const cellClasses = this.dateClass ? this.dateClass(date, 'year') : undefined;
1594 return new MatCalendarCell(month, monthName.toLocaleUpperCase(), ariaLabel, this._shouldEnableMonth(month), cellClasses);
1595 }
1596 /** Whether the given month is enabled. */
1597 _shouldEnableMonth(month) {
1598 const activeYear = this._dateAdapter.getYear(this.activeDate);
1599 if (month === undefined ||
1600 month === null ||
1601 this._isYearAndMonthAfterMaxDate(activeYear, month) ||
1602 this._isYearAndMonthBeforeMinDate(activeYear, month)) {
1603 return false;
1604 }
1605 if (!this.dateFilter) {
1606 return true;
1607 }
1608 const firstOfMonth = this._dateAdapter.createDate(activeYear, month, 1);
1609 // If any date in the month is enabled count the month as enabled.
1610 for (let date = firstOfMonth; this._dateAdapter.getMonth(date) == month; date = this._dateAdapter.addCalendarDays(date, 1)) {
1611 if (this.dateFilter(date)) {
1612 return true;
1613 }
1614 }
1615 return false;
1616 }
1617 /**
1618 * Tests whether the combination month/year is after this.maxDate, considering
1619 * just the month and year of this.maxDate
1620 */
1621 _isYearAndMonthAfterMaxDate(year, month) {
1622 if (this.maxDate) {
1623 const maxYear = this._dateAdapter.getYear(this.maxDate);
1624 const maxMonth = this._dateAdapter.getMonth(this.maxDate);
1625 return year > maxYear || (year === maxYear && month > maxMonth);
1626 }
1627 return false;
1628 }
1629 /**
1630 * Tests whether the combination month/year is before this.minDate, considering
1631 * just the month and year of this.minDate
1632 */
1633 _isYearAndMonthBeforeMinDate(year, month) {
1634 if (this.minDate) {
1635 const minYear = this._dateAdapter.getYear(this.minDate);
1636 const minMonth = this._dateAdapter.getMonth(this.minDate);
1637 return year < minYear || (year === minYear && month < minMonth);
1638 }
1639 return false;
1640 }
1641 /** Determines whether the user has the RTL layout direction. */
1642 _isRtl() {
1643 return this._dir && this._dir.value === 'rtl';
1644 }
1645 /** Sets the currently-selected month based on a model value. */
1646 _setSelectedMonth(value) {
1647 if (value instanceof DateRange) {
1648 this._selectedMonth =
1649 this._getMonthInCurrentYear(value.start) || this._getMonthInCurrentYear(value.end);
1650 }
1651 else {
1652 this._selectedMonth = this._getMonthInCurrentYear(value);
1653 }
1654 }
1655}
1656MatYearView.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatYearView, deps: [{ token: i0.ChangeDetectorRef }, { token: MAT_DATE_FORMATS, optional: true }, { token: i1$1.DateAdapter, optional: true }, { token: i2.Directionality, optional: true }], target: i0.ɵɵFactoryTarget.Component });
1657MatYearView.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatYearView, selector: "mat-year-view", inputs: { activeDate: "activeDate", selected: "selected", minDate: "minDate", maxDate: "maxDate", dateFilter: "dateFilter", dateClass: "dateClass" }, outputs: { selectedChange: "selectedChange", monthSelected: "monthSelected", activeDateChange: "activeDateChange" }, viewQueries: [{ propertyName: "_matCalendarBody", first: true, predicate: MatCalendarBody, descendants: true }], exportAs: ["matYearView"], ngImport: i0, template: "<table class=\"mat-calendar-table\" role=\"grid\">\n <thead aria-hidden=\"true\" class=\"mat-calendar-table-header\">\n <tr><th class=\"mat-calendar-table-header-divider\" colspan=\"4\"></th></tr>\n </thead>\n <tbody mat-calendar-body\n [label]=\"_yearLabel\"\n [rows]=\"_months\"\n [todayValue]=\"_todayMonth!\"\n [startValue]=\"_selectedMonth!\"\n [endValue]=\"_selectedMonth!\"\n [labelMinRequiredCells]=\"2\"\n [numCols]=\"4\"\n [cellAspectRatio]=\"4 / 7\"\n [activeCell]=\"_dateAdapter.getMonth(activeDate)\"\n (selectedValueChange)=\"_monthSelected($event)\"\n (activeDateChange)=\"_updateActiveDate($event)\"\n (keyup)=\"_handleCalendarBodyKeyup($event)\"\n (keydown)=\"_handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n", dependencies: [{ kind: "component", type: MatCalendarBody, selector: "[mat-calendar-body]", inputs: ["label", "rows", "todayValue", "startValue", "endValue", "labelMinRequiredCells", "numCols", "activeCell", "isRange", "cellAspectRatio", "comparisonStart", "comparisonEnd", "previewStart", "previewEnd"], outputs: ["selectedValueChange", "previewChange", "activeDateChange"], exportAs: ["matCalendarBody"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1658i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatYearView, decorators: [{
1659 type: Component,
1660 args: [{ selector: 'mat-year-view', exportAs: 'matYearView', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<table class=\"mat-calendar-table\" role=\"grid\">\n <thead aria-hidden=\"true\" class=\"mat-calendar-table-header\">\n <tr><th class=\"mat-calendar-table-header-divider\" colspan=\"4\"></th></tr>\n </thead>\n <tbody mat-calendar-body\n [label]=\"_yearLabel\"\n [rows]=\"_months\"\n [todayValue]=\"_todayMonth!\"\n [startValue]=\"_selectedMonth!\"\n [endValue]=\"_selectedMonth!\"\n [labelMinRequiredCells]=\"2\"\n [numCols]=\"4\"\n [cellAspectRatio]=\"4 / 7\"\n [activeCell]=\"_dateAdapter.getMonth(activeDate)\"\n (selectedValueChange)=\"_monthSelected($event)\"\n (activeDateChange)=\"_updateActiveDate($event)\"\n (keyup)=\"_handleCalendarBodyKeyup($event)\"\n (keydown)=\"_handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n" }]
1661 }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
1662 type: Optional
1663 }, {
1664 type: Inject,
1665 args: [MAT_DATE_FORMATS]
1666 }] }, { type: i1$1.DateAdapter, decorators: [{
1667 type: Optional
1668 }] }, { type: i2.Directionality, decorators: [{
1669 type: Optional
1670 }] }]; }, propDecorators: { activeDate: [{
1671 type: Input
1672 }], selected: [{
1673 type: Input
1674 }], minDate: [{
1675 type: Input
1676 }], maxDate: [{
1677 type: Input
1678 }], dateFilter: [{
1679 type: Input
1680 }], dateClass: [{
1681 type: Input
1682 }], selectedChange: [{
1683 type: Output
1684 }], monthSelected: [{
1685 type: Output
1686 }], activeDateChange: [{
1687 type: Output
1688 }], _matCalendarBody: [{
1689 type: ViewChild,
1690 args: [MatCalendarBody]
1691 }] } });
1692
1693/**
1694 * @license
1695 * Copyright Google LLC All Rights Reserved.
1696 *
1697 * Use of this source code is governed by an MIT-style license that can be
1698 * found in the LICENSE file at https://angular.io/license
1699 */
1700/** Counter used to generate unique IDs. */
1701let uniqueId = 0;
1702/** Default header for MatCalendar */
1703class MatCalendarHeader {
1704 constructor(_intl, calendar, _dateAdapter, _dateFormats, changeDetectorRef) {
1705 this._intl = _intl;
1706 this.calendar = calendar;
1707 this._dateAdapter = _dateAdapter;
1708 this._dateFormats = _dateFormats;
1709 this._buttonDescriptionId = `mat-calendar-button-${uniqueId++}`;
1710 this.calendar.stateChanges.subscribe(() => changeDetectorRef.markForCheck());
1711 }
1712 /** The label for the current calendar view. */
1713 get periodButtonText() {
1714 if (this.calendar.currentView == 'month') {
1715 return this._dateAdapter
1716 .format(this.calendar.activeDate, this._dateFormats.display.monthYearLabel)
1717 .toLocaleUpperCase();
1718 }
1719 if (this.calendar.currentView == 'year') {
1720 return this._dateAdapter.getYearName(this.calendar.activeDate);
1721 }
1722 // The offset from the active year to the "slot" for the starting year is the
1723 // *actual* first rendered year in the multi-year view, and the last year is
1724 // just yearsPerPage - 1 away.
1725 const activeYear = this._dateAdapter.getYear(this.calendar.activeDate);
1726 const minYearOfPage = activeYear -
1727 getActiveOffset(this._dateAdapter, this.calendar.activeDate, this.calendar.minDate, this.calendar.maxDate);
1728 const maxYearOfPage = minYearOfPage + yearsPerPage - 1;
1729 const minYearName = this._dateAdapter.getYearName(this._dateAdapter.createDate(minYearOfPage, 0, 1));
1730 const maxYearName = this._dateAdapter.getYearName(this._dateAdapter.createDate(maxYearOfPage, 0, 1));
1731 return this._intl.formatYearRange(minYearName, maxYearName);
1732 }
1733 get periodButtonLabel() {
1734 return this.calendar.currentView == 'month'
1735 ? this._intl.switchToMultiYearViewLabel
1736 : this._intl.switchToMonthViewLabel;
1737 }
1738 /** The label for the previous button. */
1739 get prevButtonLabel() {
1740 return {
1741 'month': this._intl.prevMonthLabel,
1742 'year': this._intl.prevYearLabel,
1743 'multi-year': this._intl.prevMultiYearLabel,
1744 }[this.calendar.currentView];
1745 }
1746 /** The label for the next button. */
1747 get nextButtonLabel() {
1748 return {
1749 'month': this._intl.nextMonthLabel,
1750 'year': this._intl.nextYearLabel,
1751 'multi-year': this._intl.nextMultiYearLabel,
1752 }[this.calendar.currentView];
1753 }
1754 /** Handles user clicks on the period label. */
1755 currentPeriodClicked() {
1756 this.calendar.currentView = this.calendar.currentView == 'month' ? 'multi-year' : 'month';
1757 }
1758 /** Handles user clicks on the previous button. */
1759 previousClicked() {
1760 this.calendar.activeDate =
1761 this.calendar.currentView == 'month'
1762 ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate, -1)
1763 : this._dateAdapter.addCalendarYears(this.calendar.activeDate, this.calendar.currentView == 'year' ? -1 : -yearsPerPage);
1764 }
1765 /** Handles user clicks on the next button. */
1766 nextClicked() {
1767 this.calendar.activeDate =
1768 this.calendar.currentView == 'month'
1769 ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate, 1)
1770 : this._dateAdapter.addCalendarYears(this.calendar.activeDate, this.calendar.currentView == 'year' ? 1 : yearsPerPage);
1771 }
1772 /** Whether the previous period button is enabled. */
1773 previousEnabled() {
1774 if (!this.calendar.minDate) {
1775 return true;
1776 }
1777 return (!this.calendar.minDate || !this._isSameView(this.calendar.activeDate, this.calendar.minDate));
1778 }
1779 /** Whether the next period button is enabled. */
1780 nextEnabled() {
1781 return (!this.calendar.maxDate || !this._isSameView(this.calendar.activeDate, this.calendar.maxDate));
1782 }
1783 /** Whether the two dates represent the same view in the current view mode (month or year). */
1784 _isSameView(date1, date2) {
1785 if (this.calendar.currentView == 'month') {
1786 return (this._dateAdapter.getYear(date1) == this._dateAdapter.getYear(date2) &&
1787 this._dateAdapter.getMonth(date1) == this._dateAdapter.getMonth(date2));
1788 }
1789 if (this.calendar.currentView == 'year') {
1790 return this._dateAdapter.getYear(date1) == this._dateAdapter.getYear(date2);
1791 }
1792 // Otherwise we are in 'multi-year' view.
1793 return isSameMultiYearView(this._dateAdapter, date1, date2, this.calendar.minDate, this.calendar.maxDate);
1794 }
1795}
1796MatCalendarHeader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatCalendarHeader, deps: [{ token: MatDatepickerIntl }, { token: forwardRef(() => MatCalendar) }, { token: i1$1.DateAdapter, optional: true }, { token: MAT_DATE_FORMATS, optional: true }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1797MatCalendarHeader.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatCalendarHeader, selector: "mat-calendar-header", exportAs: ["matCalendarHeader"], ngImport: i0, template: "<div class=\"mat-calendar-header\">\n <div class=\"mat-calendar-controls\">\n <button mat-button type=\"button\" class=\"mat-calendar-period-button\"\n (click)=\"currentPeriodClicked()\" [attr.aria-label]=\"periodButtonLabel\"\n [attr.aria-describedby]=\"_buttonDescriptionId\"\n aria-live=\"polite\">\n <span [attr.id]=\"_buttonDescriptionId\">{{periodButtonText}}</span>\n <svg class=\"mat-calendar-arrow\" [class.mat-calendar-invert]=\"calendar.currentView !== 'month'\"\n viewBox=\"0 0 10 5\" focusable=\"false\">\n <polygon points=\"0,0 5,5 10,0\"/>\n </svg>\n </button>\n\n <div class=\"mat-calendar-spacer\"></div>\n\n <ng-content></ng-content>\n\n <button mat-icon-button type=\"button\" class=\"mat-calendar-previous-button\"\n [disabled]=\"!previousEnabled()\" (click)=\"previousClicked()\"\n [attr.aria-label]=\"prevButtonLabel\">\n </button>\n\n <button mat-icon-button type=\"button\" class=\"mat-calendar-next-button\"\n [disabled]=\"!nextEnabled()\" (click)=\"nextClicked()\"\n [attr.aria-label]=\"nextButtonLabel\">\n </button>\n </div>\n</div>\n", dependencies: [{ kind: "component", type: i3.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1798i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatCalendarHeader, decorators: [{
1799 type: Component,
1800 args: [{ selector: 'mat-calendar-header', exportAs: 'matCalendarHeader', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mat-calendar-header\">\n <div class=\"mat-calendar-controls\">\n <button mat-button type=\"button\" class=\"mat-calendar-period-button\"\n (click)=\"currentPeriodClicked()\" [attr.aria-label]=\"periodButtonLabel\"\n [attr.aria-describedby]=\"_buttonDescriptionId\"\n aria-live=\"polite\">\n <span [attr.id]=\"_buttonDescriptionId\">{{periodButtonText}}</span>\n <svg class=\"mat-calendar-arrow\" [class.mat-calendar-invert]=\"calendar.currentView !== 'month'\"\n viewBox=\"0 0 10 5\" focusable=\"false\">\n <polygon points=\"0,0 5,5 10,0\"/>\n </svg>\n </button>\n\n <div class=\"mat-calendar-spacer\"></div>\n\n <ng-content></ng-content>\n\n <button mat-icon-button type=\"button\" class=\"mat-calendar-previous-button\"\n [disabled]=\"!previousEnabled()\" (click)=\"previousClicked()\"\n [attr.aria-label]=\"prevButtonLabel\">\n </button>\n\n <button mat-icon-button type=\"button\" class=\"mat-calendar-next-button\"\n [disabled]=\"!nextEnabled()\" (click)=\"nextClicked()\"\n [attr.aria-label]=\"nextButtonLabel\">\n </button>\n </div>\n</div>\n" }]
1801 }], ctorParameters: function () { return [{ type: MatDatepickerIntl }, { type: MatCalendar, decorators: [{
1802 type: Inject,
1803 args: [forwardRef(() => MatCalendar)]
1804 }] }, { type: i1$1.DateAdapter, decorators: [{
1805 type: Optional
1806 }] }, { type: undefined, decorators: [{
1807 type: Optional
1808 }, {
1809 type: Inject,
1810 args: [MAT_DATE_FORMATS]
1811 }] }, { type: i0.ChangeDetectorRef }]; } });
1812/** A calendar that is used as part of the datepicker. */
1813class MatCalendar {
1814 constructor(_intl, _dateAdapter, _dateFormats, _changeDetectorRef) {
1815 this._dateAdapter = _dateAdapter;
1816 this._dateFormats = _dateFormats;
1817 this._changeDetectorRef = _changeDetectorRef;
1818 /**
1819 * Used for scheduling that focus should be moved to the active cell on the next tick.
1820 * We need to schedule it, rather than do it immediately, because we have to wait
1821 * for Angular to re-evaluate the view children.
1822 */
1823 this._moveFocusOnNextTick = false;
1824 /** Whether the calendar should be started in month or year view. */
1825 this.startView = 'month';
1826 /** Emits when the currently selected date changes. */
1827 this.selectedChange = new EventEmitter();
1828 /**
1829 * Emits the year chosen in multiyear view.
1830 * This doesn't imply a change on the selected date.
1831 */
1832 this.yearSelected = new EventEmitter();
1833 /**
1834 * Emits the month chosen in year view.
1835 * This doesn't imply a change on the selected date.
1836 */
1837 this.monthSelected = new EventEmitter();
1838 /**
1839 * Emits when the current view changes.
1840 */
1841 this.viewChanged = new EventEmitter(true);
1842 /** Emits when any date is selected. */
1843 this._userSelection = new EventEmitter();
1844 /**
1845 * Emits whenever there is a state change that the header may need to respond to.
1846 */
1847 this.stateChanges = new Subject();
1848 if (typeof ngDevMode === 'undefined' || ngDevMode) {
1849 if (!this._dateAdapter) {
1850 throw createMissingDateImplError('DateAdapter');
1851 }
1852 if (!this._dateFormats) {
1853 throw createMissingDateImplError('MAT_DATE_FORMATS');
1854 }
1855 }
1856 this._intlChanges = _intl.changes.subscribe(() => {
1857 _changeDetectorRef.markForCheck();
1858 this.stateChanges.next();
1859 });
1860 }
1861 /** A date representing the period (month or year) to start the calendar in. */
1862 get startAt() {
1863 return this._startAt;
1864 }
1865 set startAt(value) {
1866 this._startAt = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
1867 }
1868 /** The currently selected date. */
1869 get selected() {
1870 return this._selected;
1871 }
1872 set selected(value) {
1873 if (value instanceof DateRange) {
1874 this._selected = value;
1875 }
1876 else {
1877 this._selected = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
1878 }
1879 }
1880 /** The minimum selectable date. */
1881 get minDate() {
1882 return this._minDate;
1883 }
1884 set minDate(value) {
1885 this._minDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
1886 }
1887 /** The maximum selectable date. */
1888 get maxDate() {
1889 return this._maxDate;
1890 }
1891 set maxDate(value) {
1892 this._maxDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
1893 }
1894 /**
1895 * The current active date. This determines which time period is shown and which date is
1896 * highlighted when using keyboard navigation.
1897 */
1898 get activeDate() {
1899 return this._clampedActiveDate;
1900 }
1901 set activeDate(value) {
1902 this._clampedActiveDate = this._dateAdapter.clampDate(value, this.minDate, this.maxDate);
1903 this.stateChanges.next();
1904 this._changeDetectorRef.markForCheck();
1905 }
1906 /** Whether the calendar is in month view. */
1907 get currentView() {
1908 return this._currentView;
1909 }
1910 set currentView(value) {
1911 const viewChangedResult = this._currentView !== value ? value : null;
1912 this._currentView = value;
1913 this._moveFocusOnNextTick = true;
1914 this._changeDetectorRef.markForCheck();
1915 if (viewChangedResult) {
1916 this.viewChanged.emit(viewChangedResult);
1917 }
1918 }
1919 ngAfterContentInit() {
1920 this._calendarHeaderPortal = new ComponentPortal(this.headerComponent || MatCalendarHeader);
1921 this.activeDate = this.startAt || this._dateAdapter.today();
1922 // Assign to the private property since we don't want to move focus on init.
1923 this._currentView = this.startView;
1924 }
1925 ngAfterViewChecked() {
1926 if (this._moveFocusOnNextTick) {
1927 this._moveFocusOnNextTick = false;
1928 this.focusActiveCell();
1929 }
1930 }
1931 ngOnDestroy() {
1932 this._intlChanges.unsubscribe();
1933 this.stateChanges.complete();
1934 }
1935 ngOnChanges(changes) {
1936 // Ignore date changes that are at a different time on the same day. This fixes issues where
1937 // the calendar re-renders when there is no meaningful change to [minDate] or [maxDate]
1938 // (#24435).
1939 const minDateChange = changes['minDate'] &&
1940 !this._dateAdapter.sameDate(changes['minDate'].previousValue, changes['minDate'].currentValue)
1941 ? changes['minDate']
1942 : undefined;
1943 const maxDateChange = changes['maxDate'] &&
1944 !this._dateAdapter.sameDate(changes['maxDate'].previousValue, changes['maxDate'].currentValue)
1945 ? changes['maxDate']
1946 : undefined;
1947 const change = minDateChange || maxDateChange || changes['dateFilter'];
1948 if (change && !change.firstChange) {
1949 const view = this._getCurrentViewComponent();
1950 if (view) {
1951 // We need to `detectChanges` manually here, because the `minDate`, `maxDate` etc. are
1952 // passed down to the view via data bindings which won't be up-to-date when we call `_init`.
1953 this._changeDetectorRef.detectChanges();
1954 view._init();
1955 }
1956 }
1957 this.stateChanges.next();
1958 }
1959 /** Focuses the active date. */
1960 focusActiveCell() {
1961 this._getCurrentViewComponent()._focusActiveCell(false);
1962 }
1963 /** Updates today's date after an update of the active date */
1964 updateTodaysDate() {
1965 this._getCurrentViewComponent()._init();
1966 }
1967 /** Handles date selection in the month view. */
1968 _dateSelected(event) {
1969 const date = event.value;
1970 if (this.selected instanceof DateRange ||
1971 (date && !this._dateAdapter.sameDate(date, this.selected))) {
1972 this.selectedChange.emit(date);
1973 }
1974 this._userSelection.emit(event);
1975 }
1976 /** Handles year selection in the multiyear view. */
1977 _yearSelectedInMultiYearView(normalizedYear) {
1978 this.yearSelected.emit(normalizedYear);
1979 }
1980 /** Handles month selection in the year view. */
1981 _monthSelectedInYearView(normalizedMonth) {
1982 this.monthSelected.emit(normalizedMonth);
1983 }
1984 /** Handles year/month selection in the multi-year/year views. */
1985 _goToDateInView(date, view) {
1986 this.activeDate = date;
1987 this.currentView = view;
1988 }
1989 /** Returns the component instance that corresponds to the current calendar view. */
1990 _getCurrentViewComponent() {
1991 // The return type is explicitly written as a union to ensure that the Closure compiler does
1992 // not optimize calls to _init(). Without the explict return type, TypeScript narrows it to
1993 // only the first component type. See https://github.com/angular/components/issues/22996.
1994 return this.monthView || this.yearView || this.multiYearView;
1995 }
1996}
1997MatCalendar.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatCalendar, deps: [{ token: MatDatepickerIntl }, { token: i1$1.DateAdapter, optional: true }, { token: MAT_DATE_FORMATS, optional: true }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1998MatCalendar.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatCalendar, selector: "mat-calendar", inputs: { headerComponent: "headerComponent", startAt: "startAt", startView: "startView", selected: "selected", minDate: "minDate", maxDate: "maxDate", dateFilter: "dateFilter", dateClass: "dateClass", comparisonStart: "comparisonStart", comparisonEnd: "comparisonEnd" }, outputs: { selectedChange: "selectedChange", yearSelected: "yearSelected", monthSelected: "monthSelected", viewChanged: "viewChanged", _userSelection: "_userSelection" }, host: { classAttribute: "mat-calendar" }, providers: [MAT_SINGLE_DATE_SELECTION_MODEL_PROVIDER], viewQueries: [{ propertyName: "monthView", first: true, predicate: MatMonthView, descendants: true }, { propertyName: "yearView", first: true, predicate: MatYearView, descendants: true }, { propertyName: "multiYearView", first: true, predicate: MatMultiYearView, descendants: true }], exportAs: ["matCalendar"], usesOnChanges: true, ngImport: i0, template: "<ng-template [cdkPortalOutlet]=\"_calendarHeaderPortal\"></ng-template>\n\n<div class=\"mat-calendar-content\" [ngSwitch]=\"currentView\" cdkMonitorSubtreeFocus tabindex=\"-1\">\n <mat-month-view\n *ngSwitchCase=\"'month'\"\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [dateFilter]=\"dateFilter\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [dateClass]=\"dateClass\"\n [comparisonStart]=\"comparisonStart\"\n [comparisonEnd]=\"comparisonEnd\"\n (_userSelection)=\"_dateSelected($event)\">\n </mat-month-view>\n\n <mat-year-view\n *ngSwitchCase=\"'year'\"\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [dateFilter]=\"dateFilter\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [dateClass]=\"dateClass\"\n (monthSelected)=\"_monthSelectedInYearView($event)\"\n (selectedChange)=\"_goToDateInView($event, 'month')\">\n </mat-year-view>\n\n <mat-multi-year-view\n *ngSwitchCase=\"'multi-year'\"\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [dateFilter]=\"dateFilter\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [dateClass]=\"dateClass\"\n (yearSelected)=\"_yearSelectedInMultiYearView($event)\"\n (selectedChange)=\"_goToDateInView($event, 'year')\">\n </mat-multi-year-view>\n</div>\n", styles: [".mat-calendar{display:block}.mat-calendar-header{padding:8px 8px 0 8px}.mat-calendar-content{padding:0 8px 8px 8px;outline:none}.mat-calendar-controls{display:flex;margin:5% calc(4.7142857143% - 16px)}.mat-calendar-controls .mat-icon-button:hover .mat-button-focus-overlay{opacity:.04}.mat-calendar-spacer{flex:1 1 auto}.mat-calendar-period-button{min-width:0}.mat-calendar-arrow{display:inline-block;width:10px;height:5px;margin:0 0 0 5px;vertical-align:middle}.mat-calendar-arrow.mat-calendar-invert{transform:rotate(180deg)}[dir=rtl] .mat-calendar-arrow{margin:0 5px 0 0}.cdk-high-contrast-active .mat-calendar-arrow{fill:CanvasText}.mat-calendar-previous-button,.mat-calendar-next-button{position:relative}.mat-calendar-previous-button::after,.mat-calendar-next-button::after{top:0;left:0;right:0;bottom:0;position:absolute;content:\"\";margin:15.5px;border:0 solid currentColor;border-top-width:2px}[dir=rtl] .mat-calendar-previous-button,[dir=rtl] .mat-calendar-next-button{transform:rotate(180deg)}.mat-calendar-previous-button::after{border-left-width:2px;transform:translateX(2px) rotate(-45deg)}.mat-calendar-next-button::after{border-right-width:2px;transform:translateX(-2px) rotate(45deg)}.mat-calendar-table{border-spacing:0;border-collapse:collapse;width:100%}.mat-calendar-table-header th{text-align:center;padding:0 0 8px 0}.mat-calendar-table-header-divider{position:relative;height:1px}.mat-calendar-table-header-divider::after{content:\"\";position:absolute;top:0;left:-8px;right:-8px;height:1px}"], dependencies: [{ kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i5.CdkMonitorFocus, selector: "[cdkMonitorElementFocus], [cdkMonitorSubtreeFocus]", outputs: ["cdkFocusChange"] }, { kind: "directive", type: i6.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "component", type: MatMonthView, selector: "mat-month-view", inputs: ["activeDate", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "comparisonStart", "comparisonEnd"], outputs: ["selectedChange", "_userSelection", "activeDateChange"], exportAs: ["matMonthView"] }, { kind: "component", type: MatYearView, selector: "mat-year-view", inputs: ["activeDate", "selected", "minDate", "maxDate", "dateFilter", "dateClass"], outputs: ["selectedChange", "monthSelected", "activeDateChange"], exportAs: ["matYearView"] }, { kind: "component", type: MatMultiYearView, selector: "mat-multi-year-view", inputs: ["activeDate", "selected", "minDate", "maxDate", "dateFilter", "dateClass"], outputs: ["selectedChange", "yearSelected", "activeDateChange"], exportAs: ["matMultiYearView"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1999i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatCalendar, decorators: [{
2000 type: Component,
2001 args: [{ selector: 'mat-calendar', host: {
2002 'class': 'mat-calendar',
2003 }, exportAs: 'matCalendar', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [MAT_SINGLE_DATE_SELECTION_MODEL_PROVIDER], template: "<ng-template [cdkPortalOutlet]=\"_calendarHeaderPortal\"></ng-template>\n\n<div class=\"mat-calendar-content\" [ngSwitch]=\"currentView\" cdkMonitorSubtreeFocus tabindex=\"-1\">\n <mat-month-view\n *ngSwitchCase=\"'month'\"\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [dateFilter]=\"dateFilter\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [dateClass]=\"dateClass\"\n [comparisonStart]=\"comparisonStart\"\n [comparisonEnd]=\"comparisonEnd\"\n (_userSelection)=\"_dateSelected($event)\">\n </mat-month-view>\n\n <mat-year-view\n *ngSwitchCase=\"'year'\"\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [dateFilter]=\"dateFilter\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [dateClass]=\"dateClass\"\n (monthSelected)=\"_monthSelectedInYearView($event)\"\n (selectedChange)=\"_goToDateInView($event, 'month')\">\n </mat-year-view>\n\n <mat-multi-year-view\n *ngSwitchCase=\"'multi-year'\"\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [dateFilter]=\"dateFilter\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [dateClass]=\"dateClass\"\n (yearSelected)=\"_yearSelectedInMultiYearView($event)\"\n (selectedChange)=\"_goToDateInView($event, 'year')\">\n </mat-multi-year-view>\n</div>\n", styles: [".mat-calendar{display:block}.mat-calendar-header{padding:8px 8px 0 8px}.mat-calendar-content{padding:0 8px 8px 8px;outline:none}.mat-calendar-controls{display:flex;margin:5% calc(4.7142857143% - 16px)}.mat-calendar-controls .mat-icon-button:hover .mat-button-focus-overlay{opacity:.04}.mat-calendar-spacer{flex:1 1 auto}.mat-calendar-period-button{min-width:0}.mat-calendar-arrow{display:inline-block;width:10px;height:5px;margin:0 0 0 5px;vertical-align:middle}.mat-calendar-arrow.mat-calendar-invert{transform:rotate(180deg)}[dir=rtl] .mat-calendar-arrow{margin:0 5px 0 0}.cdk-high-contrast-active .mat-calendar-arrow{fill:CanvasText}.mat-calendar-previous-button,.mat-calendar-next-button{position:relative}.mat-calendar-previous-button::after,.mat-calendar-next-button::after{top:0;left:0;right:0;bottom:0;position:absolute;content:\"\";margin:15.5px;border:0 solid currentColor;border-top-width:2px}[dir=rtl] .mat-calendar-previous-button,[dir=rtl] .mat-calendar-next-button{transform:rotate(180deg)}.mat-calendar-previous-button::after{border-left-width:2px;transform:translateX(2px) rotate(-45deg)}.mat-calendar-next-button::after{border-right-width:2px;transform:translateX(-2px) rotate(45deg)}.mat-calendar-table{border-spacing:0;border-collapse:collapse;width:100%}.mat-calendar-table-header th{text-align:center;padding:0 0 8px 0}.mat-calendar-table-header-divider{position:relative;height:1px}.mat-calendar-table-header-divider::after{content:\"\";position:absolute;top:0;left:-8px;right:-8px;height:1px}"] }]
2004 }], ctorParameters: function () { return [{ type: MatDatepickerIntl }, { type: i1$1.DateAdapter, decorators: [{
2005 type: Optional
2006 }] }, { type: undefined, decorators: [{
2007 type: Optional
2008 }, {
2009 type: Inject,
2010 args: [MAT_DATE_FORMATS]
2011 }] }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { headerComponent: [{
2012 type: Input
2013 }], startAt: [{
2014 type: Input
2015 }], startView: [{
2016 type: Input
2017 }], selected: [{
2018 type: Input
2019 }], minDate: [{
2020 type: Input
2021 }], maxDate: [{
2022 type: Input
2023 }], dateFilter: [{
2024 type: Input
2025 }], dateClass: [{
2026 type: Input
2027 }], comparisonStart: [{
2028 type: Input
2029 }], comparisonEnd: [{
2030 type: Input
2031 }], selectedChange: [{
2032 type: Output
2033 }], yearSelected: [{
2034 type: Output
2035 }], monthSelected: [{
2036 type: Output
2037 }], viewChanged: [{
2038 type: Output
2039 }], _userSelection: [{
2040 type: Output
2041 }], monthView: [{
2042 type: ViewChild,
2043 args: [MatMonthView]
2044 }], yearView: [{
2045 type: ViewChild,
2046 args: [MatYearView]
2047 }], multiYearView: [{
2048 type: ViewChild,
2049 args: [MatMultiYearView]
2050 }] } });
2051
2052/**
2053 * @license
2054 * Copyright Google LLC All Rights Reserved.
2055 *
2056 * Use of this source code is governed by an MIT-style license that can be
2057 * found in the LICENSE file at https://angular.io/license
2058 */
2059/**
2060 * Animations used by the Material datepicker.
2061 * @docs-private
2062 */
2063const matDatepickerAnimations = {
2064 /** Transforms the height of the datepicker's calendar. */
2065 transformPanel: trigger('transformPanel', [
2066 transition('void => enter-dropdown', animate('120ms cubic-bezier(0, 0, 0.2, 1)', keyframes([
2067 style({ opacity: 0, transform: 'scale(1, 0.8)' }),
2068 style({ opacity: 1, transform: 'scale(1, 1)' }),
2069 ]))),
2070 transition('void => enter-dialog', animate('150ms cubic-bezier(0, 0, 0.2, 1)', keyframes([
2071 style({ opacity: 0, transform: 'scale(0.7)' }),
2072 style({ transform: 'none', opacity: 1 }),
2073 ]))),
2074 transition('* => void', animate('100ms linear', style({ opacity: 0 }))),
2075 ]),
2076 /** Fades in the content of the calendar. */
2077 fadeInCalendar: trigger('fadeInCalendar', [
2078 state('void', style({ opacity: 0 })),
2079 state('enter', style({ opacity: 1 })),
2080 // TODO(crisbeto): this animation should be removed since it isn't quite on spec, but we
2081 // need to keep it until #12440 gets in, otherwise the exit animation will look glitchy.
2082 transition('void => *', animate('120ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)')),
2083 ]),
2084};
2085
2086/**
2087 * @license
2088 * Copyright Google LLC All Rights Reserved.
2089 *
2090 * Use of this source code is governed by an MIT-style license that can be
2091 * found in the LICENSE file at https://angular.io/license
2092 */
2093/** Used to generate a unique ID for each datepicker instance. */
2094let datepickerUid = 0;
2095/** Injection token that determines the scroll handling while the calendar is open. */
2096const MAT_DATEPICKER_SCROLL_STRATEGY = new InjectionToken('mat-datepicker-scroll-strategy');
2097/** @docs-private */
2098function MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY(overlay) {
2099 return () => overlay.scrollStrategies.reposition();
2100}
2101/** @docs-private */
2102const MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = {
2103 provide: MAT_DATEPICKER_SCROLL_STRATEGY,
2104 deps: [Overlay],
2105 useFactory: MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY,
2106};
2107// Boilerplate for applying mixins to MatDatepickerContent.
2108/** @docs-private */
2109const _MatDatepickerContentBase = mixinColor(class {
2110 constructor(_elementRef) {
2111 this._elementRef = _elementRef;
2112 }
2113});
2114/**
2115 * Component used as the content for the datepicker overlay. We use this instead of using
2116 * MatCalendar directly as the content so we can control the initial focus. This also gives us a
2117 * place to put additional features of the overlay that are not part of the calendar itself in the
2118 * future. (e.g. confirmation buttons).
2119 * @docs-private
2120 */
2121class MatDatepickerContent extends _MatDatepickerContentBase {
2122 constructor(elementRef, _changeDetectorRef, _globalModel, _dateAdapter, _rangeSelectionStrategy, intl) {
2123 super(elementRef);
2124 this._changeDetectorRef = _changeDetectorRef;
2125 this._globalModel = _globalModel;
2126 this._dateAdapter = _dateAdapter;
2127 this._rangeSelectionStrategy = _rangeSelectionStrategy;
2128 this._subscriptions = new Subscription();
2129 /** Emits when an animation has finished. */
2130 this._animationDone = new Subject();
2131 /** Portal with projected action buttons. */
2132 this._actionsPortal = null;
2133 this._closeButtonText = intl.closeCalendarLabel;
2134 }
2135 ngOnInit() {
2136 this._animationState = this.datepicker.touchUi ? 'enter-dialog' : 'enter-dropdown';
2137 }
2138 ngAfterViewInit() {
2139 this._subscriptions.add(this.datepicker.stateChanges.subscribe(() => {
2140 this._changeDetectorRef.markForCheck();
2141 }));
2142 this._calendar.focusActiveCell();
2143 }
2144 ngOnDestroy() {
2145 this._subscriptions.unsubscribe();
2146 this._animationDone.complete();
2147 }
2148 _handleUserSelection(event) {
2149 const selection = this._model.selection;
2150 const value = event.value;
2151 const isRange = selection instanceof DateRange;
2152 // If we're selecting a range and we have a selection strategy, always pass the value through
2153 // there. Otherwise don't assign null values to the model, unless we're selecting a range.
2154 // A null value when picking a range means that the user cancelled the selection (e.g. by
2155 // pressing escape), whereas when selecting a single value it means that the value didn't
2156 // change. This isn't very intuitive, but it's here for backwards-compatibility.
2157 if (isRange && this._rangeSelectionStrategy) {
2158 const newSelection = this._rangeSelectionStrategy.selectionFinished(value, selection, event.event);
2159 this._model.updateSelection(newSelection, this);
2160 }
2161 else if (value &&
2162 (isRange || !this._dateAdapter.sameDate(value, selection))) {
2163 this._model.add(value);
2164 }
2165 // Delegate closing the overlay to the actions.
2166 if ((!this._model || this._model.isComplete()) && !this._actionsPortal) {
2167 this.datepicker.close();
2168 }
2169 }
2170 _startExitAnimation() {
2171 this._animationState = 'void';
2172 this._changeDetectorRef.markForCheck();
2173 }
2174 _getSelected() {
2175 return this._model.selection;
2176 }
2177 /** Applies the current pending selection to the global model. */
2178 _applyPendingSelection() {
2179 if (this._model !== this._globalModel) {
2180 this._globalModel.updateSelection(this._model.selection, this);
2181 }
2182 }
2183 /**
2184 * Assigns a new portal containing the datepicker actions.
2185 * @param portal Portal with the actions to be assigned.
2186 * @param forceRerender Whether a re-render of the portal should be triggered. This isn't
2187 * necessary if the portal is assigned during initialization, but it may be required if it's
2188 * added at a later point.
2189 */
2190 _assignActions(portal, forceRerender) {
2191 // If we have actions, clone the model so that we have the ability to cancel the selection,
2192 // otherwise update the global model directly. Note that we want to assign this as soon as
2193 // possible, but `_actionsPortal` isn't available in the constructor so we do it in `ngOnInit`.
2194 this._model = portal ? this._globalModel.clone() : this._globalModel;
2195 this._actionsPortal = portal;
2196 if (forceRerender) {
2197 this._changeDetectorRef.detectChanges();
2198 }
2199 }
2200}
2201MatDatepickerContent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerContent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: MatDateSelectionModel }, { token: i1$1.DateAdapter }, { token: MAT_DATE_RANGE_SELECTION_STRATEGY, optional: true }, { token: MatDatepickerIntl }], target: i0.ɵɵFactoryTarget.Component });
2202MatDatepickerContent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatDatepickerContent, selector: "mat-datepicker-content", inputs: { color: "color" }, host: { listeners: { "@transformPanel.done": "_animationDone.next()" }, properties: { "@transformPanel": "_animationState", "class.mat-datepicker-content-touch": "datepicker.touchUi" }, classAttribute: "mat-datepicker-content" }, viewQueries: [{ propertyName: "_calendar", first: true, predicate: MatCalendar, descendants: true }], exportAs: ["matDatepickerContent"], usesInheritance: true, ngImport: i0, template: "<div\n cdkTrapFocus\n role=\"dialog\"\n [attr.aria-modal]=\"true\"\n [attr.aria-labelledby]=\"_dialogLabelId ?? undefined\"\n class=\"mat-datepicker-content-container\"\n [class.mat-datepicker-content-container-with-custom-header]=\"datepicker.calendarHeaderComponent\"\n [class.mat-datepicker-content-container-with-actions]=\"_actionsPortal\">\n <mat-calendar\n [id]=\"datepicker.id\"\n [ngClass]=\"datepicker.panelClass\"\n [startAt]=\"datepicker.startAt\"\n [startView]=\"datepicker.startView\"\n [minDate]=\"datepicker._getMinDate()\"\n [maxDate]=\"datepicker._getMaxDate()\"\n [dateFilter]=\"datepicker._getDateFilter()\"\n [headerComponent]=\"datepicker.calendarHeaderComponent\"\n [selected]=\"_getSelected()\"\n [dateClass]=\"datepicker.dateClass\"\n [comparisonStart]=\"comparisonStart\"\n [comparisonEnd]=\"comparisonEnd\"\n [@fadeInCalendar]=\"'enter'\"\n (yearSelected)=\"datepicker._selectYear($event)\"\n (monthSelected)=\"datepicker._selectMonth($event)\"\n (viewChanged)=\"datepicker._viewChanged($event)\"\n (_userSelection)=\"_handleUserSelection($event)\"></mat-calendar>\n\n <ng-template [cdkPortalOutlet]=\"_actionsPortal\"></ng-template>\n\n <!-- Invisible close button for screen reader users. -->\n <button\n type=\"button\"\n mat-raised-button\n [color]=\"color || 'primary'\"\n class=\"mat-datepicker-close-button\"\n [class.cdk-visually-hidden]=\"!_closeButtonFocused\"\n (focus)=\"_closeButtonFocused = true\"\n (blur)=\"_closeButtonFocused = false\"\n (click)=\"datepicker.close()\">{{ _closeButtonText }}</button>\n</div>\n", styles: [".mat-datepicker-content{display:block;border-radius:4px}.mat-datepicker-content .mat-calendar{width:296px;height:354px}.mat-datepicker-content .mat-datepicker-content-container-with-custom-header .mat-calendar{height:auto}.mat-datepicker-content .mat-datepicker-close-button{position:absolute;top:100%;left:0;margin-top:8px}.ng-animating .mat-datepicker-content .mat-datepicker-close-button{display:none}.mat-datepicker-content-container{display:flex;flex-direction:column;justify-content:space-between}.mat-datepicker-content-touch{display:block;max-height:80vh;position:relative;overflow:visible}.mat-datepicker-content-touch .mat-datepicker-content-container{min-height:312px;max-height:788px;min-width:250px;max-width:750px}.mat-datepicker-content-touch .mat-calendar{width:100%;height:auto}@media all and (orientation: landscape){.mat-datepicker-content-touch .mat-datepicker-content-container{width:64vh;height:80vh}}@media all and (orientation: portrait){.mat-datepicker-content-touch .mat-datepicker-content-container{width:80vw;height:100vw}.mat-datepicker-content-touch .mat-datepicker-content-container-with-actions{height:115vw}}"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: i3.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "directive", type: i5.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i6.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "component", type: MatCalendar, selector: "mat-calendar", inputs: ["headerComponent", "startAt", "startView", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "comparisonStart", "comparisonEnd"], outputs: ["selectedChange", "yearSelected", "monthSelected", "viewChanged", "_userSelection"], exportAs: ["matCalendar"] }], animations: [matDatepickerAnimations.transformPanel, matDatepickerAnimations.fadeInCalendar], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2203i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerContent, decorators: [{
2204 type: Component,
2205 args: [{ selector: 'mat-datepicker-content', host: {
2206 'class': 'mat-datepicker-content',
2207 '[@transformPanel]': '_animationState',
2208 '(@transformPanel.done)': '_animationDone.next()',
2209 '[class.mat-datepicker-content-touch]': 'datepicker.touchUi',
2210 }, animations: [matDatepickerAnimations.transformPanel, matDatepickerAnimations.fadeInCalendar], exportAs: 'matDatepickerContent', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, inputs: ['color'], template: "<div\n cdkTrapFocus\n role=\"dialog\"\n [attr.aria-modal]=\"true\"\n [attr.aria-labelledby]=\"_dialogLabelId ?? undefined\"\n class=\"mat-datepicker-content-container\"\n [class.mat-datepicker-content-container-with-custom-header]=\"datepicker.calendarHeaderComponent\"\n [class.mat-datepicker-content-container-with-actions]=\"_actionsPortal\">\n <mat-calendar\n [id]=\"datepicker.id\"\n [ngClass]=\"datepicker.panelClass\"\n [startAt]=\"datepicker.startAt\"\n [startView]=\"datepicker.startView\"\n [minDate]=\"datepicker._getMinDate()\"\n [maxDate]=\"datepicker._getMaxDate()\"\n [dateFilter]=\"datepicker._getDateFilter()\"\n [headerComponent]=\"datepicker.calendarHeaderComponent\"\n [selected]=\"_getSelected()\"\n [dateClass]=\"datepicker.dateClass\"\n [comparisonStart]=\"comparisonStart\"\n [comparisonEnd]=\"comparisonEnd\"\n [@fadeInCalendar]=\"'enter'\"\n (yearSelected)=\"datepicker._selectYear($event)\"\n (monthSelected)=\"datepicker._selectMonth($event)\"\n (viewChanged)=\"datepicker._viewChanged($event)\"\n (_userSelection)=\"_handleUserSelection($event)\"></mat-calendar>\n\n <ng-template [cdkPortalOutlet]=\"_actionsPortal\"></ng-template>\n\n <!-- Invisible close button for screen reader users. -->\n <button\n type=\"button\"\n mat-raised-button\n [color]=\"color || 'primary'\"\n class=\"mat-datepicker-close-button\"\n [class.cdk-visually-hidden]=\"!_closeButtonFocused\"\n (focus)=\"_closeButtonFocused = true\"\n (blur)=\"_closeButtonFocused = false\"\n (click)=\"datepicker.close()\">{{ _closeButtonText }}</button>\n</div>\n", styles: [".mat-datepicker-content{display:block;border-radius:4px}.mat-datepicker-content .mat-calendar{width:296px;height:354px}.mat-datepicker-content .mat-datepicker-content-container-with-custom-header .mat-calendar{height:auto}.mat-datepicker-content .mat-datepicker-close-button{position:absolute;top:100%;left:0;margin-top:8px}.ng-animating .mat-datepicker-content .mat-datepicker-close-button{display:none}.mat-datepicker-content-container{display:flex;flex-direction:column;justify-content:space-between}.mat-datepicker-content-touch{display:block;max-height:80vh;position:relative;overflow:visible}.mat-datepicker-content-touch .mat-datepicker-content-container{min-height:312px;max-height:788px;min-width:250px;max-width:750px}.mat-datepicker-content-touch .mat-calendar{width:100%;height:auto}@media all and (orientation: landscape){.mat-datepicker-content-touch .mat-datepicker-content-container{width:64vh;height:80vh}}@media all and (orientation: portrait){.mat-datepicker-content-touch .mat-datepicker-content-container{width:80vw;height:100vw}.mat-datepicker-content-touch .mat-datepicker-content-container-with-actions{height:115vw}}"] }]
2211 }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: MatDateSelectionModel }, { type: i1$1.DateAdapter }, { type: undefined, decorators: [{
2212 type: Optional
2213 }, {
2214 type: Inject,
2215 args: [MAT_DATE_RANGE_SELECTION_STRATEGY]
2216 }] }, { type: MatDatepickerIntl }]; }, propDecorators: { _calendar: [{
2217 type: ViewChild,
2218 args: [MatCalendar]
2219 }] } });
2220/** Base class for a datepicker. */
2221class MatDatepickerBase {
2222 constructor(_overlay, _ngZone, _viewContainerRef, scrollStrategy, _dateAdapter, _dir, _model) {
2223 this._overlay = _overlay;
2224 this._ngZone = _ngZone;
2225 this._viewContainerRef = _viewContainerRef;
2226 this._dateAdapter = _dateAdapter;
2227 this._dir = _dir;
2228 this._model = _model;
2229 this._inputStateChanges = Subscription.EMPTY;
2230 /** The view that the calendar should start in. */
2231 this.startView = 'month';
2232 this._touchUi = false;
2233 /** Preferred position of the datepicker in the X axis. */
2234 this.xPosition = 'start';
2235 /** Preferred position of the datepicker in the Y axis. */
2236 this.yPosition = 'below';
2237 this._restoreFocus = true;
2238 /**
2239 * Emits selected year in multiyear view.
2240 * This doesn't imply a change on the selected date.
2241 */
2242 this.yearSelected = new EventEmitter();
2243 /**
2244 * Emits selected month in year view.
2245 * This doesn't imply a change on the selected date.
2246 */
2247 this.monthSelected = new EventEmitter();
2248 /**
2249 * Emits when the current view changes.
2250 */
2251 this.viewChanged = new EventEmitter(true);
2252 /** Emits when the datepicker has been opened. */
2253 this.openedStream = new EventEmitter();
2254 /** Emits when the datepicker has been closed. */
2255 this.closedStream = new EventEmitter();
2256 this._opened = false;
2257 /** The id for the datepicker calendar. */
2258 this.id = `mat-datepicker-${datepickerUid++}`;
2259 /** The element that was focused before the datepicker was opened. */
2260 this._focusedElementBeforeOpen = null;
2261 /** Unique class that will be added to the backdrop so that the test harnesses can look it up. */
2262 this._backdropHarnessClass = `${this.id}-backdrop`;
2263 /** Emits when the datepicker's state changes. */
2264 this.stateChanges = new Subject();
2265 if (!this._dateAdapter && (typeof ngDevMode === 'undefined' || ngDevMode)) {
2266 throw createMissingDateImplError('DateAdapter');
2267 }
2268 this._scrollStrategy = scrollStrategy;
2269 }
2270 /** The date to open the calendar to initially. */
2271 get startAt() {
2272 // If an explicit startAt is set we start there, otherwise we start at whatever the currently
2273 // selected value is.
2274 return this._startAt || (this.datepickerInput ? this.datepickerInput.getStartValue() : null);
2275 }
2276 set startAt(value) {
2277 this._startAt = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
2278 }
2279 /** Color palette to use on the datepicker's calendar. */
2280 get color() {
2281 return (this._color || (this.datepickerInput ? this.datepickerInput.getThemePalette() : undefined));
2282 }
2283 set color(value) {
2284 this._color = value;
2285 }
2286 /**
2287 * Whether the calendar UI is in touch mode. In touch mode the calendar opens in a dialog rather
2288 * than a dropdown and elements have more padding to allow for bigger touch targets.
2289 */
2290 get touchUi() {
2291 return this._touchUi;
2292 }
2293 set touchUi(value) {
2294 this._touchUi = coerceBooleanProperty(value);
2295 }
2296 /** Whether the datepicker pop-up should be disabled. */
2297 get disabled() {
2298 return this._disabled === undefined && this.datepickerInput
2299 ? this.datepickerInput.disabled
2300 : !!this._disabled;
2301 }
2302 set disabled(value) {
2303 const newValue = coerceBooleanProperty(value);
2304 if (newValue !== this._disabled) {
2305 this._disabled = newValue;
2306 this.stateChanges.next(undefined);
2307 }
2308 }
2309 /**
2310 * Whether to restore focus to the previously-focused element when the calendar is closed.
2311 * Note that automatic focus restoration is an accessibility feature and it is recommended that
2312 * you provide your own equivalent, if you decide to turn it off.
2313 */
2314 get restoreFocus() {
2315 return this._restoreFocus;
2316 }
2317 set restoreFocus(value) {
2318 this._restoreFocus = coerceBooleanProperty(value);
2319 }
2320 /**
2321 * Classes to be passed to the date picker panel.
2322 * Supports string and string array values, similar to `ngClass`.
2323 */
2324 get panelClass() {
2325 return this._panelClass;
2326 }
2327 set panelClass(value) {
2328 this._panelClass = coerceStringArray(value);
2329 }
2330 /** Whether the calendar is open. */
2331 get opened() {
2332 return this._opened;
2333 }
2334 set opened(value) {
2335 coerceBooleanProperty(value) ? this.open() : this.close();
2336 }
2337 /** The minimum selectable date. */
2338 _getMinDate() {
2339 return this.datepickerInput && this.datepickerInput.min;
2340 }
2341 /** The maximum selectable date. */
2342 _getMaxDate() {
2343 return this.datepickerInput && this.datepickerInput.max;
2344 }
2345 _getDateFilter() {
2346 return this.datepickerInput && this.datepickerInput.dateFilter;
2347 }
2348 ngOnChanges(changes) {
2349 const positionChange = changes['xPosition'] || changes['yPosition'];
2350 if (positionChange && !positionChange.firstChange && this._overlayRef) {
2351 const positionStrategy = this._overlayRef.getConfig().positionStrategy;
2352 if (positionStrategy instanceof FlexibleConnectedPositionStrategy) {
2353 this._setConnectedPositions(positionStrategy);
2354 if (this.opened) {
2355 this._overlayRef.updatePosition();
2356 }
2357 }
2358 }
2359 this.stateChanges.next(undefined);
2360 }
2361 ngOnDestroy() {
2362 this._destroyOverlay();
2363 this.close();
2364 this._inputStateChanges.unsubscribe();
2365 this.stateChanges.complete();
2366 }
2367 /** Selects the given date */
2368 select(date) {
2369 this._model.add(date);
2370 }
2371 /** Emits the selected year in multiyear view */
2372 _selectYear(normalizedYear) {
2373 this.yearSelected.emit(normalizedYear);
2374 }
2375 /** Emits selected month in year view */
2376 _selectMonth(normalizedMonth) {
2377 this.monthSelected.emit(normalizedMonth);
2378 }
2379 /** Emits changed view */
2380 _viewChanged(view) {
2381 this.viewChanged.emit(view);
2382 }
2383 /**
2384 * Register an input with this datepicker.
2385 * @param input The datepicker input to register with this datepicker.
2386 * @returns Selection model that the input should hook itself up to.
2387 */
2388 registerInput(input) {
2389 if (this.datepickerInput && (typeof ngDevMode === 'undefined' || ngDevMode)) {
2390 throw Error('A MatDatepicker can only be associated with a single input.');
2391 }
2392 this._inputStateChanges.unsubscribe();
2393 this.datepickerInput = input;
2394 this._inputStateChanges = input.stateChanges.subscribe(() => this.stateChanges.next(undefined));
2395 return this._model;
2396 }
2397 /**
2398 * Registers a portal containing action buttons with the datepicker.
2399 * @param portal Portal to be registered.
2400 */
2401 registerActions(portal) {
2402 if (this._actionsPortal && (typeof ngDevMode === 'undefined' || ngDevMode)) {
2403 throw Error('A MatDatepicker can only be associated with a single actions row.');
2404 }
2405 this._actionsPortal = portal;
2406 this._componentRef?.instance._assignActions(portal, true);
2407 }
2408 /**
2409 * Removes a portal containing action buttons from the datepicker.
2410 * @param portal Portal to be removed.
2411 */
2412 removeActions(portal) {
2413 if (portal === this._actionsPortal) {
2414 this._actionsPortal = null;
2415 this._componentRef?.instance._assignActions(null, true);
2416 }
2417 }
2418 /** Open the calendar. */
2419 open() {
2420 if (this._opened || this.disabled) {
2421 return;
2422 }
2423 if (!this.datepickerInput && (typeof ngDevMode === 'undefined' || ngDevMode)) {
2424 throw Error('Attempted to open an MatDatepicker with no associated input.');
2425 }
2426 this._focusedElementBeforeOpen = _getFocusedElementPierceShadowDom();
2427 this._openOverlay();
2428 this._opened = true;
2429 this.openedStream.emit();
2430 }
2431 /** Close the calendar. */
2432 close() {
2433 if (!this._opened) {
2434 return;
2435 }
2436 if (this._componentRef) {
2437 const instance = this._componentRef.instance;
2438 instance._startExitAnimation();
2439 instance._animationDone.pipe(take(1)).subscribe(() => this._destroyOverlay());
2440 }
2441 const completeClose = () => {
2442 // The `_opened` could've been reset already if
2443 // we got two events in quick succession.
2444 if (this._opened) {
2445 this._opened = false;
2446 this.closedStream.emit();
2447 this._focusedElementBeforeOpen = null;
2448 }
2449 };
2450 if (this._restoreFocus &&
2451 this._focusedElementBeforeOpen &&
2452 typeof this._focusedElementBeforeOpen.focus === 'function') {
2453 // Because IE moves focus asynchronously, we can't count on it being restored before we've
2454 // marked the datepicker as closed. If the event fires out of sequence and the element that
2455 // we're refocusing opens the datepicker on focus, the user could be stuck with not being
2456 // able to close the calendar at all. We work around it by making the logic, that marks
2457 // the datepicker as closed, async as well.
2458 this._focusedElementBeforeOpen.focus();
2459 setTimeout(completeClose);
2460 }
2461 else {
2462 completeClose();
2463 }
2464 }
2465 /** Applies the current pending selection on the overlay to the model. */
2466 _applyPendingSelection() {
2467 this._componentRef?.instance?._applyPendingSelection();
2468 }
2469 /** Forwards relevant values from the datepicker to the datepicker content inside the overlay. */
2470 _forwardContentValues(instance) {
2471 instance.datepicker = this;
2472 instance.color = this.color;
2473 instance._dialogLabelId = this.datepickerInput.getOverlayLabelId();
2474 instance._assignActions(this._actionsPortal, false);
2475 }
2476 /** Opens the overlay with the calendar. */
2477 _openOverlay() {
2478 this._destroyOverlay();
2479 const isDialog = this.touchUi;
2480 const portal = new ComponentPortal(MatDatepickerContent, this._viewContainerRef);
2481 const overlayRef = (this._overlayRef = this._overlay.create(new OverlayConfig({
2482 positionStrategy: isDialog ? this._getDialogStrategy() : this._getDropdownStrategy(),
2483 hasBackdrop: true,
2484 backdropClass: [
2485 isDialog ? 'cdk-overlay-dark-backdrop' : 'mat-overlay-transparent-backdrop',
2486 this._backdropHarnessClass,
2487 ],
2488 direction: this._dir,
2489 scrollStrategy: isDialog ? this._overlay.scrollStrategies.block() : this._scrollStrategy(),
2490 panelClass: `mat-datepicker-${isDialog ? 'dialog' : 'popup'}`,
2491 })));
2492 this._getCloseStream(overlayRef).subscribe(event => {
2493 if (event) {
2494 event.preventDefault();
2495 }
2496 this.close();
2497 });
2498 // The `preventDefault` call happens inside the calendar as well, however focus moves into
2499 // it inside a timeout which can give browsers a chance to fire off a keyboard event in-between
2500 // that can scroll the page (see #24969). Always block default actions of arrow keys for the
2501 // entire overlay so the page doesn't get scrolled by accident.
2502 overlayRef.keydownEvents().subscribe(event => {
2503 const keyCode = event.keyCode;
2504 if (keyCode === UP_ARROW ||
2505 keyCode === DOWN_ARROW ||
2506 keyCode === LEFT_ARROW ||
2507 keyCode === RIGHT_ARROW ||
2508 keyCode === PAGE_UP ||
2509 keyCode === PAGE_DOWN) {
2510 event.preventDefault();
2511 }
2512 });
2513 this._componentRef = overlayRef.attach(portal);
2514 this._forwardContentValues(this._componentRef.instance);
2515 // Update the position once the calendar has rendered. Only relevant in dropdown mode.
2516 if (!isDialog) {
2517 this._ngZone.onStable.pipe(take(1)).subscribe(() => overlayRef.updatePosition());
2518 }
2519 }
2520 /** Destroys the current overlay. */
2521 _destroyOverlay() {
2522 if (this._overlayRef) {
2523 this._overlayRef.dispose();
2524 this._overlayRef = this._componentRef = null;
2525 }
2526 }
2527 /** Gets a position strategy that will open the calendar as a dropdown. */
2528 _getDialogStrategy() {
2529 return this._overlay.position().global().centerHorizontally().centerVertically();
2530 }
2531 /** Gets a position strategy that will open the calendar as a dropdown. */
2532 _getDropdownStrategy() {
2533 const strategy = this._overlay
2534 .position()
2535 .flexibleConnectedTo(this.datepickerInput.getConnectedOverlayOrigin())
2536 .withTransformOriginOn('.mat-datepicker-content')
2537 .withFlexibleDimensions(false)
2538 .withViewportMargin(8)
2539 .withLockedPosition();
2540 return this._setConnectedPositions(strategy);
2541 }
2542 /** Sets the positions of the datepicker in dropdown mode based on the current configuration. */
2543 _setConnectedPositions(strategy) {
2544 const primaryX = this.xPosition === 'end' ? 'end' : 'start';
2545 const secondaryX = primaryX === 'start' ? 'end' : 'start';
2546 const primaryY = this.yPosition === 'above' ? 'bottom' : 'top';
2547 const secondaryY = primaryY === 'top' ? 'bottom' : 'top';
2548 return strategy.withPositions([
2549 {
2550 originX: primaryX,
2551 originY: secondaryY,
2552 overlayX: primaryX,
2553 overlayY: primaryY,
2554 },
2555 {
2556 originX: primaryX,
2557 originY: primaryY,
2558 overlayX: primaryX,
2559 overlayY: secondaryY,
2560 },
2561 {
2562 originX: secondaryX,
2563 originY: secondaryY,
2564 overlayX: secondaryX,
2565 overlayY: primaryY,
2566 },
2567 {
2568 originX: secondaryX,
2569 originY: primaryY,
2570 overlayX: secondaryX,
2571 overlayY: secondaryY,
2572 },
2573 ]);
2574 }
2575 /** Gets an observable that will emit when the overlay is supposed to be closed. */
2576 _getCloseStream(overlayRef) {
2577 return merge(overlayRef.backdropClick(), overlayRef.detachments(), overlayRef.keydownEvents().pipe(filter(event => {
2578 // Closing on alt + up is only valid when there's an input associated with the datepicker.
2579 return ((event.keyCode === ESCAPE && !hasModifierKey(event)) ||
2580 (this.datepickerInput && hasModifierKey(event, 'altKey') && event.keyCode === UP_ARROW));
2581 })));
2582 }
2583}
2584MatDatepickerBase.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerBase, deps: [{ token: i9.Overlay }, { token: i0.NgZone }, { token: i0.ViewContainerRef }, { token: MAT_DATEPICKER_SCROLL_STRATEGY }, { token: i1$1.DateAdapter, optional: true }, { token: i2.Directionality, optional: true }, { token: MatDateSelectionModel }], target: i0.ɵɵFactoryTarget.Directive });
2585MatDatepickerBase.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: MatDatepickerBase, inputs: { calendarHeaderComponent: "calendarHeaderComponent", startAt: "startAt", startView: "startView", color: "color", touchUi: "touchUi", disabled: "disabled", xPosition: "xPosition", yPosition: "yPosition", restoreFocus: "restoreFocus", dateClass: "dateClass", panelClass: "panelClass", opened: "opened" }, outputs: { yearSelected: "yearSelected", monthSelected: "monthSelected", viewChanged: "viewChanged", openedStream: "opened", closedStream: "closed" }, usesOnChanges: true, ngImport: i0 });
2586i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerBase, decorators: [{
2587 type: Directive
2588 }], ctorParameters: function () { return [{ type: i9.Overlay }, { type: i0.NgZone }, { type: i0.ViewContainerRef }, { type: undefined, decorators: [{
2589 type: Inject,
2590 args: [MAT_DATEPICKER_SCROLL_STRATEGY]
2591 }] }, { type: i1$1.DateAdapter, decorators: [{
2592 type: Optional
2593 }] }, { type: i2.Directionality, decorators: [{
2594 type: Optional
2595 }] }, { type: MatDateSelectionModel }]; }, propDecorators: { calendarHeaderComponent: [{
2596 type: Input
2597 }], startAt: [{
2598 type: Input
2599 }], startView: [{
2600 type: Input
2601 }], color: [{
2602 type: Input
2603 }], touchUi: [{
2604 type: Input
2605 }], disabled: [{
2606 type: Input
2607 }], xPosition: [{
2608 type: Input
2609 }], yPosition: [{
2610 type: Input
2611 }], restoreFocus: [{
2612 type: Input
2613 }], yearSelected: [{
2614 type: Output
2615 }], monthSelected: [{
2616 type: Output
2617 }], viewChanged: [{
2618 type: Output
2619 }], dateClass: [{
2620 type: Input
2621 }], openedStream: [{
2622 type: Output,
2623 args: ['opened']
2624 }], closedStream: [{
2625 type: Output,
2626 args: ['closed']
2627 }], panelClass: [{
2628 type: Input
2629 }], opened: [{
2630 type: Input
2631 }] } });
2632
2633/**
2634 * @license
2635 * Copyright Google LLC All Rights Reserved.
2636 *
2637 * Use of this source code is governed by an MIT-style license that can be
2638 * found in the LICENSE file at https://angular.io/license
2639 */
2640// TODO(mmalerba): We use a component instead of a directive here so the user can use implicit
2641// template reference variables (e.g. #d vs #d="matDatepicker"). We can change this to a directive
2642// if angular adds support for `exportAs: '$implicit'` on directives.
2643/** Component responsible for managing the datepicker popup/dialog. */
2644class MatDatepicker extends MatDatepickerBase {
2645}
2646MatDatepicker.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepicker, deps: null, target: i0.ɵɵFactoryTarget.Component });
2647MatDatepicker.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatDatepicker, selector: "mat-datepicker", providers: [
2648 MAT_SINGLE_DATE_SELECTION_MODEL_PROVIDER,
2649 { provide: MatDatepickerBase, useExisting: MatDatepicker },
2650 ], exportAs: ["matDatepicker"], usesInheritance: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2651i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepicker, decorators: [{
2652 type: Component,
2653 args: [{
2654 selector: 'mat-datepicker',
2655 template: '',
2656 exportAs: 'matDatepicker',
2657 changeDetection: ChangeDetectionStrategy.OnPush,
2658 encapsulation: ViewEncapsulation.None,
2659 providers: [
2660 MAT_SINGLE_DATE_SELECTION_MODEL_PROVIDER,
2661 { provide: MatDatepickerBase, useExisting: MatDatepicker },
2662 ],
2663 }]
2664 }] });
2665
2666/**
2667 * @license
2668 * Copyright Google LLC All Rights Reserved.
2669 *
2670 * Use of this source code is governed by an MIT-style license that can be
2671 * found in the LICENSE file at https://angular.io/license
2672 */
2673/**
2674 * An event used for datepicker input and change events. We don't always have access to a native
2675 * input or change event because the event may have been triggered by the user clicking on the
2676 * calendar popup. For consistency, we always use MatDatepickerInputEvent instead.
2677 */
2678class MatDatepickerInputEvent {
2679 constructor(
2680 /** Reference to the datepicker input component that emitted the event. */
2681 target,
2682 /** Reference to the native input element associated with the datepicker input. */
2683 targetElement) {
2684 this.target = target;
2685 this.targetElement = targetElement;
2686 this.value = this.target.value;
2687 }
2688}
2689/** Base class for datepicker inputs. */
2690class MatDatepickerInputBase {
2691 constructor(_elementRef, _dateAdapter, _dateFormats) {
2692 this._elementRef = _elementRef;
2693 this._dateAdapter = _dateAdapter;
2694 this._dateFormats = _dateFormats;
2695 /** Emits when a `change` event is fired on this `<input>`. */
2696 this.dateChange = new EventEmitter();
2697 /** Emits when an `input` event is fired on this `<input>`. */
2698 this.dateInput = new EventEmitter();
2699 /** Emits when the internal state has changed */
2700 this.stateChanges = new Subject();
2701 this._onTouched = () => { };
2702 this._validatorOnChange = () => { };
2703 this._cvaOnChange = () => { };
2704 this._valueChangesSubscription = Subscription.EMPTY;
2705 this._localeSubscription = Subscription.EMPTY;
2706 /** The form control validator for whether the input parses. */
2707 this._parseValidator = () => {
2708 return this._lastValueValid
2709 ? null
2710 : { 'matDatepickerParse': { 'text': this._elementRef.nativeElement.value } };
2711 };
2712 /** The form control validator for the date filter. */
2713 this._filterValidator = (control) => {
2714 const controlValue = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(control.value));
2715 return !controlValue || this._matchesFilter(controlValue)
2716 ? null
2717 : { 'matDatepickerFilter': true };
2718 };
2719 /** The form control validator for the min date. */
2720 this._minValidator = (control) => {
2721 const controlValue = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(control.value));
2722 const min = this._getMinDate();
2723 return !min || !controlValue || this._dateAdapter.compareDate(min, controlValue) <= 0
2724 ? null
2725 : { 'matDatepickerMin': { 'min': min, 'actual': controlValue } };
2726 };
2727 /** The form control validator for the max date. */
2728 this._maxValidator = (control) => {
2729 const controlValue = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(control.value));
2730 const max = this._getMaxDate();
2731 return !max || !controlValue || this._dateAdapter.compareDate(max, controlValue) >= 0
2732 ? null
2733 : { 'matDatepickerMax': { 'max': max, 'actual': controlValue } };
2734 };
2735 /** Whether the last value set on the input was valid. */
2736 this._lastValueValid = false;
2737 if (typeof ngDevMode === 'undefined' || ngDevMode) {
2738 if (!this._dateAdapter) {
2739 throw createMissingDateImplError('DateAdapter');
2740 }
2741 if (!this._dateFormats) {
2742 throw createMissingDateImplError('MAT_DATE_FORMATS');
2743 }
2744 }
2745 // Update the displayed date when the locale changes.
2746 this._localeSubscription = _dateAdapter.localeChanges.subscribe(() => {
2747 this._assignValueProgrammatically(this.value);
2748 });
2749 }
2750 /** The value of the input. */
2751 get value() {
2752 return this._model ? this._getValueFromModel(this._model.selection) : this._pendingValue;
2753 }
2754 set value(value) {
2755 this._assignValueProgrammatically(value);
2756 }
2757 /** Whether the datepicker-input is disabled. */
2758 get disabled() {
2759 return !!this._disabled || this._parentDisabled();
2760 }
2761 set disabled(value) {
2762 const newValue = coerceBooleanProperty(value);
2763 const element = this._elementRef.nativeElement;
2764 if (this._disabled !== newValue) {
2765 this._disabled = newValue;
2766 this.stateChanges.next(undefined);
2767 }
2768 // We need to null check the `blur` method, because it's undefined during SSR.
2769 // In Ivy static bindings are invoked earlier, before the element is attached to the DOM.
2770 // This can cause an error to be thrown in some browsers (IE/Edge) which assert that the
2771 // element has been inserted.
2772 if (newValue && this._isInitialized && element.blur) {
2773 // Normally, native input elements automatically blur if they turn disabled. This behavior
2774 // is problematic, because it would mean that it triggers another change detection cycle,
2775 // which then causes a changed after checked error if the input element was focused before.
2776 element.blur();
2777 }
2778 }
2779 /** Gets the base validator functions. */
2780 _getValidators() {
2781 return [this._parseValidator, this._minValidator, this._maxValidator, this._filterValidator];
2782 }
2783 /** Registers a date selection model with the input. */
2784 _registerModel(model) {
2785 this._model = model;
2786 this._valueChangesSubscription.unsubscribe();
2787 if (this._pendingValue) {
2788 this._assignValue(this._pendingValue);
2789 }
2790 this._valueChangesSubscription = this._model.selectionChanged.subscribe(event => {
2791 if (this._shouldHandleChangeEvent(event)) {
2792 const value = this._getValueFromModel(event.selection);
2793 this._lastValueValid = this._isValidValue(value);
2794 this._cvaOnChange(value);
2795 this._onTouched();
2796 this._formatValue(value);
2797 this.dateInput.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
2798 this.dateChange.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
2799 }
2800 });
2801 }
2802 ngAfterViewInit() {
2803 this._isInitialized = true;
2804 }
2805 ngOnChanges(changes) {
2806 if (dateInputsHaveChanged(changes, this._dateAdapter)) {
2807 this.stateChanges.next(undefined);
2808 }
2809 }
2810 ngOnDestroy() {
2811 this._valueChangesSubscription.unsubscribe();
2812 this._localeSubscription.unsubscribe();
2813 this.stateChanges.complete();
2814 }
2815 /** @docs-private */
2816 registerOnValidatorChange(fn) {
2817 this._validatorOnChange = fn;
2818 }
2819 /** @docs-private */
2820 validate(c) {
2821 return this._validator ? this._validator(c) : null;
2822 }
2823 // Implemented as part of ControlValueAccessor.
2824 writeValue(value) {
2825 this._assignValueProgrammatically(value);
2826 }
2827 // Implemented as part of ControlValueAccessor.
2828 registerOnChange(fn) {
2829 this._cvaOnChange = fn;
2830 }
2831 // Implemented as part of ControlValueAccessor.
2832 registerOnTouched(fn) {
2833 this._onTouched = fn;
2834 }
2835 // Implemented as part of ControlValueAccessor.
2836 setDisabledState(isDisabled) {
2837 this.disabled = isDisabled;
2838 }
2839 _onKeydown(event) {
2840 const isAltDownArrow = event.altKey && event.keyCode === DOWN_ARROW;
2841 if (isAltDownArrow && !this._elementRef.nativeElement.readOnly) {
2842 this._openPopup();
2843 event.preventDefault();
2844 }
2845 }
2846 _onInput(value) {
2847 const lastValueWasValid = this._lastValueValid;
2848 let date = this._dateAdapter.parse(value, this._dateFormats.parse.dateInput);
2849 this._lastValueValid = this._isValidValue(date);
2850 date = this._dateAdapter.getValidDateOrNull(date);
2851 const hasChanged = !this._dateAdapter.sameDate(date, this.value);
2852 // We need to fire the CVA change event for all
2853 // nulls, otherwise the validators won't run.
2854 if (!date || hasChanged) {
2855 this._cvaOnChange(date);
2856 }
2857 else {
2858 // Call the CVA change handler for invalid values
2859 // since this is what marks the control as dirty.
2860 if (value && !this.value) {
2861 this._cvaOnChange(date);
2862 }
2863 if (lastValueWasValid !== this._lastValueValid) {
2864 this._validatorOnChange();
2865 }
2866 }
2867 if (hasChanged) {
2868 this._assignValue(date);
2869 this.dateInput.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
2870 }
2871 }
2872 _onChange() {
2873 this.dateChange.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));
2874 }
2875 /** Handles blur events on the input. */
2876 _onBlur() {
2877 // Reformat the input only if we have a valid value.
2878 if (this.value) {
2879 this._formatValue(this.value);
2880 }
2881 this._onTouched();
2882 }
2883 /** Formats a value and sets it on the input element. */
2884 _formatValue(value) {
2885 this._elementRef.nativeElement.value =
2886 value != null ? this._dateAdapter.format(value, this._dateFormats.display.dateInput) : '';
2887 }
2888 /** Assigns a value to the model. */
2889 _assignValue(value) {
2890 // We may get some incoming values before the model was
2891 // assigned. Save the value so that we can assign it later.
2892 if (this._model) {
2893 this._assignValueToModel(value);
2894 this._pendingValue = null;
2895 }
2896 else {
2897 this._pendingValue = value;
2898 }
2899 }
2900 /** Whether a value is considered valid. */
2901 _isValidValue(value) {
2902 return !value || this._dateAdapter.isValid(value);
2903 }
2904 /**
2905 * Checks whether a parent control is disabled. This is in place so that it can be overridden
2906 * by inputs extending this one which can be placed inside of a group that can be disabled.
2907 */
2908 _parentDisabled() {
2909 return false;
2910 }
2911 /** Programmatically assigns a value to the input. */
2912 _assignValueProgrammatically(value) {
2913 value = this._dateAdapter.deserialize(value);
2914 this._lastValueValid = this._isValidValue(value);
2915 value = this._dateAdapter.getValidDateOrNull(value);
2916 this._assignValue(value);
2917 this._formatValue(value);
2918 }
2919 /** Gets whether a value matches the current date filter. */
2920 _matchesFilter(value) {
2921 const filter = this._getDateFilter();
2922 return !filter || filter(value);
2923 }
2924}
2925MatDatepickerInputBase.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerInputBase, deps: [{ token: i0.ElementRef }, { token: i1$1.DateAdapter, optional: true }, { token: MAT_DATE_FORMATS, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
2926MatDatepickerInputBase.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: MatDatepickerInputBase, inputs: { value: "value", disabled: "disabled" }, outputs: { dateChange: "dateChange", dateInput: "dateInput" }, usesOnChanges: true, ngImport: i0 });
2927i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerInputBase, decorators: [{
2928 type: Directive
2929 }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1$1.DateAdapter, decorators: [{
2930 type: Optional
2931 }] }, { type: undefined, decorators: [{
2932 type: Optional
2933 }, {
2934 type: Inject,
2935 args: [MAT_DATE_FORMATS]
2936 }] }]; }, propDecorators: { value: [{
2937 type: Input
2938 }], disabled: [{
2939 type: Input
2940 }], dateChange: [{
2941 type: Output
2942 }], dateInput: [{
2943 type: Output
2944 }] } });
2945/**
2946 * Checks whether the `SimpleChanges` object from an `ngOnChanges`
2947 * callback has any changes, accounting for date objects.
2948 */
2949function dateInputsHaveChanged(changes, adapter) {
2950 const keys = Object.keys(changes);
2951 for (let key of keys) {
2952 const { previousValue, currentValue } = changes[key];
2953 if (adapter.isDateInstance(previousValue) && adapter.isDateInstance(currentValue)) {
2954 if (!adapter.sameDate(previousValue, currentValue)) {
2955 return true;
2956 }
2957 }
2958 else {
2959 return true;
2960 }
2961 }
2962 return false;
2963}
2964
2965/**
2966 * @license
2967 * Copyright Google LLC All Rights Reserved.
2968 *
2969 * Use of this source code is governed by an MIT-style license that can be
2970 * found in the LICENSE file at https://angular.io/license
2971 */
2972/** @docs-private */
2973const MAT_DATEPICKER_VALUE_ACCESSOR = {
2974 provide: NG_VALUE_ACCESSOR,
2975 useExisting: forwardRef(() => MatDatepickerInput),
2976 multi: true,
2977};
2978/** @docs-private */
2979const MAT_DATEPICKER_VALIDATORS = {
2980 provide: NG_VALIDATORS,
2981 useExisting: forwardRef(() => MatDatepickerInput),
2982 multi: true,
2983};
2984/** Directive used to connect an input to a MatDatepicker. */
2985class MatDatepickerInput extends MatDatepickerInputBase {
2986 constructor(elementRef, dateAdapter, dateFormats, _formField) {
2987 super(elementRef, dateAdapter, dateFormats);
2988 this._formField = _formField;
2989 this._closedSubscription = Subscription.EMPTY;
2990 this._validator = Validators.compose(super._getValidators());
2991 }
2992 /** The datepicker that this input is associated with. */
2993 set matDatepicker(datepicker) {
2994 if (datepicker) {
2995 this._datepicker = datepicker;
2996 this._closedSubscription = datepicker.closedStream.subscribe(() => this._onTouched());
2997 this._registerModel(datepicker.registerInput(this));
2998 }
2999 }
3000 /** The minimum valid date. */
3001 get min() {
3002 return this._min;
3003 }
3004 set min(value) {
3005 const validValue = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
3006 if (!this._dateAdapter.sameDate(validValue, this._min)) {
3007 this._min = validValue;
3008 this._validatorOnChange();
3009 }
3010 }
3011 /** The maximum valid date. */
3012 get max() {
3013 return this._max;
3014 }
3015 set max(value) {
3016 const validValue = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
3017 if (!this._dateAdapter.sameDate(validValue, this._max)) {
3018 this._max = validValue;
3019 this._validatorOnChange();
3020 }
3021 }
3022 /** Function that can be used to filter out dates within the datepicker. */
3023 get dateFilter() {
3024 return this._dateFilter;
3025 }
3026 set dateFilter(value) {
3027 const wasMatchingValue = this._matchesFilter(this.value);
3028 this._dateFilter = value;
3029 if (this._matchesFilter(this.value) !== wasMatchingValue) {
3030 this._validatorOnChange();
3031 }
3032 }
3033 /**
3034 * Gets the element that the datepicker popup should be connected to.
3035 * @return The element to connect the popup to.
3036 */
3037 getConnectedOverlayOrigin() {
3038 return this._formField ? this._formField.getConnectedOverlayOrigin() : this._elementRef;
3039 }
3040 /** Gets the ID of an element that should be used a description for the calendar overlay. */
3041 getOverlayLabelId() {
3042 if (this._formField) {
3043 return this._formField.getLabelId();
3044 }
3045 return this._elementRef.nativeElement.getAttribute('aria-labelledby');
3046 }
3047 /** Returns the palette used by the input's form field, if any. */
3048 getThemePalette() {
3049 return this._formField ? this._formField.color : undefined;
3050 }
3051 /** Gets the value at which the calendar should start. */
3052 getStartValue() {
3053 return this.value;
3054 }
3055 ngOnDestroy() {
3056 super.ngOnDestroy();
3057 this._closedSubscription.unsubscribe();
3058 }
3059 /** Opens the associated datepicker. */
3060 _openPopup() {
3061 if (this._datepicker) {
3062 this._datepicker.open();
3063 }
3064 }
3065 _getValueFromModel(modelValue) {
3066 return modelValue;
3067 }
3068 _assignValueToModel(value) {
3069 if (this._model) {
3070 this._model.updateSelection(value, this);
3071 }
3072 }
3073 /** Gets the input's minimum date. */
3074 _getMinDate() {
3075 return this._min;
3076 }
3077 /** Gets the input's maximum date. */
3078 _getMaxDate() {
3079 return this._max;
3080 }
3081 /** Gets the input's date filtering function. */
3082 _getDateFilter() {
3083 return this._dateFilter;
3084 }
3085 _shouldHandleChangeEvent(event) {
3086 return event.source !== this;
3087 }
3088}
3089MatDatepickerInput.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerInput, deps: [{ token: i0.ElementRef }, { token: i1$1.DateAdapter, optional: true }, { token: MAT_DATE_FORMATS, optional: true }, { token: MAT_FORM_FIELD, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3090MatDatepickerInput.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: MatDatepickerInput, selector: "input[matDatepicker]", inputs: { matDatepicker: "matDatepicker", min: "min", max: "max", dateFilter: ["matDatepickerFilter", "dateFilter"] }, host: { listeners: { "input": "_onInput($event.target.value)", "change": "_onChange()", "blur": "_onBlur()", "keydown": "_onKeydown($event)" }, properties: { "attr.aria-haspopup": "_datepicker ? \"dialog\" : null", "attr.aria-owns": "(_datepicker?.opened && _datepicker.id) || null", "attr.min": "min ? _dateAdapter.toIso8601(min) : null", "attr.max": "max ? _dateAdapter.toIso8601(max) : null", "attr.data-mat-calendar": "_datepicker ? _datepicker.id : null", "disabled": "disabled" }, classAttribute: "mat-datepicker-input" }, providers: [
3091 MAT_DATEPICKER_VALUE_ACCESSOR,
3092 MAT_DATEPICKER_VALIDATORS,
3093 { provide: MAT_INPUT_VALUE_ACCESSOR, useExisting: MatDatepickerInput },
3094 ], exportAs: ["matDatepickerInput"], usesInheritance: true, ngImport: i0 });
3095i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerInput, decorators: [{
3096 type: Directive,
3097 args: [{
3098 selector: 'input[matDatepicker]',
3099 providers: [
3100 MAT_DATEPICKER_VALUE_ACCESSOR,
3101 MAT_DATEPICKER_VALIDATORS,
3102 { provide: MAT_INPUT_VALUE_ACCESSOR, useExisting: MatDatepickerInput },
3103 ],
3104 host: {
3105 'class': 'mat-datepicker-input',
3106 '[attr.aria-haspopup]': '_datepicker ? "dialog" : null',
3107 '[attr.aria-owns]': '(_datepicker?.opened && _datepicker.id) || null',
3108 '[attr.min]': 'min ? _dateAdapter.toIso8601(min) : null',
3109 '[attr.max]': 'max ? _dateAdapter.toIso8601(max) : null',
3110 // Used by the test harness to tie this input to its calendar. We can't depend on
3111 // `aria-owns` for this, because it's only defined while the calendar is open.
3112 '[attr.data-mat-calendar]': '_datepicker ? _datepicker.id : null',
3113 '[disabled]': 'disabled',
3114 '(input)': '_onInput($event.target.value)',
3115 '(change)': '_onChange()',
3116 '(blur)': '_onBlur()',
3117 '(keydown)': '_onKeydown($event)',
3118 },
3119 exportAs: 'matDatepickerInput',
3120 }]
3121 }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1$1.DateAdapter, decorators: [{
3122 type: Optional
3123 }] }, { type: undefined, decorators: [{
3124 type: Optional
3125 }, {
3126 type: Inject,
3127 args: [MAT_DATE_FORMATS]
3128 }] }, { type: i2$1.MatFormField, decorators: [{
3129 type: Optional
3130 }, {
3131 type: Inject,
3132 args: [MAT_FORM_FIELD]
3133 }] }]; }, propDecorators: { matDatepicker: [{
3134 type: Input
3135 }], min: [{
3136 type: Input
3137 }], max: [{
3138 type: Input
3139 }], dateFilter: [{
3140 type: Input,
3141 args: ['matDatepickerFilter']
3142 }] } });
3143
3144/**
3145 * @license
3146 * Copyright Google LLC All Rights Reserved.
3147 *
3148 * Use of this source code is governed by an MIT-style license that can be
3149 * found in the LICENSE file at https://angular.io/license
3150 */
3151/** Can be used to override the icon of a `matDatepickerToggle`. */
3152class MatDatepickerToggleIcon {
3153}
3154MatDatepickerToggleIcon.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerToggleIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3155MatDatepickerToggleIcon.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: MatDatepickerToggleIcon, selector: "[matDatepickerToggleIcon]", ngImport: i0 });
3156i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerToggleIcon, decorators: [{
3157 type: Directive,
3158 args: [{
3159 selector: '[matDatepickerToggleIcon]',
3160 }]
3161 }] });
3162class MatDatepickerToggle {
3163 constructor(_intl, _changeDetectorRef, defaultTabIndex) {
3164 this._intl = _intl;
3165 this._changeDetectorRef = _changeDetectorRef;
3166 this._stateChanges = Subscription.EMPTY;
3167 const parsedTabIndex = Number(defaultTabIndex);
3168 this.tabIndex = parsedTabIndex || parsedTabIndex === 0 ? parsedTabIndex : null;
3169 }
3170 /** Whether the toggle button is disabled. */
3171 get disabled() {
3172 if (this._disabled === undefined && this.datepicker) {
3173 return this.datepicker.disabled;
3174 }
3175 return !!this._disabled;
3176 }
3177 set disabled(value) {
3178 this._disabled = coerceBooleanProperty(value);
3179 }
3180 ngOnChanges(changes) {
3181 if (changes['datepicker']) {
3182 this._watchStateChanges();
3183 }
3184 }
3185 ngOnDestroy() {
3186 this._stateChanges.unsubscribe();
3187 }
3188 ngAfterContentInit() {
3189 this._watchStateChanges();
3190 }
3191 _open(event) {
3192 if (this.datepicker && !this.disabled) {
3193 this.datepicker.open();
3194 event.stopPropagation();
3195 }
3196 }
3197 _watchStateChanges() {
3198 const datepickerStateChanged = this.datepicker ? this.datepicker.stateChanges : of();
3199 const inputStateChanged = this.datepicker && this.datepicker.datepickerInput
3200 ? this.datepicker.datepickerInput.stateChanges
3201 : of();
3202 const datepickerToggled = this.datepicker
3203 ? merge(this.datepicker.openedStream, this.datepicker.closedStream)
3204 : of();
3205 this._stateChanges.unsubscribe();
3206 this._stateChanges = merge(this._intl.changes, datepickerStateChanged, inputStateChanged, datepickerToggled).subscribe(() => this._changeDetectorRef.markForCheck());
3207 }
3208}
3209MatDatepickerToggle.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerToggle, deps: [{ token: MatDatepickerIntl }, { token: i0.ChangeDetectorRef }, { token: 'tabindex', attribute: true }], target: i0.ɵɵFactoryTarget.Component });
3210MatDatepickerToggle.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: { datepicker: ["for", "datepicker"], tabIndex: "tabIndex", ariaLabel: ["aria-label", "ariaLabel"], disabled: "disabled", disableRipple: "disableRipple" }, host: { listeners: { "click": "_open($event)" }, properties: { "attr.tabindex": "null", "class.mat-datepicker-toggle-active": "datepicker && datepicker.opened", "class.mat-accent": "datepicker && datepicker.color === \"accent\"", "class.mat-warn": "datepicker && datepicker.color === \"warn\"", "attr.data-mat-calendar": "datepicker ? datepicker.id : null" }, classAttribute: "mat-datepicker-toggle" }, queries: [{ propertyName: "_customIcon", first: true, predicate: MatDatepickerToggleIcon, descendants: true }], viewQueries: [{ propertyName: "_button", first: true, predicate: ["button"], descendants: true }], exportAs: ["matDatepickerToggle"], usesOnChanges: true, ngImport: i0, template: "<button\n #button\n mat-icon-button\n type=\"button\"\n [attr.aria-haspopup]=\"datepicker ? 'dialog' : null\"\n [attr.aria-label]=\"ariaLabel || _intl.openCalendarLabel\"\n [attr.tabindex]=\"disabled ? -1 : tabIndex\"\n [disabled]=\"disabled\"\n [disableRipple]=\"disableRipple\">\n\n <svg\n *ngIf=\"!_customIcon\"\n class=\"mat-datepicker-toggle-default-icon\"\n viewBox=\"0 0 24 24\"\n width=\"24px\"\n height=\"24px\"\n fill=\"currentColor\"\n focusable=\"false\">\n <path d=\"M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z\"/>\n </svg>\n\n <ng-content select=\"[matDatepickerToggleIcon]\"></ng-content>\n</button>\n", styles: [".mat-form-field-appearance-legacy .mat-form-field-prefix .mat-datepicker-toggle-default-icon,.mat-form-field-appearance-legacy .mat-form-field-suffix .mat-datepicker-toggle-default-icon{width:1em}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-datepicker-toggle-default-icon,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-datepicker-toggle-default-icon{display:block;width:1.5em;height:1.5em}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon-button .mat-datepicker-toggle-default-icon,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon-button .mat-datepicker-toggle-default-icon{margin:auto}.cdk-high-contrast-active .mat-datepicker-toggle-default-icon{color:CanvasText}"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3211i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerToggle, decorators: [{
3212 type: Component,
3213 args: [{ selector: 'mat-datepicker-toggle', host: {
3214 'class': 'mat-datepicker-toggle',
3215 '[attr.tabindex]': 'null',
3216 '[class.mat-datepicker-toggle-active]': 'datepicker && datepicker.opened',
3217 '[class.mat-accent]': 'datepicker && datepicker.color === "accent"',
3218 '[class.mat-warn]': 'datepicker && datepicker.color === "warn"',
3219 // Used by the test harness to tie this toggle to its datepicker.
3220 '[attr.data-mat-calendar]': 'datepicker ? datepicker.id : null',
3221 // Bind the `click` on the host, rather than the inner `button`, so that we can call
3222 // `stopPropagation` on it without affecting the user's `click` handlers. We need to stop
3223 // it so that the input doesn't get focused automatically by the form field (See #21836).
3224 '(click)': '_open($event)',
3225 }, exportAs: 'matDatepickerToggle', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n #button\n mat-icon-button\n type=\"button\"\n [attr.aria-haspopup]=\"datepicker ? 'dialog' : null\"\n [attr.aria-label]=\"ariaLabel || _intl.openCalendarLabel\"\n [attr.tabindex]=\"disabled ? -1 : tabIndex\"\n [disabled]=\"disabled\"\n [disableRipple]=\"disableRipple\">\n\n <svg\n *ngIf=\"!_customIcon\"\n class=\"mat-datepicker-toggle-default-icon\"\n viewBox=\"0 0 24 24\"\n width=\"24px\"\n height=\"24px\"\n fill=\"currentColor\"\n focusable=\"false\">\n <path d=\"M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z\"/>\n </svg>\n\n <ng-content select=\"[matDatepickerToggleIcon]\"></ng-content>\n</button>\n", styles: [".mat-form-field-appearance-legacy .mat-form-field-prefix .mat-datepicker-toggle-default-icon,.mat-form-field-appearance-legacy .mat-form-field-suffix .mat-datepicker-toggle-default-icon{width:1em}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-datepicker-toggle-default-icon,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-datepicker-toggle-default-icon{display:block;width:1.5em;height:1.5em}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon-button .mat-datepicker-toggle-default-icon,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon-button .mat-datepicker-toggle-default-icon{margin:auto}.cdk-high-contrast-active .mat-datepicker-toggle-default-icon{color:CanvasText}"] }]
3226 }], ctorParameters: function () { return [{ type: MatDatepickerIntl }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
3227 type: Attribute,
3228 args: ['tabindex']
3229 }] }]; }, propDecorators: { datepicker: [{
3230 type: Input,
3231 args: ['for']
3232 }], tabIndex: [{
3233 type: Input
3234 }], ariaLabel: [{
3235 type: Input,
3236 args: ['aria-label']
3237 }], disabled: [{
3238 type: Input
3239 }], disableRipple: [{
3240 type: Input
3241 }], _customIcon: [{
3242 type: ContentChild,
3243 args: [MatDatepickerToggleIcon]
3244 }], _button: [{
3245 type: ViewChild,
3246 args: ['button']
3247 }] } });
3248
3249/**
3250 * @license
3251 * Copyright Google LLC All Rights Reserved.
3252 *
3253 * Use of this source code is governed by an MIT-style license that can be
3254 * found in the LICENSE file at https://angular.io/license
3255 */
3256/**
3257 * Used to provide the date range input wrapper component
3258 * to the parts without circular dependencies.
3259 */
3260const MAT_DATE_RANGE_INPUT_PARENT = new InjectionToken('MAT_DATE_RANGE_INPUT_PARENT');
3261/**
3262 * Base class for the individual inputs that can be projected inside a `mat-date-range-input`.
3263 */
3264class MatDateRangeInputPartBase extends MatDatepickerInputBase {
3265 constructor(_rangeInput, elementRef, _defaultErrorStateMatcher, _injector, _parentForm, _parentFormGroup, dateAdapter, dateFormats) {
3266 super(elementRef, dateAdapter, dateFormats);
3267 this._rangeInput = _rangeInput;
3268 this._defaultErrorStateMatcher = _defaultErrorStateMatcher;
3269 this._injector = _injector;
3270 this._parentForm = _parentForm;
3271 this._parentFormGroup = _parentFormGroup;
3272 }
3273 ngOnInit() {
3274 // We need the date input to provide itself as a `ControlValueAccessor` and a `Validator`, while
3275 // injecting its `NgControl` so that the error state is handled correctly. This introduces a
3276 // circular dependency, because both `ControlValueAccessor` and `Validator` depend on the input
3277 // itself. Usually we can work around it for the CVA, but there's no API to do it for the
3278 // validator. We work around it here by injecting the `NgControl` in `ngOnInit`, after
3279 // everything has been resolved.
3280 // tslint:disable-next-line:no-bitwise
3281 const ngControl = this._injector.get(NgControl, null, InjectFlags.Self | InjectFlags.Optional);
3282 if (ngControl) {
3283 this.ngControl = ngControl;
3284 }
3285 }
3286 ngDoCheck() {
3287 if (this.ngControl) {
3288 // We need to re-evaluate this on every change detection cycle, because there are some
3289 // error triggers that we can't subscribe to (e.g. parent form submissions). This means
3290 // that whatever logic is in here has to be super lean or we risk destroying the performance.
3291 this.updateErrorState();
3292 }
3293 }
3294 /** Gets whether the input is empty. */
3295 isEmpty() {
3296 return this._elementRef.nativeElement.value.length === 0;
3297 }
3298 /** Gets the placeholder of the input. */
3299 _getPlaceholder() {
3300 return this._elementRef.nativeElement.placeholder;
3301 }
3302 /** Focuses the input. */
3303 focus() {
3304 this._elementRef.nativeElement.focus();
3305 }
3306 /** Handles `input` events on the input element. */
3307 _onInput(value) {
3308 super._onInput(value);
3309 this._rangeInput._handleChildValueChange();
3310 }
3311 /** Opens the datepicker associated with the input. */
3312 _openPopup() {
3313 this._rangeInput._openDatepicker();
3314 }
3315 /** Gets the minimum date from the range input. */
3316 _getMinDate() {
3317 return this._rangeInput.min;
3318 }
3319 /** Gets the maximum date from the range input. */
3320 _getMaxDate() {
3321 return this._rangeInput.max;
3322 }
3323 /** Gets the date filter function from the range input. */
3324 _getDateFilter() {
3325 return this._rangeInput.dateFilter;
3326 }
3327 _parentDisabled() {
3328 return this._rangeInput._groupDisabled;
3329 }
3330 _shouldHandleChangeEvent({ source }) {
3331 return source !== this._rangeInput._startInput && source !== this._rangeInput._endInput;
3332 }
3333 _assignValueProgrammatically(value) {
3334 super._assignValueProgrammatically(value);
3335 const opposite = (this === this._rangeInput._startInput
3336 ? this._rangeInput._endInput
3337 : this._rangeInput._startInput);
3338 opposite?._validatorOnChange();
3339 }
3340}
3341MatDateRangeInputPartBase.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDateRangeInputPartBase, deps: [{ token: MAT_DATE_RANGE_INPUT_PARENT }, { token: i0.ElementRef }, { token: i1$1.ErrorStateMatcher }, { token: i0.Injector }, { token: i2$2.NgForm, optional: true }, { token: i2$2.FormGroupDirective, optional: true }, { token: i1$1.DateAdapter, optional: true }, { token: MAT_DATE_FORMATS, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3342MatDateRangeInputPartBase.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: MatDateRangeInputPartBase, usesInheritance: true, ngImport: i0 });
3343i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDateRangeInputPartBase, decorators: [{
3344 type: Directive
3345 }], ctorParameters: function () { return [{ type: undefined, decorators: [{
3346 type: Inject,
3347 args: [MAT_DATE_RANGE_INPUT_PARENT]
3348 }] }, { type: i0.ElementRef }, { type: i1$1.ErrorStateMatcher }, { type: i0.Injector }, { type: i2$2.NgForm, decorators: [{
3349 type: Optional
3350 }] }, { type: i2$2.FormGroupDirective, decorators: [{
3351 type: Optional
3352 }] }, { type: i1$1.DateAdapter, decorators: [{
3353 type: Optional
3354 }] }, { type: undefined, decorators: [{
3355 type: Optional
3356 }, {
3357 type: Inject,
3358 args: [MAT_DATE_FORMATS]
3359 }] }]; } });
3360const _MatDateRangeInputBase = mixinErrorState(MatDateRangeInputPartBase);
3361/** Input for entering the start date in a `mat-date-range-input`. */
3362class MatStartDate extends _MatDateRangeInputBase {
3363 constructor(rangeInput, elementRef, defaultErrorStateMatcher, injector, parentForm, parentFormGroup, dateAdapter, dateFormats) {
3364 super(rangeInput, elementRef, defaultErrorStateMatcher, injector, parentForm, parentFormGroup, dateAdapter, dateFormats);
3365 /** Validator that checks that the start date isn't after the end date. */
3366 this._startValidator = (control) => {
3367 const start = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(control.value));
3368 const end = this._model ? this._model.selection.end : null;
3369 return !start || !end || this._dateAdapter.compareDate(start, end) <= 0
3370 ? null
3371 : { 'matStartDateInvalid': { 'end': end, 'actual': start } };
3372 };
3373 this._validator = Validators.compose([...super._getValidators(), this._startValidator]);
3374 }
3375 _getValueFromModel(modelValue) {
3376 return modelValue.start;
3377 }
3378 _shouldHandleChangeEvent(change) {
3379 if (!super._shouldHandleChangeEvent(change)) {
3380 return false;
3381 }
3382 else {
3383 return !change.oldValue?.start
3384 ? !!change.selection.start
3385 : !change.selection.start ||
3386 !!this._dateAdapter.compareDate(change.oldValue.start, change.selection.start);
3387 }
3388 }
3389 _assignValueToModel(value) {
3390 if (this._model) {
3391 const range = new DateRange(value, this._model.selection.end);
3392 this._model.updateSelection(range, this);
3393 }
3394 }
3395 _formatValue(value) {
3396 super._formatValue(value);
3397 // Any time the input value is reformatted we need to tell the parent.
3398 this._rangeInput._handleChildValueChange();
3399 }
3400 /** Gets the value that should be used when mirroring the input's size. */
3401 getMirrorValue() {
3402 const element = this._elementRef.nativeElement;
3403 const value = element.value;
3404 return value.length > 0 ? value : element.placeholder;
3405 }
3406}
3407MatStartDate.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatStartDate, deps: [{ token: MAT_DATE_RANGE_INPUT_PARENT }, { token: i0.ElementRef }, { token: i1$1.ErrorStateMatcher }, { token: i0.Injector }, { token: i2$2.NgForm, optional: true }, { token: i2$2.FormGroupDirective, optional: true }, { token: i1$1.DateAdapter, optional: true }, { token: MAT_DATE_FORMATS, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3408MatStartDate.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: MatStartDate, selector: "input[matStartDate]", inputs: { errorStateMatcher: "errorStateMatcher" }, outputs: { dateChange: "dateChange", dateInput: "dateInput" }, host: { attributes: { "type": "text" }, listeners: { "input": "_onInput($event.target.value)", "change": "_onChange()", "keydown": "_onKeydown($event)", "blur": "_onBlur()" }, properties: { "disabled": "disabled", "attr.id": "_rangeInput.id", "attr.aria-haspopup": "_rangeInput.rangePicker ? \"dialog\" : null", "attr.aria-owns": "(_rangeInput.rangePicker?.opened && _rangeInput.rangePicker.id) || null", "attr.min": "_getMinDate() ? _dateAdapter.toIso8601(_getMinDate()) : null", "attr.max": "_getMaxDate() ? _dateAdapter.toIso8601(_getMaxDate()) : null" }, classAttribute: "mat-start-date mat-date-range-input-inner" }, providers: [
3409 { provide: NG_VALUE_ACCESSOR, useExisting: MatStartDate, multi: true },
3410 { provide: NG_VALIDATORS, useExisting: MatStartDate, multi: true },
3411 ], usesInheritance: true, ngImport: i0 });
3412i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatStartDate, decorators: [{
3413 type: Directive,
3414 args: [{
3415 selector: 'input[matStartDate]',
3416 host: {
3417 'class': 'mat-start-date mat-date-range-input-inner',
3418 '[disabled]': 'disabled',
3419 '(input)': '_onInput($event.target.value)',
3420 '(change)': '_onChange()',
3421 '(keydown)': '_onKeydown($event)',
3422 '[attr.id]': '_rangeInput.id',
3423 '[attr.aria-haspopup]': '_rangeInput.rangePicker ? "dialog" : null',
3424 '[attr.aria-owns]': '(_rangeInput.rangePicker?.opened && _rangeInput.rangePicker.id) || null',
3425 '[attr.min]': '_getMinDate() ? _dateAdapter.toIso8601(_getMinDate()) : null',
3426 '[attr.max]': '_getMaxDate() ? _dateAdapter.toIso8601(_getMaxDate()) : null',
3427 '(blur)': '_onBlur()',
3428 'type': 'text',
3429 },
3430 providers: [
3431 { provide: NG_VALUE_ACCESSOR, useExisting: MatStartDate, multi: true },
3432 { provide: NG_VALIDATORS, useExisting: MatStartDate, multi: true },
3433 ],
3434 // These need to be specified explicitly, because some tooling doesn't
3435 // seem to pick them up from the base class. See #20932.
3436 outputs: ['dateChange', 'dateInput'],
3437 inputs: ['errorStateMatcher'],
3438 }]
3439 }], ctorParameters: function () { return [{ type: undefined, decorators: [{
3440 type: Inject,
3441 args: [MAT_DATE_RANGE_INPUT_PARENT]
3442 }] }, { type: i0.ElementRef }, { type: i1$1.ErrorStateMatcher }, { type: i0.Injector }, { type: i2$2.NgForm, decorators: [{
3443 type: Optional
3444 }] }, { type: i2$2.FormGroupDirective, decorators: [{
3445 type: Optional
3446 }] }, { type: i1$1.DateAdapter, decorators: [{
3447 type: Optional
3448 }] }, { type: undefined, decorators: [{
3449 type: Optional
3450 }, {
3451 type: Inject,
3452 args: [MAT_DATE_FORMATS]
3453 }] }]; } });
3454/** Input for entering the end date in a `mat-date-range-input`. */
3455class MatEndDate extends _MatDateRangeInputBase {
3456 constructor(rangeInput, elementRef, defaultErrorStateMatcher, injector, parentForm, parentFormGroup, dateAdapter, dateFormats) {
3457 super(rangeInput, elementRef, defaultErrorStateMatcher, injector, parentForm, parentFormGroup, dateAdapter, dateFormats);
3458 /** Validator that checks that the end date isn't before the start date. */
3459 this._endValidator = (control) => {
3460 const end = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(control.value));
3461 const start = this._model ? this._model.selection.start : null;
3462 return !end || !start || this._dateAdapter.compareDate(end, start) >= 0
3463 ? null
3464 : { 'matEndDateInvalid': { 'start': start, 'actual': end } };
3465 };
3466 this._validator = Validators.compose([...super._getValidators(), this._endValidator]);
3467 }
3468 _getValueFromModel(modelValue) {
3469 return modelValue.end;
3470 }
3471 _shouldHandleChangeEvent(change) {
3472 if (!super._shouldHandleChangeEvent(change)) {
3473 return false;
3474 }
3475 else {
3476 return !change.oldValue?.end
3477 ? !!change.selection.end
3478 : !change.selection.end ||
3479 !!this._dateAdapter.compareDate(change.oldValue.end, change.selection.end);
3480 }
3481 }
3482 _assignValueToModel(value) {
3483 if (this._model) {
3484 const range = new DateRange(this._model.selection.start, value);
3485 this._model.updateSelection(range, this);
3486 }
3487 }
3488 _onKeydown(event) {
3489 // If the user is pressing backspace on an empty end input, move focus back to the start.
3490 if (event.keyCode === BACKSPACE && !this._elementRef.nativeElement.value) {
3491 this._rangeInput._startInput.focus();
3492 }
3493 super._onKeydown(event);
3494 }
3495}
3496MatEndDate.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatEndDate, deps: [{ token: MAT_DATE_RANGE_INPUT_PARENT }, { token: i0.ElementRef }, { token: i1$1.ErrorStateMatcher }, { token: i0.Injector }, { token: i2$2.NgForm, optional: true }, { token: i2$2.FormGroupDirective, optional: true }, { token: i1$1.DateAdapter, optional: true }, { token: MAT_DATE_FORMATS, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3497MatEndDate.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: MatEndDate, selector: "input[matEndDate]", inputs: { errorStateMatcher: "errorStateMatcher" }, outputs: { dateChange: "dateChange", dateInput: "dateInput" }, host: { attributes: { "type": "text" }, listeners: { "input": "_onInput($event.target.value)", "change": "_onChange()", "keydown": "_onKeydown($event)", "blur": "_onBlur()" }, properties: { "disabled": "disabled", "attr.aria-haspopup": "_rangeInput.rangePicker ? \"dialog\" : null", "attr.aria-owns": "(_rangeInput.rangePicker?.opened && _rangeInput.rangePicker.id) || null", "attr.min": "_getMinDate() ? _dateAdapter.toIso8601(_getMinDate()) : null", "attr.max": "_getMaxDate() ? _dateAdapter.toIso8601(_getMaxDate()) : null" }, classAttribute: "mat-end-date mat-date-range-input-inner" }, providers: [
3498 { provide: NG_VALUE_ACCESSOR, useExisting: MatEndDate, multi: true },
3499 { provide: NG_VALIDATORS, useExisting: MatEndDate, multi: true },
3500 ], usesInheritance: true, ngImport: i0 });
3501i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatEndDate, decorators: [{
3502 type: Directive,
3503 args: [{
3504 selector: 'input[matEndDate]',
3505 host: {
3506 'class': 'mat-end-date mat-date-range-input-inner',
3507 '[disabled]': 'disabled',
3508 '(input)': '_onInput($event.target.value)',
3509 '(change)': '_onChange()',
3510 '(keydown)': '_onKeydown($event)',
3511 '[attr.aria-haspopup]': '_rangeInput.rangePicker ? "dialog" : null',
3512 '[attr.aria-owns]': '(_rangeInput.rangePicker?.opened && _rangeInput.rangePicker.id) || null',
3513 '[attr.min]': '_getMinDate() ? _dateAdapter.toIso8601(_getMinDate()) : null',
3514 '[attr.max]': '_getMaxDate() ? _dateAdapter.toIso8601(_getMaxDate()) : null',
3515 '(blur)': '_onBlur()',
3516 'type': 'text',
3517 },
3518 providers: [
3519 { provide: NG_VALUE_ACCESSOR, useExisting: MatEndDate, multi: true },
3520 { provide: NG_VALIDATORS, useExisting: MatEndDate, multi: true },
3521 ],
3522 // These need to be specified explicitly, because some tooling doesn't
3523 // seem to pick them up from the base class. See #20932.
3524 outputs: ['dateChange', 'dateInput'],
3525 inputs: ['errorStateMatcher'],
3526 }]
3527 }], ctorParameters: function () { return [{ type: undefined, decorators: [{
3528 type: Inject,
3529 args: [MAT_DATE_RANGE_INPUT_PARENT]
3530 }] }, { type: i0.ElementRef }, { type: i1$1.ErrorStateMatcher }, { type: i0.Injector }, { type: i2$2.NgForm, decorators: [{
3531 type: Optional
3532 }] }, { type: i2$2.FormGroupDirective, decorators: [{
3533 type: Optional
3534 }] }, { type: i1$1.DateAdapter, decorators: [{
3535 type: Optional
3536 }] }, { type: undefined, decorators: [{
3537 type: Optional
3538 }, {
3539 type: Inject,
3540 args: [MAT_DATE_FORMATS]
3541 }] }]; } });
3542
3543/**
3544 * @license
3545 * Copyright Google LLC All Rights Reserved.
3546 *
3547 * Use of this source code is governed by an MIT-style license that can be
3548 * found in the LICENSE file at https://angular.io/license
3549 */
3550let nextUniqueId = 0;
3551class MatDateRangeInput {
3552 constructor(_changeDetectorRef, _elementRef, control, _dateAdapter, _formField) {
3553 this._changeDetectorRef = _changeDetectorRef;
3554 this._elementRef = _elementRef;
3555 this._dateAdapter = _dateAdapter;
3556 this._formField = _formField;
3557 this._closedSubscription = Subscription.EMPTY;
3558 /** Unique ID for the input. */
3559 this.id = `mat-date-range-input-${nextUniqueId++}`;
3560 /** Whether the control is focused. */
3561 this.focused = false;
3562 /** Name of the form control. */
3563 this.controlType = 'mat-date-range-input';
3564 this._groupDisabled = false;
3565 /** Value for the `aria-describedby` attribute of the inputs. */
3566 this._ariaDescribedBy = null;
3567 /** Separator text to be shown between the inputs. */
3568 this.separator = '–';
3569 /** Start of the comparison range that should be shown in the calendar. */
3570 this.comparisonStart = null;
3571 /** End of the comparison range that should be shown in the calendar. */
3572 this.comparisonEnd = null;
3573 /** Emits when the input's state has changed. */
3574 this.stateChanges = new Subject();
3575 if (!_dateAdapter && (typeof ngDevMode === 'undefined' || ngDevMode)) {
3576 throw createMissingDateImplError('DateAdapter');
3577 }
3578 // The datepicker module can be used both with MDC and non-MDC form fields. We have
3579 // to conditionally add the MDC input class so that the range picker looks correctly.
3580 if (_formField?._elementRef.nativeElement.classList.contains('mat-mdc-form-field')) {
3581 const classList = _elementRef.nativeElement.classList;
3582 classList.add('mat-mdc-input-element');
3583 classList.add('mat-mdc-form-field-input-control');
3584 }
3585 // TODO(crisbeto): remove `as any` after #18206 lands.
3586 this.ngControl = control;
3587 }
3588 /** Current value of the range input. */
3589 get value() {
3590 return this._model ? this._model.selection : null;
3591 }
3592 /** Whether the control's label should float. */
3593 get shouldLabelFloat() {
3594 return this.focused || !this.empty;
3595 }
3596 /**
3597 * Implemented as a part of `MatFormFieldControl`.
3598 * Set the placeholder attribute on `matStartDate` and `matEndDate`.
3599 * @docs-private
3600 */
3601 get placeholder() {
3602 const start = this._startInput?._getPlaceholder() || '';
3603 const end = this._endInput?._getPlaceholder() || '';
3604 return start || end ? `${start} ${this.separator} ${end}` : '';
3605 }
3606 /** The range picker that this input is associated with. */
3607 get rangePicker() {
3608 return this._rangePicker;
3609 }
3610 set rangePicker(rangePicker) {
3611 if (rangePicker) {
3612 this._model = rangePicker.registerInput(this);
3613 this._rangePicker = rangePicker;
3614 this._closedSubscription.unsubscribe();
3615 this._closedSubscription = rangePicker.closedStream.subscribe(() => {
3616 this._startInput?._onTouched();
3617 this._endInput?._onTouched();
3618 });
3619 this._registerModel(this._model);
3620 }
3621 }
3622 /** Whether the input is required. */
3623 get required() {
3624 return !!this._required;
3625 }
3626 set required(value) {
3627 this._required = coerceBooleanProperty(value);
3628 }
3629 /** Function that can be used to filter out dates within the date range picker. */
3630 get dateFilter() {
3631 return this._dateFilter;
3632 }
3633 set dateFilter(value) {
3634 const start = this._startInput;
3635 const end = this._endInput;
3636 const wasMatchingStart = start && start._matchesFilter(start.value);
3637 const wasMatchingEnd = end && end._matchesFilter(start.value);
3638 this._dateFilter = value;
3639 if (start && start._matchesFilter(start.value) !== wasMatchingStart) {
3640 start._validatorOnChange();
3641 }
3642 if (end && end._matchesFilter(end.value) !== wasMatchingEnd) {
3643 end._validatorOnChange();
3644 }
3645 }
3646 /** The minimum valid date. */
3647 get min() {
3648 return this._min;
3649 }
3650 set min(value) {
3651 const validValue = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
3652 if (!this._dateAdapter.sameDate(validValue, this._min)) {
3653 this._min = validValue;
3654 this._revalidate();
3655 }
3656 }
3657 /** The maximum valid date. */
3658 get max() {
3659 return this._max;
3660 }
3661 set max(value) {
3662 const validValue = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value));
3663 if (!this._dateAdapter.sameDate(validValue, this._max)) {
3664 this._max = validValue;
3665 this._revalidate();
3666 }
3667 }
3668 /** Whether the input is disabled. */
3669 get disabled() {
3670 return this._startInput && this._endInput
3671 ? this._startInput.disabled && this._endInput.disabled
3672 : this._groupDisabled;
3673 }
3674 set disabled(value) {
3675 const newValue = coerceBooleanProperty(value);
3676 if (newValue !== this._groupDisabled) {
3677 this._groupDisabled = newValue;
3678 this.stateChanges.next(undefined);
3679 }
3680 }
3681 /** Whether the input is in an error state. */
3682 get errorState() {
3683 if (this._startInput && this._endInput) {
3684 return this._startInput.errorState || this._endInput.errorState;
3685 }
3686 return false;
3687 }
3688 /** Whether the datepicker input is empty. */
3689 get empty() {
3690 const startEmpty = this._startInput ? this._startInput.isEmpty() : false;
3691 const endEmpty = this._endInput ? this._endInput.isEmpty() : false;
3692 return startEmpty && endEmpty;
3693 }
3694 /**
3695 * Implemented as a part of `MatFormFieldControl`.
3696 * @docs-private
3697 */
3698 setDescribedByIds(ids) {
3699 this._ariaDescribedBy = ids.length ? ids.join(' ') : null;
3700 }
3701 /**
3702 * Implemented as a part of `MatFormFieldControl`.
3703 * @docs-private
3704 */
3705 onContainerClick() {
3706 if (!this.focused && !this.disabled) {
3707 if (!this._model || !this._model.selection.start) {
3708 this._startInput.focus();
3709 }
3710 else {
3711 this._endInput.focus();
3712 }
3713 }
3714 }
3715 ngAfterContentInit() {
3716 if (typeof ngDevMode === 'undefined' || ngDevMode) {
3717 if (!this._startInput) {
3718 throw Error('mat-date-range-input must contain a matStartDate input');
3719 }
3720 if (!this._endInput) {
3721 throw Error('mat-date-range-input must contain a matEndDate input');
3722 }
3723 }
3724 if (this._model) {
3725 this._registerModel(this._model);
3726 }
3727 // We don't need to unsubscribe from this, because we
3728 // know that the input streams will be completed on destroy.
3729 merge(this._startInput.stateChanges, this._endInput.stateChanges).subscribe(() => {
3730 this.stateChanges.next(undefined);
3731 });
3732 }
3733 ngOnChanges(changes) {
3734 if (dateInputsHaveChanged(changes, this._dateAdapter)) {
3735 this.stateChanges.next(undefined);
3736 }
3737 }
3738 ngOnDestroy() {
3739 this._closedSubscription.unsubscribe();
3740 this.stateChanges.complete();
3741 }
3742 /** Gets the date at which the calendar should start. */
3743 getStartValue() {
3744 return this.value ? this.value.start : null;
3745 }
3746 /** Gets the input's theme palette. */
3747 getThemePalette() {
3748 return this._formField ? this._formField.color : undefined;
3749 }
3750 /** Gets the element to which the calendar overlay should be attached. */
3751 getConnectedOverlayOrigin() {
3752 return this._formField ? this._formField.getConnectedOverlayOrigin() : this._elementRef;
3753 }
3754 /** Gets the ID of an element that should be used a description for the calendar overlay. */
3755 getOverlayLabelId() {
3756 return this._formField ? this._formField.getLabelId() : null;
3757 }
3758 /** Gets the value that is used to mirror the state input. */
3759 _getInputMirrorValue() {
3760 return this._startInput ? this._startInput.getMirrorValue() : '';
3761 }
3762 /** Whether the input placeholders should be hidden. */
3763 _shouldHidePlaceholders() {
3764 return this._startInput ? !this._startInput.isEmpty() : false;
3765 }
3766 /** Handles the value in one of the child inputs changing. */
3767 _handleChildValueChange() {
3768 this.stateChanges.next(undefined);
3769 this._changeDetectorRef.markForCheck();
3770 }
3771 /** Opens the date range picker associated with the input. */
3772 _openDatepicker() {
3773 if (this._rangePicker) {
3774 this._rangePicker.open();
3775 }
3776 }
3777 /** Whether the separate text should be hidden. */
3778 _shouldHideSeparator() {
3779 return ((!this._formField ||
3780 (this._formField.getLabelId() && !this._formField._shouldLabelFloat())) &&
3781 this.empty);
3782 }
3783 /** Gets the value for the `aria-labelledby` attribute of the inputs. */
3784 _getAriaLabelledby() {
3785 const formField = this._formField;
3786 return formField && formField._hasFloatingLabel() ? formField._labelId : null;
3787 }
3788 /** Updates the focused state of the range input. */
3789 _updateFocus(origin) {
3790 this.focused = origin !== null;
3791 this.stateChanges.next();
3792 }
3793 /** Re-runs the validators on the start/end inputs. */
3794 _revalidate() {
3795 if (this._startInput) {
3796 this._startInput._validatorOnChange();
3797 }
3798 if (this._endInput) {
3799 this._endInput._validatorOnChange();
3800 }
3801 }
3802 /** Registers the current date selection model with the start/end inputs. */
3803 _registerModel(model) {
3804 if (this._startInput) {
3805 this._startInput._registerModel(model);
3806 }
3807 if (this._endInput) {
3808 this._endInput._registerModel(model);
3809 }
3810 }
3811}
3812MatDateRangeInput.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDateRangeInput, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i2$2.ControlContainer, optional: true, self: true }, { token: i1$1.DateAdapter, optional: true }, { token: MAT_FORM_FIELD, optional: true }], target: i0.ɵɵFactoryTarget.Component });
3813MatDateRangeInput.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatDateRangeInput, selector: "mat-date-range-input", inputs: { rangePicker: "rangePicker", required: "required", dateFilter: "dateFilter", min: "min", max: "max", disabled: "disabled", separator: "separator", comparisonStart: "comparisonStart", comparisonEnd: "comparisonEnd" }, host: { attributes: { "role": "group" }, properties: { "class.mat-date-range-input-hide-placeholders": "_shouldHidePlaceholders()", "class.mat-date-range-input-required": "required", "attr.id": "null", "attr.aria-labelledby": "_getAriaLabelledby()", "attr.aria-describedby": "_ariaDescribedBy", "attr.data-mat-calendar": "rangePicker ? rangePicker.id : null" }, classAttribute: "mat-date-range-input" }, providers: [
3814 { provide: MatFormFieldControl, useExisting: MatDateRangeInput },
3815 { provide: MAT_DATE_RANGE_INPUT_PARENT, useExisting: MatDateRangeInput },
3816 ], queries: [{ propertyName: "_startInput", first: true, predicate: MatStartDate, descendants: true }, { propertyName: "_endInput", first: true, predicate: MatEndDate, descendants: true }], exportAs: ["matDateRangeInput"], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"mat-date-range-input-container\"\n cdkMonitorSubtreeFocus\n (cdkFocusChange)=\"_updateFocus($event)\">\n <div class=\"mat-date-range-input-start-wrapper\">\n <ng-content select=\"input[matStartDate]\"></ng-content>\n <span\n class=\"mat-date-range-input-mirror\"\n aria-hidden=\"true\">{{_getInputMirrorValue()}}</span>\n </div>\n\n <span\n class=\"mat-date-range-input-separator\"\n [class.mat-date-range-input-separator-hidden]=\"_shouldHideSeparator()\">{{separator}}</span>\n\n <div class=\"mat-date-range-input-end-wrapper\">\n <ng-content select=\"input[matEndDate]\"></ng-content>\n </div>\n</div>\n\n", styles: [".mat-date-range-input{display:block;width:100%}.mat-date-range-input-container{display:flex;align-items:center}.mat-date-range-input-separator{transition:opacity 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1);margin:0 4px}._mat-animation-noopable .mat-date-range-input-separator{transition:none}.mat-date-range-input-separator-hidden{-webkit-user-select:none;user-select:none;opacity:0;transition:none}.mat-date-range-input-inner{font:inherit;background:rgba(0,0,0,0);color:currentColor;border:none;outline:none;padding:0;margin:0;vertical-align:bottom;text-align:inherit;-webkit-appearance:none;width:100%}.mat-date-range-input-inner:-moz-ui-invalid{box-shadow:none}.mat-date-range-input-inner::placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1)}.mat-date-range-input-inner::-moz-placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1)}.mat-date-range-input-inner::-webkit-input-placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1)}.mat-date-range-input-inner:-ms-input-placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1)}.mat-form-field-hide-placeholder .mat-date-range-input-inner::placeholder,.mat-date-range-input-hide-placeholders .mat-date-range-input-inner::placeholder{-webkit-user-select:none;user-select:none;color:rgba(0,0,0,0) !important;-webkit-text-fill-color:rgba(0,0,0,0);transition:none}.cdk-high-contrast-active .mat-form-field-hide-placeholder .mat-date-range-input-inner::placeholder,.cdk-high-contrast-active .mat-date-range-input-hide-placeholders .mat-date-range-input-inner::placeholder{opacity:0}.mat-form-field-hide-placeholder .mat-date-range-input-inner::-moz-placeholder,.mat-date-range-input-hide-placeholders .mat-date-range-input-inner::-moz-placeholder{-webkit-user-select:none;user-select:none;color:rgba(0,0,0,0) !important;-webkit-text-fill-color:rgba(0,0,0,0);transition:none}.cdk-high-contrast-active .mat-form-field-hide-placeholder .mat-date-range-input-inner::-moz-placeholder,.cdk-high-contrast-active .mat-date-range-input-hide-placeholders .mat-date-range-input-inner::-moz-placeholder{opacity:0}.mat-form-field-hide-placeholder .mat-date-range-input-inner::-webkit-input-placeholder,.mat-date-range-input-hide-placeholders .mat-date-range-input-inner::-webkit-input-placeholder{-webkit-user-select:none;user-select:none;color:rgba(0,0,0,0) !important;-webkit-text-fill-color:rgba(0,0,0,0);transition:none}.cdk-high-contrast-active .mat-form-field-hide-placeholder .mat-date-range-input-inner::-webkit-input-placeholder,.cdk-high-contrast-active .mat-date-range-input-hide-placeholders .mat-date-range-input-inner::-webkit-input-placeholder{opacity:0}.mat-form-field-hide-placeholder .mat-date-range-input-inner:-ms-input-placeholder,.mat-date-range-input-hide-placeholders .mat-date-range-input-inner:-ms-input-placeholder{-webkit-user-select:none;user-select:none;color:rgba(0,0,0,0) !important;-webkit-text-fill-color:rgba(0,0,0,0);transition:none}.cdk-high-contrast-active .mat-form-field-hide-placeholder .mat-date-range-input-inner:-ms-input-placeholder,.cdk-high-contrast-active .mat-date-range-input-hide-placeholders .mat-date-range-input-inner:-ms-input-placeholder{opacity:0}._mat-animation-noopable .mat-date-range-input-inner::placeholder{transition:none}._mat-animation-noopable .mat-date-range-input-inner::-moz-placeholder{transition:none}._mat-animation-noopable .mat-date-range-input-inner::-webkit-input-placeholder{transition:none}._mat-animation-noopable .mat-date-range-input-inner:-ms-input-placeholder{transition:none}.mat-date-range-input-mirror{-webkit-user-select:none;user-select:none;visibility:hidden;white-space:nowrap;display:inline-block;min-width:2px}.mat-date-range-input-start-wrapper{position:relative;overflow:hidden;max-width:calc(50% - 4px)}.mat-date-range-input-start-wrapper .mat-date-range-input-inner{position:absolute;top:0;left:0}.mat-date-range-input-end-wrapper{flex-grow:1;max-width:calc(50% - 4px)}.mat-form-field-type-mat-date-range-input .mat-form-field-infix{width:200px}"], dependencies: [{ kind: "directive", type: i5.CdkMonitorFocus, selector: "[cdkMonitorElementFocus], [cdkMonitorSubtreeFocus]", outputs: ["cdkFocusChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3817i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDateRangeInput, decorators: [{
3818 type: Component,
3819 args: [{ selector: 'mat-date-range-input', exportAs: 'matDateRangeInput', host: {
3820 'class': 'mat-date-range-input',
3821 '[class.mat-date-range-input-hide-placeholders]': '_shouldHidePlaceholders()',
3822 '[class.mat-date-range-input-required]': 'required',
3823 '[attr.id]': 'null',
3824 'role': 'group',
3825 '[attr.aria-labelledby]': '_getAriaLabelledby()',
3826 '[attr.aria-describedby]': '_ariaDescribedBy',
3827 // Used by the test harness to tie this input to its calendar. We can't depend on
3828 // `aria-owns` for this, because it's only defined while the calendar is open.
3829 '[attr.data-mat-calendar]': 'rangePicker ? rangePicker.id : null',
3830 }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, providers: [
3831 { provide: MatFormFieldControl, useExisting: MatDateRangeInput },
3832 { provide: MAT_DATE_RANGE_INPUT_PARENT, useExisting: MatDateRangeInput },
3833 ], template: "<div\n class=\"mat-date-range-input-container\"\n cdkMonitorSubtreeFocus\n (cdkFocusChange)=\"_updateFocus($event)\">\n <div class=\"mat-date-range-input-start-wrapper\">\n <ng-content select=\"input[matStartDate]\"></ng-content>\n <span\n class=\"mat-date-range-input-mirror\"\n aria-hidden=\"true\">{{_getInputMirrorValue()}}</span>\n </div>\n\n <span\n class=\"mat-date-range-input-separator\"\n [class.mat-date-range-input-separator-hidden]=\"_shouldHideSeparator()\">{{separator}}</span>\n\n <div class=\"mat-date-range-input-end-wrapper\">\n <ng-content select=\"input[matEndDate]\"></ng-content>\n </div>\n</div>\n\n", styles: [".mat-date-range-input{display:block;width:100%}.mat-date-range-input-container{display:flex;align-items:center}.mat-date-range-input-separator{transition:opacity 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1);margin:0 4px}._mat-animation-noopable .mat-date-range-input-separator{transition:none}.mat-date-range-input-separator-hidden{-webkit-user-select:none;user-select:none;opacity:0;transition:none}.mat-date-range-input-inner{font:inherit;background:rgba(0,0,0,0);color:currentColor;border:none;outline:none;padding:0;margin:0;vertical-align:bottom;text-align:inherit;-webkit-appearance:none;width:100%}.mat-date-range-input-inner:-moz-ui-invalid{box-shadow:none}.mat-date-range-input-inner::placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1)}.mat-date-range-input-inner::-moz-placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1)}.mat-date-range-input-inner::-webkit-input-placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1)}.mat-date-range-input-inner:-ms-input-placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1)}.mat-form-field-hide-placeholder .mat-date-range-input-inner::placeholder,.mat-date-range-input-hide-placeholders .mat-date-range-input-inner::placeholder{-webkit-user-select:none;user-select:none;color:rgba(0,0,0,0) !important;-webkit-text-fill-color:rgba(0,0,0,0);transition:none}.cdk-high-contrast-active .mat-form-field-hide-placeholder .mat-date-range-input-inner::placeholder,.cdk-high-contrast-active .mat-date-range-input-hide-placeholders .mat-date-range-input-inner::placeholder{opacity:0}.mat-form-field-hide-placeholder .mat-date-range-input-inner::-moz-placeholder,.mat-date-range-input-hide-placeholders .mat-date-range-input-inner::-moz-placeholder{-webkit-user-select:none;user-select:none;color:rgba(0,0,0,0) !important;-webkit-text-fill-color:rgba(0,0,0,0);transition:none}.cdk-high-contrast-active .mat-form-field-hide-placeholder .mat-date-range-input-inner::-moz-placeholder,.cdk-high-contrast-active .mat-date-range-input-hide-placeholders .mat-date-range-input-inner::-moz-placeholder{opacity:0}.mat-form-field-hide-placeholder .mat-date-range-input-inner::-webkit-input-placeholder,.mat-date-range-input-hide-placeholders .mat-date-range-input-inner::-webkit-input-placeholder{-webkit-user-select:none;user-select:none;color:rgba(0,0,0,0) !important;-webkit-text-fill-color:rgba(0,0,0,0);transition:none}.cdk-high-contrast-active .mat-form-field-hide-placeholder .mat-date-range-input-inner::-webkit-input-placeholder,.cdk-high-contrast-active .mat-date-range-input-hide-placeholders .mat-date-range-input-inner::-webkit-input-placeholder{opacity:0}.mat-form-field-hide-placeholder .mat-date-range-input-inner:-ms-input-placeholder,.mat-date-range-input-hide-placeholders .mat-date-range-input-inner:-ms-input-placeholder{-webkit-user-select:none;user-select:none;color:rgba(0,0,0,0) !important;-webkit-text-fill-color:rgba(0,0,0,0);transition:none}.cdk-high-contrast-active .mat-form-field-hide-placeholder .mat-date-range-input-inner:-ms-input-placeholder,.cdk-high-contrast-active .mat-date-range-input-hide-placeholders .mat-date-range-input-inner:-ms-input-placeholder{opacity:0}._mat-animation-noopable .mat-date-range-input-inner::placeholder{transition:none}._mat-animation-noopable .mat-date-range-input-inner::-moz-placeholder{transition:none}._mat-animation-noopable .mat-date-range-input-inner::-webkit-input-placeholder{transition:none}._mat-animation-noopable .mat-date-range-input-inner:-ms-input-placeholder{transition:none}.mat-date-range-input-mirror{-webkit-user-select:none;user-select:none;visibility:hidden;white-space:nowrap;display:inline-block;min-width:2px}.mat-date-range-input-start-wrapper{position:relative;overflow:hidden;max-width:calc(50% - 4px)}.mat-date-range-input-start-wrapper .mat-date-range-input-inner{position:absolute;top:0;left:0}.mat-date-range-input-end-wrapper{flex-grow:1;max-width:calc(50% - 4px)}.mat-form-field-type-mat-date-range-input .mat-form-field-infix{width:200px}"] }]
3834 }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i2$2.ControlContainer, decorators: [{
3835 type: Optional
3836 }, {
3837 type: Self
3838 }] }, { type: i1$1.DateAdapter, decorators: [{
3839 type: Optional
3840 }] }, { type: i2$1.MatFormField, decorators: [{
3841 type: Optional
3842 }, {
3843 type: Inject,
3844 args: [MAT_FORM_FIELD]
3845 }] }]; }, propDecorators: { rangePicker: [{
3846 type: Input
3847 }], required: [{
3848 type: Input
3849 }], dateFilter: [{
3850 type: Input
3851 }], min: [{
3852 type: Input
3853 }], max: [{
3854 type: Input
3855 }], disabled: [{
3856 type: Input
3857 }], separator: [{
3858 type: Input
3859 }], comparisonStart: [{
3860 type: Input
3861 }], comparisonEnd: [{
3862 type: Input
3863 }], _startInput: [{
3864 type: ContentChild,
3865 args: [MatStartDate]
3866 }], _endInput: [{
3867 type: ContentChild,
3868 args: [MatEndDate]
3869 }] } });
3870
3871/**
3872 * @license
3873 * Copyright Google LLC All Rights Reserved.
3874 *
3875 * Use of this source code is governed by an MIT-style license that can be
3876 * found in the LICENSE file at https://angular.io/license
3877 */
3878// TODO(mmalerba): We use a component instead of a directive here so the user can use implicit
3879// template reference variables (e.g. #d vs #d="matDateRangePicker"). We can change this to a
3880// directive if angular adds support for `exportAs: '$implicit'` on directives.
3881/** Component responsible for managing the date range picker popup/dialog. */
3882class MatDateRangePicker extends MatDatepickerBase {
3883 _forwardContentValues(instance) {
3884 super._forwardContentValues(instance);
3885 const input = this.datepickerInput;
3886 if (input) {
3887 instance.comparisonStart = input.comparisonStart;
3888 instance.comparisonEnd = input.comparisonEnd;
3889 }
3890 }
3891}
3892MatDateRangePicker.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDateRangePicker, deps: null, target: i0.ɵɵFactoryTarget.Component });
3893MatDateRangePicker.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatDateRangePicker, selector: "mat-date-range-picker", providers: [
3894 MAT_RANGE_DATE_SELECTION_MODEL_PROVIDER,
3895 MAT_CALENDAR_RANGE_STRATEGY_PROVIDER,
3896 { provide: MatDatepickerBase, useExisting: MatDateRangePicker },
3897 ], exportAs: ["matDateRangePicker"], usesInheritance: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3898i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDateRangePicker, decorators: [{
3899 type: Component,
3900 args: [{
3901 selector: 'mat-date-range-picker',
3902 template: '',
3903 exportAs: 'matDateRangePicker',
3904 changeDetection: ChangeDetectionStrategy.OnPush,
3905 encapsulation: ViewEncapsulation.None,
3906 providers: [
3907 MAT_RANGE_DATE_SELECTION_MODEL_PROVIDER,
3908 MAT_CALENDAR_RANGE_STRATEGY_PROVIDER,
3909 { provide: MatDatepickerBase, useExisting: MatDateRangePicker },
3910 ],
3911 }]
3912 }] });
3913
3914/**
3915 * @license
3916 * Copyright Google LLC All Rights Reserved.
3917 *
3918 * Use of this source code is governed by an MIT-style license that can be
3919 * found in the LICENSE file at https://angular.io/license
3920 */
3921/** Button that will close the datepicker and assign the current selection to the data model. */
3922class MatDatepickerApply {
3923 constructor(_datepicker) {
3924 this._datepicker = _datepicker;
3925 }
3926 _applySelection() {
3927 this._datepicker._applyPendingSelection();
3928 this._datepicker.close();
3929 }
3930}
3931MatDatepickerApply.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerApply, deps: [{ token: MatDatepickerBase }], target: i0.ɵɵFactoryTarget.Directive });
3932MatDatepickerApply.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: MatDatepickerApply, selector: "[matDatepickerApply], [matDateRangePickerApply]", host: { listeners: { "click": "_applySelection()" } }, ngImport: i0 });
3933i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerApply, decorators: [{
3934 type: Directive,
3935 args: [{
3936 selector: '[matDatepickerApply], [matDateRangePickerApply]',
3937 host: { '(click)': '_applySelection()' },
3938 }]
3939 }], ctorParameters: function () { return [{ type: MatDatepickerBase }]; } });
3940/** Button that will close the datepicker and discard the current selection. */
3941class MatDatepickerCancel {
3942 constructor(_datepicker) {
3943 this._datepicker = _datepicker;
3944 }
3945}
3946MatDatepickerCancel.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerCancel, deps: [{ token: MatDatepickerBase }], target: i0.ɵɵFactoryTarget.Directive });
3947MatDatepickerCancel.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: MatDatepickerCancel, selector: "[matDatepickerCancel], [matDateRangePickerCancel]", host: { listeners: { "click": "_datepicker.close()" } }, ngImport: i0 });
3948i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerCancel, decorators: [{
3949 type: Directive,
3950 args: [{
3951 selector: '[matDatepickerCancel], [matDateRangePickerCancel]',
3952 host: { '(click)': '_datepicker.close()' },
3953 }]
3954 }], ctorParameters: function () { return [{ type: MatDatepickerBase }]; } });
3955/**
3956 * Container that can be used to project a row of action buttons
3957 * to the bottom of a datepicker or date range picker.
3958 */
3959class MatDatepickerActions {
3960 constructor(_datepicker, _viewContainerRef) {
3961 this._datepicker = _datepicker;
3962 this._viewContainerRef = _viewContainerRef;
3963 }
3964 ngAfterViewInit() {
3965 this._portal = new TemplatePortal(this._template, this._viewContainerRef);
3966 this._datepicker.registerActions(this._portal);
3967 }
3968 ngOnDestroy() {
3969 this._datepicker.removeActions(this._portal);
3970 // Needs to be null checked since we initialize it in `ngAfterViewInit`.
3971 if (this._portal && this._portal.isAttached) {
3972 this._portal?.detach();
3973 }
3974 }
3975}
3976MatDatepickerActions.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerActions, deps: [{ token: MatDatepickerBase }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component });
3977MatDatepickerActions.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatDatepickerActions, selector: "mat-datepicker-actions, mat-date-range-picker-actions", viewQueries: [{ propertyName: "_template", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0, template: `
3978 <ng-template>
3979 <div class="mat-datepicker-actions">
3980 <ng-content></ng-content>
3981 </div>
3982 </ng-template>
3983 `, isInline: true, styles: [".mat-datepicker-actions{display:flex;justify-content:flex-end;align-items:center;padding:0 8px 8px 8px}.mat-datepicker-actions .mat-button-base+.mat-button-base{margin-left:8px}[dir=rtl] .mat-datepicker-actions .mat-button-base+.mat-button-base{margin-left:0;margin-right:8px}"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3984i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerActions, decorators: [{
3985 type: Component,
3986 args: [{ selector: 'mat-datepicker-actions, mat-date-range-picker-actions', template: `
3987 <ng-template>
3988 <div class="mat-datepicker-actions">
3989 <ng-content></ng-content>
3990 </div>
3991 </ng-template>
3992 `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, styles: [".mat-datepicker-actions{display:flex;justify-content:flex-end;align-items:center;padding:0 8px 8px 8px}.mat-datepicker-actions .mat-button-base+.mat-button-base{margin-left:8px}[dir=rtl] .mat-datepicker-actions .mat-button-base+.mat-button-base{margin-left:0;margin-right:8px}"] }]
3993 }], ctorParameters: function () { return [{ type: MatDatepickerBase }, { type: i0.ViewContainerRef }]; }, propDecorators: { _template: [{
3994 type: ViewChild,
3995 args: [TemplateRef]
3996 }] } });
3997
3998/**
3999 * @license
4000 * Copyright Google LLC All Rights Reserved.
4001 *
4002 * Use of this source code is governed by an MIT-style license that can be
4003 * found in the LICENSE file at https://angular.io/license
4004 */
4005class MatDatepickerModule {
4006}
4007MatDatepickerModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
4008MatDatepickerModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerModule, declarations: [MatCalendar,
4009 MatCalendarBody,
4010 MatDatepicker,
4011 MatDatepickerContent,
4012 MatDatepickerInput,
4013 MatDatepickerToggle,
4014 MatDatepickerToggleIcon,
4015 MatMonthView,
4016 MatYearView,
4017 MatMultiYearView,
4018 MatCalendarHeader,
4019 MatDateRangeInput,
4020 MatStartDate,
4021 MatEndDate,
4022 MatDateRangePicker,
4023 MatDatepickerActions,
4024 MatDatepickerCancel,
4025 MatDatepickerApply], imports: [CommonModule,
4026 MatButtonModule,
4027 OverlayModule,
4028 A11yModule,
4029 PortalModule,
4030 MatCommonModule], exports: [CdkScrollableModule,
4031 MatCalendar,
4032 MatCalendarBody,
4033 MatDatepicker,
4034 MatDatepickerContent,
4035 MatDatepickerInput,
4036 MatDatepickerToggle,
4037 MatDatepickerToggleIcon,
4038 MatMonthView,
4039 MatYearView,
4040 MatMultiYearView,
4041 MatCalendarHeader,
4042 MatDateRangeInput,
4043 MatStartDate,
4044 MatEndDate,
4045 MatDateRangePicker,
4046 MatDatepickerActions,
4047 MatDatepickerCancel,
4048 MatDatepickerApply] });
4049MatDatepickerModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerModule, providers: [MatDatepickerIntl, MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER], imports: [CommonModule,
4050 MatButtonModule,
4051 OverlayModule,
4052 A11yModule,
4053 PortalModule,
4054 MatCommonModule, CdkScrollableModule] });
4055i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatDatepickerModule, decorators: [{
4056 type: NgModule,
4057 args: [{
4058 imports: [
4059 CommonModule,
4060 MatButtonModule,
4061 OverlayModule,
4062 A11yModule,
4063 PortalModule,
4064 MatCommonModule,
4065 ],
4066 exports: [
4067 CdkScrollableModule,
4068 MatCalendar,
4069 MatCalendarBody,
4070 MatDatepicker,
4071 MatDatepickerContent,
4072 MatDatepickerInput,
4073 MatDatepickerToggle,
4074 MatDatepickerToggleIcon,
4075 MatMonthView,
4076 MatYearView,
4077 MatMultiYearView,
4078 MatCalendarHeader,
4079 MatDateRangeInput,
4080 MatStartDate,
4081 MatEndDate,
4082 MatDateRangePicker,
4083 MatDatepickerActions,
4084 MatDatepickerCancel,
4085 MatDatepickerApply,
4086 ],
4087 declarations: [
4088 MatCalendar,
4089 MatCalendarBody,
4090 MatDatepicker,
4091 MatDatepickerContent,
4092 MatDatepickerInput,
4093 MatDatepickerToggle,
4094 MatDatepickerToggleIcon,
4095 MatMonthView,
4096 MatYearView,
4097 MatMultiYearView,
4098 MatCalendarHeader,
4099 MatDateRangeInput,
4100 MatStartDate,
4101 MatEndDate,
4102 MatDateRangePicker,
4103 MatDatepickerActions,
4104 MatDatepickerCancel,
4105 MatDatepickerApply,
4106 ],
4107 providers: [MatDatepickerIntl, MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER],
4108 }]
4109 }] });
4110
4111/**
4112 * @license
4113 * Copyright Google LLC All Rights Reserved.
4114 *
4115 * Use of this source code is governed by an MIT-style license that can be
4116 * found in the LICENSE file at https://angular.io/license
4117 */
4118
4119/**
4120 * @license
4121 * Copyright Google LLC All Rights Reserved.
4122 *
4123 * Use of this source code is governed by an MIT-style license that can be
4124 * found in the LICENSE file at https://angular.io/license
4125 */
4126
4127/**
4128 * Generated bundle index. Do not edit.
4129 */
4130
4131export { DateRange, DefaultMatCalendarRangeStrategy, MAT_DATEPICKER_SCROLL_STRATEGY, MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY, MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER, MAT_DATEPICKER_VALIDATORS, MAT_DATEPICKER_VALUE_ACCESSOR, MAT_DATE_RANGE_SELECTION_STRATEGY, MAT_RANGE_DATE_SELECTION_MODEL_FACTORY, MAT_RANGE_DATE_SELECTION_MODEL_PROVIDER, MAT_SINGLE_DATE_SELECTION_MODEL_FACTORY, MAT_SINGLE_DATE_SELECTION_MODEL_PROVIDER, MatCalendar, MatCalendarBody, MatCalendarCell, MatCalendarHeader, MatDateRangeInput, MatDateRangePicker, MatDateSelectionModel, MatDatepicker, MatDatepickerActions, MatDatepickerApply, MatDatepickerCancel, MatDatepickerContent, MatDatepickerInput, MatDatepickerInputEvent, MatDatepickerIntl, MatDatepickerModule, MatDatepickerToggle, MatDatepickerToggleIcon, MatEndDate, MatMonthView, MatMultiYearView, MatRangeDateSelectionModel, MatSingleDateSelectionModel, MatStartDate, MatYearView, matDatepickerAnimations, yearsPerPage, yearsPerRow };
4132//# sourceMappingURL=datepicker.mjs.map