UNPKG

@matheo/datepicker

Version:

Angular material date+time picker

508 lines 107 kB
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { ComponentPortal } from '@angular/cdk/portal'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, forwardRef, HostBinding, Inject, Input, Optional, Output, ViewChild, ViewEncapsulation, isDevMode, } from '@angular/core'; import { MAT_DATE_FORMATS } from '@angular/material/core'; import { Subject } from 'rxjs'; import { DateAdapter, } from '@matheo/datepicker/core'; import { matDatepickerAnimations } from './datepicker-animations'; import { createMissingDateImplError } from './datepicker-errors'; import { MatDatepickerIntl } from './datepicker-intl'; import { MatClockView } from './clock-view'; import { MatMonthView } from './month-view'; import { getActiveOffset, isSameMultiYearView, MatMultiYearView, } from './multi-year-view'; import { MatYearView } from './year-view'; import { MAT_SINGLE_DATE_SELECTION_MODEL_PROVIDER, DateRange } from './date-selection-model'; import * as i0 from "@angular/core"; import * as i1 from "./datepicker-intl"; import * as i2 from "@matheo/datepicker/core"; import * as i3 from "@angular/material/button"; import * as i4 from "@angular/common"; import * as i5 from "./clock-view"; import * as i6 from "./month-view"; import * as i7 from "./year-view"; import * as i8 from "./multi-year-view"; import * as i9 from "@angular/cdk/portal"; import * as i10 from "@angular/cdk/a11y"; /** Counter used to generate unique IDs. */ let uniqueId = 0; /** Default header for MatCalendar */ export class MatCalendarHeader { constructor(_intl, calendar, _dateAdapter, _dateFormats, changeDetectorRef) { this._intl = _intl; this.calendar = calendar; this._dateAdapter = _dateAdapter; this._dateFormats = _dateFormats; this.changeDetectorRef = changeDetectorRef; this._buttonDescriptionId = `mat-calendar-button-${uniqueId++}`; this.updateValues(); this.calendar.stateChanges.subscribe(() => this.updateValues()); } get getCssClasses() { const cssClasses = [`type-${this.calendar.type}`]; return cssClasses.join(' '); } updateValues() { const activeDate = this.calendar.getDate(); const day = this._dateAdapter.getDayOfWeek(activeDate); let hours = this._dateAdapter.getHours(activeDate); this._isAM = hours < 12; if (this.calendar.twelveHour) { hours = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours; } const minutes = this._dateAdapter.getMinutes(activeDate); this._yearButtonText = this._dateAdapter.getYear(activeDate).toString(); this._monthButtonText = this._dateAdapter.format(activeDate, this._dateFormats.display.monthLabel); this._monthdayButtonText = this._dateAdapter.format(activeDate, this._dateFormats.display.monthDayLabel); this._dayButtonText = this._dateAdapter.getDayOfWeekNames('short')[day]; this._hourButtonText = hours.toString(); this._minuteButtonText = ('00' + minutes).slice(-2); this.changeDetectorRef.markForCheck(); } hasPrevNextBlock() { return !['hour', 'minute'].includes(this.calendar.currentView); } isControlActive(views) { return views.includes(this.calendar.currentView); } switchToView(view) { this.calendar.currentView = view; } toggleAmPm(am) { if (this._isAM !== am) { this.calendar.setDate(this._dateAdapter.addCalendarHours(this.calendar.getDate(), this._isAM ? 12 : -12)); } } /** The label for the current calendar view. */ get periodButtonText() { if (this.calendar.currentView == 'month') { return this._dateAdapter .format(this.calendar.activeDate, this._dateFormats.display.monthYearLabel) .toLocaleUpperCase(); } if (this.calendar.currentView == 'year') { return this._dateAdapter.getYearName(this.calendar.activeDate); } // 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._dateAdapter.getYear(this.calendar.activeDate); const minYearOfPage = activeYear - getActiveOffset(this._dateAdapter, this.calendar.activeDate, this.calendar.minDate, this.calendar.maxDate, this.calendar.yearsPerPage); const maxYearOfPage = minYearOfPage + this.calendar.yearsPerPage - 1; const minYearName = this._dateAdapter.getYearName(this._dateAdapter.createDate(minYearOfPage, 0, 1)); const maxYearName = this._dateAdapter.getYearName(this._dateAdapter.createDate(maxYearOfPage, 0, 1)); return this._intl.formatYearRange(minYearName, maxYearName); } get monthdayButtonLabel() { return this.calendar.currentView == 'month' ? this._intl.switchToYearViewLabel : this._intl.switchToMonthViewLabel; } get periodButtonLabel() { return this.calendar.currentView == 'month' ? this._intl.switchToMultiYearViewLabel : this._intl.switchToMonthViewLabel; } /** The label for the previous button. */ get prevButtonLabel() { return { 'month': this._intl.prevMonthLabel, 'year': this._intl.prevYearLabel, 'multi-year': this._intl.prevMultiYearLabel, }[this.calendar.currentView]; } /** The label for the next button. */ get nextButtonLabel() { return { 'month': this._intl.nextMonthLabel, 'year': this._intl.nextYearLabel, 'multi-year': this._intl.nextMultiYearLabel, }[this.calendar.currentView]; } monthdayClicked() { this.calendar.currentView = this.calendar.currentView == 'month' ? 'year' : 'month'; } currentPeriodDisabled() { return ['year', 'month'].includes(this.calendar.type); } /** Handles user clicks on the period label. */ currentPeriodClicked() { this.calendar.currentView = this.calendar.currentView == 'month' ? 'multi-year' : 'month'; } /** Handles user clicks on the previous button. */ previousClicked() { const date = this.calendar.currentView == 'month' ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate, -1) : this._dateAdapter.addCalendarYears(this.calendar.activeDate, this.calendar.currentView == 'year' ? -1 : -this.calendar.yearsPerPage); this.calendar.setDate(date); } /** Handles user clicks on the next button. */ nextClicked() { const date = this.calendar.currentView == 'month' ? this._dateAdapter.addCalendarMonths(this.calendar.activeDate, 1) : this._dateAdapter.addCalendarYears(this.calendar.activeDate, this.calendar.currentView == 'year' ? 1 : this.calendar.yearsPerPage); this.calendar.setDate(date); } /** Whether the previous period button is enabled. */ previousEnabled() { if (!this.calendar.minDate) { return true; } return (!this.calendar.minDate || !this._isSameView(this.calendar.activeDate, this.calendar.minDate)); } /** Whether the next period button is enabled. */ nextEnabled() { return (!this.calendar.maxDate || !this._isSameView(this.calendar.activeDate, this.calendar.maxDate)); } /** Whether the two dates represent the same view in the current view mode (month or year). */ _isSameView(date1, date2) { if (this.calendar.currentView == 'month') { return (this._dateAdapter.getYear(date1) == this._dateAdapter.getYear(date2) && this._dateAdapter.getMonth(date1) == this._dateAdapter.getMonth(date2)); } if (this.calendar.currentView == 'year') { return this._dateAdapter.getYear(date1) == this._dateAdapter.getYear(date2); } // Otherwise we are in 'multi-year' view. return isSameMultiYearView(this._dateAdapter, date1, date2, this.calendar.minDate, this.calendar.maxDate, this.calendar.yearsPerPage); } } /** @nocollapse */ /** @nocollapse */ MatCalendarHeader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: MatCalendarHeader, deps: [{ token: i1.MatDatepickerIntl }, { token: forwardRef(() => MatCalendar) }, { token: i2.DateAdapter, optional: true }, { token: MAT_DATE_FORMATS, optional: true }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ /** @nocollapse */ MatCalendarHeader.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.0.3", type: MatCalendarHeader, selector: "mat-custom-header", host: { properties: { "class": "this.getCssClasses" } }, exportAs: ["matCalendarHeader"], ngImport: i0, template: "<div class=\"mat-custom-header\">\n <div class=\"mat-custom-controls\">\n\n <div class=\"mat-custom-date\" *ngIf=\"calendar.type === 'year'\">\n <button mat-button type=\"button\" class=\"mat-custom-date-year mat-custom-control\"\n (click)=\"switchToView('multi-year')\"\n [@controlActive]=\"isControlActive(['multi-year']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['multi-year'])\"\n [attr.aria-label]=\"_intl.switchToMultiYearViewLabel\">\n <span>{{ _yearButtonText }}</span>\n </button>\n </div>\n\n <div class=\"mat-custom-date\" *ngIf=\"calendar.type === 'month'\">\n <button mat-button type=\"button\" class=\"mat-custom-date-month mat-custom-control\"\n (click)=\"switchToView('year')\"\n [@controlActive]=\"isControlActive(['year']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['year'])\"\n [attr.aria-label]=\"_intl.switchToYearViewLabel\">\n <span>{{ _monthButtonText }}</span>\n </button>\n\n <button mat-button type=\"button\" class=\"mat-custom-date-year mat-custom-control\"\n (click)=\"switchToView('multi-year')\"\n [@controlActive]=\"isControlActive(['multi-year']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['multi-year'])\"\n [attr.aria-label]=\"_intl.switchToMultiYearViewLabel\">\n <span>{{ _yearButtonText }}</span>\n </button>\n </div>\n\n <div class=\"mat-custom-date\" *ngIf=\"calendar.hasOutput('date')\">\n <button mat-button type=\"button\" class=\"mat-custom-date-year mat-custom-control\"\n (click)=\"switchToView('multi-year')\"\n [@controlActive]=\"isControlActive(['multi-year']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['multi-year'])\"\n [attr.aria-label]=\"_intl.switchToMultiYearViewLabel\">\n <span>{{ _yearButtonText }}</span>\n </button>\n\n <button mat-button type=\"button\" class=\"mat-custom-date-monthday mat-custom-control\"\n (click)=\"monthdayClicked()\"\n [@controlActive]=\"isControlActive(['month', 'year']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['month', 'year'])\"\n [attr.aria-label]=\"monthdayButtonLabel\">\n <span class=\"mat-custom-date-year-dayname\">{{ _dayButtonText }}&nbsp;</span>\n <span class=\"mat-custom-date-year-monthday\">{{ _monthdayButtonText }}</span>\n </button>\n </div>\n\n <div class=\"mat-custom-time\" *ngIf=\"calendar.hasOutput('time')\">\n <div class=\"mat-custom-time-hour\">\n <button mat-button type=\"button\" class=\"mat-custom-time-hour mat-custom-control\"\n (click)=\"switchToView('hour')\"\n [@controlActive]=\"isControlActive(['hour']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['hour'])\"\n [attr.aria-label]=\"_intl.switchToHourViewLabel\">\n <span>{{ _hourButtonText }}</span>\n </button>\n\n <span class=\"mat-custom-separator\">:</span>\n\n <button mat-button type=\"button\" class=\"mat-custom-time-minute mat-custom-control\"\n [@controlActive]=\"isControlActive(['minute']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['minute'])\"\n (click)=\"switchToView('minute')\"\n [attr.aria-label]=\"_intl.switchToMinuteViewLabel\">\n <span>{{ _minuteButtonText }}</span>\n </button>\n\n </div>\n <div class=\"mat-custom-time-ampm\" *ngIf=\"calendar.twelveHour\">\n <button mat-button type=\"button\" class=\"mat-calendar-control\"\n [@controlActive]=\"_isAM ? 'active' : ''\"\n [class.mat-custom-control-active]=\"_isAM\"\n [attr.aria-label]=\"_intl.setToAMLabel\"\n (click)=\"toggleAmPm(true)\">\n AM\n </button>\n <button mat-button type=\"button\" class=\"mat-calendar-control\"\n [@controlActive]=\"!_isAM ? 'active' : ''\"\n [class.mat-custom-control-active]=\"!_isAM\"\n [attr.aria-label]=\"_intl.setToPMLabel\"\n (click)=\"toggleAmPm(false)\">\n PM\n </button>\n </div>\n </div>\n\n </div>\n <div class=\"mat-custom-prev-next\" *ngIf=\"hasPrevNextBlock()\">\n\n <button mat-icon-button type=\"button\"\n class=\"mat-calendar-previous-button\"\n [disabled]=\"!previousEnabled()\"\n (click)=\"previousClicked()\"\n [attr.aria-label]=\"prevButtonLabel\">\n </button>\n\n <button mat-button type=\"button\" class=\"mat-custom-period mat-custom-control\"\n disableRipple=\"true\"\n (click)=\"currentPeriodClicked()\"\n [disabled]=\"currentPeriodDisabled()\"\n [attr.aria-label]=\"periodButtonLabel\">\n <strong>{{ periodButtonText }}</strong>\n </button>\n\n <button mat-icon-button type=\"button\"\n class=\"mat-calendar-next-button\"\n [disabled]=\"!nextEnabled()\"\n (click)=\"nextClicked()\"\n [attr.aria-label]=\"nextButtonLabel\">\n </button>\n\n </div>\n</div>\n", styles: ["mat-custom-header *{box-sizing:border-box}mat-custom-header .mat-custom-control,mat-custom-header .mat-custom-separator{opacity:.75}mat-custom-header .mat-custom-control-active{opacity:1}mat-custom-header .mat-custom-controls{display:flex;flex-direction:row;padding:5% 4%}mat-custom-header .mat-custom-controls .mat-button{font-size:inherit;font-weight:inherit;line-height:inherit;padding:0;min-width:auto}mat-custom-header .mat-custom-controls .mat-custom-date{display:flex;flex-direction:column;flex:1 1 auto;place-content:flex-start;align-items:flex-start}mat-custom-header .mat-custom-controls .mat-custom-date-year{font-size:16px;line-height:20px}mat-custom-header .mat-custom-controls .mat-custom-date-year-dayname{display:none}mat-custom-header .mat-custom-controls .mat-custom-date-monthday{font-size:42px;line-height:36px}mat-custom-header .mat-custom-controls .mat-custom-time{display:flex;flex-direction:row;flex:1 1 auto;place-content:flex-end;align-items:flex-end}mat-custom-header .mat-custom-controls .mat-custom-time-hour{display:flex;flex-direction:row;place-content:center flex-end;align-items:center;font-size:42px;line-height:36px}mat-custom-header .mat-custom-controls .mat-custom-time-ampm{display:flex;flex-direction:column;font-size:16px;line-height:18px;min-width:30px}mat-custom-header .mat-custom-controls .mat-custom-time-ampm .mat-custom-control-active{font-weight:500;opacity:1}mat-custom-header .mat-custom-prev-next{display:flex;flex-direction:row;place-content:stretch space-between;align-items:stretch;padding:1% 0}mat-custom-header .mat-custom-prev-next .mat-button .mat-button-focus-overlay{display:none}mat-custom-header.type-year .mat-custom-controls .mat-custom-date{place-content:center;align-items:center}mat-custom-header.type-year .mat-custom-controls .mat-custom-date-year{font-size:42px;line-height:36px}mat-custom-header.type-month .mat-custom-controls .mat-custom-date{flex-direction:row;place-content:center;align-items:center}mat-custom-header.type-month .mat-custom-controls .mat-custom-date-month,mat-custom-header.type-month .mat-custom-controls .mat-custom-date-year{font-size:42px;line-height:36px}mat-custom-header.type-month .mat-custom-controls .mat-custom-date-month:not(:last-child),mat-custom-header.type-month .mat-custom-controls .mat-custom-date-year:not(:last-child){margin-right:.2em}mat-custom-header.type-date .mat-custom-controls .mat-custom-date{flex-direction:row-reverse;place-content:center;align-items:center}mat-custom-header.type-date .mat-custom-controls .mat-custom-date-monthday:after{content:\", \";display:inline-block}mat-custom-header.type-date .mat-custom-controls .mat-custom-date-year{font-size:42px;line-height:36px;margin-left:.2em}mat-custom-header.type-datetime .mat-custom-controls .mat-custom-date-year{font-weight:500}mat-custom-header.type-time .mat-custom-controls .mat-custom-time{place-content:center;align-items:center}mat-custom-header.type-time .mat-custom-controls .mat-custom-time-hour{font-size:56px;line-height:56px}mat-custom-header.type-time .mat-custom-controls .mat-custom-time-hour,mat-custom-header.type-time .mat-custom-controls .mat-custom-time-minute{min-width:62px}mat-custom-header.type-time .mat-custom-controls .mat-custom-time-ampm{font-size:22px;line-height:24px;margin-top:4px;min-width:45px}\n"], components: [{ 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"] }], directives: [{ type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], animations: [matDatepickerAnimations.controlActive], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: MatCalendarHeader, decorators: [{ type: Component, args: [{ selector: 'mat-custom-header', exportAs: 'matCalendarHeader', animations: [matDatepickerAnimations.controlActive], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mat-custom-header\">\n <div class=\"mat-custom-controls\">\n\n <div class=\"mat-custom-date\" *ngIf=\"calendar.type === 'year'\">\n <button mat-button type=\"button\" class=\"mat-custom-date-year mat-custom-control\"\n (click)=\"switchToView('multi-year')\"\n [@controlActive]=\"isControlActive(['multi-year']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['multi-year'])\"\n [attr.aria-label]=\"_intl.switchToMultiYearViewLabel\">\n <span>{{ _yearButtonText }}</span>\n </button>\n </div>\n\n <div class=\"mat-custom-date\" *ngIf=\"calendar.type === 'month'\">\n <button mat-button type=\"button\" class=\"mat-custom-date-month mat-custom-control\"\n (click)=\"switchToView('year')\"\n [@controlActive]=\"isControlActive(['year']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['year'])\"\n [attr.aria-label]=\"_intl.switchToYearViewLabel\">\n <span>{{ _monthButtonText }}</span>\n </button>\n\n <button mat-button type=\"button\" class=\"mat-custom-date-year mat-custom-control\"\n (click)=\"switchToView('multi-year')\"\n [@controlActive]=\"isControlActive(['multi-year']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['multi-year'])\"\n [attr.aria-label]=\"_intl.switchToMultiYearViewLabel\">\n <span>{{ _yearButtonText }}</span>\n </button>\n </div>\n\n <div class=\"mat-custom-date\" *ngIf=\"calendar.hasOutput('date')\">\n <button mat-button type=\"button\" class=\"mat-custom-date-year mat-custom-control\"\n (click)=\"switchToView('multi-year')\"\n [@controlActive]=\"isControlActive(['multi-year']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['multi-year'])\"\n [attr.aria-label]=\"_intl.switchToMultiYearViewLabel\">\n <span>{{ _yearButtonText }}</span>\n </button>\n\n <button mat-button type=\"button\" class=\"mat-custom-date-monthday mat-custom-control\"\n (click)=\"monthdayClicked()\"\n [@controlActive]=\"isControlActive(['month', 'year']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['month', 'year'])\"\n [attr.aria-label]=\"monthdayButtonLabel\">\n <span class=\"mat-custom-date-year-dayname\">{{ _dayButtonText }}&nbsp;</span>\n <span class=\"mat-custom-date-year-monthday\">{{ _monthdayButtonText }}</span>\n </button>\n </div>\n\n <div class=\"mat-custom-time\" *ngIf=\"calendar.hasOutput('time')\">\n <div class=\"mat-custom-time-hour\">\n <button mat-button type=\"button\" class=\"mat-custom-time-hour mat-custom-control\"\n (click)=\"switchToView('hour')\"\n [@controlActive]=\"isControlActive(['hour']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['hour'])\"\n [attr.aria-label]=\"_intl.switchToHourViewLabel\">\n <span>{{ _hourButtonText }}</span>\n </button>\n\n <span class=\"mat-custom-separator\">:</span>\n\n <button mat-button type=\"button\" class=\"mat-custom-time-minute mat-custom-control\"\n [@controlActive]=\"isControlActive(['minute']) ? 'active' : ''\"\n [class.mat-custom-control-active]=\"isControlActive(['minute'])\"\n (click)=\"switchToView('minute')\"\n [attr.aria-label]=\"_intl.switchToMinuteViewLabel\">\n <span>{{ _minuteButtonText }}</span>\n </button>\n\n </div>\n <div class=\"mat-custom-time-ampm\" *ngIf=\"calendar.twelveHour\">\n <button mat-button type=\"button\" class=\"mat-calendar-control\"\n [@controlActive]=\"_isAM ? 'active' : ''\"\n [class.mat-custom-control-active]=\"_isAM\"\n [attr.aria-label]=\"_intl.setToAMLabel\"\n (click)=\"toggleAmPm(true)\">\n AM\n </button>\n <button mat-button type=\"button\" class=\"mat-calendar-control\"\n [@controlActive]=\"!_isAM ? 'active' : ''\"\n [class.mat-custom-control-active]=\"!_isAM\"\n [attr.aria-label]=\"_intl.setToPMLabel\"\n (click)=\"toggleAmPm(false)\">\n PM\n </button>\n </div>\n </div>\n\n </div>\n <div class=\"mat-custom-prev-next\" *ngIf=\"hasPrevNextBlock()\">\n\n <button mat-icon-button type=\"button\"\n class=\"mat-calendar-previous-button\"\n [disabled]=\"!previousEnabled()\"\n (click)=\"previousClicked()\"\n [attr.aria-label]=\"prevButtonLabel\">\n </button>\n\n <button mat-button type=\"button\" class=\"mat-custom-period mat-custom-control\"\n disableRipple=\"true\"\n (click)=\"currentPeriodClicked()\"\n [disabled]=\"currentPeriodDisabled()\"\n [attr.aria-label]=\"periodButtonLabel\">\n <strong>{{ periodButtonText }}</strong>\n </button>\n\n <button mat-icon-button type=\"button\"\n class=\"mat-calendar-next-button\"\n [disabled]=\"!nextEnabled()\"\n (click)=\"nextClicked()\"\n [attr.aria-label]=\"nextButtonLabel\">\n </button>\n\n </div>\n</div>\n", styles: ["mat-custom-header *{box-sizing:border-box}mat-custom-header .mat-custom-control,mat-custom-header .mat-custom-separator{opacity:.75}mat-custom-header .mat-custom-control-active{opacity:1}mat-custom-header .mat-custom-controls{display:flex;flex-direction:row;padding:5% 4%}mat-custom-header .mat-custom-controls .mat-button{font-size:inherit;font-weight:inherit;line-height:inherit;padding:0;min-width:auto}mat-custom-header .mat-custom-controls .mat-custom-date{display:flex;flex-direction:column;flex:1 1 auto;place-content:flex-start;align-items:flex-start}mat-custom-header .mat-custom-controls .mat-custom-date-year{font-size:16px;line-height:20px}mat-custom-header .mat-custom-controls .mat-custom-date-year-dayname{display:none}mat-custom-header .mat-custom-controls .mat-custom-date-monthday{font-size:42px;line-height:36px}mat-custom-header .mat-custom-controls .mat-custom-time{display:flex;flex-direction:row;flex:1 1 auto;place-content:flex-end;align-items:flex-end}mat-custom-header .mat-custom-controls .mat-custom-time-hour{display:flex;flex-direction:row;place-content:center flex-end;align-items:center;font-size:42px;line-height:36px}mat-custom-header .mat-custom-controls .mat-custom-time-ampm{display:flex;flex-direction:column;font-size:16px;line-height:18px;min-width:30px}mat-custom-header .mat-custom-controls .mat-custom-time-ampm .mat-custom-control-active{font-weight:500;opacity:1}mat-custom-header .mat-custom-prev-next{display:flex;flex-direction:row;place-content:stretch space-between;align-items:stretch;padding:1% 0}mat-custom-header .mat-custom-prev-next .mat-button .mat-button-focus-overlay{display:none}mat-custom-header.type-year .mat-custom-controls .mat-custom-date{place-content:center;align-items:center}mat-custom-header.type-year .mat-custom-controls .mat-custom-date-year{font-size:42px;line-height:36px}mat-custom-header.type-month .mat-custom-controls .mat-custom-date{flex-direction:row;place-content:center;align-items:center}mat-custom-header.type-month .mat-custom-controls .mat-custom-date-month,mat-custom-header.type-month .mat-custom-controls .mat-custom-date-year{font-size:42px;line-height:36px}mat-custom-header.type-month .mat-custom-controls .mat-custom-date-month:not(:last-child),mat-custom-header.type-month .mat-custom-controls .mat-custom-date-year:not(:last-child){margin-right:.2em}mat-custom-header.type-date .mat-custom-controls .mat-custom-date{flex-direction:row-reverse;place-content:center;align-items:center}mat-custom-header.type-date .mat-custom-controls .mat-custom-date-monthday:after{content:\", \";display:inline-block}mat-custom-header.type-date .mat-custom-controls .mat-custom-date-year{font-size:42px;line-height:36px;margin-left:.2em}mat-custom-header.type-datetime .mat-custom-controls .mat-custom-date-year{font-weight:500}mat-custom-header.type-time .mat-custom-controls .mat-custom-time{place-content:center;align-items:center}mat-custom-header.type-time .mat-custom-controls .mat-custom-time-hour{font-size:56px;line-height:56px}mat-custom-header.type-time .mat-custom-controls .mat-custom-time-hour,mat-custom-header.type-time .mat-custom-controls .mat-custom-time-minute{min-width:62px}mat-custom-header.type-time .mat-custom-controls .mat-custom-time-ampm{font-size:22px;line-height:24px;margin-top:4px;min-width:45px}\n"] }] }], ctorParameters: function () { return [{ type: i1.MatDatepickerIntl }, { type: MatCalendar, decorators: [{ type: Inject, args: [forwardRef(() => MatCalendar)] }] }, { type: i2.DateAdapter, decorators: [{ type: Optional }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [MAT_DATE_FORMATS] }] }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { getCssClasses: [{ type: HostBinding, args: ['class'] }] } }); /** A calendar that is used as part of the datepicker. */ export class MatCalendar { constructor(_intl, _dateAdapter, _dateFormats, _changeDetectorRef) { this._dateAdapter = _dateAdapter; this._dateFormats = _dateFormats; this._changeDetectorRef = _changeDetectorRef; /** * Used for scheduling that focus should be moved to the active cell on the next tick. * We need to schedule it, rather than do it immediately, because we have to wait * for Angular to re-evaluate the view children. */ this._moveFocusOnNextTick = false; /** The type of value handled by the calendar. */ this.type = 'date'; /** Whether the calendar should be started in. */ this.startView = 'month'; /** multi-year inputs */ this.yearsPerPage = 24; this.yearsPerRow = 4; /** Clock interval */ this.clockStep = 1; /** Clock hour format */ this.twelveHour = false; /** Emits when the currently selected date changes. */ this.selectedChange = new EventEmitter(); /** * Emits the year chosen in multiyear view. * This doesn't imply a change on the selected date. */ this.yearSelected = new EventEmitter(); /** * Emits the month chosen in year view. * This doesn't imply a change on the selected date. */ this.monthSelected = new EventEmitter(); /** * Emits when the date changes. */ this.dateChanged = new EventEmitter(); /** * Emits when the current view changes. */ this.viewChanged = new EventEmitter(true); /** Emits when any date is selected. */ this._userSelection = new EventEmitter(); /** * Emits whenever there is a state change that the header may need to respond to. */ this.stateChanges = new Subject(); if (isDevMode()) { if (!this._dateAdapter) { throw createMissingDateImplError('DateAdapter'); } if (!this._dateFormats) { throw createMissingDateImplError('MAT_DATE_FORMATS'); } } this._intlChanges = _intl.changes.subscribe(() => { _changeDetectorRef.markForCheck(); this.stateChanges.next(); }); } /** A date representing the period (month or year) to start the calendar in. */ get startAt() { return this._startAt; } set startAt(value) { this._startAt = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } /** The currently selected date. */ get selected() { return this._selected; } set selected(value) { if (value instanceof DateRange) { this._selected = value; } else { this._selected = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } } /** The minimum selectable date. */ get minDate() { return this._minDate; } set minDate(value) { this._minDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } /** The maximum selectable date. */ get maxDate() { return this._maxDate; } set maxDate(value) { this._maxDate = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(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) { this._clampedActiveDate = this._dateAdapter.clampDate(value, this.minDate, this.maxDate); this.stateChanges.next(); this._changeDetectorRef.markForCheck(); } /** Whether the calendar is in month view. */ get currentView() { return this._currentView; } set currentView(value) { const viewChangedResult = this._currentView !== value ? value : null; this._currentView = value; this._moveFocusOnNextTick = true; this._changeDetectorRef.markForCheck(); if (viewChangedResult) { this.viewChanged.emit(viewChangedResult); } } ngAfterContentInit() { this._calendarHeaderPortal = new ComponentPortal(this.headerComponent || MatCalendarHeader); this.activeDate = this.startAt || this._dateAdapter.today(); // Assign to the private property since we don't want to move focus on init. this._currentView = this.type === 'year' ? 'multi-year' : this.type === 'month' ? 'year' : this.type === 'time' && !['hour', 'minute'].includes(this.startView) ? 'hour' : this.startView; } ngAfterViewChecked() { if (this._moveFocusOnNextTick) { this._moveFocusOnNextTick = false; this.focusActiveCell(); } } ngOnDestroy() { this._intlChanges.unsubscribe(); this.stateChanges.complete(); } ngOnChanges(changes) { const change = changes['minDate'] || changes['maxDate'] || changes['dateFilter']; if (change && !change.firstChange) { const view = this._getCurrentViewComponent(); if (view) { // We need to `detectChanges` manually here, because the `minDate`, `maxDate` etc. are // passed down to the view via data bindings which won't be up-to-date when we call `_init`. this._changeDetectorRef.detectChanges(); view._init(); } } this.stateChanges.next(); } /** Focuses the active date. */ focusActiveCell() { this._getCurrentViewComponent()._focusActiveCell(false); } hasOutput(type) { return this.type.indexOf(type) !== -1; } getDate() { return !this.selected || this.selected instanceof DateRange ? this.activeDate : this.selected; } getUnit() { switch (this.type) { case 'date': return 'day'; case 'datetime': case 'time': return 'minute'; default: return this.type; } } setDate(date) { if (!(this.selected instanceof DateRange)) { this.selected = date; } this.activeDate = date; this.dateChanged.emit(date); } /** Updates today's date after an update of the active date */ updateTodaysDate() { this._getCurrentViewComponent()._init(); } /** Handles date selection in the month view. */ _dateSelected(event) { const date = event.value; if (this.selected instanceof DateRange || (date && !this._dateAdapter.sameDate(date, this.selected, this.getUnit()))) { this.selectedChange.emit(date); } this._userSelection.emit(event); } _dateEmit(value) { this.setDate(value); this._userSelection.emit({ value, event: null }); } /** Handles date selection in the clock view. */ _hourSelectedInClockView(date) { this.setDate(date); this.selectedChange.emit(date); } _timeSelectedInClockView(event) { this.setDate(event.value); this.selectedChange.emit(event.value); this._userSelection.emit(event); } /** Handles user day selection. */ _daySelected(event) { if (!this.hasOutput('time') || this.selected instanceof DateRange) { this.setDate(event.value); this._dateSelected(event); } else { this.selectedChange.emit(event.value); this._goToDateInView(event.value, 'hour'); } } /** Handles year selection in the multiyear view. */ _yearSelectedInMultiYearView(normalizedYear) { this.yearSelected.emit(normalizedYear); } /** Handles month selection in the year view. */ _monthSelectedInYearView(normalizedMonth) { this.monthSelected.emit(normalizedMonth); } /** Handles year/month selection in the multi-year/year views. */ _goToDateInView(date, view) { this.setDate(date); this.currentView = view; } /** Returns the component instance that corresponds to the current calendar view. */ _getCurrentViewComponent() { // The return type is explicitly written as a union to ensure that the Closure compiler does // not optimize calls to _init(). Without the explict return type, TypeScript narrows it to // only the first component type. See https://github.com/angular/components/issues/22996. return this.clockView || this.monthView || this.yearView || this.multiYearView; } } /** @nocollapse */ /** @nocollapse */ MatCalendar.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: MatCalendar, deps: [{ token: i1.MatDatepickerIntl }, { token: i2.DateAdapter, optional: true }, { token: MAT_DATE_FORMATS, optional: true }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ /** @nocollapse */ MatCalendar.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.0.3", type: MatCalendar, selector: "mat-calendar", inputs: { headerComponent: "headerComponent", startAt: "startAt", type: "type", startView: "startView", yearsPerPage: "yearsPerPage", yearsPerRow: "yearsPerRow", selected: "selected", minDate: "minDate", maxDate: "maxDate", dateFilter: "dateFilter", dateClass: "dateClass", clockStep: "clockStep", twelveHour: "twelveHour", comparisonStart: "comparisonStart", comparisonEnd: "comparisonEnd" }, outputs: { selectedChange: "selectedChange", yearSelected: "yearSelected", monthSelected: "monthSelected", dateChanged: "dateChanged", viewChanged: "viewChanged", _userSelection: "_userSelection" }, host: { properties: { "class": "this.type" }, classAttribute: "mat-calendar" }, providers: [MAT_SINGLE_DATE_SELECTION_MODEL_PROVIDER], viewQueries: [{ propertyName: "clockView", first: true, predicate: MatClockView, descendants: true }, { 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\n <mat-clock-view\n *ngSwitchDefault\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [(currentView)]=\"currentView\"\n [selected]=\"selected\"\n [dateFilter]=\"dateFilter\"\n [dateClass]=\"dateClass\"\n [clockStep]=\"clockStep\"\n [twelveHour]=\"twelveHour\"\n (selectedChange)=\"setDate($event)\"\n (hourSelected)=\"_hourSelectedInClockView($event)\"\n (_userSelection)=\"_timeSelectedInClockView($event)\">\n </mat-clock-view>\n\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)=\"_daySelected($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)=\"hasOutput('month') ? _dateEmit($event) : _goToDateInView($event, 'month')\">\n </mat-year-view>\n\n <mat-multi-year-view\n *ngSwitchCase=\"'multi-year'\"\n [yearsPerPage]=\"yearsPerPage\"\n [yearsPerRow]=\"yearsPerRow\"\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [dateFilter]=\"dateFilter\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [dateClass]=\"dateClass\"\n (yearSelected)=\"_yearSelectedInMultiYearView($event)\"\n (selectedChange)=\"hasOutput('year') ? _dateEmit($event) : _goToDateInView($event, 'year')\">\n </mat-multi-year-view>\n</div>\n", styles: [".mat-calendar{display:flex;flex-direction:column}.mat-calendar-header{padding:8px 8px 0}.mat-calendar-content{padding:0 8px 8px;outline:none}.mat-calendar-controls{display:flex;margin:5% calc(4.71429% - 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:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top-width:5px;border-top-style:solid;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:translate(2px) rotate(-45deg)}.mat-calendar-next-button:after{border-right-width:2px;transform:translate(-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}.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}.mat-calendar-abbr{text-decoration:none}\n"], components: [{ type: i5.MatClockView, selector: "mat-clock-view", inputs: ["activeDate", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "clockStep", "twelveHour", "currentView"], outputs: ["currentViewChange", "selectedChange", "_userSelection"], exportAs: ["matClockView"] }, { type: i6.MatMonthView, selector: "mat-month-view", inputs: ["activeDate", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "comparisonStart", "comparisonEnd"], outputs: ["selectedChange", "_userSelection", "activeDateChange"], exportAs: ["matMonthView"] }, { type: i7.MatYearView, selector: "mat-year-view", inputs: ["activeDate", "selected", "minDate", "maxDate", "dateFilter", "dateClass"], outputs: ["selectedChange", "monthSelected", "activeDateChange"], exportAs: ["matYearView"] }, { type: i8.MatMultiYearView, selector: "mat-multi-year-view", inputs: ["yearsPerPage", "yearsPerRow", "activeDate", "selected", "minDate", "maxDate", "dateFilter", "dateClass"], outputs: ["selectedChange", "yearSelected", "activeDateChange"], exportAs: ["matMultiYearView"] }], directives: [{ type: i9.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { type: i10.CdkMonitorFocus, selector: "[cdkMonitorElementFocus], [cdkMonitorSubtreeFocus]", outputs: ["cdkFocusChange"] }, { type: i4.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { type: i4.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { type: i4.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: MatCalendar, decorators: [{ type: Component, args: [{ selector: 'mat-calendar', host: { 'class': 'mat-calendar', }, 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\n <mat-clock-view\n *ngSwitchDefault\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [(currentView)]=\"currentView\"\n [selected]=\"selected\"\n [dateFilter]=\"dateFilter\"\n [dateClass]=\"dateClass\"\n [clockStep]=\"clockStep\"\n [twelveHour]=\"twelveHour\"\n (selectedChange)=\"setDate($event)\"\n (hourSelected)=\"_hourSelectedInClockView($event)\"\n (_userSelection)=\"_timeSelectedInClockView($event)\">\n </mat-clock-view>\n\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)=\"_daySelected($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)=\"hasOutput('month') ? _dateEmit($event) : _goToDateInView($event, 'month')\">\n </mat-year-view>\n\n <mat-multi-year-view\n *ngSwitchCase=\"'multi-year'\"\n [yearsPerPage]=\"yearsPerPage\"\n [yearsPerRow]=\"yearsPerRow\"\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [dateFilter]=\"dateFilter\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [dateClass]=\"dateClass\"\n (yearSelected)=\"_yearSelectedInMultiYearView($event)\"\n (selectedChange)=\"hasOutput('year') ? _dateEmit($event) : _goToDateInView($event, 'year')\">\n </mat-multi-year-view>\n</div>\n", styles: [".mat-calendar{display:flex;flex-direction:column}.mat-calendar-header{padding:8px 8px 0}.mat-calendar-content{padding:0 8px 8px;outline:none}.mat-calendar-controls{display:flex;margin:5% calc(4.71429% - 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:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top-width:5px;border-top-style:solid;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:translate(2px) rotate(-45deg)}.mat-calendar-next-button:after{border-right-width:2px;transform:translate(-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}.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}.mat-calendar-abbr{text-decoration:none}\n"] }] }], ctorParameters: function () { return [{ type: i1.MatDatepickerIntl }, { type: i2.DateAdapter, decorators: [{ type: Optional }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [MAT_DATE_FORMATS] }] }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { headerComponent: [{ type: Input }], startAt: [{ type: Input }], type: [{ type: HostBinding, args: ['class'] }, { type: Input }], startView: [{ type: Input }], yearsPerPage: [{ type: Input }], yearsPerRow: [{ type: Input }], selected: [{ type: Input }], minDate: [{ type: Input }], maxDate: [{ type: Input }], dateFilter: [{ type: Input }], dateClass: [{ type: Input }], clockStep: [{ type: Input }], twelveHour: [{ type: Input }], comparisonStart: [{ type: Input }], comparisonEnd: [{ type: Input }], selectedChange: [{ type: Output }], yearSelected: [{ type: Output }], monthSelected: [{ type: Output }], dateChanged: [{ type: Output }], viewChanged: [{ type: Output }], _userSelection: [{ type: Output }], clockView: [{ type: ViewChild, args: [MatClockView] }], monthView: [{ type: ViewChild, args: [MatMonthView] }], yearView: [{ type: ViewChild, args: [MatYearView] }], multiYearView: