mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(datepicker): allow custom classes to be applied to individual dates (#13971)
Adds the `dateClass` function which allows consumers to apply custom CSS classes to specific dates. This is useful for highlighting specific dates like a holiday. Fixes #13943.
This commit is contained in:
committed by
vivian-hu
parent
69ffd3306e
commit
4be1b06edd
@@ -30,6 +30,7 @@
|
||||
<td *ngFor="let item of row; let colIndex = index"
|
||||
role="gridcell"
|
||||
class="mat-calendar-body-cell"
|
||||
[ngClass]="item.cssClasses"
|
||||
[tabindex]="_isActiveCell(rowIndex, colIndex) ? 0 : -1"
|
||||
[class.mat-calendar-body-disabled]="!item.enabled"
|
||||
[class.mat-calendar-body-active]="_isActiveCell(rowIndex, colIndex)"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {Component} from '@angular/core';
|
||||
import {MatCalendarBody, MatCalendarCell} from './calendar-body';
|
||||
import {MatCalendarBody, MatCalendarCell, MatCalendarCellCssClasses} from './calendar-body';
|
||||
import {By} from '@angular/platform-browser';
|
||||
|
||||
|
||||
@@ -98,6 +98,14 @@ describe('MatCalendarBody', () => {
|
||||
expect((cellEls[10] as HTMLElement).innerText.trim()).toBe('11');
|
||||
expect(cellEls[10].classList).toContain('mat-calendar-body-active');
|
||||
});
|
||||
|
||||
it('should set a class on even dates', () => {
|
||||
expect((cellEls[0] as HTMLElement).innerText.trim()).toBe('1');
|
||||
expect((cellEls[1] as HTMLElement).innerText.trim()).toBe('2');
|
||||
expect(cellEls[0].classList).not.toContain('even');
|
||||
expect(cellEls[1].classList).toContain('even');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -117,7 +125,9 @@ describe('MatCalendarBody', () => {
|
||||
})
|
||||
class StandardCalendarBody {
|
||||
label = 'Jan 2017';
|
||||
rows = [[1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14]].map(r => r.map(createCell));
|
||||
rows = [[1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14]].map(row => {
|
||||
return row.map(cell => createCell(cell, cell % 2 === 0 ? 'even' : undefined));
|
||||
});
|
||||
todayValue = 3;
|
||||
selectedValue = 4;
|
||||
labelMinRequiredCells = 3;
|
||||
@@ -128,6 +138,6 @@ class StandardCalendarBody {
|
||||
}
|
||||
}
|
||||
|
||||
function createCell(value: number) {
|
||||
return new MatCalendarCell(value, `${value}`, `${value}-label`, true);
|
||||
function createCell(value: number, cellClasses?: MatCalendarCellCssClasses) {
|
||||
return new MatCalendarCell(value, `${value}`, `${value}-label`, true, cellClasses);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,11 @@ import {
|
||||
} from '@angular/core';
|
||||
import {take} from 'rxjs/operators';
|
||||
|
||||
/**
|
||||
* Extra CSS classes that can be associated with a calendar cell.
|
||||
*/
|
||||
export type MatCalendarCellCssClasses = string | string[] | Set<string> | {[key: string]: any};
|
||||
|
||||
/**
|
||||
* An internal class that represents the data corresponding to a single calendar cell.
|
||||
* @docs-private
|
||||
@@ -26,7 +31,8 @@ export class MatCalendarCell {
|
||||
constructor(public value: number,
|
||||
public displayValue: string,
|
||||
public ariaLabel: string,
|
||||
public enabled: boolean) {}
|
||||
public enabled: boolean,
|
||||
public cssClasses?: MatCalendarCellCssClasses) {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
[dateFilter]="dateFilter"
|
||||
[maxDate]="maxDate"
|
||||
[minDate]="minDate"
|
||||
[dateClass]="dateClass"
|
||||
(selectedChange)="_dateSelected($event)"
|
||||
(_userSelection)="_userSelected()">
|
||||
</mat-month-view>
|
||||
|
||||
@@ -32,6 +32,7 @@ import {MatDatepickerIntl} from './datepicker-intl';
|
||||
import {MatMonthView} from './month-view';
|
||||
import {MatMultiYearView, yearsPerPage} from './multi-year-view';
|
||||
import {MatYearView} from './year-view';
|
||||
import {MatCalendarCellCssClasses} from './calendar-body';
|
||||
|
||||
/**
|
||||
* Possible views for the calendar.
|
||||
@@ -220,9 +221,12 @@ export class MatCalendar<D> implements AfterContentInit, AfterViewChecked, OnDes
|
||||
}
|
||||
private _maxDate: D | null;
|
||||
|
||||
/** A function used to filter which dates are selectable. */
|
||||
/** Function used to filter which dates are selectable. */
|
||||
@Input() dateFilter: (date: D) => boolean;
|
||||
|
||||
/** Function that can be used to add custom CSS classes to dates. */
|
||||
@Input() dateClass: (date: D) => MatCalendarCellCssClasses;
|
||||
|
||||
/** Emits when the currently selected date changes. */
|
||||
@Output() readonly selectedChange: EventEmitter<D> = new EventEmitter<D>();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
[dateFilter]="datepicker._dateFilter"
|
||||
[headerComponent]="datepicker.calendarHeaderComponent"
|
||||
[selected]="datepicker._selected"
|
||||
[dateClass]="datepicker.dateClass"
|
||||
[@fadeInCalendar]="'enter'"
|
||||
(selectedChange)="datepicker.select($event)"
|
||||
(yearSelected)="datepicker._selectYear($event)"
|
||||
|
||||
@@ -328,6 +328,14 @@ application root module.
|
||||
export class MyApp {}
|
||||
```
|
||||
|
||||
#### Highlighting specific dates
|
||||
If you want to apply one or more CSS classes to some dates in the calendar (e.g. to highlight a
|
||||
holiday), you can do so with the `dateClass` input. It accepts a function which will be called
|
||||
with each of the dates in the calendar and will apply any classes that are returned. The return
|
||||
value can be anything that is accepted by `ngClass`.
|
||||
|
||||
<!-- example(datepicker-date-class) -->
|
||||
|
||||
### Accessibility
|
||||
|
||||
The `MatDatepickerInput` and `MatDatepickerToggle` directives add the `aria-haspopup` attribute to
|
||||
|
||||
@@ -50,6 +50,7 @@ import {MatCalendar} from './calendar';
|
||||
import {matDatepickerAnimations} from './datepicker-animations';
|
||||
import {createMissingDateImplError} from './datepicker-errors';
|
||||
import {MatDatepickerInput} from './datepicker-input';
|
||||
import {MatCalendarCellCssClasses} from './calendar-body';
|
||||
|
||||
/** Used to generate a unique ID for each datepicker instance. */
|
||||
let datepickerUid = 0;
|
||||
@@ -212,6 +213,9 @@ export class MatDatepicker<D> implements OnDestroy, CanColor {
|
||||
/** Classes to be passed to the date picker panel. Supports the same syntax as `ngClass`. */
|
||||
@Input() panelClass: string | string[];
|
||||
|
||||
/** Function that can be used to add custom CSS classes to dates. */
|
||||
@Input() dateClass: (date: D) => MatCalendarCellCssClasses;
|
||||
|
||||
/** Emits when the datepicker has been opened. */
|
||||
@Output('opened') openedStream: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ describe('MatMonthView', () => {
|
||||
// Test components.
|
||||
StandardMonthView,
|
||||
MonthViewWithDateFilter,
|
||||
MonthViewWithDateClass,
|
||||
],
|
||||
providers: [
|
||||
{provide: Directionality, useFactory: () => dir = {value: 'ltr'}}
|
||||
@@ -291,6 +292,26 @@ describe('MatMonthView', () => {
|
||||
expect(cells[1].classList).not.toContain('mat-calendar-body-disabled');
|
||||
});
|
||||
});
|
||||
|
||||
describe('month view with custom date classes', () => {
|
||||
let fixture: ComponentFixture<MonthViewWithDateClass>;
|
||||
let monthViewNativeElement: Element;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MonthViewWithDateClass);
|
||||
fixture.detectChanges();
|
||||
|
||||
let monthViewDebugElement = fixture.debugElement.query(By.directive(MatMonthView));
|
||||
monthViewNativeElement = monthViewDebugElement.nativeElement;
|
||||
});
|
||||
|
||||
it('should be able to add a custom class to some dates', () => {
|
||||
let cells = monthViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
|
||||
expect(cells[0].classList).not.toContain('even');
|
||||
expect(cells[1].classList).toContain('even');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -312,3 +333,13 @@ class MonthViewWithDateFilter {
|
||||
return date.getDate() % 2 == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `<mat-month-view [activeDate]="activeDate" [dateClass]="dateClass"></mat-month-view>`
|
||||
})
|
||||
class MonthViewWithDateClass {
|
||||
activeDate = new Date(2017, JAN, 1);
|
||||
dateClass(date: Date) {
|
||||
return date.getDate() % 2 == 0 ? 'even' : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats} from '@angular/material/core';
|
||||
import {Directionality} from '@angular/cdk/bidi';
|
||||
import {MatCalendarBody, MatCalendarCell} from './calendar-body';
|
||||
import {MatCalendarBody, MatCalendarCell, MatCalendarCellCssClasses} from './calendar-body';
|
||||
import {createMissingDateImplError} from './datepicker-errors';
|
||||
|
||||
|
||||
@@ -94,9 +94,12 @@ export class MatMonthView<D> implements AfterContentInit {
|
||||
}
|
||||
private _maxDate: D | null;
|
||||
|
||||
/** A function used to filter which dates are selectable. */
|
||||
/** Function used to filter which dates are selectable. */
|
||||
@Input() dateFilter: (date: D) => boolean;
|
||||
|
||||
/** Function that can be used to add custom CSS classes to dates. */
|
||||
@Input() dateClass: (date: D) => MatCalendarCellCssClasses;
|
||||
|
||||
/** Emits when a new date is selected. */
|
||||
@Output() readonly selectedChange: EventEmitter<D | null> = new EventEmitter<D | null>();
|
||||
|
||||
@@ -273,8 +276,10 @@ export class MatMonthView<D> implements AfterContentInit {
|
||||
this._dateAdapter.getMonth(this.activeDate), i + 1);
|
||||
const enabled = this._shouldEnableDate(date);
|
||||
const ariaLabel = this._dateAdapter.format(date, this._dateFormats.display.dateA11yLabel);
|
||||
const cellClasses = this.dateClass ? this.dateClass(date) : undefined;
|
||||
|
||||
this._weeks[this._weeks.length - 1]
|
||||
.push(new MatCalendarCell(i + 1, dateNames[i], ariaLabel, enabled));
|
||||
.push(new MatCalendarCell(i + 1, dateNames[i], ariaLabel, enabled, cellClasses));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
.example-custom-date-class {
|
||||
background: orange;
|
||||
border-radius: 100%;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<mat-form-field class="example-full-width">
|
||||
<input matInput [matDatepicker]="picker" placeholder="Choose a date">
|
||||
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
|
||||
<mat-datepicker [dateClass]="dateClass" #picker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
@@ -0,0 +1,17 @@
|
||||
import {Component, ViewEncapsulation} from '@angular/core';
|
||||
|
||||
/** @title Datepicker with custom date classes */
|
||||
@Component({
|
||||
selector: 'datepicker-date-class-example',
|
||||
templateUrl: 'datepicker-date-class-example.html',
|
||||
styleUrls: ['datepicker-date-class-example.css'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class DatepickerDateClassExample {
|
||||
dateClass = (d: Date) => {
|
||||
const date = d.getDate();
|
||||
|
||||
// Highlight the 1st and 20th day of each month.
|
||||
return (date === 1 || date === 20) ? 'example-custom-date-class' : undefined;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user