Skip to content

Commit 25fb01d

Browse files
committed
refactor(material/datepicker): focus active cell after change detection
When receiving keyboard events on the month view, ensure that change detection runs before focusing the active cell.
1 parent b8e4183 commit 25fb01d

File tree

3 files changed

+40
-4
lines changed

3 files changed

+40
-4
lines changed

src/material/datepicker/calendar-body.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@
6363
[attr.aria-disabled]="!item.enabled || null"
6464
[attr.aria-pressed]="_isSelected(item.compareValue)"
6565
[attr.aria-current]="todayValue === item.compareValue ? 'date' : null"
66-
(click)="_cellClicked(item, $event)">
66+
(click)="_cellClicked(item, $event)"
67+
(focus)="_cellFocused(item, $event)">
6768
<div class="mat-calendar-body-cell-content mat-focus-indicator"
6869
[class.mat-calendar-body-selected]="_isSelected(item.compareValue)"
6970
[class.mat-calendar-body-comparison-identical]="_isComparisonIdentical(item.compareValue)"

src/material/datepicker/calendar-body.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
OnChanges,
1919
SimpleChanges,
2020
OnDestroy,
21+
AfterViewChecked,
2122
} from '@angular/core';
2223
import {take} from 'rxjs/operators';
2324

@@ -67,13 +68,18 @@ export interface MatCalendarUserEvent<D> {
6768
encapsulation: ViewEncapsulation.None,
6869
changeDetection: ChangeDetectionStrategy.OnPush,
6970
})
70-
export class MatCalendarBody implements OnChanges, OnDestroy {
71+
export class MatCalendarBody implements OnChanges, OnDestroy, AfterViewChecked {
7172
/**
7273
* Used to skip the next focus event when rendering the preview range.
7374
* We need a flag like this, because some browsers fire focus events asynchronously.
7475
*/
7576
private _skipNextFocus: boolean;
7677

78+
/**
79+
* Used to focus the active cell after change detection has run.
80+
*/
81+
private _focusActiveCellAfterViewChecked = false;
82+
7783
/** The label for the table. (e.g. "Jan 2017"). */
7884
@Input() label: string;
7985

@@ -96,7 +102,20 @@ export class MatCalendarBody implements OnChanges, OnDestroy {
96102
@Input() numCols: number = 7;
97103

98104
/** The cell number of the active cell in the table. */
99-
@Input() activeCell: number = 0;
105+
@Input() get activeCell(): number {
106+
return this._activeCell;
107+
}
108+
set activeCell(activeCell: number) {
109+
this._activeCell = activeCell;
110+
}
111+
private _activeCell: number = 0;
112+
113+
ngAfterViewChecked() {
114+
if (this._focusActiveCellAfterViewChecked) {
115+
this._focusActiveCell();
116+
this._focusActiveCellAfterViewChecked = false;
117+
}
118+
}
100119

101120
/** Whether a range is being selected. */
102121
@Input() isRange: boolean = false;
@@ -153,6 +172,12 @@ export class MatCalendarBody implements OnChanges, OnDestroy {
153172
}
154173
}
155174

175+
_cellFocused(cell: MatCalendarCell, event: FocusEvent): void {
176+
if (cell.enabled) {
177+
// TODO: make argument cell the active date
178+
}
179+
}
180+
156181
/** Returns whether a cell should be marked as selected. */
157182
_isSelected(value: number) {
158183
return this.startValue === value || this.endValue === value;
@@ -214,6 +239,11 @@ export class MatCalendarBody implements OnChanges, OnDestroy {
214239
});
215240
}
216241

242+
/** Focuses the active cell after change detection has run and the microtask queue is empty. */
243+
_scheduleFocusActiveCellAfterViewChecked() {
244+
this._focusActiveCellAfterViewChecked = true;
245+
}
246+
217247
/** Gets whether a value is the start of the main range. */
218248
_isRangeStart(value: number) {
219249
return isStart(value, this.startValue, this.endValue);

src/material/datepicker/month-view.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ export class MatMonthView<D> implements AfterContentInit, OnChanges, OnDestroy {
329329
this.activeDateChange.emit(this.activeDate);
330330
}
331331

332-
this._focusActiveCell();
332+
this._focusActiveCellAfterViewChecked();
333333
// Prevent unexpected default actions such as form submission.
334334
event.preventDefault();
335335
}
@@ -376,6 +376,11 @@ export class MatMonthView<D> implements AfterContentInit, OnChanges, OnDestroy {
376376
this._matCalendarBody._focusActiveCell(movePreview);
377377
}
378378

379+
/** Focuses the active cell after change detection has run and the microtask queue is empty. */
380+
_focusActiveCellAfterViewChecked() {
381+
this._matCalendarBody._scheduleFocusActiveCellAfterViewChecked();
382+
}
383+
379384
/** Called when the user has activated a new cell and the preview needs to be updated. */
380385
_previewChanged({event, value: cell}: MatCalendarUserEvent<MatCalendarCell<D> | null>) {
381386
if (this._rangeStrategy) {

0 commit comments

Comments
 (0)