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