Skip to content

Commit bf7d246

Browse files
committed
incoporate new type definition
1 parent 0a0ea04 commit bf7d246

File tree

2 files changed

+94
-100
lines changed

2 files changed

+94
-100
lines changed

src/material/core/datetime/date-selection-model.ts

Lines changed: 83 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -10,83 +10,84 @@ import {DateAdapter} from '@angular/material/core';
1010
import {FactoryProvider, Optional, SkipSelf, Injectable} from '@angular/core';
1111
import {Subject, Observable} from 'rxjs';
1212

13-
export abstract class MatDateSelectionModel<D> {
13+
export class DateRange<D> {
14+
// DateRange should be a class with a private member.
15+
// Otherwise any object with a `start` and `end` property might be considered a
16+
// `DateRange`
17+
private _disableStructuralEquivalency: never;
18+
19+
start: D | null;
20+
end: D | null;
21+
22+
constructor(range?: { start?: D | null, end?: D | null } | null) {
23+
this.start = range && range.start || null;
24+
this.end = range && range.end || null;
25+
}
26+
}
27+
28+
type ExtractDateTypeFromSelection<T> = T extends DateRange<infer D> ? D : T;
29+
30+
export abstract class MatDateSelectionModel<S, D = ExtractDateTypeFromSelection<S>> {
1431
protected _valueChangesSubject = new Subject<void>();
1532
valueChanges: Observable<void> = this._valueChangesSubject.asObservable();
1633

17-
constructor(protected readonly adapter: DateAdapter<D>) {}
34+
constructor(protected readonly adapter: DateAdapter<D>, protected _selection: S |
35+
null = null) {}
1836

19-
destroy() {
20-
this._valueChangesSubject.complete();
21-
}
22-
23-
abstract add(date: D | null): void;
37+
abstract get selection(): S | null;
38+
abstract set selection(selection: S | null);
39+
abstract add(date: D): void;
2440
abstract isComplete(): boolean;
25-
abstract isSame(other: MatDateSelectionModel<D>): boolean;
2641
abstract isValid(): boolean;
42+
abstract isSame(other: MatDateSelectionModel<any, any>):boolean;
2743
abstract overlaps(range: DateRange<D>): boolean;
2844
}
2945

30-
export interface DateRange<D> {
31-
start: D | null;
32-
end: D | null;
33-
}
34-
3546
/**
3647
* Concrete implementation of a MatDateSelectionModel that holds a single date.
3748
*/
3849
@Injectable()
39-
export class MatSingleDateSelectionModel<D> extends MatDateSelectionModel<D> {
40-
private _date: D | null = null;
50+
export class MatSingleDateSelectionModel<D> extends MatDateSelectionModel<D, D> {
4151

4252
constructor(adapter: DateAdapter<D>, date?: D | null) {
43-
super(adapter);
44-
this._date = date === undefined ? null : date;
53+
super(adapter, date);
4554
}
4655

47-
add(date: D | null) {
48-
this._date = date;
49-
this._valueChangesSubject.next();
56+
get selection(): D | null {
57+
return this._selection;
5058
}
5159

52-
compareDate(other: MatSingleDateSelectionModel<D>) {
53-
const date = this.asDate();
54-
const otherDate = other.asDate();
55-
if (date != null && otherDate != null) {
56-
return this.adapter.compareDate(date, otherDate);
57-
}
58-
return date === otherDate;
60+
set selection(selection: D | null) {
61+
this._selection = selection;
5962
}
6063

61-
isComplete() { return this._date != null; }
62-
63-
isSame(other: MatDateSelectionModel<D>): boolean {
64-
return other instanceof MatSingleDateSelectionModel &&
65-
this.adapter.sameDate(other.asDate(), this._date);
64+
add(date: D) {
65+
this._selection = date;
66+
this._valueChangesSubject.next();
6667
}
6768

68-
isValid(): boolean {
69-
return this._date != null && this.adapter.isDateInstance(this._date) &&
70-
this.adapter.isValid(this._date);
69+
isComplete(): boolean {
70+
return this._selection != null;
7171
}
7272

73-
asDate(): D | null {
74-
return this.isValid() ? this._date : null;
73+
isValid(): boolean {
74+
return this._selection != null && this.adapter.isDateInstance(this._selection) &&
75+
this.adapter.isValid(this._selection);
7576
}
7677

77-
setDate(date: D | null) {
78-
this._date = date;
79-
this._valueChangesSubject.next();
78+
isSame(other: MatDateSelectionModel<D>): boolean {
79+
return other instanceof MatSingleDateSelectionModel &&
80+
this._selection === other.selection;
8081
}
8182

8283
/**
8384
* Determines if the single date is within a given date range. Retuns false if either dates of
8485
* the range is null or if the selection is undefined.
8586
*/
8687
overlaps(range: DateRange<D>): boolean {
87-
return !!(this._date && range.start && range.end &&
88-
this.adapter.compareDate(range.start, this._date) <= 0 &&
89-
this.adapter.compareDate(this._date, range.end) <= 0);
88+
return !!(this._selection && range.start && range.end &&
89+
this.adapter.compareDate(range.start, this._selection) <= 0 &&
90+
this.adapter.compareDate(this._selection, range.end) <= 0);
9091
}
9192
}
9293

@@ -95,14 +96,20 @@ export class MatSingleDateSelectionModel<D> extends MatDateSelectionModel<D> {
9596
* a start date and an end date.
9697
*/
9798
@Injectable()
98-
export class MatRangeDateSelectionModel<D> extends MatDateSelectionModel<D> {
99-
private _start: D | null = null;
100-
private _end: D | null = null;
99+
export class MatRangeDateSelectionModel<D> extends
100+
MatDateSelectionModel<DateRange<D>, D> {
101+
protected _selection: DateRange<D>;
101102

102-
constructor(adapter: DateAdapter<D>, start?: D | null, end?: D | null) {
103-
super(adapter);
104-
this._start = start === undefined ? null : start;
105-
this._end = end === undefined ? null : end;
103+
constructor(adapter: DateAdapter<D>, range?: DateRange<D> | null) {
104+
super(adapter, range || new DateRange<D>());
105+
}
106+
107+
get selection(): DateRange<D> | null {
108+
return new DateRange(this._selection);
109+
}
110+
111+
set selection(selection: DateRange<D> | null) {
112+
this._selection = new DateRange(selection);
106113
}
107114

108115
/**
@@ -111,63 +118,60 @@ export class MatRangeDateSelectionModel<D> extends MatDateSelectionModel<D> {
111118
* If add is called on a complete selection, it will empty the selection and set it as the start.
112119
*/
113120
add(date: D | null): void {
114-
if (this._start == null) {
115-
this._start = date;
116-
} else if (this._end == null) {
117-
this._end = date;
121+
if (this._selection.start == null) {
122+
this._selection.start = date;
123+
} else if (this._selection.end == null) {
124+
this._selection.end = date;
118125
} else {
119-
this._start = date;
120-
this._end = null;
126+
this._selection.start = date;
127+
this._selection.end = null;
121128
}
122129

123130
this._valueChangesSubject.next();
124131
}
125132

126133
setRange(start: D | null, end: D | null) {
127-
this._start = start;
128-
this._end = end;
134+
this._selection.start = start;
135+
this._selection.end = end;
129136
}
130137

131138
isComplete(): boolean {
132-
return this._start != null && this._end != null;
139+
return this._selection && this._selection.start != null && this._selection.end != null;
140+
}
141+
142+
isValid(): boolean {
143+
return this._selection.start != null && this._selection.end != null &&
144+
this.adapter.isValid(this._selection.start!) &&
145+
this.adapter.isValid(this._selection.end!);
133146
}
134147

135148
isSame(other: MatDateSelectionModel<D>): boolean {
136149
if (other instanceof MatRangeDateSelectionModel) {
137-
const otherRange = other.asRange();
138-
return this.adapter.sameDate(this._start, otherRange.start) &&
139-
this.adapter.sameDate(this._end, otherRange.end);
150+
return this._selection.start === other._selection.start &&
151+
this._selection.end === other._selection.end;
140152
}
141153
return false;
142154
}
143155

144-
isValid(): boolean {
145-
return this._start != null && this._end != null &&
146-
this.adapter.isValid(this._start!) && this.adapter.isValid(this._end!);
147-
}
148-
149-
asRange(): DateRange<D> {
150-
return {
151-
start: this._start,
152-
end: this._end,
153-
};
154-
}
155-
156156
/**
157157
* Returns true if the given range and the selection overlap in any way. False if otherwise, that
158158
* includes incomplete selections or ranges.
159159
*/
160160
overlaps(range: DateRange<D>): boolean {
161-
if (!(this._start && this._end && range.start && range.end)) {
161+
const selectionStart = this._selection.start;
162+
const selectionEnd = this._selection.end;
163+
const rangeStart = range.start;
164+
const rangeEnd = range.end;
165+
if (!(selectionStart && selectionEnd && rangeStart && rangeEnd)) {
162166
return false;
163167
}
164168

165169
return (
166-
this._isBetween(range.start, this._start, this._end) ||
167-
this._isBetween(range.end, this._start, this._end) ||
170+
this._isBetween(rangeStart, selectionStart, selectionEnd) ||
171+
this._isBetween(rangeEnd, selectionStart, selectionEnd) ||
168172
(
169-
this.adapter.compareDate(range.start, this._start) <= 0 &&
170-
this.adapter.compareDate(this._end, range.end) <= 0
173+
this.adapter.compareDate(rangeStart, selectionStart) <= 0 &&
174+
this.adapter.compareDate(selectionEnd, rangeEnd) <= 0
171175
)
172176
);
173177
}
@@ -176,15 +180,3 @@ export class MatRangeDateSelectionModel<D> extends MatDateSelectionModel<D> {
176180
return this.adapter.compareDate(from, value) <= 0 && this.adapter.compareDate(value, to) <= 0;
177181
}
178182
}
179-
180-
export function MAT_SINGLE_DATE_SELECTION_MODEL_FACTORY<D>(parent:
181-
MatSingleDateSelectionModel<D>,
182-
adapter: DateAdapter<D>) {
183-
return parent || new MatSingleDateSelectionModel(adapter);
184-
}
185-
186-
export const MAT_SINGLE_DATE_SELECTION_MODEL_PROVIDER: FactoryProvider = {
187-
provide: MatDateSelectionModel,
188-
deps: [[new Optional(), new SkipSelf(), MatDateSelectionModel], DateAdapter],
189-
useFactory: MAT_SINGLE_DATE_SELECTION_MODEL_FACTORY,
190-
};

src/material/datepicker/datepicker-input.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,22 +129,22 @@ export class MatDatepickerInput<D> implements ControlValueAccessor, OnDestroy, V
129129

130130
/** The value of the input. */
131131
@Input()
132-
get value(): D | null { return this._selection.asDate(); }
132+
get value(): D | null { return this._selectionModel.selection; }
133133
set value(value: D | null) {
134134
value = this._dateAdapter.deserialize(value);
135-
const oldDate = this._selection.asDate();
135+
const oldDate = this._selectionModel.selection;
136136
const isDifferent = !this._dateAdapter.sameDate(oldDate, value);
137-
this._selection.setDate(value);
137+
this._selectionModel.selection = value;
138138

139-
this._lastValueValid = this._selection.isValid();
139+
this._lastValueValid = this._selectionModel.isValid();
140140

141-
this._formatValue(this._selection.asDate());
141+
this._formatValue(this._selectionModel.selection);
142142

143143
if (isDifferent) {
144144
this._valueChange.emit(this.value);
145145
}
146146
}
147-
private _selection: MatSingleDateSelectionModel<D>;
147+
private _selectionModel: MatSingleDateSelectionModel<D>;
148148

149149
/** The minimum valid date. */
150150
@Input()
@@ -259,7 +259,7 @@ export class MatDatepickerInput<D> implements ControlValueAccessor, OnDestroy, V
259259
throw createMissingDateImplError('MAT_DATE_FORMATS');
260260
}
261261

262-
this._selection = new MatSingleDateSelectionModel(this._dateAdapter, null);
262+
this._selectionModel = new MatSingleDateSelectionModel(this._dateAdapter);
263263

264264
// Update the displayed date when the locale changes.
265265
this._localeSubscription = _dateAdapter.localeChanges.subscribe(() => {
@@ -334,8 +334,10 @@ export class MatDatepickerInput<D> implements ControlValueAccessor, OnDestroy, V
334334
this._lastValueValid = !date || this._dateAdapter.isValid(date);
335335
date = this._getValidDateOrNull(date);
336336

337-
if (!this._dateAdapter.sameDate(date, this._selection.asDate())) {
338-
this._selection.setDate(date);
337+
if (!this._dateAdapter.sameDate(date, this._selectionModel.selection)) {
338+
if (date) {
339+
this._selectionModel.add(date);
340+
}
339341
this._cvaOnChange(date);
340342
this._valueChange.emit(date);
341343
this.dateInput.emit(new MatDatepickerInputEvent(this, this._elementRef.nativeElement));

0 commit comments

Comments
 (0)