diff --git a/goldens/material/datepicker/index.api.md b/goldens/material/datepicker/index.api.md index 9dd9173ddce0..8f109cabded8 100644 --- a/goldens/material/datepicker/index.api.md +++ b/goldens/material/datepicker/index.api.md @@ -667,8 +667,8 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { // (undocumented) readonly _changeDetectorRef: ChangeDetectorRef; comparisonEnd: D | null; - _comparisonRangeEnd: number | null; - _comparisonRangeStart: number | null; + _comparisonRangeEnd: i0.WritableSignal; + _comparisonRangeStart: i0.WritableSignal; comparisonStart: D | null; // (undocumented) _dateAdapter: DateAdapter; @@ -679,19 +679,19 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { protected _dragEnded(event: MatCalendarUserEvent): void; readonly dragStarted: EventEmitter>; endDateAccessibleName: string | null; - _firstWeekOffset: number; + _firstWeekOffset: i0.WritableSignal; _focusActiveCell(movePreview?: boolean): void; _focusActiveCellAfterViewChecked(): void; _handleCalendarBodyKeydown(event: KeyboardEvent): void; _handleCalendarBodyKeyup(event: KeyboardEvent): void; _init(): void; - _isRange: boolean; + _isRange: i0.WritableSignal; _matCalendarBody: MatCalendarBody; get maxDate(): D | null; set maxDate(value: D | null); get minDate(): D | null; set minDate(value: D | null); - _monthLabel: string; + _monthLabel: i0.WritableSignal; // (undocumented) ngAfterContentInit(): void; // (undocumented) @@ -699,23 +699,23 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { // (undocumented) ngOnDestroy(): void; _previewChanged({ event, value: cell }: MatCalendarUserEvent | null>): void; - _previewEnd: number | null; - _previewStart: number | null; - _rangeEnd: number | null; - _rangeStart: number | null; + _previewEnd: i0.WritableSignal; + _previewStart: i0.WritableSignal; + _rangeEnd: i0.WritableSignal; + _rangeStart: i0.WritableSignal; get selected(): DateRange | D | null; set selected(value: DateRange | D | null); readonly selectedChange: EventEmitter; startDateAccessibleName: string | null; - _todayDate: number | null; + _todayDate: i0.WritableSignal; _updateActiveDate(event: MatCalendarUserEvent): void; readonly _userSelection: EventEmitter>; - _weekdays: { + _weekdays: i0.WritableSignal<{ long: string; narrow: string; id: number; - }[]; - _weeks: MatCalendarCell[][]; + }[]>; + _weeks: i0.WritableSignal[][]>; // (undocumented) static ɵcmp: i0.ɵɵComponentDeclaration, "mat-month-view", ["matMonthView"], { "activeDate": { "alias": "activeDate"; "required": false; }; "selected": { "alias": "selected"; "required": false; }; "minDate": { "alias": "minDate"; "required": false; }; "maxDate": { "alias": "maxDate"; "required": false; }; "dateFilter": { "alias": "dateFilter"; "required": false; }; "dateClass": { "alias": "dateClass"; "required": false; }; "comparisonStart": { "alias": "comparisonStart"; "required": false; }; "comparisonEnd": { "alias": "comparisonEnd"; "required": false; }; "startDateAccessibleName": { "alias": "startDateAccessibleName"; "required": false; }; "endDateAccessibleName": { "alias": "endDateAccessibleName"; "required": false; }; "activeDrag": { "alias": "activeDrag"; "required": false; }; }, { "selectedChange": "selectedChange"; "_userSelection": "_userSelection"; "dragStarted": "dragStarted"; "dragEnded": "dragEnded"; "activeDateChange": "activeDateChange"; }, never, never, true, never>; // (undocumented) @@ -751,10 +751,10 @@ export class MatMultiYearView implements AfterContentInit, OnDestroy { get selected(): DateRange | D | null; set selected(value: DateRange | D | null); readonly selectedChange: EventEmitter; - _selectedYear: number | null; - _todayYear: number; + _selectedYear: i0.WritableSignal; + _todayYear: i0.WritableSignal; _updateActiveDate(event: MatCalendarUserEvent): void; - _years: MatCalendarCell[][]; + _years: i0.WritableSignal[][]>; readonly yearSelected: EventEmitter; _yearSelected(event: MatCalendarUserEvent): void; // (undocumented) @@ -831,7 +831,7 @@ export class MatYearView implements AfterContentInit, OnDestroy { set maxDate(value: D | null); get minDate(): D | null; set minDate(value: D | null); - _months: MatCalendarCell[][]; + _months: i0.WritableSignal[][]>; readonly monthSelected: EventEmitter; _monthSelected(event: MatCalendarUserEvent): void; // (undocumented) @@ -841,10 +841,10 @@ export class MatYearView implements AfterContentInit, OnDestroy { get selected(): DateRange | D | null; set selected(value: DateRange | D | null); readonly selectedChange: EventEmitter; - _selectedMonth: number | null; - _todayMonth: number | null; + _selectedMonth: i0.WritableSignal; + _todayMonth: i0.WritableSignal; _updateActiveDate(event: MatCalendarUserEvent): void; - _yearLabel: string; + _yearLabel: i0.WritableSignal; // (undocumented) static ɵcmp: i0.ɵɵComponentDeclaration, "mat-year-view", ["matYearView"], { "activeDate": { "alias": "activeDate"; "required": false; }; "selected": { "alias": "selected"; "required": false; }; "minDate": { "alias": "minDate"; "required": false; }; "maxDate": { "alias": "maxDate"; "required": false; }; "dateFilter": { "alias": "dateFilter"; "required": false; }; "dateClass": { "alias": "dateClass"; "required": false; }; }, { "selectedChange": "selectedChange"; "monthSelected": "monthSelected"; "activeDateChange": "activeDateChange"; }, never, never, true, never>; // (undocumented) diff --git a/src/material/datepicker/calendar-body.ts b/src/material/datepicker/calendar-body.ts index 34af64d8faba..18a163974166 100644 --- a/src/material/datepicker/calendar-body.ts +++ b/src/material/datepicker/calendar-body.ts @@ -615,7 +615,9 @@ export class MatCalendarBody implements OnChanges, OnDestroy, AfterView const col = cell.getAttribute('data-mat-col'); if (row && col) { - return this.rows[parseInt(row)][parseInt(col)]; + // We need the optional read here, because this can + // fire too late when the user is navigating quickly. + return this.rows[parseInt(row)]?.[parseInt(col)] || null; } } diff --git a/src/material/datepicker/month-view.html b/src/material/datepicker/month-view.html index 538d85f91435..fa0c2750d94d 100644 --- a/src/material/datepicker/month-view.html +++ b/src/material/datepicker/month-view.html @@ -1,7 +1,7 @@ - @for (day of _weekdays; track day.id) { + @for (day of _weekdays(); track day.id) { implements AfterContentInit, OnChanges, OnDestroy { @ViewChild(MatCalendarBody) _matCalendarBody: MatCalendarBody; /** The label for this month (e.g. "January 2017"). */ - _monthLabel: string; + _monthLabel = signal(''); /** Grid of calendar cells representing the dates of the month. */ - _weeks: MatCalendarCell[][]; + _weeks = signal([]); /** The number of blank cells in the first row before the 1st of the month. */ - _firstWeekOffset: number; + _firstWeekOffset = signal(0); /** Start value of the currently-shown date range. */ - _rangeStart: number | null; + _rangeStart = signal(null); /** End value of the currently-shown date range. */ - _rangeEnd: number | null; + _rangeEnd = signal(null); /** Start value of the currently-shown comparison date range. */ - _comparisonRangeStart: number | null; + _comparisonRangeStart = signal(null); /** End value of the currently-shown comparison date range. */ - _comparisonRangeEnd: number | null; + _comparisonRangeEnd = signal(null); /** Start of the preview range. */ - _previewStart: number | null; + _previewStart = signal(null); /** End of the preview range. */ - _previewEnd: number | null; + _previewEnd = signal(null); /** Whether the user is currently selecting a range of dates. */ - _isRange: boolean; + _isRange = signal(false); /** The date of the month that today falls on. Null if today is in another month. */ - _todayDate: number | null; + _todayDate = signal(null); /** The names of the weekdays. */ - _weekdays: {long: string; narrow: string; id: number}[]; + _weekdays = signal<{long: string; narrow: string; id: number}[]>([]); constructor(...args: unknown[]); @@ -359,7 +360,7 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { return; case ESCAPE: // Abort the current range selection if the user presses escape mid-selection. - if (this._previewEnd != null && !hasModifierKey(event)) { + if (this._previewEnd() != null && !hasModifierKey(event)) { this._clearPreview(); // If a drag is in progress, cancel the drag without changing the // current selection. @@ -402,23 +403,26 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { /** Initializes this month view. */ _init() { this._setRanges(this.selected); - this._todayDate = this._getCellCompareValue(this._dateAdapter.today()); - this._monthLabel = this._dateFormats.display.monthLabel - ? this._dateAdapter.format(this.activeDate, this._dateFormats.display.monthLabel) - : this._dateAdapter - .getMonthNames('short') - [this._dateAdapter.getMonth(this.activeDate)].toLocaleUpperCase(); + this._todayDate.set(this._getCellCompareValue(this._dateAdapter.today())); + this._monthLabel.set( + this._dateFormats.display.monthLabel + ? this._dateAdapter.format(this.activeDate, this._dateFormats.display.monthLabel) + : this._dateAdapter + .getMonthNames('short') + [this._dateAdapter.getMonth(this.activeDate)].toLocaleUpperCase(), + ); let firstOfMonth = this._dateAdapter.createDate( this._dateAdapter.getYear(this.activeDate), this._dateAdapter.getMonth(this.activeDate), 1, ); - this._firstWeekOffset = + this._firstWeekOffset.set( (DAYS_PER_WEEK + this._dateAdapter.getDayOfWeek(firstOfMonth) - this._dateAdapter.getFirstDayOfWeek()) % - DAYS_PER_WEEK; + DAYS_PER_WEEK, + ); this._initWeekdays(); this._createWeekCells(); @@ -446,8 +450,8 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { this.selected as DateRange, event, ); - this._previewStart = this._getCellCompareValue(previewRange.start); - this._previewEnd = this._getCellCompareValue(previewRange.end); + this._previewStart.set(this._getCellCompareValue(previewRange.start)); + this._previewEnd.set(this._getCellCompareValue(previewRange.end)); if (this.activeDrag && value) { const dragRange = this._rangeStrategy.createDrag?.( @@ -458,16 +462,10 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { ); if (dragRange) { - this._previewStart = this._getCellCompareValue(dragRange.start); - this._previewEnd = this._getCellCompareValue(dragRange.end); + this._previewStart.set(this._getCellCompareValue(dragRange.start)); + this._previewEnd.set(this._getCellCompareValue(dragRange.end)); } } - - // Note that here we need to use `detectChanges`, rather than `markForCheck`, because - // the way `_focusActiveCell` is set up at the moment makes it fire at the wrong time - // when navigating one month back using the keyboard which will cause this handler - // to throw a "changed after checked" error when updating the preview state. - this._changeDetectorRef.detectChanges(); } } @@ -512,20 +510,20 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { const longWeekdays = this._dateAdapter.getDayOfWeekNames('long'); // Rotate the labels for days of the week based on the configured first day of the week. - let weekdays = longWeekdays.map((long, i) => { + const weekdays = longWeekdays.map((long, i) => { return {long, narrow: narrowWeekdays[i], id: uniqueIdCounter++}; }); - this._weekdays = weekdays.slice(firstDayOfWeek).concat(weekdays.slice(0, firstDayOfWeek)); + this._weekdays.set(weekdays.slice(firstDayOfWeek).concat(weekdays.slice(0, firstDayOfWeek))); } /** Creates MatCalendarCells for the dates in this month. */ private _createWeekCells() { const daysInMonth = this._dateAdapter.getNumDaysInMonth(this.activeDate); const dateNames = this._dateAdapter.getDateNames(); - this._weeks = [[]]; - for (let i = 0, cell = this._firstWeekOffset; i < daysInMonth; i++, cell++) { + const weeks: MatCalendarCell[][] = [[]]; + for (let i = 0, cell = this._firstWeekOffset(); i < daysInMonth; i++, cell++) { if (cell == DAYS_PER_WEEK) { - this._weeks.push([]); + weeks.push([]); cell = 0; } const date = this._dateAdapter.createDate( @@ -537,7 +535,7 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { const ariaLabel = this._dateAdapter.format(date, this._dateFormats.display.dateA11yLabel); const cellClasses = this.dateClass ? this.dateClass(date, 'month') : undefined; - this._weeks[this._weeks.length - 1].push( + weeks[weeks.length - 1].push( new MatCalendarCell( i + 1, dateNames[i], @@ -549,6 +547,7 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { ), ); } + this._weeks.set(weeks); } /** Date filter for the month */ @@ -603,16 +602,17 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { /** Sets the current range based on a model value. */ private _setRanges(selectedValue: DateRange | D | null) { if (selectedValue instanceof DateRange) { - this._rangeStart = this._getCellCompareValue(selectedValue.start); - this._rangeEnd = this._getCellCompareValue(selectedValue.end); - this._isRange = true; + this._rangeStart.set(this._getCellCompareValue(selectedValue.start)); + this._rangeEnd.set(this._getCellCompareValue(selectedValue.end)); + this._isRange.set(true); } else { - this._rangeStart = this._rangeEnd = this._getCellCompareValue(selectedValue); - this._isRange = false; + this._rangeStart.set(this._getCellCompareValue(selectedValue)); + this._rangeEnd.set(this._rangeStart()); + this._isRange.set(false); } - this._comparisonRangeStart = this._getCellCompareValue(this.comparisonStart); - this._comparisonRangeEnd = this._getCellCompareValue(this.comparisonEnd); + this._comparisonRangeStart.set(this._getCellCompareValue(this.comparisonStart)); + this._comparisonRangeEnd.set(this._getCellCompareValue(this.comparisonEnd)); } /** Gets whether a date can be selected in the month view. */ @@ -622,6 +622,7 @@ export class MatMonthView implements AfterContentInit, OnChanges, OnDestroy { /** Clears out preview state. */ private _clearPreview() { - this._previewStart = this._previewEnd = null; + this._previewStart.set(null); + this._previewEnd.set(null); } } diff --git a/src/material/datepicker/multi-year-view.html b/src/material/datepicker/multi-year-view.html index 84220c8fa95f..91a72ab51eb0 100644 --- a/src/material/datepicker/multi-year-view.html +++ b/src/material/datepicker/multi-year-view.html @@ -3,10 +3,10 @@ implements AfterContentInit, OnDestroy { @ViewChild(MatCalendarBody) _matCalendarBody: MatCalendarBody; /** Grid of calendar cells representing the currently displayed years. */ - _years: MatCalendarCell[][]; + _years = signal([]); /** The year that today falls on. */ - _todayYear: number; + _todayYear = signal(0); /** The year of the selected date. Null if the selected date is null. */ - _selectedYear: number | null; + _selectedYear = signal(null); constructor(...args: unknown[]); @@ -180,7 +181,7 @@ export class MatMultiYearView implements AfterContentInit, OnDestroy { /** Initializes this multi-year view. */ _init() { - this._todayYear = this._dateAdapter.getYear(this._dateAdapter.today()); + this._todayYear.set(this._dateAdapter.getYear(this._dateAdapter.today())); // We want a range years such that we maximize the number of // enabled dates visible at once. This prevents issues where the minimum year @@ -192,14 +193,15 @@ export class MatMultiYearView implements AfterContentInit, OnDestroy { const minYearOfPage = activeYear - getActiveOffset(this._dateAdapter, this.activeDate, this.minDate, this.maxDate); - this._years = []; + const years: MatCalendarCell[][] = []; for (let i = 0, row: number[] = []; i < yearsPerPage; i++) { row.push(minYearOfPage + i); if (row.length == yearsPerRow) { - this._years.push(row.map(year => this._createCellForYear(year))); + years.push(row.map(year => this._createCellForYear(year))); row = []; } } + this._years.set(years); this._changeDetectorRef.markForCheck(); } @@ -389,16 +391,16 @@ export class MatMultiYearView implements AfterContentInit, OnDestroy { /** Sets the currently-highlighted year based on a model value. */ private _setSelectedYear(value: DateRange | D | null) { - this._selectedYear = null; + this._selectedYear.set(null); if (value instanceof DateRange) { const displayValue = value.start || value.end; if (displayValue) { - this._selectedYear = this._dateAdapter.getYear(displayValue); + this._selectedYear.set(this._dateAdapter.getYear(displayValue)); } } else if (value) { - this._selectedYear = this._dateAdapter.getYear(value); + this._selectedYear.set(this._dateAdapter.getYear(value)); } } } diff --git a/src/material/datepicker/year-view.html b/src/material/datepicker/year-view.html index afb9821dd416..343cd814dfd6 100644 --- a/src/material/datepicker/year-view.html +++ b/src/material/datepicker/year-view.html @@ -3,11 +3,11 @@ implements AfterContentInit, OnDestroy { @ViewChild(MatCalendarBody) _matCalendarBody: MatCalendarBody; /** Grid of calendar cells representing the months of the year. */ - _months: MatCalendarCell[][]; + _months = signal([]); /** The label for this year (e.g. "2017"). */ - _yearLabel: string; + _yearLabel = signal(''); /** The month in this year that today falls on. Null if today is in a different year. */ - _todayMonth: number | null; + _todayMonth = signal(null); /** * The month in this year that the selected Date falls on. * Null if the selected Date is in a different year. */ - _selectedMonth: number | null; + _selectedMonth = signal(null); constructor(...args: unknown[]); @@ -296,16 +297,18 @@ export class MatYearView implements AfterContentInit, OnDestroy { /** Initializes this year view. */ _init() { this._setSelectedMonth(this.selected); - this._todayMonth = this._getMonthInCurrentYear(this._dateAdapter.today()); - this._yearLabel = this._dateAdapter.getYearName(this.activeDate); + this._todayMonth.set(this._getMonthInCurrentYear(this._dateAdapter.today())); + this._yearLabel.set(this._dateAdapter.getYearName(this.activeDate)); let monthNames = this._dateAdapter.getMonthNames('short'); // First row of months only contains 5 elements so we can fit the year label on the same row. - this._months = [ - [0, 1, 2, 3], - [4, 5, 6, 7], - [8, 9, 10, 11], - ].map(row => row.map(month => this._createCellForMonth(month, monthNames[month]))); + this._months.set( + [ + [0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + ].map(row => row.map(month => this._createCellForMonth(month, monthNames[month]))), + ); this._changeDetectorRef.markForCheck(); } @@ -435,10 +438,11 @@ export class MatYearView implements AfterContentInit, OnDestroy { /** Sets the currently-selected month based on a model value. */ private _setSelectedMonth(value: DateRange | D | null) { if (value instanceof DateRange) { - this._selectedMonth = - this._getMonthInCurrentYear(value.start) || this._getMonthInCurrentYear(value.end); + this._selectedMonth.set( + this._getMonthInCurrentYear(value.start) || this._getMonthInCurrentYear(value.end), + ); } else { - this._selectedMonth = this._getMonthInCurrentYear(value); + this._selectedMonth.set(this._getMonthInCurrentYear(value)); } } }
{{day.long}} @@ -11,16 +11,16 @@