Skip to content

feat(datepicker): add date range picker #19125

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

Merged
merged 39 commits into from
May 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
5ee1938
refactor(datepicker): implement date selection model provider (#18090)
crisbeto Jan 7, 2020
eb75cf2
feat(datepicker): set up input for date range picker (#18159)
crisbeto Jan 17, 2020
454b449
refactor: move common date input logic into base class (#18213)
crisbeto Jan 21, 2020
fb25577
refactor(datepicker): implement range selection model (#18247)
crisbeto Jan 23, 2020
dfd766d
refactor: clean up datepicker base class abstract methods (#18278)
crisbeto Jan 24, 2020
831d710
refactor(datepicker): set up date range picker classes (#18292)
crisbeto Jan 29, 2020
fcdb439
feat(datepicker): initial styling for date range (#18369)
crisbeto Feb 5, 2020
aec2956
fix(datepicker): handle date ranges across multiple months (#18404)
crisbeto Feb 6, 2020
a253aa0
feat(datepicker): polish up date range selector (#18531)
crisbeto Feb 20, 2020
1c3aea8
chore: fix grammar
crisbeto Feb 20, 2020
4f13bed
fix(datepicker): cancel current range selection when pressing escape …
crisbeto Feb 27, 2020
bf03c30
fix(datepicker): add gap between range rows and handle rtl (#18660)
crisbeto Mar 1, 2020
59f4ec3
feat(datepicker): implement comparison and overlap ranges (#18753)
crisbeto Mar 12, 2020
3ae1ade
fix(datepicker): resolve visual inconsistencies with comparison range…
crisbeto Mar 13, 2020
21d2fe7
test(datepicker): add unit tests for the date range input (#18874)
crisbeto Mar 20, 2020
1eb777e
test(datepicker): add tests for the date range picker (#18884)
crisbeto Mar 27, 2020
aab79a9
refactor(datepicker): clean up date selection model (#18943)
crisbeto Mar 31, 2020
3d07915
refactor(datepicker): align preview range appearance with Material De…
crisbeto Mar 31, 2020
f5b3125
build: fix ci issues
crisbeto Mar 31, 2020
e2e284f
feat(datepicker): allow for date range selection logic to be customiz…
crisbeto Apr 8, 2020
156e9ed
fix(datepicker): range input label pointing to non-existent id (#19031)
crisbeto Apr 9, 2020
614bf3f
fix(datepicker): allow same start and end dates for range (#19098)
crisbeto Apr 17, 2020
c21c421
feat(datepicker): allow for the preview range logic to be customized …
crisbeto Apr 17, 2020
428fccb
fix(datepicker): don't render invalid ranges and clean up range displ…
crisbeto Apr 20, 2020
3a5a6ce
Merge branch 'master' into date-range
crisbeto Apr 23, 2020
be46a36
fix: golden failure
crisbeto Apr 23, 2020
7e9ce93
Merge branch 'master' into date-range
crisbeto Apr 25, 2020
e2c0c97
fix: remove unused method
crisbeto Apr 25, 2020
33a15ae
fix(datepicker): changed after checked error when moving preview betw…
crisbeto Apr 27, 2020
8fd3b2f
fix(datepicker): clearing completed range when pressing escape (#19179)
crisbeto Apr 27, 2020
e73875b
fix(datepicker): end input reset on load when bound through ngModel (…
crisbeto Apr 27, 2020
8199775
fix(datepicker): change event dispatched before value is formatted (#…
crisbeto Apr 27, 2020
373613e
fix(datepicker): calendar dispatching change event on non-user intera…
crisbeto Apr 28, 2020
5dd582c
fix(datepicker): unable to type in range inputs
crisbeto Apr 28, 2020
125475f
fix(datepicker): user selection event not emitting value when clickin…
crisbeto Apr 30, 2020
5e8a974
refactor(datepicker): move date range selection logic out of calendar…
crisbeto May 4, 2020
96f968e
fix(datepicker): move focus into start input when pressing backspace …
crisbeto May 4, 2020
3c69895
docs(datepicker): add docs and live example for date range picker (#1…
crisbeto May 4, 2020
ea7f9a4
Merge branch 'master' into date-range
crisbeto May 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions goldens/ts-circular-deps.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
"src/material/core/ripple/ripple-ref.ts",
"src/material/core/ripple/ripple-renderer.ts"
],
[
"src/material/datepicker/date-range-input.ts",
"src/material/datepicker/date-range-picker.ts"
],
[
"src/material/datepicker/datepicker-input.ts",
"src/material/datepicker/datepicker.ts"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.example-form-field {
margin: 0 8px 16px 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<mat-form-field class="example-form-field">
<mat-label>First campaign</mat-label>
<mat-date-range-input
[formGroup]="campaignOne"
[rangePicker]="campaignOnePicker"
[comparisonStart]="campaignTwo.value.start"
[comparisonEnd]="campaignTwo.value.end">
<input matStartDate matInput placeholder="Start date" formControlName="start">
<input matEndDate matInput placeholder="End date" formControlName="end">
</mat-date-range-input>
<mat-datepicker-toggle matSuffix [for]="campaignOnePicker"></mat-datepicker-toggle>
<mat-date-range-picker #campaignOnePicker></mat-date-range-picker>
</mat-form-field>

<mat-form-field class="example-form-field">
<mat-label>Second campaign</mat-label>
<mat-date-range-input
[formGroup]="campaignTwo"
[rangePicker]="campaignTwoPicker"
[comparisonStart]="campaignOne.value.start"
[comparisonEnd]="campaignOne.value.end">
<input matStartDate matInput placeholder="Start date" formControlName="start">
<input matEndDate matInput placeholder="End date" formControlName="end">
</mat-date-range-input>
<mat-datepicker-toggle matSuffix [for]="campaignTwoPicker"></mat-datepicker-toggle>
<mat-date-range-picker #campaignTwoPicker></mat-date-range-picker>
</mat-form-field>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {Component} from '@angular/core';
import {FormGroup, FormControl} from '@angular/forms';

/** @title Date range picker comparison ranges */
@Component({
selector: 'date-range-picker-comparison-example',
templateUrl: 'date-range-picker-comparison-example.html',
styleUrls: ['date-range-picker-comparison-example.css'],
})
export class DateRangePickerComparisonExample {
campaignOne: FormGroup;
campaignTwo: FormGroup;

constructor() {
const today = new Date();
const month = today.getMonth();
const year = today.getFullYear();

this.campaignOne = new FormGroup({
start: new FormControl(new Date(year, month, 13)),
end: new FormControl(new Date(year, month, 16))
});

this.campaignTwo = new FormGroup({
start: new FormControl(new Date(year, month, 15)),
end: new FormControl(new Date(year, month, 19))
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/** No CSS for this example */
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<mat-form-field>
<mat-label>Enter a date range</mat-label>
<mat-date-range-input [formGroup]="range" [rangePicker]="picker">
<input matStartDate matInput formControlName="start" placeholder="Start date">
<input matEndDate matInput formControlName="end" placeholder="End date">
</mat-date-range-input>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker>

<mat-error *ngIf="range.controls.start.hasError('matStartDateInvalid')">Invalid start date</mat-error>
<mat-error *ngIf="range.controls.end.hasError('matEndDateInvalid')">Invalid end date</mat-error>
</mat-form-field>

<p>Selected range: {{range.value | json}}</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Component} from '@angular/core';
import {FormGroup, FormControl} from '@angular/forms';

/** @title Date range picker forms integration */
@Component({
selector: 'date-range-picker-forms-example',
templateUrl: 'date-range-picker-forms-example.html',
styleUrls: ['date-range-picker-forms-example.css'],
})
export class DateRangePickerFormsExample {
range = new FormGroup({
start: new FormControl(),
end: new FormControl()
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/** No CSS for this example */
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<mat-form-field>
<mat-label>Enter a date range</mat-label>
<mat-date-range-input [rangePicker]="picker">
<input matStartDate matInput placeholder="Start date">
<input matEndDate matInput placeholder="End date">
</mat-date-range-input>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker>
</mat-form-field>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {Component} from '@angular/core';

/** @title Basic date range picker */
@Component({
selector: 'date-range-picker-overview-example',
templateUrl: 'date-range-picker-overview-example.html',
styleUrls: ['date-range-picker-overview-example.css'],
})
export class DateRangePickerOverviewExample {}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/** No CSS for this example */
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<mat-form-field>
<mat-label>Enter a date range</mat-label>
<mat-date-range-input [rangePicker]="picker">
<input matStartDate matInput placeholder="Start date">
<input matEndDate matInput placeholder="End date">
</mat-date-range-input>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker>
</mat-form-field>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {Component, Injectable} from '@angular/core';
import {DateAdapter} from '@angular/material/core';
import {
MatDateRangeSelectionStrategy,
DateRange,
MAT_DATE_RANGE_SELECTION_STRATEGY,
} from '@angular/material/datepicker';

@Injectable()
export class FiveDayRangeSelectionStrategy<D> implements MatDateRangeSelectionStrategy<D> {
constructor(private _dateAdapter: DateAdapter<D>) {}

selectionFinished(date: D | null): DateRange<D> {
return this._createFiveDayRange(date);
}

createPreview(activeDate: D | null): DateRange<D> {
return this._createFiveDayRange(activeDate);
}

private _createFiveDayRange(date: D | null): DateRange<D> {
if (date) {
const start = this._dateAdapter.addCalendarDays(date, -2);
const end = this._dateAdapter.addCalendarDays(date, 2);
return new DateRange<D>(start, end);
}

return new DateRange<D>(null, null);
}
}

/** @title Date range picker with custom a selection strategy */
@Component({
selector: 'date-range-picker-selection-strategy-example',
templateUrl: 'date-range-picker-selection-strategy-example.html',
styleUrls: ['date-range-picker-selection-strategy-example.css'],
providers: [{
provide: MAT_DATE_RANGE_SELECTION_STRATEGY,
useClass: FiveDayRangeSelectionStrategy
}]
})
export class DateRangePickerSelectionStrategyExample {}
23 changes: 22 additions & 1 deletion src/components-examples/material/datepicker/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import {ReactiveFormsModule, FormsModule} from '@angular/forms';
import {MatButtonModule} from '@angular/material/button';
import {MatNativeDateModule} from '@angular/material/core';
import {MatDatepickerModule} from '@angular/material/datepicker';
Expand Down Expand Up @@ -29,6 +29,18 @@ import {DatepickerValueExample} from './datepicker-value/datepicker-value-exampl
import {
DatepickerViewsSelectionExample
} from './datepicker-views-selection/datepicker-views-selection-example';
import {
DateRangePickerOverviewExample
} from './date-range-picker-overview/date-range-picker-overview-example';
import {
DateRangePickerFormsExample
} from './date-range-picker-forms/date-range-picker-forms-example';
import {
DateRangePickerComparisonExample
} from './date-range-picker-comparison/date-range-picker-comparison-example';
import {
DateRangePickerSelectionStrategyExample
} from './date-range-picker-selection-strategy/date-range-picker-selection-strategy-example';

export {
DatepickerApiExample,
Expand All @@ -48,6 +60,10 @@ export {
DatepickerTouchExample,
DatepickerValueExample,
DatepickerViewsSelectionExample,
DateRangePickerOverviewExample,
DateRangePickerFormsExample,
DateRangePickerComparisonExample,
DateRangePickerSelectionStrategyExample,
ExampleHeader,
};

Expand All @@ -69,6 +85,10 @@ const EXAMPLES = [
DatepickerTouchExample,
DatepickerValueExample,
DatepickerViewsSelectionExample,
DateRangePickerOverviewExample,
DateRangePickerFormsExample,
DateRangePickerComparisonExample,
DateRangePickerSelectionStrategyExample,
ExampleHeader,
];

Expand All @@ -81,6 +101,7 @@ const EXAMPLES = [
MatIconModule,
MatNativeDateModule,
ReactiveFormsModule,
FormsModule,
],
declarations: EXAMPLES,
exports: EXAMPLES,
Expand Down
3 changes: 3 additions & 0 deletions src/dev-app/datepicker/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ ng_module(
sass_binary(
name = "datepicker_demo_scss",
src = "datepicker-demo.scss",
deps = [
"//src/material/datepicker:datepicker_scss_lib",
],
)

sass_binary(
Expand Down
9 changes: 7 additions & 2 deletions src/dev-app/datepicker/datepicker-demo-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ import {MatIconModule} from '@angular/material/icon';
import {MatInputModule} from '@angular/material/input';
import {MatSelectModule} from '@angular/material/select';
import {RouterModule} from '@angular/router';
import {CustomHeader, CustomHeaderNgContent, DatepickerDemo} from './datepicker-demo';
import {
CustomHeader,
CustomHeaderNgContent,
DatepickerDemo,
CustomRangeStrategy,
} from './datepicker-demo';

@NgModule({
imports: [
Expand All @@ -35,7 +40,7 @@ import {CustomHeader, CustomHeaderNgContent, DatepickerDemo} from './datepicker-
ReactiveFormsModule,
RouterModule.forChild([{path: '', component: DatepickerDemo}]),
],
declarations: [CustomHeader, CustomHeaderNgContent, DatepickerDemo],
declarations: [CustomHeader, CustomHeaderNgContent, DatepickerDemo, CustomRangeStrategy],
})
export class DatepickerDemoModule {
}
89 changes: 89 additions & 0 deletions src/dev-app/datepicker/datepicker-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,92 @@ <h2>Datepicker with custom header extending the default header</h2>
<mat-datepicker #customHeaderNgContentPicker [calendarHeaderComponent]="customHeaderNgContent"></mat-datepicker>
</mat-form-field>
</p>

<h2>Range picker</h2>

<div class="demo-range-group">
<mat-form-field>
<mat-label>Enter a date range</mat-label>
<mat-date-range-input
[formGroup]="range1"
[rangePicker]="range1Picker"
[min]="minDate"
[max]="maxDate"
[disabled]="inputDisabled"
[comparisonStart]="comparisonStart"
[comparisonEnd]="comparisonEnd"
[dateFilter]="filterOdd ? dateFilter : undefined">
<input matStartDate formControlName="start" placeholder="Start date"/>
<input matEndDate formControlName="end" placeholder="End date"/>
</mat-date-range-input>
<mat-datepicker-toggle [for]="range1Picker" matSuffix></mat-datepicker-toggle>
<mat-date-range-picker
[touchUi]="touch"
[disabled]="datepickerDisabled"
#range1Picker></mat-date-range-picker>
</mat-form-field>
<div>{{range1.value | json}}</div>
</div>

<div class="demo-range-group">
<mat-form-field appearance="fill">
<mat-label>Enter a date range</mat-label>
<mat-date-range-input
[formGroup]="range2"
[rangePicker]="range2Picker"
[min]="minDate"
[max]="maxDate"
[disabled]="inputDisabled"
[comparisonStart]="comparisonStart"
[comparisonEnd]="comparisonEnd"
[dateFilter]="filterOdd ? dateFilter : undefined">
<input matStartDate formControlName="start" placeholder="Start date"/>
<input matEndDate formControlName="end" placeholder="End date"/>
</mat-date-range-input>
<mat-datepicker-toggle [for]="range2Picker" matSuffix></mat-datepicker-toggle>
<mat-date-range-picker
[touchUi]="touch"
[disabled]="datepickerDisabled"
panelClass="demo-custom-range"
#range2Picker></mat-date-range-picker>
</mat-form-field>
<div>{{range2.value | json}}</div>
</div>

<div class="demo-range-group">
<mat-form-field appearance="outline">
<mat-label>Enter a date range</mat-label>
<mat-date-range-input
[formGroup]="range3"
[rangePicker]="range3Picker"
[min]="minDate"
[max]="maxDate"
[disabled]="inputDisabled"
[comparisonStart]="comparisonStart"
[comparisonEnd]="comparisonEnd"
[dateFilter]="filterOdd ? dateFilter : undefined">
<input matStartDate formControlName="start" placeholder="Start date"/>
<input matEndDate formControlName="end" placeholder="End date"/>
</mat-date-range-input>
<mat-datepicker-toggle [for]="range3Picker" matSuffix></mat-datepicker-toggle>
<mat-date-range-picker
[touchUi]="touch"
[disabled]="datepickerDisabled"
#range3Picker></mat-date-range-picker>
</mat-form-field>
<div>{{range3.value | json}}</div>
</div>


<h2>Range picker with custom selection strategy</h2>
<div class="demo-range-group">
<mat-form-field>
<mat-label>Enter a date range</mat-label>
<mat-date-range-input [rangePicker]="range4Picker">
<input matStartDate placeholder="Start date"/>
<input matEndDate placeholder="End date"/>
</mat-date-range-input>
<mat-datepicker-toggle [for]="range4Picker" matSuffix></mat-datepicker-toggle>
<mat-date-range-picker customRangeStrategy #range4Picker></mat-date-range-picker>
</mat-form-field>
</div>
9 changes: 9 additions & 0 deletions src/dev-app/datepicker/datepicker-demo.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
@import '../../material/datepicker/datepicker-theme';

mat-calendar {
width: 300px;
}

.demo-range-group {
margin-bottom: 30px;
}

.demo-custom-range {
@include mat-date-range-colors(hotpink, teal, yellow, purple);
}
Loading