@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.
Files changed (59) hide show
  1. package/README.md +1867 -0
  2. package/fesm2022/apexara-ng-components-button.mjs +66 -0
  3. package/fesm2022/apexara-ng-components-button.mjs.map +1 -0
  4. package/fesm2022/apexara-ng-components-carousel.mjs +161 -0
  5. package/fesm2022/apexara-ng-components-carousel.mjs.map +1 -0
  6. package/fesm2022/apexara-ng-components-checkbox.mjs +64 -0
  7. package/fesm2022/apexara-ng-components-checkbox.mjs.map +1 -0
  8. package/fesm2022/apexara-ng-components-chip.mjs +26 -0
  9. package/fesm2022/apexara-ng-components-chip.mjs.map +1 -0
  10. package/fesm2022/apexara-ng-components-datepicker.mjs +2175 -0
  11. package/fesm2022/apexara-ng-components-datepicker.mjs.map +1 -0
  12. package/fesm2022/apexara-ng-components-dialog.mjs +65 -0
  13. package/fesm2022/apexara-ng-components-dialog.mjs.map +1 -0
  14. package/fesm2022/apexara-ng-components-drawer.mjs +69 -0
  15. package/fesm2022/apexara-ng-components-drawer.mjs.map +1 -0
  16. package/fesm2022/apexara-ng-components-expander.mjs +41 -0
  17. package/fesm2022/apexara-ng-components-expander.mjs.map +1 -0
  18. package/fesm2022/apexara-ng-components-image.mjs +201 -0
  19. package/fesm2022/apexara-ng-components-image.mjs.map +1 -0
  20. package/fesm2022/apexara-ng-components-input.mjs +129 -0
  21. package/fesm2022/apexara-ng-components-input.mjs.map +1 -0
  22. package/fesm2022/apexara-ng-components-listbox.mjs +308 -0
  23. package/fesm2022/apexara-ng-components-listbox.mjs.map +1 -0
  24. package/fesm2022/apexara-ng-components-radiobutton.mjs +40 -0
  25. package/fesm2022/apexara-ng-components-radiobutton.mjs.map +1 -0
  26. package/fesm2022/apexara-ng-components-select.mjs +620 -0
  27. package/fesm2022/apexara-ng-components-select.mjs.map +1 -0
  28. package/fesm2022/apexara-ng-components-services.mjs +290 -0
  29. package/fesm2022/apexara-ng-components-services.mjs.map +1 -0
  30. package/fesm2022/apexara-ng-components-snackbar.mjs +279 -0
  31. package/fesm2022/apexara-ng-components-snackbar.mjs.map +1 -0
  32. package/fesm2022/apexara-ng-components-table.mjs +686 -0
  33. package/fesm2022/apexara-ng-components-table.mjs.map +1 -0
  34. package/fesm2022/apexara-ng-components-textarea.mjs +82 -0
  35. package/fesm2022/apexara-ng-components-textarea.mjs.map +1 -0
  36. package/fesm2022/apexara-ng-components-tooltip.mjs +319 -0
  37. package/fesm2022/apexara-ng-components-tooltip.mjs.map +1 -0
  38. package/fesm2022/apexara-ng-components.mjs +8 -0
  39. package/fesm2022/apexara-ng-components.mjs.map +1 -0
  40. package/package.json +146 -0
  41. package/types/apexara-ng-components-button.d.ts +27 -0
  42. package/types/apexara-ng-components-carousel.d.ts +31 -0
  43. package/types/apexara-ng-components-checkbox.d.ts +29 -0
  44. package/types/apexara-ng-components-chip.d.ts +11 -0
  45. package/types/apexara-ng-components-datepicker.d.ts +172 -0
  46. package/types/apexara-ng-components-dialog.d.ts +24 -0
  47. package/types/apexara-ng-components-drawer.d.ts +22 -0
  48. package/types/apexara-ng-components-expander.d.ts +10 -0
  49. package/types/apexara-ng-components-image.d.ts +46 -0
  50. package/types/apexara-ng-components-input.d.ts +51 -0
  51. package/types/apexara-ng-components-listbox.d.ts +70 -0
  52. package/types/apexara-ng-components-radiobutton.d.ts +30 -0
  53. package/types/apexara-ng-components-select.d.ts +111 -0
  54. package/types/apexara-ng-components-services.d.ts +96 -0
  55. package/types/apexara-ng-components-snackbar.d.ts +73 -0
  56. package/types/apexara-ng-components-table.d.ts +157 -0
  57. package/types/apexara-ng-components-textarea.d.ts +34 -0
  58. package/types/apexara-ng-components-tooltip.d.ts +54 -0
  59. 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