@brickclay/calendar 0.0.6

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.

Potentially problematic release.


This version of @brickclay/calendar might be problematic. Click here for more details.

@@ -0,0 +1,2421 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, EventEmitter, HostListener, ViewChildren, Output, Input, Component, NgModule } from '@angular/core';
3
+ import * as i1 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import { FormsModule } from '@angular/forms';
6
+ import moment from 'moment';
7
+ import { Subject } from 'rxjs';
8
+
9
+ class CalendarService {
10
+ constructor() { }
11
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CalendarService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
12
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CalendarService, providedIn: 'root' });
13
+ }
14
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CalendarService, decorators: [{
15
+ type: Injectable,
16
+ args: [{
17
+ providedIn: 'root'
18
+ }]
19
+ }], ctorParameters: () => [] });
20
+
21
+ class TimePickerComponent {
22
+ value = '1:00 AM'; // Time in format "H:MM AM/PM"
23
+ label = 'Time';
24
+ placeholder = 'Select time';
25
+ position = 'left';
26
+ pickerId = ''; // Unique ID for this picker
27
+ closePicker = 0; // Close counter from parent (triggers close when changed)
28
+ timeFormat = 12; // Visual mode: 12h or 24h
29
+ showSeconds = false; // Whether to show/edit seconds
30
+ timeChange = new EventEmitter();
31
+ pickerOpened = new EventEmitter(); // Notify parent when opened
32
+ pickerClosed = new EventEmitter(); // Notify parent when closed
33
+ timeScrollElements;
34
+ showPicker = false;
35
+ currentHour = 1;
36
+ currentMinute = 0;
37
+ currentAMPM = 'AM';
38
+ currentSecond = 0;
39
+ ngOnInit() {
40
+ this.parseTimeValue();
41
+ }
42
+ ngAfterViewInit() {
43
+ if (this.showPicker) {
44
+ setTimeout(() => {
45
+ this.scrollToSelectedTimes();
46
+ }, 100);
47
+ }
48
+ }
49
+ parseTimeValue() {
50
+ const parsed = this.parseTimeStringToComponents(this.value);
51
+ this.currentHour = parsed.hour;
52
+ this.currentMinute = parsed.minute;
53
+ this.currentSecond = parsed.second;
54
+ this.currentAMPM = parsed.ampm;
55
+ }
56
+ getHours() {
57
+ // 12-hour: 1-12, 24-hour: 0-23
58
+ if (this.timeFormat === 24) {
59
+ return Array.from({ length: 24 }, (_, i) => i);
60
+ }
61
+ return Array.from({ length: 12 }, (_, i) => i + 1);
62
+ }
63
+ getMinutes() {
64
+ return Array.from({ length: 60 }, (_, i) => i);
65
+ }
66
+ getSeconds() {
67
+ return Array.from({ length: 60 }, (_, i) => i);
68
+ }
69
+ getAMPMOptions() {
70
+ return ['AM', 'PM'];
71
+ }
72
+ parseTimeStringToComponents(timeStr) {
73
+ // Supports:
74
+ // - "H:MM AM/PM"
75
+ // - "H:MM:SS AM/PM"
76
+ // - "HH:MM" (24h)
77
+ // - "HH:MM:SS" (24h)
78
+ if (!timeStr) {
79
+ return {
80
+ hour: this.timeFormat === 24 ? 0 : 12,
81
+ minute: 0,
82
+ second: 0,
83
+ ampm: this.timeFormat === 24 ? '' : 'AM'
84
+ };
85
+ }
86
+ const parts = timeStr.trim().split(' ');
87
+ const timePart = parts[0] || (this.timeFormat === 24 ? '00:00' : '12:00');
88
+ let ampm = (parts[1] || '').toUpperCase();
89
+ const [hoursStr, minutesStr, secondsStr] = timePart.split(':');
90
+ let hour = parseInt(hoursStr || (this.timeFormat === 24 ? '0' : '12'), 10);
91
+ const minute = parseInt(minutesStr || '0', 10);
92
+ const second = parseInt(secondsStr || '0', 10);
93
+ if (this.timeFormat === 24) {
94
+ // In 24-hour mode we ignore AM/PM and keep hour as 0-23
95
+ return {
96
+ hour: isNaN(hour) ? 0 : Math.min(Math.max(hour, 0), 23),
97
+ minute: isNaN(minute) ? 0 : Math.min(Math.max(minute, 0), 59),
98
+ second: isNaN(second) ? 0 : Math.min(Math.max(second, 0), 59),
99
+ ampm: ''
100
+ };
101
+ }
102
+ // 12-hour mode: normalize AM/PM and convert 24h inputs if needed
103
+ let ampmValue = ampm === 'PM' || ampm === 'AM' ? ampm : '';
104
+ if (!ampmValue) {
105
+ // No AM/PM provided -> interpret as 24-hour and convert to 12-hour with AM/PM
106
+ if (hour >= 12) {
107
+ ampmValue = 'PM';
108
+ if (hour > 12)
109
+ hour -= 12;
110
+ }
111
+ else {
112
+ ampmValue = 'AM';
113
+ if (hour === 0)
114
+ hour = 12;
115
+ }
116
+ }
117
+ // Clamp to 1-12 range
118
+ if (hour < 1)
119
+ hour = 1;
120
+ if (hour > 12)
121
+ hour = 12;
122
+ return {
123
+ hour,
124
+ minute: isNaN(minute) ? 0 : Math.min(Math.max(minute, 0), 59),
125
+ second: isNaN(second) ? 0 : Math.min(Math.max(second, 0), 59),
126
+ ampm: ampmValue
127
+ };
128
+ }
129
+ formatTimeFromComponents(hour, minute, second, ampm) {
130
+ const hStr = hour.toString().padStart(2, '0');
131
+ const minuteStr = minute.toString().padStart(2, '0');
132
+ const secondStr = second.toString().padStart(2, '0');
133
+ if (this.timeFormat === 24) {
134
+ // "HH:mm" or "HH:mm:ss"
135
+ return this.showSeconds
136
+ ? `${hStr}:${minuteStr}:${secondStr}`
137
+ : `${hStr}:${minuteStr}`;
138
+ }
139
+ // 12-hour: "H:MM" or "H:MM:SS" with AM/PM
140
+ const displayHour = hour; // already 1-12
141
+ return this.showSeconds
142
+ ? `${displayHour}:${minuteStr}:${secondStr} ${ampm}`
143
+ : `${displayHour}:${minuteStr} ${ampm}`;
144
+ }
145
+ togglePicker() {
146
+ if (!this.showPicker) {
147
+ this.showPicker = true;
148
+ this.parseTimeValue();
149
+ this.pickerOpened.emit(this.pickerId);
150
+ setTimeout(() => {
151
+ this.scrollToSelectedTimes();
152
+ }, 100);
153
+ }
154
+ else {
155
+ this.showPicker = false;
156
+ this.pickerClosed.emit(this.pickerId);
157
+ }
158
+ }
159
+ onHourChange(hour) {
160
+ this.currentHour = hour;
161
+ this.updateTime();
162
+ setTimeout(() => {
163
+ this.scrollToSelectedTimes();
164
+ }, 50);
165
+ }
166
+ onMinuteChange(minute) {
167
+ this.currentMinute = minute;
168
+ this.updateTime();
169
+ setTimeout(() => {
170
+ this.scrollToSelectedTimes();
171
+ }, 50);
172
+ }
173
+ onSecondChange(second) {
174
+ this.currentSecond = second;
175
+ this.updateTime();
176
+ setTimeout(() => {
177
+ this.scrollToSelectedTimes();
178
+ }, 50);
179
+ }
180
+ onAMPMChange(ampm) {
181
+ this.currentAMPM = ampm;
182
+ this.updateTime();
183
+ setTimeout(() => {
184
+ this.scrollToSelectedTimes();
185
+ }, 50);
186
+ }
187
+ updateTime() {
188
+ const newTime = this.formatTimeFromComponents(this.currentHour, this.currentMinute, this.currentSecond, this.currentAMPM);
189
+ this.value = newTime;
190
+ this.timeChange.emit(newTime);
191
+ }
192
+ scrollToSelectedTimes() {
193
+ this.timeScrollElements.forEach((elementRef) => {
194
+ const element = elementRef.nativeElement;
195
+ const selectedItem = element.querySelector('.time-item.selected');
196
+ if (selectedItem) {
197
+ const scrollTop = selectedItem.offsetTop - element.offsetHeight / 40 + selectedItem.offsetHeight / 40;
198
+ element.scrollTop = scrollTop;
199
+ }
200
+ });
201
+ }
202
+ onDocumentClick(event) {
203
+ const target = event.target;
204
+ if (!target.closest('.time-picker-wrapper') && this.showPicker) {
205
+ this.showPicker = false;
206
+ this.pickerClosed.emit(this.pickerId);
207
+ }
208
+ }
209
+ previousCloseCounter = 0;
210
+ ngOnChanges(changes) {
211
+ if (changes['value'] && changes['value'].currentValue) {
212
+ this.parseTimeValue();
213
+ }
214
+ if (changes['closePicker'] && this.showPicker) {
215
+ const newCounter = changes['closePicker'].currentValue;
216
+ // If counter increased, close the picker
217
+ if (newCounter > this.previousCloseCounter) {
218
+ this.showPicker = false;
219
+ this.pickerClosed.emit(this.pickerId);
220
+ this.previousCloseCounter = newCounter;
221
+ }
222
+ }
223
+ }
224
+ // Basic keyboard support on the input (combobox behavior)
225
+ onInputKeydown(event) {
226
+ const key = event.key;
227
+ if (key === 'Enter' || key === ' ') {
228
+ event.preventDefault();
229
+ this.togglePicker();
230
+ return;
231
+ }
232
+ if (key === 'Escape' && this.showPicker) {
233
+ this.showPicker = false;
234
+ this.pickerClosed.emit(this.pickerId);
235
+ return;
236
+ }
237
+ // Simple hour increment/decrement when closed
238
+ if (!this.showPicker && (key === 'ArrowUp' || key === 'ArrowDown')) {
239
+ event.preventDefault();
240
+ if (this.timeFormat === 24) {
241
+ if (key === 'ArrowUp') {
242
+ this.currentHour = (this.currentHour + 1) % 24;
243
+ }
244
+ else {
245
+ this.currentHour = this.currentHour <= 0 ? 23 : this.currentHour - 1;
246
+ }
247
+ }
248
+ else {
249
+ if (key === 'ArrowUp') {
250
+ this.currentHour = this.currentHour >= 12 ? 1 : this.currentHour + 1;
251
+ }
252
+ else {
253
+ this.currentHour = this.currentHour <= 1 ? 12 : this.currentHour - 1;
254
+ }
255
+ }
256
+ this.updateTime();
257
+ }
258
+ }
259
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: TimePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
260
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.16", type: TimePickerComponent, isStandalone: true, selector: "app-time-picker", inputs: { value: "value", label: "label", placeholder: "placeholder", position: "position", pickerId: "pickerId", closePicker: "closePicker", timeFormat: "timeFormat", showSeconds: "showSeconds" }, outputs: { timeChange: "timeChange", pickerOpened: "pickerOpened", pickerClosed: "pickerClosed" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "timeScrollElements", predicate: ["timeScroll"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"time-picker-wrapper\">\r\n <div class=\"time-input-group\">\r\n <label *ngIf=\"label\">{{ label }}</label>\r\n <div class=\"time-input-wrapper\">\r\n <input\r\n type=\"text\"\r\n class=\"time-input\"\r\n [value]=\"value\"\r\n [placeholder]=\"placeholder\"\r\n readonly\r\n (click)=\"togglePicker()\">\r\n <span class=\"time-icon\">\r\n <img src=\"assets/calender/timer.svg\" alt=\"timer\" class=\"timer-icon\">\r\n </span>\r\n <div class=\"custom-time-picker-dropdown\" *ngIf=\"showPicker\">\r\n <div class=\"custom-time-picker\" [ngClass]=\"position === 'left' ? 'left-position' : 'right-position'\">\r\n <!-- Hours Column -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #timeScroll>\r\n <div\r\n *ngFor=\"let h of getHours()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentHour === h\"\r\n (click)=\"onHourChange(h)\">\r\n {{ h.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n <span class=\"time-separator\">:</span>\r\n <!-- Minutes Column -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #timeScroll>\r\n <div\r\n *ngFor=\"let m of getMinutes()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentMinute === m\"\r\n (click)=\"onMinuteChange(m)\">\r\n {{ m.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n <span class=\"time-separator\">:</span>\r\n <!-- AM/PM Column -->\r\n <div class=\"time-column ampm-column\">\r\n <div class=\"time-scroll\" #timeScroll>\r\n <div\r\n *ngFor=\"let ap of getAMPMOptions()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentAMPM === ap\"\r\n (click)=\"onAMPMChange(ap)\">\r\n {{ ap }}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n", styles: [".time-picker-wrapper{width:100%;font-family:Inter,sans-serif}.time-input-group{display:flex;flex-direction:column;gap:4px}.time-input-group label{font-size:11px;font-weight:500;color:#15191e;text-transform:uppercase;letter-spacing:-.28px}.time-input-wrapper{position:relative;display:flex;align-items:center}.time-input{padding:8px 40px 8px 12px;border:1px solid #d1d5db;border-radius:4px;font-size:12px;font-family:Inter,sans-serif;color:#6f737b;background:#fff;transition:all .2s;width:100%;box-sizing:border-box;cursor:pointer}.time-input:focus{outline:none;border-color:#111827;box-shadow:0 0 0 3px #1118271a}.time-input:hover{border-color:#9ca3af}.time-icon{position:absolute;right:12px;font-size:16px;pointer-events:none;color:#9ca3af;cursor:pointer}.custom-time-picker-wrapper{width:100%;display:flex;justify-content:center}.custom-time-picker{display:flex;gap:8px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:10px;box-shadow:0 4px 12px #00000026;width:172px;position:absolute;top:calc(100% + 4px);z-index:1000}.custom-time-picker.left-position{left:0}.custom-time-picker.right-position{right:0}.time-column{display:flex;flex-direction:column;position:relative}.time-scroll{display:flex;flex-direction:column;max-height:95px;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:#cbd5e1 transparent;scrollbar-width:none;-ms-overflow-style:none}.time-scroll::-webkit-scrollbar{display:none}.time-scroll::-webkit-scrollbar-track{background:transparent}.time-scroll::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:2px}.time-scroll::-webkit-scrollbar-thumb:hover{background:#94a3b8}.time-item{min-width:40px;width:40px;height:32px;min-height:32px;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:400;color:#374151;cursor:pointer;border-radius:4px;transition:all .15s ease;-webkit-user-select:none;user-select:none;font-family:Inter,sans-serif}.time-item:hover{background:#f3f4f6}.time-item.selected{background:#111827;color:#fff;font-weight:500;box-shadow:0 1px 2px #2563eb4d}.ampm-column .time-item{min-width:40px;width:40px}.time-separator{font-size:16px;font-weight:600;color:#6b7280;margin:5px 0 0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
261
+ }
262
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: TimePickerComponent, decorators: [{
263
+ type: Component,
264
+ args: [{ selector: 'app-time-picker', standalone: true, imports: [CommonModule], template: "<div class=\"time-picker-wrapper\">\r\n <div class=\"time-input-group\">\r\n <label *ngIf=\"label\">{{ label }}</label>\r\n <div class=\"time-input-wrapper\">\r\n <input\r\n type=\"text\"\r\n class=\"time-input\"\r\n [value]=\"value\"\r\n [placeholder]=\"placeholder\"\r\n readonly\r\n (click)=\"togglePicker()\">\r\n <span class=\"time-icon\">\r\n <img src=\"assets/calender/timer.svg\" alt=\"timer\" class=\"timer-icon\">\r\n </span>\r\n <div class=\"custom-time-picker-dropdown\" *ngIf=\"showPicker\">\r\n <div class=\"custom-time-picker\" [ngClass]=\"position === 'left' ? 'left-position' : 'right-position'\">\r\n <!-- Hours Column -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #timeScroll>\r\n <div\r\n *ngFor=\"let h of getHours()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentHour === h\"\r\n (click)=\"onHourChange(h)\">\r\n {{ h.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n <span class=\"time-separator\">:</span>\r\n <!-- Minutes Column -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroll\" #timeScroll>\r\n <div\r\n *ngFor=\"let m of getMinutes()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentMinute === m\"\r\n (click)=\"onMinuteChange(m)\">\r\n {{ m.toString().padStart(2, '0') }}\r\n </div>\r\n </div>\r\n </div>\r\n <span class=\"time-separator\">:</span>\r\n <!-- AM/PM Column -->\r\n <div class=\"time-column ampm-column\">\r\n <div class=\"time-scroll\" #timeScroll>\r\n <div\r\n *ngFor=\"let ap of getAMPMOptions()\"\r\n class=\"time-item\"\r\n [class.selected]=\"currentAMPM === ap\"\r\n (click)=\"onAMPMChange(ap)\">\r\n {{ ap }}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n", styles: [".time-picker-wrapper{width:100%;font-family:Inter,sans-serif}.time-input-group{display:flex;flex-direction:column;gap:4px}.time-input-group label{font-size:11px;font-weight:500;color:#15191e;text-transform:uppercase;letter-spacing:-.28px}.time-input-wrapper{position:relative;display:flex;align-items:center}.time-input{padding:8px 40px 8px 12px;border:1px solid #d1d5db;border-radius:4px;font-size:12px;font-family:Inter,sans-serif;color:#6f737b;background:#fff;transition:all .2s;width:100%;box-sizing:border-box;cursor:pointer}.time-input:focus{outline:none;border-color:#111827;box-shadow:0 0 0 3px #1118271a}.time-input:hover{border-color:#9ca3af}.time-icon{position:absolute;right:12px;font-size:16px;pointer-events:none;color:#9ca3af;cursor:pointer}.custom-time-picker-wrapper{width:100%;display:flex;justify-content:center}.custom-time-picker{display:flex;gap:8px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:10px;box-shadow:0 4px 12px #00000026;width:172px;position:absolute;top:calc(100% + 4px);z-index:1000}.custom-time-picker.left-position{left:0}.custom-time-picker.right-position{right:0}.time-column{display:flex;flex-direction:column;position:relative}.time-scroll{display:flex;flex-direction:column;max-height:95px;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:#cbd5e1 transparent;scrollbar-width:none;-ms-overflow-style:none}.time-scroll::-webkit-scrollbar{display:none}.time-scroll::-webkit-scrollbar-track{background:transparent}.time-scroll::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:2px}.time-scroll::-webkit-scrollbar-thumb:hover{background:#94a3b8}.time-item{min-width:40px;width:40px;height:32px;min-height:32px;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:400;color:#374151;cursor:pointer;border-radius:4px;transition:all .15s ease;-webkit-user-select:none;user-select:none;font-family:Inter,sans-serif}.time-item:hover{background:#f3f4f6}.time-item.selected{background:#111827;color:#fff;font-weight:500;box-shadow:0 1px 2px #2563eb4d}.ampm-column .time-item{min-width:40px;width:40px}.time-separator{font-size:16px;font-weight:600;color:#6b7280;margin:5px 0 0}\n"] }]
265
+ }], propDecorators: { value: [{
266
+ type: Input
267
+ }], label: [{
268
+ type: Input
269
+ }], placeholder: [{
270
+ type: Input
271
+ }], position: [{
272
+ type: Input
273
+ }], pickerId: [{
274
+ type: Input
275
+ }], closePicker: [{
276
+ type: Input
277
+ }], timeFormat: [{
278
+ type: Input
279
+ }], showSeconds: [{
280
+ type: Input
281
+ }], timeChange: [{
282
+ type: Output
283
+ }], pickerOpened: [{
284
+ type: Output
285
+ }], pickerClosed: [{
286
+ type: Output
287
+ }], timeScrollElements: [{
288
+ type: ViewChildren,
289
+ args: ['timeScroll']
290
+ }], onDocumentClick: [{
291
+ type: HostListener,
292
+ args: ['document:click', ['$event']]
293
+ }] } });
294
+
295
+ class CalendarManagerService {
296
+ calendarInstances = new Set();
297
+ closeAllSubject = new Subject();
298
+ closeAll$ = this.closeAllSubject.asObservable();
299
+ /**
300
+ * Register a calendar instance with its close function
301
+ */
302
+ register(closeFn) {
303
+ this.calendarInstances.add(closeFn);
304
+ // Return unregister function
305
+ return () => {
306
+ this.calendarInstances.delete(closeFn);
307
+ };
308
+ }
309
+ /**
310
+ * Close all calendars except the one being opened
311
+ */
312
+ closeAllExcept(exceptCloseFn) {
313
+ this.calendarInstances.forEach(closeFn => {
314
+ if (closeFn !== exceptCloseFn) {
315
+ closeFn();
316
+ }
317
+ });
318
+ }
319
+ /**
320
+ * Close all calendars
321
+ */
322
+ closeAll() {
323
+ this.closeAllSubject.next();
324
+ this.calendarInstances.forEach(closeFn => closeFn());
325
+ }
326
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CalendarManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
327
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CalendarManagerService, providedIn: 'root' });
328
+ }
329
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CalendarManagerService, decorators: [{
330
+ type: Injectable,
331
+ args: [{
332
+ providedIn: 'root'
333
+ }]
334
+ }] });
335
+
336
+ class CustomCalendarComponent {
337
+ calendarManager;
338
+ // Basic Options
339
+ enableTimepicker = false;
340
+ autoApply = false;
341
+ closeOnAutoApply = false;
342
+ showCancel = true;
343
+ linkedCalendars = false;
344
+ singleDatePicker = false;
345
+ showWeekNumbers = false;
346
+ showISOWeekNumbers = false;
347
+ customRangeDirection = false;
348
+ lockStartDate = false;
349
+ position = 'left';
350
+ drop = 'down';
351
+ dualCalendar = false;
352
+ showRanges = true;
353
+ timeFormat = 24;
354
+ enableSeconds = false;
355
+ customRanges;
356
+ multiDateSelection = false; // NEW: Enable multi-date selection
357
+ maxDate; // NEW: Maximum selectable date
358
+ minDate; // NEW: Minimum selectable date
359
+ placeholder = 'Select date range'; // NEW: Custom placeholder
360
+ opens = 'left'; // NEW: Popup position
361
+ inline = false; // NEW: Always show calendar inline (no popup)
362
+ isDisplayCrossIcon = true; // NEW: Show/Hide clear (X) icon
363
+ selected = new EventEmitter();
364
+ opened = new EventEmitter();
365
+ closed = new EventEmitter();
366
+ /**
367
+ * External value passed from parent. If provided, component will select these dates on init / change.
368
+ * Accepts { startDate: Date|null, endDate: Date|null, selectedDates?: Date[] }
369
+ */
370
+ selectedValue = null;
371
+ /** Optional display format for the input value. Uses moment formatting tokens. */
372
+ displayFormat = 'MM/DD/YYYY';
373
+ show = false;
374
+ today = new Date();
375
+ month = this.today.getMonth();
376
+ year = this.today.getFullYear();
377
+ calendar = [];
378
+ leftMonth;
379
+ leftYear;
380
+ rightMonth;
381
+ rightYear;
382
+ leftCalendar = [];
383
+ rightCalendar = [];
384
+ startDate = null;
385
+ endDate = null;
386
+ selectedDates = []; // NEW: For multi-date selection
387
+ disableHighlight = false;
388
+ hoveredDate = null; // For hover preview
389
+ // Track raw input values for minutes to allow free typing
390
+ minuteInputValues = {};
391
+ // Time picker for single calendar (12-hour format: 1-12)
392
+ selectedHour = 1;
393
+ selectedMinute = 0;
394
+ selectedSecond = 0;
395
+ selectedAMPM = 'AM';
396
+ // NEW: Separate time pickers for dual calendar (12-hour format: 1-12)
397
+ startHour = 1;
398
+ startMinute = 0;
399
+ startSecond = 0;
400
+ startAMPM = 'AM';
401
+ endHour = 2;
402
+ endMinute = 0;
403
+ endSecond = 0;
404
+ endAMPM = 'AM';
405
+ // Track open time-picker within this calendar (for single-open behavior)
406
+ openTimePickerId = null;
407
+ closePickerCounter = {};
408
+ defaultRanges = {};
409
+ activeRange = null; // Track which range is currently active
410
+ rangeOrder = []; // Maintain order of ranges
411
+ unregisterFn;
412
+ closeAllSubscription;
413
+ closeFn;
414
+ constructor(calendarManager) {
415
+ this.calendarManager = calendarManager;
416
+ }
417
+ onClickOutside(event) {
418
+ // Don't handle click outside if inline mode is enabled
419
+ if (this.inline) {
420
+ return;
421
+ }
422
+ const target = event.target;
423
+ if (this.show && !target.closest('.calendar-container')) {
424
+ this.close();
425
+ }
426
+ }
427
+ ngOnInit() {
428
+ if (!this.customRanges) {
429
+ this.initializeDefaultRanges();
430
+ }
431
+ else {
432
+ // If customRanges is provided via @Input, set the order based on the keys
433
+ // Maintain the desired order if keys match, otherwise use provided order
434
+ const desiredOrder = ['Today', 'Yesterday', 'Last 7 Days', 'Last 30 Days', 'This Month', 'Last Month', 'Custom Range'];
435
+ const providedKeys = Object.keys(this.customRanges);
436
+ // Check if Custom Range exists, if not add it
437
+ if (!this.customRanges['Custom Range']) {
438
+ this.customRanges['Custom Range'] = { start: new Date(), end: new Date() };
439
+ }
440
+ // Build order: first add desired order items that exist, then add any remaining
441
+ this.rangeOrder = desiredOrder.filter(key => providedKeys.includes(key) || key === 'Custom Range');
442
+ const remaining = providedKeys.filter(key => !this.rangeOrder.includes(key));
443
+ this.rangeOrder = [...this.rangeOrder, ...remaining];
444
+ }
445
+ if (this.dualCalendar)
446
+ this.initializeDual();
447
+ else
448
+ this.generateCalendar();
449
+ // Initialize time from existing dates if available
450
+ if (this.startDate) {
451
+ this.initializeTimeFromDate(this.startDate, true);
452
+ }
453
+ if (this.endDate) {
454
+ this.initializeTimeFromDate(this.endDate, false);
455
+ }
456
+ // Check if current dates match any predefined range
457
+ if (this.startDate && this.endDate) {
458
+ this.checkAndSetActiveRange();
459
+ }
460
+ // If inline mode, always show calendar
461
+ if (this.inline) {
462
+ this.show = true;
463
+ }
464
+ // Register this calendar instance with the manager service
465
+ this.closeFn = () => {
466
+ if (this.show && !this.inline) {
467
+ this.close();
468
+ }
469
+ };
470
+ this.unregisterFn = this.calendarManager.register(this.closeFn);
471
+ // Subscribe to close all events (skip if inline)
472
+ this.closeAllSubscription = this.calendarManager.closeAll$.subscribe(() => {
473
+ if (this.show && !this.inline) {
474
+ this.close();
475
+ }
476
+ });
477
+ }
478
+ ngOnChanges(changes) {
479
+ if (changes['selectedValue'] && this.selectedValue) {
480
+ // Normalize incoming values to Date or null
481
+ const s = this.selectedValue;
482
+ this.startDate = s.startDate ? new Date(s.startDate) : null;
483
+ this.endDate = s.endDate ? new Date(s.endDate) : null;
484
+ this.selectedDates = (s.selectedDates || []).map((d) => new Date(d));
485
+ // Update calendar month/year to show the start date (or end date if start missing)
486
+ const focusDate = this.startDate ?? this.endDate ?? new Date();
487
+ this.month = focusDate.getMonth();
488
+ this.year = focusDate.getFullYear();
489
+ if (this.dualCalendar) {
490
+ this.initializeDual();
491
+ }
492
+ else {
493
+ this.generateCalendar();
494
+ }
495
+ // Re-evaluate active range if any
496
+ this.checkAndSetActiveRange();
497
+ }
498
+ }
499
+ ngOnDestroy() {
500
+ // Unregister this calendar instance
501
+ if (this.unregisterFn) {
502
+ this.unregisterFn();
503
+ }
504
+ // Unsubscribe from close all events
505
+ if (this.closeAllSubscription) {
506
+ this.closeAllSubscription.unsubscribe();
507
+ }
508
+ }
509
+ checkAndSetActiveRange() {
510
+ if (!this.customRanges || !this.startDate || !this.endDate)
511
+ return;
512
+ // Normalize dates for comparison (ignore time)
513
+ const normalizeDate = (date) => {
514
+ const d = new Date(date);
515
+ d.setHours(0, 0, 0, 0);
516
+ return d;
517
+ };
518
+ const start = normalizeDate(this.startDate);
519
+ const end = normalizeDate(this.endDate);
520
+ // Check each range (except Custom Range)
521
+ for (const key of this.rangeOrder) {
522
+ if (key === 'Custom Range')
523
+ continue;
524
+ const range = this.customRanges[key];
525
+ if (range) {
526
+ const rangeStart = normalizeDate(range.start);
527
+ const rangeEnd = normalizeDate(range.end);
528
+ if (start.getTime() === rangeStart.getTime() && end.getTime() === rangeEnd.getTime()) {
529
+ this.activeRange = key;
530
+ return;
531
+ }
532
+ }
533
+ }
534
+ // If no match found, it's a custom range
535
+ this.activeRange = 'Custom Range';
536
+ }
537
+ initializeDefaultRanges() {
538
+ const today = new Date();
539
+ this.customRanges = {
540
+ 'Today': { start: new Date(today.getFullYear(), today.getMonth(), today.getDate()), end: new Date(today.getFullYear(), today.getMonth(), today.getDate()) },
541
+ 'Yesterday': { start: this.addDays(today, -1), end: this.addDays(today, -1) },
542
+ 'Last 7 Days': { start: this.addDays(today, -6), end: today },
543
+ 'Last 30 Days': { start: this.addDays(today, -29), end: today },
544
+ 'This Month': { start: new Date(today.getFullYear(), today.getMonth(), 1), end: today },
545
+ 'Last Month': { start: new Date(today.getFullYear(), today.getMonth() - 1, 1), end: new Date(today.getFullYear(), today.getMonth(), 0) },
546
+ 'Custom Range': { start: new Date(), end: new Date() }, // Placeholder, won't be used for selection
547
+ };
548
+ // Set the order of ranges
549
+ this.rangeOrder = ['Today', 'Yesterday', 'Last 7 Days', 'Last 30 Days', 'This Month', 'Last Month', 'Custom Range'];
550
+ }
551
+ initializeTimeFromDate(date, isStart) {
552
+ // Always use 12-hour format
553
+ const hours24 = date.getHours();
554
+ const minutes = date.getMinutes();
555
+ const seconds = date.getSeconds();
556
+ if (isStart) {
557
+ this.startMinute = minutes;
558
+ this.startSecond = seconds;
559
+ if (hours24 >= 12) {
560
+ this.startAMPM = 'PM';
561
+ this.startHour = hours24 > 12 ? hours24 - 12 : 12;
562
+ }
563
+ else {
564
+ this.startAMPM = 'AM';
565
+ this.startHour = hours24 === 0 ? 12 : hours24;
566
+ }
567
+ }
568
+ else {
569
+ this.endMinute = minutes;
570
+ this.endSecond = seconds;
571
+ if (hours24 >= 12) {
572
+ this.endAMPM = 'PM';
573
+ this.endHour = hours24 > 12 ? hours24 - 12 : 12;
574
+ }
575
+ else {
576
+ this.endAMPM = 'AM';
577
+ this.endHour = hours24 === 0 ? 12 : hours24;
578
+ }
579
+ }
580
+ }
581
+ toggle() {
582
+ // Don't toggle if inline mode is enabled
583
+ if (this.inline) {
584
+ return;
585
+ }
586
+ const wasOpen = this.show;
587
+ this.show = !this.show;
588
+ if (this.show) {
589
+ // If opening, close all other calendars first
590
+ if (!wasOpen && this.closeFn) {
591
+ this.calendarManager.closeAllExcept(this.closeFn);
592
+ }
593
+ this.disableHighlight = false;
594
+ this.opened.emit();
595
+ }
596
+ else {
597
+ this.closed.emit();
598
+ }
599
+ }
600
+ close() {
601
+ // Don't close if inline mode is enabled
602
+ if (this.inline) {
603
+ return;
604
+ }
605
+ this.show = false;
606
+ this.closed.emit();
607
+ }
608
+ onDateHover(day, fromRight = false) {
609
+ if (!day || this.singleDatePicker || this.multiDateSelection) {
610
+ this.hoveredDate = null;
611
+ return;
612
+ }
613
+ // Only show hover preview if start date is selected but end date is not
614
+ if (!this.startDate || this.endDate) {
615
+ this.hoveredDate = null;
616
+ return;
617
+ }
618
+ if (!this.dualCalendar) {
619
+ this.hoveredDate = new Date(this.year, this.month, day);
620
+ }
621
+ else {
622
+ this.hoveredDate = fromRight
623
+ ? new Date(this.rightYear, this.rightMonth, day)
624
+ : new Date(this.leftYear, this.leftMonth, day);
625
+ }
626
+ }
627
+ onDateLeave() {
628
+ this.hoveredDate = null;
629
+ }
630
+ selectDate(day, fromRight = false) {
631
+ if (!day)
632
+ return;
633
+ let selected;
634
+ if (!this.dualCalendar) {
635
+ selected = new Date(this.year, this.month, day);
636
+ }
637
+ else {
638
+ selected = fromRight
639
+ ? new Date(this.rightYear, this.rightMonth, day)
640
+ : new Date(this.leftYear, this.leftMonth, day);
641
+ }
642
+ // Clear hover on selection
643
+ this.hoveredDate = null;
644
+ // Check min/max date constraints
645
+ if (this.minDate && selected < this.minDate)
646
+ return;
647
+ if (this.maxDate && selected > this.maxDate)
648
+ return;
649
+ // Multi-date selection mode
650
+ if (this.multiDateSelection) {
651
+ this.handleMultiDateSelection(selected);
652
+ return;
653
+ }
654
+ // Apply time if timepicker is enabled (convert 12-hour to 24-hour)
655
+ if (this.enableTimepicker) {
656
+ if (this.dualCalendar) {
657
+ // For dual calendar, use separate start/end times
658
+ // If no startDate OR endDate exists, we're selecting start date
659
+ const isStart = !this.startDate || !!this.endDate;
660
+ this.applyTimeToDate(selected, isStart);
661
+ }
662
+ else {
663
+ // For single calendar, always use selected time for start
664
+ this.applyTimeToDate(selected, true);
665
+ }
666
+ }
667
+ // Single date picker mode
668
+ if (this.singleDatePicker) {
669
+ this.startDate = selected;
670
+ this.endDate = null;
671
+ // Activate Custom Range when manually selecting dates
672
+ this.activeRange = 'Custom Range';
673
+ // Apply time immediately if timepicker is enabled
674
+ if (this.enableTimepicker) {
675
+ this.applyTimeToDate(this.startDate, true);
676
+ }
677
+ if (this.autoApply) {
678
+ this.apply();
679
+ if (this.closeOnAutoApply && !this.inline)
680
+ this.close();
681
+ }
682
+ else {
683
+ // Always emit selection event even if autoApply is false (especially for inline calendars)
684
+ this.emitSelection();
685
+ }
686
+ return;
687
+ }
688
+ // Range selection mode
689
+ if (!this.startDate || this.endDate) {
690
+ this.startDate = selected;
691
+ this.endDate = null;
692
+ // Activate Custom Range when manually selecting dates
693
+ this.activeRange = 'Custom Range';
694
+ // Keep left calendar on the selected month for better UX
695
+ if (this.dualCalendar) {
696
+ this.leftMonth = selected.getMonth();
697
+ this.leftYear = selected.getFullYear();
698
+ // Reset right calendar to original position (next month after left) when end date is cleared
699
+ this.rightMonth = this.leftMonth + 1;
700
+ this.rightYear = this.leftYear;
701
+ if (this.rightMonth > 11) {
702
+ this.rightMonth = 0;
703
+ this.rightYear++;
704
+ }
705
+ this.generateDualCalendars();
706
+ }
707
+ // Don't overwrite time picker values - keep current values and apply them to the date
708
+ // Time picker values are already set by user, we just apply them to the selected date
709
+ }
710
+ else {
711
+ if (selected < this.startDate && !this.customRangeDirection) {
712
+ this.endDate = this.startDate;
713
+ this.startDate = selected;
714
+ // Activate Custom Range when manually selecting dates
715
+ this.activeRange = 'Custom Range';
716
+ // Swap times if needed
717
+ if (this.dualCalendar && this.enableTimepicker) {
718
+ [this.startHour, this.endHour] = [this.endHour, this.startHour];
719
+ [this.startMinute, this.endMinute] = [this.endMinute, this.startMinute];
720
+ [this.startSecond, this.endSecond] = [this.endSecond, this.startSecond];
721
+ [this.startAMPM, this.endAMPM] = [this.endAMPM, this.startAMPM];
722
+ }
723
+ // Keep left calendar on the selected month
724
+ if (this.dualCalendar) {
725
+ this.leftMonth = selected.getMonth();
726
+ this.leftYear = selected.getFullYear();
727
+ this.leftCalendar = this.buildCalendar(this.leftYear, this.leftMonth);
728
+ }
729
+ }
730
+ else {
731
+ this.endDate = selected;
732
+ // Activate Custom Range when manually selecting dates
733
+ this.activeRange = 'Custom Range';
734
+ // Only move right calendar if end date is in a different month than start date
735
+ if (this.dualCalendar) {
736
+ if (this.startDate) {
737
+ const startMonth = this.startDate.getMonth();
738
+ const startYear = this.startDate.getFullYear();
739
+ const endMonth = selected.getMonth();
740
+ const endYear = selected.getFullYear();
741
+ // Only move right calendar if end date is in a different month
742
+ if (endMonth !== startMonth || endYear !== startYear) {
743
+ this.rightMonth = endMonth;
744
+ this.rightYear = endYear;
745
+ this.rightCalendar = this.buildCalendar(this.rightYear, this.rightMonth);
746
+ }
747
+ // If both dates are in same month, keep right calendar in its current position
748
+ }
749
+ else {
750
+ // If no start date, move right calendar to end date month
751
+ this.rightMonth = selected.getMonth();
752
+ this.rightYear = selected.getFullYear();
753
+ this.rightCalendar = this.buildCalendar(this.rightYear, this.rightMonth);
754
+ }
755
+ }
756
+ // Don't overwrite time picker values - keep current end time values
757
+ // Time picker values are already set by user
758
+ }
759
+ if (this.autoApply) {
760
+ this.apply();
761
+ if (this.closeOnAutoApply && !this.inline)
762
+ this.close();
763
+ }
764
+ else {
765
+ // Check if the selection matches a predefined range
766
+ this.checkAndSetActiveRange();
767
+ // Always emit selection event for inline calendars
768
+ if (this.inline) {
769
+ this.emitSelection();
770
+ }
771
+ }
772
+ }
773
+ }
774
+ handleMultiDateSelection(selected) {
775
+ const dateStr = this.getDateString(selected);
776
+ const existingIndex = this.selectedDates.findIndex(d => this.getDateString(d) === dateStr);
777
+ if (existingIndex >= 0) {
778
+ // Deselect if already selected
779
+ this.selectedDates.splice(existingIndex, 1);
780
+ }
781
+ else {
782
+ // Add to selection
783
+ this.selectedDates.push(new Date(selected));
784
+ this.selectedDates.sort((a, b) => a.getTime() - b.getTime());
785
+ }
786
+ // Update startDate and endDate for compatibility
787
+ if (this.selectedDates.length > 0) {
788
+ this.startDate = new Date(this.selectedDates[0]);
789
+ this.endDate = new Date(this.selectedDates[this.selectedDates.length - 1]);
790
+ // Activate Custom Range when manually selecting dates
791
+ this.activeRange = 'Custom Range';
792
+ }
793
+ else {
794
+ this.startDate = null;
795
+ this.endDate = null;
796
+ this.activeRange = null;
797
+ }
798
+ // Always emit selection event for inline calendars or when autoApply is true
799
+ if (this.autoApply || this.inline) {
800
+ this.emitSelection();
801
+ if (this.closeOnAutoApply && !this.inline)
802
+ this.close();
803
+ }
804
+ }
805
+ getDateString(date) {
806
+ return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
807
+ }
808
+ isDateInMultiSelection(year, month, day) {
809
+ if (!this.multiDateSelection || this.selectedDates.length === 0)
810
+ return false;
811
+ const cellDate = new Date(year, month, day);
812
+ return this.selectedDates.some(d => this.getDateString(d) === this.getDateString(cellDate));
813
+ }
814
+ apply() {
815
+ // Format minute inputs to 2 digits before applying
816
+ this.formatAllMinuteInputs();
817
+ // Apply time to dates
818
+ if (this.enableTimepicker) {
819
+ if (this.dualCalendar) {
820
+ // Dual calendar with separate start/end times (always 12-hour format)
821
+ if (this.startDate) {
822
+ this.applyTimeToDate(this.startDate, true);
823
+ }
824
+ if (this.endDate) {
825
+ this.applyTimeToDate(this.endDate, false);
826
+ }
827
+ }
828
+ else {
829
+ // Single calendar with time (always 12-hour format)
830
+ if (this.startDate) {
831
+ this.applyTimeToDate(this.startDate, true);
832
+ }
833
+ if (this.endDate && !this.singleDatePicker) {
834
+ this.applyTimeToDate(this.endDate, true);
835
+ }
836
+ }
837
+ }
838
+ // Check if the selection matches a predefined range
839
+ this.checkAndSetActiveRange();
840
+ this.emitSelection();
841
+ this.disableHighlight = true;
842
+ this.close();
843
+ }
844
+ cancel() {
845
+ this.startDate = null;
846
+ this.endDate = null;
847
+ this.selectedDates = [];
848
+ this.close();
849
+ }
850
+ clear() {
851
+ this.startDate = null;
852
+ this.endDate = null;
853
+ this.selectedDates = [];
854
+ this.activeRange = null; // Clear active range
855
+ // Reset right calendar to original position (next month after left) when end date is cleared
856
+ if (this.dualCalendar && !this.endDate) {
857
+ this.rightMonth = this.leftMonth + 1;
858
+ this.rightYear = this.leftYear;
859
+ if (this.rightMonth > 11) {
860
+ this.rightMonth = 0;
861
+ this.rightYear++;
862
+ }
863
+ this.generateDualCalendars();
864
+ }
865
+ this.emitSelection();
866
+ }
867
+ chooseRange(key) {
868
+ if (!this.customRanges)
869
+ return;
870
+ // Don't allow selecting "Custom Range" directly - it's only activated when manually selecting dates
871
+ if (key === 'Custom Range')
872
+ return;
873
+ const r = this.customRanges[key];
874
+ if (!r)
875
+ return;
876
+ this.startDate = new Date(r.start);
877
+ this.endDate = new Date(r.end);
878
+ this.selectedDates = [];
879
+ this.activeRange = key; // Set active range
880
+ // Navigate calendars to show the selected date range
881
+ if (this.dualCalendar) {
882
+ // For dual calendar: left always shows start date month
883
+ if (this.startDate) {
884
+ this.leftMonth = this.startDate.getMonth();
885
+ this.leftYear = this.startDate.getFullYear();
886
+ }
887
+ // Right calendar logic
888
+ if (this.endDate && this.startDate) {
889
+ const startMonth = this.startDate.getMonth();
890
+ const startYear = this.startDate.getFullYear();
891
+ const endMonth = this.endDate.getMonth();
892
+ const endYear = this.endDate.getFullYear();
893
+ // Only move right calendar if end date is in a different month than start date
894
+ if (endMonth !== startMonth || endYear !== startYear) {
895
+ this.rightMonth = endMonth;
896
+ this.rightYear = endYear;
897
+ }
898
+ else {
899
+ // If both dates are in same month, reset right calendar to default position (next month after left)
900
+ this.rightMonth = this.leftMonth + 1;
901
+ this.rightYear = this.leftYear;
902
+ if (this.rightMonth > 11) {
903
+ this.rightMonth = 0;
904
+ this.rightYear++;
905
+ }
906
+ }
907
+ }
908
+ else if (this.endDate && !this.startDate) {
909
+ // If only end date exists, show it in right calendar
910
+ this.rightMonth = this.endDate.getMonth();
911
+ this.rightYear = this.endDate.getFullYear();
912
+ }
913
+ else {
914
+ // If no end date, reset right calendar to default position
915
+ this.rightMonth = this.leftMonth + 1;
916
+ this.rightYear = this.leftYear;
917
+ if (this.rightMonth > 11) {
918
+ this.rightMonth = 0;
919
+ this.rightYear++;
920
+ }
921
+ }
922
+ this.generateDualCalendars();
923
+ }
924
+ else {
925
+ // For single calendar: show the start date month (or end date if only end date exists)
926
+ if (this.startDate) {
927
+ this.month = this.startDate.getMonth();
928
+ this.year = this.startDate.getFullYear();
929
+ }
930
+ else if (this.endDate) {
931
+ this.month = this.endDate.getMonth();
932
+ this.year = this.endDate.getFullYear();
933
+ }
934
+ this.generateCalendar();
935
+ }
936
+ this.emitSelection();
937
+ if (this.autoApply || this.closeOnAutoApply) {
938
+ this.close();
939
+ }
940
+ }
941
+ emitSelection() {
942
+ const selection = {
943
+ startDate: this.startDate,
944
+ endDate: this.endDate
945
+ };
946
+ if (this.multiDateSelection) {
947
+ selection.selectedDates = [...this.selectedDates];
948
+ }
949
+ this.selected.emit(selection);
950
+ }
951
+ addDays(date, days) {
952
+ const d = new Date(date);
953
+ d.setDate(d.getDate() + days);
954
+ return d;
955
+ }
956
+ generateCalendar() {
957
+ this.calendar = this.buildCalendar(this.year, this.month);
958
+ }
959
+ nextMonth() {
960
+ if (!this.dualCalendar) {
961
+ this.month++;
962
+ if (this.month > 11) {
963
+ this.month = 0;
964
+ this.year++;
965
+ }
966
+ this.generateCalendar();
967
+ return;
968
+ }
969
+ // For dual calendar, this should not be used - use nextLeftMonth or nextRightMonth instead
970
+ this.nextLeftMonth();
971
+ }
972
+ prevMonth() {
973
+ if (!this.dualCalendar) {
974
+ this.month--;
975
+ if (this.month < 0) {
976
+ this.month = 11;
977
+ this.year--;
978
+ }
979
+ this.generateCalendar();
980
+ return;
981
+ }
982
+ // For dual calendar, this should not be used - use prevLeftMonth or prevRightMonth instead
983
+ this.prevLeftMonth();
984
+ }
985
+ // Independent navigation for left calendar
986
+ nextLeftMonth() {
987
+ this.leftMonth++;
988
+ if (this.leftMonth > 11) {
989
+ this.leftMonth = 0;
990
+ this.leftYear++;
991
+ }
992
+ this.leftCalendar = this.buildCalendar(this.leftYear, this.leftMonth);
993
+ }
994
+ prevLeftMonth() {
995
+ this.leftMonth--;
996
+ if (this.leftMonth < 0) {
997
+ this.leftMonth = 11;
998
+ this.leftYear--;
999
+ }
1000
+ this.leftCalendar = this.buildCalendar(this.leftYear, this.leftMonth);
1001
+ }
1002
+ // Independent navigation for right calendar
1003
+ nextRightMonth() {
1004
+ this.rightMonth++;
1005
+ if (this.rightMonth > 11) {
1006
+ this.rightMonth = 0;
1007
+ this.rightYear++;
1008
+ }
1009
+ this.rightCalendar = this.buildCalendar(this.rightYear, this.rightMonth);
1010
+ }
1011
+ prevRightMonth() {
1012
+ this.rightMonth--;
1013
+ if (this.rightMonth < 0) {
1014
+ this.rightMonth = 11;
1015
+ this.rightYear--;
1016
+ }
1017
+ this.rightCalendar = this.buildCalendar(this.rightYear, this.rightMonth);
1018
+ }
1019
+ initializeDual() {
1020
+ this.leftMonth = this.today.getMonth();
1021
+ this.leftYear = this.today.getFullYear();
1022
+ // Initialize right calendar to next month, but they can move independently
1023
+ this.rightMonth = this.leftMonth + 1;
1024
+ this.rightYear = this.leftYear;
1025
+ if (this.rightMonth > 11) {
1026
+ this.rightMonth = 0;
1027
+ this.rightYear++;
1028
+ }
1029
+ this.generateDualCalendars();
1030
+ }
1031
+ generateDualCalendars() {
1032
+ this.leftCalendar = this.buildCalendar(this.leftYear, this.leftMonth);
1033
+ this.rightCalendar = this.buildCalendar(this.rightYear, this.rightMonth);
1034
+ }
1035
+ buildCalendar(year, month) {
1036
+ const firstDay = new Date(year, month, 1).getDay();
1037
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
1038
+ const prevMonthDays = new Date(year, month, 0).getDate();
1039
+ const grid = [];
1040
+ let row = [];
1041
+ // Adjust first day (0 = Sunday, 1 = Monday, etc.)
1042
+ const adjustedFirstDay = firstDay === 0 ? 6 : firstDay - 1; // Make Monday = 0
1043
+ for (let i = adjustedFirstDay - 1; i >= 0; i--) {
1044
+ row.push({ day: prevMonthDays - i, currentMonth: false });
1045
+ }
1046
+ for (let d = 1; d <= daysInMonth; d++) {
1047
+ row.push({ day: d, currentMonth: true });
1048
+ if (row.length === 7) {
1049
+ grid.push(row);
1050
+ row = [];
1051
+ }
1052
+ }
1053
+ let nextMonthDay = 1;
1054
+ while (row.length > 0 && row.length < 7) {
1055
+ row.push({ day: nextMonthDay++, currentMonth: false });
1056
+ }
1057
+ if (row.length)
1058
+ grid.push(row);
1059
+ // Ensure we always have 6 rows (42 cells total) for consistent layout
1060
+ while (grid.length < 6) {
1061
+ const newRow = [];
1062
+ for (let i = 0; i < 7; i++) {
1063
+ newRow.push({ day: nextMonthDay++, currentMonth: false });
1064
+ }
1065
+ grid.push(newRow);
1066
+ }
1067
+ return grid;
1068
+ }
1069
+ isDateSelected(year, month, day) {
1070
+ if (this.disableHighlight)
1071
+ return false;
1072
+ if (!day)
1073
+ return false;
1074
+ // Multi-date selection
1075
+ if (this.multiDateSelection) {
1076
+ return this.isDateInMultiSelection(year, month, day);
1077
+ }
1078
+ const cellDate = new Date(year, month, day);
1079
+ // Check if it's today (highlight today by default if no date selected)
1080
+ const today = new Date();
1081
+ const isToday = cellDate.getFullYear() === today.getFullYear() &&
1082
+ cellDate.getMonth() === today.getMonth() &&
1083
+ cellDate.getDate() === today.getDate();
1084
+ // If no startDate is set and it's today, highlight it
1085
+ if (!this.startDate && isToday) {
1086
+ return true;
1087
+ }
1088
+ if (!this.startDate)
1089
+ return false;
1090
+ // Check if date is disabled
1091
+ if (this.minDate && cellDate < this.minDate)
1092
+ return false;
1093
+ if (this.maxDate && cellDate > this.maxDate)
1094
+ return false;
1095
+ const sameDay = cellDate.getFullYear() === this.startDate.getFullYear() &&
1096
+ cellDate.getMonth() === this.startDate.getMonth() &&
1097
+ cellDate.getDate() === this.startDate.getDate();
1098
+ if (this.singleDatePicker)
1099
+ return sameDay;
1100
+ // For range selection: only highlight start and end dates (not in-between)
1101
+ if (this.startDate && this.endDate) {
1102
+ const start = new Date(this.startDate.getFullYear(), this.startDate.getMonth(), this.startDate.getDate());
1103
+ const end = new Date(this.endDate.getFullYear(), this.endDate.getMonth(), this.endDate.getDate());
1104
+ return cellDate.getTime() === start.getTime() || cellDate.getTime() === end.getTime();
1105
+ }
1106
+ // If only start date is selected and hovering, check if this is start or hovered end
1107
+ if (this.startDate && !this.endDate && this.hoveredDate) {
1108
+ const start = new Date(this.startDate.getFullYear(), this.startDate.getMonth(), this.startDate.getDate());
1109
+ const hovered = new Date(this.hoveredDate.getFullYear(), this.hoveredDate.getMonth(), this.hoveredDate.getDate());
1110
+ // Show both start and hovered date as selected (circular black)
1111
+ return cellDate.getTime() === start.getTime() || cellDate.getTime() === hovered.getTime();
1112
+ }
1113
+ return sameDay;
1114
+ }
1115
+ isDateInRange(year, month, day) {
1116
+ if (this.disableHighlight || !day)
1117
+ return false;
1118
+ if (this.singleDatePicker)
1119
+ return false;
1120
+ if (this.multiDateSelection)
1121
+ return false;
1122
+ const cellDate = new Date(year, month, day);
1123
+ // If both start and end are selected, show gray background for dates in between
1124
+ if (this.startDate && this.endDate) {
1125
+ const start = new Date(this.startDate.getFullYear(), this.startDate.getMonth(), this.startDate.getDate());
1126
+ const end = new Date(this.endDate.getFullYear(), this.endDate.getMonth(), this.endDate.getDate());
1127
+ return cellDate > start && cellDate < end;
1128
+ }
1129
+ // If only start is selected and hovering, show preview range
1130
+ if (this.startDate && !this.endDate && this.hoveredDate) {
1131
+ const start = new Date(this.startDate.getFullYear(), this.startDate.getMonth(), this.startDate.getDate());
1132
+ const hovered = new Date(this.hoveredDate.getFullYear(), this.hoveredDate.getMonth(), this.hoveredDate.getDate());
1133
+ // Determine which is earlier - show gray background for dates between them
1134
+ const minDate = hovered < start ? hovered : start;
1135
+ const maxDate = hovered >= start ? hovered : start;
1136
+ return cellDate > minDate && cellDate < maxDate;
1137
+ }
1138
+ return false;
1139
+ }
1140
+ isDateDisabled(year, month, day) {
1141
+ if (!day)
1142
+ return false;
1143
+ const cellDate = new Date(year, month, day);
1144
+ if (this.minDate && cellDate < this.minDate)
1145
+ return true;
1146
+ if (this.maxDate && cellDate > this.maxDate)
1147
+ return true;
1148
+ return false;
1149
+ }
1150
+ isToday(year, month, day) {
1151
+ if (!day)
1152
+ return false;
1153
+ const today = new Date();
1154
+ const cellDate = new Date(year, month, day);
1155
+ return cellDate.getFullYear() === today.getFullYear() &&
1156
+ cellDate.getMonth() === today.getMonth() &&
1157
+ cellDate.getDate() === today.getDate();
1158
+ }
1159
+ getDisplayValue() {
1160
+ if (this.multiDateSelection && this.selectedDates.length > 0) {
1161
+ if (this.selectedDates.length === 1) {
1162
+ return moment(this.selectedDates[0]).format(this.displayFormat);
1163
+ }
1164
+ return `${this.selectedDates.length} dates selected`;
1165
+ }
1166
+ if (!this.startDate)
1167
+ return '';
1168
+ // Prefer moment formatting for consistent display
1169
+ let dateStr = moment(this.startDate).format(this.displayFormat);
1170
+ if (this.enableTimepicker && !this.dualCalendar) {
1171
+ const hr = this.startDate.getHours().toString().padStart(2, '0');
1172
+ const min = this.startDate.getMinutes().toString().padStart(2, '0');
1173
+ dateStr += ` ${hr}:${min}`;
1174
+ if (this.enableSeconds) {
1175
+ const sec = this.startDate.getSeconds().toString().padStart(2, '0');
1176
+ dateStr += `:${sec}`;
1177
+ }
1178
+ }
1179
+ if (this.endDate && !this.singleDatePicker) {
1180
+ let endStr = moment(this.endDate).format(this.displayFormat);
1181
+ if (this.enableTimepicker) {
1182
+ if (this.dualCalendar) {
1183
+ const startHr = this.startDate.getHours().toString().padStart(2, '0');
1184
+ const startMin = this.startDate.getMinutes().toString().padStart(2, '0');
1185
+ dateStr += ` ${startHr}:${startMin}`;
1186
+ if (this.enableSeconds) {
1187
+ const startSec = this.startDate.getSeconds().toString().padStart(2, '0');
1188
+ dateStr += `:${startSec}`;
1189
+ }
1190
+ }
1191
+ const endHr = this.endDate.getHours().toString().padStart(2, '0');
1192
+ const endMin = this.endDate.getMinutes().toString().padStart(2, '0');
1193
+ endStr += ` ${endHr}:${endMin}`;
1194
+ if (this.enableSeconds) {
1195
+ const endSec = this.endDate.getSeconds().toString().padStart(2, '0');
1196
+ endStr += `:${endSec}`;
1197
+ }
1198
+ }
1199
+ return `${dateStr} - ${endStr}`;
1200
+ }
1201
+ return dateStr;
1202
+ }
1203
+ // Time picker helpers
1204
+ getTimeInputValue(isStart = true) {
1205
+ const h = isStart ? this.startHour : this.endHour;
1206
+ const m = isStart ? this.startMinute : this.endMinute;
1207
+ return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`;
1208
+ }
1209
+ getSingleTimeInputValue() {
1210
+ return `${this.selectedHour.toString().padStart(2, '0')}:${this.selectedMinute.toString().padStart(2, '0')}`;
1211
+ }
1212
+ // NEW: Helper to build display value for TimePickerComponent (single calendar)
1213
+ getSingleTimePickerDisplay() {
1214
+ const hour = this.selectedHour || 12;
1215
+ const minuteStr = this.selectedMinute.toString().padStart(2, '0');
1216
+ const ampm = this.selectedAMPM || 'AM';
1217
+ return `${hour}:${minuteStr} ${ampm}`;
1218
+ }
1219
+ // NEW: Helper to build display value for TimePickerComponent (dual calendar)
1220
+ getDualTimePickerDisplay(isStart = true) {
1221
+ const hour = isStart ? (this.startHour || 12) : (this.endHour || 12);
1222
+ const minute = isStart ? this.startMinute : this.endMinute;
1223
+ const ampm = isStart ? (this.startAMPM || 'AM') : (this.endAMPM || 'AM');
1224
+ const minuteStr = minute.toString().padStart(2, '0');
1225
+ return `${hour}:${minuteStr} ${ampm}`;
1226
+ }
1227
+ // Coordination helpers for embedded TimePickerComponent instances
1228
+ onTimePickerOpened(pickerId) {
1229
+ // Close previously open picker inside this calendar
1230
+ if (this.openTimePickerId && this.openTimePickerId !== pickerId) {
1231
+ if (!this.closePickerCounter[this.openTimePickerId]) {
1232
+ this.closePickerCounter[this.openTimePickerId] = 0;
1233
+ }
1234
+ this.closePickerCounter[this.openTimePickerId]++;
1235
+ }
1236
+ this.openTimePickerId = pickerId;
1237
+ }
1238
+ onTimePickerClosed(pickerId) {
1239
+ if (this.openTimePickerId === pickerId) {
1240
+ this.openTimePickerId = null;
1241
+ }
1242
+ }
1243
+ shouldClosePicker(pickerId) {
1244
+ return this.closePickerCounter[pickerId] || 0;
1245
+ }
1246
+ // NEW: Parse "H:MM AM/PM" (or "HH:MM" 24h) from TimePickerComponent
1247
+ parsePickerTimeString(timeStr) {
1248
+ if (!timeStr) {
1249
+ return { hour12: 12, minute: 0, ampm: 'AM' };
1250
+ }
1251
+ const parts = timeStr.trim().split(' ');
1252
+ const timePart = parts[0] || '12:00';
1253
+ let ampmPart = (parts[1] || '').toUpperCase();
1254
+ const [hourStr, minuteStr] = timePart.split(':');
1255
+ let hour = parseInt(hourStr || '12', 10);
1256
+ const minute = parseInt(minuteStr || '0', 10);
1257
+ if (ampmPart !== 'AM' && ampmPart !== 'PM') {
1258
+ // Interpret as 24-hour input and convert
1259
+ if (hour >= 12) {
1260
+ ampmPart = 'PM';
1261
+ if (hour > 12)
1262
+ hour -= 12;
1263
+ }
1264
+ else {
1265
+ ampmPart = 'AM';
1266
+ if (hour === 0)
1267
+ hour = 12;
1268
+ }
1269
+ }
1270
+ // Clamp to 1-12 range just in case
1271
+ if (hour < 1)
1272
+ hour = 1;
1273
+ if (hour > 12)
1274
+ hour = 12;
1275
+ return { hour12: hour, minute, ampm: ampmPart };
1276
+ }
1277
+ // NEW: Handle TimePickerComponent change for single calendar
1278
+ onSingleTimePickerChange(time) {
1279
+ const { hour12, minute, ampm } = this.parsePickerTimeString(time);
1280
+ this.selectedHour = hour12;
1281
+ this.selectedMinute = minute;
1282
+ this.selectedAMPM = ampm;
1283
+ if (this.startDate) {
1284
+ let h24 = hour12;
1285
+ if (ampm === 'PM' && h24 < 12)
1286
+ h24 += 12;
1287
+ if (ampm === 'AM' && h24 === 12)
1288
+ h24 = 0;
1289
+ this.startDate.setHours(h24, minute, this.selectedSecond);
1290
+ this.emitSelection();
1291
+ }
1292
+ }
1293
+ // NEW: Handle TimePickerComponent change for dual calendar
1294
+ onDualTimePickerChange(time, isStart = true) {
1295
+ const { hour12, minute, ampm } = this.parsePickerTimeString(time);
1296
+ if (isStart) {
1297
+ this.startHour = hour12;
1298
+ this.startMinute = minute;
1299
+ this.startAMPM = ampm;
1300
+ if (this.startDate) {
1301
+ let h24 = hour12;
1302
+ if (ampm === 'PM' && h24 < 12)
1303
+ h24 += 12;
1304
+ if (ampm === 'AM' && h24 === 12)
1305
+ h24 = 0;
1306
+ this.startDate.setHours(h24, minute, this.startSecond);
1307
+ }
1308
+ }
1309
+ else {
1310
+ this.endHour = hour12;
1311
+ this.endMinute = minute;
1312
+ this.endAMPM = ampm;
1313
+ if (this.endDate) {
1314
+ let h24 = hour12;
1315
+ if (ampm === 'PM' && h24 < 12)
1316
+ h24 += 12;
1317
+ if (ampm === 'AM' && h24 === 12)
1318
+ h24 = 0;
1319
+ this.endDate.setHours(h24, minute, this.endSecond);
1320
+ }
1321
+ }
1322
+ this.emitSelection();
1323
+ }
1324
+ onTimeChange(event, isStart = true) {
1325
+ const [h, m] = event.target.value.split(':').map(Number);
1326
+ if (isStart) {
1327
+ this.startHour = h;
1328
+ this.startMinute = m;
1329
+ if (this.startDate) {
1330
+ this.startDate.setHours(h, m, this.startSecond);
1331
+ this.emitSelection();
1332
+ }
1333
+ }
1334
+ else {
1335
+ this.endHour = h;
1336
+ this.endMinute = m;
1337
+ if (this.endDate) {
1338
+ this.endDate.setHours(h, m, this.endSecond);
1339
+ this.emitSelection();
1340
+ }
1341
+ }
1342
+ }
1343
+ onSingleTimeChange(event) {
1344
+ const [h, m] = event.target.value.split(':').map(Number);
1345
+ this.selectedHour = h;
1346
+ this.selectedMinute = m;
1347
+ if (this.startDate) {
1348
+ this.startDate.setHours(h, m, this.selectedSecond);
1349
+ this.emitSelection();
1350
+ }
1351
+ }
1352
+ // Custom time picker controls
1353
+ incrementHour(isStart = true) {
1354
+ // 12-hour format: 1-12
1355
+ if (isStart) {
1356
+ this.startHour = this.startHour >= 12 ? 1 : this.startHour + 1;
1357
+ // Toggle AM/PM at 12
1358
+ if (this.startHour === 12) {
1359
+ this.startAMPM = this.startAMPM === 'AM' ? 'PM' : 'AM';
1360
+ }
1361
+ if (this.startDate) {
1362
+ let h = this.startHour;
1363
+ if (this.startAMPM === 'PM' && h < 12)
1364
+ h += 12;
1365
+ if (this.startAMPM === 'AM' && h === 12)
1366
+ h = 0;
1367
+ this.startDate.setHours(h, this.startMinute, this.startSecond);
1368
+ }
1369
+ }
1370
+ else {
1371
+ this.endHour = this.endHour >= 12 ? 1 : this.endHour + 1;
1372
+ // Toggle AM/PM at 12
1373
+ if (this.endHour === 12) {
1374
+ this.endAMPM = this.endAMPM === 'AM' ? 'PM' : 'AM';
1375
+ }
1376
+ if (this.endDate) {
1377
+ let h = this.endHour;
1378
+ if (this.endAMPM === 'PM' && h < 12)
1379
+ h += 12;
1380
+ if (this.endAMPM === 'AM' && h === 12)
1381
+ h = 0;
1382
+ this.endDate.setHours(h, this.endMinute, this.endSecond);
1383
+ }
1384
+ }
1385
+ this.emitSelection();
1386
+ }
1387
+ decrementHour(isStart = true) {
1388
+ // 12-hour format: 1-12
1389
+ if (isStart) {
1390
+ this.startHour = this.startHour <= 1 ? 12 : this.startHour - 1;
1391
+ // Toggle AM/PM at 12
1392
+ if (this.startHour === 12) {
1393
+ this.startAMPM = this.startAMPM === 'AM' ? 'PM' : 'AM';
1394
+ }
1395
+ if (this.startDate) {
1396
+ let h = this.startHour;
1397
+ if (this.startAMPM === 'PM' && h < 12)
1398
+ h += 12;
1399
+ if (this.startAMPM === 'AM' && h === 12)
1400
+ h = 0;
1401
+ this.startDate.setHours(h, this.startMinute, this.startSecond);
1402
+ }
1403
+ }
1404
+ else {
1405
+ this.endHour = this.endHour <= 1 ? 12 : this.endHour - 1;
1406
+ // Toggle AM/PM at 12
1407
+ if (this.endHour === 12) {
1408
+ this.endAMPM = this.endAMPM === 'AM' ? 'PM' : 'AM';
1409
+ }
1410
+ if (this.endDate) {
1411
+ let h = this.endHour;
1412
+ if (this.endAMPM === 'PM' && h < 12)
1413
+ h += 12;
1414
+ if (this.endAMPM === 'AM' && h === 12)
1415
+ h = 0;
1416
+ this.endDate.setHours(h, this.endMinute, this.endSecond);
1417
+ }
1418
+ }
1419
+ this.emitSelection();
1420
+ }
1421
+ incrementMinute(isStart = true) {
1422
+ if (isStart) {
1423
+ this.startMinute = (this.startMinute + 1) % 60;
1424
+ if (this.startDate) {
1425
+ let h = this.startHour;
1426
+ if (this.startAMPM === 'PM' && h < 12)
1427
+ h += 12;
1428
+ if (this.startAMPM === 'AM' && h === 12)
1429
+ h = 0;
1430
+ this.startDate.setHours(h, this.startMinute, this.startSecond);
1431
+ }
1432
+ }
1433
+ else {
1434
+ this.endMinute = (this.endMinute + 1) % 60;
1435
+ if (this.endDate) {
1436
+ let h = this.endHour;
1437
+ if (this.endAMPM === 'PM' && h < 12)
1438
+ h += 12;
1439
+ if (this.endAMPM === 'AM' && h === 12)
1440
+ h = 0;
1441
+ this.endDate.setHours(h, this.endMinute, this.endSecond);
1442
+ }
1443
+ }
1444
+ this.emitSelection();
1445
+ }
1446
+ decrementMinute(isStart = true) {
1447
+ if (isStart) {
1448
+ this.startMinute = this.startMinute <= 0 ? 59 : this.startMinute - 1;
1449
+ if (this.startDate) {
1450
+ let h = this.startHour;
1451
+ if (this.startAMPM === 'PM' && h < 12)
1452
+ h += 12;
1453
+ if (this.startAMPM === 'AM' && h === 12)
1454
+ h = 0;
1455
+ this.startDate.setHours(h, this.startMinute, this.startSecond);
1456
+ }
1457
+ }
1458
+ else {
1459
+ this.endMinute = this.endMinute <= 0 ? 59 : this.endMinute - 1;
1460
+ if (this.endDate) {
1461
+ let h = this.endHour;
1462
+ if (this.endAMPM === 'PM' && h < 12)
1463
+ h += 12;
1464
+ if (this.endAMPM === 'AM' && h === 12)
1465
+ h = 0;
1466
+ this.endDate.setHours(h, this.endMinute, this.endSecond);
1467
+ }
1468
+ }
1469
+ this.emitSelection();
1470
+ }
1471
+ toggleAMPM(isStart = true) {
1472
+ if (isStart) {
1473
+ this.startAMPM = this.startAMPM === 'AM' ? 'PM' : 'AM';
1474
+ if (this.startDate) {
1475
+ let h = this.startHour;
1476
+ if (this.startAMPM === 'PM' && h < 12)
1477
+ h += 12;
1478
+ if (this.startAMPM === 'AM' && h === 12)
1479
+ h = 0;
1480
+ this.startDate.setHours(h, this.startMinute, this.startSecond);
1481
+ }
1482
+ }
1483
+ else {
1484
+ this.endAMPM = this.endAMPM === 'AM' ? 'PM' : 'AM';
1485
+ if (this.endDate) {
1486
+ let h = this.endHour;
1487
+ if (this.endAMPM === 'PM' && h < 12)
1488
+ h += 12;
1489
+ if (this.endAMPM === 'AM' && h === 12)
1490
+ h = 0;
1491
+ this.endDate.setHours(h, this.endMinute, this.endSecond);
1492
+ }
1493
+ }
1494
+ this.emitSelection();
1495
+ }
1496
+ // Single calendar time picker controls (12-hour format: 1-12)
1497
+ incrementSingleHour() {
1498
+ this.selectedHour = this.selectedHour >= 12 ? 1 : this.selectedHour + 1;
1499
+ // Toggle AM/PM at 12
1500
+ if (this.selectedHour === 12) {
1501
+ this.selectedAMPM = this.selectedAMPM === 'AM' ? 'PM' : 'AM';
1502
+ }
1503
+ if (this.startDate) {
1504
+ let h = this.selectedHour;
1505
+ if (this.selectedAMPM === 'PM' && h < 12)
1506
+ h += 12;
1507
+ if (this.selectedAMPM === 'AM' && h === 12)
1508
+ h = 0;
1509
+ this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
1510
+ this.emitSelection();
1511
+ }
1512
+ }
1513
+ decrementSingleHour() {
1514
+ this.selectedHour = this.selectedHour <= 1 ? 12 : this.selectedHour - 1;
1515
+ // Toggle AM/PM at 12
1516
+ if (this.selectedHour === 12) {
1517
+ this.selectedAMPM = this.selectedAMPM === 'AM' ? 'PM' : 'AM';
1518
+ }
1519
+ if (this.startDate) {
1520
+ let h = this.selectedHour;
1521
+ if (this.selectedAMPM === 'PM' && h < 12)
1522
+ h += 12;
1523
+ if (this.selectedAMPM === 'AM' && h === 12)
1524
+ h = 0;
1525
+ this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
1526
+ this.emitSelection();
1527
+ }
1528
+ }
1529
+ incrementSingleMinute() {
1530
+ this.selectedMinute = (this.selectedMinute + 1) % 60;
1531
+ if (this.startDate) {
1532
+ let h = this.selectedHour;
1533
+ if (this.selectedAMPM === 'PM' && h < 12)
1534
+ h += 12;
1535
+ if (this.selectedAMPM === 'AM' && h === 12)
1536
+ h = 0;
1537
+ this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
1538
+ this.emitSelection();
1539
+ }
1540
+ }
1541
+ decrementSingleMinute() {
1542
+ this.selectedMinute = this.selectedMinute <= 0 ? 59 : this.selectedMinute - 1;
1543
+ if (this.startDate) {
1544
+ let h = this.selectedHour;
1545
+ if (this.selectedAMPM === 'PM' && h < 12)
1546
+ h += 12;
1547
+ if (this.selectedAMPM === 'AM' && h === 12)
1548
+ h = 0;
1549
+ this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
1550
+ this.emitSelection();
1551
+ }
1552
+ }
1553
+ toggleSingleAMPM() {
1554
+ this.selectedAMPM = this.selectedAMPM === 'AM' ? 'PM' : 'AM';
1555
+ if (this.startDate) {
1556
+ let h = this.selectedHour;
1557
+ if (this.selectedAMPM === 'PM' && h < 12)
1558
+ h += 12;
1559
+ if (this.selectedAMPM === 'AM' && h === 12)
1560
+ h = 0;
1561
+ this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
1562
+ this.emitSelection();
1563
+ }
1564
+ }
1565
+ getMonthName(month) {
1566
+ const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
1567
+ return months[month];
1568
+ }
1569
+ // Input handlers for direct hour/minute input (12-hour format only)
1570
+ onHourInput(event, isStart = true, isSingle = false) {
1571
+ const inputValue = event.target.value;
1572
+ // Allow empty input while typing
1573
+ if (inputValue === '' || inputValue === null || inputValue === undefined) {
1574
+ return;
1575
+ }
1576
+ let value = parseInt(inputValue) || 0;
1577
+ // Validate: 1-12 for 12-hour format
1578
+ if (value < 1)
1579
+ value = 1;
1580
+ if (value > 12)
1581
+ value = 12;
1582
+ if (isSingle) {
1583
+ this.selectedHour = value;
1584
+ if (this.startDate) {
1585
+ let h = value;
1586
+ if (this.selectedAMPM === 'PM' && h < 12)
1587
+ h += 12;
1588
+ if (this.selectedAMPM === 'AM' && h === 12)
1589
+ h = 0;
1590
+ this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
1591
+ this.emitSelection();
1592
+ }
1593
+ }
1594
+ else if (isStart) {
1595
+ this.startHour = value;
1596
+ if (this.startDate) {
1597
+ let h = value;
1598
+ if (this.startAMPM === 'PM' && h < 12)
1599
+ h += 12;
1600
+ if (this.startAMPM === 'AM' && h === 12)
1601
+ h = 0;
1602
+ this.startDate.setHours(h, this.startMinute, this.startSecond);
1603
+ this.emitSelection();
1604
+ }
1605
+ }
1606
+ else {
1607
+ this.endHour = value;
1608
+ if (this.endDate) {
1609
+ let h = value;
1610
+ if (this.endAMPM === 'PM' && h < 12)
1611
+ h += 12;
1612
+ if (this.endAMPM === 'AM' && h === 12)
1613
+ h = 0;
1614
+ this.endDate.setHours(h, this.endMinute, this.endSecond);
1615
+ this.emitSelection();
1616
+ }
1617
+ }
1618
+ // Don't format during input, only on blur
1619
+ event.target.value = value.toString();
1620
+ }
1621
+ onHourBlur(event, isStart = true, isSingle = false) {
1622
+ const inputValue = event.target.value;
1623
+ if (inputValue === '' || inputValue === null || inputValue === undefined) {
1624
+ // If empty, set to current value
1625
+ const currentValue = isSingle ? this.selectedHour : (isStart ? this.startHour : this.endHour);
1626
+ event.target.value = currentValue.toString();
1627
+ return;
1628
+ }
1629
+ let value = parseInt(inputValue) || 0;
1630
+ if (value < 1)
1631
+ value = 1;
1632
+ if (value > 12)
1633
+ value = 12;
1634
+ // Format to single digit (no padding for hours in 12-hour format)
1635
+ event.target.value = value.toString();
1636
+ // Update the value
1637
+ if (isSingle) {
1638
+ this.selectedHour = value;
1639
+ if (this.startDate) {
1640
+ let h = value;
1641
+ if (this.selectedAMPM === 'PM' && h < 12)
1642
+ h += 12;
1643
+ if (this.selectedAMPM === 'AM' && h === 12)
1644
+ h = 0;
1645
+ this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
1646
+ this.emitSelection();
1647
+ }
1648
+ }
1649
+ else if (isStart) {
1650
+ this.startHour = value;
1651
+ if (this.startDate) {
1652
+ let h = value;
1653
+ if (this.startAMPM === 'PM' && h < 12)
1654
+ h += 12;
1655
+ if (this.startAMPM === 'AM' && h === 12)
1656
+ h = 0;
1657
+ this.startDate.setHours(h, this.startMinute, this.startSecond);
1658
+ this.emitSelection();
1659
+ }
1660
+ }
1661
+ else {
1662
+ this.endHour = value;
1663
+ if (this.endDate) {
1664
+ let h = value;
1665
+ if (this.endAMPM === 'PM' && h < 12)
1666
+ h += 12;
1667
+ if (this.endAMPM === 'AM' && h === 12)
1668
+ h = 0;
1669
+ this.endDate.setHours(h, this.endMinute, this.endSecond);
1670
+ this.emitSelection();
1671
+ }
1672
+ }
1673
+ }
1674
+ onMinuteInput(event, isStart = true, isSingle = false) {
1675
+ const inputValue = event.target.value;
1676
+ const key = `${isStart ? 'start' : 'end'}_${isSingle ? 'single' : 'dual'}`;
1677
+ // Remove any non-digit characters
1678
+ const digitsOnly = inputValue.replace(/\D/g, '');
1679
+ // Store raw input value for display
1680
+ this.minuteInputValues[key] = digitsOnly;
1681
+ // Allow empty input while typing
1682
+ if (digitsOnly === '' || digitsOnly === null || digitsOnly === undefined) {
1683
+ return; // Don't modify the input, let user clear it
1684
+ }
1685
+ // Allow typing up to 2 digits without formatting
1686
+ let value = parseInt(digitsOnly) || 0;
1687
+ // If user types more than 2 digits, take only first 2
1688
+ if (digitsOnly.length > 2) {
1689
+ value = parseInt(digitsOnly.substring(0, 2));
1690
+ this.minuteInputValues[key] = digitsOnly.substring(0, 2);
1691
+ event.target.value = digitsOnly.substring(0, 2);
1692
+ }
1693
+ // If value exceeds 59, clamp it to 59
1694
+ if (value > 59) {
1695
+ value = 59;
1696
+ this.minuteInputValues[key] = '59';
1697
+ event.target.value = '59';
1698
+ }
1699
+ // Update the internal value silently (don't emit during typing to avoid re-rendering)
1700
+ if (isSingle) {
1701
+ this.selectedMinute = value;
1702
+ }
1703
+ else if (isStart) {
1704
+ this.startMinute = value;
1705
+ }
1706
+ else {
1707
+ this.endMinute = value;
1708
+ }
1709
+ // Don't update dates or emit during typing - wait for blur or apply
1710
+ }
1711
+ onMinuteBlur(event, isStart = true, isSingle = false) {
1712
+ const key = `${isStart ? 'start' : 'end'}_${isSingle ? 'single' : 'dual'}`;
1713
+ const inputValue = event.target.value;
1714
+ if (inputValue === '' || inputValue === null || inputValue === undefined) {
1715
+ // If empty, set to current value
1716
+ const currentValue = isSingle ? this.selectedMinute : (isStart ? this.startMinute : this.endMinute);
1717
+ event.target.value = currentValue.toString().padStart(2, '0');
1718
+ delete this.minuteInputValues[key];
1719
+ return;
1720
+ }
1721
+ const digitsOnly = inputValue.replace(/\D/g, '');
1722
+ let value = parseInt(digitsOnly) || 0;
1723
+ if (value < 0)
1724
+ value = 0;
1725
+ if (value > 59)
1726
+ value = 59;
1727
+ // Format to 2 digits on blur (01-09 becomes 01-09, 10-59 stays as is)
1728
+ event.target.value = value.toString().padStart(2, '0');
1729
+ delete this.minuteInputValues[key]; // Clear raw input, use formatted value
1730
+ // Update the value
1731
+ if (isSingle) {
1732
+ this.selectedMinute = value;
1733
+ if (this.startDate) {
1734
+ let h = this.selectedHour;
1735
+ if (this.selectedAMPM === 'PM' && h < 12)
1736
+ h += 12;
1737
+ if (this.selectedAMPM === 'AM' && h === 12)
1738
+ h = 0;
1739
+ this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
1740
+ this.emitSelection();
1741
+ }
1742
+ }
1743
+ else if (isStart) {
1744
+ this.startMinute = value;
1745
+ if (this.startDate) {
1746
+ let h = this.startHour;
1747
+ if (this.startAMPM === 'PM' && h < 12)
1748
+ h += 12;
1749
+ if (this.startAMPM === 'AM' && h === 12)
1750
+ h = 0;
1751
+ this.startDate.setHours(h, this.startMinute, this.startSecond);
1752
+ this.emitSelection();
1753
+ }
1754
+ }
1755
+ else {
1756
+ this.endMinute = value;
1757
+ if (this.endDate) {
1758
+ let h = this.endHour;
1759
+ if (this.endAMPM === 'PM' && h < 12)
1760
+ h += 12;
1761
+ if (this.endAMPM === 'AM' && h === 12)
1762
+ h = 0;
1763
+ this.endDate.setHours(h, this.endMinute, this.endSecond);
1764
+ this.emitSelection();
1765
+ }
1766
+ }
1767
+ }
1768
+ // Get display value for hour (always 12-hour format)
1769
+ getDisplayHour(hour) {
1770
+ if (hour === 0)
1771
+ return 12;
1772
+ if (hour > 12)
1773
+ return hour - 12;
1774
+ return hour;
1775
+ }
1776
+ // Get display value for minute (formatted only when not actively typing)
1777
+ getMinuteDisplayValue(isStart, isSingle) {
1778
+ const key = `${isStart ? 'start' : 'end'}_${isSingle ? 'single' : 'dual'}`;
1779
+ // If user is typing (has raw input), show that, otherwise show formatted value
1780
+ if (this.minuteInputValues[key] !== undefined) {
1781
+ return this.minuteInputValues[key];
1782
+ }
1783
+ // Otherwise return formatted value
1784
+ const value = isSingle ? this.selectedMinute : (isStart ? this.startMinute : this.endMinute);
1785
+ return value.toString().padStart(2, '0');
1786
+ }
1787
+ // Helper method to apply time picker values to a date
1788
+ applyTimeToDate(date, isStart) {
1789
+ if (this.dualCalendar) {
1790
+ if (isStart) {
1791
+ let h = this.startHour;
1792
+ if (this.startAMPM === 'PM' && h < 12)
1793
+ h += 12;
1794
+ if (this.startAMPM === 'AM' && h === 12)
1795
+ h = 0;
1796
+ date.setHours(h, this.startMinute, this.startSecond);
1797
+ }
1798
+ else {
1799
+ let h = this.endHour;
1800
+ if (this.endAMPM === 'PM' && h < 12)
1801
+ h += 12;
1802
+ if (this.endAMPM === 'AM' && h === 12)
1803
+ h = 0;
1804
+ date.setHours(h, this.endMinute, this.endSecond);
1805
+ }
1806
+ }
1807
+ else {
1808
+ let h = this.selectedHour;
1809
+ if (this.selectedAMPM === 'PM' && h < 12)
1810
+ h += 12;
1811
+ if (this.selectedAMPM === 'AM' && h === 12)
1812
+ h = 0;
1813
+ date.setHours(h, this.selectedMinute, this.selectedSecond);
1814
+ }
1815
+ }
1816
+ // Select all text on focus for easy replacement
1817
+ onTimeInputFocus(event) {
1818
+ event.target.select();
1819
+ }
1820
+ // Format all minute inputs to 2 digits (called before Apply)
1821
+ formatAllMinuteInputs() {
1822
+ // Format minute inputs in the DOM - find all minute inputs and format single digits
1823
+ const inputs = document.querySelectorAll('.time-input');
1824
+ inputs.forEach((input) => {
1825
+ const value = parseInt(input.value);
1826
+ // If it's a valid minute value (0-59) and not already formatted (single digit or 2 digits without leading zero)
1827
+ if (!isNaN(value) && value >= 0 && value <= 59) {
1828
+ // Format if it's a single digit (1-9) or if it's 2 digits but the first is not 0
1829
+ if (input.value.length === 1 || (input.value.length === 2 && !input.value.startsWith('0') && value < 10)) {
1830
+ input.value = value.toString().padStart(2, '0');
1831
+ }
1832
+ }
1833
+ });
1834
+ }
1835
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CustomCalendarComponent, deps: [{ token: CalendarManagerService }], target: i0.ɵɵFactoryTarget.Component });
1836
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.16", type: CustomCalendarComponent, isStandalone: true, selector: "app-custom-calendar", inputs: { enableTimepicker: "enableTimepicker", autoApply: "autoApply", closeOnAutoApply: "closeOnAutoApply", showCancel: "showCancel", linkedCalendars: "linkedCalendars", singleDatePicker: "singleDatePicker", showWeekNumbers: "showWeekNumbers", showISOWeekNumbers: "showISOWeekNumbers", customRangeDirection: "customRangeDirection", lockStartDate: "lockStartDate", position: "position", drop: "drop", dualCalendar: "dualCalendar", showRanges: "showRanges", timeFormat: "timeFormat", enableSeconds: "enableSeconds", customRanges: "customRanges", multiDateSelection: "multiDateSelection", maxDate: "maxDate", minDate: "minDate", placeholder: "placeholder", opens: "opens", inline: "inline", isDisplayCrossIcon: "isDisplayCrossIcon", selectedValue: "selectedValue", displayFormat: "displayFormat" }, outputs: { selected: "selected", opened: "opened", closed: "closed" }, host: { listeners: { "document:click": "onClickOutside($event)" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"calendar-container relative\" [class.open]=\"show\" [class.inline-mode]=\"inline\">\r\n <!-- Input field -->\r\n <div class=\"input-wrapper\" *ngIf=\"!inline\">\r\n <input\r\n type=\"text\"\r\n (click)=\"toggle()\"\r\n readonly\r\n [value]=\"getDisplayValue()\"\r\n [placeholder]=\"placeholder\"\r\n class=\"calendar-input\">\r\n <!-- *ngIf=\"!getDisplayValue()\" -->\r\n <span class=\"calendar-icon\" >\r\n <img src=\"assets/calender/calender.svg\" alt=\"calendar\" class=\"calendar-icon-img\">\r\n </span>\r\n <button class=\"clear-btn\" *ngIf=\"getDisplayValue() && isDisplayCrossIcon\" (click)=\"clear(); $event.stopPropagation()\" title=\"Clear\">\u00D7</button>\r\n </div>\r\n\r\n <!-- Calendar Popup / Inline -->\r\n <div class=\"calendar-popup\"\r\n [class.inline-calendar]=\"inline\"\r\n [ngClass]=\"{\r\n 'position-right': !inline && opens === 'right',\r\n 'position-center': !inline && opens === 'center',\r\n 'drop-up': !inline && drop === 'up',\r\n 'has-ranges': showRanges && customRanges,\r\n 'dual-calendar-mode': dualCalendar\r\n }\"\r\n *ngIf=\"inline || show\">\r\n\r\n <!-- RANGES -->\r\n <div class=\"ranges\" *ngIf=\"showRanges && customRanges\">\r\n <button\r\n *ngFor=\"let rangeKey of rangeOrder\"\r\n (click)=\"chooseRange(rangeKey)\"\r\n [class.active]=\"activeRange === rangeKey\"\r\n [class.custom-range]=\"rangeKey === 'Custom Range'\"\r\n class=\"range-btn\"\r\n [disabled]=\"rangeKey === 'Custom Range'\">\r\n {{ rangeKey }}\r\n </button>\r\n </div>\r\n<div class=\"\" [ngClass]=\"showRanges ? 'w-100 flex-grow-1' : ''\">\r\n\r\n\r\n <!-- SINGLE CALENDAR -->\r\n <div *ngIf=\"!dualCalendar\" class=\"calendar-wrapper\">\r\n <div class=\"header\">\r\n <!-- <button (click)=\"prevMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/pagination-left-gray.svg\" alt=\"arrow-left\" class=\"arrow-left\">\r\n </button> -->\r\n <button class=\"nav-btn\" type=\"button\" (click)=\"prevMonth()\" matTooltip=\"Prev month\"\r\n >\r\n <img src=\"assets/calender/chevron-left.svg\" alt=\"prev\" class=\"h-3 w-3\" />\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(month) }} {{ year }}</span>\r\n <!-- <button (click)=\"nextMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/pagination-right-gray.svg\" alt=\"arrow-right\" class=\"arrow-right\">\r\n </button> -->\r\n <button class=\"nav-btn\" type=\"button\" (click)=\"nextMonth()\" matTooltip=\"Next month\"\r\n >\r\n <img src=\"assets/calender/chevron-right.svg\" alt=\"next\" class=\"h-3 w-3\" />\r\n <!--<img src=\"assets/calender/pagination-right-gray.svg\" alt=\"next\" class=\"h-3 w-3\" /> -->\r\n </button>\r\n </div>\r\n\r\n <table class=\"calendar-table\">\r\n <thead>\r\n <tr>\r\n <th *ngFor=\"let d of ['Mo','Tu','We','Th','Fr','Sa','Su']\" class=\"weekday-header\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of calendar\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(year, month, dayObj.day) && selectDate(dayObj.day)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(year, month, dayObj.day) && onDateHover(dayObj.day, false)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(year, month, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(year, month, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(year, month, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(year, month, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(year, month, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Single Calendar Time Picker -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">Time</div>\r\n <div class=\"timepicker-controls\">\r\n <app-time-picker\r\n pickerId=\"single-time\"\r\n [label]=\"''\"\r\n [value]=\"getSingleTimePickerDisplay()\"\r\n [closePicker]=\"shouldClosePicker('single-time')\"\r\n (timeChange)=\"onSingleTimePickerChange($event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- DUAL CALENDAR -->\r\n <div class=\"dual-calendar\" *ngIf=\"dualCalendar\">\r\n <!-- LEFT CALENDAR -->\r\n <div class=\"calendar-left\">\r\n <div class=\"header\">\r\n <button (click)=\"prevLeftMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/chevron-left.svg\" alt=\"arrow-left\" class=\"arrow-left\">\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(leftMonth) }} {{ leftYear }}</span>\r\n <button (click)=\"nextLeftMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/chevron-right.svg\" alt=\"arrow-right\" class=\"arrow-right\">\r\n </button>\r\n </div>\r\n <table class=\"calendar-table\">\r\n <thead>\r\n <tr>\r\n <th *ngFor=\"let d of ['Mo','Tu','We','Th','Fr','Sa','Su']\" class=\"weekday-header\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of leftCalendar\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(leftYear, leftMonth, dayObj.day) && selectDate(dayObj.day, false)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(leftYear, leftMonth, dayObj.day) && onDateHover(dayObj.day, false)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(leftYear, leftMonth, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(leftYear, leftMonth, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(leftYear, leftMonth, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(leftYear, leftMonth, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(leftYear, leftMonth, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Start Time Picker for Dual Calendar -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">Start Time</div>\r\n <div class=\"timepicker-controls\">\r\n <app-time-picker\r\n pickerId=\"dual-start\"\r\n [label]=\"''\"\r\n [value]=\"getDualTimePickerDisplay(true)\"\r\n [closePicker]=\"shouldClosePicker('dual-start')\"\r\n (timeChange)=\"onDualTimePickerChange($event, true)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- RIGHT CALENDAR -->\r\n <div class=\"calendar-right\">\r\n <div class=\"header\">\r\n <button (click)=\"prevRightMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/chevron-left.svg\" alt=\"arrow-left\" class=\"arrow-left\">\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(rightMonth) }} {{ rightYear }}</span>\r\n <button (click)=\"nextRightMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/chevron-right.svg\" alt=\"arrow-right\" class=\"arrow-right\">\r\n </button>\r\n </div>\r\n <table class=\"calendar-table\">\r\n <thead>\r\n <tr>\r\n <th *ngFor=\"let d of ['Mo','Tu','We','Th','Fr','Sa','Su']\" class=\"weekday-header\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of rightCalendar\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(rightYear, rightMonth, dayObj.day) && selectDate(dayObj.day, true)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(rightYear, rightMonth, dayObj.day) && onDateHover(dayObj.day, true)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(rightYear, rightMonth, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(rightYear, rightMonth, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(rightYear, rightMonth, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(rightYear, rightMonth, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(rightYear, rightMonth, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- End Time Picker for Dual Calendar -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">End Time</div>\r\n <div class=\"timepicker-controls\">\r\n <app-time-picker\r\n pickerId=\"dual-end\"\r\n [label]=\"''\"\r\n [value]=\"getDualTimePickerDisplay(false)\"\r\n [closePicker]=\"shouldClosePicker('dual-end')\"\r\n (timeChange)=\"onDualTimePickerChange($event, false)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- FOOTER -->\r\n <div class=\"footer\" *ngIf=\"!inline\">\r\n <button *ngIf=\"showCancel\" (click)=\"cancel()\" class=\"btn-cancel\" type=\"button\">Cancel</button>\r\n <button (click)=\"apply()\" class=\"btn-apply\" type=\"button\">Apply</button>\r\n </div>\r\n\r\n </div>\r\n\r\n </div>\r\n</div>\r\n", styles: [".calendar-container,.calendar-container *{font-family:Inter,sans-serif!important}.calendar-container{position:relative;display:inline-block;width:100%}.input-wrapper{position:relative;display:flex;align-items:center}.calendar-input{width:100%;padding:9px 14px 9px 40px;border:1px solid #ddd;border-radius:8px;font-size:14px;cursor:pointer;background:#fff;transition:all .2s}.calendar-input:hover{border-color:#999}.calendar-input:focus{outline:none;border-color:#999;box-shadow:0 0 0 3px #6a6a6a1a}.calendar-icon{position:absolute;left:12px;pointer-events:none;font-size:18px}.clear-btn{position:absolute;right:9px;background:none;border:none;font-size:20px;color:#999;cursor:pointer;padding:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center;line-height:1;transition:color .2s;top:8px}.clear-btn:hover{color:#333}.calendar-popup{position:absolute;top:110%;left:0;width:320px;background:#fff;border-radius:12px;box-shadow:0 10px 40px #00000026;z-index:1000;animation:slideDown .2s ease-out}.calendar-popup.inline-calendar{position:relative;top:0;left:0;width:100%;margin-top:0;animation:none;box-shadow:0 2px 8px #0000001a}.calendar-container.inline-mode{display:block;width:100%}.calendar-popup.dual-calendar-mode{width:600px}.calendar-popup.dual-calendar-mode.has-ranges{width:730px}.calendar-popup.has-ranges{width:450px}.calendar-popup.dual-calendar-mode.has-ranges .dual-calendar{border-left:1px solid #eee}.calendar-popup.drop-up{top:auto;bottom:110%;animation:slideUp .2s ease-out}.calendar-popup.position-right{left:auto;right:0}.calendar-popup.position-center{left:50%;transform:translate(-50%)}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.ranges{display:flex;flex-direction:column;gap:4px;margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid #eee;min-width:150px;padding-right:8px}.range-btn{padding:7px 10px;border:1px solid transparent;background:transparent;border-radius:4px;cursor:pointer;text-align:left;font-size:14px;transition:all .2s;color:#838383;font-weight:500}.range-btn:hover{background:#f5f5f5;color:#000}.range-btn.active{background:#f0f0f0;color:#000;font-weight:500}.calendar-wrapper{padding:0 12px 12px;border-left:1px solid #eee}.header{display:flex;justify-content:space-between;align-items:center;padding:12px 0}.month-year{font-size:15px;font-weight:500;color:#333;flex:1;text-align:center;text-transform:capitalize}.nav-btn{background:none;border:none;font-size:24px;cursor:pointer;padding:11.5px 14px;color:#666;border-radius:4px;transition:all .2s;line-height:1;height:30px;width:30px;display:flex;justify-content:center;align-items:center}.nav-btn:hover{background:#f0f0f0;color:#000}.calendar-table{width:100%;border-collapse:collapse;text-align:center}.weekday-header{font-size:12px;color:#7e7e7e;font-weight:600;padding:8px 4px;letter-spacing:.3px}.calendar-day{padding:8px 4px;font-size:14px;cursor:pointer;border-radius:6px;transition:all .2s;position:relative;color:#333;font-weight:500;line-height:1.5}.calendar-day:hover:not(.disabled):not(.other-month){background:#efefef;color:#000}.calendar-day.other-month{color:#ccc;cursor:default}.calendar-day.disabled{color:#ddd;cursor:not-allowed;opacity:.5}.calendar-day.active{background:#000!important;color:#fff!important;font-weight:600}.calendar-day.today{font-weight:600}.calendar-day.today:not(.active){background:#e5e4e4}.calendar-day.active:hover{background:#000!important}.calendar-day.in-range{background:#f5f5f5;color:#333;border-radius:0;position:relative}.calendar-day.in-range:hover{background:#e8e8e8}.calendar-day.in-range:before{content:\"\";position:absolute;inset:0;background:#f5f5f5;z-index:-1}.calendar-day.in-range:hover:before{background:#e8e8e8}.calendar-day.multi-selected{background:#4caf50;color:#fff;font-weight:600;border-radius:6px}.calendar-day.multi-selected:hover{background:#45a049}.dual-calendar{display:flex;width:100%;border-left:1px solid #eee}.calendar-left,.calendar-right{flex:1;min-width:0;padding:0 12px 12px}.calendar-popup.has-ranges{display:flex;flex-direction:row}.calendar-popup.has-ranges .ranges{margin-bottom:0;border-bottom:none;padding:10px}.calendar-popup.has-ranges .dual-calendar,.calendar-popup.has-ranges .calendar-wrapper{flex:1}.calendar-right .header{justify-content:space-between}.calendar-right .header .month-year{text-align:center;flex:1}.timepicker-section{margin-top:12px;padding-top:12px;border-top:1px solid #eee}.timepicker-label{font-size:12px;font-weight:500;color:#000;margin-bottom:4px;letter-spacing:-.28px}.custom-time-picker{display:flex;flex-direction:column;gap:8px;align-items:start}.time-input-group{display:flex;align-items:center;justify-content:center;gap:8px;background:#f8f9fa;padding:12px;border-radius:8px;border:1px solid #e0e0e0}.time-control{display:flex;flex-direction:column;align-items:center}.time-btn{background:#fff;border:1px solid #ddd;width:28px;height:20px;cursor:pointer;font-size:10px;color:#666;border-radius:4px;transition:all .2s;display:flex;align-items:center;justify-content:center;padding:0;line-height:1}.time-btn:hover{background:#e4e4e4;color:#fff;border-color:#e4e4e4}.time-btn.up{border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom:none}.time-btn.down{border-top-left-radius:0;border-top-right-radius:0;border-top:none}.time-input{width:40px;height:32px;text-align:center;border:1px solid #ddd;border-radius:4px;font-size:16px;font-weight:600;background:#fff;color:#333}.time-separator{font-size:18px;font-weight:600;color:#666;margin:0 2px}.ampm-control{display:flex;flex-direction:column;gap:4px;margin-left:8px}.ampm-btn{padding:6px 12px;border:1px solid #ddd;background:#fff;border-radius:4px;cursor:pointer;font-size:12px;font-weight:600;color:#666;transition:all .2s;min-width:45px}.ampm-btn:hover{background:#f0f0f0}.ampm-btn.active{background:#000;color:#fff;border-color:#000}.html5-time-input{margin-top:8px;padding:8px;border:1px solid #ddd;border-radius:6px;font-size:14px;width:100%;max-width:120px}.footer{padding:12px;display:flex;justify-content:flex-end;gap:8px;border-top:1px solid #eee}.btn-cancel,.btn-apply{padding:8px 16px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s;min-width:80px}.btn-cancel{background:#fff;color:#666;border:1px solid #ddd}.btn-cancel:hover{background:#f5f5f5;border-color:#bbb}.btn-apply{background:#000;color:#fff}.btn-apply:hover{background:#333}.btn-apply:active{transform:translateY(0)}@media (max-width: 768px){.calendar-popup{width:100%;max-width:320px}.calendar-popup.dual-calendar-mode{width:100%;max-width:100%}.calendar-popup.has-ranges{flex-direction:column}.calendar-popup.has-ranges .ranges{border-right:none;border-bottom:1px solid #eee;padding-right:0;margin-right:0;padding-bottom:16px;margin-bottom:16px}.dual-calendar{flex-direction:column}.time-input-group{flex-wrap:wrap;justify-content:center}}.ranges::-webkit-scrollbar{width:6px}.ranges::-webkit-scrollbar-track{background:#f1f1f1;border-radius:3px}.ranges::-webkit-scrollbar-thumb{background:#888;border-radius:3px}.ranges::-webkit-scrollbar-thumb:hover{background:#555}.w-100{width:100%}.flex-grow-1{flex-grow:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: TimePickerComponent, selector: "app-time-picker", inputs: ["value", "label", "placeholder", "position", "pickerId", "closePicker", "timeFormat", "showSeconds"], outputs: ["timeChange", "pickerOpened", "pickerClosed"] }] });
1837
+ }
1838
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CustomCalendarComponent, decorators: [{
1839
+ type: Component,
1840
+ args: [{ selector: 'app-custom-calendar', standalone: true, imports: [CommonModule, FormsModule, TimePickerComponent], template: "<div class=\"calendar-container relative\" [class.open]=\"show\" [class.inline-mode]=\"inline\">\r\n <!-- Input field -->\r\n <div class=\"input-wrapper\" *ngIf=\"!inline\">\r\n <input\r\n type=\"text\"\r\n (click)=\"toggle()\"\r\n readonly\r\n [value]=\"getDisplayValue()\"\r\n [placeholder]=\"placeholder\"\r\n class=\"calendar-input\">\r\n <!-- *ngIf=\"!getDisplayValue()\" -->\r\n <span class=\"calendar-icon\" >\r\n <img src=\"assets/calender/calender.svg\" alt=\"calendar\" class=\"calendar-icon-img\">\r\n </span>\r\n <button class=\"clear-btn\" *ngIf=\"getDisplayValue() && isDisplayCrossIcon\" (click)=\"clear(); $event.stopPropagation()\" title=\"Clear\">\u00D7</button>\r\n </div>\r\n\r\n <!-- Calendar Popup / Inline -->\r\n <div class=\"calendar-popup\"\r\n [class.inline-calendar]=\"inline\"\r\n [ngClass]=\"{\r\n 'position-right': !inline && opens === 'right',\r\n 'position-center': !inline && opens === 'center',\r\n 'drop-up': !inline && drop === 'up',\r\n 'has-ranges': showRanges && customRanges,\r\n 'dual-calendar-mode': dualCalendar\r\n }\"\r\n *ngIf=\"inline || show\">\r\n\r\n <!-- RANGES -->\r\n <div class=\"ranges\" *ngIf=\"showRanges && customRanges\">\r\n <button\r\n *ngFor=\"let rangeKey of rangeOrder\"\r\n (click)=\"chooseRange(rangeKey)\"\r\n [class.active]=\"activeRange === rangeKey\"\r\n [class.custom-range]=\"rangeKey === 'Custom Range'\"\r\n class=\"range-btn\"\r\n [disabled]=\"rangeKey === 'Custom Range'\">\r\n {{ rangeKey }}\r\n </button>\r\n </div>\r\n<div class=\"\" [ngClass]=\"showRanges ? 'w-100 flex-grow-1' : ''\">\r\n\r\n\r\n <!-- SINGLE CALENDAR -->\r\n <div *ngIf=\"!dualCalendar\" class=\"calendar-wrapper\">\r\n <div class=\"header\">\r\n <!-- <button (click)=\"prevMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/pagination-left-gray.svg\" alt=\"arrow-left\" class=\"arrow-left\">\r\n </button> -->\r\n <button class=\"nav-btn\" type=\"button\" (click)=\"prevMonth()\" matTooltip=\"Prev month\"\r\n >\r\n <img src=\"assets/calender/chevron-left.svg\" alt=\"prev\" class=\"h-3 w-3\" />\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(month) }} {{ year }}</span>\r\n <!-- <button (click)=\"nextMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/pagination-right-gray.svg\" alt=\"arrow-right\" class=\"arrow-right\">\r\n </button> -->\r\n <button class=\"nav-btn\" type=\"button\" (click)=\"nextMonth()\" matTooltip=\"Next month\"\r\n >\r\n <img src=\"assets/calender/chevron-right.svg\" alt=\"next\" class=\"h-3 w-3\" />\r\n <!--<img src=\"assets/calender/pagination-right-gray.svg\" alt=\"next\" class=\"h-3 w-3\" /> -->\r\n </button>\r\n </div>\r\n\r\n <table class=\"calendar-table\">\r\n <thead>\r\n <tr>\r\n <th *ngFor=\"let d of ['Mo','Tu','We','Th','Fr','Sa','Su']\" class=\"weekday-header\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of calendar\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(year, month, dayObj.day) && selectDate(dayObj.day)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(year, month, dayObj.day) && onDateHover(dayObj.day, false)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(year, month, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(year, month, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(year, month, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(year, month, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(year, month, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Single Calendar Time Picker -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">Time</div>\r\n <div class=\"timepicker-controls\">\r\n <app-time-picker\r\n pickerId=\"single-time\"\r\n [label]=\"''\"\r\n [value]=\"getSingleTimePickerDisplay()\"\r\n [closePicker]=\"shouldClosePicker('single-time')\"\r\n (timeChange)=\"onSingleTimePickerChange($event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- DUAL CALENDAR -->\r\n <div class=\"dual-calendar\" *ngIf=\"dualCalendar\">\r\n <!-- LEFT CALENDAR -->\r\n <div class=\"calendar-left\">\r\n <div class=\"header\">\r\n <button (click)=\"prevLeftMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/chevron-left.svg\" alt=\"arrow-left\" class=\"arrow-left\">\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(leftMonth) }} {{ leftYear }}</span>\r\n <button (click)=\"nextLeftMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/chevron-right.svg\" alt=\"arrow-right\" class=\"arrow-right\">\r\n </button>\r\n </div>\r\n <table class=\"calendar-table\">\r\n <thead>\r\n <tr>\r\n <th *ngFor=\"let d of ['Mo','Tu','We','Th','Fr','Sa','Su']\" class=\"weekday-header\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of leftCalendar\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(leftYear, leftMonth, dayObj.day) && selectDate(dayObj.day, false)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(leftYear, leftMonth, dayObj.day) && onDateHover(dayObj.day, false)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(leftYear, leftMonth, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(leftYear, leftMonth, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(leftYear, leftMonth, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(leftYear, leftMonth, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(leftYear, leftMonth, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- Start Time Picker for Dual Calendar -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">Start Time</div>\r\n <div class=\"timepicker-controls\">\r\n <app-time-picker\r\n pickerId=\"dual-start\"\r\n [label]=\"''\"\r\n [value]=\"getDualTimePickerDisplay(true)\"\r\n [closePicker]=\"shouldClosePicker('dual-start')\"\r\n (timeChange)=\"onDualTimePickerChange($event, true)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- RIGHT CALENDAR -->\r\n <div class=\"calendar-right\">\r\n <div class=\"header\">\r\n <button (click)=\"prevRightMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/chevron-left.svg\" alt=\"arrow-left\" class=\"arrow-left\">\r\n </button>\r\n <span class=\"month-year\">{{ getMonthName(rightMonth) }} {{ rightYear }}</span>\r\n <button (click)=\"nextRightMonth()\" class=\"nav-btn\" type=\"button\">\r\n <img src=\"assets/calender/chevron-right.svg\" alt=\"arrow-right\" class=\"arrow-right\">\r\n </button>\r\n </div>\r\n <table class=\"calendar-table\">\r\n <thead>\r\n <tr>\r\n <th *ngFor=\"let d of ['Mo','Tu','We','Th','Fr','Sa','Su']\" class=\"weekday-header\">{{ d }}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let week of rightCalendar\">\r\n <td\r\n *ngFor=\"let dayObj of week\"\r\n (click)=\"dayObj.currentMonth && !isDateDisabled(rightYear, rightMonth, dayObj.day) && selectDate(dayObj.day, true)\"\r\n (mouseenter)=\"dayObj.currentMonth && !isDateDisabled(rightYear, rightMonth, dayObj.day) && onDateHover(dayObj.day, true)\"\r\n (mouseleave)=\"onDateLeave()\"\r\n [class.active]=\"dayObj.currentMonth && isDateSelected(rightYear, rightMonth, dayObj.day)\"\r\n [class.in-range]=\"dayObj.currentMonth && isDateInRange(rightYear, rightMonth, dayObj.day)\"\r\n [class.other-month]=\"!dayObj.currentMonth\"\r\n [class.disabled]=\"isDateDisabled(rightYear, rightMonth, dayObj.day)\"\r\n [class.multi-selected]=\"multiDateSelection && isDateInMultiSelection(rightYear, rightMonth, dayObj.day)\"\r\n [class.today]=\"dayObj.currentMonth && isToday(rightYear, rightMonth, dayObj.day)\"\r\n class=\"calendar-day\">\r\n {{ dayObj.day }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <!-- End Time Picker for Dual Calendar -->\r\n <div *ngIf=\"enableTimepicker\" class=\"timepicker-section\">\r\n <div class=\"timepicker-label\">End Time</div>\r\n <div class=\"timepicker-controls\">\r\n <app-time-picker\r\n pickerId=\"dual-end\"\r\n [label]=\"''\"\r\n [value]=\"getDualTimePickerDisplay(false)\"\r\n [closePicker]=\"shouldClosePicker('dual-end')\"\r\n (timeChange)=\"onDualTimePickerChange($event, false)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- FOOTER -->\r\n <div class=\"footer\" *ngIf=\"!inline\">\r\n <button *ngIf=\"showCancel\" (click)=\"cancel()\" class=\"btn-cancel\" type=\"button\">Cancel</button>\r\n <button (click)=\"apply()\" class=\"btn-apply\" type=\"button\">Apply</button>\r\n </div>\r\n\r\n </div>\r\n\r\n </div>\r\n</div>\r\n", styles: [".calendar-container,.calendar-container *{font-family:Inter,sans-serif!important}.calendar-container{position:relative;display:inline-block;width:100%}.input-wrapper{position:relative;display:flex;align-items:center}.calendar-input{width:100%;padding:9px 14px 9px 40px;border:1px solid #ddd;border-radius:8px;font-size:14px;cursor:pointer;background:#fff;transition:all .2s}.calendar-input:hover{border-color:#999}.calendar-input:focus{outline:none;border-color:#999;box-shadow:0 0 0 3px #6a6a6a1a}.calendar-icon{position:absolute;left:12px;pointer-events:none;font-size:18px}.clear-btn{position:absolute;right:9px;background:none;border:none;font-size:20px;color:#999;cursor:pointer;padding:0;width:20px;height:20px;display:flex;align-items:center;justify-content:center;line-height:1;transition:color .2s;top:8px}.clear-btn:hover{color:#333}.calendar-popup{position:absolute;top:110%;left:0;width:320px;background:#fff;border-radius:12px;box-shadow:0 10px 40px #00000026;z-index:1000;animation:slideDown .2s ease-out}.calendar-popup.inline-calendar{position:relative;top:0;left:0;width:100%;margin-top:0;animation:none;box-shadow:0 2px 8px #0000001a}.calendar-container.inline-mode{display:block;width:100%}.calendar-popup.dual-calendar-mode{width:600px}.calendar-popup.dual-calendar-mode.has-ranges{width:730px}.calendar-popup.has-ranges{width:450px}.calendar-popup.dual-calendar-mode.has-ranges .dual-calendar{border-left:1px solid #eee}.calendar-popup.drop-up{top:auto;bottom:110%;animation:slideUp .2s ease-out}.calendar-popup.position-right{left:auto;right:0}.calendar-popup.position-center{left:50%;transform:translate(-50%)}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.ranges{display:flex;flex-direction:column;gap:4px;margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid #eee;min-width:150px;padding-right:8px}.range-btn{padding:7px 10px;border:1px solid transparent;background:transparent;border-radius:4px;cursor:pointer;text-align:left;font-size:14px;transition:all .2s;color:#838383;font-weight:500}.range-btn:hover{background:#f5f5f5;color:#000}.range-btn.active{background:#f0f0f0;color:#000;font-weight:500}.calendar-wrapper{padding:0 12px 12px;border-left:1px solid #eee}.header{display:flex;justify-content:space-between;align-items:center;padding:12px 0}.month-year{font-size:15px;font-weight:500;color:#333;flex:1;text-align:center;text-transform:capitalize}.nav-btn{background:none;border:none;font-size:24px;cursor:pointer;padding:11.5px 14px;color:#666;border-radius:4px;transition:all .2s;line-height:1;height:30px;width:30px;display:flex;justify-content:center;align-items:center}.nav-btn:hover{background:#f0f0f0;color:#000}.calendar-table{width:100%;border-collapse:collapse;text-align:center}.weekday-header{font-size:12px;color:#7e7e7e;font-weight:600;padding:8px 4px;letter-spacing:.3px}.calendar-day{padding:8px 4px;font-size:14px;cursor:pointer;border-radius:6px;transition:all .2s;position:relative;color:#333;font-weight:500;line-height:1.5}.calendar-day:hover:not(.disabled):not(.other-month){background:#efefef;color:#000}.calendar-day.other-month{color:#ccc;cursor:default}.calendar-day.disabled{color:#ddd;cursor:not-allowed;opacity:.5}.calendar-day.active{background:#000!important;color:#fff!important;font-weight:600}.calendar-day.today{font-weight:600}.calendar-day.today:not(.active){background:#e5e4e4}.calendar-day.active:hover{background:#000!important}.calendar-day.in-range{background:#f5f5f5;color:#333;border-radius:0;position:relative}.calendar-day.in-range:hover{background:#e8e8e8}.calendar-day.in-range:before{content:\"\";position:absolute;inset:0;background:#f5f5f5;z-index:-1}.calendar-day.in-range:hover:before{background:#e8e8e8}.calendar-day.multi-selected{background:#4caf50;color:#fff;font-weight:600;border-radius:6px}.calendar-day.multi-selected:hover{background:#45a049}.dual-calendar{display:flex;width:100%;border-left:1px solid #eee}.calendar-left,.calendar-right{flex:1;min-width:0;padding:0 12px 12px}.calendar-popup.has-ranges{display:flex;flex-direction:row}.calendar-popup.has-ranges .ranges{margin-bottom:0;border-bottom:none;padding:10px}.calendar-popup.has-ranges .dual-calendar,.calendar-popup.has-ranges .calendar-wrapper{flex:1}.calendar-right .header{justify-content:space-between}.calendar-right .header .month-year{text-align:center;flex:1}.timepicker-section{margin-top:12px;padding-top:12px;border-top:1px solid #eee}.timepicker-label{font-size:12px;font-weight:500;color:#000;margin-bottom:4px;letter-spacing:-.28px}.custom-time-picker{display:flex;flex-direction:column;gap:8px;align-items:start}.time-input-group{display:flex;align-items:center;justify-content:center;gap:8px;background:#f8f9fa;padding:12px;border-radius:8px;border:1px solid #e0e0e0}.time-control{display:flex;flex-direction:column;align-items:center}.time-btn{background:#fff;border:1px solid #ddd;width:28px;height:20px;cursor:pointer;font-size:10px;color:#666;border-radius:4px;transition:all .2s;display:flex;align-items:center;justify-content:center;padding:0;line-height:1}.time-btn:hover{background:#e4e4e4;color:#fff;border-color:#e4e4e4}.time-btn.up{border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom:none}.time-btn.down{border-top-left-radius:0;border-top-right-radius:0;border-top:none}.time-input{width:40px;height:32px;text-align:center;border:1px solid #ddd;border-radius:4px;font-size:16px;font-weight:600;background:#fff;color:#333}.time-separator{font-size:18px;font-weight:600;color:#666;margin:0 2px}.ampm-control{display:flex;flex-direction:column;gap:4px;margin-left:8px}.ampm-btn{padding:6px 12px;border:1px solid #ddd;background:#fff;border-radius:4px;cursor:pointer;font-size:12px;font-weight:600;color:#666;transition:all .2s;min-width:45px}.ampm-btn:hover{background:#f0f0f0}.ampm-btn.active{background:#000;color:#fff;border-color:#000}.html5-time-input{margin-top:8px;padding:8px;border:1px solid #ddd;border-radius:6px;font-size:14px;width:100%;max-width:120px}.footer{padding:12px;display:flex;justify-content:flex-end;gap:8px;border-top:1px solid #eee}.btn-cancel,.btn-apply{padding:8px 16px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s;min-width:80px}.btn-cancel{background:#fff;color:#666;border:1px solid #ddd}.btn-cancel:hover{background:#f5f5f5;border-color:#bbb}.btn-apply{background:#000;color:#fff}.btn-apply:hover{background:#333}.btn-apply:active{transform:translateY(0)}@media (max-width: 768px){.calendar-popup{width:100%;max-width:320px}.calendar-popup.dual-calendar-mode{width:100%;max-width:100%}.calendar-popup.has-ranges{flex-direction:column}.calendar-popup.has-ranges .ranges{border-right:none;border-bottom:1px solid #eee;padding-right:0;margin-right:0;padding-bottom:16px;margin-bottom:16px}.dual-calendar{flex-direction:column}.time-input-group{flex-wrap:wrap;justify-content:center}}.ranges::-webkit-scrollbar{width:6px}.ranges::-webkit-scrollbar-track{background:#f1f1f1;border-radius:3px}.ranges::-webkit-scrollbar-thumb{background:#888;border-radius:3px}.ranges::-webkit-scrollbar-thumb:hover{background:#555}.w-100{width:100%}.flex-grow-1{flex-grow:1}\n"] }]
1841
+ }], ctorParameters: () => [{ type: CalendarManagerService }], propDecorators: { enableTimepicker: [{
1842
+ type: Input
1843
+ }], autoApply: [{
1844
+ type: Input
1845
+ }], closeOnAutoApply: [{
1846
+ type: Input
1847
+ }], showCancel: [{
1848
+ type: Input
1849
+ }], linkedCalendars: [{
1850
+ type: Input
1851
+ }], singleDatePicker: [{
1852
+ type: Input
1853
+ }], showWeekNumbers: [{
1854
+ type: Input
1855
+ }], showISOWeekNumbers: [{
1856
+ type: Input
1857
+ }], customRangeDirection: [{
1858
+ type: Input
1859
+ }], lockStartDate: [{
1860
+ type: Input
1861
+ }], position: [{
1862
+ type: Input
1863
+ }], drop: [{
1864
+ type: Input
1865
+ }], dualCalendar: [{
1866
+ type: Input
1867
+ }], showRanges: [{
1868
+ type: Input
1869
+ }], timeFormat: [{
1870
+ type: Input
1871
+ }], enableSeconds: [{
1872
+ type: Input
1873
+ }], customRanges: [{
1874
+ type: Input
1875
+ }], multiDateSelection: [{
1876
+ type: Input
1877
+ }], maxDate: [{
1878
+ type: Input
1879
+ }], minDate: [{
1880
+ type: Input
1881
+ }], placeholder: [{
1882
+ type: Input
1883
+ }], opens: [{
1884
+ type: Input
1885
+ }], inline: [{
1886
+ type: Input
1887
+ }], isDisplayCrossIcon: [{
1888
+ type: Input
1889
+ }], selected: [{
1890
+ type: Output
1891
+ }], opened: [{
1892
+ type: Output
1893
+ }], closed: [{
1894
+ type: Output
1895
+ }], selectedValue: [{
1896
+ type: Input
1897
+ }], displayFormat: [{
1898
+ type: Input
1899
+ }], onClickOutside: [{
1900
+ type: HostListener,
1901
+ args: ['document:click', ['$event']]
1902
+ }] } });
1903
+
1904
+ class CalendarComponent {
1905
+ // Mirror `CustomCalendarComponent` inputs/outputs so consumers can use `<lib-calendar>` as a drop-in.
1906
+ enableTimepicker = false;
1907
+ autoApply = false;
1908
+ closeOnAutoApply = false;
1909
+ showCancel = true;
1910
+ linkedCalendars = false;
1911
+ singleDatePicker = false;
1912
+ showWeekNumbers = false;
1913
+ showISOWeekNumbers = false;
1914
+ customRangeDirection = false;
1915
+ lockStartDate = false;
1916
+ position = 'left';
1917
+ drop = 'down';
1918
+ dualCalendar = false;
1919
+ showRanges = true;
1920
+ timeFormat = 24;
1921
+ enableSeconds = false;
1922
+ customRanges;
1923
+ multiDateSelection = false;
1924
+ maxDate;
1925
+ minDate;
1926
+ placeholder = 'Select date range';
1927
+ opens = 'left';
1928
+ inline = false;
1929
+ isDisplayCrossIcon = true;
1930
+ selectedValue = null;
1931
+ displayFormat = 'MM/DD/YYYY';
1932
+ selected = new EventEmitter();
1933
+ opened = new EventEmitter();
1934
+ closed = new EventEmitter();
1935
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CalendarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1936
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.16", type: CalendarComponent, isStandalone: true, selector: "lib-calendar", inputs: { enableTimepicker: "enableTimepicker", autoApply: "autoApply", closeOnAutoApply: "closeOnAutoApply", showCancel: "showCancel", linkedCalendars: "linkedCalendars", singleDatePicker: "singleDatePicker", showWeekNumbers: "showWeekNumbers", showISOWeekNumbers: "showISOWeekNumbers", customRangeDirection: "customRangeDirection", lockStartDate: "lockStartDate", position: "position", drop: "drop", dualCalendar: "dualCalendar", showRanges: "showRanges", timeFormat: "timeFormat", enableSeconds: "enableSeconds", customRanges: "customRanges", multiDateSelection: "multiDateSelection", maxDate: "maxDate", minDate: "minDate", placeholder: "placeholder", opens: "opens", inline: "inline", isDisplayCrossIcon: "isDisplayCrossIcon", selectedValue: "selectedValue", displayFormat: "displayFormat" }, outputs: { selected: "selected", opened: "opened", closed: "closed" }, ngImport: i0, template: `
1937
+ <app-custom-calendar
1938
+ [enableTimepicker]="enableTimepicker"
1939
+ [autoApply]="autoApply"
1940
+ [closeOnAutoApply]="closeOnAutoApply"
1941
+ [showCancel]="showCancel"
1942
+ [linkedCalendars]="linkedCalendars"
1943
+ [singleDatePicker]="singleDatePicker"
1944
+ [showWeekNumbers]="showWeekNumbers"
1945
+ [showISOWeekNumbers]="showISOWeekNumbers"
1946
+ [customRangeDirection]="customRangeDirection"
1947
+ [lockStartDate]="lockStartDate"
1948
+ [position]="position"
1949
+ [drop]="drop"
1950
+ [dualCalendar]="dualCalendar"
1951
+ [showRanges]="showRanges"
1952
+ [timeFormat]="timeFormat"
1953
+ [enableSeconds]="enableSeconds"
1954
+ [customRanges]="customRanges"
1955
+ [multiDateSelection]="multiDateSelection"
1956
+ [maxDate]="maxDate"
1957
+ [minDate]="minDate"
1958
+ [placeholder]="placeholder"
1959
+ [opens]="opens"
1960
+ [inline]="inline"
1961
+ [isDisplayCrossIcon]="isDisplayCrossIcon"
1962
+ [selectedValue]="selectedValue"
1963
+ [displayFormat]="displayFormat"
1964
+ (selected)="selected.emit($event)"
1965
+ (opened)="opened.emit()"
1966
+ (closed)="closed.emit()"
1967
+ />
1968
+ `, isInline: true, styles: [""], dependencies: [{ kind: "component", type: CustomCalendarComponent, selector: "app-custom-calendar", inputs: ["enableTimepicker", "autoApply", "closeOnAutoApply", "showCancel", "linkedCalendars", "singleDatePicker", "showWeekNumbers", "showISOWeekNumbers", "customRangeDirection", "lockStartDate", "position", "drop", "dualCalendar", "showRanges", "timeFormat", "enableSeconds", "customRanges", "multiDateSelection", "maxDate", "minDate", "placeholder", "opens", "inline", "isDisplayCrossIcon", "selectedValue", "displayFormat"], outputs: ["selected", "opened", "closed"] }] });
1969
+ }
1970
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CalendarComponent, decorators: [{
1971
+ type: Component,
1972
+ args: [{ selector: 'lib-calendar', standalone: true, imports: [CustomCalendarComponent], template: `
1973
+ <app-custom-calendar
1974
+ [enableTimepicker]="enableTimepicker"
1975
+ [autoApply]="autoApply"
1976
+ [closeOnAutoApply]="closeOnAutoApply"
1977
+ [showCancel]="showCancel"
1978
+ [linkedCalendars]="linkedCalendars"
1979
+ [singleDatePicker]="singleDatePicker"
1980
+ [showWeekNumbers]="showWeekNumbers"
1981
+ [showISOWeekNumbers]="showISOWeekNumbers"
1982
+ [customRangeDirection]="customRangeDirection"
1983
+ [lockStartDate]="lockStartDate"
1984
+ [position]="position"
1985
+ [drop]="drop"
1986
+ [dualCalendar]="dualCalendar"
1987
+ [showRanges]="showRanges"
1988
+ [timeFormat]="timeFormat"
1989
+ [enableSeconds]="enableSeconds"
1990
+ [customRanges]="customRanges"
1991
+ [multiDateSelection]="multiDateSelection"
1992
+ [maxDate]="maxDate"
1993
+ [minDate]="minDate"
1994
+ [placeholder]="placeholder"
1995
+ [opens]="opens"
1996
+ [inline]="inline"
1997
+ [isDisplayCrossIcon]="isDisplayCrossIcon"
1998
+ [selectedValue]="selectedValue"
1999
+ [displayFormat]="displayFormat"
2000
+ (selected)="selected.emit($event)"
2001
+ (opened)="opened.emit()"
2002
+ (closed)="closed.emit()"
2003
+ />
2004
+ ` }]
2005
+ }], propDecorators: { enableTimepicker: [{
2006
+ type: Input
2007
+ }], autoApply: [{
2008
+ type: Input
2009
+ }], closeOnAutoApply: [{
2010
+ type: Input
2011
+ }], showCancel: [{
2012
+ type: Input
2013
+ }], linkedCalendars: [{
2014
+ type: Input
2015
+ }], singleDatePicker: [{
2016
+ type: Input
2017
+ }], showWeekNumbers: [{
2018
+ type: Input
2019
+ }], showISOWeekNumbers: [{
2020
+ type: Input
2021
+ }], customRangeDirection: [{
2022
+ type: Input
2023
+ }], lockStartDate: [{
2024
+ type: Input
2025
+ }], position: [{
2026
+ type: Input
2027
+ }], drop: [{
2028
+ type: Input
2029
+ }], dualCalendar: [{
2030
+ type: Input
2031
+ }], showRanges: [{
2032
+ type: Input
2033
+ }], timeFormat: [{
2034
+ type: Input
2035
+ }], enableSeconds: [{
2036
+ type: Input
2037
+ }], customRanges: [{
2038
+ type: Input
2039
+ }], multiDateSelection: [{
2040
+ type: Input
2041
+ }], maxDate: [{
2042
+ type: Input
2043
+ }], minDate: [{
2044
+ type: Input
2045
+ }], placeholder: [{
2046
+ type: Input
2047
+ }], opens: [{
2048
+ type: Input
2049
+ }], inline: [{
2050
+ type: Input
2051
+ }], isDisplayCrossIcon: [{
2052
+ type: Input
2053
+ }], selectedValue: [{
2054
+ type: Input
2055
+ }], displayFormat: [{
2056
+ type: Input
2057
+ }], selected: [{
2058
+ type: Output
2059
+ }], opened: [{
2060
+ type: Output
2061
+ }], closed: [{
2062
+ type: Output
2063
+ }] } });
2064
+
2065
+ class ScheduledDatePickerComponent {
2066
+ timeFormat = 12;
2067
+ enableSeconds = false;
2068
+ scheduled = new EventEmitter();
2069
+ cleared = new EventEmitter();
2070
+ activeTab = 'single';
2071
+ openTimePickerId = null; // Track which time picker is currently open
2072
+ closePickerCounter = {}; // Track close signals for each picker
2073
+ // Single Date
2074
+ singleDate = null;
2075
+ singleAllDay = false;
2076
+ singleStartTime = '1:00 AM';
2077
+ singleEndTime = '2:00 AM';
2078
+ // Multiple Dates
2079
+ multipleDates = [];
2080
+ // Date Range
2081
+ rangeStartDate = null;
2082
+ rangeEndDate = null;
2083
+ rangeAllDay = false;
2084
+ rangeStartTime = '1:00 AM';
2085
+ rangeEndTime = '2:00 AM';
2086
+ ngOnInit() {
2087
+ // Initialize with default time if needed
2088
+ }
2089
+ onTabChange(tab) {
2090
+ this.activeTab = tab;
2091
+ this.openTimePickerId = null; // Close any open pickers when switching tabs
2092
+ }
2093
+ onTimePickerOpened(pickerId) {
2094
+ // Close all other pickers when one opens
2095
+ if (this.openTimePickerId && this.openTimePickerId !== pickerId) {
2096
+ // Increment close counter for the previously open picker
2097
+ if (!this.closePickerCounter[this.openTimePickerId]) {
2098
+ this.closePickerCounter[this.openTimePickerId] = 0;
2099
+ }
2100
+ this.closePickerCounter[this.openTimePickerId]++;
2101
+ }
2102
+ this.openTimePickerId = pickerId;
2103
+ }
2104
+ onTimePickerClosed(pickerId) {
2105
+ // Reset open picker ID if this was the open one
2106
+ if (this.openTimePickerId === pickerId) {
2107
+ this.openTimePickerId = null;
2108
+ }
2109
+ }
2110
+ shouldClosePicker(pickerId) {
2111
+ // Return the counter value for this picker (triggers change detection)
2112
+ return this.closePickerCounter[pickerId] || 0;
2113
+ }
2114
+ // Single Date Handlers
2115
+ onSingleDateSelected(event) {
2116
+ if (event.startDate) {
2117
+ this.singleDate = new Date(event.startDate);
2118
+ // Initialize time if not all day
2119
+ if (!this.singleAllDay) {
2120
+ this.updateSingleDateTimes();
2121
+ }
2122
+ this.emitScheduled();
2123
+ }
2124
+ else {
2125
+ this.singleDate = null;
2126
+ }
2127
+ }
2128
+ onSingleAllDayChange() {
2129
+ this.singleAllDay = !this.singleAllDay;
2130
+ if (this.singleDate) {
2131
+ this.updateSingleDateTimes();
2132
+ this.emitScheduled();
2133
+ }
2134
+ }
2135
+ onSingleStartTimeChange(time) {
2136
+ this.singleStartTime = time;
2137
+ if (this.singleDate) {
2138
+ this.updateSingleDateTimes();
2139
+ this.emitScheduled();
2140
+ }
2141
+ }
2142
+ onSingleEndTimeChange(time) {
2143
+ this.singleEndTime = time;
2144
+ if (this.singleDate) {
2145
+ this.updateSingleDateTimes();
2146
+ this.emitScheduled();
2147
+ }
2148
+ }
2149
+ updateSingleDateTimes() {
2150
+ if (!this.singleDate)
2151
+ return;
2152
+ if (this.singleAllDay) {
2153
+ this.singleDate.setHours(0, 0, 0, 0);
2154
+ }
2155
+ else {
2156
+ const startTime = this.parseTimeString(this.singleStartTime);
2157
+ const endTime = this.parseTimeString(this.singleEndTime);
2158
+ this.singleDate.setHours(startTime.hours, startTime.minutes, 0, 0);
2159
+ }
2160
+ }
2161
+ // Multiple Dates Handlers
2162
+ onMultipleDatesSelected(event) {
2163
+ if (event.selectedDates && event.selectedDates.length > 0) {
2164
+ const newDates = [];
2165
+ event.selectedDates.forEach(date => {
2166
+ const dateStr = this.getDateString(date);
2167
+ const existing = this.multipleDates.find(d => this.getDateString(d.date) === dateStr);
2168
+ if (existing) {
2169
+ newDates.push(existing);
2170
+ }
2171
+ else {
2172
+ // Create new time configuration for this date
2173
+ newDates.push({
2174
+ date: new Date(date),
2175
+ allDay: false,
2176
+ startTime: '1:00 AM',
2177
+ endTime: '2:00 AM'
2178
+ });
2179
+ }
2180
+ });
2181
+ this.multipleDates = newDates;
2182
+ this.emitScheduled();
2183
+ }
2184
+ else {
2185
+ this.multipleDates = [];
2186
+ this.emitScheduled();
2187
+ }
2188
+ }
2189
+ onMultipleDateAllDayChange(index) {
2190
+ if (this.multipleDates[index]) {
2191
+ this.multipleDates[index].allDay = !this.multipleDates[index].allDay;
2192
+ if (this.multipleDates[index].allDay) {
2193
+ this.multipleDates[index].date.setHours(0, 0, 0, 0);
2194
+ }
2195
+ else {
2196
+ const time = this.parseTimeString(this.multipleDates[index].startTime);
2197
+ this.multipleDates[index].date.setHours(time.hours, time.minutes, 0, 0);
2198
+ }
2199
+ this.emitScheduled();
2200
+ }
2201
+ }
2202
+ onMultipleDateStartTimeChange(index, time) {
2203
+ if (this.multipleDates[index]) {
2204
+ this.multipleDates[index].startTime = time;
2205
+ if (!this.multipleDates[index].allDay) {
2206
+ const parsed = this.parseTimeString(time);
2207
+ this.multipleDates[index].date.setHours(parsed.hours, parsed.minutes, 0, 0);
2208
+ }
2209
+ this.emitScheduled();
2210
+ }
2211
+ }
2212
+ onMultipleDateEndTimeChange(index, time) {
2213
+ if (this.multipleDates[index]) {
2214
+ this.multipleDates[index].endTime = time;
2215
+ this.emitScheduled();
2216
+ }
2217
+ }
2218
+ // Date Range Handlers
2219
+ onRangeSelected(event) {
2220
+ if (event.startDate && event.endDate) {
2221
+ this.rangeStartDate = new Date(event.startDate);
2222
+ this.rangeEndDate = new Date(event.endDate);
2223
+ if (!this.rangeAllDay) {
2224
+ this.updateRangeTimes();
2225
+ }
2226
+ this.emitScheduled();
2227
+ }
2228
+ else {
2229
+ this.rangeStartDate = null;
2230
+ this.rangeEndDate = null;
2231
+ }
2232
+ }
2233
+ onRangeAllDayChange() {
2234
+ this.rangeAllDay = !this.rangeAllDay;
2235
+ if (this.rangeStartDate && this.rangeEndDate) {
2236
+ this.updateRangeTimes();
2237
+ this.emitScheduled();
2238
+ }
2239
+ }
2240
+ onRangeStartTimeChange(time) {
2241
+ this.rangeStartTime = time;
2242
+ if (this.rangeStartDate && !this.rangeAllDay) {
2243
+ this.updateRangeTimes();
2244
+ this.emitScheduled();
2245
+ }
2246
+ }
2247
+ onRangeEndTimeChange(time) {
2248
+ this.rangeEndTime = time;
2249
+ if (this.rangeEndDate && !this.rangeAllDay) {
2250
+ this.updateRangeTimes();
2251
+ this.emitScheduled();
2252
+ }
2253
+ }
2254
+ updateRangeTimes() {
2255
+ if (!this.rangeStartDate || !this.rangeEndDate)
2256
+ return;
2257
+ if (this.rangeAllDay) {
2258
+ this.rangeStartDate.setHours(0, 0, 0, 0);
2259
+ this.rangeEndDate.setHours(23, 59, 59, 999);
2260
+ }
2261
+ else {
2262
+ const startTime = this.parseTimeString(this.rangeStartTime);
2263
+ const endTime = this.parseTimeString(this.rangeEndTime);
2264
+ this.rangeStartDate.setHours(startTime.hours, startTime.minutes, 0, 0);
2265
+ this.rangeEndDate.setHours(endTime.hours, endTime.minutes, 0, 0);
2266
+ }
2267
+ }
2268
+ // Utility Methods
2269
+ parseTimeString(timeStr) {
2270
+ // Parse time string like "7:01 AM" or "19:01"
2271
+ const parts = timeStr.split(' ');
2272
+ let timePart = parts[0];
2273
+ const ampm = parts[1];
2274
+ const [hoursStr, minutesStr] = timePart.split(':');
2275
+ let hours = parseInt(hoursStr, 10);
2276
+ const minutes = parseInt(minutesStr || '0', 10);
2277
+ if (ampm) {
2278
+ if (ampm.toUpperCase() === 'PM' && hours !== 12) {
2279
+ hours += 12;
2280
+ }
2281
+ else if (ampm.toUpperCase() === 'AM' && hours === 12) {
2282
+ hours = 0;
2283
+ }
2284
+ }
2285
+ return { hours, minutes };
2286
+ }
2287
+ getDateString(date) {
2288
+ return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
2289
+ }
2290
+ formatDate(date) {
2291
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
2292
+ return `${months[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;
2293
+ }
2294
+ emitScheduled() {
2295
+ const selection = {
2296
+ mode: this.activeTab
2297
+ };
2298
+ if (this.activeTab === 'single' && this.singleDate) {
2299
+ // For single date, create startDate and endDate with same date but different times
2300
+ const startDate = new Date(this.singleDate);
2301
+ const endDate = new Date(this.singleDate);
2302
+ if (!this.singleAllDay) {
2303
+ const startTime = this.parseTimeString(this.singleStartTime);
2304
+ const endTime = this.parseTimeString(this.singleEndTime);
2305
+ startDate.setHours(startTime.hours, startTime.minutes, 0, 0);
2306
+ endDate.setHours(endTime.hours, endTime.minutes, 0, 0);
2307
+ }
2308
+ else {
2309
+ startDate.setHours(0, 0, 0, 0);
2310
+ endDate.setHours(23, 59, 59, 999);
2311
+ }
2312
+ selection.singleDate = {
2313
+ startDate: startDate,
2314
+ endDate: endDate,
2315
+ allDay: this.singleAllDay,
2316
+ startTime: this.singleStartTime,
2317
+ endTime: this.singleEndTime
2318
+ };
2319
+ }
2320
+ else if (this.activeTab === 'multiple') {
2321
+ selection.multipleDates = this.multipleDates.map(d => ({
2322
+ date: new Date(d.date),
2323
+ allDay: d.allDay,
2324
+ startTime: d.startTime,
2325
+ endTime: d.endTime
2326
+ }));
2327
+ }
2328
+ else if (this.activeTab === 'range' && this.rangeStartDate && this.rangeEndDate) {
2329
+ selection.dateRange = {
2330
+ startDate: new Date(this.rangeStartDate),
2331
+ endDate: new Date(this.rangeEndDate),
2332
+ allDay: this.rangeAllDay,
2333
+ startTime: this.rangeStartTime,
2334
+ endTime: this.rangeEndTime
2335
+ };
2336
+ }
2337
+ this.scheduled.emit(selection);
2338
+ }
2339
+ clear() {
2340
+ this.singleDate = null;
2341
+ this.multipleDates = [];
2342
+ this.rangeStartDate = null;
2343
+ this.rangeEndDate = null;
2344
+ this.singleAllDay = false;
2345
+ this.rangeAllDay = false;
2346
+ this.singleStartTime = '1:00 AM';
2347
+ this.singleEndTime = '2:00 AM';
2348
+ this.rangeStartTime = '1:00 AM';
2349
+ this.rangeEndTime = '2:00 AM';
2350
+ this.cleared.emit();
2351
+ }
2352
+ apply() {
2353
+ this.emitScheduled();
2354
+ }
2355
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: ScheduledDatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2356
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.16", type: ScheduledDatePickerComponent, isStandalone: true, selector: "app-scheduled-date-picker", inputs: { timeFormat: "timeFormat", enableSeconds: "enableSeconds" }, outputs: { scheduled: "scheduled", cleared: "cleared" }, ngImport: i0, template: "<div class=\"scheduled-date-picker-container\">\r\n <!-- Header with Tabs -->\r\n\r\n\r\n <!-- Main Content Area -->\r\n \r\n <div class=\"scheduled-content\">\r\n <!-- Left Side: Calendar -->\r\n <div class=\"calendar-section\">\r\n <h2 class=\"scheduled-title\">Scheduled Dates</h2>\r\n <div class=\"tabs\">\r\n <button \r\n class=\"tab-button\" \r\n [class.active]=\"activeTab === 'single'\"\r\n (click)=\"onTabChange('single')\">\r\n Single Date\r\n </button>\r\n <button \r\n class=\"tab-button\" \r\n [class.active]=\"activeTab === 'multiple'\"\r\n (click)=\"onTabChange('multiple')\">\r\n Multiple Dates\r\n </button>\r\n <button \r\n class=\"tab-button\" \r\n [class.active]=\"activeTab === 'range'\"\r\n (click)=\"onTabChange('range')\">\r\n Date Range\r\n </button>\r\n </div>\r\n <!-- Single Date Calendar -->\r\n <div *ngIf=\"activeTab === 'single'\" class=\"calendar-wrapper-inline\">\r\n <app-custom-calendar\r\n [inline]=\"true\"\r\n [dualCalendar]=\"false\"\r\n [singleDatePicker]=\"true\"\r\n [showRanges]=\"false\"\r\n [enableTimepicker]=\"false\"\r\n [showCancel]=\"false\"\r\n placeholder=\"Select a date\"\r\n (selected)=\"onSingleDateSelected($event)\">\r\n </app-custom-calendar>\r\n </div>\r\n\r\n <!-- Multiple Dates Calendar -->\r\n <div *ngIf=\"activeTab === 'multiple'\" class=\"calendar-wrapper-inline\">\r\n <app-custom-calendar\r\n [inline]=\"true\"\r\n [dualCalendar]=\"false\"\r\n [singleDatePicker]=\"false\"\r\n [showRanges]=\"false\"\r\n [enableTimepicker]=\"false\"\r\n [multiDateSelection]=\"true\"\r\n [showCancel]=\"false\"\r\n placeholder=\"Select multiple dates\"\r\n (selected)=\"onMultipleDatesSelected($event)\">\r\n </app-custom-calendar>\r\n </div>\r\n\r\n <!-- Date Range Calendar -->\r\n <div *ngIf=\"activeTab === 'range'\" class=\"calendar-wrapper-inline\">\r\n <app-custom-calendar\r\n [inline]=\"true\"\r\n [dualCalendar]=\"false\"\r\n [singleDatePicker]=\"false\"\r\n [showRanges]=\"false\"\r\n [enableTimepicker]=\"false\"\r\n [showCancel]=\"false\"\r\n placeholder=\"Select date range\"\r\n (selected)=\"onRangeSelected($event)\">\r\n </app-custom-calendar>\r\n </div>\r\n </div>\r\n\r\n <!-- Right Side: Time Configuration -->\r\n <div class=\"time-config-section\">\r\n <h3 class=\"time-config-title\">Time Configuration</h3>\r\n\r\n <!-- Single Date Time Configuration -->\r\n <div *ngIf=\"activeTab === 'single'\">\r\n <div *ngIf=\"singleDate\" class=\"time-config-item\">\r\n <div class=\"time-config-header\">\r\n <span class=\"date-label\">{{ formatDate(singleDate) }}</span>\r\n <label class=\"all-day-toggle\">\r\n <span class=\"toggle-label\">All Day</span>\r\n <input \r\n type=\"checkbox\" \r\n [checked]=\"singleAllDay\"\r\n (change)=\"onSingleAllDayChange()\">\r\n </label>\r\n </div>\r\n <div *ngIf=\"!singleAllDay\" class=\"time-inputs\">\r\n <app-time-picker\r\n pickerId=\"single-start\"\r\n label=\"Start Time\"\r\n [value]=\"singleStartTime\"\r\n [position]=\"'left'\"\r\n [closePicker]=\"shouldClosePicker('single-start')\"\r\n (timeChange)=\"onSingleStartTimeChange($event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n <app-time-picker\r\n pickerId=\"single-end\"\r\n label=\"End Time\"\r\n [value]=\"singleEndTime\"\r\n [position]=\"'right'\"\r\n [closePicker]=\"shouldClosePicker('single-end')\"\r\n (timeChange)=\"onSingleEndTimeChange($event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n </div>\r\n </div>\r\n <div *ngIf=\"!singleDate\" class=\"no-selection\">\r\n <p>No date selected. Select a date from the calendar.</p>\r\n </div>\r\n </div>\r\n\r\n <!-- Multiple Dates Time Configuration -->\r\n <div *ngIf=\"activeTab === 'multiple'\" class=\"time-config-list\">\r\n <div \r\n *ngFor=\"let dateConfig of multipleDates; let i = index\" \r\n class=\"time-config-item\">\r\n <div class=\"time-config-header\">\r\n <span class=\"date-label\">{{ formatDate(dateConfig.date) }}</span>\r\n <label class=\"all-day-toggle\">\r\n <span class=\"toggle-label\">All Day</span>\r\n <input \r\n type=\"checkbox\" \r\n [checked]=\"dateConfig.allDay\"\r\n (change)=\"onMultipleDateAllDayChange(i)\">\r\n </label>\r\n </div>\r\n <div *ngIf=\"!dateConfig.allDay\" class=\"time-inputs\">\r\n <app-time-picker\r\n [pickerId]=\"'multiple-' + i + '-start'\"\r\n label=\"Start Time\"\r\n [value]=\"dateConfig.startTime\"\r\n [position]=\"'left'\"\r\n [closePicker]=\"shouldClosePicker('multiple-' + i + '-start')\"\r\n (timeChange)=\"onMultipleDateStartTimeChange(i, $event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n <app-time-picker\r\n [pickerId]=\"'multiple-' + i + '-end'\"\r\n label=\"End Time\"\r\n [value]=\"dateConfig.endTime\"\r\n [position]=\"'right'\"\r\n [closePicker]=\"shouldClosePicker('multiple-' + i + '-end')\"\r\n (timeChange)=\"onMultipleDateEndTimeChange(i, $event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n </div>\r\n </div>\r\n <div *ngIf=\"multipleDates.length === 0\" class=\"no-selection\">\r\n <p>No dates selected. Select dates from the calendar.</p>\r\n </div>\r\n </div>\r\n\r\n <!-- Date Range Time Configuration -->\r\n <div *ngIf=\"activeTab === 'range' && rangeStartDate && rangeEndDate\" class=\"time-config-item\">\r\n <div class=\"time-config-header\">\r\n <span class=\"date-label\">{{ formatDate(rangeStartDate) }} - {{ formatDate(rangeEndDate) }}</span>\r\n <label class=\"all-day-toggle\">\r\n <span class=\"toggle-label\">All Day</span>\r\n <input \r\n type=\"checkbox\" \r\n [checked]=\"rangeAllDay\"\r\n (change)=\"onRangeAllDayChange()\">\r\n </label>\r\n </div>\r\n <div *ngIf=\"!rangeAllDay\" class=\"time-inputs\">\r\n <app-time-picker\r\n pickerId=\"range-start\"\r\n label=\"Start Time\"\r\n [value]=\"rangeStartTime\"\r\n [position]=\"'left'\"\r\n [closePicker]=\"shouldClosePicker('range-start')\"\r\n (timeChange)=\"onRangeStartTimeChange($event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n <app-time-picker\r\n pickerId=\"range-end\"\r\n label=\"End Time\"\r\n [value]=\"rangeEndTime\"\r\n [position]=\"'right'\"\r\n [closePicker]=\"shouldClosePicker('range-end')\"\r\n (timeChange)=\"onRangeEndTimeChange($event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n </div>\r\n </div>\r\n <div *ngIf=\"activeTab === 'range' && (!rangeStartDate || !rangeEndDate)\" class=\"no-selection\">\r\n <p>No date range selected. Select a date range from the calendar.</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Action Buttons -->\r\n <div class=\"action-buttons\">\r\n <button class=\"btn-clear\" (click)=\"clear()\">Clear</button>\r\n <button class=\"btn-apply\" (click)=\"apply()\">Apply</button>\r\n </div>\r\n</div>\r\n\r\n", styles: [".scheduled-date-picker-container{font-family:Inter,sans-serif;background:#fff;border-radius:12px;padding:0;box-shadow:0 2px 8px #0000001a;overflow:hidden;width:100%;max-width:100%;box-sizing:border-box}.scheduled-header{padding:24px 24px 16px;border-bottom:1px solid #e5e7eb;background:#fff}.scheduled-title{font-size:18px;font-weight:500;line-height:26px;color:#111827;letter-spacing:-.28px;margin:0 0 16px}.tabs{display:flex;margin-bottom:16px;border-radius:6px;padding:3px;background-color:#54578e12}.tab-button{padding:5px 11px;border:none;background:transparent;color:#6b7080;font-size:11px;font-weight:500;cursor:pointer;border:1px solid transparent;transition:all .2s;font-family:Inter,sans-serif;flex:1;border-radius:4px}.tab-button.active{color:#15191e;border-color:#42578a26;background:#fff}.scheduled-content{display:flex;gap:0;align-items:stretch}.calendar-section{flex:0 0 55%;max-width:55%;padding:12px;border-right:1px solid #e5e7eb;background:#fff;box-sizing:border-box}.calendar-wrapper-inline{width:100%}.calendar-wrapper-inline app-custom-calendar{width:100%}.time-config-section{flex:0 0 45%;max-width:45%;padding:12px;background:#fff;overflow-y:auto;max-height:600px;box-sizing:border-box}.time-config-title{font-size:16px;font-weight:600;color:#111827;margin:17px 0 14px}.time-config-item{padding:14px;border:1px solid #e5e7eb;border-radius:8px;background:#fff}.time-config-header{display:flex;justify-content:space-between;align-items:center}.date-label{font-size:12px;font-weight:500;color:#15191e;letter-spacing:-.28px}.all-day-toggle{display:flex;align-items:center;gap:5px;cursor:pointer;-webkit-user-select:none;user-select:none}.all-day-toggle input[type=checkbox]{width:28px;height:16px;appearance:none;background:#bbbdc5;border-radius:10px;position:relative;cursor:pointer;transition:background .2s;margin:0}.all-day-toggle input[type=checkbox]:checked{background:#22973f}.all-day-toggle input[type=checkbox]:before{content:\"\";position:absolute;width:12px;height:12px;border-radius:50%;background:#fff;top:1.5px;left:2.5px;transition:transform .2s;box-shadow:0 1px 3px #0003}.all-day-toggle input[type=checkbox]:checked:before{transform:translate(12px)}.toggle-label{font-size:12px;font-weight:500;color:#111827}.all-day-toggle input[type=checkbox]:checked+.toggle-label{color:#111827}.time-inputs{display:flex;gap:14px;margin-top:12px}.time-config-list{display:flex;flex-direction:column;gap:14px;max-height:350px;overflow-y:auto;padding-right:4px}.time-config-list::-webkit-scrollbar{width:6px;height:6px}.time-config-list::-webkit-scrollbar-track{background:#f1f1f1;border-radius:3px}.time-config-list::-webkit-scrollbar-thumb{background:#b4b4b4;border-radius:3px}.time-config-list::-webkit-scrollbar-thumb:hover{background:#9b9b9b}.no-selection{padding:24px;text-align:center;color:#9ca3af;font-size:14px}.action-buttons{display:flex;justify-content:flex-end;gap:12px;padding:12px;border-top:1px solid #e5e7eb;background:#fff}.btn-clear,.btn-apply{padding:10px 20px;border:none;border-radius:6px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s;font-family:Inter,sans-serif;min-width:80px}.btn-clear{background:#fff;color:#6b7280;border:1px solid #d1d5db}.btn-clear:hover{background:#f9fafb;border-color:#9ca3af}.btn-apply{background:#111827;color:#fff}.btn-apply:hover{background:#374151}@media (max-width: 1200px){.calendar-section{flex:0 0 52%;max-width:52%}.time-config-section{flex:0 0 48%;max-width:48%}}@media (max-width: 1024px){.scheduled-content{flex-direction:column}.calendar-section{flex:1 1 auto;max-width:100%;border-right:none;border-bottom:1px solid #e5e7eb}.time-config-section{flex:1 1 auto;max-width:100%;max-height:none}.time-config-list{max-height:320px}}@media (max-width: 768px){.scheduled-date-picker-container{border-radius:0}.scheduled-header{padding:16px}.calendar-section,.time-config-section{padding:12px 16px}.tabs{overflow-x:auto}.tab-button{white-space:nowrap;font-size:12px;padding:6px 10px}.time-inputs{flex-direction:column}.time-config-item{padding:12px}.action-buttons{padding:10px}}@media (max-width: 480px){.scheduled-title{font-size:16px}.time-config-title{font-size:14px}.date-label{font-size:11px}.time-config-list{max-height:260px}.btn-clear,.btn-apply{padding:8px 14px;font-size:13px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CustomCalendarComponent, selector: "app-custom-calendar", inputs: ["enableTimepicker", "autoApply", "closeOnAutoApply", "showCancel", "linkedCalendars", "singleDatePicker", "showWeekNumbers", "showISOWeekNumbers", "customRangeDirection", "lockStartDate", "position", "drop", "dualCalendar", "showRanges", "timeFormat", "enableSeconds", "customRanges", "multiDateSelection", "maxDate", "minDate", "placeholder", "opens", "inline", "isDisplayCrossIcon", "selectedValue", "displayFormat"], outputs: ["selected", "opened", "closed"] }, { kind: "component", type: TimePickerComponent, selector: "app-time-picker", inputs: ["value", "label", "placeholder", "position", "pickerId", "closePicker", "timeFormat", "showSeconds"], outputs: ["timeChange", "pickerOpened", "pickerClosed"] }] });
2357
+ }
2358
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: ScheduledDatePickerComponent, decorators: [{
2359
+ type: Component,
2360
+ args: [{ selector: 'app-scheduled-date-picker', standalone: true, imports: [CommonModule, FormsModule, CustomCalendarComponent, TimePickerComponent], template: "<div class=\"scheduled-date-picker-container\">\r\n <!-- Header with Tabs -->\r\n\r\n\r\n <!-- Main Content Area -->\r\n \r\n <div class=\"scheduled-content\">\r\n <!-- Left Side: Calendar -->\r\n <div class=\"calendar-section\">\r\n <h2 class=\"scheduled-title\">Scheduled Dates</h2>\r\n <div class=\"tabs\">\r\n <button \r\n class=\"tab-button\" \r\n [class.active]=\"activeTab === 'single'\"\r\n (click)=\"onTabChange('single')\">\r\n Single Date\r\n </button>\r\n <button \r\n class=\"tab-button\" \r\n [class.active]=\"activeTab === 'multiple'\"\r\n (click)=\"onTabChange('multiple')\">\r\n Multiple Dates\r\n </button>\r\n <button \r\n class=\"tab-button\" \r\n [class.active]=\"activeTab === 'range'\"\r\n (click)=\"onTabChange('range')\">\r\n Date Range\r\n </button>\r\n </div>\r\n <!-- Single Date Calendar -->\r\n <div *ngIf=\"activeTab === 'single'\" class=\"calendar-wrapper-inline\">\r\n <app-custom-calendar\r\n [inline]=\"true\"\r\n [dualCalendar]=\"false\"\r\n [singleDatePicker]=\"true\"\r\n [showRanges]=\"false\"\r\n [enableTimepicker]=\"false\"\r\n [showCancel]=\"false\"\r\n placeholder=\"Select a date\"\r\n (selected)=\"onSingleDateSelected($event)\">\r\n </app-custom-calendar>\r\n </div>\r\n\r\n <!-- Multiple Dates Calendar -->\r\n <div *ngIf=\"activeTab === 'multiple'\" class=\"calendar-wrapper-inline\">\r\n <app-custom-calendar\r\n [inline]=\"true\"\r\n [dualCalendar]=\"false\"\r\n [singleDatePicker]=\"false\"\r\n [showRanges]=\"false\"\r\n [enableTimepicker]=\"false\"\r\n [multiDateSelection]=\"true\"\r\n [showCancel]=\"false\"\r\n placeholder=\"Select multiple dates\"\r\n (selected)=\"onMultipleDatesSelected($event)\">\r\n </app-custom-calendar>\r\n </div>\r\n\r\n <!-- Date Range Calendar -->\r\n <div *ngIf=\"activeTab === 'range'\" class=\"calendar-wrapper-inline\">\r\n <app-custom-calendar\r\n [inline]=\"true\"\r\n [dualCalendar]=\"false\"\r\n [singleDatePicker]=\"false\"\r\n [showRanges]=\"false\"\r\n [enableTimepicker]=\"false\"\r\n [showCancel]=\"false\"\r\n placeholder=\"Select date range\"\r\n (selected)=\"onRangeSelected($event)\">\r\n </app-custom-calendar>\r\n </div>\r\n </div>\r\n\r\n <!-- Right Side: Time Configuration -->\r\n <div class=\"time-config-section\">\r\n <h3 class=\"time-config-title\">Time Configuration</h3>\r\n\r\n <!-- Single Date Time Configuration -->\r\n <div *ngIf=\"activeTab === 'single'\">\r\n <div *ngIf=\"singleDate\" class=\"time-config-item\">\r\n <div class=\"time-config-header\">\r\n <span class=\"date-label\">{{ formatDate(singleDate) }}</span>\r\n <label class=\"all-day-toggle\">\r\n <span class=\"toggle-label\">All Day</span>\r\n <input \r\n type=\"checkbox\" \r\n [checked]=\"singleAllDay\"\r\n (change)=\"onSingleAllDayChange()\">\r\n </label>\r\n </div>\r\n <div *ngIf=\"!singleAllDay\" class=\"time-inputs\">\r\n <app-time-picker\r\n pickerId=\"single-start\"\r\n label=\"Start Time\"\r\n [value]=\"singleStartTime\"\r\n [position]=\"'left'\"\r\n [closePicker]=\"shouldClosePicker('single-start')\"\r\n (timeChange)=\"onSingleStartTimeChange($event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n <app-time-picker\r\n pickerId=\"single-end\"\r\n label=\"End Time\"\r\n [value]=\"singleEndTime\"\r\n [position]=\"'right'\"\r\n [closePicker]=\"shouldClosePicker('single-end')\"\r\n (timeChange)=\"onSingleEndTimeChange($event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n </div>\r\n </div>\r\n <div *ngIf=\"!singleDate\" class=\"no-selection\">\r\n <p>No date selected. Select a date from the calendar.</p>\r\n </div>\r\n </div>\r\n\r\n <!-- Multiple Dates Time Configuration -->\r\n <div *ngIf=\"activeTab === 'multiple'\" class=\"time-config-list\">\r\n <div \r\n *ngFor=\"let dateConfig of multipleDates; let i = index\" \r\n class=\"time-config-item\">\r\n <div class=\"time-config-header\">\r\n <span class=\"date-label\">{{ formatDate(dateConfig.date) }}</span>\r\n <label class=\"all-day-toggle\">\r\n <span class=\"toggle-label\">All Day</span>\r\n <input \r\n type=\"checkbox\" \r\n [checked]=\"dateConfig.allDay\"\r\n (change)=\"onMultipleDateAllDayChange(i)\">\r\n </label>\r\n </div>\r\n <div *ngIf=\"!dateConfig.allDay\" class=\"time-inputs\">\r\n <app-time-picker\r\n [pickerId]=\"'multiple-' + i + '-start'\"\r\n label=\"Start Time\"\r\n [value]=\"dateConfig.startTime\"\r\n [position]=\"'left'\"\r\n [closePicker]=\"shouldClosePicker('multiple-' + i + '-start')\"\r\n (timeChange)=\"onMultipleDateStartTimeChange(i, $event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n <app-time-picker\r\n [pickerId]=\"'multiple-' + i + '-end'\"\r\n label=\"End Time\"\r\n [value]=\"dateConfig.endTime\"\r\n [position]=\"'right'\"\r\n [closePicker]=\"shouldClosePicker('multiple-' + i + '-end')\"\r\n (timeChange)=\"onMultipleDateEndTimeChange(i, $event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n </div>\r\n </div>\r\n <div *ngIf=\"multipleDates.length === 0\" class=\"no-selection\">\r\n <p>No dates selected. Select dates from the calendar.</p>\r\n </div>\r\n </div>\r\n\r\n <!-- Date Range Time Configuration -->\r\n <div *ngIf=\"activeTab === 'range' && rangeStartDate && rangeEndDate\" class=\"time-config-item\">\r\n <div class=\"time-config-header\">\r\n <span class=\"date-label\">{{ formatDate(rangeStartDate) }} - {{ formatDate(rangeEndDate) }}</span>\r\n <label class=\"all-day-toggle\">\r\n <span class=\"toggle-label\">All Day</span>\r\n <input \r\n type=\"checkbox\" \r\n [checked]=\"rangeAllDay\"\r\n (change)=\"onRangeAllDayChange()\">\r\n </label>\r\n </div>\r\n <div *ngIf=\"!rangeAllDay\" class=\"time-inputs\">\r\n <app-time-picker\r\n pickerId=\"range-start\"\r\n label=\"Start Time\"\r\n [value]=\"rangeStartTime\"\r\n [position]=\"'left'\"\r\n [closePicker]=\"shouldClosePicker('range-start')\"\r\n (timeChange)=\"onRangeStartTimeChange($event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n <app-time-picker\r\n pickerId=\"range-end\"\r\n label=\"End Time\"\r\n [value]=\"rangeEndTime\"\r\n [position]=\"'right'\"\r\n [closePicker]=\"shouldClosePicker('range-end')\"\r\n (timeChange)=\"onRangeEndTimeChange($event)\"\r\n (pickerOpened)=\"onTimePickerOpened($event)\"\r\n (pickerClosed)=\"onTimePickerClosed($event)\">\r\n </app-time-picker>\r\n </div>\r\n </div>\r\n <div *ngIf=\"activeTab === 'range' && (!rangeStartDate || !rangeEndDate)\" class=\"no-selection\">\r\n <p>No date range selected. Select a date range from the calendar.</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Action Buttons -->\r\n <div class=\"action-buttons\">\r\n <button class=\"btn-clear\" (click)=\"clear()\">Clear</button>\r\n <button class=\"btn-apply\" (click)=\"apply()\">Apply</button>\r\n </div>\r\n</div>\r\n\r\n", styles: [".scheduled-date-picker-container{font-family:Inter,sans-serif;background:#fff;border-radius:12px;padding:0;box-shadow:0 2px 8px #0000001a;overflow:hidden;width:100%;max-width:100%;box-sizing:border-box}.scheduled-header{padding:24px 24px 16px;border-bottom:1px solid #e5e7eb;background:#fff}.scheduled-title{font-size:18px;font-weight:500;line-height:26px;color:#111827;letter-spacing:-.28px;margin:0 0 16px}.tabs{display:flex;margin-bottom:16px;border-radius:6px;padding:3px;background-color:#54578e12}.tab-button{padding:5px 11px;border:none;background:transparent;color:#6b7080;font-size:11px;font-weight:500;cursor:pointer;border:1px solid transparent;transition:all .2s;font-family:Inter,sans-serif;flex:1;border-radius:4px}.tab-button.active{color:#15191e;border-color:#42578a26;background:#fff}.scheduled-content{display:flex;gap:0;align-items:stretch}.calendar-section{flex:0 0 55%;max-width:55%;padding:12px;border-right:1px solid #e5e7eb;background:#fff;box-sizing:border-box}.calendar-wrapper-inline{width:100%}.calendar-wrapper-inline app-custom-calendar{width:100%}.time-config-section{flex:0 0 45%;max-width:45%;padding:12px;background:#fff;overflow-y:auto;max-height:600px;box-sizing:border-box}.time-config-title{font-size:16px;font-weight:600;color:#111827;margin:17px 0 14px}.time-config-item{padding:14px;border:1px solid #e5e7eb;border-radius:8px;background:#fff}.time-config-header{display:flex;justify-content:space-between;align-items:center}.date-label{font-size:12px;font-weight:500;color:#15191e;letter-spacing:-.28px}.all-day-toggle{display:flex;align-items:center;gap:5px;cursor:pointer;-webkit-user-select:none;user-select:none}.all-day-toggle input[type=checkbox]{width:28px;height:16px;appearance:none;background:#bbbdc5;border-radius:10px;position:relative;cursor:pointer;transition:background .2s;margin:0}.all-day-toggle input[type=checkbox]:checked{background:#22973f}.all-day-toggle input[type=checkbox]:before{content:\"\";position:absolute;width:12px;height:12px;border-radius:50%;background:#fff;top:1.5px;left:2.5px;transition:transform .2s;box-shadow:0 1px 3px #0003}.all-day-toggle input[type=checkbox]:checked:before{transform:translate(12px)}.toggle-label{font-size:12px;font-weight:500;color:#111827}.all-day-toggle input[type=checkbox]:checked+.toggle-label{color:#111827}.time-inputs{display:flex;gap:14px;margin-top:12px}.time-config-list{display:flex;flex-direction:column;gap:14px;max-height:350px;overflow-y:auto;padding-right:4px}.time-config-list::-webkit-scrollbar{width:6px;height:6px}.time-config-list::-webkit-scrollbar-track{background:#f1f1f1;border-radius:3px}.time-config-list::-webkit-scrollbar-thumb{background:#b4b4b4;border-radius:3px}.time-config-list::-webkit-scrollbar-thumb:hover{background:#9b9b9b}.no-selection{padding:24px;text-align:center;color:#9ca3af;font-size:14px}.action-buttons{display:flex;justify-content:flex-end;gap:12px;padding:12px;border-top:1px solid #e5e7eb;background:#fff}.btn-clear,.btn-apply{padding:10px 20px;border:none;border-radius:6px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s;font-family:Inter,sans-serif;min-width:80px}.btn-clear{background:#fff;color:#6b7280;border:1px solid #d1d5db}.btn-clear:hover{background:#f9fafb;border-color:#9ca3af}.btn-apply{background:#111827;color:#fff}.btn-apply:hover{background:#374151}@media (max-width: 1200px){.calendar-section{flex:0 0 52%;max-width:52%}.time-config-section{flex:0 0 48%;max-width:48%}}@media (max-width: 1024px){.scheduled-content{flex-direction:column}.calendar-section{flex:1 1 auto;max-width:100%;border-right:none;border-bottom:1px solid #e5e7eb}.time-config-section{flex:1 1 auto;max-width:100%;max-height:none}.time-config-list{max-height:320px}}@media (max-width: 768px){.scheduled-date-picker-container{border-radius:0}.scheduled-header{padding:16px}.calendar-section,.time-config-section{padding:12px 16px}.tabs{overflow-x:auto}.tab-button{white-space:nowrap;font-size:12px;padding:6px 10px}.time-inputs{flex-direction:column}.time-config-item{padding:12px}.action-buttons{padding:10px}}@media (max-width: 480px){.scheduled-title{font-size:16px}.time-config-title{font-size:14px}.date-label{font-size:11px}.time-config-list{max-height:260px}.btn-clear,.btn-apply{padding:8px 14px;font-size:13px}}\n"] }]
2361
+ }], propDecorators: { timeFormat: [{
2362
+ type: Input
2363
+ }], enableSeconds: [{
2364
+ type: Input
2365
+ }], scheduled: [{
2366
+ type: Output
2367
+ }], cleared: [{
2368
+ type: Output
2369
+ }] } });
2370
+
2371
+ /**
2372
+ * Optional NgModule wrapper for projects that prefer module-based usage.
2373
+ *
2374
+ * Note:
2375
+ * - The components themselves are standalone, so you can also import them
2376
+ * directly into any standalone component without using this module.
2377
+ * - This module is mainly for:
2378
+ * - Existing apps that still use feature modules
2379
+ * - Easier "plug-and-play" integration: import CalendarModule once and use
2380
+ * the three exported components anywhere in your templates.
2381
+ */
2382
+ class CalendarModule {
2383
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CalendarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2384
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.16", ngImport: i0, type: CalendarModule, imports: [CommonModule,
2385
+ CustomCalendarComponent,
2386
+ ScheduledDatePickerComponent,
2387
+ TimePickerComponent], exports: [CustomCalendarComponent,
2388
+ ScheduledDatePickerComponent,
2389
+ TimePickerComponent] });
2390
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CalendarModule, imports: [CommonModule,
2391
+ CustomCalendarComponent,
2392
+ ScheduledDatePickerComponent,
2393
+ TimePickerComponent] });
2394
+ }
2395
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: CalendarModule, decorators: [{
2396
+ type: NgModule,
2397
+ args: [{
2398
+ imports: [
2399
+ CommonModule,
2400
+ CustomCalendarComponent,
2401
+ ScheduledDatePickerComponent,
2402
+ TimePickerComponent
2403
+ ],
2404
+ exports: [
2405
+ CustomCalendarComponent,
2406
+ ScheduledDatePickerComponent,
2407
+ TimePickerComponent
2408
+ ]
2409
+ }]
2410
+ }] });
2411
+
2412
+ /*
2413
+ * Public API Surface of calendar
2414
+ */
2415
+
2416
+ /**
2417
+ * Generated bundle index. Do not edit.
2418
+ */
2419
+
2420
+ export { CalendarComponent, CalendarManagerService, CalendarModule, CalendarService, CustomCalendarComponent, ScheduledDatePickerComponent, TimePickerComponent };
2421
+ //# sourceMappingURL=brickclay-calendar.mjs.map