99import { ComponentPortal , ComponentType , Portal } from '@angular/cdk/portal' ;
1010import {
1111 AfterContentInit ,
12+ AfterViewChecked ,
1213 ChangeDetectionStrategy ,
1314 ChangeDetectorRef ,
1415 Component ,
@@ -32,6 +33,12 @@ import {MatMonthView} from './month-view';
3233import { MatMultiYearView , yearsPerPage } from './multi-year-view' ;
3334import { MatYearView } from './year-view' ;
3435
36+ /**
37+ * Possible views for the calendar.
38+ * @docs -private
39+ */
40+ export type MatCalendarView = 'month' | 'year' | 'multi-year' ;
41+
3542/** Default header for MatCalendar */
3643@Component ( {
3744 moduleId : module . id ,
@@ -162,7 +169,7 @@ export class MatCalendarHeader<D> {
162169 encapsulation : ViewEncapsulation . None ,
163170 changeDetection : ChangeDetectionStrategy . OnPush ,
164171} )
165- export class MatCalendar < D > implements AfterContentInit , OnDestroy , OnChanges {
172+ export class MatCalendar < D > implements AfterContentInit , AfterViewChecked , OnDestroy , OnChanges {
166173 /** An input indicating the type of the header component, if set. */
167174 @Input ( ) headerComponent : ComponentType < any > ;
168175
@@ -171,6 +178,13 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
171178
172179 private _intlChanges : Subscription ;
173180
181+ /**
182+ * Used for scheduling that focus should be moved to the active cell on the next tick.
183+ * We need to schedule it, rather than do it immediately, because we have to wait
184+ * for Angular to re-evaluate the view children.
185+ */
186+ private _moveFocusOnNextTick = false ;
187+
174188 /** A date representing the period (month or year) to start the calendar in. */
175189 @Input ( )
176190 get startAt ( ) : D | null { return this . _startAt ; }
@@ -180,7 +194,7 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
180194 private _startAt : D | null ;
181195
182196 /** Whether the calendar should be started in month or year view. */
183- @Input ( ) startView : 'month' | 'year' | 'multi-year' = 'month' ;
197+ @Input ( ) startView : MatCalendarView = 'month' ;
184198
185199 /** The currently selected date. */
186200 @Input ( )
@@ -248,7 +262,12 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
248262 private _clampedActiveDate : D ;
249263
250264 /** Whether the calendar is in month view. */
251- currentView : 'month' | 'year' | 'multi-year' ;
265+ get currentView ( ) : MatCalendarView { return this . _currentView ; }
266+ set currentView ( value : MatCalendarView ) {
267+ this . _currentView = value ;
268+ this . _moveFocusOnNextTick = true ;
269+ }
270+ private _currentView : MatCalendarView ;
252271
253272 /**
254273 * Emits whenever there is a state change that the header may need to respond to.
@@ -276,9 +295,17 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
276295
277296 ngAfterContentInit ( ) {
278297 this . _calendarHeaderPortal = new ComponentPortal ( this . headerComponent || MatCalendarHeader ) ;
279-
280298 this . activeDate = this . startAt || this . _dateAdapter . today ( ) ;
281- this . currentView = this . startView ;
299+
300+ // Assign to the private property since we don't want to move focus on init.
301+ this . _currentView = this . startView ;
302+ }
303+
304+ ngAfterViewChecked ( ) {
305+ if ( this . _moveFocusOnNextTick ) {
306+ this . _moveFocusOnNextTick = false ;
307+ this . focusActiveCell ( ) ;
308+ }
282309 }
283310
284311 ngOnDestroy ( ) {
@@ -290,7 +317,7 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
290317 const change = changes . minDate || changes . maxDate || changes . dateFilter ;
291318
292319 if ( change && ! change . firstChange ) {
293- const view = this . monthView || this . yearView || this . multiYearView ;
320+ const view = this . _getCurrentViewComponent ( ) ;
294321
295322 if ( view ) {
296323 view . _init ( ) ;
@@ -300,6 +327,10 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
300327 this . stateChanges . next ( ) ;
301328 }
302329
330+ focusActiveCell ( ) {
331+ this . _getCurrentViewComponent ( ) . _focusActiveCell ( ) ;
332+ }
333+
303334 /** Handles date selection in the month view. */
304335 _dateSelected ( date : D ) : void {
305336 if ( ! this . _dateAdapter . sameDate ( date , this . selected ) ) {
@@ -334,4 +365,9 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
334365 private _getValidDateOrNull ( obj : any ) : D | null {
335366 return ( this . _dateAdapter . isDateInstance ( obj ) && this . _dateAdapter . isValid ( obj ) ) ? obj : null ;
336367 }
368+
369+ /** Returns the component instance that corresponds to the current calendar view. */
370+ private _getCurrentViewComponent ( ) {
371+ return this . monthView || this . yearView || this . multiYearView ;
372+ }
337373}
0 commit comments