UNPKG

@ng-matero/extensions

Version:
622 lines 136 kB
import { DOWN_ARROW, END, ENTER, HOME, LEFT_ARROW, PAGE_DOWN, PAGE_UP, RIGHT_ARROW, UP_ARROW, } from '@angular/cdk/keycodes'; import { CdkPortalOutlet, ComponentPortal, } from '@angular/cdk/portal'; import { ChangeDetectionStrategy, Component, EventEmitter, Inject, Injector, Input, Optional, Output, ViewEncapsulation, afterNextRender, booleanAttribute, inject, } from '@angular/core'; import { MatButton, MatIconButton } from '@angular/material/button'; import { MTX_DATETIME_FORMATS, } from '@ng-matero/extensions/core'; import { MtxClock } from './clock'; import { mtxDatetimepickerAnimations } from './datetimepicker-animations'; import { createMissingDateImplError } from './datetimepicker-errors'; import { MtxDatetimepickerFilterType } from './datetimepicker-filtertype'; import { MtxMonthView } from './month-view'; import { MtxMultiYearView, getActiveOffset, isSameMultiYearView, yearsPerPage, yearsPerRow, } from './multi-year-view'; import { MtxTime } from './time'; import { MtxYearView } from './year-view'; import * as i0 from "@angular/core"; import * as i1 from "./datetimepicker-intl"; import * as i2 from "@ng-matero/extensions/core"; /** * A calendar that is used as part of the datetimepicker. * @docs-private */ export class MtxCalendar { constructor(_elementRef, _intl, _ngZone, _adapter, _dateFormats, _changeDetectorRef) { this._elementRef = _elementRef; this._intl = _intl; this._ngZone = _ngZone; this._adapter = _adapter; this._dateFormats = _dateFormats; /** Whether to show multi-year view. */ this.multiYearSelector = false; /** Whether the clock uses 12 hour format. */ this.twelvehour = false; /** Whether the calendar should be started in month or year view. */ this.startView = 'month'; /** Step over minutes. */ this.timeInterval = 1; /** Prevent user to select same date time */ this.preventSameDateTimeSelection = false; /** Input for action buttons. */ this.actionsPortal = null; /** Emits when the currently selected date changes. */ this.selectedChange = new EventEmitter(); /** Emits when the view has been changed. */ this.viewChanged = new EventEmitter(); this._userSelection = new EventEmitter(); this._clockView = 'hour'; this._injector = inject(Injector); this._type = 'date'; /** * Whether the calendar is in time mode. In time mode the calendar clock gets time input elements * rather then just clock. When touchUi is enabled this will be disabled */ this.timeInput = false; /** Date filter for the month and year views. */ this._dateFilterForViews = (date) => { return (!!date && (!this.dateFilter || this.dateFilter(date, MtxDatetimepickerFilterType.DATE)) && (!this.minDate || this._adapter.compareDate(date, this.minDate) >= 0) && (!this.maxDate || this._adapter.compareDate(date, this.maxDate) <= 0)); }; if (!this._adapter) { throw createMissingDateImplError('DatetimeAdapter'); } if (!this._dateFormats) { throw createMissingDateImplError('MTX_DATETIME_FORMATS'); } this._intlChanges = _intl.changes.subscribe(() => _changeDetectorRef.markForCheck()); } /** The display type of datetimepicker. */ get type() { return this._type; } set type(value) { this._type = value || 'date'; if (this.type === 'year') { this.multiYearSelector = true; } } /** A date representing the period (month or year) to start the calendar in. */ get startAt() { return this._startAt; } set startAt(value) { this._startAt = this._adapter.getValidDateOrNull(value); } /** The currently selected date. */ get selected() { return this._selected; } set selected(value) { this._selected = this._adapter.getValidDateOrNull(value); } /** The minimum selectable date. */ get minDate() { return this._minDate; } set minDate(value) { this._minDate = this._adapter.getValidDateOrNull(value); } /** The maximum selectable date. */ get maxDate() { return this._maxDate; } set maxDate(value) { this._maxDate = this._adapter.getValidDateOrNull(value); } /** * The current active date. This determines which time period is shown and which date is * highlighted when using keyboard navigation. */ get _activeDate() { return this._clampedActiveDate; } set _activeDate(value) { const oldActiveDate = this._clampedActiveDate; this._clampedActiveDate = this._adapter.clampDate(value, this.minDate, this.maxDate); // whenever active date changed, and possibly got clamped we should adjust the am/pm setting this._selectAMPM(this._clampedActiveDate); if (oldActiveDate && this._clampedActiveDate && this.currentView === 'month' && !this._adapter.sameMonthAndYear(oldActiveDate, this._clampedActiveDate)) { if (this._adapter.isInNextMonth(oldActiveDate, this._clampedActiveDate)) { this.calendarState('right'); } else { this.calendarState('left'); } } } /** Whether the calendar is in month view. */ get currentView() { return this._currentView; } set currentView(view) { this._currentView = view; this.viewChanged.emit(view); } get _yearPeriodText() { if (this.currentView === 'multi-year') { // The offset from the active year to the "slot" for the starting year is the // *actual* first rendered year in the multi-year view, and the last year is // just yearsPerPage - 1 away. const activeYear = this._adapter.getYear(this._activeDate); const minYearOfPage = activeYear - getActiveOffset(this._adapter, this._activeDate, this.minDate, this.maxDate); const maxYearOfPage = minYearOfPage + yearsPerPage - 1; const minYearName = this._adapter.getYearName(this._adapter.createDate(minYearOfPage, 0, 1)); const maxYearName = this._adapter.getYearName(this._adapter.createDate(maxYearOfPage, 0, 1)); return this._intl.formatYearRange(minYearName, maxYearName); } return this.currentView === 'month' ? this._adapter.getMonthNames('long')[this._adapter.getMonth(this._activeDate)] : this._adapter.getYearName(this._activeDate); } get _yearButtonText() { return this._adapter.getYearName(this._activeDate); } get _yearButtonLabel() { return this.multiYearSelector ? this._intl.switchToMultiYearViewLabel : this._intl.switchToYearViewLabel; } get _dateButtonText() { switch (this.type) { case 'month': return this._adapter.getMonthNames('long')[this._adapter.getMonth(this._activeDate)]; default: return this._adapter.format(this._activeDate, this._dateFormats.display.popupHeaderDateLabel); } } get _dateButtonLabel() { return this._intl.switchToMonthViewLabel; } get _hoursButtonText() { let hour = this._adapter.getHour(this._activeDate); if (this.twelvehour) { if (hour === 0) { hour = 24; } hour = hour > 12 ? hour - 12 : hour; } return this._2digit(hour); } get _hourButtonLabel() { return this._intl.switchToClockHourViewLabel; } get _minutesButtonText() { return this._2digit(this._adapter.getMinute(this._activeDate)); } get _minuteButtonLabel() { return this._intl.switchToClockMinuteViewLabel; } get _prevButtonLabel() { switch (this._currentView) { case 'month': return this._intl.prevMonthLabel; case 'year': return this._intl.prevYearLabel; case 'multi-year': return this._intl.prevMultiYearLabel; default: return ''; } } get _nextButtonLabel() { switch (this._currentView) { case 'month': return this._intl.nextMonthLabel; case 'year': return this._intl.nextYearLabel; case 'multi-year': return this._intl.nextMultiYearLabel; default: return ''; } } _userSelected() { this._userSelection.emit(); } ngAfterContentInit() { if (this.headerComponent) { this._calendarHeaderPortal = new ComponentPortal(this.headerComponent); } this._activeDate = this.startAt || this._adapter.today(); this._selectAMPM(this._activeDate); if (this.type === 'year') { this.currentView = 'multi-year'; } else if (this.type === 'month') { this.currentView = 'year'; } else if (this.type === 'time') { this.currentView = 'clock'; } else { this.currentView = this.startView || 'month'; } } ngOnDestroy() { this._intlChanges.unsubscribe(); } /** Handles date selection in the month view. */ _dateSelected(date) { if (this.type === 'date') { if (!this._adapter.sameDate(date, this.selected) || !this.preventSameDateTimeSelection) { this.selectedChange.emit(date); } } else { this.selectedChange.emit(date); this._activeDate = date; this.currentView = 'clock'; } } /** Handles month selection in the year view. */ _monthSelected(month) { if (this.type === 'month') { if (!this._adapter.sameMonthAndYear(month, this.selected) || !this.preventSameDateTimeSelection) { this.selectedChange.emit(this._adapter.getFirstDateOfMonth(month)); } } else { this._activeDate = month; this.currentView = 'month'; this._clockView = 'hour'; } } /** Handles year selection in the multi year view. */ _yearSelected(year) { if (this.type === 'year') { if (!this._adapter.sameYear(year, this.selected) || !this.preventSameDateTimeSelection) { const normalizedDate = this._adapter.createDatetime(this._adapter.getYear(year), 0, 1, 0, 0); this.selectedChange.emit(normalizedDate); } } else { this._activeDate = year; this.currentView = 'year'; } } _timeSelected(date) { this._activeDate = this._updateDate(date); if (!this._adapter.sameDatetime(date, this.selected) || !this.preventSameDateTimeSelection) { this.selectedChange.emit(date); } } _dialTimeSelected(date) { if (this._clockView !== 'minute') { this._activeDate = this._updateDate(date); this._clockView = 'minute'; } else { if (!this._adapter.sameDatetime(date, this.selected) || !this.preventSameDateTimeSelection) { this.selectedChange.emit(date); } } } _onActiveDateChange(date) { this._activeDate = date; } _updateDate(date) { if (this.twelvehour) { const HOUR = this._adapter.getHour(date); if (HOUR === 12) { if (this._AMPM === 'AM') { return this._adapter.addCalendarHours(date, -12); } } else if (this._AMPM === 'PM') { return this._adapter.addCalendarHours(date, 12); } } return date; } _selectAMPM(date) { const hour = this._adapter.getHour(date); if (hour > 11) { this._AMPM = 'PM'; } else { this._AMPM = 'AM'; } } _ampmClicked(source) { this._currentView = 'clock'; if (source === this._AMPM) { return; } // if AMPM changed from PM to AM substract 12 hours const currentHour = this._adapter.getHour(this._activeDate); let newHourValue; if (source === 'AM') { newHourValue = currentHour >= 12 ? this._adapter.getHour(this._activeDate) - 12 : 12; } // otherwise add 12 hours else { newHourValue = (currentHour + 12) % 24; } const newActiveDate = this._adapter.clampDate(this._adapter.createDatetime(this._adapter.getYear(this._activeDate), this._adapter.getMonth(this._activeDate), this._adapter.getDate(this._activeDate), newHourValue, this._adapter.getMinute(this._activeDate)), this.minDate, this.maxDate); // only if our clamped date is not changed, we know we can apply the newActiveDate to the // activeDate if (this._adapter.getHour(newActiveDate) === newHourValue) { this._activeDate = newActiveDate; this._AMPM = source; } } _yearClicked() { if (this.type === 'year' || this.multiYearSelector) { this.currentView = 'multi-year'; return; } this.currentView = 'year'; } _dateClicked() { if (this.type !== 'month') { this.currentView = 'month'; } } _hoursClicked() { this.currentView = 'clock'; this._clockView = 'hour'; } _minutesClicked() { this.currentView = 'clock'; this._clockView = 'minute'; } /** Handles user clicks on the previous button. */ _previousClicked() { this._activeDate = this.currentView === 'month' ? this._adapter.addCalendarMonths(this._activeDate, -1) : this._adapter.addCalendarYears(this._activeDate, this.currentView === 'year' ? -1 : -yearsPerPage); } /** Handles user clicks on the next button. */ _nextClicked() { this._activeDate = this.currentView === 'month' ? this._adapter.addCalendarMonths(this._activeDate, 1) : this._adapter.addCalendarYears(this._activeDate, this.currentView === 'year' ? 1 : yearsPerPage); } /** Whether the previous period button is enabled. */ _previousEnabled() { if (!this.minDate) { return true; } return !this.minDate || !this._isSameView(this._activeDate, this.minDate); } /** Whether the next period button is enabled. */ _nextEnabled() { return !this.maxDate || !this._isSameView(this._activeDate, this.maxDate); } /** Handles keydown events on the calendar body. */ _handleCalendarBodyKeydown(event) { // TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent // disabled ones from being selected. This may not be ideal, we should look into whether // navigation should skip over disabled dates, and if so, how to implement that efficiently. if (this.currentView === 'month') { this._handleCalendarBodyKeydownInMonthView(event); } else if (this.currentView === 'year') { this._handleCalendarBodyKeydownInYearView(event); } else if (this.currentView === 'multi-year') { this._handleCalendarBodyKeydownInMultiYearView(event); } else { this._handleCalendarBodyKeydownInClockView(event); } } _focusActiveCell() { afterNextRender(() => { this._elementRef.nativeElement.focus(); }, { injector: this._injector }); } _calendarStateDone() { this._calendarState = ''; } /** Whether the two dates represent the same view in the current view mode (month or year). */ _isSameView(date1, date2) { if (this.currentView === 'month') { return (this._adapter.getYear(date1) === this._adapter.getYear(date2) && this._adapter.getMonth(date1) === this._adapter.getMonth(date2)); } if (this.currentView === 'year') { return this._adapter.getYear(date1) === this._adapter.getYear(date2); } // Otherwise we are in 'multi-year' view. return isSameMultiYearView(this._adapter, date1, date2, this.minDate, this.maxDate); } /** Handles keydown events on the calendar body when calendar is in month view. */ _handleCalendarBodyKeydownInMonthView(event) { switch (event.keyCode) { case LEFT_ARROW: this._activeDate = this._adapter.addCalendarDays(this._activeDate, -1); break; case RIGHT_ARROW: this._activeDate = this._adapter.addCalendarDays(this._activeDate, 1); break; case UP_ARROW: this._activeDate = this._adapter.addCalendarDays(this._activeDate, -7); break; case DOWN_ARROW: this._activeDate = this._adapter.addCalendarDays(this._activeDate, 7); break; case HOME: this._activeDate = this._adapter.addCalendarDays(this._activeDate, 1 - this._adapter.getDate(this._activeDate)); break; case END: this._activeDate = this._adapter.addCalendarDays(this._activeDate, this._adapter.getNumDaysInMonth(this._activeDate) - this._adapter.getDate(this._activeDate)); break; case PAGE_UP: this._activeDate = event.altKey ? this._adapter.addCalendarYears(this._activeDate, -1) : this._adapter.addCalendarMonths(this._activeDate, -1); break; case PAGE_DOWN: this._activeDate = event.altKey ? this._adapter.addCalendarYears(this._activeDate, 1) : this._adapter.addCalendarMonths(this._activeDate, 1); break; case ENTER: if (this._dateFilterForViews(this._activeDate)) { this._dateSelected(this._activeDate); // Prevent unexpected default actions such as form submission. event.preventDefault(); } return; default: // Don't prevent default or focus active cell on keys that we don't explicitly handle. return; } // Prevent unexpected default actions such as form submission. event.preventDefault(); } /** Handles keydown events on the calendar body when calendar is in year view. */ _handleCalendarBodyKeydownInYearView(event) { switch (event.keyCode) { case LEFT_ARROW: this._activeDate = this._adapter.addCalendarMonths(this._activeDate, -1); break; case RIGHT_ARROW: this._activeDate = this._adapter.addCalendarMonths(this._activeDate, 1); break; case UP_ARROW: this._activeDate = this._prevMonthInSameCol(this._activeDate); break; case DOWN_ARROW: this._activeDate = this._nextMonthInSameCol(this._activeDate); break; case HOME: this._activeDate = this._adapter.addCalendarMonths(this._activeDate, -this._adapter.getMonth(this._activeDate)); break; case END: this._activeDate = this._adapter.addCalendarMonths(this._activeDate, 11 - this._adapter.getMonth(this._activeDate)); break; case PAGE_UP: this._activeDate = this._adapter.addCalendarYears(this._activeDate, event.altKey ? -10 : -1); break; case PAGE_DOWN: this._activeDate = this._adapter.addCalendarYears(this._activeDate, event.altKey ? 10 : 1); break; case ENTER: this._monthSelected(this._activeDate); break; default: // Don't prevent default or focus active cell on keys that we don't explicitly handle. return; } // Prevent unexpected default actions such as form submission. event.preventDefault(); } /** Handles keydown events on the calendar body when calendar is in multi-year view. */ _handleCalendarBodyKeydownInMultiYearView(event) { switch (event.keyCode) { case LEFT_ARROW: this._activeDate = this._adapter.addCalendarYears(this._activeDate, -1); break; case RIGHT_ARROW: this._activeDate = this._adapter.addCalendarYears(this._activeDate, 1); break; case UP_ARROW: this._activeDate = this._adapter.addCalendarYears(this._activeDate, -yearsPerRow); break; case DOWN_ARROW: this._activeDate = this._adapter.addCalendarYears(this._activeDate, yearsPerRow); break; case HOME: this._activeDate = this._adapter.addCalendarYears(this._activeDate, -getActiveOffset(this._adapter, this._activeDate, this.minDate, this.maxDate)); break; case END: this._activeDate = this._adapter.addCalendarYears(this._activeDate, yearsPerPage - getActiveOffset(this._adapter, this._activeDate, this.minDate, this.maxDate) - 1); break; case PAGE_UP: this._activeDate = this._adapter.addCalendarYears(this._activeDate, event.altKey ? -yearsPerPage * 10 : -yearsPerPage); break; case PAGE_DOWN: this._activeDate = this._adapter.addCalendarYears(this._activeDate, event.altKey ? yearsPerPage * 10 : yearsPerPage); break; case ENTER: this._yearSelected(this._activeDate); break; default: // Don't prevent default or focus active cell on keys that we don't explicitly handle. return; } } /** Handles keydown events on the calendar body when calendar is in month view. */ _handleCalendarBodyKeydownInClockView(event) { switch (event.keyCode) { case UP_ARROW: this._activeDate = this._clockView === 'hour' ? this._adapter.addCalendarHours(this._activeDate, 1) : this._adapter.addCalendarMinutes(this._activeDate, this.timeInterval); break; case DOWN_ARROW: this._activeDate = this._clockView === 'hour' ? this._adapter.addCalendarHours(this._activeDate, -1) : this._adapter.addCalendarMinutes(this._activeDate, -this.timeInterval); break; case ENTER: if (!this.timeInput) { this._dialTimeSelected(this._activeDate); } return; default: // Don't prevent default or focus active cell on keys that we don't explicitly handle. return; } // Prevent unexpected default actions such as form submission. event.preventDefault(); } /** * Determine the date for the month that comes before the given month in the same column in the * calendar table. */ _prevMonthInSameCol(date) { // Determine how many months to jump forward given that there are 2 empty slots at the beginning // of each year. const increment = this._adapter.getMonth(date) <= 4 ? -5 : this._adapter.getMonth(date) >= 7 ? -7 : -12; return this._adapter.addCalendarMonths(date, increment); } /** * Determine the date for the month that comes after the given month in the same column in the * calendar table. */ _nextMonthInSameCol(date) { // Determine how many months to jump forward given that there are 2 empty slots at the beginning // of each year. const increment = this._adapter.getMonth(date) <= 4 ? 7 : this._adapter.getMonth(date) >= 7 ? 5 : 12; return this._adapter.addCalendarMonths(date, increment); } calendarState(direction) { this._calendarState = direction; } _2digit(n) { return ('00' + n).slice(-2); } /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: MtxCalendar, deps: [{ token: i0.ElementRef }, { token: i1.MtxDatetimepickerIntl }, { token: i0.NgZone }, { token: i2.DatetimeAdapter, optional: true }, { token: MTX_DATETIME_FORMATS, optional: true }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); } /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.0", type: MtxCalendar, isStandalone: true, selector: "mtx-calendar", inputs: { multiYearSelector: ["multiYearSelector", "multiYearSelector", booleanAttribute], twelvehour: ["twelvehour", "twelvehour", booleanAttribute], startView: "startView", timeInterval: "timeInterval", dateFilter: "dateFilter", preventSameDateTimeSelection: "preventSameDateTimeSelection", headerComponent: "headerComponent", actionsPortal: "actionsPortal", type: "type", startAt: "startAt", timeInput: ["timeInput", "timeInput", booleanAttribute], selected: "selected", minDate: "minDate", maxDate: "maxDate" }, outputs: { selectedChange: "selectedChange", viewChanged: "viewChanged", _userSelection: "_userSelection" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "_handleCalendarBodyKeydown($event)" }, properties: { "class.mtx-calendar-with-time-input": "timeInput" }, classAttribute: "mtx-calendar" }, exportAs: ["mtxCalendar"], ngImport: i0, template: "<div class=\"mtx-calendar-header\">\n @if (_calendarHeaderPortal) {\n <ng-template [cdkPortalOutlet]=\"_calendarHeaderPortal\"></ng-template>\n } @else {\n @if (type !== 'time') {\n <button\n mat-button type=\"button\" class=\"mtx-calendar-header-year\"\n [class.active]=\"currentView === 'year' || currentView === 'multi-year'\"\n [attr.aria-label]=\"_yearButtonLabel\"\n (click)=\"_yearClicked()\">\n <span>{{ _yearButtonText }}</span>\n @if (multiYearSelector || type === 'year') {\n <svg\n class=\"mtx-calendar-header-year-dropdown\" matButtonIcon iconPositionEnd\n width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M7,10L12,15L17,10H7Z\" />\n </svg>\n }\n </button>\n }\n @if (type !== 'year') {\n <div class=\"mtx-calendar-header-date-time\">\n @if (type !== 'time') {\n <button\n mat-button type=\"button\" class=\"mtx-calendar-header-date\"\n [class.active]=\"currentView === 'month'\"\n [class.not-clickable]=\"type === 'month'\"\n [attr.aria-label]=\"_dateButtonLabel\"\n (click)=\"_dateClicked()\">{{ _dateButtonText }}</button>\n }\n @if (type.endsWith('time')) {\n <span class=\"mtx-calendar-header-time\" [class.active]=\"currentView === 'clock'\">\n <span class=\"mtx-calendar-header-hour-minute-container\">\n <button mat-button type=\"button\" class=\"mtx-calendar-header-hours\"\n [class.active]=\"_clockView === 'hour'\"\n [attr.aria-label]=\"_hourButtonLabel\"\n (click)=\"_hoursClicked()\">{{ _hoursButtonText }}</button>\n <span class=\"mtx-calendar-header-hour-minute-separator\">:</span>\n <button mat-button type=\"button\" class=\"mtx-calendar-header-minutes\"\n [class.active]=\"_clockView === 'minute'\"\n [attr.aria-label]=\"_minuteButtonLabel\"\n (click)=\"_minutesClicked()\">{{ _minutesButtonText }}</button>\n </span>\n @if (twelvehour) {\n <span class=\"mtx-calendar-header-ampm-container\">\n <button mat-button type=\"button\" class=\"mtx-calendar-header-ampm\"\n [class.active]=\"_AMPM === 'AM'\" aria-label=\"AM\"\n (click)=\"_ampmClicked('AM')\">AM</button>\n <button mat-button type=\"button\" class=\"mtx-calendar-header-ampm\"\n [class.active]=\"_AMPM === 'PM'\" aria-label=\"PM\"\n (click)=\"_ampmClicked('PM')\">PM</button>\n </span>\n }\n </span>\n }\n </div>\n }\n }\n</div>\n\n<div class=\"mtx-calendar-content\">\n @if (currentView === 'month' || currentView === 'year' || currentView === 'multi-year') {\n <div class=\"mtx-month-content\">\n <div class=\"mtx-calendar-controls\">\n <button mat-icon-button type=\"button\"\n class=\"mtx-calendar-previous-button\"\n [class.disabled]=\"!_previousEnabled()\"\n [attr.aria-disabled]=\"!_previousEnabled()\"\n [attr.aria-label]=\"_prevButtonLabel\"\n (click)=\"_previousClicked()\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\n <path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\" />\n </svg>\n </button>\n <div class=\"mtx-calendar-period-button\"\n [@slideCalendar]=\"_calendarState\"\n (@slideCalendar.done)=\"_calendarStateDone()\">\n <strong>{{ _yearPeriodText }}</strong>\n </div>\n <button mat-icon-button type=\"button\"\n class=\"mtx-calendar-next-button\"\n [class.disabled]=\"!_nextEnabled()\"\n [attr.aria-disabled]=\"!_nextEnabled()\"\n [attr.aria-label]=\"_nextButtonLabel\"\n (click)=\"_nextClicked()\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\n <path d=\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\" />\n </svg>\n </button>\n </div>\n </div>\n }\n\n @switch (currentView) {\n @case ('month') {\n <mtx-month-view\n (_userSelection)=\"_userSelected()\"\n (selectedChange)=\"_dateSelected($event)\"\n [activeDate]=\"_activeDate\"\n [dateFilter]=\"_dateFilterForViews\"\n [selected]=\"selected!\"\n [type]=\"type\">\n </mtx-month-view>\n }\n @case ('year') {\n <mtx-year-view\n (_userSelection)=\"_userSelected()\"\n (selectedChange)=\"_monthSelected($event)\"\n [activeDate]=\"_activeDate\"\n [dateFilter]=\"_dateFilterForViews\"\n [selected]=\"selected!\"\n [type]=\"type\">\n </mtx-year-view>\n }\n @case ('multi-year') {\n <mtx-multi-year-view\n (_userSelection)=\"_userSelected()\"\n (selectedChange)=\"_yearSelected($event)\"\n [activeDate]=\"_activeDate\"\n [dateFilter]=\"_dateFilterForViews\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [selected]=\"selected!\"\n [type]=\"type\">\n </mtx-multi-year-view>\n }\n @default {\n @if (timeInput) {\n <mtx-time\n (_userSelection)=\"_userSelected()\"\n (activeDateChange)=\"_onActiveDateChange($event)\"\n (selectedChange)=\"_timeSelected($event)\"\n [AMPM]=\"_AMPM\"\n (ampmChange)=\"_ampmClicked($event)\"\n [clockView]=\"_clockView\"\n (clockViewChange)=\"_clockView = $event\"\n [twelvehour]=\"twelvehour\"\n [dateFilter]=\"dateFilter\"\n [interval]=\"timeInterval\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [selected]=\"_activeDate\"\n [actionsPortal]=\"actionsPortal\">\n </mtx-time>\n } @else {\n <mtx-clock (_userSelection)=\"_userSelected()\"\n (activeDateChange)=\"_onActiveDateChange($event)\"\n (selectedChange)=\"_dialTimeSelected($event)\"\n [AMPM]=\"_AMPM\"\n [dateFilter]=\"dateFilter\"\n [interval]=\"timeInterval\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [selected]=\"_activeDate\"\n [startView]=\"_clockView\"\n [twelvehour]=\"twelvehour\">\n </mtx-clock>\n }\n }\n }\n</div>\n", styles: [".mtx-calendar{display:block;outline:none;font-family:var(--mtx-datetimepicker-calendar-text-font, var(--mat-app-body-large-font));font-size:var(--mtx-datetimepicker-calendar-text-size, var(--mat-app-body-large-size))}.mtx-calendar-header{box-sizing:border-box;padding:8px;border-bottom:1px solid var(--mtx-datetimepicker-calendar-header-divider-color, var(--mat-app-outline-variant));border-top-left-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large));border-top-right-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large));background-color:var(--mtx-datetimepicker-calendar-header-background-color);color:var(--mtx-datetimepicker-calendar-header-text-color, var(--mat-app-on-surface-variant));--mdc-text-button-container-shape: var(--mtx-datetimepicker-selector-container-shape, var(--mat-app-corner-small))}.mtx-calendar-header .mtx-calendar-header-year,.mtx-calendar-header .mtx-calendar-header-date,.mtx-calendar-header .mtx-calendar-header-hours,.mtx-calendar-header .mtx-calendar-header-minutes,.mtx-calendar-header .mtx-calendar-header-ampm{height:auto;min-width:auto;padding:0 4px;text-align:inherit;line-height:inherit;color:inherit;font-size:inherit;font-weight:inherit;letter-spacing:normal;white-space:normal;word-break:break-word}.mtx-calendar-header .mtx-calendar-header-year .mat-mdc-button-touch-target,.mtx-calendar-header .mtx-calendar-header-date .mat-mdc-button-touch-target,.mtx-calendar-header .mtx-calendar-header-hours .mat-mdc-button-touch-target,.mtx-calendar-header .mtx-calendar-header-minutes .mat-mdc-button-touch-target,.mtx-calendar-header .mtx-calendar-header-ampm .mat-mdc-button-touch-target{height:100%}.mtx-calendar-header .mtx-calendar-header-year{line-height:24px}.mtx-calendar-header-date-time{font-size:24px;line-height:36px}.mtx-calendar-header-year:not(.active),.mtx-calendar-header-date:not(.active),.mtx-calendar-header-hours:not(.active),.mtx-calendar-header-minutes:not(.active),.mtx-calendar-header-ampm:not(.active){opacity:.6}.mtx-calendar-header-year.not-clickable,.mtx-calendar-header-date.not-clickable,.mtx-calendar-header-hours.not-clickable,.mtx-calendar-header-minutes.not-clickable,.mtx-calendar-header-ampm.not-clickable{cursor:initial}.mtx-calendar-header-time{display:inline-flex}.mtx-calendar-header-time:not(.active){opacity:.6}.mtx-calendar-header-time:not(.active) .mtx-calendar-header-hours,.mtx-calendar-header-time:not(.active) .mtx-calendar-header-minutes,.mtx-calendar-header-time:not(.active) .mtx-calendar-header-ampm{opacity:1}.mtx-calendar-header-hour-minute-separator{display:inline-block;width:8px;text-align:center}.mtx-calendar-header-ampm-container{display:inline-flex;flex-direction:column;line-height:18px;font-size:12px}[mode=landscape] .mtx-calendar{display:flex}[mode=landscape] .mtx-calendar .mtx-calendar-header{width:144px;min-width:144px;padding:16px 8px;border-bottom-width:0;border-top-right-radius:0;border-bottom-right-radius:0;border-right:1px solid var(--mtx-datetimepicker-calendar-header-divider-color, var(--mat-app-outline-variant));border-top-left-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large));border-bottom-left-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large))}[dir=rtl] [mode=landscape] .mtx-calendar .mtx-calendar-header{border-top-left-radius:0;border-bottom-left-radius:0;border-right-width:0;border-left:1px solid var(--mtx-datetimepicker-calendar-header-divider-color, var(--mat-app-outline-variant));border-top-right-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large));border-bottom-right-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large))}[mode=landscape] .mtx-calendar .mtx-calendar-header-year+.mtx-calendar-header-date-time,[mode=landscape] .mtx-calendar .mtx-calendar-header-date+.mtx-calendar-header-time{margin-top:4px}[mode=landscape] .mtx-calendar .mtx-calendar-header-date-time{font-size:28px}[mode=landscape] .mtx-calendar .mtx-calendar-header-time{display:flex;flex-direction:column}[mode=landscape] .mtx-calendar .mtx-calendar-header-time .mtx-calendar-header-hours,[mode=landscape] .mtx-calendar .mtx-calendar-header-time .mtx-calendar-header-minutes,[mode=landscape] .mtx-calendar .mtx-calendar-header-time .mtx-calendar-header-ampm{width:40px;text-align:center}[mode=landscape] .mtx-calendar .mtx-calendar-header-ampm-container{flex-direction:row;font-size:20px}[mode=landscape] .mtx-calendar .mtx-calendar-header-ampm{padding:4px}[mode=landscape] .mtx-calendar .mtx-calendar-header-ampm+.mtx-calendar-header-ampm{margin:0 8px}[mode=landscape] .mtx-datetimepicker-content-container-with-actions .mtx-calendar .mtx-calendar-header{border-bottom-left-radius:0;border-bottom-right-radius:0}[mode=landscape] .mtx-datetimepicker-actions:before{position:absolute;top:0;left:0;box-sizing:border-box;width:144px;height:100%;content:\"\";border-right:1px solid var(--mtx-datetimepicker-calendar-header-divider-color, var(--mat-app-outline-variant));background-color:var(--mtx-datetimepicker-calendar-header-background-color);border-bottom-left-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large))}[dir=rtl] [mode=landscape] .mtx-datetimepicker-actions:before{left:auto;right:0;border-right-width:0;border-left:1px solid var(--mtx-datetimepicker-calendar-header-divider-color, var(--mat-app-outline-variant));border-bottom-left-radius:0;border-bottom-right-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large))}@media all and (orientation: landscape){[mode=auto] .mtx-calendar{display:flex}[mode=auto] .mtx-calendar .mtx-calendar-header{width:144px;min-width:144px;padding:16px 8px;border-bottom-width:0;border-top-right-radius:0;border-bottom-right-radius:0;border-right:1px solid var(--mtx-datetimepicker-calendar-header-divider-color, var(--mat-app-outline-variant));border-top-left-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large));border-bottom-left-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large))}[dir=rtl] [mode=auto] .mtx-calendar .mtx-calendar-header{border-top-left-radius:0;border-bottom-left-radius:0;border-right-width:0;border-left:1px solid var(--mtx-datetimepicker-calendar-header-divider-color, var(--mat-app-outline-variant));border-top-right-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large));border-bottom-right-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large))}[mode=auto] .mtx-calendar .mtx-calendar-header-year+.mtx-calendar-header-date-time,[mode=auto] .mtx-calendar .mtx-calendar-header-date+.mtx-calendar-header-time{margin-top:4px}[mode=auto] .mtx-calendar .mtx-calendar-header-date-time{font-size:28px}[mode=auto] .mtx-calendar .mtx-calendar-header-time{display:flex;flex-direction:column}[mode=auto] .mtx-calendar .mtx-calendar-header-time .mtx-calendar-header-hours,[mode=auto] .mtx-calendar .mtx-calendar-header-time .mtx-calendar-header-minutes,[mode=auto] .mtx-calendar .mtx-calendar-header-time .mtx-calendar-header-ampm{width:40px;text-align:center}[mode=auto] .mtx-calendar .mtx-calendar-header-ampm-container{flex-direction:row;font-size:20px}[mode=auto] .mtx-calendar .mtx-calendar-header-ampm{padding:4px}[mode=auto] .mtx-calendar .mtx-calendar-header-ampm+.mtx-calendar-header-ampm{margin:0 8px}[mode=auto] .mtx-datetimepicker-content-container-with-actions .mtx-calendar .mtx-calendar-header{border-bottom-left-radius:0;border-bottom-right-radius:0}[mode=auto] .mtx-datetimepicker-actions:before{position:absolute;top:0;left:0;box-sizing:border-box;width:144px;height:100%;content:\"\";border-right:1px solid var(--mtx-datetimepicker-calendar-header-divider-color, var(--mat-app-outline-variant));background-color:var(--mtx-datetimepicker-calendar-header-background-color);border-bottom-left-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large))}[dir=rtl] [mode=auto] .mtx-datetimepicker-actions:before{left:auto;right:0;border-right-width:0;border-left:1px solid var(--mtx-datetimepicker-calendar-header-divider-color, var(--mat-app-outline-variant));border-bottom-left-radius:0;border-bottom-right-radius:var(--mtx-datetimepicker-container-shape, var(--mat-app-corner-large))}}.mtx-calendar-content{width:100%;padding:8px;outline:none;box-sizing:border-box;overflow:hidden}.mtx-calendar-controls{display:flex;align-items:center;justify-content:space-between;margin:0 calc(4.7142857143% - 16px)}.mtx-calendar-controls .mat-icon-button:hover .mat-button-focus-overlay{opacity:.04}.mtx-calendar-period-button{display:inline-block;height:40px;line-height:40px;outline:none;border:0;background:transparent;box-sizing:border-box;font-size:var(--mtx-datetimepicker-calendar-period-button-text-size, var(--mat-app-title-small-size));font-weight:var(--mtx-datetimepicker-calendar-period-button-text-weight, var(--mat-app-title-small-weight))}.mtx-calendar-previous-button.disabled,.mtx-calendar-next-button.disabled{pointer-events:none;color:var(--mtx-datetimepicker-calendar-date-disabled-state-text-color)}.mtx-calendar-previous-button svg,.mtx-calendar-next-button svg{fill:currentColor;vertical-align:top}[dir=rtl] .mtx-calendar-previous-button svg,[dir=rtl] .mtx-calendar-next-button svg{transform:rotate(180deg)}.mtx-calendar-table{border-spacing:0;border-collapse:collapse;width:100%}.mtx-calendar-table-header th{text-align:center;padding:8px 0;color:var(--mtx-datetimepicker-calendar-table-header-text-color, var(--mat-app-on-surface));font-size:var(--mtx-datetimepicker-calendar-table-header-text-size);font-weight:var(--mtx-datetimepicker-calendar-table-header-text-weight)}\n"], dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "component", type: MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: MtxMonthView, selector: "mtx-month-view", inputs: ["type", "dateFilter", "activeDate", "selected"], outputs: ["selectedChange", "_userSelection"], exportAs: ["mtxMonthView"] }, { kind: "component", type: MtxYearView, selector: "mtx-year-view", inputs: ["type", "dateFilter", "activeDate", "selected"], outputs: ["selectedChange", "_userSelection"], exportAs: ["mtxYearView"] }, { kind: "component", type: MtxMultiYearView, selector: "mtx-multi-year-view", inputs: ["type", "dateFilter", "activeDate", "selected", "minDate", "maxDate"], outputs: ["selectedChange", "_userSelection"], exportAs: ["mtxMultiYearView"] }, { kind: "component", type: MtxTime, selector: "mtx-time", inputs: ["dateFilter", "interval", "actionsPortal", "twelvehour", "AMPM", "activeDate", "selected", "minDate", "maxDate", "clockView"], outputs: ["selectedChange", "activeDateChange", "_userSelection", "ampmChange", "clockViewChange"], exportAs: ["mtxTime"] }, { kind: "component", type: MtxClock, selector: "mtx-clock", inputs: ["dateFilter", "interval", "twelvehour", "AMPM", "activeDate", "selected", "minDate", "maxDate", "startView"], outputs: ["selectedChange", "activeDateChange", "_userSelection"], exportAs: ["mtxClock"] }], animations: [mtxDatetimepickerAnimations.slideCalendar], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.0", ngImport: i0, type: MtxCalendar, decorators: [{ type: Component, args: [{ selector: 'mtx-calendar', host: { 'class': 'mtx-calendar', '[class.mtx-calendar-with-time-input]': 'timeInput', 'tabindex': '0', '(keydown)': '_handleCalendarBodyKeydown($event)', }, exportAs: 'mtxCalendar', animations: [mtxDatetimepickerAnimations.slideCalendar], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ CdkPortalOutlet, MatButton, MatIconButton, MtxMonthView, MtxYearView, MtxMultiYearView, MtxTime, MtxClock, ], template: "<div class=\"mtx-calendar-header\">\n @if (_calendarHeaderPortal) {\n <ng-template [cdkPortalOutlet]=\"_calendarHeaderPortal\"></ng-template>\n } @else {\n @if (type !== 'time') {\n <button\n mat-button type=\"button\" class=\"mtx-calendar-header-year\"\n [class.active]=\"currentView === 'year' || currentView === 'multi-year'\"\n [attr.aria-label]=\"_yearButtonLabel\"\n (click)=\"_yearClicked()\">\n <span>{{ _yearButtonText }}</span>\n @if (multiYearSelector || type === 'year') {\n <svg\n class=\"mtx-calendar-header-year-dropdown\" matButtonIcon iconPositionEnd\n width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M7,10L12,15L17,10H7Z\" />\n </svg>\n }\n </button>\n }\n @if (type !== 'year') {\n <div class=\"mtx-calendar-header-date-time\">\n @if (type !== 'time') {\n <button\n mat-button type=\"button\" class=\"mtx-calendar-header-date\"\n [class.active]=\"currentView === 'month'\"\n [class.not-clickable]=\"type === 'month'\"\n [attr.aria-label]=\"_dateButtonLabel\"\n (click)=\"_dateClicked()\">{{ _dateButtonText }}</button>\n }\n @if (type.endsWith('time')) {\n <span class=\"mtx-calendar-header-time\" [class.active]=\"currentView === 'clock'\">\n <span class=\"mtx-calendar-header-hour-minute-container\">\n <button mat-button type=\"button\" class=\"mtx-calendar-header-hours\"\n [class.active]=\"_clockView === 'hour'\"\n [attr.aria-label]=\"_hourButtonLabel\"\n (click)=\"_hoursClicked()\">{{ _hoursButtonText }}</button>\n <span class=\"mtx-calendar-header-hour-minute-separator\">:</span>\n <button mat-button type=\"button\" class=\"mtx-calendar-header-minutes\"\n [class.active]=\"_clockView === 'minute'\"\n [attr.aria-label]=\"_minuteButtonLabel\"\n (click)=\"_minutesClicked()\">{{ _minutesButtonText }}</button>\n </span>\n @if (twelvehour) {\n <span class=\"mtx-calendar-header-ampm-container\">\n <button mat-button type=\"button\" class=\"mtx-calendar-header-ampm\"\n [class.active]=\"_AMPM === 'AM'\" aria-label=\"AM\"\n (click)=\"_ampmClicked('AM')\">AM</button>\n <button mat-button type=\"button\" class=\"mtx-calendar-header-ampm\"\n [class.active]=\"_AMPM === 'PM'\" aria-label=\"PM\"\n (click)=\"_ampmClicked('PM')\">PM</button>\n </span>\n }\n </span>\n }\n </div>\n }\n }\n</div>\n\n<div class=\"mtx-calendar-content\">\n @if (currentView === 'month' || currentView === 'year' || currentView === 'multi-year') {\n <div class=\"mtx-month-content\">\n <div class=\"mtx-calendar-controls\">\n <button mat-icon-button type=\"button\"\n class=\"mtx-calendar-previous-button\"\n [class.disabled]=\"!_previousEnabled()\"\n [attr.aria-disabled]=\"!_previousEnabled()\"\n [attr.aria-label]=\"_prevButtonLabel\"\n (click)=\"_previousClicked()\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\n <path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\" />\n </svg>\n </button>\n <div class=\"mtx-calendar-period-button\"\n [@slideCalendar]=\"_calendarState\"\n (@slideCalendar.done)=\"_calendarStateDone()\">\n <strong>{{ _yearPeriodText }}</strong>\n </div>\n <button mat-icon-button type=\"button\"\n class=\"mtx-calendar-next-button\"\n [class.disabled]=\"!_nextEnabled()\"\n [attr.aria-disabled]=\"!_nextEnabled()\"\n [attr.aria-label]=\"_nextButtonLabel\"\n (click)=\"_nextClicked()\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\n <path d=\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\" />\n </svg>\n </button>\n </div>\n </div>\n }\n\n @switch (currentView) {\n @case ('month') {\n <mtx-month-view\n (_userSelection)=\"_userSelected()\"\n (selectedChange)=\"_dateSelected($event)\"\n [activeDate]=\"_activeDate\"\n [dateFilter]=\"_dateFilterForViews\"\n [selected]=\"selected!\"\n [type]=\"type\">\n </mtx-month-view>\n }\n @case ('