@apexara/ng-components 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1867 -0
- package/fesm2022/apexara-ng-components-button.mjs +66 -0
- package/fesm2022/apexara-ng-components-button.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-carousel.mjs +161 -0
- package/fesm2022/apexara-ng-components-carousel.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-checkbox.mjs +64 -0
- package/fesm2022/apexara-ng-components-checkbox.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-chip.mjs +26 -0
- package/fesm2022/apexara-ng-components-chip.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-datepicker.mjs +2175 -0
- package/fesm2022/apexara-ng-components-datepicker.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-dialog.mjs +65 -0
- package/fesm2022/apexara-ng-components-dialog.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-drawer.mjs +69 -0
- package/fesm2022/apexara-ng-components-drawer.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-expander.mjs +41 -0
- package/fesm2022/apexara-ng-components-expander.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-image.mjs +201 -0
- package/fesm2022/apexara-ng-components-image.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-input.mjs +129 -0
- package/fesm2022/apexara-ng-components-input.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-listbox.mjs +308 -0
- package/fesm2022/apexara-ng-components-listbox.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-radiobutton.mjs +40 -0
- package/fesm2022/apexara-ng-components-radiobutton.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-select.mjs +620 -0
- package/fesm2022/apexara-ng-components-select.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-services.mjs +290 -0
- package/fesm2022/apexara-ng-components-services.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-snackbar.mjs +279 -0
- package/fesm2022/apexara-ng-components-snackbar.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-table.mjs +686 -0
- package/fesm2022/apexara-ng-components-table.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-textarea.mjs +82 -0
- package/fesm2022/apexara-ng-components-textarea.mjs.map +1 -0
- package/fesm2022/apexara-ng-components-tooltip.mjs +319 -0
- package/fesm2022/apexara-ng-components-tooltip.mjs.map +1 -0
- package/fesm2022/apexara-ng-components.mjs +8 -0
- package/fesm2022/apexara-ng-components.mjs.map +1 -0
- package/package.json +146 -0
- package/types/apexara-ng-components-button.d.ts +27 -0
- package/types/apexara-ng-components-carousel.d.ts +31 -0
- package/types/apexara-ng-components-checkbox.d.ts +29 -0
- package/types/apexara-ng-components-chip.d.ts +11 -0
- package/types/apexara-ng-components-datepicker.d.ts +172 -0
- package/types/apexara-ng-components-dialog.d.ts +24 -0
- package/types/apexara-ng-components-drawer.d.ts +22 -0
- package/types/apexara-ng-components-expander.d.ts +10 -0
- package/types/apexara-ng-components-image.d.ts +46 -0
- package/types/apexara-ng-components-input.d.ts +51 -0
- package/types/apexara-ng-components-listbox.d.ts +70 -0
- package/types/apexara-ng-components-radiobutton.d.ts +30 -0
- package/types/apexara-ng-components-select.d.ts +111 -0
- package/types/apexara-ng-components-services.d.ts +96 -0
- package/types/apexara-ng-components-snackbar.d.ts +73 -0
- package/types/apexara-ng-components-table.d.ts +157 -0
- package/types/apexara-ng-components-textarea.d.ts +34 -0
- package/types/apexara-ng-components-tooltip.d.ts +54 -0
- package/types/apexara-ng-components.d.ts +3 -0
|
@@ -0,0 +1,2175 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { EventEmitter, Output, Input, ChangeDetectionStrategy, Component, Injectable, ViewChild, HostListener } from '@angular/core';
|
|
3
|
+
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
4
|
+
import * as i1$1 from '@apexara/ng-components/services';
|
|
5
|
+
import { MobileOS } from '@apexara/ng-components/services';
|
|
6
|
+
import { IpiButtonComponent } from '@apexara/ng-components/button';
|
|
7
|
+
import * as i1 from '@angular/cdk/platform';
|
|
8
|
+
import { normalizePassiveListenerOptions } from '@angular/cdk/platform';
|
|
9
|
+
import { hasModifierKey } from '@angular/cdk/keycodes';
|
|
10
|
+
|
|
11
|
+
class CalendarCell {
|
|
12
|
+
constructor(value, displayValue, ariaLabel, enabled, compareValue = value, rawValue) {
|
|
13
|
+
this.value = value;
|
|
14
|
+
this.displayValue = displayValue;
|
|
15
|
+
this.ariaLabel = ariaLabel;
|
|
16
|
+
this.enabled = enabled;
|
|
17
|
+
this.compareValue = compareValue;
|
|
18
|
+
this.rawValue = rawValue;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
let calendarBodyId = 1;
|
|
22
|
+
class CalendarBody {
|
|
23
|
+
constructor(ngZone, platform, elementRef) {
|
|
24
|
+
this.ngZone = ngZone;
|
|
25
|
+
this.platform = platform;
|
|
26
|
+
this.elementRef = elementRef;
|
|
27
|
+
/** The number of columns in the table. */
|
|
28
|
+
this.numCols = 7;
|
|
29
|
+
/** The cell number of the active cell in the table. */
|
|
30
|
+
this.activeCell = 0;
|
|
31
|
+
/** Whether a range is being selected. */
|
|
32
|
+
this.isRange = false;
|
|
33
|
+
/**
|
|
34
|
+
* The aspect ratio (width / height) to use for the cells in the table. This aspect ratio will be
|
|
35
|
+
* maintained even as the table resizes.
|
|
36
|
+
*/
|
|
37
|
+
this.cellAspectRatio = 1;
|
|
38
|
+
/** Start of the preview range. */
|
|
39
|
+
this.previewStart = null;
|
|
40
|
+
/** End of the preview range. */
|
|
41
|
+
this.previewEnd = null;
|
|
42
|
+
/** Emits when a new value is selected. */
|
|
43
|
+
this.selectedValueChange = new EventEmitter();
|
|
44
|
+
/** Emits when the preview has changed as a result of a user action. */
|
|
45
|
+
this.previewChange = new EventEmitter();
|
|
46
|
+
this.activeDateChange = new EventEmitter();
|
|
47
|
+
/** Emits the date at the possible start of a drag event. */
|
|
48
|
+
this.dragStarted = new EventEmitter();
|
|
49
|
+
/** Emits the date at the conclusion of a drag, or null if mouse was not released on a date. */
|
|
50
|
+
this.dragEnded = new EventEmitter();
|
|
51
|
+
this.id = `calendar-body-${calendarBodyId++}`;
|
|
52
|
+
this.startDateLabelId = `${this.id}-start-date`;
|
|
53
|
+
this.endDateLabelId = `${this.id}-end-date`;
|
|
54
|
+
/**
|
|
55
|
+
* Used to focus the active cell after change detection has run.
|
|
56
|
+
*/
|
|
57
|
+
this.focusActiveCellAfterViewChecked = true;
|
|
58
|
+
this.didDragSinceMouseDown = false;
|
|
59
|
+
this.activeCapturingEventOptions = normalizePassiveListenerOptions({
|
|
60
|
+
passive: false,
|
|
61
|
+
capture: true,
|
|
62
|
+
});
|
|
63
|
+
this.passiveCapturingEventOptions = normalizePassiveListenerOptions({
|
|
64
|
+
passive: true,
|
|
65
|
+
capture: true,
|
|
66
|
+
});
|
|
67
|
+
this.passiveEventOptions = normalizePassiveListenerOptions({ passive: true });
|
|
68
|
+
this.trackRow = (row) => row;
|
|
69
|
+
/**
|
|
70
|
+
* Event handler for when the user enters an element
|
|
71
|
+
* inside the calendar body (e.g. by hovering in or focus).
|
|
72
|
+
*/
|
|
73
|
+
this.enterHandler = (event) => {
|
|
74
|
+
if (this.skipNextFocus && event.type === 'focus') {
|
|
75
|
+
this.skipNextFocus = false;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// We only need to hit the zone when we're selecting a range.
|
|
79
|
+
if (event.target && this.isRange) {
|
|
80
|
+
const cell = this.getCellFromElement(event.target);
|
|
81
|
+
if (!cell?.enabled) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (cell) {
|
|
85
|
+
this.ngZone.run(() => this.previewChange.emit({ value: cell.enabled ? cell : null, event }));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
this.touchmoveHandler = (event) => {
|
|
90
|
+
if (!this.isRange)
|
|
91
|
+
return;
|
|
92
|
+
const target = this.getActualTouchTarget(event);
|
|
93
|
+
const cell = target ? this.getCellFromElement(target) : null;
|
|
94
|
+
if (!cell?.enabled) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (target !== event.target) {
|
|
98
|
+
this.didDragSinceMouseDown = true;
|
|
99
|
+
}
|
|
100
|
+
// If the initial target of the touch is a date cell, prevent default so
|
|
101
|
+
// that the move is not handled as a scroll.
|
|
102
|
+
if (this.getCellElement(event.target)) {
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
}
|
|
105
|
+
this.ngZone.run(() => this.previewChange.emit({ value: cell?.enabled ? cell : null, event }));
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Event handler for when the user's pointer leaves an element
|
|
109
|
+
* inside the calendar body (e.g. by hovering out or blurring).
|
|
110
|
+
*/
|
|
111
|
+
this.leaveHandler = (event) => {
|
|
112
|
+
// We only need to hit the zone when we're selecting a range.
|
|
113
|
+
if (this.previewEnd !== null && this.isRange) {
|
|
114
|
+
if (event.type !== 'blur') {
|
|
115
|
+
this.didDragSinceMouseDown = true;
|
|
116
|
+
}
|
|
117
|
+
const cell = this.getCellFromElement(event.target);
|
|
118
|
+
if (!cell?.enabled) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Only reset the preview end value when leaving cells. This looks better, because
|
|
122
|
+
// we have a gap between the cells and the rows and we don't want to remove the
|
|
123
|
+
// range just for it to show up again when the user moves a few pixels to the side.
|
|
124
|
+
if (event.target &&
|
|
125
|
+
cell &&
|
|
126
|
+
!(event.relatedTarget &&
|
|
127
|
+
this.getCellFromElement(event.relatedTarget))) {
|
|
128
|
+
this.ngZone.run(() => this.previewChange.emit({ value: null, event }));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Triggered on mousedown or touchstart on a date cell.
|
|
134
|
+
* Respsonsible for starting a drag sequence.
|
|
135
|
+
*/
|
|
136
|
+
this.mousedownHandler = (event) => {
|
|
137
|
+
if (!this.isRange)
|
|
138
|
+
return;
|
|
139
|
+
this.didDragSinceMouseDown = false;
|
|
140
|
+
// Begin a drag if a cell within the current range was targeted.
|
|
141
|
+
const cell = event.target && this.getCellFromElement(event.target);
|
|
142
|
+
if (!cell || !this.isCellInRange(cell.compareValue)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
this.ngZone.run(() => {
|
|
146
|
+
this.dragStarted.emit({
|
|
147
|
+
value: cell.rawValue,
|
|
148
|
+
event,
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
/** Triggered on mouseup anywhere. Respsonsible for ending a drag sequence. */
|
|
153
|
+
this.mouseupHandler = (event) => {
|
|
154
|
+
if (!this.isRange)
|
|
155
|
+
return;
|
|
156
|
+
const cellElement = this.getCellElement(event.target);
|
|
157
|
+
if (!cellElement) {
|
|
158
|
+
// Mouseup happened outside of datepicker. Cancel drag.
|
|
159
|
+
this.ngZone.run(() => {
|
|
160
|
+
this.dragEnded.emit({ value: null, event });
|
|
161
|
+
});
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// if (cellElement.closest('.calendar-body') !== this.elementRef.nativeElement) {
|
|
165
|
+
// // Mouseup happened inside a different month instance.
|
|
166
|
+
// // Allow it to handle the event.
|
|
167
|
+
// return;
|
|
168
|
+
// }
|
|
169
|
+
this.ngZone.run(() => {
|
|
170
|
+
const cell = this.getCellFromElement(cellElement);
|
|
171
|
+
this.dragEnded.emit({ value: cell?.rawValue ?? null, event });
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
/** Triggered on touchend anywhere. Respsonsible for ending a drag sequence. */
|
|
175
|
+
this.touchendHandler = (event) => {
|
|
176
|
+
const target = this.getActualTouchTarget(event);
|
|
177
|
+
if (target) {
|
|
178
|
+
this.mouseupHandler({ target });
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
this.ngZone.runOutsideAngular(() => {
|
|
182
|
+
const element = this.elementRef.nativeElement;
|
|
183
|
+
element.addEventListener('touchmove', this.touchmoveHandler, this.activeCapturingEventOptions);
|
|
184
|
+
element.addEventListener('mouseenter', this.enterHandler, this.passiveCapturingEventOptions);
|
|
185
|
+
element.addEventListener('focus', this.enterHandler, this.passiveCapturingEventOptions);
|
|
186
|
+
element.addEventListener('mouseleave', this.leaveHandler, this.passiveCapturingEventOptions);
|
|
187
|
+
element.addEventListener('mousedown', this.mousedownHandler, this.passiveEventOptions);
|
|
188
|
+
element.addEventListener('touchstart', this.mousedownHandler, this.passiveEventOptions);
|
|
189
|
+
if (this.platform.isBrowser) {
|
|
190
|
+
window.addEventListener('mouseup', this.mouseupHandler);
|
|
191
|
+
window.addEventListener('touchend', this.touchendHandler);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
ngOnChanges(changes) {
|
|
196
|
+
const columnChanges = changes['numCols'];
|
|
197
|
+
const { rows, numCols } = this;
|
|
198
|
+
if (changes['rows'] || columnChanges) {
|
|
199
|
+
this.firstRowOffset = rows && rows.length && rows[0].length ? numCols - rows[0].length : 0;
|
|
200
|
+
}
|
|
201
|
+
if (changes['cellAspectRatio'] || columnChanges || !this.cellPadding) {
|
|
202
|
+
this.cellPadding = `${(50 * this.cellAspectRatio) / numCols}%`;
|
|
203
|
+
}
|
|
204
|
+
if (columnChanges || !this.cellWidth) {
|
|
205
|
+
this.cellWidth = `${100 / numCols}%`;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
ngAfterViewChecked() {
|
|
209
|
+
if (this.focusActiveCellAfterViewChecked) {
|
|
210
|
+
this.focusActiveCell();
|
|
211
|
+
this.focusActiveCellAfterViewChecked = false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
ngOnDestroy() {
|
|
215
|
+
const element = this.elementRef.nativeElement;
|
|
216
|
+
element.removeEventListener('touchmove', this.touchmoveHandler, this.activeCapturingEventOptions);
|
|
217
|
+
element.removeEventListener('mouseenter', this.enterHandler, this.passiveCapturingEventOptions);
|
|
218
|
+
element.removeEventListener('focus', this.enterHandler, this.passiveCapturingEventOptions);
|
|
219
|
+
element.removeEventListener('mouseleave', this.leaveHandler, this.passiveCapturingEventOptions);
|
|
220
|
+
element.removeEventListener('mousedown', this.mousedownHandler, this.passiveEventOptions);
|
|
221
|
+
element.removeEventListener('touchstart', this.mousedownHandler, this.passiveEventOptions);
|
|
222
|
+
if (this.platform.isBrowser) {
|
|
223
|
+
window.removeEventListener('mouseup', this.mouseupHandler);
|
|
224
|
+
window.removeEventListener('touchend', this.touchendHandler);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/** Called when a cell is clicked. */
|
|
228
|
+
cellClicked(cell, event) {
|
|
229
|
+
// Ignore "clicks" that are actually canceled drags (eg the user dragged
|
|
230
|
+
// off and then went back to this cell to undo).
|
|
231
|
+
if (this.didDragSinceMouseDown) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (cell.enabled) {
|
|
235
|
+
this.selectedValueChange.emit({ value: cell.value, event });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
emitActiveDateChange(cell, event) {
|
|
239
|
+
if (cell.enabled) {
|
|
240
|
+
this.activeDateChange.emit({ value: cell.value, event });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/** Returns whether a cell should be marked as selected. */
|
|
244
|
+
isSelected(value) {
|
|
245
|
+
return this.startValue === value || this.endValue === value;
|
|
246
|
+
}
|
|
247
|
+
/** Returns whether a cell is active. */
|
|
248
|
+
isActiveCell(rowIndex, colIndex) {
|
|
249
|
+
let cellNumber = rowIndex * this.numCols + colIndex;
|
|
250
|
+
// Account for the fact that the first row may not have as many cells.
|
|
251
|
+
if (rowIndex) {
|
|
252
|
+
cellNumber -= this.firstRowOffset;
|
|
253
|
+
}
|
|
254
|
+
return cellNumber == this.activeCell;
|
|
255
|
+
}
|
|
256
|
+
focusActiveCell(movePreview = true) {
|
|
257
|
+
const activeCell = this.elementRef.nativeElement.querySelector('.active');
|
|
258
|
+
if (activeCell) {
|
|
259
|
+
if (!movePreview) {
|
|
260
|
+
this.skipNextFocus = true;
|
|
261
|
+
}
|
|
262
|
+
activeCell.focus();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/** Focuses the active cell after change detection has run and the microtask queue is empty. */
|
|
266
|
+
scheduleFocusActiveCellAfterViewChecked() {
|
|
267
|
+
this.focusActiveCellAfterViewChecked = true;
|
|
268
|
+
}
|
|
269
|
+
/** Gets whether a value is the start of the main range. */
|
|
270
|
+
isRangeStart(value) {
|
|
271
|
+
return this.isStart(value, this.startValue, this.endValue);
|
|
272
|
+
}
|
|
273
|
+
/** Gets whether a value is the end of the main range. */
|
|
274
|
+
isRangeEnd(value) {
|
|
275
|
+
return this.isEnd(value, this.startValue, this.endValue);
|
|
276
|
+
}
|
|
277
|
+
/** Gets whether a value is within the currently-selected range. */
|
|
278
|
+
isCellInRange(value) {
|
|
279
|
+
return this.isInRange(value, this.startValue, this.endValue, this.isRange);
|
|
280
|
+
}
|
|
281
|
+
/** Gets whether a value is the start of the preview range. */
|
|
282
|
+
isPreviewStart(value) {
|
|
283
|
+
return this.isStart(value, this.previewStart, this.previewEnd);
|
|
284
|
+
}
|
|
285
|
+
/** Gets whether a value is the end of the preview range. */
|
|
286
|
+
isPreviewEnd(value) {
|
|
287
|
+
return this.isEnd(value, this.previewStart, this.previewEnd);
|
|
288
|
+
}
|
|
289
|
+
/** Gets whether a value is inside the preview range. */
|
|
290
|
+
isInPreview(value) {
|
|
291
|
+
return this.isInRange(value, this.previewStart, this.previewEnd, this.isRange);
|
|
292
|
+
}
|
|
293
|
+
/** Gets ids of aria descriptions for the start and end of a date range. */
|
|
294
|
+
getDescribedby(value) {
|
|
295
|
+
if (!this.isRange) {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
if (this.startValue === value && this.endValue === value) {
|
|
299
|
+
return `${this.startDateLabelId} ${this.endDateLabelId}`;
|
|
300
|
+
}
|
|
301
|
+
else if (this.startValue === value) {
|
|
302
|
+
return this.startDateLabelId;
|
|
303
|
+
}
|
|
304
|
+
else if (this.endValue === value) {
|
|
305
|
+
return this.endDateLabelId;
|
|
306
|
+
}
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
/** Finds the CalendarCell that corresponds to a DOM node. */
|
|
310
|
+
getCellFromElement(element) {
|
|
311
|
+
const cell = this.getCellElement(element);
|
|
312
|
+
if (cell) {
|
|
313
|
+
const row = cell.getAttribute('data-row');
|
|
314
|
+
const col = cell.getAttribute('data-col');
|
|
315
|
+
if (row && col) {
|
|
316
|
+
return this.rows[parseInt(row)][parseInt(col)];
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Gets the date table cell element that is or contains the specified element.
|
|
323
|
+
* Or returns null if element is not part of a date cell.
|
|
324
|
+
*/
|
|
325
|
+
getCellElement(element) {
|
|
326
|
+
let cell;
|
|
327
|
+
if (this.isTableCell(element)) {
|
|
328
|
+
cell = element;
|
|
329
|
+
}
|
|
330
|
+
else if (this.isTableCell(element.parentNode)) {
|
|
331
|
+
cell = element.parentNode;
|
|
332
|
+
}
|
|
333
|
+
else if (this.isTableCell(element.parentNode?.parentNode)) {
|
|
334
|
+
cell = element.parentNode.parentNode;
|
|
335
|
+
}
|
|
336
|
+
return cell?.getAttribute('data-row') != null ? cell : null;
|
|
337
|
+
}
|
|
338
|
+
/** Checks whether a node is a table cell element. */
|
|
339
|
+
isTableCell(node) {
|
|
340
|
+
return node?.nodeName === 'TD';
|
|
341
|
+
}
|
|
342
|
+
/** Checks whether a value is the start of a range. */
|
|
343
|
+
isStart(value, start, end) {
|
|
344
|
+
return end !== null && start !== end && value < end && value === start;
|
|
345
|
+
}
|
|
346
|
+
/** Checks whether a value is the end of a range. */
|
|
347
|
+
isEnd(value, start, end) {
|
|
348
|
+
return start !== null && start !== end && value >= start && value === end;
|
|
349
|
+
}
|
|
350
|
+
isInRange(value, start, end, rangeEnabled) {
|
|
351
|
+
return (rangeEnabled &&
|
|
352
|
+
start !== null &&
|
|
353
|
+
end !== null &&
|
|
354
|
+
start !== end &&
|
|
355
|
+
value >= start &&
|
|
356
|
+
value <= end);
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Extracts the element that actually corresponds to a touch event's location
|
|
360
|
+
* (rather than the element that initiated the sequence of touch events).
|
|
361
|
+
*/
|
|
362
|
+
getActualTouchTarget(event) {
|
|
363
|
+
const touchLocation = event.changedTouches[0];
|
|
364
|
+
return document.elementFromPoint(touchLocation.clientX, touchLocation.clientY);
|
|
365
|
+
}
|
|
366
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: CalendarBody, deps: [{ token: i0.NgZone }, { token: i1.Platform }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
367
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: CalendarBody, isStandalone: true, selector: "[calendar-body]", inputs: { label: "label", rows: "rows", todayValue: "todayValue", startValue: "startValue", endValue: "endValue", labelMinRequiredCells: "labelMinRequiredCells", numCols: "numCols", activeCell: "activeCell", isRange: "isRange", cellAspectRatio: "cellAspectRatio", comparisonStart: "comparisonStart", comparisonEnd: "comparisonEnd", previewStart: "previewStart", previewEnd: "previewEnd", startDateAccessibleName: "startDateAccessibleName", endDateAccessibleName: "endDateAccessibleName" }, outputs: { selectedValueChange: "selectedValueChange", previewChange: "previewChange", activeDateChange: "activeDateChange", dragStarted: "dragStarted", dragEnded: "dragEnded" }, host: { classAttribute: "calendar-body" }, usesOnChanges: true, ngImport: i0, template: "@if (firstRowOffset < labelMinRequiredCells) {\n <tr aria-hidden=\"true\">\n <td class=\"calendar-body-label\"\n [attr.colspan]=\"numCols\"\n [style.paddingTop]=\"cellPadding\"\n [style.paddingBottom]=\"cellPadding\">\n {{ label }}\n </td>\n </tr>\n}\n\n@for (row of rows; track trackRow(row); let rowIndex = $index) {\n <tr role=\"row\">\n @if (rowIndex === 0 && firstRowOffset) {\n <td\n class=\"calendar-body-label\"\n [attr.colspan]=\"firstRowOffset\"\n [style.paddingTop]=\"cellPadding\"\n [style.paddingBottom]=\"cellPadding\">\n {{ firstRowOffset >= labelMinRequiredCells ? label : ''}}\n </td>\n }\n\n @for (item of row; track item; let colIndex = $index) {\n <td\n role=\"gridcell\"\n class=\"calendar-body-cell-container\"\n [style.width]=\"cellWidth\"\n [style.paddingTop]=\"cellPadding\"\n [style.paddingBottom]=\"cellPadding\"\n [attr.data-row]=\"rowIndex\"\n [attr.data-col]=\"colIndex\"\n >\n <button\n type=\"button\"\n class=\"calendar-body-cell\"\n [tabindex]=\"isActiveCell(rowIndex, colIndex) ? 0 : -1\"\n [class.disabled]=\"!item.enabled\"\n [class.selected]=\"isSelected(item.compareValue)\"\n [class.active]=\"isActiveCell(rowIndex, colIndex)\"\n [class.today]=\"todayValue === item.compareValue\"\n [class.range-start]=\"isRangeStart(item.compareValue)\"\n [class.range-end]=\"isRangeEnd(item.compareValue)\"\n [class.in-range]=\"isCellInRange(item.compareValue)\"\n [class.in-preview]=\"isInPreview(item.compareValue)\"\n [class.preview-start]=\"isPreviewStart(item.compareValue)\"\n [class.preview-end]=\"isPreviewEnd(item.compareValue)\"\n [attr.aria-label]=\"item.ariaLabel\"\n [attr.aria-disabled]=\"!item.enabled || null\"\n [attr.aria-pressed]=\"isSelected(item.compareValue)\"\n [attr.aria-current]=\"todayValue === item.compareValue ? 'date' : null\"\n [attr.aria-describedby]=\"getDescribedby(item.compareValue)\"\n (click)=\"cellClicked(item, $event)\"\n (focus)=\"emitActiveDateChange(item, $event)\">\n <span class=\"calendar-body-cell-content focus-indicator\">\n {{ item.displayValue }}\n </span>\n\n <span class=\"calendar-body-cell-preview\" aria-hidden=\"true\"></span>\n </button>\n </td>\n }\n </tr>\n}\n\n<span [id]=\"startDateLabelId\" class=\"calendar-body-hidden-label\">{{ startDateAccessibleName }}</span>\n\n<span [id]=\"endDateLabelId\" class=\"calendar-body-hidden-label\">{{ endDateAccessibleName }}</span>\n", styles: ["tr{height:40px}.calendar-body-label{height:0;line-height:0;text-align:start}.calendar-body-cell{width:100%;height:100%;position:absolute;top:0;left:0;outline:none;text-align:center;color:var(--ipi-calendar-body-cell-color, #0B1222);background:var(--ipi-datepicker-calendar-body-cell-background-color, none);border:var(--ipi-calendar-body-cell-border, none);margin:0}.calendar-body-hidden-label{display:none}.calendar-body-cell-container{position:relative;height:0;line-height:0}.calendar-body-cell:before,.calendar-body-cell:after,.calendar-body-cell-preview{height:100%;width:100%;display:block;position:absolute;top:0;left:0;content:\"\";box-sizing:border-box;z-index:0}.calendar-body-cell{border-radius:4px;transition:border-radius .1s ease-out}.calendar-body-cell:hover{color:var(--ipi-calendar-body-cell-hover-color, white);background-color:var(--ipi-calendar-body-cell-hover-background-color, #FFD2C6)}.today{border:var(--ipi-calendar-body-today-cell-border, 1px solid #FFD2C6)}.in-preview,.calendar-body-cell.in-preview,.calendar-body-cell.in-range.in-preview{background-color:var(--ipi-calendar-body-cell-in-preview-background-color, #FFD2C6);border-left:0px;border-right:0px;border-radius:0;border-top:var(--ipi-calendar-body-cell-in-preview-border, 1px dashed #F96138);border-bottom:var(--ipi-calendar-body-cell-in-preview-border, 1px dashed #F96138)}.calendar-body-cell.in-preview.preview-end{border-top-right-radius:20px;border-bottom-right-radius:20px;border-right:var(--ipi-calendar-body-cell-preview-end-border, 1px dashed #F96138);background-color:var(--ipi-calendar-body-cell-preview-end-background-color, #FFD2C6);color:var(--ipi-calendar-body-cell-preview-end-color, #FFFFFF)}.calendar-body-cell.in-preview.preview-start{border-top-left-radius:20px;border-bottom-left-radius:20px;border-left:var(--ipi-calendar-body-cell-preview-start-border, 1px dashed #F96138);background-color:var(--ipi-calendar-body-cell-preview-start-background-color, #FFD2C6);color:var(--ipi-calendar-body-cell-preview-start-color, #FFFFFF)}.calendar-body-cell:hover .in-preview,.calendar-body-cell:hover .in-range{border-radius:0 20px 20px 0/0px 20px 20px 0px}.active:focus{background-color:var(--ipi-calendar-body-active-cell-background-color, #FFD2C6)}.calendar-body-cell.in-range{background-color:var(--ipi-calendar-body-cell-in-range-background-color, #FFF2EF);border-left:0px;border-right:0px;border-radius:0;border-top:var(--ipi-calendar-body-cell-in-range-border, 1px solid #F96138);border-bottom:var(--ipi-calendar-body-cell-in-range-border, 1px solid #F96138)}.in-range.active{background-color:var(--ipi-calendar-body-cell-in-range-active-background-color, #FFD2C6)}.in-range.range-start{border-top-left-radius:20px;border-bottom-left-radius:20px;color:var(--ipi-calendar-body-cell-range-start-color, #FFFFFF);border-left:var(--ipi-calendar-body-cell-range-start-border, 1px solid #F96138);background-color:var(--ipi-calendar-body-cell-range-start-background-color, #F96138)}.in-range.range-end{border-top-right-radius:20px;border-bottom-right-radius:20px;color:var(--ipi-calendar-body-cell-range-end-color, #FFFFFF);border-right:var(--ipi-datepicker-calendar-body-cell-end-start-border, 1px solid #F96138);background-color:var(--ipi-datepicker-calendar-body-cell-end-start-background-color, #F96138)}.calendar-body-cell.in-range.range-end,.calendar-body-cell.in-range.range-start{background-color:var(--ipi-datepicker-calendar-body-cell-end-start-background-color, #F96138)}.active.in-range.range-end,.active.in-range.range-start{background-color:var(--ipi-calendar-body-cell-active-in-range-start-end-background-color, #FFD2C6)}.calendar-body-cell.selected{color:var(--ipi-calendar-body-cell-selected-color, #FFFFFF);background-color:var(--ipi-calendar-body-cell-selected-background-color, #F96138)}.disabled{cursor:none;border:none;border-radius:0;pointer-events:none;background-color:var(--ipi-calendar-body-cell-disabled-background-color, #E0DFDE)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
368
|
+
}
|
|
369
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: CalendarBody, decorators: [{
|
|
370
|
+
type: Component,
|
|
371
|
+
args: [{ selector: '[calendar-body]', host: {
|
|
372
|
+
'class': 'calendar-body',
|
|
373
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (firstRowOffset < labelMinRequiredCells) {\n <tr aria-hidden=\"true\">\n <td class=\"calendar-body-label\"\n [attr.colspan]=\"numCols\"\n [style.paddingTop]=\"cellPadding\"\n [style.paddingBottom]=\"cellPadding\">\n {{ label }}\n </td>\n </tr>\n}\n\n@for (row of rows; track trackRow(row); let rowIndex = $index) {\n <tr role=\"row\">\n @if (rowIndex === 0 && firstRowOffset) {\n <td\n class=\"calendar-body-label\"\n [attr.colspan]=\"firstRowOffset\"\n [style.paddingTop]=\"cellPadding\"\n [style.paddingBottom]=\"cellPadding\">\n {{ firstRowOffset >= labelMinRequiredCells ? label : ''}}\n </td>\n }\n\n @for (item of row; track item; let colIndex = $index) {\n <td\n role=\"gridcell\"\n class=\"calendar-body-cell-container\"\n [style.width]=\"cellWidth\"\n [style.paddingTop]=\"cellPadding\"\n [style.paddingBottom]=\"cellPadding\"\n [attr.data-row]=\"rowIndex\"\n [attr.data-col]=\"colIndex\"\n >\n <button\n type=\"button\"\n class=\"calendar-body-cell\"\n [tabindex]=\"isActiveCell(rowIndex, colIndex) ? 0 : -1\"\n [class.disabled]=\"!item.enabled\"\n [class.selected]=\"isSelected(item.compareValue)\"\n [class.active]=\"isActiveCell(rowIndex, colIndex)\"\n [class.today]=\"todayValue === item.compareValue\"\n [class.range-start]=\"isRangeStart(item.compareValue)\"\n [class.range-end]=\"isRangeEnd(item.compareValue)\"\n [class.in-range]=\"isCellInRange(item.compareValue)\"\n [class.in-preview]=\"isInPreview(item.compareValue)\"\n [class.preview-start]=\"isPreviewStart(item.compareValue)\"\n [class.preview-end]=\"isPreviewEnd(item.compareValue)\"\n [attr.aria-label]=\"item.ariaLabel\"\n [attr.aria-disabled]=\"!item.enabled || null\"\n [attr.aria-pressed]=\"isSelected(item.compareValue)\"\n [attr.aria-current]=\"todayValue === item.compareValue ? 'date' : null\"\n [attr.aria-describedby]=\"getDescribedby(item.compareValue)\"\n (click)=\"cellClicked(item, $event)\"\n (focus)=\"emitActiveDateChange(item, $event)\">\n <span class=\"calendar-body-cell-content focus-indicator\">\n {{ item.displayValue }}\n </span>\n\n <span class=\"calendar-body-cell-preview\" aria-hidden=\"true\"></span>\n </button>\n </td>\n }\n </tr>\n}\n\n<span [id]=\"startDateLabelId\" class=\"calendar-body-hidden-label\">{{ startDateAccessibleName }}</span>\n\n<span [id]=\"endDateLabelId\" class=\"calendar-body-hidden-label\">{{ endDateAccessibleName }}</span>\n", styles: ["tr{height:40px}.calendar-body-label{height:0;line-height:0;text-align:start}.calendar-body-cell{width:100%;height:100%;position:absolute;top:0;left:0;outline:none;text-align:center;color:var(--ipi-calendar-body-cell-color, #0B1222);background:var(--ipi-datepicker-calendar-body-cell-background-color, none);border:var(--ipi-calendar-body-cell-border, none);margin:0}.calendar-body-hidden-label{display:none}.calendar-body-cell-container{position:relative;height:0;line-height:0}.calendar-body-cell:before,.calendar-body-cell:after,.calendar-body-cell-preview{height:100%;width:100%;display:block;position:absolute;top:0;left:0;content:\"\";box-sizing:border-box;z-index:0}.calendar-body-cell{border-radius:4px;transition:border-radius .1s ease-out}.calendar-body-cell:hover{color:var(--ipi-calendar-body-cell-hover-color, white);background-color:var(--ipi-calendar-body-cell-hover-background-color, #FFD2C6)}.today{border:var(--ipi-calendar-body-today-cell-border, 1px solid #FFD2C6)}.in-preview,.calendar-body-cell.in-preview,.calendar-body-cell.in-range.in-preview{background-color:var(--ipi-calendar-body-cell-in-preview-background-color, #FFD2C6);border-left:0px;border-right:0px;border-radius:0;border-top:var(--ipi-calendar-body-cell-in-preview-border, 1px dashed #F96138);border-bottom:var(--ipi-calendar-body-cell-in-preview-border, 1px dashed #F96138)}.calendar-body-cell.in-preview.preview-end{border-top-right-radius:20px;border-bottom-right-radius:20px;border-right:var(--ipi-calendar-body-cell-preview-end-border, 1px dashed #F96138);background-color:var(--ipi-calendar-body-cell-preview-end-background-color, #FFD2C6);color:var(--ipi-calendar-body-cell-preview-end-color, #FFFFFF)}.calendar-body-cell.in-preview.preview-start{border-top-left-radius:20px;border-bottom-left-radius:20px;border-left:var(--ipi-calendar-body-cell-preview-start-border, 1px dashed #F96138);background-color:var(--ipi-calendar-body-cell-preview-start-background-color, #FFD2C6);color:var(--ipi-calendar-body-cell-preview-start-color, #FFFFFF)}.calendar-body-cell:hover .in-preview,.calendar-body-cell:hover .in-range{border-radius:0 20px 20px 0/0px 20px 20px 0px}.active:focus{background-color:var(--ipi-calendar-body-active-cell-background-color, #FFD2C6)}.calendar-body-cell.in-range{background-color:var(--ipi-calendar-body-cell-in-range-background-color, #FFF2EF);border-left:0px;border-right:0px;border-radius:0;border-top:var(--ipi-calendar-body-cell-in-range-border, 1px solid #F96138);border-bottom:var(--ipi-calendar-body-cell-in-range-border, 1px solid #F96138)}.in-range.active{background-color:var(--ipi-calendar-body-cell-in-range-active-background-color, #FFD2C6)}.in-range.range-start{border-top-left-radius:20px;border-bottom-left-radius:20px;color:var(--ipi-calendar-body-cell-range-start-color, #FFFFFF);border-left:var(--ipi-calendar-body-cell-range-start-border, 1px solid #F96138);background-color:var(--ipi-calendar-body-cell-range-start-background-color, #F96138)}.in-range.range-end{border-top-right-radius:20px;border-bottom-right-radius:20px;color:var(--ipi-calendar-body-cell-range-end-color, #FFFFFF);border-right:var(--ipi-datepicker-calendar-body-cell-end-start-border, 1px solid #F96138);background-color:var(--ipi-datepicker-calendar-body-cell-end-start-background-color, #F96138)}.calendar-body-cell.in-range.range-end,.calendar-body-cell.in-range.range-start{background-color:var(--ipi-datepicker-calendar-body-cell-end-start-background-color, #F96138)}.active.in-range.range-end,.active.in-range.range-start{background-color:var(--ipi-calendar-body-cell-active-in-range-start-end-background-color, #FFD2C6)}.calendar-body-cell.selected{color:var(--ipi-calendar-body-cell-selected-color, #FFFFFF);background-color:var(--ipi-calendar-body-cell-selected-background-color, #F96138)}.disabled{cursor:none;border:none;border-radius:0;pointer-events:none;background-color:var(--ipi-calendar-body-cell-disabled-background-color, #E0DFDE)}\n"] }]
|
|
374
|
+
}], ctorParameters: () => [{ type: i0.NgZone }, { type: i1.Platform }, { type: i0.ElementRef }], propDecorators: { label: [{
|
|
375
|
+
type: Input
|
|
376
|
+
}], rows: [{
|
|
377
|
+
type: Input
|
|
378
|
+
}], todayValue: [{
|
|
379
|
+
type: Input
|
|
380
|
+
}], startValue: [{
|
|
381
|
+
type: Input
|
|
382
|
+
}], endValue: [{
|
|
383
|
+
type: Input
|
|
384
|
+
}], labelMinRequiredCells: [{
|
|
385
|
+
type: Input
|
|
386
|
+
}], numCols: [{
|
|
387
|
+
type: Input
|
|
388
|
+
}], activeCell: [{
|
|
389
|
+
type: Input
|
|
390
|
+
}], isRange: [{
|
|
391
|
+
type: Input
|
|
392
|
+
}], cellAspectRatio: [{
|
|
393
|
+
type: Input
|
|
394
|
+
}], comparisonStart: [{
|
|
395
|
+
type: Input
|
|
396
|
+
}], comparisonEnd: [{
|
|
397
|
+
type: Input
|
|
398
|
+
}], previewStart: [{
|
|
399
|
+
type: Input
|
|
400
|
+
}], previewEnd: [{
|
|
401
|
+
type: Input
|
|
402
|
+
}], startDateAccessibleName: [{
|
|
403
|
+
type: Input
|
|
404
|
+
}], endDateAccessibleName: [{
|
|
405
|
+
type: Input
|
|
406
|
+
}], selectedValueChange: [{
|
|
407
|
+
type: Output
|
|
408
|
+
}], previewChange: [{
|
|
409
|
+
type: Output
|
|
410
|
+
}], activeDateChange: [{
|
|
411
|
+
type: Output
|
|
412
|
+
}], dragStarted: [{
|
|
413
|
+
type: Output
|
|
414
|
+
}], dragEnded: [{
|
|
415
|
+
type: Output
|
|
416
|
+
}] } });
|
|
417
|
+
|
|
418
|
+
class IpiDatepickerService {
|
|
419
|
+
constructor() {
|
|
420
|
+
this.DAYS_PER_WEEK = 7;
|
|
421
|
+
this.yearsPerRow = 4;
|
|
422
|
+
this.yearsPerPage = 24;
|
|
423
|
+
this.dateLocale = 'en-US';
|
|
424
|
+
this.formatter = new Intl.DateTimeFormat(this.dateLocale, {
|
|
425
|
+
month: '2-digit',
|
|
426
|
+
day: '2-digit',
|
|
427
|
+
year: 'numeric',
|
|
428
|
+
});
|
|
429
|
+
this.dateFormats = {
|
|
430
|
+
display: {
|
|
431
|
+
dateInput: { year: 'numeric', month: 'numeric', day: 'numeric' },
|
|
432
|
+
monthLabel: { month: 'short' },
|
|
433
|
+
monthYearLabel: { year: 'numeric', month: 'short' },
|
|
434
|
+
dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },
|
|
435
|
+
monthYearA11yLabel: { year: 'numeric', month: 'long' },
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
today() {
|
|
440
|
+
return new Date();
|
|
441
|
+
}
|
|
442
|
+
getDate(date) {
|
|
443
|
+
return date.getDate();
|
|
444
|
+
}
|
|
445
|
+
getMonth(date) {
|
|
446
|
+
return date.getMonth();
|
|
447
|
+
}
|
|
448
|
+
getYear(date) {
|
|
449
|
+
return date.getFullYear();
|
|
450
|
+
}
|
|
451
|
+
getDayOfWeek(date) {
|
|
452
|
+
return date.getDay();
|
|
453
|
+
}
|
|
454
|
+
getDateLocale() {
|
|
455
|
+
return this.dateLocale;
|
|
456
|
+
}
|
|
457
|
+
setDateLocale(value) {
|
|
458
|
+
this.formatter = new Intl.DateTimeFormat(value, {
|
|
459
|
+
month: '2-digit',
|
|
460
|
+
day: '2-digit',
|
|
461
|
+
year: 'numeric',
|
|
462
|
+
});
|
|
463
|
+
this.dateLocale = value;
|
|
464
|
+
}
|
|
465
|
+
getMonthNames(style) {
|
|
466
|
+
const dtf = new Intl.DateTimeFormat(this.dateLocale, { month: style, timeZone: 'utc' });
|
|
467
|
+
return this.range(12, i => this.format(dtf, new Date(2017, i, 1)));
|
|
468
|
+
}
|
|
469
|
+
getFirstDayOfWeek() {
|
|
470
|
+
if (typeof Intl !== 'undefined' && Intl.Locale) {
|
|
471
|
+
const locale = new Intl.Locale(this.dateLocale);
|
|
472
|
+
// Some browsers implement a `getWeekInfo` method while others have a `weekInfo` getter.
|
|
473
|
+
// Note that this isn't supported in all browsers so we need to null check it.
|
|
474
|
+
const firstDay = (locale.getWeekInfo?.() || locale.weekInfo)?.firstDay ?? 0;
|
|
475
|
+
// `weekInfo.firstDay` is a number between 1 and 7 where, starting from Monday,
|
|
476
|
+
// whereas our representation is 0 to 6 where 0 is Sunday so we need to normalize it.
|
|
477
|
+
return firstDay === 7 ? 0 : firstDay;
|
|
478
|
+
}
|
|
479
|
+
// Default to Sunday if the browser doesn't provide the week information.
|
|
480
|
+
return 0;
|
|
481
|
+
}
|
|
482
|
+
getValidDateOrNull(obj) {
|
|
483
|
+
return this.isDateInstance(obj) && this.isValid(obj) ? obj : null;
|
|
484
|
+
}
|
|
485
|
+
getDayOfWeekNames(style) {
|
|
486
|
+
const dtf = new Intl.DateTimeFormat(this.dateLocale, { weekday: style, timeZone: 'utc' });
|
|
487
|
+
return this.range(7, i => this.format(dtf, new Date(2017, 0, i + 1)));
|
|
488
|
+
}
|
|
489
|
+
getDateNames() {
|
|
490
|
+
const dtf = new Intl.DateTimeFormat(this.dateLocale, { day: 'numeric', timeZone: 'utc' });
|
|
491
|
+
return this.range(31, i => this.format(dtf, new Date(2017, 0, i + 1)));
|
|
492
|
+
}
|
|
493
|
+
getYearName(date) {
|
|
494
|
+
const dtf = new Intl.DateTimeFormat(this.dateLocale, { year: 'numeric', timeZone: 'utc' });
|
|
495
|
+
return this.format(dtf, date);
|
|
496
|
+
}
|
|
497
|
+
getNumDaysInMonth(date) {
|
|
498
|
+
return this.getDate(this.createDateWithOverflow(this.getYear(date), this.getMonth(date) + 1, 0));
|
|
499
|
+
}
|
|
500
|
+
createDate(year, month, date) {
|
|
501
|
+
// Check for invalid month and date (except upper bound on date which we have to check after
|
|
502
|
+
// creating the Date).
|
|
503
|
+
if (month < 0 || month > 11) {
|
|
504
|
+
throw Error(`Invalid month index "${month}". Month index has to be between 0 and 11.`);
|
|
505
|
+
}
|
|
506
|
+
if (date < 1) {
|
|
507
|
+
throw Error(`Invalid date "${date}". Date has to be greater than 0.`);
|
|
508
|
+
}
|
|
509
|
+
let result = this.createDateWithOverflow(year, month, date);
|
|
510
|
+
// Check that the date wasn't above the upper bound for the month, causing the month to overflow
|
|
511
|
+
if (result.getMonth() != month) {
|
|
512
|
+
throw Error(`Invalid date "${date}" for month with index "${month}".`);
|
|
513
|
+
}
|
|
514
|
+
return result;
|
|
515
|
+
}
|
|
516
|
+
addCalendarDays(date, days) {
|
|
517
|
+
return this.createDateWithOverflow(this.getYear(date), this.getMonth(date), this.getDate(date) + days);
|
|
518
|
+
}
|
|
519
|
+
addCalendarMonths(date, months) {
|
|
520
|
+
let newDate = this.createDateWithOverflow(this.getYear(date), this.getMonth(date) + months, this.getDate(date));
|
|
521
|
+
// It's possible to wind up in the wrong month if the original month has more days than the new
|
|
522
|
+
// month. In this case we want to go to the last day of the desired month.
|
|
523
|
+
// Note: the additional + 12 % 12 ensures we end up with a positive number, since JS % doesn't
|
|
524
|
+
// guarantee this.
|
|
525
|
+
if (this.getMonth(newDate) != (((this.getMonth(date) + months) % 12) + 12) % 12) {
|
|
526
|
+
newDate = this.createDateWithOverflow(this.getYear(newDate), this.getMonth(newDate), 0);
|
|
527
|
+
}
|
|
528
|
+
return newDate;
|
|
529
|
+
}
|
|
530
|
+
addCalendarYears(date, years) {
|
|
531
|
+
return this.addCalendarMonths(date, years * 12);
|
|
532
|
+
}
|
|
533
|
+
setTime(date, hours, minutes) {
|
|
534
|
+
if (!this.isValid(date)) {
|
|
535
|
+
throw new Error("Invalid date provided");
|
|
536
|
+
}
|
|
537
|
+
if (hours < 0 || hours > 23) {
|
|
538
|
+
throw new Error("Invalid hours provided, must be between 0 and 23");
|
|
539
|
+
}
|
|
540
|
+
if (minutes < 0 || minutes > 59) {
|
|
541
|
+
throw new Error("Invalid minutes provided, must be between 0 and 59");
|
|
542
|
+
}
|
|
543
|
+
const updatedDate = new Date(date);
|
|
544
|
+
updatedDate.setHours(hours);
|
|
545
|
+
updatedDate.setMinutes(minutes);
|
|
546
|
+
return updatedDate;
|
|
547
|
+
}
|
|
548
|
+
sameDate(first, second) {
|
|
549
|
+
if (first && second) {
|
|
550
|
+
let firstValid = this.isValid(first);
|
|
551
|
+
let secondValid = this.isValid(second);
|
|
552
|
+
if (firstValid && secondValid) {
|
|
553
|
+
return !this.compareDate(first, second);
|
|
554
|
+
}
|
|
555
|
+
return firstValid == secondValid;
|
|
556
|
+
}
|
|
557
|
+
return first == second;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Compares two dates.
|
|
561
|
+
* @param first The first date to compare.
|
|
562
|
+
* @param second The second date to compare.
|
|
563
|
+
* @returns 0 if the dates are equal, a number less than 0 if the first date is earlier,
|
|
564
|
+
* a number greater than 0 if the first date is later.
|
|
565
|
+
*/
|
|
566
|
+
compareDate(first, second) {
|
|
567
|
+
return (this.getYear(first) - this.getYear(second) ||
|
|
568
|
+
this.getMonth(first) - this.getMonth(second) ||
|
|
569
|
+
this.getDate(first) - this.getDate(second));
|
|
570
|
+
}
|
|
571
|
+
deserialize(value) {
|
|
572
|
+
if (value == null || (this.isDateInstance(value) && this.isValid(value))) {
|
|
573
|
+
return value;
|
|
574
|
+
}
|
|
575
|
+
return this.invalid();
|
|
576
|
+
}
|
|
577
|
+
clampDate(date, min, max) {
|
|
578
|
+
if (min && this.compareDate(date, min) < 0) {
|
|
579
|
+
return min;
|
|
580
|
+
}
|
|
581
|
+
if (max && this.compareDate(date, max) > 0) {
|
|
582
|
+
return max;
|
|
583
|
+
}
|
|
584
|
+
return date;
|
|
585
|
+
}
|
|
586
|
+
formatDate(date, displayFormat) {
|
|
587
|
+
if (!this.isValid(date)) {
|
|
588
|
+
throw Error('DatepickerService: Cannot format invalid date.');
|
|
589
|
+
}
|
|
590
|
+
const dtf = new Intl.DateTimeFormat(this.dateLocale, { ...displayFormat, timeZone: 'utc' });
|
|
591
|
+
return this.format(dtf, date);
|
|
592
|
+
}
|
|
593
|
+
minValidator(minDate) {
|
|
594
|
+
return (control) => {
|
|
595
|
+
const controlValue = control.value;
|
|
596
|
+
const min = this.getValidDateOrNull(minDate);
|
|
597
|
+
return !min || !controlValue || this.compareDate(min, controlValue) <= 0
|
|
598
|
+
? null
|
|
599
|
+
: { 'datepickerMin': { min, actual: controlValue } };
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
maxValidator(maxDate) {
|
|
603
|
+
return (control) => {
|
|
604
|
+
const controlValue = control.value;
|
|
605
|
+
const max = this.getValidDateOrNull(maxDate);
|
|
606
|
+
return !max || !controlValue || this.compareDate(max, controlValue) >= 0
|
|
607
|
+
? null
|
|
608
|
+
: { 'datepickerMax': { max, actual: controlValue } };
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
/** Creates a date but allows the month and date to overflow. */
|
|
612
|
+
createDateWithOverflow(year, month, date) {
|
|
613
|
+
// Passing the year to the constructor causes year numbers <100 to be converted to 19xx.
|
|
614
|
+
// To work around this we use `setFullYear` and `setHours` instead.
|
|
615
|
+
const d = new Date();
|
|
616
|
+
d.setFullYear(year, month, date);
|
|
617
|
+
d.setHours(0, 0, 0, 0);
|
|
618
|
+
return d;
|
|
619
|
+
}
|
|
620
|
+
format(dtf, date) {
|
|
621
|
+
// Passing the year to the constructor causes year numbers <100 to be converted to 19xx.
|
|
622
|
+
// To work around this we use `setUTCFullYear` and `setUTCHours` instead.
|
|
623
|
+
const d = new Date();
|
|
624
|
+
d.setUTCFullYear(date.getFullYear(), date.getMonth(), date.getDate());
|
|
625
|
+
d.setUTCHours(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
|
|
626
|
+
return dtf.format(d);
|
|
627
|
+
}
|
|
628
|
+
isDateInstance(obj) {
|
|
629
|
+
return obj instanceof Date;
|
|
630
|
+
}
|
|
631
|
+
isValid(date) {
|
|
632
|
+
return !isNaN(date.getTime());
|
|
633
|
+
}
|
|
634
|
+
invalid() {
|
|
635
|
+
return new Date(NaN);
|
|
636
|
+
}
|
|
637
|
+
getActiveOffset(activeDate, minDate, maxDate) {
|
|
638
|
+
const activeYear = this.getYear(activeDate);
|
|
639
|
+
return this.positiveModulo(activeYear - this.getStartingYear(minDate, maxDate), this.yearsPerPage);
|
|
640
|
+
}
|
|
641
|
+
getStartingYear(minDate, maxDate) {
|
|
642
|
+
let startingYear = 0;
|
|
643
|
+
if (maxDate) {
|
|
644
|
+
const maxYear = this.getYear(maxDate);
|
|
645
|
+
startingYear = maxYear - this.yearsPerPage + 1;
|
|
646
|
+
}
|
|
647
|
+
else if (minDate) {
|
|
648
|
+
startingYear = this.getYear(minDate);
|
|
649
|
+
}
|
|
650
|
+
return startingYear;
|
|
651
|
+
}
|
|
652
|
+
positiveModulo(a, b) {
|
|
653
|
+
return ((a % b) + b) % b;
|
|
654
|
+
}
|
|
655
|
+
isSameMultiYearView(date1, date2, minDate, maxDate) {
|
|
656
|
+
const year1 = this.getYear(date1);
|
|
657
|
+
const year2 = this.getYear(date2);
|
|
658
|
+
const startingYear = this.getStartingYear(minDate, maxDate);
|
|
659
|
+
return (Math.floor((year1 - startingYear) / this.yearsPerPage) === Math.floor((year2 - startingYear) / this.yearsPerPage));
|
|
660
|
+
}
|
|
661
|
+
range(length, valueFunction) {
|
|
662
|
+
const valuesArray = Array(length);
|
|
663
|
+
for (let i = 0; i < length; i++) {
|
|
664
|
+
valuesArray[i] = valueFunction(i);
|
|
665
|
+
}
|
|
666
|
+
return valuesArray;
|
|
667
|
+
}
|
|
668
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiDatepickerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
669
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiDatepickerService, providedIn: 'root' }); }
|
|
670
|
+
}
|
|
671
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiDatepickerService, decorators: [{
|
|
672
|
+
type: Injectable,
|
|
673
|
+
args: [{
|
|
674
|
+
providedIn: 'root'
|
|
675
|
+
}]
|
|
676
|
+
}] });
|
|
677
|
+
|
|
678
|
+
class IpiCalendarYearView {
|
|
679
|
+
constructor(datePickerService, changeDetectorRef) {
|
|
680
|
+
this.datePickerService = datePickerService;
|
|
681
|
+
this.changeDetectorRef = changeDetectorRef;
|
|
682
|
+
/** Emits when a new month is selected. */
|
|
683
|
+
this.selectedChange = new EventEmitter();
|
|
684
|
+
/** Emits the selected month. This doesn't imply a change on the selected date */
|
|
685
|
+
this.monthSelected = new EventEmitter();
|
|
686
|
+
/** Emits when any date is activated. */
|
|
687
|
+
this.activeDateChange = new EventEmitter();
|
|
688
|
+
this.dateFormats = {
|
|
689
|
+
dateInput: null,
|
|
690
|
+
display: {
|
|
691
|
+
dateInput: { year: 'numeric', month: 'numeric', day: 'numeric' },
|
|
692
|
+
monthLabel: { month: 'short' },
|
|
693
|
+
monthYearLabel: { year: 'numeric', month: 'short' },
|
|
694
|
+
dateA11yLabel: { year: 'numeric', month: 'long', day: 'numeric' },
|
|
695
|
+
monthYearA11yLabel: { year: 'numeric', month: 'long' },
|
|
696
|
+
},
|
|
697
|
+
};
|
|
698
|
+
this._activeDate = this.datePickerService.today();
|
|
699
|
+
}
|
|
700
|
+
get activeDate() {
|
|
701
|
+
return this._activeDate;
|
|
702
|
+
}
|
|
703
|
+
set activeDate(value) {
|
|
704
|
+
let oldActiveDate = this._activeDate;
|
|
705
|
+
const validDate = this.datePickerService.getValidDateOrNull(this.datePickerService.deserialize(value)) ||
|
|
706
|
+
this.datePickerService.today();
|
|
707
|
+
this._activeDate = this.datePickerService.clampDate(validDate, this.minDate, this.maxDate);
|
|
708
|
+
if (this.datePickerService.getYear(oldActiveDate) !== this.datePickerService.getYear(this._activeDate)) {
|
|
709
|
+
this.init();
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
get selected() {
|
|
713
|
+
return this._selected;
|
|
714
|
+
}
|
|
715
|
+
set selected(value) {
|
|
716
|
+
if (value instanceof DateRange) {
|
|
717
|
+
this._selected = value;
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
this._selected = this.datePickerService.getValidDateOrNull(this.datePickerService.deserialize(value));
|
|
721
|
+
}
|
|
722
|
+
this.setSelectedMonth(value);
|
|
723
|
+
}
|
|
724
|
+
get minDate() {
|
|
725
|
+
return this._minDate;
|
|
726
|
+
}
|
|
727
|
+
set minDate(value) {
|
|
728
|
+
this._minDate = this.datePickerService.getValidDateOrNull(this.datePickerService.deserialize(value));
|
|
729
|
+
}
|
|
730
|
+
get maxDate() {
|
|
731
|
+
return this._maxDate;
|
|
732
|
+
}
|
|
733
|
+
set maxDate(value) {
|
|
734
|
+
this._maxDate = this.datePickerService.getValidDateOrNull(this.datePickerService.deserialize(value));
|
|
735
|
+
}
|
|
736
|
+
ngAfterContentInit() {
|
|
737
|
+
this.init();
|
|
738
|
+
}
|
|
739
|
+
/** Initializes this year view. */
|
|
740
|
+
init() {
|
|
741
|
+
this.setSelectedMonth(this.selected);
|
|
742
|
+
this.todayMonth = this.getMonthInCurrentYear(this.datePickerService.today());
|
|
743
|
+
this.yearLabel = this.datePickerService.getYearName(this.activeDate);
|
|
744
|
+
let monthNames = this.datePickerService.getMonthNames('short');
|
|
745
|
+
// First row of months only contains 5 elements so we can fit the year label on the same row.
|
|
746
|
+
this.months = [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
|
|
747
|
+
.map(row => row.map(month => this.createCellForMonth(month, monthNames[month])));
|
|
748
|
+
this.changeDetectorRef.markForCheck();
|
|
749
|
+
}
|
|
750
|
+
/** Handles when a new month is selected. */
|
|
751
|
+
onMonthSelected(event) {
|
|
752
|
+
const month = event.value;
|
|
753
|
+
const selectedMonth = this.datePickerService.createDate(this.datePickerService.getYear(this.activeDate), month, 1);
|
|
754
|
+
this.monthSelected.emit(selectedMonth);
|
|
755
|
+
const selectedDate = this.getDateFromMonth(month);
|
|
756
|
+
this.selectedChange.emit(selectedDate);
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Takes the index of a calendar body cell wrapped in an event as argument. For the date that
|
|
760
|
+
* corresponds to the given cell, set `activeDate` to that date and fire `activeDateChange` with
|
|
761
|
+
* that date.
|
|
762
|
+
*
|
|
763
|
+
* This function is used to match each component's model of the active date with the calendar
|
|
764
|
+
* body cell that was focused. It updates its value of `activeDate` synchronously and updates the
|
|
765
|
+
* parent's value asynchronously via the `activeDateChange` event. The child component receives an
|
|
766
|
+
* updated value asynchronously via the `activeCell` Input.
|
|
767
|
+
*/
|
|
768
|
+
updateActiveDate(event) {
|
|
769
|
+
const month = event.value;
|
|
770
|
+
const oldActiveDate = this._activeDate;
|
|
771
|
+
this.activeDate = this.getDateFromMonth(month);
|
|
772
|
+
if (this.datePickerService.compareDate(oldActiveDate, this.activeDate)) {
|
|
773
|
+
this.activeDateChange.emit(this.activeDate);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
/** Handles keydown events on the calendar body when calendar is in year view. */
|
|
777
|
+
handleCalendarBodyKeydown(event) {
|
|
778
|
+
const oldActiveDate = this._activeDate;
|
|
779
|
+
switch (event.code) {
|
|
780
|
+
case 'ArrowLeft':
|
|
781
|
+
this.activeDate = this.datePickerService.addCalendarMonths(this._activeDate, -1);
|
|
782
|
+
break;
|
|
783
|
+
case 'ArrowRight':
|
|
784
|
+
this.activeDate = this.datePickerService.addCalendarMonths(this._activeDate, 1);
|
|
785
|
+
break;
|
|
786
|
+
case 'ArrowUp':
|
|
787
|
+
this.activeDate = this.datePickerService.addCalendarMonths(this._activeDate, -4);
|
|
788
|
+
break;
|
|
789
|
+
case 'ArrowDown':
|
|
790
|
+
this.activeDate = this.datePickerService.addCalendarMonths(this._activeDate, 4);
|
|
791
|
+
break;
|
|
792
|
+
case 'Home':
|
|
793
|
+
this.activeDate = this.datePickerService.addCalendarMonths(this._activeDate, -this.datePickerService.getMonth(this._activeDate));
|
|
794
|
+
break;
|
|
795
|
+
case 'End':
|
|
796
|
+
this.activeDate = this.datePickerService.addCalendarMonths(this._activeDate, 11 - this.datePickerService.getMonth(this._activeDate));
|
|
797
|
+
break;
|
|
798
|
+
case 'PageUp':
|
|
799
|
+
this.activeDate = this.datePickerService.addCalendarYears(this._activeDate, event.altKey ? -10 : -1);
|
|
800
|
+
break;
|
|
801
|
+
case 'PageDown':
|
|
802
|
+
this.activeDate = this.datePickerService.addCalendarYears(this._activeDate, event.altKey ? 10 : 1);
|
|
803
|
+
break;
|
|
804
|
+
case 'Enter':
|
|
805
|
+
case 'Space':
|
|
806
|
+
// Note that we only prevent the default action here while the selection happens in
|
|
807
|
+
// `keyup` below. We can't do the selection here, because it can cause the calendar to
|
|
808
|
+
// reopen if focus is restored immediately. We also can't call `preventDefault` on `keyup`
|
|
809
|
+
// because it's too late (see #23305).
|
|
810
|
+
this.selectionKeyPressed = true;
|
|
811
|
+
break;
|
|
812
|
+
default:
|
|
813
|
+
// Don't prevent default or focus active cell on keys that we don't explicitly handle.
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
if (this.datePickerService.compareDate(oldActiveDate, this.activeDate)) {
|
|
817
|
+
this.activeDateChange.emit(this.activeDate);
|
|
818
|
+
this.focusActiveCellAfterViewChecked();
|
|
819
|
+
}
|
|
820
|
+
// Prevent unexpected default actions such as form submission.
|
|
821
|
+
event.preventDefault();
|
|
822
|
+
}
|
|
823
|
+
/** Handles keyup events on the calendar body when calendar is in year view. */
|
|
824
|
+
handleCalendarBodyKeyup(event) {
|
|
825
|
+
if (event.code === 'Space' || event.code === 'Enter') {
|
|
826
|
+
if (this.selectionKeyPressed) {
|
|
827
|
+
this.onMonthSelected({ value: this.datePickerService.getMonth(this._activeDate), event });
|
|
828
|
+
}
|
|
829
|
+
this.selectionKeyPressed = false;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
/** Focuses the active cell after the microtask queue is empty. */
|
|
833
|
+
focusActiveCell() {
|
|
834
|
+
this.CalendarBody.focusActiveCell();
|
|
835
|
+
}
|
|
836
|
+
/** Schedules the matCalendarBody to focus the active cell after change detection has run */
|
|
837
|
+
focusActiveCellAfterViewChecked() {
|
|
838
|
+
this.CalendarBody.scheduleFocusActiveCellAfterViewChecked();
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Gets the month in this year that the given Date falls on.
|
|
842
|
+
* Returns null if the given Date is in another year.
|
|
843
|
+
*/
|
|
844
|
+
getMonthInCurrentYear(date) {
|
|
845
|
+
return date && this.datePickerService.getYear(date) == this.datePickerService.getYear(this.activeDate)
|
|
846
|
+
? this.datePickerService.getMonth(date)
|
|
847
|
+
: null;
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Takes a month and returns a new date in the same day and year as the currently active date.
|
|
851
|
+
* The returned date will have the same month as the argument date.
|
|
852
|
+
*/
|
|
853
|
+
getDateFromMonth(month) {
|
|
854
|
+
const normalizedDate = this.datePickerService.createDate(this.datePickerService.getYear(this.activeDate), month, 1);
|
|
855
|
+
const daysInMonth = this.datePickerService.getNumDaysInMonth(normalizedDate);
|
|
856
|
+
return this.datePickerService.createDate(this.datePickerService.getYear(this.activeDate), month, Math.min(this.datePickerService.getDate(this.activeDate), daysInMonth));
|
|
857
|
+
}
|
|
858
|
+
/** Creates an MatCalendarCell for the given month. */
|
|
859
|
+
createCellForMonth(month, monthName) {
|
|
860
|
+
const date = this.datePickerService.createDate(this.datePickerService.getYear(this.activeDate), month, 1);
|
|
861
|
+
const ariaLabel = this.datePickerService.formatDate(date, this.dateFormats.display.monthYearA11yLabel);
|
|
862
|
+
return new CalendarCell(month, monthName.toLocaleUpperCase(), ariaLabel, this.shouldEnableMonth(month));
|
|
863
|
+
}
|
|
864
|
+
/** Whether the given month is enabled. */
|
|
865
|
+
shouldEnableMonth(month) {
|
|
866
|
+
const activeYear = this.datePickerService.getYear(this.activeDate);
|
|
867
|
+
if (month === undefined || month === null || this.isYearAndMonthAfterMaxDate(activeYear, month) || this.isYearAndMonthBeforeMinDate(activeYear, month)) {
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
return true;
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Tests whether the combination month/year is after this.maxDate, considering
|
|
874
|
+
* just the month and year of this.maxDate
|
|
875
|
+
*/
|
|
876
|
+
isYearAndMonthAfterMaxDate(year, month) {
|
|
877
|
+
if (this.maxDate) {
|
|
878
|
+
const maxYear = this.datePickerService.getYear(this.maxDate);
|
|
879
|
+
const maxMonth = this.datePickerService.getMonth(this.maxDate);
|
|
880
|
+
return year > maxYear || (year === maxYear && month > maxMonth);
|
|
881
|
+
}
|
|
882
|
+
return false;
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Tests whether the combination month/year is before this.minDate, considering
|
|
886
|
+
* just the month and year of this.minDate
|
|
887
|
+
*/
|
|
888
|
+
isYearAndMonthBeforeMinDate(year, month) {
|
|
889
|
+
if (this.minDate) {
|
|
890
|
+
const minYear = this.datePickerService.getYear(this.minDate);
|
|
891
|
+
const minMonth = this.datePickerService.getMonth(this.minDate);
|
|
892
|
+
return year < minYear || (year === minYear && month < minMonth);
|
|
893
|
+
}
|
|
894
|
+
return false;
|
|
895
|
+
}
|
|
896
|
+
/** Sets the currently-selected month based on a model value. */
|
|
897
|
+
setSelectedMonth(value) {
|
|
898
|
+
if (value instanceof DateRange) {
|
|
899
|
+
this.selectedMonth =
|
|
900
|
+
this.getMonthInCurrentYear(value.start) || this.getMonthInCurrentYear(value.end);
|
|
901
|
+
}
|
|
902
|
+
else {
|
|
903
|
+
this.selectedMonth = this.getMonthInCurrentYear(value);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiCalendarYearView, deps: [{ token: IpiDatepickerService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
907
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: IpiCalendarYearView, isStandalone: true, selector: "ipi-calendar-year-view", inputs: { activeDate: "activeDate", selected: "selected", minDate: "minDate", maxDate: "maxDate" }, outputs: { selectedChange: "selectedChange", monthSelected: "monthSelected", activeDateChange: "activeDateChange" }, viewQueries: [{ propertyName: "CalendarBody", first: true, predicate: CalendarBody, descendants: true }], ngImport: i0, template: "<table class=\"calendar-table\" role=\"grid\">\n <thead aria-hidden=\"true\" class=\"calendar-table-header\">\n <tr>\n <th class=\"calendar-table-header-divider\" colspan=\"4\"></th>\n </tr>\n </thead>\n\n <tbody calendar-body\n [label]=\"yearLabel\"\n [rows]=\"months\"\n [todayValue]=\"todayMonth!\"\n [startValue]=\"selectedMonth!\"\n [endValue]=\"selectedMonth!\"\n [labelMinRequiredCells]=\"2\"\n [numCols]=\"4\"\n [cellAspectRatio]=\"4 / 7\"\n [activeCell]=\"datePickerService.getMonth(activeDate)\"\n (selectedValueChange)=\"onMonthSelected($event)\"\n (activeDateChange)=\"updateActiveDate($event)\"\n (keyup)=\"handleCalendarBodyKeyup($event)\"\n (keydown)=\"handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n", styles: [":host{display:block}table{width:100%;padding:6px;border-collapse:separate}.calendar-table-header-divider{border-bottom:1px solid var(--ipi-calendar-year-view-table-header-border-bottom-color, #C6C6C6)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "component", type: CalendarBody, selector: "[calendar-body]", inputs: ["label", "rows", "todayValue", "startValue", "endValue", "labelMinRequiredCells", "numCols", "activeCell", "isRange", "cellAspectRatio", "comparisonStart", "comparisonEnd", "previewStart", "previewEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["selectedValueChange", "previewChange", "activeDateChange", "dragStarted", "dragEnded"] }, { kind: "ngmodule", type: ReactiveFormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
908
|
+
}
|
|
909
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiCalendarYearView, decorators: [{
|
|
910
|
+
type: Component,
|
|
911
|
+
args: [{ selector: 'ipi-calendar-year-view', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
912
|
+
FormsModule,
|
|
913
|
+
CalendarBody,
|
|
914
|
+
ReactiveFormsModule,
|
|
915
|
+
], template: "<table class=\"calendar-table\" role=\"grid\">\n <thead aria-hidden=\"true\" class=\"calendar-table-header\">\n <tr>\n <th class=\"calendar-table-header-divider\" colspan=\"4\"></th>\n </tr>\n </thead>\n\n <tbody calendar-body\n [label]=\"yearLabel\"\n [rows]=\"months\"\n [todayValue]=\"todayMonth!\"\n [startValue]=\"selectedMonth!\"\n [endValue]=\"selectedMonth!\"\n [labelMinRequiredCells]=\"2\"\n [numCols]=\"4\"\n [cellAspectRatio]=\"4 / 7\"\n [activeCell]=\"datePickerService.getMonth(activeDate)\"\n (selectedValueChange)=\"onMonthSelected($event)\"\n (activeDateChange)=\"updateActiveDate($event)\"\n (keyup)=\"handleCalendarBodyKeyup($event)\"\n (keydown)=\"handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n", styles: [":host{display:block}table{width:100%;padding:6px;border-collapse:separate}.calendar-table-header-divider{border-bottom:1px solid var(--ipi-calendar-year-view-table-header-border-bottom-color, #C6C6C6)}\n"] }]
|
|
916
|
+
}], ctorParameters: () => [{ type: IpiDatepickerService }, { type: i0.ChangeDetectorRef }], propDecorators: { CalendarBody: [{
|
|
917
|
+
type: ViewChild,
|
|
918
|
+
args: [CalendarBody]
|
|
919
|
+
}], activeDate: [{
|
|
920
|
+
type: Input
|
|
921
|
+
}], selected: [{
|
|
922
|
+
type: Input
|
|
923
|
+
}], minDate: [{
|
|
924
|
+
type: Input
|
|
925
|
+
}], maxDate: [{
|
|
926
|
+
type: Input
|
|
927
|
+
}], selectedChange: [{
|
|
928
|
+
type: Output
|
|
929
|
+
}], monthSelected: [{
|
|
930
|
+
type: Output
|
|
931
|
+
}], activeDateChange: [{
|
|
932
|
+
type: Output
|
|
933
|
+
}] } });
|
|
934
|
+
|
|
935
|
+
class DefaultCalendarRangeStrategy {
|
|
936
|
+
constructor(datepickerService) {
|
|
937
|
+
this.datepickerService = datepickerService;
|
|
938
|
+
}
|
|
939
|
+
selectionFinished(date, currentRange) {
|
|
940
|
+
let { start, end } = currentRange;
|
|
941
|
+
if (start == null) {
|
|
942
|
+
start = date;
|
|
943
|
+
}
|
|
944
|
+
else if (end == null && date && this.datepickerService.compareDate(date, start) >= 0) {
|
|
945
|
+
end = date;
|
|
946
|
+
}
|
|
947
|
+
else {
|
|
948
|
+
start = date;
|
|
949
|
+
end = null;
|
|
950
|
+
}
|
|
951
|
+
return new DateRange(start, end);
|
|
952
|
+
}
|
|
953
|
+
createPreview(activeDate, currentRange) {
|
|
954
|
+
let start = null;
|
|
955
|
+
let end = null;
|
|
956
|
+
if (currentRange.start && !currentRange.end && activeDate) {
|
|
957
|
+
start = currentRange.start;
|
|
958
|
+
end = activeDate;
|
|
959
|
+
}
|
|
960
|
+
return new DateRange(start, end);
|
|
961
|
+
}
|
|
962
|
+
createDrag(dragOrigin, originalRange, newDate) {
|
|
963
|
+
let start = originalRange.start;
|
|
964
|
+
let end = originalRange.end;
|
|
965
|
+
if (!start || !end) {
|
|
966
|
+
// Can't drag from an incomplete range.
|
|
967
|
+
return null;
|
|
968
|
+
}
|
|
969
|
+
const service = this.datepickerService;
|
|
970
|
+
const isRange = service.compareDate(start, end) !== 0;
|
|
971
|
+
const diffYears = service.getYear(newDate) - service.getYear(dragOrigin);
|
|
972
|
+
const diffMonths = service.getMonth(newDate) - service.getMonth(dragOrigin);
|
|
973
|
+
const diffDays = service.getDate(newDate) - service.getDate(dragOrigin);
|
|
974
|
+
if (isRange && service.sameDate(dragOrigin, originalRange.start)) {
|
|
975
|
+
start = newDate;
|
|
976
|
+
// when selecting start date of range move only start date
|
|
977
|
+
if (service.compareDate(newDate, end) > 0) {
|
|
978
|
+
// if start date becomes after end date, move whole range
|
|
979
|
+
end = service.addCalendarYears(end, diffYears);
|
|
980
|
+
end = service.addCalendarMonths(end, diffMonths);
|
|
981
|
+
end = service.addCalendarDays(end, diffDays);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
else if (isRange && service.sameDate(dragOrigin, originalRange.end)) {
|
|
985
|
+
// when selecting end date of range move only end date
|
|
986
|
+
end = newDate;
|
|
987
|
+
if (service.compareDate(newDate, start) < 0) {
|
|
988
|
+
// if end date becomes before start date, move whole range
|
|
989
|
+
start = service.addCalendarYears(start, diffYears);
|
|
990
|
+
start = service.addCalendarMonths(start, diffMonths);
|
|
991
|
+
start = service.addCalendarDays(start, diffDays);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
else {
|
|
995
|
+
// moving whole range if dragged from a middle date
|
|
996
|
+
start = service.addCalendarYears(start, diffYears);
|
|
997
|
+
start = service.addCalendarMonths(start, diffMonths);
|
|
998
|
+
start = service.addCalendarDays(start, diffDays);
|
|
999
|
+
end = service.addCalendarYears(end, diffYears);
|
|
1000
|
+
end = service.addCalendarMonths(end, diffMonths);
|
|
1001
|
+
end = service.addCalendarDays(end, diffDays);
|
|
1002
|
+
}
|
|
1003
|
+
return new DateRange(start, end);
|
|
1004
|
+
}
|
|
1005
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: DefaultCalendarRangeStrategy, deps: [{ token: IpiDatepickerService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1006
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: DefaultCalendarRangeStrategy, providedIn: 'root' }); }
|
|
1007
|
+
}
|
|
1008
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: DefaultCalendarRangeStrategy, decorators: [{
|
|
1009
|
+
type: Injectable,
|
|
1010
|
+
args: [{
|
|
1011
|
+
providedIn: 'root'
|
|
1012
|
+
}]
|
|
1013
|
+
}], ctorParameters: () => [{ type: IpiDatepickerService }] });
|
|
1014
|
+
|
|
1015
|
+
class IpiCalendarMonthView {
|
|
1016
|
+
constructor(datepickerService, changeDetectorRef, rangeSelectionStartegy) {
|
|
1017
|
+
this.datepickerService = datepickerService;
|
|
1018
|
+
this.changeDetectorRef = changeDetectorRef;
|
|
1019
|
+
this.rangeSelectionStartegy = rangeSelectionStartegy;
|
|
1020
|
+
this.activeDrag = null;
|
|
1021
|
+
this.dragStarted = new EventEmitter();
|
|
1022
|
+
this.dragEnded = new EventEmitter();
|
|
1023
|
+
this.activeDateChange = new EventEmitter();
|
|
1024
|
+
this.userSelection = new EventEmitter();
|
|
1025
|
+
this.selectedChange = new EventEmitter();
|
|
1026
|
+
this.uniqueIdCounter = 0;
|
|
1027
|
+
this._activeDate = this.datepickerService.today();
|
|
1028
|
+
}
|
|
1029
|
+
get activeDate() {
|
|
1030
|
+
return this._activeDate;
|
|
1031
|
+
}
|
|
1032
|
+
set activeDate(value) {
|
|
1033
|
+
const oldActiveDate = this._activeDate;
|
|
1034
|
+
const validDate = this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(value)) ||
|
|
1035
|
+
this.datepickerService.today();
|
|
1036
|
+
this._activeDate = this.datepickerService.clampDate(validDate, validDate, validDate);
|
|
1037
|
+
if (!this.hasSameMonthAndYear(oldActiveDate, this._activeDate)) {
|
|
1038
|
+
this.init();
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
get selected() {
|
|
1042
|
+
return this._selected;
|
|
1043
|
+
}
|
|
1044
|
+
set selected(value) {
|
|
1045
|
+
if (value instanceof DateRange) {
|
|
1046
|
+
this._selected = value;
|
|
1047
|
+
}
|
|
1048
|
+
else {
|
|
1049
|
+
this._selected = this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(value));
|
|
1050
|
+
}
|
|
1051
|
+
this.setRanges(this._selected);
|
|
1052
|
+
}
|
|
1053
|
+
get minDate() {
|
|
1054
|
+
return this._minDate;
|
|
1055
|
+
}
|
|
1056
|
+
set minDate(value) {
|
|
1057
|
+
this._minDate = this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(value));
|
|
1058
|
+
}
|
|
1059
|
+
get maxDate() {
|
|
1060
|
+
return this._maxDate;
|
|
1061
|
+
}
|
|
1062
|
+
set maxDate(value) {
|
|
1063
|
+
this._maxDate = this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(value));
|
|
1064
|
+
}
|
|
1065
|
+
ngOnChanges(changes) {
|
|
1066
|
+
const comparisonChange = changes['comparisonStart'] || changes['comparisonEnd'];
|
|
1067
|
+
if (comparisonChange && !comparisonChange.firstChange) {
|
|
1068
|
+
this.setRanges(this.selected);
|
|
1069
|
+
}
|
|
1070
|
+
if (changes['activeDrag']) {
|
|
1071
|
+
this.clearPreview();
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
ngAfterContentInit() {
|
|
1075
|
+
this.init();
|
|
1076
|
+
}
|
|
1077
|
+
getActiveCell() {
|
|
1078
|
+
return this.datepickerService.getDate(this.activeDate) - 1;
|
|
1079
|
+
}
|
|
1080
|
+
updateActiveDate(event) {
|
|
1081
|
+
const month = event.value;
|
|
1082
|
+
const oldActiveDate = this._activeDate;
|
|
1083
|
+
this.activeDate = this.getDateFromDayOfMonth(month);
|
|
1084
|
+
if (this.datepickerService.compareDate(oldActiveDate, this.activeDate)) {
|
|
1085
|
+
this.activeDateChange.emit(this._activeDate);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
/** Handles when a new date is selected. */
|
|
1089
|
+
dateSelected(event) {
|
|
1090
|
+
const date = event.value;
|
|
1091
|
+
const selectedDate = this.getDateFromDayOfMonth(date);
|
|
1092
|
+
let rangeStartDate;
|
|
1093
|
+
let rangeEndDate;
|
|
1094
|
+
if (this._selected instanceof DateRange) {
|
|
1095
|
+
rangeStartDate = this.getDateInCurrentMonth(this._selected.start);
|
|
1096
|
+
rangeEndDate = this.getDateInCurrentMonth(this._selected.end);
|
|
1097
|
+
}
|
|
1098
|
+
else {
|
|
1099
|
+
rangeStartDate = rangeEndDate = this.getDateInCurrentMonth(this._selected);
|
|
1100
|
+
}
|
|
1101
|
+
if (rangeStartDate !== date || rangeEndDate !== date) {
|
|
1102
|
+
this.selectedChange.emit(selectedDate);
|
|
1103
|
+
}
|
|
1104
|
+
this.userSelection.emit({ value: selectedDate, event: event.event });
|
|
1105
|
+
this.clearPreview();
|
|
1106
|
+
}
|
|
1107
|
+
handleCalendarBodyKeydown(event) {
|
|
1108
|
+
const oldActiveDate = this._activeDate;
|
|
1109
|
+
switch (event.code) {
|
|
1110
|
+
case 'ArrowLeft':
|
|
1111
|
+
this.activeDate = this.datepickerService.addCalendarDays(this._activeDate, -1);
|
|
1112
|
+
break;
|
|
1113
|
+
case 'ArrowRight':
|
|
1114
|
+
this.activeDate = this.datepickerService.addCalendarDays(this._activeDate, 1);
|
|
1115
|
+
break;
|
|
1116
|
+
case 'ArrowUp':
|
|
1117
|
+
this.activeDate = this.datepickerService.addCalendarDays(this._activeDate, -7);
|
|
1118
|
+
break;
|
|
1119
|
+
case 'ArrowDown':
|
|
1120
|
+
this.activeDate = this.datepickerService.addCalendarDays(this._activeDate, 7);
|
|
1121
|
+
break;
|
|
1122
|
+
case 'Home':
|
|
1123
|
+
this.activeDate = this.datepickerService.addCalendarDays(this._activeDate, 1 - this.datepickerService.getDate(this._activeDate));
|
|
1124
|
+
break;
|
|
1125
|
+
case 'End':
|
|
1126
|
+
this.activeDate = this.datepickerService.addCalendarDays(this._activeDate, this.datepickerService.getNumDaysInMonth(this._activeDate) -
|
|
1127
|
+
this.datepickerService.getDate(this._activeDate));
|
|
1128
|
+
break;
|
|
1129
|
+
case 'PageUp':
|
|
1130
|
+
this.activeDate = event.altKey
|
|
1131
|
+
? this.datepickerService.addCalendarYears(this._activeDate, -1)
|
|
1132
|
+
: this.datepickerService.addCalendarMonths(this._activeDate, -1);
|
|
1133
|
+
break;
|
|
1134
|
+
case 'PageDown':
|
|
1135
|
+
this.activeDate = event.altKey
|
|
1136
|
+
? this.datepickerService.addCalendarYears(this._activeDate, 1)
|
|
1137
|
+
: this.datepickerService.addCalendarMonths(this._activeDate, 1);
|
|
1138
|
+
break;
|
|
1139
|
+
case 'Enter':
|
|
1140
|
+
case 'Space':
|
|
1141
|
+
this.selectionKeyPressed = true;
|
|
1142
|
+
// Prevent unexpected default actions such as form submission.
|
|
1143
|
+
// Note that we only prevent the default action here while the selection happens in
|
|
1144
|
+
// `keyup` below. We can't do the selection here, because it can cause the calendar to
|
|
1145
|
+
// reopen if focus is restored immediately. We also can't call `preventDefault` on `keyup`
|
|
1146
|
+
// because it's too late (see #23305).
|
|
1147
|
+
event.preventDefault();
|
|
1148
|
+
return;
|
|
1149
|
+
case 'Escape':
|
|
1150
|
+
// Abort the current range selection if the user presses escape mid-selection.
|
|
1151
|
+
if (this.previewEnd != null && !hasModifierKey(event)) {
|
|
1152
|
+
this.clearPreview();
|
|
1153
|
+
// If a drag is in progress, cancel the drag without changing the
|
|
1154
|
+
// current selection.
|
|
1155
|
+
if (this.activeDrag) {
|
|
1156
|
+
this.dragEnded.emit({ value: null, event });
|
|
1157
|
+
}
|
|
1158
|
+
else {
|
|
1159
|
+
this.selectedChange.emit(null);
|
|
1160
|
+
this.userSelection.emit({ value: null, event });
|
|
1161
|
+
}
|
|
1162
|
+
event.preventDefault();
|
|
1163
|
+
}
|
|
1164
|
+
return;
|
|
1165
|
+
default:
|
|
1166
|
+
// Don't prevent default or focus active cell on keys that we don't explicitly handle.
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
if (this.datepickerService.compareDate(oldActiveDate, this.activeDate)) {
|
|
1170
|
+
this.activeDateChange.emit(this.activeDate);
|
|
1171
|
+
this.focusActiveCellAfterViewChecked();
|
|
1172
|
+
}
|
|
1173
|
+
event.preventDefault();
|
|
1174
|
+
}
|
|
1175
|
+
previewChanged({ value: cell }) {
|
|
1176
|
+
if (this.rangeSelectionStartegy) {
|
|
1177
|
+
if (!this.selected) {
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
// We can assume that this will be a range, because preview
|
|
1181
|
+
// events aren't fired for single date selections.
|
|
1182
|
+
const value = (cell ? cell.rawValue : null);
|
|
1183
|
+
const previewRange = this.rangeSelectionStartegy.createPreview(value, this.selected);
|
|
1184
|
+
this.previewStart = this.getCellCompareValue(previewRange.start);
|
|
1185
|
+
this.previewEnd = this.getCellCompareValue(previewRange.end);
|
|
1186
|
+
if (this.activeDrag && value) {
|
|
1187
|
+
const dragRange = this.rangeSelectionStartegy.createDrag?.(this.activeDrag.value, this.selected, value);
|
|
1188
|
+
if (dragRange) {
|
|
1189
|
+
this.previewStart = this.getCellCompareValue(dragRange.start);
|
|
1190
|
+
this.previewEnd = this.getCellCompareValue(dragRange.end);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
this.changeDetectorRef.markForCheck();
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
handleCalendarBodyKeyup(event) {
|
|
1197
|
+
if (event.code === 'Space' || event.code === 'Enter') {
|
|
1198
|
+
if (this.selectionKeyPressed && this.shouldEnableDate(this._activeDate)) {
|
|
1199
|
+
this.dateSelected({ value: this.datepickerService.getDate(this._activeDate), event });
|
|
1200
|
+
}
|
|
1201
|
+
this.selectionKeyPressed = false;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
focusActiveCellAfterViewChecked() {
|
|
1205
|
+
this.calendarBody.scheduleFocusActiveCellAfterViewChecked();
|
|
1206
|
+
}
|
|
1207
|
+
clearPreview() {
|
|
1208
|
+
this.previewStart = this.previewEnd = null;
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* Gets the date in this month that the given Date falls on.
|
|
1212
|
+
* Returns null if the given Date is in another month.
|
|
1213
|
+
*/
|
|
1214
|
+
getDateInCurrentMonth(date) {
|
|
1215
|
+
return date && this.hasSameMonthAndYear(date, this.activeDate)
|
|
1216
|
+
? this.datepickerService.getDate(date)
|
|
1217
|
+
: null;
|
|
1218
|
+
}
|
|
1219
|
+
init() {
|
|
1220
|
+
this.setRanges(this.selected);
|
|
1221
|
+
IpiDatepickerService;
|
|
1222
|
+
this.todayDate = this.getCellCompareValue(this.datepickerService.today());
|
|
1223
|
+
this.monthLabel = this.datepickerService.dateFormats.display.monthLabel
|
|
1224
|
+
? this.datepickerService.formatDate(this.activeDate, this.datepickerService.dateFormats.display.monthLabel)
|
|
1225
|
+
: this.datepickerService
|
|
1226
|
+
.getMonthNames('short')[this.datepickerService.getMonth(this.activeDate)].toLocaleUpperCase();
|
|
1227
|
+
let firstOfMonth = this.datepickerService.createDate(this.datepickerService.getYear(this.activeDate), this.datepickerService.getMonth(this.activeDate), 1);
|
|
1228
|
+
this.firstWeekOffset =
|
|
1229
|
+
(this.datepickerService.DAYS_PER_WEEK +
|
|
1230
|
+
this.datepickerService.getDayOfWeek(firstOfMonth) -
|
|
1231
|
+
this.datepickerService.getFirstDayOfWeek()) %
|
|
1232
|
+
this.datepickerService.DAYS_PER_WEEK;
|
|
1233
|
+
this.initWeekdays();
|
|
1234
|
+
this.createWeekCells();
|
|
1235
|
+
}
|
|
1236
|
+
setRanges(selectedValue) {
|
|
1237
|
+
if (selectedValue instanceof DateRange) {
|
|
1238
|
+
this.rangeStart = this.getCellCompareValue(selectedValue.start);
|
|
1239
|
+
this.rangeEnd = this.getCellCompareValue(selectedValue.end);
|
|
1240
|
+
this.isRange = true;
|
|
1241
|
+
}
|
|
1242
|
+
else if (selectedValue instanceof Date) {
|
|
1243
|
+
this.rangeStart = this.rangeEnd = this.getCellCompareValue(selectedValue);
|
|
1244
|
+
this.isRange = false;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
getCellCompareValue(date) {
|
|
1248
|
+
if (date) {
|
|
1249
|
+
// We use the time since the Unix epoch to compare dates in this view, rather than the
|
|
1250
|
+
// cell values, because we need to support ranges that span across multiple months/years.
|
|
1251
|
+
const year = this.datepickerService.getYear(date);
|
|
1252
|
+
const month = this.datepickerService.getMonth(date);
|
|
1253
|
+
const day = this.datepickerService.getDate(date);
|
|
1254
|
+
return new Date(year, month, day).getTime();
|
|
1255
|
+
}
|
|
1256
|
+
return null;
|
|
1257
|
+
}
|
|
1258
|
+
initWeekdays() {
|
|
1259
|
+
const firstDayOfWeek = this.datepickerService.getFirstDayOfWeek();
|
|
1260
|
+
const narrowWeekdays = this.datepickerService.getDayOfWeekNames('narrow');
|
|
1261
|
+
const longWeekdays = this.datepickerService.getDayOfWeekNames('long');
|
|
1262
|
+
// Rotate the labels for days of the week based on the configured first day of the week.
|
|
1263
|
+
let weekdays = longWeekdays.map((long, i) => {
|
|
1264
|
+
return { long, narrow: narrowWeekdays[i], id: this.uniqueIdCounter++ };
|
|
1265
|
+
});
|
|
1266
|
+
this.weekdays = weekdays.slice(firstDayOfWeek).concat(weekdays.slice(0, firstDayOfWeek));
|
|
1267
|
+
}
|
|
1268
|
+
createWeekCells() {
|
|
1269
|
+
const daysInMonth = this.datepickerService.getNumDaysInMonth(this.activeDate);
|
|
1270
|
+
const dateNames = this.datepickerService.getDateNames();
|
|
1271
|
+
this.weeks = [[]];
|
|
1272
|
+
for (let i = 0, cell = this.firstWeekOffset; i < daysInMonth; i++, cell++) {
|
|
1273
|
+
if (cell == this.datepickerService.DAYS_PER_WEEK) {
|
|
1274
|
+
this.weeks.push([]);
|
|
1275
|
+
cell = 0;
|
|
1276
|
+
}
|
|
1277
|
+
const date = this.datepickerService.createDate(this.datepickerService.getYear(this.activeDate), this.datepickerService.getMonth(this.activeDate), i + 1);
|
|
1278
|
+
const enabled = this.shouldEnableDate(date);
|
|
1279
|
+
const ariaLabel = this.datepickerService.formatDate(date, this.datepickerService.dateFormats.display.dateA11yLabel);
|
|
1280
|
+
this.weeks[this.weeks.length - 1].push(new CalendarCell(i + 1, dateNames[i], ariaLabel, enabled, this.getCellCompareValue(date), date));
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
hasSameMonthAndYear(firstDate, secondDate) {
|
|
1284
|
+
return !!(firstDate &&
|
|
1285
|
+
secondDate &&
|
|
1286
|
+
this.datepickerService.getMonth(firstDate) == this.datepickerService.getMonth(secondDate) &&
|
|
1287
|
+
this.datepickerService.getYear(firstDate) == this.datepickerService.getYear(secondDate));
|
|
1288
|
+
}
|
|
1289
|
+
onDragEnded(event) {
|
|
1290
|
+
if (!this.activeDrag)
|
|
1291
|
+
return;
|
|
1292
|
+
if (event.value) {
|
|
1293
|
+
// Propagate drag effect
|
|
1294
|
+
const dragDropResult = this.rangeSelectionStartegy?.createDrag?.(this.activeDrag.value, this.selected, event.value);
|
|
1295
|
+
if ((!dragDropResult?.end || !this.shouldEnableDate(dragDropResult.end)) || (!dragDropResult?.start || !this.shouldEnableDate(dragDropResult.start))) {
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
this.dragEnded.emit({ value: dragDropResult, event: event.event });
|
|
1299
|
+
}
|
|
1300
|
+
else {
|
|
1301
|
+
this.dragEnded.emit({ value: null, event: event.event });
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
shouldEnableDate(date) {
|
|
1305
|
+
return (!!date &&
|
|
1306
|
+
(!this.minDate || this.datepickerService.compareDate(date, this.minDate) >= 0) &&
|
|
1307
|
+
(!this.maxDate || this.datepickerService.compareDate(date, this.maxDate) <= 0));
|
|
1308
|
+
}
|
|
1309
|
+
getDateFromDayOfMonth(dayOfMonth) {
|
|
1310
|
+
return this.datepickerService.createDate(this.datepickerService.getYear(this.activeDate), this.datepickerService.getMonth(this.activeDate), dayOfMonth);
|
|
1311
|
+
}
|
|
1312
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiCalendarMonthView, deps: [{ token: IpiDatepickerService }, { token: i0.ChangeDetectorRef }, { token: DefaultCalendarRangeStrategy }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1313
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: IpiCalendarMonthView, isStandalone: true, selector: "ipi-calendar-month-view", inputs: { activeDate: "activeDate", selected: "selected", minDate: "minDate", maxDate: "maxDate", activeDrag: "activeDrag" }, outputs: { dragStarted: "dragStarted", dragEnded: "dragEnded", activeDateChange: "activeDateChange", userSelection: "userSelection", selectedChange: "selectedChange" }, viewQueries: [{ propertyName: "calendarBody", first: true, predicate: CalendarBody, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<table class=\"calendar-table\" role=\"grid\">\n <thead class=\"calendar-table-header\">\n <tr>\n @for (day of weekdays; track day.id) {\n <th scope=\"col\">\n <span class=\"cdk-visually-hidden\">{{ day.long }}</span>\n\n <span aria-hidden=\"true\">{{ day.narrow }}</span>\n </th>\n }\n </tr>\n\n <tr aria-hidden=\"true\">\n <th class=\"calendar-table-header-divider\" colspan=\"7\"></th>\n </tr>\n </thead>\n\n <tbody calendar-body\n [label]=\"monthLabel\"\n [rows]=\"weeks\"\n [todayValue]=\"todayDate!\"\n [startValue]=\"rangeStart!\"\n [endValue]=\"rangeEnd!\"\n [previewStart]=\"previewStart\"\n [previewEnd]=\"previewEnd\"\n [isRange]=\"isRange\"\n [activeCell]=\"getActiveCell()\"\n [labelMinRequiredCells]=\"2\"\n (dragStarted)=\"dragStarted.emit($event)\"\n (dragEnded)=\"onDragEnded($event)\"\n (activeDateChange)=\"updateActiveDate($event)\"\n (selectedValueChange)=\"dateSelected($event)\"\n (previewChange)=\"previewChanged($event)\"\n (keyup)=\"handleCalendarBodyKeyup($event)\"\n (keydown)=\"handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n", styles: ["table{width:100%;border-spacing:0px 4px;border-collapse:separate;padding:6px}th{color:var(--ipi-calendar-month-view-table-header-color, #C6C6C6)}.calendar-table-header-divider{border-bottom:1px solid var(--ipi-datepicker-calendar-month-view-table-header-border-bottom-color, #C6C6C6)}\n"], dependencies: [{ kind: "component", type: CalendarBody, selector: "[calendar-body]", inputs: ["label", "rows", "todayValue", "startValue", "endValue", "labelMinRequiredCells", "numCols", "activeCell", "isRange", "cellAspectRatio", "comparisonStart", "comparisonEnd", "previewStart", "previewEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["selectedValueChange", "previewChange", "activeDateChange", "dragStarted", "dragEnded"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1314
|
+
}
|
|
1315
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiCalendarMonthView, decorators: [{
|
|
1316
|
+
type: Component,
|
|
1317
|
+
args: [{ selector: 'ipi-calendar-month-view', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
1318
|
+
CalendarBody,
|
|
1319
|
+
], template: "<table class=\"calendar-table\" role=\"grid\">\n <thead class=\"calendar-table-header\">\n <tr>\n @for (day of weekdays; track day.id) {\n <th scope=\"col\">\n <span class=\"cdk-visually-hidden\">{{ day.long }}</span>\n\n <span aria-hidden=\"true\">{{ day.narrow }}</span>\n </th>\n }\n </tr>\n\n <tr aria-hidden=\"true\">\n <th class=\"calendar-table-header-divider\" colspan=\"7\"></th>\n </tr>\n </thead>\n\n <tbody calendar-body\n [label]=\"monthLabel\"\n [rows]=\"weeks\"\n [todayValue]=\"todayDate!\"\n [startValue]=\"rangeStart!\"\n [endValue]=\"rangeEnd!\"\n [previewStart]=\"previewStart\"\n [previewEnd]=\"previewEnd\"\n [isRange]=\"isRange\"\n [activeCell]=\"getActiveCell()\"\n [labelMinRequiredCells]=\"2\"\n (dragStarted)=\"dragStarted.emit($event)\"\n (dragEnded)=\"onDragEnded($event)\"\n (activeDateChange)=\"updateActiveDate($event)\"\n (selectedValueChange)=\"dateSelected($event)\"\n (previewChange)=\"previewChanged($event)\"\n (keyup)=\"handleCalendarBodyKeyup($event)\"\n (keydown)=\"handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n", styles: ["table{width:100%;border-spacing:0px 4px;border-collapse:separate;padding:6px}th{color:var(--ipi-calendar-month-view-table-header-color, #C6C6C6)}.calendar-table-header-divider{border-bottom:1px solid var(--ipi-datepicker-calendar-month-view-table-header-border-bottom-color, #C6C6C6)}\n"] }]
|
|
1320
|
+
}], ctorParameters: () => [{ type: IpiDatepickerService }, { type: i0.ChangeDetectorRef }, { type: DefaultCalendarRangeStrategy }], propDecorators: { calendarBody: [{
|
|
1321
|
+
type: ViewChild,
|
|
1322
|
+
args: [CalendarBody]
|
|
1323
|
+
}], activeDate: [{
|
|
1324
|
+
type: Input
|
|
1325
|
+
}], selected: [{
|
|
1326
|
+
type: Input
|
|
1327
|
+
}], minDate: [{
|
|
1328
|
+
type: Input
|
|
1329
|
+
}], maxDate: [{
|
|
1330
|
+
type: Input
|
|
1331
|
+
}], activeDrag: [{
|
|
1332
|
+
type: Input
|
|
1333
|
+
}], dragStarted: [{
|
|
1334
|
+
type: Output
|
|
1335
|
+
}], dragEnded: [{
|
|
1336
|
+
type: Output
|
|
1337
|
+
}], activeDateChange: [{
|
|
1338
|
+
type: Output
|
|
1339
|
+
}], userSelection: [{
|
|
1340
|
+
type: Output
|
|
1341
|
+
}], selectedChange: [{
|
|
1342
|
+
type: Output
|
|
1343
|
+
}] } });
|
|
1344
|
+
|
|
1345
|
+
class IpiCalendarMultiYearView {
|
|
1346
|
+
constructor(changeDetectorRef, datePickerService) {
|
|
1347
|
+
this.changeDetectorRef = changeDetectorRef;
|
|
1348
|
+
this.datePickerService = datePickerService;
|
|
1349
|
+
/** Emits when a new year is selected. */
|
|
1350
|
+
this.selectedChange = new EventEmitter();
|
|
1351
|
+
/** Emits the selected year. This doesn't imply a change on the selected date */
|
|
1352
|
+
this.yearSelected = new EventEmitter();
|
|
1353
|
+
/** Emits when any date is activated. */
|
|
1354
|
+
this.activeDateChange = new EventEmitter();
|
|
1355
|
+
this._activeDate = this.datePickerService.today();
|
|
1356
|
+
}
|
|
1357
|
+
/** The date to display in this multi-year view (everything other than the year is ignored). */
|
|
1358
|
+
get activeDate() {
|
|
1359
|
+
return this._activeDate;
|
|
1360
|
+
}
|
|
1361
|
+
set activeDate(value) {
|
|
1362
|
+
let oldActiveDate = this._activeDate;
|
|
1363
|
+
const validDate = this.datePickerService.getValidDateOrNull(this.datePickerService.deserialize(value)) ||
|
|
1364
|
+
this.datePickerService.today();
|
|
1365
|
+
this._activeDate = this.datePickerService.clampDate(validDate, this.minDate, this.maxDate);
|
|
1366
|
+
if (!this.isSameMultiYearView(oldActiveDate, this._activeDate, this.minDate, this.maxDate)) {
|
|
1367
|
+
this.init();
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
get selected() {
|
|
1371
|
+
return this._selected;
|
|
1372
|
+
}
|
|
1373
|
+
set selected(value) {
|
|
1374
|
+
if (value instanceof DateRange) {
|
|
1375
|
+
this._selected = value;
|
|
1376
|
+
}
|
|
1377
|
+
else {
|
|
1378
|
+
this._selected = this.datePickerService.getValidDateOrNull(this.datePickerService.deserialize(value));
|
|
1379
|
+
}
|
|
1380
|
+
this.setSelectedYear(value);
|
|
1381
|
+
}
|
|
1382
|
+
/** The minimum selectable date. */
|
|
1383
|
+
get minDate() {
|
|
1384
|
+
return this._minDate;
|
|
1385
|
+
}
|
|
1386
|
+
set minDate(value) {
|
|
1387
|
+
this._minDate = this.datePickerService.getValidDateOrNull(this.datePickerService.deserialize(value));
|
|
1388
|
+
}
|
|
1389
|
+
/** The maximum selectable date. */
|
|
1390
|
+
get maxDate() {
|
|
1391
|
+
return this._maxDate;
|
|
1392
|
+
}
|
|
1393
|
+
set maxDate(value) {
|
|
1394
|
+
this._maxDate = this.datePickerService.getValidDateOrNull(this.datePickerService.deserialize(value));
|
|
1395
|
+
}
|
|
1396
|
+
ngAfterContentInit() {
|
|
1397
|
+
this.init();
|
|
1398
|
+
}
|
|
1399
|
+
init() {
|
|
1400
|
+
this._todayYear = this.datePickerService.getYear(this.datePickerService.today());
|
|
1401
|
+
// We want a range years such that we maximize the number of
|
|
1402
|
+
// enabled dates visible at once. This prevents issues where the minimum year
|
|
1403
|
+
// is the last item of a page OR the maximum year is the first item of a page.
|
|
1404
|
+
// The offset from the active year to the "slot" for the starting year is the
|
|
1405
|
+
// *actual* first rendered year in the multi-year view.
|
|
1406
|
+
const activeYear = this.datePickerService.getYear(this._activeDate);
|
|
1407
|
+
const minYearOfPage = activeYear - this.datePickerService.getActiveOffset(this.activeDate, this.minDate, this.maxDate);
|
|
1408
|
+
this._years = [];
|
|
1409
|
+
for (let i = 0, row = []; i < this.datePickerService.yearsPerPage; i++) {
|
|
1410
|
+
row.push(minYearOfPage + i);
|
|
1411
|
+
if (row.length == this.datePickerService.yearsPerRow) {
|
|
1412
|
+
this._years.push(row.map(year => this.createCellForYear(year)));
|
|
1413
|
+
row = [];
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
this.changeDetectorRef.markForCheck();
|
|
1417
|
+
}
|
|
1418
|
+
/** Handles when a new year is selected. */
|
|
1419
|
+
onYearSelected(event) {
|
|
1420
|
+
const year = event.value;
|
|
1421
|
+
const selectedYear = this.datePickerService.createDate(year, 0, 1);
|
|
1422
|
+
const selectedDate = this.getDateFromYear(year);
|
|
1423
|
+
this.yearSelected.emit(selectedYear);
|
|
1424
|
+
this.selectedChange.emit(selectedDate);
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* Takes the index of a calendar body cell wrapped in an event as argument. For the date that
|
|
1428
|
+
* corresponds to the given cell, set `activeDate` to that date and fire `activeDateChange` with
|
|
1429
|
+
* that date.
|
|
1430
|
+
*
|
|
1431
|
+
* This function is used to match each component's model of the active date with the calendar
|
|
1432
|
+
* body cell that was focused. It updates its value of `activeDate` synchronously and updates the
|
|
1433
|
+
* parent's value asynchronously via the `activeDateChange` event. The child component receives an
|
|
1434
|
+
* updated value asynchronously via the `activeCell` Input.
|
|
1435
|
+
*/
|
|
1436
|
+
updateActiveDate(event) {
|
|
1437
|
+
const year = event.value;
|
|
1438
|
+
const oldActiveDate = this._activeDate;
|
|
1439
|
+
this.activeDate = this.getDateFromYear(year);
|
|
1440
|
+
if (this.datePickerService.compareDate(oldActiveDate, this.activeDate)) {
|
|
1441
|
+
this.activeDateChange.emit(this.activeDate);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
/** Handles keydown events on the calendar body when calendar is in multi-year view. */
|
|
1445
|
+
handleCalendarBodyKeydown(event) {
|
|
1446
|
+
const oldActiveDate = this._activeDate;
|
|
1447
|
+
switch (event.code) {
|
|
1448
|
+
case 'ArrowLeft':
|
|
1449
|
+
this.activeDate = this.datePickerService.addCalendarYears(this._activeDate, -1);
|
|
1450
|
+
break;
|
|
1451
|
+
case 'ArrowRight':
|
|
1452
|
+
this.activeDate = this.datePickerService.addCalendarYears(this._activeDate, 1);
|
|
1453
|
+
break;
|
|
1454
|
+
case 'ArrowUp':
|
|
1455
|
+
this.activeDate = this.datePickerService.addCalendarYears(this._activeDate, -this.datePickerService.yearsPerRow);
|
|
1456
|
+
break;
|
|
1457
|
+
case 'ArrowDown':
|
|
1458
|
+
this.activeDate = this.datePickerService.addCalendarYears(this._activeDate, this.datePickerService.yearsPerRow);
|
|
1459
|
+
break;
|
|
1460
|
+
case 'Home':
|
|
1461
|
+
this.activeDate = this.datePickerService.addCalendarYears(this._activeDate, -this.datePickerService.getActiveOffset(this.activeDate, this.minDate, this.maxDate));
|
|
1462
|
+
break;
|
|
1463
|
+
case 'End':
|
|
1464
|
+
this.activeDate = this.datePickerService.addCalendarYears(this._activeDate, this.datePickerService.yearsPerPage -
|
|
1465
|
+
this.datePickerService.getActiveOffset(this.activeDate, this.minDate, this.maxDate) -
|
|
1466
|
+
1);
|
|
1467
|
+
break;
|
|
1468
|
+
case 'PageUp':
|
|
1469
|
+
this.activeDate = this.datePickerService.addCalendarYears(this._activeDate, event.altKey ? -this.datePickerService.yearsPerPage * 10 : -this.datePickerService.yearsPerPage);
|
|
1470
|
+
break;
|
|
1471
|
+
case 'PageDown':
|
|
1472
|
+
this.activeDate = this.datePickerService.addCalendarYears(this._activeDate, event.altKey ? this.datePickerService.yearsPerPage * 10 : this.datePickerService.yearsPerPage);
|
|
1473
|
+
break;
|
|
1474
|
+
case 'Enter':
|
|
1475
|
+
case 'Space':
|
|
1476
|
+
this.selectionKeyPressed = true;
|
|
1477
|
+
break;
|
|
1478
|
+
default:
|
|
1479
|
+
// Don't prevent default or focus active cell on keys that we don't explicitly handle.
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
if (this.datePickerService.compareDate(oldActiveDate, this.activeDate)) {
|
|
1483
|
+
this.activeDateChange.emit(this.activeDate);
|
|
1484
|
+
}
|
|
1485
|
+
this.focusActiveCellAfterViewChecked();
|
|
1486
|
+
// Prevent unexpected default actions such as form submission.
|
|
1487
|
+
event.preventDefault();
|
|
1488
|
+
}
|
|
1489
|
+
/** Handles keyup events on the calendar body when calendar is in multi-year view. */
|
|
1490
|
+
handleCalendarBodyKeyup(event) {
|
|
1491
|
+
if (event.code === 'Space' || event.code === 'Enter') {
|
|
1492
|
+
if (this.selectionKeyPressed) {
|
|
1493
|
+
this.onYearSelected({ value: this.datePickerService.getYear(this._activeDate), event });
|
|
1494
|
+
}
|
|
1495
|
+
this.selectionKeyPressed = false;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
getActiveCell() {
|
|
1499
|
+
return this.datePickerService.getActiveOffset(this.activeDate, this.minDate, this.maxDate);
|
|
1500
|
+
}
|
|
1501
|
+
/** Focuses the active cell after the microtask queue is empty. */
|
|
1502
|
+
focusActiveCell() {
|
|
1503
|
+
this.calendarBody.focusActiveCell();
|
|
1504
|
+
}
|
|
1505
|
+
/** Focuses the active cell after change detection has run and the microtask queue is empty. */
|
|
1506
|
+
focusActiveCellAfterViewChecked() {
|
|
1507
|
+
this.calendarBody.scheduleFocusActiveCellAfterViewChecked();
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Takes a year and returns a new date on the same day and month as the currently active date
|
|
1511
|
+
* The returned date will have the same year as the argument date.
|
|
1512
|
+
*/
|
|
1513
|
+
getDateFromYear(year) {
|
|
1514
|
+
const activeMonth = this.datePickerService.getMonth(this.activeDate);
|
|
1515
|
+
const daysInMonth = this.datePickerService.getNumDaysInMonth(this.datePickerService.createDate(year, activeMonth, 1));
|
|
1516
|
+
const normalizedDate = this.datePickerService.createDate(year, activeMonth, Math.min(this.datePickerService.getDate(this.activeDate), daysInMonth));
|
|
1517
|
+
return normalizedDate;
|
|
1518
|
+
}
|
|
1519
|
+
/** Creates an CalendarCell for the given year. */
|
|
1520
|
+
createCellForYear(year) {
|
|
1521
|
+
const date = this.datePickerService.createDate(year, 0, 1);
|
|
1522
|
+
const yearName = this.datePickerService.getYearName(date);
|
|
1523
|
+
return new CalendarCell(year, yearName, yearName, this.shouldEnableYear(year));
|
|
1524
|
+
}
|
|
1525
|
+
/** Whether the given year is enabled. */
|
|
1526
|
+
shouldEnableYear(year) {
|
|
1527
|
+
// disable if the year is greater than maxDate lower than minDate
|
|
1528
|
+
if (year === undefined ||
|
|
1529
|
+
year === null ||
|
|
1530
|
+
(this.maxDate && year > this.datePickerService.getYear(this.maxDate)) ||
|
|
1531
|
+
(this.minDate && year < this.datePickerService.getYear(this.minDate))) {
|
|
1532
|
+
return false;
|
|
1533
|
+
}
|
|
1534
|
+
return true;
|
|
1535
|
+
}
|
|
1536
|
+
/** Sets the currently-highlighted year based on a model value. */
|
|
1537
|
+
setSelectedYear(value) {
|
|
1538
|
+
this.selectedYear = null;
|
|
1539
|
+
if (value instanceof DateRange) {
|
|
1540
|
+
const displayValue = value.start || value.end;
|
|
1541
|
+
if (displayValue) {
|
|
1542
|
+
this.selectedYear = this.datePickerService.getYear(displayValue);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
else if (value) {
|
|
1546
|
+
this.selectedYear = this.datePickerService.getYear(value);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
isSameMultiYearView(date1, date2, minDate, maxDate) {
|
|
1550
|
+
const year1 = this.datePickerService.getYear(date1);
|
|
1551
|
+
const year2 = this.datePickerService.getYear(date2);
|
|
1552
|
+
const startingYear = this.getStartingYear(minDate, maxDate);
|
|
1553
|
+
return (Math.floor((year1 - startingYear) / this.datePickerService.yearsPerPage) ===
|
|
1554
|
+
Math.floor((year2 - startingYear) / this.datePickerService.yearsPerPage));
|
|
1555
|
+
}
|
|
1556
|
+
getStartingYear(minDate, maxDate) {
|
|
1557
|
+
let startingYear = 0;
|
|
1558
|
+
if (maxDate) {
|
|
1559
|
+
const maxYear = this.datePickerService.getYear(maxDate);
|
|
1560
|
+
startingYear = maxYear - this.datePickerService.yearsPerPage + 1;
|
|
1561
|
+
}
|
|
1562
|
+
else if (minDate) {
|
|
1563
|
+
startingYear = this.datePickerService.getYear(minDate);
|
|
1564
|
+
}
|
|
1565
|
+
return startingYear;
|
|
1566
|
+
}
|
|
1567
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiCalendarMultiYearView, deps: [{ token: i0.ChangeDetectorRef }, { token: IpiDatepickerService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1568
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: IpiCalendarMultiYearView, isStandalone: true, selector: "ipi-calendar-multi-year-view", inputs: { activeDate: "activeDate", selected: "selected", minDate: "minDate", maxDate: "maxDate" }, outputs: { selectedChange: "selectedChange", yearSelected: "yearSelected", activeDateChange: "activeDateChange" }, viewQueries: [{ propertyName: "calendarBody", first: true, predicate: CalendarBody, descendants: true }], ngImport: i0, template: "<table class=\"calendar-table\" role=\"grid\">\n <thead aria-hidden=\"true\" class=\"calendar-table-header\">\n <tr>\n <th class=\"calendar-table-header-divider\" colspan=\"4\"></th>\n </tr>\n </thead>\n\n <tbody calendar-body\n [rows]=\"_years\"\n [todayValue]=\"_todayYear\"\n [startValue]=\"selectedYear!\"\n [endValue]=\"selectedYear!\"\n [numCols]=\"4\"\n [cellAspectRatio]=\"4 / 7\"\n [activeCell]=\"getActiveCell()\"\n (selectedValueChange)=\"onYearSelected($event)\"\n (activeDateChange)=\"updateActiveDate($event)\"\n (keyup)=\"handleCalendarBodyKeyup($event)\"\n (keydown)=\"handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n", styles: [":host{display:block}table{width:100%;padding:6px;border-collapse:separate}.calendar-table-header-divider{border-bottom:1px solid var(--ipi-calendar-multi-year-view-table-header-border-bottom-color, #C6C6C6)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "component", type: CalendarBody, selector: "[calendar-body]", inputs: ["label", "rows", "todayValue", "startValue", "endValue", "labelMinRequiredCells", "numCols", "activeCell", "isRange", "cellAspectRatio", "comparisonStart", "comparisonEnd", "previewStart", "previewEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["selectedValueChange", "previewChange", "activeDateChange", "dragStarted", "dragEnded"] }, { kind: "ngmodule", type: ReactiveFormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1569
|
+
}
|
|
1570
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiCalendarMultiYearView, decorators: [{
|
|
1571
|
+
type: Component,
|
|
1572
|
+
args: [{ selector: 'ipi-calendar-multi-year-view', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
1573
|
+
FormsModule,
|
|
1574
|
+
CalendarBody,
|
|
1575
|
+
ReactiveFormsModule,
|
|
1576
|
+
], template: "<table class=\"calendar-table\" role=\"grid\">\n <thead aria-hidden=\"true\" class=\"calendar-table-header\">\n <tr>\n <th class=\"calendar-table-header-divider\" colspan=\"4\"></th>\n </tr>\n </thead>\n\n <tbody calendar-body\n [rows]=\"_years\"\n [todayValue]=\"_todayYear\"\n [startValue]=\"selectedYear!\"\n [endValue]=\"selectedYear!\"\n [numCols]=\"4\"\n [cellAspectRatio]=\"4 / 7\"\n [activeCell]=\"getActiveCell()\"\n (selectedValueChange)=\"onYearSelected($event)\"\n (activeDateChange)=\"updateActiveDate($event)\"\n (keyup)=\"handleCalendarBodyKeyup($event)\"\n (keydown)=\"handleCalendarBodyKeydown($event)\">\n </tbody>\n</table>\n", styles: [":host{display:block}table{width:100%;padding:6px;border-collapse:separate}.calendar-table-header-divider{border-bottom:1px solid var(--ipi-calendar-multi-year-view-table-header-border-bottom-color, #C6C6C6)}\n"] }]
|
|
1577
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: IpiDatepickerService }], propDecorators: { activeDate: [{
|
|
1578
|
+
type: Input
|
|
1579
|
+
}], selected: [{
|
|
1580
|
+
type: Input
|
|
1581
|
+
}], minDate: [{
|
|
1582
|
+
type: Input
|
|
1583
|
+
}], maxDate: [{
|
|
1584
|
+
type: Input
|
|
1585
|
+
}], selectedChange: [{
|
|
1586
|
+
type: Output
|
|
1587
|
+
}], yearSelected: [{
|
|
1588
|
+
type: Output
|
|
1589
|
+
}], activeDateChange: [{
|
|
1590
|
+
type: Output
|
|
1591
|
+
}], calendarBody: [{
|
|
1592
|
+
type: ViewChild,
|
|
1593
|
+
args: [CalendarBody]
|
|
1594
|
+
}] } });
|
|
1595
|
+
|
|
1596
|
+
const months = [
|
|
1597
|
+
'January',
|
|
1598
|
+
'February',
|
|
1599
|
+
'March',
|
|
1600
|
+
'April',
|
|
1601
|
+
'May',
|
|
1602
|
+
'Jun',
|
|
1603
|
+
'July',
|
|
1604
|
+
'August',
|
|
1605
|
+
'September',
|
|
1606
|
+
'October',
|
|
1607
|
+
'November',
|
|
1608
|
+
'December',
|
|
1609
|
+
];
|
|
1610
|
+
class IpiCalendarComponent {
|
|
1611
|
+
constructor(datePickerService) {
|
|
1612
|
+
this.datePickerService = datePickerService;
|
|
1613
|
+
this.monthSelected = new EventEmitter();
|
|
1614
|
+
this.yearSelected = new EventEmitter();
|
|
1615
|
+
this.activeDateChange = new EventEmitter();
|
|
1616
|
+
this.userDragDrop = new EventEmitter();
|
|
1617
|
+
this.selectedChange = new EventEmitter();
|
|
1618
|
+
this.userSelection = new EventEmitter();
|
|
1619
|
+
this.calendarClose = new EventEmitter();
|
|
1620
|
+
this.isButtonHidden = true;
|
|
1621
|
+
this.activeDateView = 'month';
|
|
1622
|
+
this.activeDrag = null;
|
|
1623
|
+
}
|
|
1624
|
+
get activeDate() {
|
|
1625
|
+
return this._activeDate;
|
|
1626
|
+
}
|
|
1627
|
+
set activeDate(value) {
|
|
1628
|
+
const validDate = this.datePickerService.getValidDateOrNull(this.datePickerService.deserialize(value)) ||
|
|
1629
|
+
this.datePickerService.today();
|
|
1630
|
+
this._activeDate = this.datePickerService.clampDate(validDate, validDate, validDate);
|
|
1631
|
+
}
|
|
1632
|
+
get minDate() {
|
|
1633
|
+
return this._minDate;
|
|
1634
|
+
}
|
|
1635
|
+
set minDate(value) {
|
|
1636
|
+
this._minDate = this.datePickerService.getValidDateOrNull(this.datePickerService.deserialize(value));
|
|
1637
|
+
}
|
|
1638
|
+
get selected() {
|
|
1639
|
+
return this._selected;
|
|
1640
|
+
}
|
|
1641
|
+
set selected(value) {
|
|
1642
|
+
if (value instanceof DateRange) {
|
|
1643
|
+
this._selected = value;
|
|
1644
|
+
}
|
|
1645
|
+
else {
|
|
1646
|
+
this._selected = this.datePickerService.getValidDateOrNull(this.datePickerService.deserialize(value));
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
get maxDate() {
|
|
1650
|
+
return this._maxDate;
|
|
1651
|
+
}
|
|
1652
|
+
set maxDate(value) {
|
|
1653
|
+
this._maxDate = this.datePickerService.getValidDateOrNull(this.datePickerService.deserialize(value));
|
|
1654
|
+
}
|
|
1655
|
+
calendarDateViewChange() {
|
|
1656
|
+
this.activeDateView = this.activeDateView == 'month' ? 'multi-year' : 'month';
|
|
1657
|
+
}
|
|
1658
|
+
onCalendarDateViewKeydown(event) {
|
|
1659
|
+
if (event.code.startsWith('Arrow')) {
|
|
1660
|
+
event.preventDefault();
|
|
1661
|
+
}
|
|
1662
|
+
if (event.code === 'Enter' || event.code === 'Space') {
|
|
1663
|
+
this.calendarDateViewChange();
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
goToPreviousView() {
|
|
1667
|
+
this.activeDate =
|
|
1668
|
+
this.activeDateView == 'month'
|
|
1669
|
+
? this.datePickerService.addCalendarMonths(this.activeDate, -1)
|
|
1670
|
+
: this.datePickerService.addCalendarYears(this.activeDate, this.activeDateView == 'year' ? -1 : -this.datePickerService.yearsPerPage);
|
|
1671
|
+
}
|
|
1672
|
+
goToNextView() {
|
|
1673
|
+
this.activeDate =
|
|
1674
|
+
this.activeDateView == 'month'
|
|
1675
|
+
? this.datePickerService.addCalendarMonths(this.activeDate, 1)
|
|
1676
|
+
: this.datePickerService.addCalendarYears(this.activeDate, this.activeDateView == 'year' ? 1 : this.datePickerService.yearsPerPage);
|
|
1677
|
+
}
|
|
1678
|
+
onArrowKeydown(event, direction) {
|
|
1679
|
+
if (event.code.startsWith('Arrow')) {
|
|
1680
|
+
event.preventDefault();
|
|
1681
|
+
}
|
|
1682
|
+
if (event.code === 'Enter' || event.code === 'Space') {
|
|
1683
|
+
if (direction === 'next') {
|
|
1684
|
+
this.goToNextView();
|
|
1685
|
+
}
|
|
1686
|
+
else {
|
|
1687
|
+
this.goToPreviousView();
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
previousEnabled() {
|
|
1692
|
+
if (!this.minDate) {
|
|
1693
|
+
return true;
|
|
1694
|
+
}
|
|
1695
|
+
return (!this.minDate || !this.isSameView(this.activeDate, this.minDate));
|
|
1696
|
+
}
|
|
1697
|
+
nextEnabled() {
|
|
1698
|
+
return (!this.maxDate || !this.isSameView(this.activeDate, this.maxDate));
|
|
1699
|
+
}
|
|
1700
|
+
dragStarted(event) {
|
|
1701
|
+
this.activeDrag = event;
|
|
1702
|
+
}
|
|
1703
|
+
/**
|
|
1704
|
+
* Called when a drag completes. It may end in cancelation or in the selection
|
|
1705
|
+
* of a new range.
|
|
1706
|
+
*/
|
|
1707
|
+
dragEnded(event) {
|
|
1708
|
+
if (!this.activeDrag)
|
|
1709
|
+
return;
|
|
1710
|
+
if (event.value) {
|
|
1711
|
+
this.userDragDrop.emit(event);
|
|
1712
|
+
}
|
|
1713
|
+
this.activeDrag = null;
|
|
1714
|
+
}
|
|
1715
|
+
get viewButtonText() {
|
|
1716
|
+
if (this.activeDateView === 'month') {
|
|
1717
|
+
return this.getCurrentMonthAndYear(this.activeDate);
|
|
1718
|
+
}
|
|
1719
|
+
if (this.activeDateView === 'year') {
|
|
1720
|
+
return this.getCurrentYear(this.activeDate);
|
|
1721
|
+
}
|
|
1722
|
+
return this.formatYearRange(...this.formatMinAndMaxYearLabels());
|
|
1723
|
+
}
|
|
1724
|
+
goToDateInView(date, view) {
|
|
1725
|
+
this.activeDate = date;
|
|
1726
|
+
this.activeDateView = view;
|
|
1727
|
+
}
|
|
1728
|
+
monthSelectedInYearView(normalizedMonth) {
|
|
1729
|
+
this.monthSelected.emit(normalizedMonth);
|
|
1730
|
+
}
|
|
1731
|
+
yearSelectedInMultiYearView(normalizedYear) {
|
|
1732
|
+
this.yearSelected.emit(normalizedYear);
|
|
1733
|
+
}
|
|
1734
|
+
dateSelected(event) {
|
|
1735
|
+
const date = event.value;
|
|
1736
|
+
if (this.selected instanceof DateRange ||
|
|
1737
|
+
(date && !this.datePickerService.sameDate(date, this.selected))) {
|
|
1738
|
+
this.selectedChange.emit(date);
|
|
1739
|
+
}
|
|
1740
|
+
this.userSelection.emit({ value: date, event });
|
|
1741
|
+
}
|
|
1742
|
+
toggleButtonVisibility(event) {
|
|
1743
|
+
if (event.type === 'focusout') {
|
|
1744
|
+
this.isButtonHidden = true;
|
|
1745
|
+
}
|
|
1746
|
+
else if (event.type === 'focusin') {
|
|
1747
|
+
this.isButtonHidden = false;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
getCurrentMonthAndYear(date) {
|
|
1751
|
+
const month = months[date.getMonth()];
|
|
1752
|
+
const year = date.getFullYear();
|
|
1753
|
+
return `${month} ${year}`;
|
|
1754
|
+
}
|
|
1755
|
+
getCurrentYear(date) {
|
|
1756
|
+
return date.getFullYear().toString();
|
|
1757
|
+
}
|
|
1758
|
+
formatYearRange(start, end) {
|
|
1759
|
+
return `${start} \u2013 ${end}`;
|
|
1760
|
+
}
|
|
1761
|
+
formatMinAndMaxYearLabels() {
|
|
1762
|
+
// The offset from the active year to the "slot" for the starting year is the
|
|
1763
|
+
// *actual* first rendered year in the multi-year view, and the last year is
|
|
1764
|
+
// just yearsPerPage - 1 away.
|
|
1765
|
+
const activeYear = this.datePickerService.getYear(this.activeDate);
|
|
1766
|
+
const minYearOfPage = activeYear -
|
|
1767
|
+
this.datePickerService.getActiveOffset(this.activeDate, this.minDate, this.maxDate);
|
|
1768
|
+
const maxYearOfPage = minYearOfPage + this.datePickerService.yearsPerPage - 1;
|
|
1769
|
+
const minYearLabel = this.datePickerService.getYearName(this.datePickerService.createDate(minYearOfPage, 0, 1));
|
|
1770
|
+
const maxYearLabel = this.datePickerService.getYearName(this.datePickerService.createDate(maxYearOfPage, 0, 1));
|
|
1771
|
+
return [minYearLabel, maxYearLabel];
|
|
1772
|
+
}
|
|
1773
|
+
isSameView(date1, date2) {
|
|
1774
|
+
if (this.activeDateView == 'month') {
|
|
1775
|
+
return (this.datePickerService.getYear(date1) == this.datePickerService.getYear(date2) &&
|
|
1776
|
+
this.datePickerService.getMonth(date1) == this.datePickerService.getMonth(date2));
|
|
1777
|
+
}
|
|
1778
|
+
if (this.activeDateView == 'year') {
|
|
1779
|
+
return this.datePickerService.getYear(date1) == this.datePickerService.getYear(date2);
|
|
1780
|
+
}
|
|
1781
|
+
// Otherwise we are in 'multi-year' view.
|
|
1782
|
+
return this.datePickerService.isSameMultiYearView(date1, date2, this.minDate, this.maxDate);
|
|
1783
|
+
}
|
|
1784
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiCalendarComponent, deps: [{ token: IpiDatepickerService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1785
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: IpiCalendarComponent, isStandalone: true, selector: "ipi-calendar", inputs: { activeDate: "activeDate", minDate: "minDate", selected: "selected", maxDate: "maxDate", comparisonStart: "comparisonStart", comparisonEnd: "comparisonEnd", startDateAccessibleName: "startDateAccessibleName", endDateAccessibleName: "endDateAccessibleName" }, outputs: { monthSelected: "monthSelected", yearSelected: "yearSelected", activeDateChange: "activeDateChange", userDragDrop: "userDragDrop", selectedChange: "selectedChange", userSelection: "userSelection", calendarClose: "calendarClose" }, ngImport: i0, template: "<div class=\"container\">\n <div class=\"header\">\n <span \n tabindex=\"0\"\n class=\"active-date-view\"\n (click)=\"calendarDateViewChange()\"\n (keydown)=\"onCalendarDateViewKeydown($event)\">\n {{ viewButtonText }}\n </span>\n\n <div class=\"header-navigation\">\n <div class=\"chevron-wrapper\"\n tabindex=\"0\"\n [class.disabled]=\"!previousEnabled()\"\n (click)=\"goToPreviousView()\"\n (keydown)=\"onArrowKeydown($event, 'previous')\">\n <span class=\"chevron left\"></span>\n </div>\n\n <div class=\"chevron-wrapper\" \n tabindex=\"0\"\n [class.disabled]=\"!nextEnabled()\" \n (click)=\"goToNextView()\"\n (keydown)=\"onArrowKeydown($event, 'next')\">\n <span class=\"chevron right\"></span>\n </div>\n </div>\n </div>\n\n <div class=\"calendar\">\n @switch (activeDateView) {\n @case ('month') {\n <ipi-calendar-month-view\n [(activeDate)]=\"activeDate\"\n [(selected)]=\"selected\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n (userSelection)=\"dateSelected($event)\"\n (dragStarted)=\"dragStarted($event)\"\n (dragEnded)=\"dragEnded($event)\"\n [activeDrag]=\"activeDrag\">\n </ipi-calendar-month-view>\n }\n\n @case ('year') {\n <ipi-calendar-year-view\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n (monthSelected)=\"monthSelectedInYearView($event)\"\n (selectedChange)=\"goToDateInView($event, 'month')\">\n </ipi-calendar-year-view>\n }\n\n @case ('multi-year') {\n <ipi-calendar-multi-year-view\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n (yearSelected)=\"yearSelectedInMultiYearView($event)\"\n (selectedChange)=\"goToDateInView($event, 'year')\">\n </ipi-calendar-multi-year-view>\n }\n }\n </div>\n</div>\n\n<ipi-button\n [ariaLabel]=\"'Button for closing the calendar.'\" \n (clickChange)=\"calendarClose.emit($event)\" \n (focusChange)=\"toggleButtonVisibility($event)\"\n [class.cdk-visually-hidden]=\"isButtonHidden\">\n Close Calendar\n</ipi-button>\n", styles: [":host{position:relative}.header{display:flex;justify-content:space-between;align-items:center;color:var(--ipi-datepicker-header-text-color, #FF7955);padding:8px 8px 0}.active-date-view{cursor:pointer;border-radius:4px;padding:2px 4px}.active-date-view:focus{outline:none;color:var(--ipi-calendar-header-active-date-view-focus-color, #FFFFFF);background-color:var(--ipi-calendar-header-active-date-view-focus-background-color, #FF7955)}.active-date-view:hover{border-radius:4px;color:var(--ipi-calendar-header-active-date-view-hover-color, #FFFFFF);background-color:var(--ipi-calendar-header-active-date-view-hover-background-color, #FF7955)}.header-navigation{display:flex}.chevron-wrapper{display:flex;border-radius:50%}.chevron-wrapper:focus{outline:none}.chevron-wrapper:hover,.chevron-wrapper:focus:not(.disabled){color:var(--ipi-calendar-header-chevron-hover-focus-color, #FFFFFF);background-color:var(--ipi-calendar-header-chevron-hover-focus-background-color, #FF7955)}.chevron{width:24px;height:24px;padding:8px;position:relative;cursor:pointer}.chevron:after{inset:0;position:absolute;content:\"\";margin:15.5px;border:0 solid currentColor;border-top-width:2px;border-left-width:2px}.chevron.right{transform:rotate(135deg)}.chevron-wrapper.disabled{pointer-events:none;background-color:none;color:var(--ipi-calendar-header-chevron-disabled-color, #5D6068)}.chevron.left{transform:rotate(-45deg)}ipi-button{position:absolute;bottom:-50px}\n"], dependencies: [{ kind: "component", type: IpiButtonComponent, selector: "ipi-button", inputs: ["link", "state", "options", "ariaLabel"], outputs: ["focusChange", "clickChange"] }, { kind: "component", type: IpiCalendarYearView, selector: "ipi-calendar-year-view", inputs: ["activeDate", "selected", "minDate", "maxDate"], outputs: ["selectedChange", "monthSelected", "activeDateChange"] }, { kind: "component", type: IpiCalendarMonthView, selector: "ipi-calendar-month-view", inputs: ["activeDate", "selected", "minDate", "maxDate", "activeDrag"], outputs: ["dragStarted", "dragEnded", "activeDateChange", "userSelection", "selectedChange"] }, { kind: "component", type: IpiCalendarMultiYearView, selector: "ipi-calendar-multi-year-view", inputs: ["activeDate", "selected", "minDate", "maxDate"], outputs: ["selectedChange", "yearSelected", "activeDateChange"] }] }); }
|
|
1786
|
+
}
|
|
1787
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiCalendarComponent, decorators: [{
|
|
1788
|
+
type: Component,
|
|
1789
|
+
args: [{ selector: 'ipi-calendar', imports: [
|
|
1790
|
+
IpiButtonComponent,
|
|
1791
|
+
IpiCalendarYearView,
|
|
1792
|
+
IpiCalendarMonthView,
|
|
1793
|
+
IpiCalendarMultiYearView,
|
|
1794
|
+
], template: "<div class=\"container\">\n <div class=\"header\">\n <span \n tabindex=\"0\"\n class=\"active-date-view\"\n (click)=\"calendarDateViewChange()\"\n (keydown)=\"onCalendarDateViewKeydown($event)\">\n {{ viewButtonText }}\n </span>\n\n <div class=\"header-navigation\">\n <div class=\"chevron-wrapper\"\n tabindex=\"0\"\n [class.disabled]=\"!previousEnabled()\"\n (click)=\"goToPreviousView()\"\n (keydown)=\"onArrowKeydown($event, 'previous')\">\n <span class=\"chevron left\"></span>\n </div>\n\n <div class=\"chevron-wrapper\" \n tabindex=\"0\"\n [class.disabled]=\"!nextEnabled()\" \n (click)=\"goToNextView()\"\n (keydown)=\"onArrowKeydown($event, 'next')\">\n <span class=\"chevron right\"></span>\n </div>\n </div>\n </div>\n\n <div class=\"calendar\">\n @switch (activeDateView) {\n @case ('month') {\n <ipi-calendar-month-view\n [(activeDate)]=\"activeDate\"\n [(selected)]=\"selected\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n (userSelection)=\"dateSelected($event)\"\n (dragStarted)=\"dragStarted($event)\"\n (dragEnded)=\"dragEnded($event)\"\n [activeDrag]=\"activeDrag\">\n </ipi-calendar-month-view>\n }\n\n @case ('year') {\n <ipi-calendar-year-view\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n (monthSelected)=\"monthSelectedInYearView($event)\"\n (selectedChange)=\"goToDateInView($event, 'month')\">\n </ipi-calendar-year-view>\n }\n\n @case ('multi-year') {\n <ipi-calendar-multi-year-view\n [(activeDate)]=\"activeDate\"\n [selected]=\"selected\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n (yearSelected)=\"yearSelectedInMultiYearView($event)\"\n (selectedChange)=\"goToDateInView($event, 'year')\">\n </ipi-calendar-multi-year-view>\n }\n }\n </div>\n</div>\n\n<ipi-button\n [ariaLabel]=\"'Button for closing the calendar.'\" \n (clickChange)=\"calendarClose.emit($event)\" \n (focusChange)=\"toggleButtonVisibility($event)\"\n [class.cdk-visually-hidden]=\"isButtonHidden\">\n Close Calendar\n</ipi-button>\n", styles: [":host{position:relative}.header{display:flex;justify-content:space-between;align-items:center;color:var(--ipi-datepicker-header-text-color, #FF7955);padding:8px 8px 0}.active-date-view{cursor:pointer;border-radius:4px;padding:2px 4px}.active-date-view:focus{outline:none;color:var(--ipi-calendar-header-active-date-view-focus-color, #FFFFFF);background-color:var(--ipi-calendar-header-active-date-view-focus-background-color, #FF7955)}.active-date-view:hover{border-radius:4px;color:var(--ipi-calendar-header-active-date-view-hover-color, #FFFFFF);background-color:var(--ipi-calendar-header-active-date-view-hover-background-color, #FF7955)}.header-navigation{display:flex}.chevron-wrapper{display:flex;border-radius:50%}.chevron-wrapper:focus{outline:none}.chevron-wrapper:hover,.chevron-wrapper:focus:not(.disabled){color:var(--ipi-calendar-header-chevron-hover-focus-color, #FFFFFF);background-color:var(--ipi-calendar-header-chevron-hover-focus-background-color, #FF7955)}.chevron{width:24px;height:24px;padding:8px;position:relative;cursor:pointer}.chevron:after{inset:0;position:absolute;content:\"\";margin:15.5px;border:0 solid currentColor;border-top-width:2px;border-left-width:2px}.chevron.right{transform:rotate(135deg)}.chevron-wrapper.disabled{pointer-events:none;background-color:none;color:var(--ipi-calendar-header-chevron-disabled-color, #5D6068)}.chevron.left{transform:rotate(-45deg)}ipi-button{position:absolute;bottom:-50px}\n"] }]
|
|
1795
|
+
}], ctorParameters: () => [{ type: IpiDatepickerService }], propDecorators: { activeDate: [{
|
|
1796
|
+
type: Input
|
|
1797
|
+
}], minDate: [{
|
|
1798
|
+
type: Input
|
|
1799
|
+
}], selected: [{
|
|
1800
|
+
type: Input
|
|
1801
|
+
}], maxDate: [{
|
|
1802
|
+
type: Input
|
|
1803
|
+
}], comparisonStart: [{
|
|
1804
|
+
type: Input
|
|
1805
|
+
}], comparisonEnd: [{
|
|
1806
|
+
type: Input
|
|
1807
|
+
}], startDateAccessibleName: [{
|
|
1808
|
+
type: Input
|
|
1809
|
+
}], endDateAccessibleName: [{
|
|
1810
|
+
type: Input
|
|
1811
|
+
}], monthSelected: [{
|
|
1812
|
+
type: Output
|
|
1813
|
+
}], yearSelected: [{
|
|
1814
|
+
type: Output
|
|
1815
|
+
}], activeDateChange: [{
|
|
1816
|
+
type: Output
|
|
1817
|
+
}], userDragDrop: [{
|
|
1818
|
+
type: Output
|
|
1819
|
+
}], selectedChange: [{
|
|
1820
|
+
type: Output
|
|
1821
|
+
}], userSelection: [{
|
|
1822
|
+
type: Output
|
|
1823
|
+
}], calendarClose: [{
|
|
1824
|
+
type: Output
|
|
1825
|
+
}] } });
|
|
1826
|
+
|
|
1827
|
+
/** A class representing a range of dates. */
|
|
1828
|
+
class DateRange {
|
|
1829
|
+
constructor(start, end) {
|
|
1830
|
+
this.start = start;
|
|
1831
|
+
this.end = end;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
class DateRangeFormControls {
|
|
1835
|
+
constructor(start, end) {
|
|
1836
|
+
this.start = start;
|
|
1837
|
+
this.end = end;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
class IpiDatepickerComponent {
|
|
1841
|
+
constructor(ngZone, osService, elementRef, overlayService, changeDetectorRef, datepickerService, rangeSelectionStrategy) {
|
|
1842
|
+
this.ngZone = ngZone;
|
|
1843
|
+
this.osService = osService;
|
|
1844
|
+
this.elementRef = elementRef;
|
|
1845
|
+
this.overlayService = overlayService;
|
|
1846
|
+
this.changeDetectorRef = changeDetectorRef;
|
|
1847
|
+
this.datepickerService = datepickerService;
|
|
1848
|
+
this.rangeSelectionStrategy = rangeSelectionStrategy;
|
|
1849
|
+
this.closeButtonFocused = false;
|
|
1850
|
+
this.formErrorMessage = null;
|
|
1851
|
+
this._minDate = this.options?.min ? this.options.min : null;
|
|
1852
|
+
this._maxDate = this.options?.max ? this.options.max : null;
|
|
1853
|
+
this.formControlSubscriptions = [];
|
|
1854
|
+
}
|
|
1855
|
+
get activeDate() {
|
|
1856
|
+
return this._activeDate;
|
|
1857
|
+
}
|
|
1858
|
+
set activeDate(value) {
|
|
1859
|
+
const validDate = this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(value)) ||
|
|
1860
|
+
this.datepickerService.today();
|
|
1861
|
+
this._activeDate = this.datepickerService.clampDate(validDate, this.minDate, this.maxDate);
|
|
1862
|
+
this._activeDate = value;
|
|
1863
|
+
this.shouldShowCalendar = false;
|
|
1864
|
+
}
|
|
1865
|
+
get minDate() {
|
|
1866
|
+
return this._minDate;
|
|
1867
|
+
}
|
|
1868
|
+
set minDate(value) {
|
|
1869
|
+
this._minDate = this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(value));
|
|
1870
|
+
}
|
|
1871
|
+
get maxDate() {
|
|
1872
|
+
return this._maxDate;
|
|
1873
|
+
}
|
|
1874
|
+
set maxDate(value) {
|
|
1875
|
+
this._maxDate = this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(value));
|
|
1876
|
+
}
|
|
1877
|
+
get selectedDate() {
|
|
1878
|
+
return this._selected;
|
|
1879
|
+
}
|
|
1880
|
+
set selectedDate(value) {
|
|
1881
|
+
if (this.options.formControlName instanceof DateRangeFormControls) {
|
|
1882
|
+
if (value instanceof DateRange) {
|
|
1883
|
+
this._selected = value;
|
|
1884
|
+
}
|
|
1885
|
+
else {
|
|
1886
|
+
this._selected = new DateRange(this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(value)), null);
|
|
1887
|
+
}
|
|
1888
|
+
this.options.formGroup.controls[this.options.formControlName.start].setValue(this._selected.start, { emitEvent: false });
|
|
1889
|
+
this.options.formGroup.controls[this.options.formControlName.end].setValue(this._selected.end, { emitEvent: false });
|
|
1890
|
+
}
|
|
1891
|
+
else {
|
|
1892
|
+
this._selected = this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(value));
|
|
1893
|
+
this.options.formGroup.controls[this.options.formControlName].setValue(this._selected, { emitEvent: false });
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
ngOnInit() {
|
|
1897
|
+
this._selected =
|
|
1898
|
+
this.options.formControlName instanceof DateRangeFormControls
|
|
1899
|
+
? new DateRange(this.options.formGroup.value[this.options.formControlName.start], this.options.formGroup.value[this.options.formControlName.end])
|
|
1900
|
+
: this.options.formGroup.value[this.options.formControlName];
|
|
1901
|
+
this.maxDate = this.options.max ? this.options.max : null;
|
|
1902
|
+
this.minDate = this.options.min ? this.options.min : null;
|
|
1903
|
+
this.initDefaultErrors();
|
|
1904
|
+
this.changeDetectorRef.detectChanges();
|
|
1905
|
+
this.ngZone.runOutsideAngular(() => {
|
|
1906
|
+
if (this.datepicker) {
|
|
1907
|
+
this.datepicker.nativeElement.addEventListener('focus', () => this.activateControl());
|
|
1908
|
+
}
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
ngAfterViewInit() {
|
|
1912
|
+
this.setFormSubscriptions();
|
|
1913
|
+
}
|
|
1914
|
+
ngOnDestroy() {
|
|
1915
|
+
if (this.datepicker) {
|
|
1916
|
+
this.datepicker.nativeElement.removeEventListener('focusin', () => this.activateControl());
|
|
1917
|
+
}
|
|
1918
|
+
this.formControlSubscriptions.forEach((subscription) => subscription.unsubscribe());
|
|
1919
|
+
}
|
|
1920
|
+
activateControl() {
|
|
1921
|
+
if (this.calendar) {
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
this.ngZone.run(() => {
|
|
1925
|
+
this.shouldShowCalendar = true;
|
|
1926
|
+
});
|
|
1927
|
+
this.changeDetectorRef.markForCheck();
|
|
1928
|
+
setTimeout(() => {
|
|
1929
|
+
this.generateCalendarPosition(this.calendar);
|
|
1930
|
+
this.overlayService.appendToOverlay(this.calendar.nativeElement, true);
|
|
1931
|
+
this.changeDetectorRef.detectChanges();
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
onClick(event) {
|
|
1935
|
+
if (!this.shouldShowCalendar) {
|
|
1936
|
+
return;
|
|
1937
|
+
}
|
|
1938
|
+
if (this.datepicker) {
|
|
1939
|
+
const isIputClicked = this.checkPosition(event, this.elementRef.nativeElement.getBoundingClientRect());
|
|
1940
|
+
if (isIputClicked) {
|
|
1941
|
+
this.shouldShowCalendar = isIputClicked;
|
|
1942
|
+
if (!this.shouldShowCalendar) {
|
|
1943
|
+
this.overlayService.removeFromOverlay(this.calendar.nativeElement);
|
|
1944
|
+
}
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
if (this.calendar) {
|
|
1949
|
+
const isCalendarClicked = this.checkPosition(event, this.calendar.nativeElement.getBoundingClientRect());
|
|
1950
|
+
if (isCalendarClicked) {
|
|
1951
|
+
this.ngZone.run(() => {
|
|
1952
|
+
this.shouldShowCalendar = isCalendarClicked;
|
|
1953
|
+
});
|
|
1954
|
+
if (!this.shouldShowCalendar) {
|
|
1955
|
+
this.overlayService.removeFromOverlay(this.calendar.nativeElement);
|
|
1956
|
+
}
|
|
1957
|
+
return;
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
this.closeCalendar();
|
|
1961
|
+
if (!this.shouldShowCalendar) {
|
|
1962
|
+
this.overlayService.removeFromOverlay(this.calendar.nativeElement);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
getDisplayValue() {
|
|
1966
|
+
if (!this.selectedDate) {
|
|
1967
|
+
return 'No date selected.';
|
|
1968
|
+
}
|
|
1969
|
+
if (this.selectedDate instanceof Date) {
|
|
1970
|
+
return this.datepickerService.formatter.format(this.selectedDate);
|
|
1971
|
+
}
|
|
1972
|
+
if (this.selectedDate instanceof DateRange) {
|
|
1973
|
+
if (this.selectedDate.start && this.selectedDate.end) {
|
|
1974
|
+
return `${this.datepickerService.formatter.format(this.selectedDate.start)} - ${this.datepickerService.formatter.format(this.selectedDate.end)}`;
|
|
1975
|
+
}
|
|
1976
|
+
if (this.selectedDate.start) {
|
|
1977
|
+
return `${this.datepickerService.formatter.format(this.selectedDate.start)} - `;
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
return 'No date selected.';
|
|
1981
|
+
}
|
|
1982
|
+
getformErrorMessage() {
|
|
1983
|
+
this.formErrorMessage = this.getErrorIfControlInvalid();
|
|
1984
|
+
return this.formErrorMessage ? this.formErrorMessage : null;
|
|
1985
|
+
}
|
|
1986
|
+
onCloseClick() {
|
|
1987
|
+
this.closeCalendar();
|
|
1988
|
+
if (this.datepicker) {
|
|
1989
|
+
this.datepicker.nativeElement.focus();
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
onCloseKeyDown(event) {
|
|
1993
|
+
if (event.code.startsWith('Arrow') || event.code === 'Space') {
|
|
1994
|
+
event.preventDefault();
|
|
1995
|
+
}
|
|
1996
|
+
if (event.code === 'Enter' || event.code === 'Space') {
|
|
1997
|
+
this.closeCalendar();
|
|
1998
|
+
if (this.datepicker) {
|
|
1999
|
+
this.datepicker.nativeElement.focus();
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
handleDragAndDropEnd(event) {
|
|
2004
|
+
this.selectedDate = new DateRange(event.value.start, event.value.end);
|
|
2005
|
+
}
|
|
2006
|
+
handleUserSelection(event) {
|
|
2007
|
+
const selection = this.selectedDate;
|
|
2008
|
+
const value = event.value;
|
|
2009
|
+
const isRange = selection instanceof DateRange;
|
|
2010
|
+
if (!value) {
|
|
2011
|
+
this.selectedDate = null;
|
|
2012
|
+
return;
|
|
2013
|
+
}
|
|
2014
|
+
if (isRange && this.rangeSelectionStrategy) {
|
|
2015
|
+
const newSelection = this.rangeSelectionStrategy.selectionFinished(value, selection);
|
|
2016
|
+
this.selectedDate = newSelection;
|
|
2017
|
+
}
|
|
2018
|
+
else if (value && (isRange || !this.datepickerService.sameDate(value, selection))) {
|
|
2019
|
+
this.selectedDate = value;
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
closeCalendar() {
|
|
2023
|
+
this.shouldShowCalendar = false;
|
|
2024
|
+
if (this.options.formControlName instanceof DateRangeFormControls) {
|
|
2025
|
+
this.options.formGroup.controls[this.options.formControlName.start].markAsTouched();
|
|
2026
|
+
this.options.formGroup.controls[this.options.formControlName.end].markAsTouched();
|
|
2027
|
+
}
|
|
2028
|
+
else {
|
|
2029
|
+
this.options.formGroup.controls[this.options.formControlName].markAsTouched();
|
|
2030
|
+
}
|
|
2031
|
+
this.overlayService.removeFromOverlay(this.calendar.nativeElement);
|
|
2032
|
+
}
|
|
2033
|
+
generateCalendarPosition(calendar) {
|
|
2034
|
+
const calendarEl = calendar.nativeElement;
|
|
2035
|
+
const elementRefRect = this.elementRef.nativeElement.getBoundingClientRect();
|
|
2036
|
+
if (this.osService.mobileOS === MobileOS.iOS) {
|
|
2037
|
+
elementRefRect.y += window.visualViewport.offsetTop;
|
|
2038
|
+
elementRefRect.x += window.visualViewport.offsetLeft;
|
|
2039
|
+
}
|
|
2040
|
+
calendarEl.style.left = elementRefRect.x + 'px';
|
|
2041
|
+
calendarEl.style.top = elementRefRect.y + elementRefRect.height + 10 + 'px';
|
|
2042
|
+
if (window.innerHeight < calendarEl.getBoundingClientRect().bottom) {
|
|
2043
|
+
calendarEl.style.top = 'unset';
|
|
2044
|
+
calendarEl.style.left = elementRefRect.x + 'px';
|
|
2045
|
+
calendarEl.style.bottom = window.innerHeight - elementRefRect.y + 10 + 'px';
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
setFormSubscriptions() {
|
|
2049
|
+
if (this.options.formControlName instanceof DateRangeFormControls) {
|
|
2050
|
+
const startControl = this.options.formGroup.controls[this.options.formControlName.start];
|
|
2051
|
+
const endControl = this.options.formGroup.controls[this.options.formControlName.end];
|
|
2052
|
+
this.formControlSubscriptions.push(startControl.valueChanges.subscribe((value) => {
|
|
2053
|
+
if (value && this.selectedDate instanceof DateRange) {
|
|
2054
|
+
this.selectedDate = new DateRange(this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(value)), this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(this.selectedDate.end)));
|
|
2055
|
+
}
|
|
2056
|
+
this.changeDetectorRef.detectChanges();
|
|
2057
|
+
}), endControl.valueChanges.subscribe((value) => {
|
|
2058
|
+
if (value && this.selectedDate instanceof DateRange) {
|
|
2059
|
+
this.selectedDate = new DateRange(this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(this.selectedDate.start)), this.datepickerService.getValidDateOrNull(this.datepickerService.deserialize(value)));
|
|
2060
|
+
this.changeDetectorRef.detectChanges();
|
|
2061
|
+
}
|
|
2062
|
+
}));
|
|
2063
|
+
return;
|
|
2064
|
+
}
|
|
2065
|
+
else {
|
|
2066
|
+
const control = this.options.formGroup.controls[this.options.formControlName];
|
|
2067
|
+
this.formControlSubscriptions.push(control.valueChanges.subscribe(() => {
|
|
2068
|
+
this.selectedDate = control.value;
|
|
2069
|
+
this.changeDetectorRef.detectChanges();
|
|
2070
|
+
}));
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
// Unified position checker for both input and calendar
|
|
2074
|
+
checkPosition(event, rect) {
|
|
2075
|
+
const x = event.clientX;
|
|
2076
|
+
const y = event.clientY;
|
|
2077
|
+
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
|
|
2078
|
+
return true;
|
|
2079
|
+
}
|
|
2080
|
+
return false;
|
|
2081
|
+
}
|
|
2082
|
+
initDefaultErrors() {
|
|
2083
|
+
let defaultErrors = {
|
|
2084
|
+
required: 'This is a required field',
|
|
2085
|
+
};
|
|
2086
|
+
if (this.options.min) {
|
|
2087
|
+
this.setMinValidator();
|
|
2088
|
+
defaultErrors['datepickerMin'] = `Start date should be before ${this.datepickerService.formatter.format(this.minDate)}`;
|
|
2089
|
+
}
|
|
2090
|
+
if (this.options.max) {
|
|
2091
|
+
this.setMaxValidator();
|
|
2092
|
+
defaultErrors['datepickerMax'] = `End date should be before ${this.datepickerService.formatter.format(this.maxDate)}`;
|
|
2093
|
+
}
|
|
2094
|
+
if (!this.options.errors) {
|
|
2095
|
+
this.errors = defaultErrors;
|
|
2096
|
+
return;
|
|
2097
|
+
}
|
|
2098
|
+
this.errors = {
|
|
2099
|
+
...defaultErrors,
|
|
2100
|
+
...this.options.errors
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
getErrorIfControlInvalid() {
|
|
2104
|
+
if (this.options.formControlName instanceof DateRangeFormControls) {
|
|
2105
|
+
const startControl = this.options.formGroup.controls[this.options.formControlName.start];
|
|
2106
|
+
const endControl = this.options.formGroup.controls[this.options.formControlName.end];
|
|
2107
|
+
if (startControl.touched && startControl.invalid || endControl.touched && endControl.invalid) {
|
|
2108
|
+
for (const error in this.errors) {
|
|
2109
|
+
if (this.options.formGroup.controls[this.options.formControlName.start].hasError(error)) {
|
|
2110
|
+
return this.errors[error];
|
|
2111
|
+
}
|
|
2112
|
+
if (this.options.formGroup.controls[this.options.formControlName.end].hasError(error)) {
|
|
2113
|
+
return this.errors[error];
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
else {
|
|
2119
|
+
const control = this.options.formGroup.controls[this.options.formControlName];
|
|
2120
|
+
if (control.touched && control.invalid) {
|
|
2121
|
+
for (const error in this.errors) {
|
|
2122
|
+
if (this.options.formGroup.controls[this.options.formControlName].hasError(error)) {
|
|
2123
|
+
return this.errors[error];
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
return null;
|
|
2129
|
+
}
|
|
2130
|
+
setMinValidator() {
|
|
2131
|
+
const minValidator = this.datepickerService.minValidator(this.options.min);
|
|
2132
|
+
if (this.options.formControlName instanceof DateRangeFormControls) {
|
|
2133
|
+
this.options.formGroup.controls[this.options.formControlName.start].addValidators(minValidator);
|
|
2134
|
+
}
|
|
2135
|
+
else {
|
|
2136
|
+
this.options.formGroup.controls[this.options.formControlName].addValidators(minValidator);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
setMaxValidator() {
|
|
2140
|
+
const maxValidator = this.datepickerService.maxValidator(this.options.max);
|
|
2141
|
+
if (this.options.formControlName instanceof DateRangeFormControls) {
|
|
2142
|
+
this.options.formGroup.controls[this.options.formControlName.end].addValidators(maxValidator);
|
|
2143
|
+
}
|
|
2144
|
+
else {
|
|
2145
|
+
this.options.formGroup.controls[this.options.formControlName].addValidators(maxValidator);
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiDatepickerComponent, deps: [{ token: i0.NgZone }, { token: i1$1.OSService }, { token: i0.ElementRef }, { token: i1$1.OverlayService }, { token: i0.ChangeDetectorRef }, { token: IpiDatepickerService }, { token: DefaultCalendarRangeStrategy }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2149
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: IpiDatepickerComponent, isStandalone: true, selector: "ipi-datepicker", inputs: { options: "options" }, host: { listeners: { "document:click": "onClick($event)" } }, viewQueries: [{ propertyName: "datepicker", first: true, predicate: ["datepicker"], descendants: true }, { propertyName: "calendar", first: true, predicate: ["calendar"], descendants: true }], ngImport: i0, template: "@if (options) {\n @if (options.formControlName && options.formGroup) {\n <div class=\"label-wrapper\">\n @if (options.label !== '') {\n <label>{{ options.label }}</label>\n }\n </div>\n\n <div tabindex=\"0\" \n #datepicker \n class=\"datepicker\"\n [class.datepicker-error]=\"formErrorMessage\"\n (click)=\"this.activateControl()\">\n <span\n class=\"datepicker-value\"\n role=\"textbox\" \n aria-readonly=\"true\" \n aria-label=\"Selected date\"\n aria-live=\"polite\">\n {{ getDisplayValue() }}\n </span>\n\n <div class=\"svg-wrapper\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"#f1f1f1\" \n viewBox=\"0 0 256 256\"><path d=\"M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM72,48v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80H48V48ZM208,208H48V96H208V208Zm-68-76a12,12,0,1,1-12-12A12,12,0,0,1,140,132Zm44,0a12,12,0,1,1-12-12A12,12,0,0,1,184,132ZM96,172a12,12,0,1,1-12-12A12,12,0,0,1,96,172Zm44,0a12,12,0,1,1-12-12A12,12,0,0,1,140,172Zm44,0a12,12,0,1,1-12-12A12,12,0,0,1,184,172Z\"></path>\n </svg>\n </div>\n </div>\n\n <div class=\"footer\">\n <div>{{ getformErrorMessage() }}</div>\n </div>\n\n @if (shouldShowCalendar) {\n <div #calendar class=\"calendar\">\n <ipi-calendar\n [(activeDate)]=\"activeDate\"\n [(selected)]=\"selectedDate\"\n [minDate]=\"minDate\"\n [maxDate]=\"maxDate\"\n (calendarClose)=\"onCloseKeyDown($event)\"\n (userSelection)=\"handleUserSelection($event)\"\n (userDragDrop)=\"handleDragAndDropEnd($event)\">\n </ipi-calendar>\n </div>\n }\n }\n}\n", styles: [":host{width:296px;display:block;touch-action:manipulation}ipi-calendar{width:100%;height:358px}.header .label-wrapper{display:flex;justify-content:center;align-items:center;text-wrap:wrap}.label-wrapper label{font-size:14px;font-weight:600;margin-right:4px}.datepicker{display:flex;flex-direction:row;justify-content:space-between;align-items:center;cursor:pointer;border:var(--ipi-datepicker-border, 1px solid #E9E9E9);border-radius:4px 24px 24px 4px;transition:border .3s ease}.datepicker-error{border:var(--ipi-datepicker-error-border, 1px solid #F96138)}.datepicker-value{width:100%;text-align:center}.datepicker:hover,.datepicker:focus{outline:none;border-color:var(--ipi-datepicker-border-hover-color, #FF7955)}.svg-wrapper{height:40px}.svg-wrapper svg{background-color:var(--ipi-datepicker-svg-background-color, #FF7955);fill:var(--ipi-datepicker-svg-fill-color, #FFFFFF);border-radius:50%;padding:8px}.svg-wrapper svg:hover{background-color:var(--ipi-datepicker-svg-background-hover-color, #F96138)}.calendar{width:296px;display:flex;position:fixed;flex-direction:column;justify-content:center;align-items:center;pointer-events:all;border-radius:4px;background-color:var(--ipi-calendar-background-color, #ffffff);box-shadow:var(--ipi-calendar-box-shadow, 0px 2px 4px -1px rgba(0, 0, 0, .2), 0px 4px 5px 0px rgba(0, 0, 0, .14), 0px 1px 10px 0px rgba(0, 0, 0, .12));z-index:98;animation:fadeIn .3s}.footer{height:14px;font-size:12px;margin-top:2px;color:var(--ipi-datepicker-error-color, #F96138)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}::ng-deep .cdk-visually-hidden{width:1px;height:1px;clip:rect(0 0 0 0);overflow:hidden;position:absolute;white-space:nowrap;outline:0;border:0;left:0;padding:0;margin:-1px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "component", type: IpiCalendarComponent, selector: "ipi-calendar", inputs: ["activeDate", "minDate", "selected", "maxDate", "comparisonStart", "comparisonEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["monthSelected", "yearSelected", "activeDateChange", "userDragDrop", "selectedChange", "userSelection", "calendarClose"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2150
|
+
}
|
|
2151
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: IpiDatepickerComponent, decorators: [{
|
|
2152
|
+
type: Component,
|
|
2153
|
+
args: [{ selector: 'ipi-datepicker', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
2154
|
+
FormsModule,
|
|
2155
|
+
IpiCalendarComponent,
|
|
2156
|
+
], template: "@if (options) {\n @if (options.formControlName && options.formGroup) {\n <div class=\"label-wrapper\">\n @if (options.label !== '') {\n <label>{{ options.label }}</label>\n }\n </div>\n\n <div tabindex=\"0\" \n #datepicker \n class=\"datepicker\"\n [class.datepicker-error]=\"formErrorMessage\"\n (click)=\"this.activateControl()\">\n <span\n class=\"datepicker-value\"\n role=\"textbox\" \n aria-readonly=\"true\" \n aria-label=\"Selected date\"\n aria-live=\"polite\">\n {{ getDisplayValue() }}\n </span>\n\n <div class=\"svg-wrapper\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"#f1f1f1\" \n viewBox=\"0 0 256 256\"><path d=\"M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM72,48v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80H48V48ZM208,208H48V96H208V208Zm-68-76a12,12,0,1,1-12-12A12,12,0,0,1,140,132Zm44,0a12,12,0,1,1-12-12A12,12,0,0,1,184,132ZM96,172a12,12,0,1,1-12-12A12,12,0,0,1,96,172Zm44,0a12,12,0,1,1-12-12A12,12,0,0,1,140,172Zm44,0a12,12,0,1,1-12-12A12,12,0,0,1,184,172Z\"></path>\n </svg>\n </div>\n </div>\n\n <div class=\"footer\">\n <div>{{ getformErrorMessage() }}</div>\n </div>\n\n @if (shouldShowCalendar) {\n <div #calendar class=\"calendar\">\n <ipi-calendar\n [(activeDate)]=\"activeDate\"\n [(selected)]=\"selectedDate\"\n [minDate]=\"minDate\"\n [maxDate]=\"maxDate\"\n (calendarClose)=\"onCloseKeyDown($event)\"\n (userSelection)=\"handleUserSelection($event)\"\n (userDragDrop)=\"handleDragAndDropEnd($event)\">\n </ipi-calendar>\n </div>\n }\n }\n}\n", styles: [":host{width:296px;display:block;touch-action:manipulation}ipi-calendar{width:100%;height:358px}.header .label-wrapper{display:flex;justify-content:center;align-items:center;text-wrap:wrap}.label-wrapper label{font-size:14px;font-weight:600;margin-right:4px}.datepicker{display:flex;flex-direction:row;justify-content:space-between;align-items:center;cursor:pointer;border:var(--ipi-datepicker-border, 1px solid #E9E9E9);border-radius:4px 24px 24px 4px;transition:border .3s ease}.datepicker-error{border:var(--ipi-datepicker-error-border, 1px solid #F96138)}.datepicker-value{width:100%;text-align:center}.datepicker:hover,.datepicker:focus{outline:none;border-color:var(--ipi-datepicker-border-hover-color, #FF7955)}.svg-wrapper{height:40px}.svg-wrapper svg{background-color:var(--ipi-datepicker-svg-background-color, #FF7955);fill:var(--ipi-datepicker-svg-fill-color, #FFFFFF);border-radius:50%;padding:8px}.svg-wrapper svg:hover{background-color:var(--ipi-datepicker-svg-background-hover-color, #F96138)}.calendar{width:296px;display:flex;position:fixed;flex-direction:column;justify-content:center;align-items:center;pointer-events:all;border-radius:4px;background-color:var(--ipi-calendar-background-color, #ffffff);box-shadow:var(--ipi-calendar-box-shadow, 0px 2px 4px -1px rgba(0, 0, 0, .2), 0px 4px 5px 0px rgba(0, 0, 0, .14), 0px 1px 10px 0px rgba(0, 0, 0, .12));z-index:98;animation:fadeIn .3s}.footer{height:14px;font-size:12px;margin-top:2px;color:var(--ipi-datepicker-error-color, #F96138)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}::ng-deep .cdk-visually-hidden{width:1px;height:1px;clip:rect(0 0 0 0);overflow:hidden;position:absolute;white-space:nowrap;outline:0;border:0;left:0;padding:0;margin:-1px}\n"] }]
|
|
2157
|
+
}], ctorParameters: () => [{ type: i0.NgZone }, { type: i1$1.OSService }, { type: i0.ElementRef }, { type: i1$1.OverlayService }, { type: i0.ChangeDetectorRef }, { type: IpiDatepickerService }, { type: DefaultCalendarRangeStrategy }], propDecorators: { datepicker: [{
|
|
2158
|
+
type: ViewChild,
|
|
2159
|
+
args: ['datepicker']
|
|
2160
|
+
}], calendar: [{
|
|
2161
|
+
type: ViewChild,
|
|
2162
|
+
args: ['calendar']
|
|
2163
|
+
}], options: [{
|
|
2164
|
+
type: Input
|
|
2165
|
+
}], onClick: [{
|
|
2166
|
+
type: HostListener,
|
|
2167
|
+
args: ['document:click', ['$event']]
|
|
2168
|
+
}] } });
|
|
2169
|
+
|
|
2170
|
+
/**
|
|
2171
|
+
* Generated bundle index. Do not edit.
|
|
2172
|
+
*/
|
|
2173
|
+
|
|
2174
|
+
export { DateRange, DateRangeFormControls, IpiDatepickerComponent, IpiDatepickerService };
|
|
2175
|
+
//# sourceMappingURL=apexara-ng-components-datepicker.mjs.map
|