Skip to content
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

feat(datepicker): min & max dates + dateFilter #3556

Merged
merged 8 commits into from
Mar 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions src/demo-app/datepicker/datepicker-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ <h1>Work in progress, not ready for use.</h1>
<p>
<md-input-container>
<input mdInput [mdDatepicker]="dp2" [(ngModel)]="date">
<button [mdDatepickerToggle]="dp2"></button>
<button mdSuffix [mdDatepickerToggle]="dp2"></button>
<md-datepicker #dp2 [touchUi]="touch"></md-datepicker>
</md-input-container>
</p>
<p>
<button [mdDatepickerToggle]="dp3"></button>
<md-input-container>
<input mdInput [mdDatepicker]="dp3" [(ngModel)]="date">
<md-datepicker #dp3 [touchUi]="touch"></md-datepicker>
<md-datepicker #dp3 [touchUi]="touch" startAt="1/1/17" minDate="1/1/16" maxDate="1/1/18"
[dateFilter]="dateFilter">
</md-datepicker>
</md-input-container>
</p>
2 changes: 2 additions & 0 deletions src/demo-app/datepicker/datepicker-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ import {SimpleDate} from '@angular/material';
export class DatepickerDemo {
date: SimpleDate;
touch = false;
dateFilter = (date: SimpleDate) => !this._blacklistedMonths.has(date.month) && date.date % 2 == 0;
private _blacklistedMonths = new Set([2, 3]);
}
19 changes: 19 additions & 0 deletions src/lib/core/datetime/simple-date.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,23 @@ describe('SimpleDate', () => {
expect(new SimpleDate(2017, 0, 1).add({years: 1, months: 1, days: 1}))
.toEqual(new SimpleDate(2018, 1, 2));
});

it('clamps date at lower bound', () => {
let date = new SimpleDate(2017, 0, 1);
let lower = new SimpleDate(2018, 1, 2);
let upper = new SimpleDate(2019, 2, 3);
expect(date.clamp(lower, upper)).toEqual(lower);
});

it('clamps date at upper bound', () => {
let date = new SimpleDate(2020, 0, 1);
let lower = new SimpleDate(2018, 1, 2);
let upper = new SimpleDate(2019, 2, 3);
expect(date.clamp(lower, upper)).toEqual(upper);
});

it('clamp treats null as unbounded', () => {
let date = new SimpleDate(2017, 0, 1);
expect(date.clamp(null, null)).toEqual(date);
});
});
18 changes: 18 additions & 0 deletions src/lib/core/datetime/simple-date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export class SimpleDate {
/**
* Adds an amount of time (in days, months, and years) to the date.
* @param amount The amount of time to add.
* @returns A new SimpleDate with the given amount of time added.
*/
add(amount: {days?: number, months?: number, years?: number}): SimpleDate {
return new SimpleDate(
Expand All @@ -69,6 +70,23 @@ export class SimpleDate {
return this.year - other.year || this.month - other.month || this.date - other.date;
}

/**
* Clamps the date between the given min and max dates.
* @param min The minimum date
* @param max The maximum date
* @returns A new SimpleDate equal to this one clamped between the given min and max dates.
*/
clamp(min: SimpleDate, max: SimpleDate): SimpleDate {
let clampedDate: SimpleDate = this;
if (min && this.compare(min) < 0) {
clampedDate = min;
}
if (max && this.compare(max) > 0) {
clampedDate = max;
}
return new SimpleDate(clampedDate.year, clampedDate.month, clampedDate.date);
}

/** Converts the SimpleDate to a native JS Date object. */
toNativeDate(): Date {
return new Date(this.year, this.month, this.date);
Expand Down
22 changes: 19 additions & 3 deletions src/lib/datepicker/_datepicker-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@


@mixin mat-datepicker-theme($theme) {
$mat-datepicker-selected-today-box-shadow-width: 1px;
$primary: map-get($theme, primary);
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);

$mat-datepicker-selected-today-box-shadow-width: 1px;
$mat-datepicker-selected-fade-amount: 0.6;
$mat-datepicker-today-fade-amount: 0.2;

.mat-calendar {
background-color: mat-color($background, card);
}
Expand Down Expand Up @@ -36,24 +39,37 @@
.mat-calendar-table-cell-content {
color: mat-color($foreground, text);
border-color: transparent;

.mat-calendar-table-disabled > &:not(.mat-calendar-table-selected) {
color: mat-color($foreground, disabled-text);
}
}

.mat-calendar-table-cell:hover {
.mat-calendar-table-cell-content:not(.mat-calendar-table-selected) {
:not(.mat-calendar-table-disabled):hover {
& > .mat-calendar-table-cell-content:not(.mat-calendar-table-selected) {
background-color: mat-color($background, hover);
}
}

.mat-calendar-table-selected {
background-color: mat-color($primary);
color: mat-color($primary, default-contrast);

.mat-calendar-table-disabled > & {
background-color: fade-out(mat-color($primary), $mat-datepicker-selected-fade-amount);
}
}

.mat-calendar-table-today {
&:not(.mat-calendar-table-selected) {
// Note: though it's not text, the border is a hint about the fact that this is today's date,
// so we use the hint color.
border-color: mat-color($foreground, hint-text);

.mat-calendar-table-disabled > & {
border-color:
fade-out(mat-color($foreground, hint-text), $mat-datepicker-today-fade-amount);
}
}

&.mat-calendar-table-selected {
Expand Down
3 changes: 2 additions & 1 deletion src/lib/datepicker/calendar-table.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
</td>
<td *ngFor="let item of row"
class="mat-calendar-table-cell"
(click)="_cellClicked(item.value)">
[class.mat-calendar-table-disabled]="!item.enabled"
(click)="_cellClicked(item)">
<div class="mat-calendar-table-cell-content"
[class.mat-calendar-table-selected]="selectedValue === item.value"
[class.mat-calendar-table-today]="todayValue === item.value">
Expand Down
53 changes: 52 additions & 1 deletion src/lib/datepicker/calendar-table.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {Component} from '@angular/core';
import {MdCalendarCell, MdCalendarTable} from './calendar-table';
import {By} from '@angular/platform-browser';
import {SimpleDate} from '../core/datetime/simple-date';


describe('MdCalendarTable', () => {
Expand All @@ -12,6 +13,7 @@ describe('MdCalendarTable', () => {

// Test components.
StandardCalendarTable,
CalendarTableWithDisabledCells,
],
});

Expand Down Expand Up @@ -85,6 +87,38 @@ describe('MdCalendarTable', () => {
.toContain('mat-calendar-table-selected', 'today should be selected');
});
});

describe('calendar table with disabled cells', () => {
let fixture: ComponentFixture<CalendarTableWithDisabledCells>;
let testComponent: CalendarTableWithDisabledCells;
let calendarTableNativeElement: Element;
let cellEls: NodeListOf<Element>;

beforeEach(() => {
fixture = TestBed.createComponent(CalendarTableWithDisabledCells);
fixture.detectChanges();

let calendarTableDebugElement = fixture.debugElement.query(By.directive(MdCalendarTable));
calendarTableNativeElement = calendarTableDebugElement.nativeElement;
testComponent = fixture.componentInstance;
cellEls = calendarTableNativeElement.querySelectorAll('.mat-calendar-table-cell');
});

it('should only allow selection of disabled cells when allowDisabledSelection is true', () => {
(cellEls[0] as HTMLElement).click();
fixture.detectChanges();

expect(testComponent.selected).toBeFalsy();

testComponent.allowDisabledSelection = true;
fixture.detectChanges();

(cellEls[0] as HTMLElement).click();
fixture.detectChanges();

expect(testComponent.selected).toBe(1);
});
});
});


Expand Down Expand Up @@ -112,6 +146,23 @@ class StandardCalendarTable {
}


@Component({
template: `<md-calendar-table [rows]="rows"
[allowDisabledSelection]="allowDisabledSelection"
(selectedValueChange)="selected = $event">
</md-calendar-table>`
})
class CalendarTableWithDisabledCells {
rows = [[1, 2, 3, 4]].map(r => r.map(d => {
let cell = createCell(d);
cell.enabled = d % 2 == 0;
return cell;
}));
allowDisabledSelection = false;
selected: SimpleDate;
}


function createCell(value: number) {
return new MdCalendarCell(value, `${value}`);
return new MdCalendarCell(value, `${value}`, true);
}
11 changes: 7 additions & 4 deletions src/lib/datepicker/calendar-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
* @docs-private
*/
export class MdCalendarCell {
constructor(public value: number, public displayValue: string) {}
constructor(public value: number, public displayValue: string, public enabled: boolean) {}
}


Expand Down Expand Up @@ -48,14 +48,17 @@ export class MdCalendarTable {
/** The number of columns in the table. */
@Input() numCols = 7;

/** Whether to allow selection of disabled cells. */
@Input() allowDisabledSelection = false;

/** Emits when a new value is selected. */
@Output() selectedValueChange = new EventEmitter<number>();

_cellClicked(value: number) {
if (this.selectedValue && this.selectedValue === value) {
_cellClicked(cell: MdCalendarCell) {
if (!this.allowDisabledSelection && !cell.enabled) {
return;
}
this.selectedValueChange.emit(value);
this.selectedValueChange.emit(cell.value);
}

/** The number of blank cells to put at the beginning for the first row. */
Expand Down
8 changes: 6 additions & 2 deletions src/lib/datepicker/calendar.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
<div class="mat-calendar-arrow" [class.mat-calendar-invert]="!_monthView"></div>
</button>
<div class="mat-calendar-spacer"></div>
<button class="mat-calendar-button mat-calendar-previous-button" (click)="_previousClicked()">
<button class="mat-calendar-button mat-calendar-previous-button"
[class.mat-calendar-disabled]="!_previousEnabled()" (click)="_previousClicked()">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24"
fill="currentColor">
<path d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"/>
<path d="M0-.5h24v24H0z" fill="none"/>
</svg>
</button>
<button class="mat-calendar-button mat-calendar-next-button" (click)="_nextClicked()">
<button class="mat-calendar-button mat-calendar-next-button"
[class.mat-calendar-disabled]="!_nextEnabled()" (click)="_nextClicked()">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24"
fill="currentColor">
<path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"/>
Expand All @@ -32,13 +34,15 @@
*ngIf="_monthView"
[date]="_currentPeriod"
[selected]="selected"
[dateFilter]="_dateFilterForViews"
(selectedChange)="_dateSelected($event)">
</md-month-view>

<md-year-view
*ngIf="!_monthView"
[date]="_currentPeriod"
[selected]="selected"
[dateFilter]="_dateFilterForViews"
(selectedChange)="_monthSelected($event)">
</md-year-view>
</div>
5 changes: 5 additions & 0 deletions src/lib/datepicker/calendar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ $mat-calendar-controls-start-padding: calc(100% / 14 - 6px);
$mat-calendar-controls-end-padding: calc(100% / 14 - 12px);
$mat-calendar-period-font-size: 14px;
$mat-calendar-arrow-size: 5px !default;
$mat-calendar-arrow-disabled-opacity: 0.5 !default;
$mat-calendar-weekday-table-font-size: 11px !default;


Expand Down Expand Up @@ -41,6 +42,10 @@ $mat-calendar-weekday-table-font-size: 11px !default;
margin: 0;
border: none;
outline: none;

&.mat-calendar-disabled {
opacity: $mat-calendar-arrow-disabled-opacity;
}
}

.mat-calendar-period-button {
Expand Down
Loading