@@ -14,6 +14,7 @@ import {
1414 computed ,
1515 untracked ,
1616 afterRenderEffect ,
17+ viewChildren ,
1718} from '@angular/core' ;
1819import { DateAdapter , MAT_DATE_FORMATS , MatDateFormats } from '@angular/material/core' ;
1920import { Grid , GridRow , GridCell , GridCellWidget } from '@angular/aria/grid' ;
@@ -25,6 +26,7 @@ interface CalendarCell<D = any> {
2526 ariaLabel : string ;
2627 date : D ;
2728 selected : WritableSignal < boolean > ;
29+ dayNum : number ;
2830}
2931
3032/** @title Grid Calendar. */
@@ -35,6 +37,8 @@ interface CalendarCell<D = any> {
3537 imports : [ Grid , GridRow , GridCell , GridCellWidget ] ,
3638} )
3739export class GridCalendarExample < D > {
40+ private readonly _dayButtons = viewChildren ( GridCellWidget ) ;
41+
3842 private readonly _dateAdapter = inject < DateAdapter < D > > ( DateAdapter , { optional : true } ) ! ;
3943 private readonly _dateFormats = inject < MatDateFormats > ( MAT_DATE_FORMATS , { optional : true } ) ! ;
4044 private readonly _firstWeekOffset : Signal < number > = computed ( ( ) => {
@@ -62,6 +66,9 @@ export class GridCalendarExample<D> {
6266 . format ( this . viewMonth ( ) , this . _dateFormats . display . monthYearLabel )
6367 . toLocaleUpperCase ( ) ,
6468 ) ;
69+ readonly viewMonthNumDays : Signal < number > = computed ( ( ) =>
70+ this . _dateAdapter . getNumDaysInMonth ( this . viewMonth ( ) ) ,
71+ ) ;
6572 readonly prevMonthNumDays : Signal < number > = computed ( ( ) =>
6673 this . _dateAdapter . getNumDaysInMonth ( this . _dateAdapter . addCalendarMonths ( this . viewMonth ( ) , - 1 ) ) ,
6774 ) ;
@@ -88,9 +95,6 @@ export class GridCalendarExample<D> {
8895 this . _createWeekCells ( this . viewMonth ( ) ) ,
8996 ) ;
9097
91- readonly scrolledUp = signal ( false ) ;
92- readonly scrolledDown = signal ( false ) ;
93-
9498 constructor ( ) {
9599 afterRenderEffect ( ( ) => {
96100 for ( const day of this . weeks ( ) . flat ( ) ) {
@@ -110,8 +114,40 @@ export class GridCalendarExample<D> {
110114 this . viewMonth . set ( this . _dateAdapter . addCalendarMonths ( this . viewMonth ( ) , - 1 ) ) ;
111115 }
112116
117+ scrollDown ( ) : void {
118+ this . nextMonth ( ) ;
119+ setTimeout ( ( ) => this . _dayButtons ( ) [ 0 ] ?. element . focus ( ) ) ;
120+ }
121+
122+ scrollUp ( ) : void {
123+ this . prevMonth ( ) ;
124+ setTimeout ( ( ) => this . _dayButtons ( ) [ this . _dayButtons ( ) . length - 1 ] ?. element . focus ( ) ) ;
125+ }
126+
127+ onKeyDown ( event : KeyboardEvent ) : void {
128+ const day = Number ( ( event . target as Element ) . getAttribute ( 'data-day' ) ) ;
129+ if ( ! day ) return ;
130+ if ( day > 7 && day <= this . viewMonthNumDays ( ) - 7 ) return ;
131+
132+ const arrowLeft = event . key === 'ArrowLeft' ;
133+ const arrowRight = event . key === 'ArrowRight' ;
134+ const arrowUp = event . key === 'ArrowUp' ;
135+ const arrowDown = event . key === 'ArrowDown' ;
136+
137+ if ( ( day === 1 && arrowLeft ) || ( day <= 7 && arrowUp ) ) {
138+ this . scrollUp ( ) ;
139+ }
140+
141+ if (
142+ ( day === this . viewMonthNumDays ( ) && arrowRight ) ||
143+ ( day > this . viewMonthNumDays ( ) - 7 && arrowDown )
144+ ) {
145+ this . scrollDown ( ) ;
146+ }
147+ }
148+
113149 private _createWeekCells ( viewMonth : D ) : CalendarCell [ ] [ ] {
114- const daysInMonth = this . _dateAdapter . getNumDaysInMonth ( viewMonth ) ;
150+ const daysInMonth = this . viewMonthNumDays ( ) ;
115151 const dateNames = this . _dateAdapter . getDateNames ( ) ;
116152 const weeks : CalendarCell [ ] [ ] = [ [ ] ] ;
117153 for ( let i = 0 , cell = this . _firstWeekOffset ( ) ; i < daysInMonth ; i ++ , cell ++ ) {
@@ -136,6 +172,7 @@ export class GridCalendarExample<D> {
136172 untracked ( ( ) => this . _activeDate ( ) ) ,
137173 ) === 0 ,
138174 ) ,
175+ dayNum : i + 1 ,
139176 } ) ;
140177 }
141178 return weeks ;
0 commit comments