diff --git a/.gitignore b/.gitignore index ce0991dfb3e0..2a71b14567d2 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,8 @@ node_modules /.vs *.swo *.swp +.vimrc +.nvimrc # misc .DS_Store diff --git a/src/material/datepicker/calendar-body.html b/src/material/datepicker/calendar-body.html index ffdfef27c1cd..d0c1f7870611 100644 --- a/src/material/datepicker/calendar-body.html +++ b/src/material/datepicker/calendar-body.html @@ -26,40 +26,59 @@ [style.paddingBottom]="_cellPadding"> {{_firstRowOffset >= labelMinRequiredCells ? label : ''}} - -
- {{item.displayValue}} + + +
+
+ {{item.displayValue}} +
+ +
diff --git a/src/material/datepicker/calendar-body.scss b/src/material/datepicker/calendar-body.scss index c2d2f78df048..d77f2fc4377c 100644 --- a/src/material/datepicker/calendar-body.scss +++ b/src/material/datepicker/calendar-body.scss @@ -31,7 +31,12 @@ $calendar-range-end-body-cell-size: padding-right: $calendar-body-label-side-padding; } +.mat-calendar-body-cell-wrapper { + display: contents; +} + .mat-calendar-body-cell { + display: table-cell; position: relative; height: 0; line-height: 0; diff --git a/src/material/datepicker/calendar-body.ts b/src/material/datepicker/calendar-body.ts index 895db4b41472..ecb25003ee8f 100644 --- a/src/material/datepicker/calendar-body.ts +++ b/src/material/datepicker/calendar-body.ts @@ -122,6 +122,9 @@ export class MatCalendarBody implements OnChanges, OnDestroy { /** Emits when a new value is selected. */ @Output() readonly selectedValueChange = new EventEmitter>(); + /** Emits when a new date becomes active. */ + @Output() readonly activeValueChange = new EventEmitter>(); + /** Emits when the preview has changed as a result of a user action. */ @Output() readonly previewChange = new EventEmitter< MatCalendarUserEvent @@ -153,6 +156,13 @@ export class MatCalendarBody implements OnChanges, OnDestroy { } } + /** Called when a cell is focused. */ + _cellFocused(cell: MatCalendarCell, event: any): void { + if (cell.enabled) { + this.activeValueChange.emit({value: cell.value, event}); + } + } + /** Returns whether a cell should be marked as selected. */ _isSelected(value: number) { return this.startValue === value || this.endValue === value; @@ -337,7 +347,11 @@ export class MatCalendarBody implements OnChanges, OnDestroy { // Only reset the preview end value when leaving cells. This looks better, because // we have a gap between the cells and the rows and we don't want to remove the // range just for it to show up again when the user moves a few pixels to the side. - if (event.target && isTableCell(event.target as HTMLElement)) { + if ( + event.target && + (event.target as HTMLElement).parentElement && + isTableCell((event.target as HTMLElement).parentElement as HTMLElement) + ) { this._ngZone.run(() => this.previewChange.emit({value: null, event})); } } @@ -350,7 +364,7 @@ export class MatCalendarBody implements OnChanges, OnDestroy { if (isTableCell(element)) { cell = element; } else if (isTableCell(element.parentNode!)) { - cell = element.parentNode as HTMLElement; + cell = element; } if (cell) { diff --git a/src/material/datepicker/month-view.html b/src/material/datepicker/month-view.html index 35deaf2339c2..e3c5440d9ff3 100644 --- a/src/material/datepicker/month-view.html +++ b/src/material/datepicker/month-view.html @@ -24,6 +24,7 @@ [labelMinRequiredCells]="3" [activeCell]="_dateAdapter.getDate(activeDate) - 1" (selectedValueChange)="_dateSelected($event)" + (activeValueChange)="_dayBecomesActive($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 7a4909f8efbb..a769a80c67ec 100644 --- a/src/material/datepicker/month-view.spec.ts +++ b/src/material/datepicker/month-view.spec.ts @@ -149,13 +149,14 @@ describe('MatMonthView', () => { fixture.detectChanges(); }); - it('should decrement date on left arrow press', () => { + fit('should decrement date on left arrow press', () => { dispatchKeyboardEvent(calendarBodyEl, 'keydown', LEFT_ARROW); fixture.detectChanges(); expect(calendarInstance.date).toEqual(new Date(2017, JAN, 4)); calendarInstance.date = new Date(2017, JAN, 1); fixture.detectChanges(); + expect(calendarInstance.date).toEqual(new Date(2017, JAN, 1)); dispatchKeyboardEvent(calendarBodyEl, 'keydown', LEFT_ARROW); fixture.detectChanges(); @@ -653,7 +654,16 @@ describe('MatMonthView', () => { (_userSelection)="userSelectionSpy($event)">`, }) class StandardMonthView { - date = new Date(2017, JAN, 5); + get date(): Date { + console.log('test code. `get date`', this._date); + return this._date; + } + set date(date: Date) { + console.log('test code. `set date`', date); + this._date = date; + } + _date = new Date(2017, JAN, 5); + selected: Date | DateRange = new Date(2017, JAN, 10); selectedChangeSpy = jasmine.createSpy('selectedChange'); userSelectionSpy = jasmine.createSpy('userSelection'); diff --git a/src/material/datepicker/month-view.ts b/src/material/datepicker/month-view.ts index a46abfb4dfbd..bece84601f4f 100644 --- a/src/material/datepicker/month-view.ts +++ b/src/material/datepicker/month-view.ts @@ -252,6 +252,30 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { this._changeDetectorRef.markForCheck(); } + _dayBecomesActive(event: MatCalendarUserEvent) { + const date = event.value; + const selectedYear = this._dateAdapter.getYear(this.activeDate); + const selectedMonth = this._dateAdapter.getMonth(this.activeDate); + const activeDate = this._dateAdapter.createDate(selectedYear, selectedMonth, date); + let rangeStartDate: number | null; + let rangeEndDate: number | null; + + if (this._activeDate instanceof DateRange) { + rangeStartDate = this._getDateInCurrentMonth(this._activeDate.start); + rangeEndDate = this._getDateInCurrentMonth(this._activeDate.end); + } else { + rangeStartDate = rangeEndDate = this._getDateInCurrentMonth(this._activeDate); + } + + this.activeDate = activeDate; + + if (rangeStartDate !== date || rangeEndDate !== date) { + this.activeDateChange.emit(activeDate); + } + + this._changeDetectorRef.markForCheck(); + } + /** 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 @@ -326,10 +350,12 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { } if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) { + console.log("firing activeDateChange", this.activeDate); this.activeDateChange.emit(this.activeDate); } this._focusActiveCell(); + // Prevent unexpected default actions such as form submission. event.preventDefault(); } diff --git a/src/material/datepicker/multi-year-view.html b/src/material/datepicker/multi-year-view.html index ee12a9e67d29..da2de1cf26e4 100644 --- a/src/material/datepicker/multi-year-view.html +++ b/src/material/datepicker/multi-year-view.html @@ -11,6 +11,7 @@ [cellAspectRatio]="4 / 7" [activeCell]="_getActiveCell()" (selectedValueChange)="_yearSelected($event)" + (activeValueChange)="_yearBecomesActive($event)" (keyup)="_handleCalendarBodyKeyup($event)" (keydown)="_handleCalendarBodyKeydown($event)"> diff --git a/src/material/datepicker/multi-year-view.ts b/src/material/datepicker/multi-year-view.ts index da7206057bdc..c11e58e08f54 100644 --- a/src/material/datepicker/multi-year-view.ts +++ b/src/material/datepicker/multi-year-view.ts @@ -218,6 +218,21 @@ export class MatMultiYearView implements AfterContentInit, OnDestroy { ); } + _yearBecomesActive(event: MatCalendarUserEvent) { + const year = event.value; + let month = this._dateAdapter.getMonth(this.activeDate); + let daysInMonth = this._dateAdapter.getNumDaysInMonth( + this._dateAdapter.createDate(year, month, 1), + ); + this.activeDateChange.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. */ _handleCalendarBodyKeydown(event: KeyboardEvent): void { const oldActiveDate = this._activeDate; @@ -278,7 +293,6 @@ export class MatMultiYearView implements AfterContentInit, OnDestroy { this.activeDateChange.emit(this.activeDate); } - this._focusActiveCell(); // Prevent unexpected default actions such as form submission. event.preventDefault(); } diff --git a/src/material/datepicker/year-view.html b/src/material/datepicker/year-view.html index dae81c5e2a27..c54b0c71269d 100644 --- a/src/material/datepicker/year-view.html +++ b/src/material/datepicker/year-view.html @@ -13,6 +13,7 @@ [cellAspectRatio]="4 / 7" [activeCell]="_dateAdapter.getMonth(activeDate)" (selectedValueChange)="_monthSelected($event)" + (activeValueChange)="_monthBecomesActive($event)" (keyup)="_handleCalendarBodyKeyup($event)" (keydown)="_handleCalendarBodyKeydown($event)"> diff --git a/src/material/datepicker/year-view.ts b/src/material/datepicker/year-view.ts index 2e121f75cc15..c7931e1acac0 100644 --- a/src/material/datepicker/year-view.ts +++ b/src/material/datepicker/year-view.ts @@ -198,6 +198,25 @@ export class MatYearView implements AfterContentInit, OnDestroy { ); } + /** Handles when a new month becomes active. */ + _monthBecomesActive(event: MatCalendarUserEvent) { + const month = event.value; + const normalizedDate = this._dateAdapter.createDate( + this._dateAdapter.getYear(this.activeDate), + month, + 1, + ); + + const daysInMonth = this._dateAdapter.getNumDaysInMonth(normalizedDate); + + this.activeDateChange.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. */ _handleCalendarBodyKeydown(event: KeyboardEvent): void { // TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent @@ -261,7 +280,6 @@ export class MatYearView implements AfterContentInit, OnDestroy { this.activeDateChange.emit(this.activeDate); } - this._focusActiveCell(); // Prevent unexpected default actions such as form submission. event.preventDefault(); }