diff --git a/src/material/datepicker/calendar-body.html b/src/material/datepicker/calendar-body.html index 25db33ecbee5..1b0f5f0b1a4a 100644 --- a/src/material/datepicker/calendar-body.html +++ b/src/material/datepicker/calendar-body.html @@ -63,8 +63,7 @@ [attr.aria-disabled]="!item.enabled || null" [attr.aria-pressed]="_isSelected(item.compareValue)" [attr.aria-current]="todayValue === item.compareValue ? 'date' : null" - (click)="_cellClicked(item, $event)" - (focus)="_emitActiveDateChange(item, $event)"> + (click)="_cellClicked(item, $event)">
{ encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MatCalendarBody implements OnChanges, OnDestroy, AfterViewChecked { +export class MatCalendarBody implements OnChanges, OnDestroy { /** * Used to skip the next focus event when rendering the preview range. * We need a flag like this, because some browsers fire focus events asynchronously. */ private _skipNextFocus: boolean; - /** - * Used to focus the active cell after change detection has run. - */ - private _focusActiveCellAfterViewChecked = false; - /** The label for the table. (e.g. "Jan 2017"). */ @Input() label: string; @@ -104,13 +98,6 @@ export class MatCalendarBody implements OnChanges, OnDestroy, AfterViewChecked { /** The cell number of the active cell in the table. */ @Input() activeCell: number = 0; - ngAfterViewChecked() { - if (this._focusActiveCellAfterViewChecked) { - this._focusActiveCell(); - this._focusActiveCellAfterViewChecked = false; - } - } - /** Whether a range is being selected. */ @Input() isRange: boolean = false; @@ -140,8 +127,6 @@ export class MatCalendarBody implements OnChanges, OnDestroy, AfterViewChecked { MatCalendarUserEvent >(); - @Output() readonly activeDateChange = new EventEmitter>(); - /** The number of blank cells to put at the beginning for the first row. */ _firstRowOffset: number; @@ -168,12 +153,6 @@ export class MatCalendarBody implements OnChanges, OnDestroy, AfterViewChecked { } } - _emitActiveDateChange(cell: MatCalendarCell, event: FocusEvent): void { - if (cell.enabled) { - this.activeDateChange.emit({value: cell.value, event}); - } - } - /** Returns whether a cell should be marked as selected. */ _isSelected(value: number) { return this.startValue === value || this.endValue === value; @@ -235,11 +214,6 @@ export class MatCalendarBody implements OnChanges, OnDestroy, AfterViewChecked { }); } - /** Focuses the active cell after change detection has run and the microtask queue is empty. */ - _scheduleFocusActiveCellAfterViewChecked() { - this._focusActiveCellAfterViewChecked = true; - } - /** Gets whether a value is the start of the main range. */ _isRangeStart(value: number) { return isStart(value, this.startValue, this.endValue); diff --git a/src/material/datepicker/month-view.html b/src/material/datepicker/month-view.html index 3ccafd6d2e94..736d7eff7cd1 100644 --- a/src/material/datepicker/month-view.html +++ b/src/material/datepicker/month-view.html @@ -21,7 +21,6 @@ [labelMinRequiredCells]="3" [activeCell]="_dateAdapter.getDate(activeDate) - 1" (selectedValueChange)="_dateSelected($event)" - (activeDateChange)="_updateActiveDate($event)" (previewChange)="_previewChanged($event)" (keyup)="_handleCalendarBodyKeyup($event)" (keydown)="_handleCalendarBodyKeydown($event)"> diff --git a/src/material/datepicker/month-view.spec.ts b/src/material/datepicker/month-view.spec.ts index 27bada546455..7a4909f8efbb 100644 --- a/src/material/datepicker/month-view.spec.ts +++ b/src/material/datepicker/month-view.spec.ts @@ -520,30 +520,6 @@ describe('MatMonthView', () => { ); }, ); - - it('should go to month that is focused', () => { - const jan11Cell = fixture.debugElement.nativeElement.querySelector( - '[data-mat-row="1"][data-mat-col="3"] button', - ) as HTMLElement; - - dispatchFakeEvent(jan11Cell, 'focus'); - fixture.detectChanges(); - - expect(calendarInstance.date).toEqual(new Date(2017, JAN, 11)); - }); - - it('should not call `.focus()` when the active date is focused', () => { - const jan5Cell = fixture.debugElement.nativeElement.querySelector( - '[data-mat-row="0"][data-mat-col="4"] button', - ) as HTMLElement; - const focusSpy = (jan5Cell.focus = jasmine.createSpy('cellFocused')); - - dispatchFakeEvent(jan5Cell, 'focus'); - fixture.detectChanges(); - - expect(calendarInstance.date).toEqual(new Date(2017, JAN, 5)); - expect(focusSpy).not.toHaveBeenCalled(); - }); }); }); }); diff --git a/src/material/datepicker/month-view.ts b/src/material/datepicker/month-view.ts index 03fdfa9dc0eb..a46abfb4dfbd 100644 --- a/src/material/datepicker/month-view.ts +++ b/src/material/datepicker/month-view.ts @@ -230,7 +230,9 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { /** Handles when a new date is selected. */ _dateSelected(event: MatCalendarUserEvent) { const date = event.value; - const selectedDate = this._getDateFromDayOfMonth(date); + const selectedYear = this._dateAdapter.getYear(this.activeDate); + const selectedMonth = this._dateAdapter.getMonth(this.activeDate); + const selectedDate = this._dateAdapter.createDate(selectedYear, selectedMonth, date); let rangeStartDate: number | null; let rangeEndDate: number | null; @@ -250,26 +252,6 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { this._changeDetectorRef.markForCheck(); } - /** - * Takes the index of a calendar body cell wrapped in in an event as argument. For the date that - * corresponds to the given cell, set `activeDate` to that date and fire `activeDateChange` with - * that date. - * - * This fucntion is used to match each component's model of the active date with the calendar - * body cell that was focused. It updates its value of `activeDate` synchronously and updates the - * parent's value asynchonously via the `activeDateChange` event. The child component receives an - * updated value asynchronously via the `activeCell` Input. - */ - _updateActiveDate(event: MatCalendarUserEvent) { - const month = event.value; - const oldActiveDate = this._activeDate; - this.activeDate = this._getDateFromDayOfMonth(month); - - if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { - this.activeDateChange.emit(this._activeDate); - } - } - /** Handles keydown events on the calendar body when calendar is in month view. */ _handleCalendarBodyKeydown(event: KeyboardEvent): void { // TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent @@ -345,10 +327,9 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { this.activeDateChange.emit(this.activeDate); - - this._focusActiveCellAfterViewChecked(); } + this._focusActiveCell(); // Prevent unexpected default actions such as form submission. event.preventDefault(); } @@ -395,11 +376,6 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { this._matCalendarBody._focusActiveCell(movePreview); } - /** Focuses the active cell after change detection has run and the microtask queue is empty. */ - _focusActiveCellAfterViewChecked() { - this._matCalendarBody._scheduleFocusActiveCellAfterViewChecked(); - } - /** Called when the user has activated a new cell and the preview needs to be updated. */ _previewChanged({event, value: cell}: MatCalendarUserEvent | null>) { if (this._rangeStrategy) { @@ -422,18 +398,6 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { } } - /** - * Takes a day of the month and returns a new date in the same month and year as the currently - * active date. The returned date will have the same day of the month as the argument date. - */ - private _getDateFromDayOfMonth(dayOfMonth: number): D { - return this._dateAdapter.createDate( - this._dateAdapter.getYear(this.activeDate), - this._dateAdapter.getMonth(this.activeDate), - dayOfMonth, - ); - } - /** Initializes the weekdays. */ private _initWeekdays() { const firstDayOfWeek = this._dateAdapter.getFirstDayOfWeek(); diff --git a/src/material/datepicker/multi-year-view.html b/src/material/datepicker/multi-year-view.html index 84220c8fa95f..ee12a9e67d29 100644 --- a/src/material/datepicker/multi-year-view.html +++ b/src/material/datepicker/multi-year-view.html @@ -11,7 +11,6 @@ [cellAspectRatio]="4 / 7" [activeCell]="_getActiveCell()" (selectedValueChange)="_yearSelected($event)" - (activeDateChange)="_updateActiveDate($event)" (keyup)="_handleCalendarBodyKeyup($event)" (keydown)="_handleCalendarBodyKeydown($event)"> diff --git a/src/material/datepicker/multi-year-view.spec.ts b/src/material/datepicker/multi-year-view.spec.ts index 47e56f395d1a..61c14607eb26 100644 --- a/src/material/datepicker/multi-year-view.spec.ts +++ b/src/material/datepicker/multi-year-view.spec.ts @@ -13,7 +13,7 @@ import {dispatchFakeEvent, dispatchKeyboardEvent} from '../../cdk/testing/privat import {Component, ViewChild} from '@angular/core'; import {waitForAsync, ComponentFixture, TestBed} from '@angular/core/testing'; import {MatNativeDateModule} from '@angular/material/core'; -import {JAN, MAR} from '../testing'; +import {JAN} from '../testing'; import {By} from '@angular/platform-browser'; import {MatCalendarBody} from './calendar-body'; import {MatMultiYearView, yearsPerPage, yearsPerRow} from './multi-year-view'; @@ -216,34 +216,6 @@ describe('MatMultiYearView', () => { expect(calendarInstance.date).toEqual(new Date(2017 + yearsPerPage * 2, JAN, 1)); }); - - it('should go to the year that is focused', () => { - fixture.componentInstance.date = new Date(2017, MAR, 5); - fixture.detectChanges(); - expect(calendarInstance.date).toEqual(new Date(2017, MAR, 5)); - - const year2022Cell = fixture.debugElement.nativeElement.querySelector( - '[data-mat-row="1"][data-mat-col="2"] button', - ) as HTMLElement; - - dispatchFakeEvent(year2022Cell, 'focus'); - fixture.detectChanges(); - - expect(calendarInstance.date).toEqual(new Date(2022, MAR, 5)); - }); - - it('should not call `.focus()` when the active date is focused', () => { - const year2017Cell = fixture.debugElement.nativeElement.querySelector( - '[data-mat-row="0"][data-mat-col="1"] button', - ) as HTMLElement; - const focusSpy = (year2017Cell.focus = jasmine.createSpy('cellFocused')); - - dispatchFakeEvent(year2017Cell, 'focus'); - fixture.detectChanges(); - - expect(calendarInstance.date).toEqual(new Date(2017, JAN, 1)); - expect(focusSpy).not.toHaveBeenCalled(); - }); }); }); }); diff --git a/src/material/datepicker/multi-year-view.ts b/src/material/datepicker/multi-year-view.ts index 4d1d6b133255..da7206057bdc 100644 --- a/src/material/datepicker/multi-year-view.ts +++ b/src/material/datepicker/multi-year-view.ts @@ -204,31 +204,18 @@ export class MatMultiYearView implements AfterContentInit, OnDestroy { /** Handles when a new year is selected. */ _yearSelected(event: MatCalendarUserEvent) { const year = event.value; - const selectedYear = this._dateAdapter.createDate(year, 0, 1); - const selectedDate = this._getDateFromYear(year); - - this.yearSelected.emit(selectedYear); - this.selectedChange.emit(selectedDate); - } - - /** - * Takes the index of a calendar body cell wrapped in in an event as argument. For the date that - * corresponds to the given cell, set `activeDate` to that date and fire `activeDateChange` with - * that date. - * - * This fucntion is used to match each component's model of the active date with the calendar - * body cell that was focused. It updates its value of `activeDate` synchronously and updates the - * parent's value asynchonously via the `activeDateChange` event. The child component receives an - * updated value asynchronously via the `activeCell` Input. - */ - _updateActiveDate(event: MatCalendarUserEvent) { - const year = event.value; - const oldActiveDate = this._activeDate; - - this.activeDate = this._getDateFromYear(year); - if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { - this.activeDateChange.emit(this.activeDate); - } + this.yearSelected.emit(this._dateAdapter.createDate(year, 0, 1)); + let month = this._dateAdapter.getMonth(this.activeDate); + let daysInMonth = this._dateAdapter.getNumDaysInMonth( + this._dateAdapter.createDate(year, month, 1), + ); + this.selectedChange.emit( + this._dateAdapter.createDate( + year, + month, + Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth), + ), + ); } /** Handles keydown events on the calendar body when calendar is in multi-year view. */ @@ -291,7 +278,7 @@ export class MatMultiYearView implements AfterContentInit, OnDestroy { this.activeDateChange.emit(this.activeDate); } - this._focusActiveCellAfterViewChecked(); + this._focusActiveCell(); // Prevent unexpected default actions such as form submission. event.preventDefault(); } @@ -316,28 +303,6 @@ export class MatMultiYearView implements AfterContentInit, OnDestroy { this._matCalendarBody._focusActiveCell(); } - /** Focuses the active cell after change detection has run and the microtask queue is empty. */ - _focusActiveCellAfterViewChecked() { - this._matCalendarBody._scheduleFocusActiveCellAfterViewChecked(); - } - - /** - * Takes a year and returns a new date on the same day and month as the currently active date - * The returned date will have the same year as the argument date. - */ - private _getDateFromYear(year: number) { - const activeMonth = this._dateAdapter.getMonth(this.activeDate); - const daysInMonth = this._dateAdapter.getNumDaysInMonth( - this._dateAdapter.createDate(year, activeMonth, 1), - ); - const normalizedDate = this._dateAdapter.createDate( - year, - activeMonth, - Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth), - ); - return normalizedDate; - } - /** Creates an MatCalendarCell for the given year. */ private _createCellForYear(year: number) { const date = this._dateAdapter.createDate(year, 0, 1); diff --git a/src/material/datepicker/year-view.html b/src/material/datepicker/year-view.html index afb9821dd416..dae81c5e2a27 100644 --- a/src/material/datepicker/year-view.html +++ b/src/material/datepicker/year-view.html @@ -13,7 +13,6 @@ [cellAspectRatio]="4 / 7" [activeCell]="_dateAdapter.getMonth(activeDate)" (selectedValueChange)="_monthSelected($event)" - (activeDateChange)="_updateActiveDate($event)" (keyup)="_handleCalendarBodyKeyup($event)" (keydown)="_handleCalendarBodyKeydown($event)"> diff --git a/src/material/datepicker/year-view.spec.ts b/src/material/datepicker/year-view.spec.ts index 2fb07ef50423..99e6ca6783f9 100644 --- a/src/material/datepicker/year-view.spec.ts +++ b/src/material/datepicker/year-view.spec.ts @@ -292,30 +292,6 @@ describe('MatYearView', () => { expect(calendarInstance.date).toEqual(new Date(2018, FEB, 28)); }); - - it('should go to date that is focused', () => { - const juneCell = fixture.debugElement.nativeElement.querySelector( - '[data-mat-row="1"][data-mat-col="1"] button', - ) as HTMLElement; - - dispatchFakeEvent(juneCell, 'focus'); - fixture.detectChanges(); - - expect(calendarInstance.date).toEqual(new Date(2017, JUN, 5)); - }); - - it('should not call `.focus()` when the active date is focused', () => { - const janCell = fixture.debugElement.nativeElement.querySelector( - '[data-mat-row="0"][data-mat-col="0"] button', - ) as HTMLElement; - const focusSpy = (janCell.focus = jasmine.createSpy('cellFocused')); - - dispatchFakeEvent(janCell, 'focus'); - fixture.detectChanges(); - - expect(calendarInstance.date).toEqual(new Date(2017, JAN, 5)); - expect(focusSpy).not.toHaveBeenCalled(); - }); }); }); }); diff --git a/src/material/datepicker/year-view.ts b/src/material/datepicker/year-view.ts index b69c98bba7f9..2e121f75cc15 100644 --- a/src/material/datepicker/year-view.ts +++ b/src/material/datepicker/year-view.ts @@ -179,37 +179,23 @@ export class MatYearView implements AfterContentInit, OnDestroy { /** Handles when a new month is selected. */ _monthSelected(event: MatCalendarUserEvent) { const month = event.value; - - const selectedMonth = this._dateAdapter.createDate( + const normalizedDate = this._dateAdapter.createDate( this._dateAdapter.getYear(this.activeDate), month, 1, ); - this.monthSelected.emit(selectedMonth); - - const selectedDate = this._getDateFromMonth(month); - this.selectedChange.emit(selectedDate); - } - /** - * Takes the index of a calendar body cell wrapped in in an event as argument. For the date that - * corresponds to the given cell, set `activeDate` to that date and fire `activeDateChange` with - * that date. - * - * This fucntion is used to match each component's model of the active date with the calendar - * body cell that was focused. It updates its value of `activeDate` synchronously and updates the - * parent's value asynchonously via the `activeDateChange` event. The child component receives an - * updated value asynchronously via the `activeCell` Input. - */ - _updateActiveDate(event: MatCalendarUserEvent) { - const month = event.value; - const oldActiveDate = this._activeDate; + this.monthSelected.emit(normalizedDate); - this.activeDate = this._getDateFromMonth(month); + const daysInMonth = this._dateAdapter.getNumDaysInMonth(normalizedDate); - if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { - this.activeDateChange.emit(this.activeDate); - } + this.selectedChange.emit( + this._dateAdapter.createDate( + this._dateAdapter.getYear(this.activeDate), + month, + Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth), + ), + ); } /** Handles keydown events on the calendar body when calendar is in year view. */ @@ -273,9 +259,9 @@ export class MatYearView implements AfterContentInit, OnDestroy { if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { this.activeDateChange.emit(this.activeDate); - this._focusActiveCellAfterViewChecked(); } + this._focusActiveCell(); // Prevent unexpected default actions such as form submission. event.preventDefault(); } @@ -312,11 +298,6 @@ export class MatYearView implements AfterContentInit, OnDestroy { this._matCalendarBody._focusActiveCell(); } - /** Schedules the matCalendarBody to focus the active cell after change detection has run */ - _focusActiveCellAfterViewChecked() { - this._matCalendarBody._scheduleFocusActiveCellAfterViewChecked(); - } - /** * Gets the month in this year that the given Date falls on. * Returns null if the given Date is in another year. @@ -327,26 +308,6 @@ export class MatYearView implements AfterContentInit, OnDestroy { : null; } - /** - * Takes a month and returns a new date in the same day and year as the currently active date. - * The returned date will have the same month as the argument date. - */ - private _getDateFromMonth(month: number) { - const normalizedDate = this._dateAdapter.createDate( - this._dateAdapter.getYear(this.activeDate), - month, - 1, - ); - - const daysInMonth = this._dateAdapter.getNumDaysInMonth(normalizedDate); - - return this._dateAdapter.createDate( - this._dateAdapter.getYear(this.activeDate), - month, - Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth), - ); - } - /** Creates an MatCalendarCell for the given month. */ private _createCellForMonth(month: number, monthName: string) { const date = this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), month, 1); diff --git a/tools/public_api_guard/material/datepicker.md b/tools/public_api_guard/material/datepicker.md index 4d3a353d8c48..e38e5f64f5fd 100644 --- a/tools/public_api_guard/material/datepicker.md +++ b/tools/public_api_guard/material/datepicker.md @@ -197,19 +197,15 @@ export class MatCalendar implements AfterContentInit, AfterViewChecked, OnDes } // @public -export class MatCalendarBody implements OnChanges, OnDestroy, AfterViewChecked { +export class MatCalendarBody implements OnChanges, OnDestroy { constructor(_elementRef: ElementRef, _ngZone: NgZone); activeCell: number; - // (undocumented) - readonly activeDateChange: EventEmitter>; cellAspectRatio: number; _cellClicked(cell: MatCalendarCell, event: MouseEvent): void; _cellPadding: string; _cellWidth: string; comparisonEnd: number | null; comparisonStart: number | null; - // (undocumented) - _emitActiveDateChange(cell: MatCalendarCell, event: FocusEvent): void; endValue: number; _firstRowOffset: number; _focusActiveCell(movePreview?: boolean): void; @@ -231,8 +227,6 @@ export class MatCalendarBody implements OnChanges, OnDestroy, AfterViewChecked { label: string; labelMinRequiredCells: number; // (undocumented) - ngAfterViewChecked(): void; - // (undocumented) ngOnChanges(changes: SimpleChanges): void; // (undocumented) ngOnDestroy(): void; @@ -241,12 +235,11 @@ export class MatCalendarBody implements OnChanges, OnDestroy, AfterViewChecked { previewEnd: number | null; previewStart: number | null; rows: MatCalendarCell[][]; - _scheduleFocusActiveCellAfterViewChecked(): void; readonly selectedValueChange: EventEmitter>; startValue: number; todayValue: number; // (undocumented) - static ɵcmp: i0.ɵɵComponentDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; } @@ -772,7 +765,6 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { _dateSelected(event: MatCalendarUserEvent): void; _firstWeekOffset: number; _focusActiveCell(movePreview?: boolean): void; - _focusActiveCellAfterViewChecked(): void; _handleCalendarBodyKeydown(event: KeyboardEvent): void; _handleCalendarBodyKeyup(event: KeyboardEvent): void; _init(): void; @@ -798,7 +790,6 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { set selected(value: DateRange | D | null); readonly selectedChange: EventEmitter; _todayDate: number | null; - _updateActiveDate(event: MatCalendarUserEvent): void; readonly _userSelection: EventEmitter>; _weekdays: { long: string; @@ -822,7 +813,6 @@ export class MatMultiYearView implements AfterContentInit, OnDestroy { dateClass: MatCalendarCellClassFunction; dateFilter: (date: D) => boolean; _focusActiveCell(): void; - _focusActiveCellAfterViewChecked(): void; // (undocumented) _getActiveCell(): number; _handleCalendarBodyKeydown(event: KeyboardEvent): void; @@ -842,7 +832,6 @@ export class MatMultiYearView implements AfterContentInit, OnDestroy { readonly selectedChange: EventEmitter; _selectedYear: number | null; _todayYear: number; - _updateActiveDate(event: MatCalendarUserEvent): void; _years: MatCalendarCell[][]; readonly yearSelected: EventEmitter; _yearSelected(event: MatCalendarUserEvent): void; @@ -911,7 +900,6 @@ export class MatYearView implements AfterContentInit, OnDestroy { dateClass: MatCalendarCellClassFunction; dateFilter: (date: D) => boolean; _focusActiveCell(): void; - _focusActiveCellAfterViewChecked(): void; _handleCalendarBodyKeydown(event: KeyboardEvent): void; _handleCalendarBodyKeyup(event: KeyboardEvent): void; _init(): void; @@ -932,7 +920,6 @@ export class MatYearView implements AfterContentInit, OnDestroy { readonly selectedChange: EventEmitter; _selectedMonth: number | null; _todayMonth: number | null; - _updateActiveDate(event: MatCalendarUserEvent): void; _yearLabel: string; // (undocumented) static ɵcmp: i0.ɵɵComponentDeclaration, "mat-year-view", ["matYearView"], { "activeDate": "activeDate"; "selected": "selected"; "minDate": "minDate"; "maxDate": "maxDate"; "dateFilter": "dateFilter"; "dateClass": "dateClass"; }, { "selectedChange": "selectedChange"; "monthSelected": "monthSelected"; "activeDateChange": "activeDateChange"; }, never, never>;