fix(datepicker): add max/min filter to multi year and year views (#9727)

* add max/min filter to multi year and year views

* address @mmalerba's comments

* fix and tighten up logic

* possibility to have a null filter in month view
This commit is contained in:
Juliano
2018-02-06 15:35:30 -02:00
committed by mmalerba
parent c2e108e0bf
commit d062ccb6ae
5 changed files with 125 additions and 26 deletions
+9 -3
View File
@@ -26,7 +26,9 @@
*ngSwitchCase="'month'"
[activeDate]="_activeDate"
[selected]="selected"
[dateFilter]="_dateFilterForViews"
[dateFilter]="dateFilter"
[maxDate]="maxDate"
[minDate]="minDate"
(selectedChange)="_dateSelected($event)"
(_userSelection)="_userSelected()">
</mat-month-view>
@@ -35,7 +37,9 @@
*ngSwitchCase="'year'"
[activeDate]="_activeDate"
[selected]="selected"
[dateFilter]="_dateFilterForViews"
[dateFilter]="dateFilter"
[maxDate]="maxDate"
[minDate]="minDate"
(monthSelected)="_monthSelectedInYearView($event)"
(selectedChange)="_goToDateInView($event, 'month')">
</mat-year-view>
@@ -44,7 +48,9 @@
*ngSwitchCase="'multi-year'"
[activeDate]="_activeDate"
[selected]="selected"
[dateFilter]="_dateFilterForViews"
[dateFilter]="dateFilter"
[maxDate]="maxDate"
[minDate]="minDate"
(yearSelected)="_yearSelectedInMultiYearView($event)"
(selectedChange)="_goToDateInView($event, 'year')">
</mat-multi-year-view>
+1 -9
View File
@@ -131,14 +131,6 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
/** Reference to the current multi-year view component. */
@ViewChild(MatMultiYearView) multiYearView: MatMultiYearView<D>;
/** Date filter for the month, year, and multi-year views. */
_dateFilterForViews = (date: D) => {
return !!date &&
(!this.dateFilter || this.dateFilter(date)) &&
(!this.minDate || this._dateAdapter.compareDate(date, this.minDate) >= 0) &&
(!this.maxDate || this._dateAdapter.compareDate(date, this.maxDate) <= 0);
}
/**
* The current active date. This determines which time period is shown and which date is
* highlighted when using keyboard navigation.
@@ -368,7 +360,7 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
this._dateAdapter.addCalendarMonths(this._activeDate, 1);
break;
case ENTER:
if (this._dateFilterForViews(this._activeDate)) {
if (!this.dateFilter || this.dateFilter(this._activeDate)) {
this._dateSelected(this._activeDate);
this._userSelected();
// Prevent unexpected default actions such as form submission.
+31 -8
View File
@@ -64,6 +64,22 @@ export class MatMonthView<D> implements AfterContentInit {
}
private _selected: D | null;
/** The minimum selectable date. */
@Input()
get minDate(): D | null { return this._minDate; }
set minDate(value: D | null) {
this._minDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
}
private _minDate: D | null;
/** The maximum selectable date. */
@Input()
get maxDate(): D | null { return this._maxDate; }
set maxDate(value: D | null) {
this._maxDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
}
private _maxDate: D | null;
/** A function used to filter which dates are selectable. */
@Input() dateFilter: (date: D) => boolean;
@@ -154,25 +170,32 @@ export class MatMonthView<D> implements AfterContentInit {
/** Creates MatCalendarCells for the dates in this month. */
private _createWeekCells() {
let daysInMonth = this._dateAdapter.getNumDaysInMonth(this.activeDate);
let dateNames = this._dateAdapter.getDateNames();
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++) {
if (cell == DAYS_PER_WEEK) {
this._weeks.push([]);
cell = 0;
}
let date = this._dateAdapter.createDate(
this._dateAdapter.getYear(this.activeDate),
this._dateAdapter.getMonth(this.activeDate), i + 1);
let enabled = !this.dateFilter ||
this.dateFilter(date);
let ariaLabel = this._dateAdapter.format(date, this._dateFormats.display.dateA11yLabel);
const date = this._dateAdapter.createDate(
this._dateAdapter.getYear(this.activeDate),
this._dateAdapter.getMonth(this.activeDate), i + 1);
const enabled = this._shouldEnableDate(date);
const ariaLabel = this._dateAdapter.format(date, this._dateFormats.display.dateA11yLabel);
this._weeks[this._weeks.length - 1]
.push(new MatCalendarCell(i + 1, dateNames[i], ariaLabel, enabled));
}
}
/** Date filter for the month */
private _shouldEnableDate(date: D): boolean {
return !!date &&
(!this.dateFilter || this.dateFilter(date)) &&
(!this.minDate || this._dateAdapter.compareDate(date, this.minDate) >= 0) &&
(!this.maxDate || this._dateAdapter.compareDate(date, this.maxDate) <= 0);
}
/**
* Gets the date in this month that the given Date falls on.
* Returns null if the given Date is in another month.
+26 -2
View File
@@ -64,6 +64,22 @@ export class MatMultiYearView<D> implements AfterContentInit {
}
private _selected: D | null;
/** The minimum selectable date. */
@Input()
get minDate(): D | null { return this._minDate; }
set minDate(value: D | null) {
this._minDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
}
private _minDate: D | null;
/** The maximum selectable date. */
@Input()
get maxDate(): D | null { return this._maxDate; }
set maxDate(value: D | null) {
this._maxDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
}
private _maxDate: D | null;
/** A function used to filter which dates are selectable. */
@Input() dateFilter: (date: D) => boolean;
@@ -128,11 +144,19 @@ export class MatMultiYearView<D> implements AfterContentInit {
/** Creates an MatCalendarCell for the given year. */
private _createCellForYear(year: number) {
let yearName = this._dateAdapter.getYearName(this._dateAdapter.createDate(year, 0, 1));
return new MatCalendarCell(year, yearName, yearName, this._isYearEnabled(year));
return new MatCalendarCell(year, yearName, yearName, this._shouldEnableYear(year));
}
/** Whether the given year is enabled. */
private _isYearEnabled(year: number) {
private _shouldEnableYear(year: number) {
// disable if the year is greater than maxDate lower than minDate
if (year === undefined || year === null ||
(this.maxDate && year > this._dateAdapter.getYear(this.maxDate)) ||
(this.minDate && year < this._dateAdapter.getYear(this.minDate))) {
return false;
}
// enable if it reaches here and there's no filter defined
if (!this.dateFilter) {
return true;
}
+58 -4
View File
@@ -59,6 +59,22 @@ export class MatYearView<D> implements AfterContentInit {
}
private _selected: D | null;
/** The minimum selectable date. */
@Input()
get minDate(): D | null { return this._minDate; }
set minDate(value: D | null) {
this._minDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
}
private _minDate: D | null;
/** The maximum selectable date. */
@Input()
get maxDate(): D | null { return this._maxDate; }
set maxDate(value: D | null) {
this._maxDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
}
private _maxDate: D | null;
/** A function used to filter which dates are selectable. */
@Input() dateFilter: (date: D) => boolean;
@@ -142,17 +158,25 @@ export class MatYearView<D> implements AfterContentInit {
this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), month, 1),
this._dateFormats.display.monthYearA11yLabel);
return new MatCalendarCell(
month, monthName.toLocaleUpperCase(), ariaLabel, this._isMonthEnabled(month));
month, monthName.toLocaleUpperCase(), ariaLabel, this._shouldEnableMonth(month));
}
/** Whether the given month is enabled. */
private _isMonthEnabled(month: number) {
private _shouldEnableMonth(month: number) {
const activeYear = this._dateAdapter.getYear(this.activeDate);
if (month === undefined || month === null ||
this._isYearAndMonthAfterMaxDate(activeYear, month) ||
this._isYearAndMonthBeforeMinDate(activeYear, month)) {
return false;
}
if (!this.dateFilter) {
return true;
}
let firstOfMonth = this._dateAdapter.createDate(
this._dateAdapter.getYear(this.activeDate), month, 1);
const firstOfMonth = this._dateAdapter.createDate(activeYear, month, 1);
// If any date in the month is enabled count the month as enabled.
for (let date = firstOfMonth; this._dateAdapter.getMonth(date) == month;
@@ -165,6 +189,36 @@ export class MatYearView<D> implements AfterContentInit {
return false;
}
/**
* Tests whether the combination month/year is after this.maxDate, considering
* just the month and year of this.maxDate
*/
private _isYearAndMonthAfterMaxDate(year: number, month: number) {
if (this.maxDate) {
const maxYear = this._dateAdapter.getYear(this.maxDate);
const maxMonth = this._dateAdapter.getMonth(this.maxDate);
return year > maxYear || (year === maxYear && month > maxMonth);
}
return false;
}
/**
* Tests whether the combination month/year is before this.minDate, considering
* just the month and year of this.minDate
*/
private _isYearAndMonthBeforeMinDate(year: number, month: number) {
if (this.minDate) {
const minYear = this._dateAdapter.getYear(this.minDate);
const minMonth = this._dateAdapter.getMonth(this.minDate);
return year < minYear || (year === minYear && month < minMonth);
}
return false;
}
/**
* @param obj The object to check.
* @returns The given object if it is both a date instance and valid, otherwise null.