-
Notifications
You must be signed in to change notification settings - Fork 6.8k
feat(datepicker): Add Custom Header to DatePicker #9639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 16 commits
6251e1e
8d30140
81c89fa
737a563
998be7c
a14dbd4
585407f
6668ae6
f9628a8
050904b
a2db283
7f071ec
5c9f662
3467c0a
014777a
179c9af
b6938f2
0faefc4
aa8f837
2b4d4d2
b238107
46b2c9d
a45f3ee
f8c4c5b
b6b50cc
ca7a0db
2058afb
f6f19e8
702317d
528361e
e36dc71
a7975d2
02b5604
57bd12c
a88fecb
4c66467
258929a
81147f1
586d5da
a9d4c58
9bba08f
1ff7e45
5196ee7
4cee00c
da49938
70ff16f
533739e
c287495
6d0a4d2
a33b70c
c7f21bf
715e413
5f4b0fc
3057f9d
d81db60
94c825a
b6fc003
796cbf2
abf1c62
1fdb1c3
822dd56
ba4e839
1ee0f93
aff641d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,9 +6,11 @@ | |
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {ChangeDetectionStrategy, Component} from '@angular/core'; | ||
import {ChangeDetectionStrategy, Component, Host} from '@angular/core'; | ||
import {FormControl} from '@angular/forms'; | ||
import {MatDatepickerInputEvent} from '@angular/material/datepicker'; | ||
import {DateAdapter} from '@angular/material/core'; | ||
import {MatCalendar} from '@angular/material'; | ||
|
||
|
||
@Component({ | ||
|
@@ -32,10 +34,24 @@ export class DatepickerDemo { | |
lastDateChange: Date | null; | ||
|
||
dateFilter = | ||
(date: Date) => !(date.getFullYear() % 2) && (date.getMonth() % 2) && !(date.getDate() % 2) | ||
(date: Date) => !(date.getFullYear() % 2) && (date.getMonth() % 2) && !(date.getDate() % 2) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: continuation lines indented 2 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in 0faefc4 |
||
|
||
onDateInput = (e: MatDatepickerInputEvent<Date>) => this.lastDateInput = e.value; | ||
onDateChange = (e: MatDatepickerInputEvent<Date>) => this.lastDateChange = e.value; | ||
|
||
dateCtrl = new FormControl(); | ||
|
||
// pass custom header component type as input | ||
customHeader = CustomHeader; | ||
} | ||
|
||
// Custom header component for datepicker | ||
@Component({ | ||
selector: 'custom-header', | ||
template: 'custom header' | ||
}) | ||
export class CustomHeader { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of using below, can you add a type parameter to this class There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in 715e413 |
||
constructor(@Host() public calendar: MatCalendar<any>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. might as well make calendar private too, since it doesn't need to be public There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in c7f21bf |
||
public adapter: DateAdapter<any>) { | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
<div class="mat-calendar-header"> | ||
|
||
<ng-template [cdkPortalOutlet]="calendarHeaderPortal"></ng-template> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you planning to move the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good idea. In that case it would make sense to add them to So the template of <div class="mat-calendar-controls">
<button mat-button class="mat-calendar-period-button"
(click)="_currentPeriodClicked()" [attr.aria-label]="_periodButtonLabel">
{{_periodButtonText}}
<div class="mat-calendar-arrow" [class.mat-calendar-invert]="_currentView != 'month'"></div>
</button>
<div class="mat-calendar-spacer"></div>
<button mat-icon-button class="mat-calendar-previous-button"
[disabled]="!_previousEnabled()" (click)="_previousClicked()"
[attr.aria-label]="_prevButtonLabel">
</button>
<button mat-icon-button class="mat-calendar-next-button"
[disabled]="!_nextEnabled()" (click)="_nextClicked()"
[attr.aria-label]="_nextButtonLabel">
</button>
</div> And then we could remove that code from However, if someone provides a custom header, she would have to take care of these controls. Could we make more sub-components for the buttons so they could be combined in a more modular way? Could we make components for the buttons (previous, next)? But how would that work with the callback methods that have to be called on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh sorry, I missed this comment somehow. Yeah, that's pretty much what I was thinking. We could package the buttons up into their own components, but it seems a little bit overkill to me since they're not very complex. We will need to make some of the internal properties and methods on calendar public (remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One test is failing in
And another test does not pass in
I assume that this related to the use of |
||
|
||
<div class="mat-calendar-controls"> | ||
<button mat-button class="mat-calendar-period-button" | ||
(click)="_currentPeriodClicked()" [attr.aria-label]="_periodButtonLabel"> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,7 +44,20 @@ import {MatMonthView} from './month-view'; | |
import {MatMultiYearView, yearsPerPage, yearsPerRow} from './multi-year-view'; | ||
import {MatYearView} from './year-view'; | ||
import {Directionality} from '@angular/cdk/bidi'; | ||
import {ComponentPortal, ComponentType, Portal} from '@angular/cdk/portal'; | ||
|
||
/** | ||
* Default header of a [MatCalendar]. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in 0faefc4 |
||
*/ | ||
@Component({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lint: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in a45f3ee |
||
selector: 'mat-calendar-header', | ||
template: 'default header' | ||
}) | ||
export class MatCalendarHeader { | ||
constructor() { | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: remove this blank line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in a88fecb There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: no newline There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in 4c66467 |
||
} | ||
} | ||
|
||
/** | ||
* A calendar that is used as part of the datepicker. | ||
|
@@ -64,6 +77,17 @@ import {Directionality} from '@angular/cdk/bidi'; | |
changeDetection: ChangeDetectionStrategy.OnPush, | ||
}) | ||
export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges { | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: no blank line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in a88fecb |
||
/** An input indicating the type of the header component, if set. */ | ||
@Input() headerComponent: ComponentType<any>; | ||
|
||
/** A portal containing the header component type for this calendar. */ | ||
private _calendarHeaderPortal: Portal<any>; | ||
|
||
get calendarHeaderPortal() { | ||
return this._calendarHeaderPortal; | ||
} | ||
|
||
private _intlChanges: Subscription; | ||
|
||
/** A date representing the period (month or year) to start the calendar in. */ | ||
|
@@ -125,7 +149,7 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges { | |
(!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 | ||
|
@@ -200,6 +224,9 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges { | |
} | ||
|
||
ngAfterContentInit() { | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: no blank line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in a88fecb |
||
this._calendarHeaderPortal = new ComponentPortal(this.headerComponent || MatCalendarHeader); | ||
|
||
this._activeDate = this.startAt || this._dateAdapter.today(); | ||
this._focusActiveCell(); | ||
this._currentView = this.startView; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,5 +8,6 @@ | |
[dateFilter]="datepicker._dateFilter" | ||
[selected]="datepicker._selected" | ||
(selectedChange)="datepicker._select($event)" | ||
(_userSelection)="datepicker.close()"> | ||
(_userSelection)="datepicker.close()" | ||
[headerComponent]="datepicker.calendarHeaderComponent"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: move above There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in a7975d2 |
||
</mat-calendar> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ import { | |
RepositionScrollStrategy, | ||
ScrollStrategy, | ||
} from '@angular/cdk/overlay'; | ||
import {ComponentPortal} from '@angular/cdk/portal'; | ||
import {ComponentPortal, ComponentType} from '@angular/cdk/portal'; | ||
import {take} from 'rxjs/operators/take'; | ||
import {filter} from 'rxjs/operators/filter'; | ||
import { | ||
|
@@ -115,6 +115,10 @@ export class MatDatepickerContent<D> implements AfterContentInit { | |
preserveWhitespaces: false, | ||
}) | ||
export class MatDatepicker<D> implements OnDestroy { | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: no blank line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in a88fecb |
||
/** An input indicating the type of the custom header component for the calendar, if set. */ | ||
@Input() calendarHeaderComponent: ComponentType<any>; | ||
|
||
/** The date to open the calendar to initially. */ | ||
@Input() | ||
get startAt(): D | null { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of
placeholder="Custom calendar header"
can you do<mat-label>Custom calendar header</mat-label>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 6d0a4d2