Skip to content

Commit

Permalink
fix: #19 implement dateFilter for hours/minutes
Browse files Browse the repository at this point in the history
This adds a "MatDatetimepickerFilterType" which a date filter function always requires as 2nd parameter. This allows for different filtering on either date/hours/minutes. A combined filtering function is otherwise not possible since the  calendar always uses the current/inital hour/minute when filtering dates.
  • Loading branch information
Peter Leibiger committed Apr 3, 2018
1 parent edff5bf commit 6e150ee
Show file tree
Hide file tree
Showing 12 changed files with 89 additions and 45 deletions.
1 change: 1 addition & 0 deletions lib/core/src/datetimepicker/calendar.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
[interval]="timeInterval"
[minDate]="minDate"
[maxDate]="maxDate"
[dateFilter]="dateFilter"
[selected]="_activeDate"
(activeDateChange)="_onActiveDateChange($event)"
(selectedChange)="_timeSelected($event)"
Expand Down
5 changes: 3 additions & 2 deletions lib/core/src/datetimepicker/calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { first } from "rxjs/operators/first";
import { createMissingDateImplError } from "./datetimepicker-errors";
import { Subscription } from "rxjs/Subscription";
import { slideCalendar } from "./datetimepicker-animations";
import { MatDatetimepickerFilterType } from "./datetimepicker-filtertype";

/**
* A calendar that is used as part of the datepicker.
Expand Down Expand Up @@ -119,15 +120,15 @@ export class MatDatetimepickerCalendar<D> implements AfterContentInit, OnDestroy
@Input() timeInterval: number = 1;

/** A function used to filter which dates are selectable. */
@Input() dateFilter: (date: D) => boolean;
@Input() dateFilter: (date: D, type: MatDatetimepickerFilterType) => boolean;

/** Emits when the currently selected date changes. */
@Output() selectedChange = new EventEmitter<D>();

/** Date filter for the month and year views. */
_dateFilterForViews = (date: D) => {
return !!date &&
(!this.dateFilter || this.dateFilter(date)) &&
(!this.dateFilter || this.dateFilter(date, MatDatetimepickerFilterType.DATE)) &&
(!this.minDate || this._adapter.compareDate(date, this.minDate) >= 0) &&
(!this.maxDate || this._adapter.compareDate(date, this.maxDate) <= 0);
};
Expand Down
9 changes: 6 additions & 3 deletions lib/core/src/datetimepicker/clock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Output
} from "@angular/core";
import { DatetimeAdapter } from "../adapter/datetime-adapter";
import { MatDatetimepickerFilterType } from "./datetimepicker-filtertype";

export const CLOCK_RADIUS = 50;
export const CLOCK_INNER_RADIUS = 27.5;
Expand Down Expand Up @@ -99,7 +100,7 @@ export class MatDatetimepickerClock<D> implements AfterContentInit {
}

/** A function used to filter which dates are selectable. */
@Input() dateFilter: (date: D) => boolean;
@Input() dateFilter: (date: D, type: MatDatetimepickerFilterType) => boolean;

@Input() interval: number = 1;

Expand Down Expand Up @@ -226,7 +227,8 @@ export class MatDatetimepickerClock<D> implements AfterContentInit {
this._adapter.getDate(this.activeDate), i, 0);
let enabled =
(!this.minDate || this._adapter.compareDatetime(date, this.minDate) >= 0) &&
(!this.maxDate || this._adapter.compareDatetime(date, this.maxDate) <= 0);
(!this.maxDate || this._adapter.compareDatetime(date, this.maxDate) <= 0) &&
(!this.dateFilter || this.dateFilter(date, MatDatetimepickerFilterType.HOUR));
this._hours.push({
value: i,
displayValue: i === 0 ? "00" : hourNames[i],
Expand All @@ -246,7 +248,8 @@ export class MatDatetimepickerClock<D> implements AfterContentInit {
this._adapter.getDate(this.activeDate), this._adapter.getHour(this.activeDate), i);
let enabled =
(!this.minDate || this._adapter.compareDatetime(date, this.minDate) >= 0) &&
(!this.maxDate || this._adapter.compareDatetime(date, this.maxDate) <= 0);
(!this.maxDate || this._adapter.compareDatetime(date, this.maxDate) <= 0) &&
(!this.dateFilter || this.dateFilter(date, MatDatetimepickerFilterType.MINUTE));
this._minutes.push({
value: i,
displayValue: i === 0 ? "00" : minuteNames[i],
Expand Down
3 changes: 3 additions & 0 deletions lib/core/src/datetimepicker/datetimepicker-filtertype.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum MatDatetimepickerFilterType {
DATE, HOUR, MINUTE
}
8 changes: 4 additions & 4 deletions lib/core/src/datetimepicker/datetimepicker-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
import { Subscription } from "rxjs/Subscription";
import { MatDatetimepicker } from "./datetimepicker";
import { createMissingDateImplError } from "./datetimepicker-errors";
import {MatDatetimepickerFilterType} from "./datetimepicker-filtertype";

export const MAT_DATETIMEPICKER_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
Expand All @@ -48,7 +49,6 @@ export const MAT_DATETIMEPICKER_VALIDATORS: any = {
multi: true
};


/**
* An event used for datepicker input and change events. We don't always have access to a native
* input or change event because the event may have been triggered by the user clicking on the
Expand Down Expand Up @@ -102,12 +102,12 @@ export class MatDatetimepickerInput<D> implements AfterContentInit, ControlValue
}
}

@Input() set matDatepickerFilter(filter: (date: D | null) => boolean) {
@Input() set matDatepickerFilter(filter: (date: D | null, type: MatDatetimepickerFilterType) => boolean) {
this._dateFilter = filter;
this._validatorOnChange();
}

_dateFilter: (date: D | null) => boolean;
_dateFilter: (date: D | null, type: MatDatetimepickerFilterType) => boolean;

/** The value of the input. */
@Input()
Expand Down Expand Up @@ -264,7 +264,7 @@ export class MatDatetimepickerInput<D> implements AfterContentInit, ControlValue
/** The form control validator for the date filter. */
private _filterValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
const controlValue = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(control.value));
return !this._dateFilter || !controlValue || this._dateFilter(controlValue) ?
return !this._dateFilter || !controlValue || this._dateFilter(controlValue, MatDatetimepickerFilterType.DATE) ?
null : {"matDatepickerFilter": true};
};

Expand Down
3 changes: 2 additions & 1 deletion lib/core/src/datetimepicker/datetimepicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { DatetimeAdapter } from "../adapter/datetime-adapter";
import { MatDatetimepickerCalendar } from "./calendar";
import { createMissingDateImplError } from "./datetimepicker-errors";
import { MatDatetimepickerInput } from "./datetimepicker-input";
import { MatDatetimepickerFilterType } from "./datetimepicker-filtertype";

/** Used to generate a unique ID for each datepicker instance. */
let datetimepickerUid = 0;
Expand Down Expand Up @@ -209,7 +210,7 @@ export class MatDatetimepicker<D> implements OnDestroy {
return this._datepickerInput && this._datepickerInput.max;
}

get _dateFilter(): (date: D | null) => boolean {
get _dateFilter(): (date: D | null, type: MatDatetimepickerFilterType) => boolean {
return this._datepickerInput && this._datepickerInput._dateFilter;
}

Expand Down
1 change: 1 addition & 0 deletions lib/core/src/datetimepicker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from "./datetimepicker.module";
export * from "./calendar";
export * from "./calendar-body";
export * from "./datetimepicker";
export * from "./datetimepicker-filtertype";
export * from "./datetimepicker-input";
export * from "./datetimepicker-toggle";
export * from "./month-view";
Expand Down
1 change: 1 addition & 0 deletions lib/core/src/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export * from "./datetimepicker/datetimepicker.module";
export * from "./datetimepicker/calendar";
export * from "./datetimepicker/calendar-body";
export * from "./datetimepicker/datetimepicker";
export * from "./datetimepicker/datetimepicker-filtertype";
export * from "./datetimepicker/datetimepicker-input";
export * from "./datetimepicker/datetimepicker-toggle";
export * from "./datetimepicker/month-view";
Expand Down
13 changes: 11 additions & 2 deletions src/app/date.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,21 @@ <h2>Date types with: {{ type }}</h2>
<mat-error *ngIf="group.get('month').errors?.required">required</mat-error>
</mat-form-field>
<mat-form-field>
<mat-placeholder>Min Test</mat-placeholder>
<input matInput formControlName="mintest" [min]="min" [matDatetimepicker]="minTestPicker" required>
<mat-placeholder>Min/Max Test</mat-placeholder>
<input matInput formControlName="mintest" [min]="min" [max]="max" [matDatetimepicker]="minTestPicker" required>
<mat-datetimepicker-toggle [for]="minTestPicker" matSuffix></mat-datetimepicker-toggle>
<mat-datetimepicker #minTestPicker type="datetime" openOnFocus="true" mode="landscape" timeInterval="5"></mat-datetimepicker>
<mat-error *ngIf="group.get('mintest').errors?.required">required</mat-error>
<mat-error *ngIf="group.get('mintest').errors?.matDatepickerMin">min</mat-error>
<mat-error *ngIf="group.get('mintest').errors?.matDatepickerMax">max</mat-error>
</mat-form-field>
<mat-form-field>
<mat-placeholder>Filter Test</mat-placeholder>
<input matInput formControlName="filtertest" [matDatepickerFilter]="filter" [matDatetimepicker]="filterTestPicker" required>
<mat-datetimepicker-toggle [for]="filterTestPicker" matSuffix></mat-datetimepicker-toggle>
<mat-datetimepicker #filterTestPicker type="datetime" openOnFocus="true" mode="landscape" timeInterval="5"></mat-datetimepicker>
<mat-error *ngIf="group.get('filtertest').errors?.required">required</mat-error>
<mat-error *ngIf="group.get('filtertest').errors?.matDatepickerFilter">filter</mat-error>
</mat-form-field>
<mat-form-field>
<mat-placeholder>TouchUi</mat-placeholder>
Expand Down
64 changes: 37 additions & 27 deletions src/app/moment/moment.component.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,28 @@
import { Component } from "@angular/core";
import {
FormBuilder,
FormGroup,
Validators
} from "@angular/forms";
import { Moment } from "moment";
import * as moment from "moment";
import {MomentDatetimeAdapter, MAT_MOMENT_DATETIME_FORMATS} from "@mat-datetimepicker/moment";
import {DatetimeAdapter, MAT_DATETIME_FORMATS} from "@mat-datetimepicker/core";
import {Component} from "@angular/core";
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
import {Moment, utc} from "moment/moment";
import {MAT_MOMENT_DATETIME_FORMATS, MomentDatetimeAdapter} from "@mat-datetimepicker/moment";
import {DatetimeAdapter, MatDatetimepickerFilterType, MAT_DATETIME_FORMATS} from "@mat-datetimepicker/core";
import {DateAdapter} from "@angular/material";
import {MomentDateAdapter} from "@angular/material-moment-adapter";

@Component({
selector: "app-moment-datetime",
templateUrl: "../date.component.html",
providers: [
{
provide: DateAdapter,
useClass: MomentDateAdapter
},
{
provide: DatetimeAdapter,
useClass: MomentDatetimeAdapter
},
{
provide: MAT_DATETIME_FORMATS,
useValue: MAT_MOMENT_DATETIME_FORMATS
}
]
providers: [
{
provide: DateAdapter,
useClass: MomentDateAdapter
},
{
provide: DatetimeAdapter,
useClass: MomentDatetimeAdapter
},
{
provide: MAT_DATETIME_FORMATS,
useValue: MAT_MOMENT_DATETIME_FORMATS
}
]
})
export class MomentDatetimeComponent {
type = "moment";
Expand All @@ -36,21 +31,36 @@ export class MomentDatetimeComponent {
today: Moment;
tomorrow: Moment;
min: Moment;
max: Moment;
start: Moment;

filter: (date: Moment, type: MatDatetimepickerFilterType) => boolean;

constructor(fb: FormBuilder) {
this.today = moment().year(1929);
this.tomorrow = moment().date(moment().date() + 1);
this.today = utc().year(1929);
this.tomorrow = utc().date(utc().date() + 1);
this.min = this.today.clone().year(2018).month(10).date(3).hour(11).minute(10);
this.max = this.min.clone().date(4).minute(45);
this.start = this.today.clone().year(1930).month(9).date(28);
this.filter = (date: Moment, type: MatDatetimepickerFilterType) => {
switch (type) {
case MatDatetimepickerFilterType.DATE:
return date.year() % 2 === 0 &&
date.month() % 2 === 0 &&
date.date() % 2 === 0;
case MatDatetimepickerFilterType.HOUR:
return date.hour() % 2 === 0;
case MatDatetimepickerFilterType.MINUTE:
return date.minute() % 2 === 0;
}
};

this.group = fb.group({
dateTime: ["2017-11-09T12:10:00.000Z", Validators.required],
date: [null, Validators.required],
time: [null, Validators.required],
month: [null, Validators.required],
mintest: [this.today, Validators.required],
filtertest: [this.today, Validators.required],
touch: [null, Validators.required]
});
}
Expand Down
4 changes: 0 additions & 4 deletions src/app/moment/moment.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import {
import {
MatDatetimepickerModule
} from "@mat-datetimepicker/core";
import {
MatMomentDatetimeModule,
MAT_MOMENT_DATETIME_FORMATS
} from "@mat-datetimepicker/moment";
import { MomentDatetimeComponent } from "./moment.component";
import {ReactiveFormsModule} from "@angular/forms";
import {MatDatepickerModule, MatFormFieldModule, MatInputModule} from "@angular/material";
Expand Down
22 changes: 20 additions & 2 deletions src/app/native/native.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
FormGroup,
Validators
} from "@angular/forms";
import {DatetimeAdapter, NativeDatetimeAdapter, MAT_DATETIME_FORMATS, MAT_NATIVE_DATETIME_FORMATS} from "@mat-datetimepicker/core";
import {DatetimeAdapter, NativeDatetimeAdapter, MatDatetimepickerFilterType, MAT_DATETIME_FORMATS, MAT_NATIVE_DATETIME_FORMATS} from "@mat-datetimepicker/core";
import {DateAdapter, NativeDateAdapter} from "@angular/material";

@Component({
Expand Down Expand Up @@ -32,23 +32,41 @@ export class NativeDatetimeComponent {
today = new Date();
tomorrow = new Date();
min = new Date();
max = new Date();
start = new Date();
filter: (date: Date, type: MatDatetimepickerFilterType) => boolean;

constructor(fb: FormBuilder) {
this.today.setFullYear(1929);
this.tomorrow.setDate(this.tomorrow.getDate() + 1);
this.min.setFullYear(2018, 10, 3);
this.min.setHours(11);
this.min.setMinutes(10);
this.max.setFullYear(2018, 10, 4);
this.max.setHours(11);
this.max.setMinutes(45);
this.start.setFullYear(1930, 9, 28);
console.log(this.tomorrow);

this.filter = (date: Date, type: MatDatetimepickerFilterType) => {
switch (type) {
case MatDatetimepickerFilterType.DATE:
return date.getUTCFullYear() % 2 === 0 &&
date.getMonth() % 2 === 0 &&
date.getDate() % 2 === 0;
case MatDatetimepickerFilterType.HOUR:
return date.getHours() % 2 === 0;
case MatDatetimepickerFilterType.MINUTE:
return date.getMinutes() % 2 === 0;
}
};

this.group = fb.group({
dateTime: ["2017-11-09T12:10:00.000Z", Validators.required],
date: [null, Validators.required],
time: [null, Validators.required],
month: [null, Validators.required],
mintest: [this.today, Validators.required],
filtertest: [this.today, Validators.required],
touch: [null, Validators.required]
});
}
Expand Down

0 comments on commit 6e150ee

Please sign in to comment.