@brickclay-org/ui 0.0.10 → 0.0.14

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