mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(datepicker): align multi-year-view based on minDate and maxDate (#16018)
* feat(datepicker): align multi-year-view based on minDate and maxDate If minDate is set (and maxDate not set), let first page begin with minYear. If maxDate is set, let last page end with maxYear, and disable year(s) < minYear (if any) in first page. Fixes #10646 * add tests for feat(datepicker): align multi-year-view based on minDate and maxDate * refactor feat(datepicker): align multi-year-view based on minDate and maxDate * omit helper func from public api * fixup, resolve conflict refactor with private methods feat(datepicker): align multi-year-view based on minDate and maxDate * share func feat(datepicker): align multi-year-view based on minDate and maxDate * fix formatting nit
This commit is contained in:
@@ -19,6 +19,7 @@ describe('MatCalendarHeader', () => {
|
||||
declarations: [
|
||||
// Test components.
|
||||
StandardCalendar,
|
||||
CalendarWithMinMaxDate,
|
||||
],
|
||||
providers: [
|
||||
MatDatepickerIntl,
|
||||
@@ -148,7 +149,174 @@ describe('MatCalendarHeader', () => {
|
||||
expect(calendarInstance.activeDate).toEqual(new Date(2016, DEC, 31));
|
||||
expect(testComponent.selected).toBeFalsy('no date should be selected yet');
|
||||
});
|
||||
});
|
||||
|
||||
describe('calendar with minDate only', () => {
|
||||
let fixture: ComponentFixture<CalendarWithMinMaxDate>;
|
||||
let testComponent: CalendarWithMinMaxDate;
|
||||
let calendarElement: HTMLElement;
|
||||
let periodButton: HTMLButtonElement;
|
||||
let prevButton: HTMLButtonElement;
|
||||
let nextButton: HTMLButtonElement;
|
||||
let calendarInstance: MatCalendar<Date>;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CalendarWithMinMaxDate);
|
||||
fixture.detectChanges();
|
||||
|
||||
let calendarDebugElement = fixture.debugElement.query(By.directive(MatCalendar));
|
||||
calendarElement = calendarDebugElement.nativeElement;
|
||||
periodButton =
|
||||
calendarElement.querySelector('.mat-calendar-period-button') as HTMLButtonElement;
|
||||
prevButton =
|
||||
calendarElement.querySelector('.mat-calendar-previous-button') as HTMLButtonElement;
|
||||
nextButton =
|
||||
calendarElement.querySelector('.mat-calendar-next-button') as HTMLButtonElement;
|
||||
calendarInstance = calendarDebugElement.componentInstance;
|
||||
testComponent = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should start the first page with minDate', () => {
|
||||
testComponent.minDate = new Date(2010, JAN, 1);
|
||||
periodButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance.currentView).toBe('multi-year');
|
||||
expect(periodButton.innerText.trim()).toEqual('2010 \u2013 2033');
|
||||
});
|
||||
|
||||
|
||||
it('should disable the page before the one showing minDate', () => {
|
||||
testComponent.minDate = new Date(2010, JAN, 1);
|
||||
periodButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance.currentView).toBe('multi-year');
|
||||
expect(prevButton.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should enable the page after the one showing minDate', () => {
|
||||
testComponent.minDate = new Date(2010, JAN, 1);
|
||||
periodButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance.currentView).toBe('multi-year');
|
||||
expect(nextButton.disabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calendar with maxDate only', () => {
|
||||
let fixture: ComponentFixture<CalendarWithMinMaxDate>;
|
||||
let testComponent: CalendarWithMinMaxDate;
|
||||
let calendarElement: HTMLElement;
|
||||
let periodButton: HTMLButtonElement;
|
||||
let prevButton: HTMLButtonElement;
|
||||
let nextButton: HTMLButtonElement;
|
||||
let calendarInstance: MatCalendar<Date>;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CalendarWithMinMaxDate);
|
||||
fixture.detectChanges();
|
||||
|
||||
let calendarDebugElement = fixture.debugElement.query(By.directive(MatCalendar));
|
||||
calendarElement = calendarDebugElement.nativeElement;
|
||||
periodButton =
|
||||
calendarElement.querySelector('.mat-calendar-period-button') as HTMLButtonElement;
|
||||
prevButton =
|
||||
calendarElement.querySelector('.mat-calendar-previous-button') as HTMLButtonElement;
|
||||
nextButton =
|
||||
calendarElement.querySelector('.mat-calendar-next-button') as HTMLButtonElement;
|
||||
calendarInstance = calendarDebugElement.componentInstance;
|
||||
testComponent = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should end the last page with maxDate', () => {
|
||||
testComponent.maxDate = new Date(2020, JAN, 1);
|
||||
periodButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance.currentView).toBe('multi-year');
|
||||
expect(periodButton.innerText.trim()).toEqual('1997 \u2013 2020');
|
||||
});
|
||||
|
||||
it('should disable the page after the one showing maxDate', () => {
|
||||
testComponent.maxDate = new Date(2020, JAN, 1);
|
||||
periodButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance.currentView).toBe('multi-year');
|
||||
expect(nextButton.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should enable the page before the one showing maxDate', () => {
|
||||
testComponent.maxDate = new Date(2020, JAN, 1);
|
||||
periodButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance.currentView).toBe('multi-year');
|
||||
expect(prevButton.disabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calendar with minDate and maxDate', () => {
|
||||
let fixture: ComponentFixture<CalendarWithMinMaxDate>;
|
||||
let testComponent: CalendarWithMinMaxDate;
|
||||
let calendarElement: HTMLElement;
|
||||
let periodButton: HTMLButtonElement;
|
||||
let prevButton: HTMLButtonElement;
|
||||
let nextButton: HTMLButtonElement;
|
||||
let calendarInstance: MatCalendar<Date>;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CalendarWithMinMaxDate);
|
||||
fixture.detectChanges();
|
||||
|
||||
let calendarDebugElement = fixture.debugElement.query(By.directive(MatCalendar));
|
||||
calendarElement = calendarDebugElement.nativeElement;
|
||||
periodButton =
|
||||
calendarElement.querySelector('.mat-calendar-period-button') as HTMLButtonElement;
|
||||
prevButton =
|
||||
calendarElement.querySelector('.mat-calendar-previous-button') as HTMLButtonElement;
|
||||
nextButton =
|
||||
calendarElement.querySelector('.mat-calendar-next-button') as HTMLButtonElement;
|
||||
calendarInstance = calendarDebugElement.componentInstance;
|
||||
testComponent = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should end the last page with maxDate', () => {
|
||||
testComponent.minDate = new Date(1993, JAN, 1);
|
||||
testComponent.maxDate = new Date(2020, JAN, 1);
|
||||
periodButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance.currentView).toBe('multi-year');
|
||||
expect(periodButton.innerText.trim()).toEqual('1997 \u2013 2020');
|
||||
});
|
||||
|
||||
it('should disable the page after the one showing maxDate', () => {
|
||||
testComponent.minDate = new Date(1993, JAN, 1);
|
||||
testComponent.maxDate = new Date(2020, JAN, 1);
|
||||
periodButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance.currentView).toBe('multi-year');
|
||||
expect(nextButton.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should disable the page before the one showing minDate', () => {
|
||||
testComponent.minDate = new Date(1993, JAN, 1);
|
||||
testComponent.maxDate = new Date(2020, JAN, 1);
|
||||
periodButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance.currentView).toBe('multi-year');
|
||||
|
||||
prevButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance.activeDate).toEqual(new Date(2018 - yearsPerPage, JAN, 1));
|
||||
expect(prevButton.disabled).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -167,3 +335,18 @@ class StandardCalendar {
|
||||
selectedMonth: Date;
|
||||
startDate = new Date(2017, JAN, 31);
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-calendar
|
||||
[startAt]="startAt"
|
||||
[minDate]="minDate"
|
||||
[maxDate]="maxDate">
|
||||
</mat-calendar>
|
||||
`
|
||||
})
|
||||
class CalendarWithMinMaxDate {
|
||||
startAt = new Date(2018, JAN, 1);
|
||||
minDate: Date | null;
|
||||
maxDate: Date | null;
|
||||
}
|
||||
|
||||
@@ -27,12 +27,17 @@ import {
|
||||
} from '@angular/core';
|
||||
import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats} from '@angular/material/core';
|
||||
import {Subject, Subscription} from 'rxjs';
|
||||
import {MatCalendarCellCssClasses} from './calendar-body';
|
||||
import {createMissingDateImplError} from './datepicker-errors';
|
||||
import {MatDatepickerIntl} from './datepicker-intl';
|
||||
import {MatMonthView} from './month-view';
|
||||
import {MatMultiYearView, yearsPerPage} from './multi-year-view';
|
||||
import {
|
||||
getActiveOffset,
|
||||
isSameMultiYearView,
|
||||
MatMultiYearView,
|
||||
yearsPerPage
|
||||
} from './multi-year-view';
|
||||
import {MatYearView} from './year-view';
|
||||
import {MatCalendarCellCssClasses} from './calendar-body';
|
||||
|
||||
/**
|
||||
* Possible views for the calendar.
|
||||
@@ -69,12 +74,15 @@ export class MatCalendarHeader<D> {
|
||||
if (this.calendar.currentView == 'year') {
|
||||
return this._dateAdapter.getYearName(this.calendar.activeDate);
|
||||
}
|
||||
|
||||
// The offset from the active year to the "slot" for the starting year is the
|
||||
// *actual* first rendered year in the multi-year view, and the last year is
|
||||
// just yearsPerPage - 1 away.
|
||||
const activeYear = this._dateAdapter.getYear(this.calendar.activeDate);
|
||||
const firstYearInView = this._dateAdapter.getYearName(
|
||||
this._dateAdapter.createDate(activeYear - activeYear % 24, 0, 1));
|
||||
const lastYearInView = this._dateAdapter.getYearName(
|
||||
this._dateAdapter.createDate(activeYear + yearsPerPage - 1 - activeYear % 24, 0, 1));
|
||||
return `${firstYearInView} \u2013 ${lastYearInView}`;
|
||||
const minYearOfPage = activeYear - getActiveOffset(
|
||||
this._dateAdapter, this.calendar.activeDate, this.calendar.minDate, this.calendar.maxDate);
|
||||
const maxYearOfPage = minYearOfPage + yearsPerPage - 1;
|
||||
return `${minYearOfPage} \u2013 ${maxYearOfPage}`;
|
||||
}
|
||||
|
||||
get periodButtonLabel(): string {
|
||||
@@ -149,8 +157,8 @@ export class MatCalendarHeader<D> {
|
||||
return this._dateAdapter.getYear(date1) == this._dateAdapter.getYear(date2);
|
||||
}
|
||||
// Otherwise we are in 'multi-year' view.
|
||||
return Math.floor(this._dateAdapter.getYear(date1) / yearsPerPage) ==
|
||||
Math.floor(this._dateAdapter.getYear(date2) / yearsPerPage);
|
||||
return isSameMultiYearView(
|
||||
this._dateAdapter, date1, date2, this.calendar.minDate, this.calendar.maxDate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ describe('MatMultiYearView', () => {
|
||||
// Test components.
|
||||
StandardMultiYearView,
|
||||
MultiYearViewWithDateFilter,
|
||||
MultiYearViewWithMinMaxDate,
|
||||
],
|
||||
providers: [
|
||||
{provide: Directionality, useFactory: () => dir = {value: 'ltr'}}
|
||||
@@ -247,8 +248,87 @@ describe('MatMultiYearView', () => {
|
||||
expect(cells[1].classList).toContain('mat-calendar-body-disabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('multi year view with minDate only', () => {
|
||||
let fixture: ComponentFixture<MultiYearViewWithMinMaxDate>;
|
||||
let testComponent: MultiYearViewWithMinMaxDate;
|
||||
let multiYearViewNativeElement: Element;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MultiYearViewWithMinMaxDate);
|
||||
|
||||
const multiYearViewDebugElement = fixture.debugElement.query(By.directive(MatMultiYearView));
|
||||
multiYearViewNativeElement = multiYearViewDebugElement.nativeElement;
|
||||
testComponent = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should begin first page with minDate', () => {
|
||||
testComponent.minDate = new Date(2014, JAN, 1);
|
||||
testComponent.maxDate = null;
|
||||
fixture.detectChanges();
|
||||
|
||||
const cells = multiYearViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
|
||||
expect((cells[0] as HTMLElement).innerText.trim()).toBe('2014');
|
||||
});
|
||||
});
|
||||
|
||||
describe('multi year view with maxDate only', () => {
|
||||
let fixture: ComponentFixture<MultiYearViewWithMinMaxDate>;
|
||||
let testComponent: MultiYearViewWithMinMaxDate;
|
||||
let multiYearViewNativeElement: Element;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MultiYearViewWithMinMaxDate);
|
||||
|
||||
const multiYearViewDebugElement = fixture.debugElement.query(By.directive(MatMultiYearView));
|
||||
multiYearViewNativeElement = multiYearViewDebugElement.nativeElement;
|
||||
testComponent = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should end last page with maxDate', () => {
|
||||
testComponent.minDate = null;
|
||||
testComponent.maxDate = new Date(2020, JAN, 1);
|
||||
fixture.detectChanges();
|
||||
|
||||
const cells = multiYearViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
|
||||
expect((cells[cells.length - 1] as HTMLElement).innerText.trim()).toBe('2020');
|
||||
});
|
||||
});
|
||||
|
||||
describe('multi year view with minDate and maxDate', () => {
|
||||
let fixture: ComponentFixture<MultiYearViewWithMinMaxDate>;
|
||||
let testComponent: MultiYearViewWithMinMaxDate;
|
||||
let multiYearViewNativeElement: Element;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MultiYearViewWithMinMaxDate);
|
||||
|
||||
const multiYearViewDebugElement = fixture.debugElement.query(By.directive(MatMultiYearView));
|
||||
multiYearViewNativeElement = multiYearViewDebugElement.nativeElement;
|
||||
testComponent = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should end last page with maxDate', () => {
|
||||
testComponent.minDate = new Date(2006, JAN, 1);
|
||||
testComponent.maxDate = new Date (2020, JAN, 1);
|
||||
fixture.detectChanges();
|
||||
|
||||
const cells = multiYearViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
|
||||
expect((cells[cells.length - 1] as HTMLElement).innerText.trim()).toBe('2020');
|
||||
});
|
||||
|
||||
it('should disable dates before minDate', () => {
|
||||
testComponent.minDate = new Date(2006, JAN, 1);
|
||||
testComponent.maxDate = new Date (2020, JAN, 1);
|
||||
fixture.detectChanges();
|
||||
|
||||
const cells = multiYearViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
|
||||
expect(cells[0].classList).toContain('mat-calendar-body-disabled');
|
||||
expect(cells[8].classList).toContain('mat-calendar-body-disabled');
|
||||
expect(cells[9].classList).not.toContain('mat-calendar-body-disabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@@ -265,7 +345,8 @@ class StandardMultiYearView {
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-multi-year-view [activeDate]="activeDate" [dateFilter]="dateFilter"></mat-multi-year-view>
|
||||
<mat-multi-year-view [(activeDate)]="activeDate" [dateFilter]="dateFilter">
|
||||
</mat-multi-year-view>
|
||||
`
|
||||
})
|
||||
class MultiYearViewWithDateFilter {
|
||||
@@ -274,3 +355,15 @@ class MultiYearViewWithDateFilter {
|
||||
return date.getFullYear() !== 2017;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-multi-year-view [(activeDate)]="activeDate" [minDate]="minDate" [maxDate]="maxDate">
|
||||
</mat-multi-year-view>
|
||||
`
|
||||
})
|
||||
class MultiYearViewWithMinMaxDate {
|
||||
activeDate = new Date(2019, JAN, 1);
|
||||
minDate: Date | null;
|
||||
maxDate: Date | null;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ export const yearsPerPage = 24;
|
||||
|
||||
export const yearsPerRow = 4;
|
||||
|
||||
|
||||
/**
|
||||
* An internal component used to display a year selector in the datepicker.
|
||||
* @docs-private
|
||||
@@ -61,8 +60,9 @@ export class MatMultiYearView<D> implements AfterContentInit {
|
||||
const validDate =
|
||||
this._getValidDateOrNull(this._dateAdapter.deserialize(value)) || this._dateAdapter.today();
|
||||
this._activeDate = this._dateAdapter.clampDate(validDate, this.minDate, this.maxDate);
|
||||
if (Math.floor(this._dateAdapter.getYear(oldActiveDate) / yearsPerPage) !=
|
||||
Math.floor(this._dateAdapter.getYear(this._activeDate) / yearsPerPage)) {
|
||||
|
||||
if (!isSameMultiYearView(
|
||||
this._dateAdapter, oldActiveDate, this._activeDate, this.minDate, this.maxDate)) {
|
||||
this._init();
|
||||
}
|
||||
}
|
||||
@@ -134,11 +134,20 @@ export class MatMultiYearView<D> implements AfterContentInit {
|
||||
/** Initializes this multi-year view. */
|
||||
_init() {
|
||||
this._todayYear = this._dateAdapter.getYear(this._dateAdapter.today());
|
||||
let activeYear = this._dateAdapter.getYear(this._activeDate);
|
||||
let activeOffset = activeYear % yearsPerPage;
|
||||
|
||||
// We want a range years such that we maximize the number of
|
||||
// enabled dates visible at once. This prevents issues where the minimum year
|
||||
// is the last item of a page OR the maximum year is the first item of a page.
|
||||
|
||||
// The offset from the active year to the "slot" for the starting year is the
|
||||
// *actual* first rendered year in the multi-year view.
|
||||
const activeYear = this._dateAdapter.getYear(this._activeDate);
|
||||
const minYearOfPage = activeYear - getActiveOffset(
|
||||
this._dateAdapter, this.activeDate, this.minDate, this.maxDate);
|
||||
|
||||
this._years = [];
|
||||
for (let i = 0, row: number[] = []; i < yearsPerPage; i++) {
|
||||
row.push(activeYear - activeOffset + i);
|
||||
row.push(minYearOfPage + i);
|
||||
if (row.length == yearsPerRow) {
|
||||
this._years.push(row.map(year => this._createCellForYear(year)));
|
||||
row = [];
|
||||
@@ -159,10 +168,6 @@ export class MatMultiYearView<D> implements AfterContentInit {
|
||||
|
||||
/** Handles keydown events on the calendar body when calendar is in multi-year view. */
|
||||
_handleCalendarBodyKeydown(event: KeyboardEvent): void {
|
||||
// TODO(mmalerba): We currently allow keyboard navigation to disabled dates, but just prevent
|
||||
// disabled ones from being selected. This may not be ideal, we should look into whether
|
||||
// navigation should skip over disabled dates, and if so, how to implement that efficiently.
|
||||
|
||||
const oldActiveDate = this._activeDate;
|
||||
const isRtl = this._isRtl();
|
||||
|
||||
@@ -181,11 +186,12 @@ export class MatMultiYearView<D> implements AfterContentInit {
|
||||
break;
|
||||
case HOME:
|
||||
this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate,
|
||||
-this._dateAdapter.getYear(this._activeDate) % yearsPerPage);
|
||||
-getActiveOffset(this._dateAdapter, this.activeDate, this.minDate, this.maxDate));
|
||||
break;
|
||||
case END:
|
||||
this.activeDate = this._dateAdapter.addCalendarYears(this._activeDate,
|
||||
yearsPerPage - this._dateAdapter.getYear(this._activeDate) % yearsPerPage - 1);
|
||||
yearsPerPage - getActiveOffset(
|
||||
this._dateAdapter, this.activeDate, this.minDate, this.maxDate) - 1);
|
||||
break;
|
||||
case PAGE_UP:
|
||||
this.activeDate =
|
||||
@@ -205,7 +211,6 @@ export class MatMultiYearView<D> implements AfterContentInit {
|
||||
// Don't prevent default or focus active cell on keys that we don't explicitly handle.
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._dateAdapter.compareDate(oldActiveDate, this.activeDate)) {
|
||||
this.activeDateChange.emit(this.activeDate);
|
||||
}
|
||||
@@ -216,7 +221,7 @@ export class MatMultiYearView<D> implements AfterContentInit {
|
||||
}
|
||||
|
||||
_getActiveCell(): number {
|
||||
return this._dateAdapter.getYear(this.activeDate) % yearsPerPage;
|
||||
return getActiveOffset(this._dateAdapter, this.activeDate, this.minDate, this.maxDate);
|
||||
}
|
||||
|
||||
/** Focuses the active cell after the microtask queue is empty. */
|
||||
@@ -270,3 +275,45 @@ export class MatMultiYearView<D> implements AfterContentInit {
|
||||
return this._dir && this._dir.value === 'rtl';
|
||||
}
|
||||
}
|
||||
|
||||
export function isSameMultiYearView<D>(
|
||||
dateAdapter: DateAdapter<D>, date1: D, date2: D, minDate: D | null, maxDate: D | null): boolean {
|
||||
const year1 = dateAdapter.getYear(date1);
|
||||
const year2 = dateAdapter.getYear(date2);
|
||||
const startingYear = getStartingYear(dateAdapter, minDate, maxDate);
|
||||
return Math.floor((year1 - startingYear) / yearsPerPage) ===
|
||||
Math.floor((year2 - startingYear) / yearsPerPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* When the multi-year view is first opened, the active year will be in view.
|
||||
* So we compute how many years are between the active year and the *slot* where our
|
||||
* "startingYear" will render when paged into view.
|
||||
*/
|
||||
export function getActiveOffset<D>(
|
||||
dateAdapter: DateAdapter<D>, activeDate: D, minDate: D | null, maxDate: D | null): number {
|
||||
const activeYear = dateAdapter.getYear(activeDate);
|
||||
return euclideanModulo((activeYear - getStartingYear(dateAdapter, minDate, maxDate)),
|
||||
yearsPerPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* We pick a "starting" year such that either the maximum year would be at the end
|
||||
* or the minimum year would be at the beginning of a page.
|
||||
*/
|
||||
function getStartingYear<D>(
|
||||
dateAdapter: DateAdapter<D>, minDate: D | null, maxDate: D | null): number {
|
||||
let startingYear = 0;
|
||||
if (maxDate) {
|
||||
const maxYear = dateAdapter.getYear(maxDate);
|
||||
startingYear = maxYear - yearsPerPage + 1;
|
||||
} else if (minDate) {
|
||||
startingYear = dateAdapter.getYear(minDate);
|
||||
}
|
||||
return startingYear;
|
||||
}
|
||||
|
||||
/** Gets remainder that is non-negative, even if first number is negative */
|
||||
function euclideanModulo (a: number, b: number): number {
|
||||
return (a % b + b) % b;
|
||||
}
|
||||
|
||||
@@ -16,4 +16,4 @@ export * from './datepicker-intl';
|
||||
export * from './datepicker-toggle';
|
||||
export * from './month-view';
|
||||
export * from './year-view';
|
||||
export * from './multi-year-view';
|
||||
export {MatMultiYearView, yearsPerPage, yearsPerRow} from './multi-year-view';
|
||||
|
||||
Reference in New Issue
Block a user