universal-datetime-picker 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2054 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var react = require('react');
6
+ var reactDom = require('react-dom');
7
+ var dayjs = require('dayjs');
8
+ var customParseFormat = require('dayjs/plugin/customParseFormat');
9
+ var isSameOrAfter = require('dayjs/plugin/isSameOrAfter');
10
+ var isSameOrBefore = require('dayjs/plugin/isSameOrBefore');
11
+ var localeData = require('dayjs/plugin/localeData');
12
+ var weekday = require('dayjs/plugin/weekday');
13
+ var jsxRuntime = require('react/jsx-runtime');
14
+
15
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
+
17
+ var dayjs__default = /*#__PURE__*/_interopDefault(dayjs);
18
+ var customParseFormat__default = /*#__PURE__*/_interopDefault(customParseFormat);
19
+ var isSameOrAfter__default = /*#__PURE__*/_interopDefault(isSameOrAfter);
20
+ var isSameOrBefore__default = /*#__PURE__*/_interopDefault(isSameOrBefore);
21
+ var localeData__default = /*#__PURE__*/_interopDefault(localeData);
22
+ var weekday__default = /*#__PURE__*/_interopDefault(weekday);
23
+
24
+ dayjs__default.default.extend(customParseFormat__default.default);
25
+ dayjs__default.default.extend(isSameOrAfter__default.default);
26
+ dayjs__default.default.extend(isSameOrBefore__default.default);
27
+ dayjs__default.default.extend(localeData__default.default);
28
+ dayjs__default.default.extend(weekday__default.default);
29
+ var DEFAULT_FORMAT = "YYYY-MM-DD HH:mm:ss";
30
+ var DATE_FORMAT = "YYYY-MM-DD";
31
+ var TIME_FORMAT = "HH:mm:ss";
32
+ function resolveFormat(options) {
33
+ if (options.format) {
34
+ return options.format;
35
+ }
36
+ const mode = options.mode ?? "datetime";
37
+ const showSeconds = options.showSeconds !== false;
38
+ const use12Hours = Boolean(options.use12Hours);
39
+ const timePart = use12Hours ? showSeconds ? "hh:mm:ss A" : "hh:mm A" : showSeconds ? "HH:mm:ss" : "HH:mm";
40
+ if (mode === "date") {
41
+ return DATE_FORMAT;
42
+ }
43
+ if (mode === "time") {
44
+ return timePart;
45
+ }
46
+ return `${DATE_FORMAT} ${timePart}`;
47
+ }
48
+ function buildTimeValue(value, format) {
49
+ const hour24 = value.hour();
50
+ const { hour, isAm } = to12Hour(hour24);
51
+ return {
52
+ hour,
53
+ hour24,
54
+ minute: value.minute(),
55
+ second: value.second(),
56
+ ampm: isAm ? "AM" : "PM",
57
+ formatted: value.format(format)
58
+ };
59
+ }
60
+ var asStringDeprecationWarned = false;
61
+ function warnAsStringDeprecation() {
62
+ if (asStringDeprecationWarned) {
63
+ return;
64
+ }
65
+ if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
66
+ return;
67
+ }
68
+ asStringDeprecationWarned = true;
69
+ console.warn(
70
+ "[universal-datetime-picker] Omitting `asString` currently returns a formatted string. Set `asString={true}` to keep strings, or `asString={false}` to receive a Date / TimeValue. A future major release will default to Date / TimeValue."
71
+ );
72
+ }
73
+ function parseValue(value, format = DEFAULT_FORMAT) {
74
+ if (value === void 0 || value === null || value === "") {
75
+ return null;
76
+ }
77
+ if (dayjs__default.default.isDayjs(value)) {
78
+ return value.isValid() ? value : null;
79
+ }
80
+ if (value instanceof Date) {
81
+ const parsed2 = dayjs__default.default(value);
82
+ return parsed2.isValid() ? parsed2 : null;
83
+ }
84
+ if (typeof value === "string") {
85
+ const parsed2 = dayjs__default.default(value, format, true);
86
+ if (parsed2.isValid()) {
87
+ return parsed2;
88
+ }
89
+ const fallback = dayjs__default.default(value);
90
+ return fallback.isValid() ? fallback : null;
91
+ }
92
+ const parsed = dayjs__default.default(value);
93
+ return parsed.isValid() ? parsed : null;
94
+ }
95
+ function formatValue(value, format = DEFAULT_FORMAT) {
96
+ if (!value || !value.isValid()) {
97
+ return null;
98
+ }
99
+ return value.format(format);
100
+ }
101
+ function pad2(n) {
102
+ return String(n).padStart(2, "0");
103
+ }
104
+ var HOURS_24 = Array.from({ length: 24 }, (_, i) => pad2(i));
105
+ var HOURS_12 = Array.from(
106
+ { length: 12 },
107
+ (_, i) => pad2(i === 0 ? 12 : i)
108
+ );
109
+ var MINUTES = Array.from({ length: 60 }, (_, i) => pad2(i));
110
+ function to12Hour(hour24) {
111
+ const isAm = hour24 < 12;
112
+ const hour = hour24 % 12 === 0 ? 12 : hour24 % 12;
113
+ return { hour, isAm };
114
+ }
115
+ function to24Hour(hour12, isAm) {
116
+ if (hour12 === 12) {
117
+ return isAm ? 0 : 12;
118
+ }
119
+ return isAm ? hour12 : hour12 + 12;
120
+ }
121
+ function formatLocalized(date, template, locale) {
122
+ return date.locale(locale).format(template);
123
+ }
124
+ function getWeekdayLabels(locale, weekStartsOn) {
125
+ const labels = dayjs__default.default().locale(locale).localeData().weekdaysMin();
126
+ return [...labels.slice(weekStartsOn), ...labels.slice(0, weekStartsOn)];
127
+ }
128
+ function startOfWeek(date, weekStartsOn) {
129
+ const day = date.day();
130
+ const diff = (day - weekStartsOn + 7) % 7;
131
+ return date.subtract(diff, "day").startOf("day");
132
+ }
133
+ function endOfWeek(date, weekStartsOn) {
134
+ return startOfWeek(date, weekStartsOn).add(6, "day").endOf("day");
135
+ }
136
+ function cx(...parts) {
137
+ return parts.filter(Boolean).join(" ");
138
+ }
139
+ function yearWindowStart(year) {
140
+ return Math.floor(year / 12) * 12;
141
+ }
142
+ function CalendarHeader(props) {
143
+ const {
144
+ viewMonth,
145
+ setViewMonth,
146
+ panel,
147
+ onPanelChange,
148
+ locale,
149
+ labels,
150
+ titleId
151
+ } = props;
152
+ const year = viewMonth.year();
153
+ const windowStart = yearWindowStart(year);
154
+ const windowEnd = windowStart + 11;
155
+ const now = dayjs__default.default();
156
+ const onPrev = () => {
157
+ if (panel === "day") {
158
+ setViewMonth((m) => m.subtract(1, "month"));
159
+ return;
160
+ }
161
+ if (panel === "month") {
162
+ setViewMonth((m) => m.subtract(1, "year"));
163
+ return;
164
+ }
165
+ setViewMonth((m) => m.subtract(12, "year"));
166
+ };
167
+ const onNext = () => {
168
+ if (panel === "day") {
169
+ setViewMonth((m) => m.add(1, "month"));
170
+ return;
171
+ }
172
+ if (panel === "month") {
173
+ setViewMonth((m) => m.add(1, "year"));
174
+ return;
175
+ }
176
+ setViewMonth((m) => m.add(12, "year"));
177
+ };
178
+ const prevLabel = panel === "day" ? labels.previousMonth : labels.previousYear;
179
+ const nextLabel = panel === "day" ? labels.nextMonth : labels.nextYear;
180
+ let titleContent;
181
+ if (panel === "day") {
182
+ titleContent = /* @__PURE__ */ jsxRuntime.jsx(
183
+ "button",
184
+ {
185
+ type: "button",
186
+ className: "ctp-current-month",
187
+ id: titleId,
188
+ "aria-label": `${formatLocalized(viewMonth, "MMMM YYYY", locale)}. ${labels.chooseMonth}`,
189
+ onClick: () => onPanelChange("month"),
190
+ children: formatLocalized(viewMonth, "MMMM YYYY", locale)
191
+ }
192
+ );
193
+ } else if (panel === "month") {
194
+ titleContent = /* @__PURE__ */ jsxRuntime.jsx(
195
+ "button",
196
+ {
197
+ type: "button",
198
+ className: "ctp-current-month",
199
+ id: titleId,
200
+ "aria-label": `${year}. ${labels.chooseYear}`,
201
+ onClick: () => onPanelChange("year"),
202
+ children: String(year)
203
+ }
204
+ );
205
+ } else {
206
+ titleContent = /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ctp-current-month", id: titleId, children: [
207
+ windowStart,
208
+ " \u2013 ",
209
+ windowEnd
210
+ ] });
211
+ }
212
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
213
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ctp-month-year", children: [
214
+ /* @__PURE__ */ jsxRuntime.jsx(
215
+ "button",
216
+ {
217
+ type: "button",
218
+ className: "ctp-prev-month",
219
+ "aria-label": prevLabel,
220
+ onClick: onPrev,
221
+ children: "\u2039"
222
+ }
223
+ ),
224
+ titleContent,
225
+ /* @__PURE__ */ jsxRuntime.jsx(
226
+ "button",
227
+ {
228
+ type: "button",
229
+ className: "ctp-next-month",
230
+ "aria-label": nextLabel,
231
+ onClick: onNext,
232
+ children: "\u203A"
233
+ }
234
+ )
235
+ ] }),
236
+ panel === "month" && /* @__PURE__ */ jsxRuntime.jsx(
237
+ "div",
238
+ {
239
+ className: "ctp-month-grid",
240
+ role: "grid",
241
+ "aria-label": labels.chooseMonth,
242
+ children: Array.from({ length: 12 }, (_, monthIndex) => {
243
+ const monthDate = viewMonth.month(monthIndex);
244
+ const isCurrent = viewMonth.year() === now.year() && monthIndex === now.month();
245
+ return /* @__PURE__ */ jsxRuntime.jsx(
246
+ "button",
247
+ {
248
+ type: "button",
249
+ role: "gridcell",
250
+ "aria-current": isCurrent ? "date" : void 0,
251
+ "aria-label": formatLocalized(monthDate, "MMMM YYYY", locale),
252
+ className: cx(
253
+ "ctp-box",
254
+ "ctp-box-month",
255
+ isCurrent && "ctp-current"
256
+ ),
257
+ onClick: () => {
258
+ setViewMonth((m) => m.month(monthIndex));
259
+ onPanelChange("day");
260
+ },
261
+ children: formatLocalized(monthDate, "MMM", locale)
262
+ },
263
+ monthIndex
264
+ );
265
+ })
266
+ }
267
+ ),
268
+ panel === "year" && /* @__PURE__ */ jsxRuntime.jsx(
269
+ "div",
270
+ {
271
+ className: "ctp-year-grid",
272
+ role: "grid",
273
+ "aria-label": labels.chooseYear,
274
+ children: Array.from({ length: 12 }, (_, offset) => {
275
+ const y = windowStart + offset;
276
+ const isCurrent = now.year() === y;
277
+ return /* @__PURE__ */ jsxRuntime.jsx(
278
+ "button",
279
+ {
280
+ type: "button",
281
+ role: "gridcell",
282
+ "aria-current": isCurrent ? "date" : void 0,
283
+ "aria-label": String(y),
284
+ className: cx(
285
+ "ctp-box",
286
+ "ctp-box-year",
287
+ isCurrent && "ctp-current"
288
+ ),
289
+ onClick: () => {
290
+ setViewMonth((m) => m.year(y));
291
+ onPanelChange("month");
292
+ },
293
+ children: y
294
+ },
295
+ y
296
+ );
297
+ })
298
+ }
299
+ )
300
+ ] });
301
+ }
302
+
303
+ // src/core/logic/calendar.ts
304
+ function isDisabledDay(date, options) {
305
+ const today = dayjs__default.default().startOf("day");
306
+ const day = date.startOf("day");
307
+ if (options.disablePastDates && day.isBefore(today, "day")) {
308
+ return true;
309
+ }
310
+ if (options.disableFutureDates && day.isAfter(today, "day")) {
311
+ return true;
312
+ }
313
+ const min = parseValue(options.minDate ?? null);
314
+ const max = parseValue(options.maxDate ?? null);
315
+ if (min && day.isBefore(min.startOf("day"), "day")) {
316
+ return true;
317
+ }
318
+ if (max && day.isAfter(max.startOf("day"), "day")) {
319
+ return true;
320
+ }
321
+ return false;
322
+ }
323
+ function buildCalendarMonth(options) {
324
+ const weekStartsOn = options.weekStartsOn ?? 0;
325
+ const monthStart = options.viewMonth.startOf("month");
326
+ const monthEnd = options.viewMonth.endOf("month");
327
+ const gridStart = startOfWeek(monthStart, weekStartsOn);
328
+ const gridEnd = endOfWeek(monthEnd, weekStartsOn);
329
+ const weeks = [];
330
+ let cursor = gridStart.clone();
331
+ while (cursor.isBefore(gridEnd) || cursor.isSame(gridEnd, "day")) {
332
+ const week = [];
333
+ for (let i = 0; i < 7; i += 1) {
334
+ const date = cursor.clone();
335
+ const isCurrentMonth = date.isSame(options.viewMonth, "month");
336
+ const isSelected = Boolean(
337
+ options.selected && date.isSame(options.selected, "day")
338
+ );
339
+ const isRangeStart = Boolean(
340
+ options.rangeStart && date.isSame(options.rangeStart, "day")
341
+ );
342
+ const isRangeEnd = Boolean(
343
+ options.rangeEnd && date.isSame(options.rangeEnd, "day")
344
+ );
345
+ const effectiveEnd = options.rangeEnd ?? options.hoverEnd ?? null;
346
+ let isInRange = false;
347
+ if (options.rangeStart && effectiveEnd) {
348
+ const rangeLow = options.rangeStart.isBefore(effectiveEnd, "day") ? options.rangeStart : effectiveEnd;
349
+ const rangeHigh = options.rangeStart.isBefore(effectiveEnd, "day") ? effectiveEnd : options.rangeStart;
350
+ isInRange = date.isAfter(rangeLow, "day") && date.isBefore(rangeHigh, "day") || date.isSame(rangeLow, "day") || date.isSame(rangeHigh, "day");
351
+ }
352
+ week.push({
353
+ date,
354
+ isCurrentMonth,
355
+ isCurrentDate: date.isSame(dayjs__default.default(), "day"),
356
+ isFuture: date.isAfter(dayjs__default.default(), "day"),
357
+ isPast: date.isBefore(dayjs__default.default(), "day"),
358
+ isWeekend: date.day() === 0 || date.day() === 6,
359
+ isDisabled: isDisabledDay(date, options),
360
+ isSelected,
361
+ isInRange,
362
+ isRangeStart,
363
+ isRangeEnd
364
+ });
365
+ cursor = cursor.add(1, "day");
366
+ }
367
+ weeks.push(week);
368
+ }
369
+ return weeks;
370
+ }
371
+
372
+ // src/core/types.ts
373
+ var DEFAULT_LABELS = {
374
+ date: "Date",
375
+ time: "Time",
376
+ clear: "Clear",
377
+ close: "Close",
378
+ ok: "OK",
379
+ start: "Start",
380
+ end: "End",
381
+ chooseDate: "Choose date",
382
+ chooseDateRange: "Choose date range",
383
+ chooseMonth: "Choose month",
384
+ chooseYear: "Choose year",
385
+ previousMonth: "Previous month",
386
+ nextMonth: "Next month",
387
+ previousYear: "Previous year",
388
+ nextYear: "Next year",
389
+ selectEnd: "Select end date"
390
+ };
391
+
392
+ // src/core/controller.ts
393
+ function padDisplay(n) {
394
+ return String(n).padStart(2, "0");
395
+ }
396
+ var PickerController = class {
397
+ constructor(options = {}) {
398
+ this.listeners = /* @__PURE__ */ new Set();
399
+ this.calPanel = "day";
400
+ this.showHours = false;
401
+ this.showMinutes = false;
402
+ this.showSecondsOpen = false;
403
+ this.showAmPm = false;
404
+ this.subscribe = (listener) => {
405
+ this.listeners.add(listener);
406
+ return () => {
407
+ this.listeners.delete(listener);
408
+ };
409
+ };
410
+ this.getSnapshot = () => this.snapshot;
411
+ /** Stable for useSyncExternalStore — returns same reference until mutate. */
412
+ this.getServerSnapshot = () => this.snapshot;
413
+ this.options = { ...options };
414
+ const mode = options.mode ?? "datetime";
415
+ const showSeconds = options.showSeconds !== false;
416
+ const use12Hours = Boolean(options.use12Hours);
417
+ const format = resolveFormat({
418
+ mode,
419
+ format: options.format,
420
+ use12Hours,
421
+ showSeconds
422
+ });
423
+ const initial = parseValue(options.value ?? options.defaultValue ?? dayjs__default.default(), format) ?? dayjs__default.default();
424
+ this.draft = initial;
425
+ this.viewMonth = initial.startOf("month");
426
+ this.focusedDay = initial;
427
+ this.tab = mode === "time" ? "time" : "date";
428
+ this.open = options.open ?? (options.inline ? true : options.defaultOpen ?? true);
429
+ this.snapshot = this.buildSnapshot();
430
+ }
431
+ setOptions(partial) {
432
+ const prev = this.options;
433
+ this.options = { ...this.options, ...partial };
434
+ if (partial.mode !== void 0 && partial.mode !== prev.mode) {
435
+ this.tab = partial.mode === "time" ? "time" : "date";
436
+ }
437
+ if (partial.open !== void 0) {
438
+ this.open = partial.open;
439
+ }
440
+ if ("value" in partial) {
441
+ if (partial.value === null) {
442
+ const fallback = dayjs__default.default();
443
+ this.draft = fallback;
444
+ this.viewMonth = fallback.startOf("month");
445
+ this.focusedDay = fallback;
446
+ } else if (partial.value !== void 0) {
447
+ const format = this.getResolvedFormat();
448
+ const parsed = parseValue(partial.value, format);
449
+ if (parsed) {
450
+ this.draft = parsed;
451
+ this.viewMonth = parsed.startOf("month");
452
+ this.focusedDay = parsed;
453
+ }
454
+ }
455
+ }
456
+ this.emit();
457
+ }
458
+ getResolvedFormat() {
459
+ return resolveFormat({
460
+ mode: this.options.mode ?? "datetime",
461
+ format: this.options.format,
462
+ use12Hours: Boolean(this.options.use12Hours),
463
+ showSeconds: this.options.showSeconds !== false
464
+ });
465
+ }
466
+ getLabels() {
467
+ return { ...DEFAULT_LABELS, ...this.options.labels };
468
+ }
469
+ emit() {
470
+ this.snapshot = this.buildSnapshot();
471
+ this.listeners.forEach((l) => l());
472
+ }
473
+ buildSnapshot() {
474
+ const mode = this.options.mode ?? "datetime";
475
+ const layout = this.options.layout ?? "combined";
476
+ const showSeconds = this.options.showSeconds !== false;
477
+ const use12Hours = Boolean(this.options.use12Hours);
478
+ const inline = Boolean(this.options.inline);
479
+ const popover = Boolean(this.options.popover);
480
+ const weekStartsOn = this.options.weekStartsOn ?? 0;
481
+ const locale = this.options.locale ?? "en";
482
+ const resolvedFormat = this.getResolvedFormat();
483
+ const labels = this.getLabels();
484
+ const weeks = buildCalendarMonth({
485
+ viewMonth: this.viewMonth,
486
+ selected: this.draft,
487
+ minDate: this.options.minDate,
488
+ maxDate: this.options.maxDate,
489
+ disablePastDates: this.options.disablePastDates,
490
+ disableFutureDates: this.options.disableFutureDates,
491
+ weekStartsOn
492
+ });
493
+ const showDate = mode !== "time";
494
+ const showTime = mode !== "date";
495
+ const useTabs = mode === "datetime" && layout === "tabs";
496
+ const showDatePanel = showDate && (!useTabs || this.tab === "date");
497
+ const showTimePanel = showTime && (!useTabs || this.tab === "time");
498
+ const hour24 = this.draft.hour();
499
+ const { hour: hour12, isAm } = to12Hour(hour24);
500
+ return {
501
+ draft: this.draft,
502
+ viewMonth: this.viewMonth,
503
+ calPanel: this.calPanel,
504
+ tab: this.tab,
505
+ showHours: this.showHours,
506
+ showMinutes: this.showMinutes,
507
+ showSecondsOpen: this.showSecondsOpen,
508
+ showAmPm: this.showAmPm,
509
+ focusedDay: this.focusedDay,
510
+ open: this.open,
511
+ mode,
512
+ layout,
513
+ showSeconds,
514
+ use12Hours,
515
+ inline,
516
+ popover,
517
+ weekStartsOn,
518
+ locale,
519
+ labels,
520
+ resolvedFormat,
521
+ asString: this.options.asString,
522
+ className: this.options.className,
523
+ theme: this.options.theme,
524
+ weeks,
525
+ weekdayLabels: getWeekdayLabels(locale, weekStartsOn),
526
+ showDate,
527
+ showTime,
528
+ useTabs,
529
+ showDatePanel,
530
+ showTimePanel,
531
+ showModeTabs: useTabs,
532
+ hour24,
533
+ hour12,
534
+ isAm,
535
+ hourOptions: use12Hours ? HOURS_12 : HOURS_24,
536
+ displayHour: use12Hours ? padDisplay(hour12) : padDisplay(hour24),
537
+ displayMinute: padDisplay(this.draft.minute()),
538
+ displaySecond: padDisplay(this.draft.second()),
539
+ minuteOptions: MINUTES
540
+ };
541
+ }
542
+ setOpen(open) {
543
+ if (this.options.inline) {
544
+ return;
545
+ }
546
+ this.open = open;
547
+ this.options.onOpenChange?.(open);
548
+ this.emit();
549
+ }
550
+ close() {
551
+ if (!this.options.inline) {
552
+ this.setOpen(false);
553
+ }
554
+ }
555
+ setTab(tab) {
556
+ this.tab = tab;
557
+ this.emit();
558
+ }
559
+ setCalPanel(panel) {
560
+ this.calPanel = panel;
561
+ this.emit();
562
+ }
563
+ setViewMonth(next) {
564
+ this.viewMonth = typeof next === "function" ? next(this.viewMonth) : next;
565
+ this.emit();
566
+ }
567
+ selectDay(day) {
568
+ this.draft = this.draft.year(day.year()).month(day.month()).date(day.date());
569
+ this.focusedDay = day;
570
+ this.emit();
571
+ }
572
+ setHour(hourValue) {
573
+ this.draft = this.draft.hour(hourValue);
574
+ this.emit();
575
+ }
576
+ setMinute(minute) {
577
+ this.draft = this.draft.minute(minute);
578
+ this.emit();
579
+ }
580
+ setSecond(second) {
581
+ this.draft = this.draft.second(second);
582
+ this.emit();
583
+ }
584
+ setAmPm(isAm) {
585
+ const { hour } = to12Hour(this.draft.hour());
586
+ this.draft = this.draft.hour(to24Hour(hour, isAm));
587
+ this.emit();
588
+ }
589
+ toggleHours() {
590
+ this.showHours = !this.showHours;
591
+ this.showMinutes = false;
592
+ this.showSecondsOpen = false;
593
+ this.showAmPm = false;
594
+ this.emit();
595
+ }
596
+ toggleMinutes() {
597
+ this.showMinutes = !this.showMinutes;
598
+ this.showHours = false;
599
+ this.showSecondsOpen = false;
600
+ this.showAmPm = false;
601
+ this.emit();
602
+ }
603
+ toggleSeconds() {
604
+ this.showSecondsOpen = !this.showSecondsOpen;
605
+ this.showHours = false;
606
+ this.showMinutes = false;
607
+ this.showAmPm = false;
608
+ this.emit();
609
+ }
610
+ toggleAmPm() {
611
+ this.showAmPm = !this.showAmPm;
612
+ this.showHours = false;
613
+ this.showMinutes = false;
614
+ this.showSecondsOpen = false;
615
+ this.emit();
616
+ }
617
+ closeTimeColumns() {
618
+ this.showHours = false;
619
+ this.showMinutes = false;
620
+ this.showSecondsOpen = false;
621
+ this.showAmPm = false;
622
+ this.emit();
623
+ }
624
+ selectHourOption(opt) {
625
+ const snap = this.snapshot;
626
+ if (snap.use12Hours) {
627
+ this.draft = this.draft.hour(to24Hour(Number(opt), snap.isAm));
628
+ } else {
629
+ this.draft = this.draft.hour(Number(opt));
630
+ }
631
+ this.showHours = false;
632
+ this.emit();
633
+ }
634
+ selectMinuteOption(opt) {
635
+ this.draft = this.draft.minute(Number(opt));
636
+ this.showMinutes = false;
637
+ this.emit();
638
+ }
639
+ selectSecondOption(opt) {
640
+ this.draft = this.draft.second(Number(opt));
641
+ this.showSecondsOpen = false;
642
+ this.emit();
643
+ }
644
+ selectAmPmOption(opt) {
645
+ const { hour } = to12Hour(this.draft.hour());
646
+ this.draft = this.draft.hour(to24Hour(hour, opt === "AM"));
647
+ this.showAmPm = false;
648
+ this.emit();
649
+ }
650
+ handleGridKeyDown(key) {
651
+ const snap = this.snapshot;
652
+ let next = this.focusedDay;
653
+ switch (key) {
654
+ case "ArrowLeft":
655
+ next = this.focusedDay.subtract(1, "day");
656
+ break;
657
+ case "ArrowRight":
658
+ next = this.focusedDay.add(1, "day");
659
+ break;
660
+ case "ArrowUp":
661
+ next = this.focusedDay.subtract(7, "day");
662
+ break;
663
+ case "ArrowDown":
664
+ next = this.focusedDay.add(7, "day");
665
+ break;
666
+ case "Home":
667
+ next = startOfWeek(this.focusedDay, snap.weekStartsOn);
668
+ break;
669
+ case "End":
670
+ next = endOfWeek(this.focusedDay, snap.weekStartsOn);
671
+ break;
672
+ case "PageUp":
673
+ next = this.focusedDay.subtract(1, "month");
674
+ this.viewMonth = next.startOf("month");
675
+ break;
676
+ case "PageDown":
677
+ next = this.focusedDay.add(1, "month");
678
+ this.viewMonth = next.startOf("month");
679
+ break;
680
+ case "Enter":
681
+ case " ": {
682
+ const cell = snap.weeks.flat().find((d) => d.date.isSame(this.focusedDay, "day"));
683
+ if (cell && !cell.isDisabled && cell.isCurrentMonth) {
684
+ this.selectDay(cell.date);
685
+ }
686
+ return true;
687
+ }
688
+ default:
689
+ return false;
690
+ }
691
+ this.focusedDay = next;
692
+ if (!next.isSame(this.viewMonth, "month")) {
693
+ this.viewMonth = next.startOf("month");
694
+ }
695
+ this.emit();
696
+ return true;
697
+ }
698
+ confirm() {
699
+ const snap = this.snapshot;
700
+ let payload;
701
+ if (snap.asString === false) {
702
+ if (snap.mode === "time") {
703
+ payload = buildTimeValue(this.draft, snap.resolvedFormat);
704
+ } else if (snap.mode === "date") {
705
+ payload = this.draft.startOf("day").toDate();
706
+ } else {
707
+ payload = this.draft.toDate();
708
+ }
709
+ } else {
710
+ if (snap.asString === void 0) {
711
+ warnAsStringDeprecation();
712
+ }
713
+ payload = formatValue(this.draft, snap.resolvedFormat);
714
+ }
715
+ this.options.onChange?.(payload);
716
+ this.close();
717
+ return payload;
718
+ }
719
+ clear() {
720
+ this.options.onChange?.(null);
721
+ this.close();
722
+ }
723
+ /** Year window helpers for month/year panels */
724
+ yearWindowStart(year) {
725
+ return Math.floor(year / 12) * 12;
726
+ }
727
+ navigatePrev() {
728
+ if (this.calPanel === "day") {
729
+ this.viewMonth = this.viewMonth.subtract(1, "month");
730
+ } else if (this.calPanel === "month") {
731
+ this.viewMonth = this.viewMonth.subtract(1, "year");
732
+ } else {
733
+ this.viewMonth = this.viewMonth.subtract(12, "year");
734
+ }
735
+ this.emit();
736
+ }
737
+ navigateNext() {
738
+ if (this.calPanel === "day") {
739
+ this.viewMonth = this.viewMonth.add(1, "month");
740
+ } else if (this.calPanel === "month") {
741
+ this.viewMonth = this.viewMonth.add(1, "year");
742
+ } else {
743
+ this.viewMonth = this.viewMonth.add(12, "year");
744
+ }
745
+ this.emit();
746
+ }
747
+ selectMonth(monthIndex) {
748
+ this.viewMonth = this.viewMonth.month(monthIndex);
749
+ this.calPanel = "day";
750
+ this.emit();
751
+ }
752
+ selectYear(year) {
753
+ this.viewMonth = this.viewMonth.year(year);
754
+ this.calPanel = "month";
755
+ this.emit();
756
+ }
757
+ };
758
+
759
+ // src/vanilla/a11y.ts
760
+ var FOCUSABLE = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
761
+ var POPOVER_GAP = 8;
762
+ var POPOVER_PADDING = 8;
763
+ var DEFAULT_PICKER_WIDTH = 300;
764
+ var DEFAULT_PICKER_HEIGHT = 360;
765
+ function attachFocusTrap(container, active) {
766
+ const previouslyFocused = document.activeElement;
767
+ const focusables = () => Array.from(container.querySelectorAll(FOCUSABLE)).filter(
768
+ (el) => !el.hasAttribute("disabled") && el.offsetParent !== null
769
+ );
770
+ const first = focusables()[0];
771
+ first?.focus({ preventScroll: true });
772
+ const onKeyDown = (event) => {
773
+ if (event.key !== "Tab") {
774
+ return;
775
+ }
776
+ const items = focusables();
777
+ if (items.length === 0) {
778
+ return;
779
+ }
780
+ const firstItem = items[0];
781
+ const lastItem = items[items.length - 1];
782
+ if (event.shiftKey && document.activeElement === firstItem) {
783
+ event.preventDefault();
784
+ lastItem.focus({ preventScroll: true });
785
+ } else if (!event.shiftKey && document.activeElement === lastItem) {
786
+ event.preventDefault();
787
+ firstItem.focus({ preventScroll: true });
788
+ }
789
+ };
790
+ container.addEventListener("keydown", onKeyDown);
791
+ return () => {
792
+ container.removeEventListener("keydown", onKeyDown);
793
+ previouslyFocused?.focus?.({ preventScroll: true });
794
+ };
795
+ }
796
+ function attachEscape(handler, active) {
797
+ const onKeyDown = (event) => {
798
+ if (event.key === "Escape") {
799
+ event.preventDefault();
800
+ handler();
801
+ }
802
+ };
803
+ document.addEventListener("keydown", onKeyDown);
804
+ return () => document.removeEventListener("keydown", onKeyDown);
805
+ }
806
+ function attachClickOutside(handler, active, floating, anchorEl) {
807
+ const onPointerDown = (event) => {
808
+ const target = event.target;
809
+ if (!(target instanceof Node)) {
810
+ return;
811
+ }
812
+ if (floating?.contains(target)) {
813
+ return;
814
+ }
815
+ if (anchorEl?.contains(target)) {
816
+ return;
817
+ }
818
+ handler();
819
+ };
820
+ document.addEventListener("pointerdown", onPointerDown, true);
821
+ return () => {
822
+ document.removeEventListener("pointerdown", onPointerDown, true);
823
+ };
824
+ }
825
+ function computePopoverPosition(anchorEl, pickerWidth, pickerHeight) {
826
+ const rect = anchorEl.getBoundingClientRect();
827
+ let top = rect.bottom + POPOVER_GAP;
828
+ let left = rect.left;
829
+ if (left + pickerWidth > window.innerWidth - POPOVER_PADDING) {
830
+ left = Math.max(
831
+ POPOVER_PADDING,
832
+ window.innerWidth - pickerWidth - POPOVER_PADDING
833
+ );
834
+ }
835
+ if (left < POPOVER_PADDING) {
836
+ left = POPOVER_PADDING;
837
+ }
838
+ if (top + pickerHeight > window.innerHeight - POPOVER_PADDING) {
839
+ const above = rect.top - pickerHeight - POPOVER_GAP;
840
+ if (above >= POPOVER_PADDING) {
841
+ top = above;
842
+ } else {
843
+ top = Math.max(
844
+ POPOVER_PADDING,
845
+ window.innerHeight - pickerHeight - POPOVER_PADDING
846
+ );
847
+ }
848
+ }
849
+ return { top, left };
850
+ }
851
+ function resolveThemeAttr(theme, anchorEl) {
852
+ if (theme === "light" || theme === "dark") {
853
+ return theme;
854
+ }
855
+ let node = anchorEl ?? null;
856
+ while (node) {
857
+ const attr = node.getAttribute("data-ctp-theme");
858
+ if (attr === "dark" || attr === "light") {
859
+ return attr;
860
+ }
861
+ node = node.parentElement;
862
+ }
863
+ if (typeof document !== "undefined") {
864
+ const root = document.documentElement.getAttribute("data-ctp-theme");
865
+ if (root === "dark" || root === "light") {
866
+ return root;
867
+ }
868
+ }
869
+ return void 0;
870
+ }
871
+ function useControllableState(options) {
872
+ const { value, defaultValue, onChange } = options;
873
+ const [uncontrolled, setUncontrolled] = react.useState(defaultValue);
874
+ const isControlled = value !== void 0;
875
+ const state = isControlled ? value : uncontrolled;
876
+ const setState = react.useCallback(
877
+ (next) => {
878
+ const resolved = typeof next === "function" ? next(state) : next;
879
+ if (!isControlled) {
880
+ setUncontrolled(resolved);
881
+ }
882
+ onChange?.(resolved);
883
+ },
884
+ [isControlled, onChange, state]
885
+ );
886
+ return [state, setState];
887
+ }
888
+ function cx2(...parts) {
889
+ return parts.filter(Boolean).join(" ");
890
+ }
891
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
892
+ function usePopoverPosition(anchorEl, open, enabled, floatingRef) {
893
+ const [position, setPosition] = react.useState(null);
894
+ const update = react.useCallback(() => {
895
+ if (!enabled || !open || !anchorEl) {
896
+ setPosition(null);
897
+ return;
898
+ }
899
+ const floating = floatingRef.current;
900
+ const width = floating?.offsetWidth || DEFAULT_PICKER_WIDTH;
901
+ const height = floating?.offsetHeight || DEFAULT_PICKER_HEIGHT;
902
+ setPosition(computePopoverPosition(anchorEl, width, height));
903
+ }, [anchorEl, enabled, open, floatingRef]);
904
+ useIsomorphicLayoutEffect(() => {
905
+ update();
906
+ }, [update]);
907
+ react.useEffect(() => {
908
+ if (!open || !enabled) {
909
+ return;
910
+ }
911
+ window.addEventListener("resize", update);
912
+ window.addEventListener("scroll", update, true);
913
+ const floating = floatingRef.current;
914
+ let observer;
915
+ if (floating && typeof ResizeObserver !== "undefined") {
916
+ observer = new ResizeObserver(() => update());
917
+ observer.observe(floating);
918
+ }
919
+ return () => {
920
+ window.removeEventListener("resize", update);
921
+ window.removeEventListener("scroll", update, true);
922
+ observer?.disconnect();
923
+ };
924
+ }, [open, enabled, update, floatingRef]);
925
+ if (!open || !enabled) {
926
+ return null;
927
+ }
928
+ return position;
929
+ }
930
+ function DateTime(props) {
931
+ const {
932
+ value,
933
+ defaultValue,
934
+ onChange,
935
+ asString,
936
+ showSeconds = true,
937
+ format,
938
+ mode = "datetime",
939
+ layout = "combined",
940
+ minDate,
941
+ maxDate,
942
+ disablePastDates = false,
943
+ disableFutureDates = false,
944
+ weekStartsOn = 0,
945
+ use12Hours = false,
946
+ inline = false,
947
+ className,
948
+ style,
949
+ locale = "en",
950
+ labels: labelsProp,
951
+ theme,
952
+ open: openProp,
953
+ defaultOpen = true,
954
+ onOpenChange,
955
+ anchorEl,
956
+ popover = false
957
+ } = props;
958
+ const titleId = react.useId();
959
+ const dialogRef = react.useRef(null);
960
+ const [open, setOpen] = useControllableState({
961
+ value: openProp,
962
+ defaultValue: inline ? true : defaultOpen,
963
+ onChange: onOpenChange
964
+ });
965
+ const controllerRef = react.useRef(null);
966
+ if (!controllerRef.current) {
967
+ controllerRef.current = new PickerController({
968
+ value,
969
+ defaultValue,
970
+ onChange,
971
+ asString,
972
+ showSeconds,
973
+ format,
974
+ mode,
975
+ layout,
976
+ minDate,
977
+ maxDate,
978
+ disablePastDates,
979
+ disableFutureDates,
980
+ weekStartsOn,
981
+ use12Hours,
982
+ inline,
983
+ className,
984
+ locale,
985
+ labels: labelsProp,
986
+ theme,
987
+ open,
988
+ popover,
989
+ anchorEl
990
+ });
991
+ }
992
+ const controller = controllerRef.current;
993
+ react.useEffect(() => {
994
+ controller.setOptions({
995
+ value,
996
+ onChange,
997
+ asString,
998
+ showSeconds,
999
+ format,
1000
+ mode,
1001
+ layout,
1002
+ minDate,
1003
+ maxDate,
1004
+ disablePastDates,
1005
+ disableFutureDates,
1006
+ weekStartsOn,
1007
+ use12Hours,
1008
+ inline,
1009
+ className,
1010
+ locale,
1011
+ labels: labelsProp,
1012
+ theme,
1013
+ open,
1014
+ popover,
1015
+ anchorEl,
1016
+ onOpenChange: setOpen
1017
+ });
1018
+ }, [
1019
+ controller,
1020
+ value,
1021
+ onChange,
1022
+ asString,
1023
+ showSeconds,
1024
+ format,
1025
+ mode,
1026
+ layout,
1027
+ minDate,
1028
+ maxDate,
1029
+ disablePastDates,
1030
+ disableFutureDates,
1031
+ weekStartsOn,
1032
+ use12Hours,
1033
+ inline,
1034
+ className,
1035
+ locale,
1036
+ labelsProp,
1037
+ theme,
1038
+ open,
1039
+ popover,
1040
+ anchorEl,
1041
+ setOpen
1042
+ ]);
1043
+ const snap = react.useSyncExternalStore(
1044
+ controller.subscribe,
1045
+ controller.getSnapshot,
1046
+ controller.getServerSnapshot
1047
+ );
1048
+ const close = react.useCallback(() => {
1049
+ if (!inline) {
1050
+ setOpen(false);
1051
+ }
1052
+ }, [inline, setOpen]);
1053
+ react.useEffect(() => {
1054
+ if (!(open && !inline)) {
1055
+ return;
1056
+ }
1057
+ const el = dialogRef.current;
1058
+ if (!el) {
1059
+ return;
1060
+ }
1061
+ return attachEscape(close);
1062
+ }, [open, inline, close]);
1063
+ react.useEffect(() => {
1064
+ if (!(open && !inline)) {
1065
+ return;
1066
+ }
1067
+ const el = dialogRef.current;
1068
+ if (!el) {
1069
+ return;
1070
+ }
1071
+ return attachFocusTrap(el);
1072
+ }, [open, inline]);
1073
+ react.useEffect(() => {
1074
+ if (!(open && !inline && popover)) {
1075
+ return;
1076
+ }
1077
+ return attachClickOutside(close, true, dialogRef.current, anchorEl);
1078
+ }, [open, inline, popover, close, anchorEl]);
1079
+ const position = usePopoverPosition(
1080
+ anchorEl,
1081
+ open && !inline,
1082
+ popover,
1083
+ dialogRef
1084
+ );
1085
+ const onGridKeyDown = (event) => {
1086
+ if (controller.handleGridKeyDown(event.key)) {
1087
+ event.preventDefault();
1088
+ }
1089
+ };
1090
+ const onBackdropClick = (event) => {
1091
+ if (event.target === event.currentTarget) {
1092
+ close();
1093
+ }
1094
+ };
1095
+ if (!open && !inline) {
1096
+ return null;
1097
+ }
1098
+ const themeAttr = resolveThemeAttr(theme, anchorEl);
1099
+ const pickerStyle = popover && !inline ? {
1100
+ ...style,
1101
+ position: "fixed",
1102
+ top: position?.top ?? 80,
1103
+ left: position?.left ?? 16
1104
+ } : style;
1105
+ const datePanel = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ctp-body ctp-body-calendar-date", children: [
1106
+ /* @__PURE__ */ jsxRuntime.jsx(
1107
+ CalendarHeader,
1108
+ {
1109
+ viewMonth: snap.viewMonth,
1110
+ setViewMonth: (next) => controller.setViewMonth(next),
1111
+ panel: snap.calPanel,
1112
+ onPanelChange: (p) => controller.setCalPanel(p),
1113
+ locale: snap.locale,
1114
+ labels: snap.labels,
1115
+ titleId: snap.showDatePanel ? titleId : void 0
1116
+ }
1117
+ ),
1118
+ snap.calPanel === "day" && /* @__PURE__ */ jsxRuntime.jsxs(
1119
+ "div",
1120
+ {
1121
+ className: "ctp-main-calendar",
1122
+ role: "grid",
1123
+ "aria-label": snap.labels.chooseDate,
1124
+ tabIndex: 0,
1125
+ onKeyDown: onGridKeyDown,
1126
+ children: [
1127
+ snap.weekdayLabels.map((label) => /* @__PURE__ */ jsxRuntime.jsx(
1128
+ "div",
1129
+ {
1130
+ className: "ctp-box ctp-box-days",
1131
+ role: "columnheader",
1132
+ children: label
1133
+ },
1134
+ label
1135
+ )),
1136
+ snap.weeks.map(
1137
+ (week) => week.map((dayData) => {
1138
+ const selected = dayData.isSelected;
1139
+ const focused = dayData.date.isSame(snap.focusedDay, "day");
1140
+ const disabled = !dayData.isCurrentMonth || dayData.isDisabled;
1141
+ return /* @__PURE__ */ jsxRuntime.jsx(
1142
+ "button",
1143
+ {
1144
+ type: "button",
1145
+ role: "gridcell",
1146
+ tabIndex: focused ? 0 : -1,
1147
+ "aria-selected": selected,
1148
+ "aria-disabled": disabled,
1149
+ disabled,
1150
+ "aria-label": formatLocalized(
1151
+ dayData.date,
1152
+ "dddd, MMMM D, YYYY",
1153
+ snap.locale
1154
+ ),
1155
+ className: cx2(
1156
+ "ctp-box",
1157
+ "ctp-box-date",
1158
+ !dayData.isCurrentMonth && "not-current-month",
1159
+ selected && "selected",
1160
+ dayData.isDisabled && "disabled-date",
1161
+ dayData.isWeekend && "weekend-day",
1162
+ dayData.isCurrentDate && "ctp-today",
1163
+ dayData.isInRange && "ctp-in-range",
1164
+ dayData.isRangeStart && "ctp-range-start",
1165
+ dayData.isRangeEnd && "ctp-range-end"
1166
+ ),
1167
+ onClick: () => {
1168
+ if (!disabled) {
1169
+ controller.selectDay(dayData.date);
1170
+ }
1171
+ },
1172
+ children: dayData.date.format("D")
1173
+ },
1174
+ dayData.date.format("YYYY-MM-DD")
1175
+ );
1176
+ })
1177
+ )
1178
+ ]
1179
+ }
1180
+ )
1181
+ ] });
1182
+ const timePanel = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ctp-body ctp-body-calendar-time", children: [
1183
+ snap.showDatePanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ctp-section-label", children: snap.labels.time }),
1184
+ !snap.showDatePanel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ctp-visually-hidden", id: titleId, children: snap.labels.time }),
1185
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ctp-main-time", children: [
1186
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ctp-main-time-header", children: [
1187
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ctp-box", children: "Hr" }),
1188
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ctp-box", children: "Min" }),
1189
+ snap.showSeconds && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ctp-box", children: "Sec" }),
1190
+ snap.use12Hours && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ctp-box", children: "AM/PM" })
1191
+ ] }),
1192
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ctp-main-time-body", children: [
1193
+ /* @__PURE__ */ jsxRuntime.jsx(
1194
+ TimeColumn,
1195
+ {
1196
+ label: "hours",
1197
+ open: snap.showHours,
1198
+ onToggle: () => controller.toggleHours(),
1199
+ display: snap.displayHour,
1200
+ options: snap.hourOptions,
1201
+ onSelect: (opt) => controller.selectHourOption(opt)
1202
+ }
1203
+ ),
1204
+ /* @__PURE__ */ jsxRuntime.jsx(
1205
+ TimeColumn,
1206
+ {
1207
+ label: "minutes",
1208
+ open: snap.showMinutes,
1209
+ onToggle: () => controller.toggleMinutes(),
1210
+ display: snap.displayMinute,
1211
+ options: snap.minuteOptions,
1212
+ onSelect: (opt) => controller.selectMinuteOption(opt)
1213
+ }
1214
+ ),
1215
+ snap.showSeconds && /* @__PURE__ */ jsxRuntime.jsx(
1216
+ TimeColumn,
1217
+ {
1218
+ label: "seconds",
1219
+ open: snap.showSecondsOpen,
1220
+ onToggle: () => controller.toggleSeconds(),
1221
+ display: snap.displaySecond,
1222
+ options: snap.minuteOptions,
1223
+ onSelect: (opt) => controller.selectSecondOption(opt)
1224
+ }
1225
+ ),
1226
+ snap.use12Hours && /* @__PURE__ */ jsxRuntime.jsx(
1227
+ TimeColumn,
1228
+ {
1229
+ label: "am-pm",
1230
+ open: snap.showAmPm,
1231
+ onToggle: () => controller.toggleAmPm(),
1232
+ display: snap.isAm ? "AM" : "PM",
1233
+ options: ["AM", "PM"],
1234
+ onSelect: (opt) => controller.selectAmPmOption(opt)
1235
+ }
1236
+ )
1237
+ ] })
1238
+ ] })
1239
+ ] });
1240
+ const picker = /* @__PURE__ */ jsxRuntime.jsxs(
1241
+ "div",
1242
+ {
1243
+ ref: dialogRef,
1244
+ className: cx2(
1245
+ "ctp-calendar-time-picker",
1246
+ popover && !inline && "ctp-popover",
1247
+ snap.use12Hours && "ctp-use-12h",
1248
+ snap.mode === "time" && "ctp-mode-time",
1249
+ !snap.showSeconds && "ctp-no-seconds",
1250
+ snap.showDatePanel && snap.showTimePanel && "ctp-layout-combined",
1251
+ className
1252
+ ),
1253
+ style: pickerStyle,
1254
+ "data-ctp-theme": themeAttr,
1255
+ role: inline ? void 0 : "dialog",
1256
+ "aria-modal": inline ? void 0 : true,
1257
+ "aria-labelledby": titleId,
1258
+ children: [
1259
+ snap.showModeTabs && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ctp-header", children: /* @__PURE__ */ jsxRuntime.jsxs(
1260
+ "div",
1261
+ {
1262
+ className: "ctp-button-container",
1263
+ role: "tablist",
1264
+ "aria-label": "Picker mode",
1265
+ children: [
1266
+ /* @__PURE__ */ jsxRuntime.jsx(
1267
+ "button",
1268
+ {
1269
+ type: "button",
1270
+ role: "tab",
1271
+ "aria-selected": snap.tab === "date",
1272
+ className: cx2("ctp-date", snap.tab === "date" && "ctp-active"),
1273
+ onClick: () => controller.setTab("date"),
1274
+ children: snap.labels.date
1275
+ }
1276
+ ),
1277
+ /* @__PURE__ */ jsxRuntime.jsx(
1278
+ "button",
1279
+ {
1280
+ type: "button",
1281
+ role: "tab",
1282
+ "aria-selected": snap.tab === "time",
1283
+ className: cx2("ctp-time", snap.tab === "time" && "ctp-active"),
1284
+ onClick: () => controller.setTab("time"),
1285
+ children: snap.labels.time
1286
+ }
1287
+ )
1288
+ ]
1289
+ }
1290
+ ) }),
1291
+ snap.showDatePanel && datePanel,
1292
+ snap.showTimePanel && timePanel,
1293
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ctp-footer", children: [
1294
+ /* @__PURE__ */ jsxRuntime.jsx(
1295
+ "button",
1296
+ {
1297
+ type: "button",
1298
+ className: "close-button",
1299
+ onClick: () => {
1300
+ onChange?.(null);
1301
+ close();
1302
+ },
1303
+ children: snap.labels.clear
1304
+ }
1305
+ ),
1306
+ !inline && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "ctp-cancel", onClick: close, children: snap.labels.close }),
1307
+ /* @__PURE__ */ jsxRuntime.jsx(
1308
+ "button",
1309
+ {
1310
+ type: "button",
1311
+ onClick: () => {
1312
+ controller.confirm();
1313
+ close();
1314
+ },
1315
+ children: snap.labels.ok
1316
+ }
1317
+ )
1318
+ ] })
1319
+ ]
1320
+ }
1321
+ );
1322
+ if (inline) {
1323
+ return picker;
1324
+ }
1325
+ if (popover) {
1326
+ return reactDom.createPortal(picker, document.body);
1327
+ }
1328
+ return reactDom.createPortal(
1329
+ /* @__PURE__ */ jsxRuntime.jsx(
1330
+ "div",
1331
+ {
1332
+ className: "ctp-calendar-time-picker-absolute-container",
1333
+ "data-ctp-theme": themeAttr,
1334
+ onClick: onBackdropClick,
1335
+ children: picker
1336
+ }
1337
+ ),
1338
+ document.body
1339
+ );
1340
+ }
1341
+ function TimeColumn(props) {
1342
+ const listId = react.useId();
1343
+ const listRef = react.useRef(null);
1344
+ useIsomorphicLayoutEffect(() => {
1345
+ if (props.open && listRef.current) {
1346
+ listRef.current.scrollTop = 0;
1347
+ }
1348
+ }, [props.open]);
1349
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1350
+ "div",
1351
+ {
1352
+ className: cx2("ctp-box", "ctp-box-time", !props.open && "not-opened"),
1353
+ children: [
1354
+ /* @__PURE__ */ jsxRuntime.jsx(
1355
+ "button",
1356
+ {
1357
+ type: "button",
1358
+ className: `ctp-${props.label === "am-pm" ? "am-pm" : props.label.slice(0, -1)} ctp-initial-time`,
1359
+ "aria-haspopup": "listbox",
1360
+ "aria-expanded": props.open,
1361
+ "aria-controls": listId,
1362
+ "aria-label": `Select ${props.label}`,
1363
+ onClick: props.onToggle,
1364
+ children: props.display
1365
+ }
1366
+ ),
1367
+ /* @__PURE__ */ jsxRuntime.jsx(
1368
+ "div",
1369
+ {
1370
+ ref: listRef,
1371
+ id: listId,
1372
+ role: "listbox",
1373
+ "aria-label": props.label,
1374
+ className: cx2(
1375
+ `ctp-overflow-${props.label}`,
1376
+ !props.open && "not-visible"
1377
+ ),
1378
+ children: props.options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
1379
+ "button",
1380
+ {
1381
+ type: "button",
1382
+ role: "option",
1383
+ "aria-selected": opt === props.display,
1384
+ className: `ctp-${props.label === "am-pm" ? "am-pm" : props.label.slice(0, -1)}`,
1385
+ onClick: () => props.onSelect(opt),
1386
+ children: opt
1387
+ },
1388
+ opt
1389
+ ))
1390
+ }
1391
+ )
1392
+ ]
1393
+ }
1394
+ );
1395
+ }
1396
+ function cx3(...parts) {
1397
+ return parts.filter(Boolean).join(" ");
1398
+ }
1399
+ function isTimeValue(value) {
1400
+ return typeof value === "object" && value !== null && !(value instanceof Date) && "formatted" in value && "hour24" in value;
1401
+ }
1402
+ function toDisplayString(value, format) {
1403
+ if (value === null) {
1404
+ return null;
1405
+ }
1406
+ if (typeof value === "string") {
1407
+ return value;
1408
+ }
1409
+ if (isTimeValue(value)) {
1410
+ return value.formatted;
1411
+ }
1412
+ return formatValue(dayjs__default.default(value), format);
1413
+ }
1414
+ function DateTimeInput(props) {
1415
+ const {
1416
+ value,
1417
+ defaultValue = null,
1418
+ onChange,
1419
+ asString,
1420
+ showSeconds = true,
1421
+ format,
1422
+ mode = "datetime",
1423
+ use12Hours = false,
1424
+ open: openProp,
1425
+ defaultOpen = false,
1426
+ onOpenChange,
1427
+ placeholder = "Select date and time",
1428
+ id,
1429
+ name,
1430
+ disabled = false,
1431
+ readOnly = true,
1432
+ className,
1433
+ inputClassName,
1434
+ style,
1435
+ "aria-labelledby": ariaLabelledBy,
1436
+ "aria-label": ariaLabel,
1437
+ ...pickerProps
1438
+ } = props;
1439
+ const resolvedFormat = react.useMemo(
1440
+ () => resolveFormat({ mode, format, use12Hours, showSeconds }),
1441
+ [mode, format, use12Hours, showSeconds]
1442
+ );
1443
+ const [anchorEl, setAnchorEl] = react.useState(null);
1444
+ const controlledFormatted = value !== void 0 ? formatValue(parseValue(value, resolvedFormat), resolvedFormat) : void 0;
1445
+ const defaultFormatted = react.useMemo(
1446
+ () => formatValue(parseValue(defaultValue, resolvedFormat), resolvedFormat),
1447
+ // only seed once from defaultValue
1448
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1449
+ []
1450
+ );
1451
+ const [internalValue, setInternalValue] = useControllableState({
1452
+ value: controlledFormatted,
1453
+ defaultValue: defaultFormatted,
1454
+ onChange: void 0
1455
+ });
1456
+ const [open, setOpen] = useControllableState({
1457
+ value: openProp,
1458
+ defaultValue: defaultOpen,
1459
+ onChange: onOpenChange
1460
+ });
1461
+ const display = internalValue ?? "";
1462
+ const setInputRef = react.useCallback((node) => {
1463
+ setAnchorEl(node);
1464
+ }, []);
1465
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cx3("ctp-input-root", className), style, children: [
1466
+ /* @__PURE__ */ jsxRuntime.jsx(
1467
+ "input",
1468
+ {
1469
+ ref: setInputRef,
1470
+ id,
1471
+ name,
1472
+ className: cx3("ctp-input", inputClassName),
1473
+ value: display,
1474
+ readOnly,
1475
+ disabled,
1476
+ placeholder,
1477
+ "aria-haspopup": "dialog",
1478
+ "aria-expanded": open,
1479
+ "aria-labelledby": ariaLabelledBy,
1480
+ "aria-label": ariaLabel ?? placeholder,
1481
+ onClick: () => {
1482
+ if (!disabled) {
1483
+ setOpen(true);
1484
+ }
1485
+ },
1486
+ onKeyDown: (event) => {
1487
+ if (disabled) {
1488
+ return;
1489
+ }
1490
+ if (event.key === "Enter" || event.key === " ") {
1491
+ event.preventDefault();
1492
+ setOpen(true);
1493
+ }
1494
+ }
1495
+ }
1496
+ ),
1497
+ /* @__PURE__ */ jsxRuntime.jsx(
1498
+ DateTime,
1499
+ {
1500
+ ...pickerProps,
1501
+ format,
1502
+ mode,
1503
+ use12Hours,
1504
+ asString,
1505
+ showSeconds,
1506
+ value: internalValue,
1507
+ open,
1508
+ onOpenChange: setOpen,
1509
+ popover: true,
1510
+ anchorEl,
1511
+ onChange: (next) => {
1512
+ setInternalValue(toDisplayString(next, resolvedFormat));
1513
+ onChange?.(next);
1514
+ }
1515
+ }
1516
+ )
1517
+ ] });
1518
+ }
1519
+
1520
+ // src/core/rangeController.ts
1521
+ var RangeController = class {
1522
+ constructor(options = {}) {
1523
+ this.listeners = /* @__PURE__ */ new Set();
1524
+ this.hoverEnd = null;
1525
+ this.calPanel = "day";
1526
+ this.subscribe = (listener) => {
1527
+ this.listeners.add(listener);
1528
+ return () => {
1529
+ this.listeners.delete(listener);
1530
+ };
1531
+ };
1532
+ this.getSnapshot = () => this.snapshot;
1533
+ this.getServerSnapshot = () => this.snapshot;
1534
+ this.options = { ...options };
1535
+ const format = options.format ?? DATE_FORMAT;
1536
+ const parsed = this.parseRange(options.value ?? options.defaultValue, format);
1537
+ this.start = parsed.start;
1538
+ this.end = parsed.end;
1539
+ this.viewMonth = (parsed.start ?? dayjs__default.default()).startOf("month");
1540
+ this.focusedDay = parsed.start ?? dayjs__default.default();
1541
+ this.open = options.open ?? (options.inline ? true : options.defaultOpen ?? true);
1542
+ this.snapshot = this.buildSnapshot();
1543
+ }
1544
+ parseRange(range, format) {
1545
+ return {
1546
+ start: parseValue(range?.start ?? null, format),
1547
+ end: parseValue(range?.end ?? null, format)
1548
+ };
1549
+ }
1550
+ setOptions(partial) {
1551
+ this.options = { ...this.options, ...partial };
1552
+ if (partial.open !== void 0) {
1553
+ this.open = partial.open;
1554
+ }
1555
+ if ("value" in partial) {
1556
+ if (partial.value === null) {
1557
+ this.start = null;
1558
+ this.end = null;
1559
+ this.hoverEnd = null;
1560
+ } else if (partial.value !== void 0) {
1561
+ const format = this.options.format ?? DATE_FORMAT;
1562
+ const parsed = this.parseRange(partial.value, format);
1563
+ this.start = parsed.start;
1564
+ this.end = parsed.end;
1565
+ if (parsed.start) {
1566
+ this.viewMonth = parsed.start.startOf("month");
1567
+ this.focusedDay = parsed.start;
1568
+ }
1569
+ }
1570
+ }
1571
+ this.emit();
1572
+ }
1573
+ getLabels() {
1574
+ return { ...DEFAULT_LABELS, ...this.options.labels };
1575
+ }
1576
+ emit() {
1577
+ this.snapshot = this.buildSnapshot();
1578
+ this.listeners.forEach((l) => l());
1579
+ }
1580
+ buildSnapshot() {
1581
+ const format = this.options.format ?? DATE_FORMAT;
1582
+ const weekStartsOn = this.options.weekStartsOn ?? 0;
1583
+ const locale = this.options.locale ?? "en";
1584
+ const weeks = buildCalendarMonth({
1585
+ viewMonth: this.viewMonth,
1586
+ rangeStart: this.start,
1587
+ rangeEnd: this.end,
1588
+ hoverEnd: this.start && !this.end ? this.hoverEnd : null,
1589
+ minDate: this.options.minDate,
1590
+ maxDate: this.options.maxDate,
1591
+ disablePastDates: this.options.disablePastDates,
1592
+ disableFutureDates: this.options.disableFutureDates,
1593
+ weekStartsOn
1594
+ });
1595
+ return {
1596
+ start: this.start,
1597
+ end: this.end,
1598
+ hoverEnd: this.hoverEnd,
1599
+ viewMonth: this.viewMonth,
1600
+ focusedDay: this.focusedDay,
1601
+ calPanel: this.calPanel,
1602
+ open: this.open,
1603
+ inline: Boolean(this.options.inline),
1604
+ format,
1605
+ asString: this.options.asString,
1606
+ weekStartsOn,
1607
+ locale,
1608
+ labels: this.getLabels(),
1609
+ className: this.options.className,
1610
+ weeks,
1611
+ weekdayLabels: getWeekdayLabels(locale, weekStartsOn)
1612
+ };
1613
+ }
1614
+ setOpen(open) {
1615
+ if (this.options.inline) {
1616
+ return;
1617
+ }
1618
+ this.open = open;
1619
+ this.options.onOpenChange?.(open);
1620
+ this.emit();
1621
+ }
1622
+ close() {
1623
+ if (!this.options.inline) {
1624
+ this.setOpen(false);
1625
+ }
1626
+ }
1627
+ setCalPanel(panel) {
1628
+ this.calPanel = panel;
1629
+ this.emit();
1630
+ }
1631
+ setViewMonth(next) {
1632
+ this.viewMonth = typeof next === "function" ? next(this.viewMonth) : next;
1633
+ this.emit();
1634
+ }
1635
+ navigatePrev() {
1636
+ if (this.calPanel === "day") {
1637
+ this.viewMonth = this.viewMonth.subtract(1, "month");
1638
+ } else if (this.calPanel === "month") {
1639
+ this.viewMonth = this.viewMonth.subtract(1, "year");
1640
+ } else {
1641
+ this.viewMonth = this.viewMonth.subtract(12, "year");
1642
+ }
1643
+ this.emit();
1644
+ }
1645
+ navigateNext() {
1646
+ if (this.calPanel === "day") {
1647
+ this.viewMonth = this.viewMonth.add(1, "month");
1648
+ } else if (this.calPanel === "month") {
1649
+ this.viewMonth = this.viewMonth.add(1, "year");
1650
+ } else {
1651
+ this.viewMonth = this.viewMonth.add(12, "year");
1652
+ }
1653
+ this.emit();
1654
+ }
1655
+ selectMonth(monthIndex) {
1656
+ this.viewMonth = this.viewMonth.month(monthIndex);
1657
+ this.calPanel = "day";
1658
+ this.emit();
1659
+ }
1660
+ selectYear(year) {
1661
+ this.viewMonth = this.viewMonth.year(year);
1662
+ this.calPanel = "month";
1663
+ this.emit();
1664
+ }
1665
+ emitRange(nextStart, nextEnd) {
1666
+ if (this.options.asString === false) {
1667
+ this.options.onChange?.({
1668
+ start: nextStart ? nextStart.toDate() : null,
1669
+ end: nextEnd ? nextEnd.toDate() : null
1670
+ });
1671
+ } else {
1672
+ if (this.options.asString === void 0) {
1673
+ warnAsStringDeprecation();
1674
+ }
1675
+ const format = this.options.format ?? DATE_FORMAT;
1676
+ this.options.onChange?.({
1677
+ start: formatValue(nextStart, format),
1678
+ end: formatValue(nextEnd, format)
1679
+ });
1680
+ }
1681
+ }
1682
+ pickDay(day) {
1683
+ if (!this.start || this.start && this.end) {
1684
+ this.start = day;
1685
+ this.end = null;
1686
+ this.hoverEnd = null;
1687
+ this.focusedDay = day;
1688
+ } else if (day.isBefore(this.start, "day")) {
1689
+ this.start = day;
1690
+ this.end = null;
1691
+ this.hoverEnd = null;
1692
+ this.focusedDay = day;
1693
+ } else {
1694
+ this.end = day;
1695
+ this.hoverEnd = null;
1696
+ this.focusedDay = day;
1697
+ }
1698
+ this.emit();
1699
+ }
1700
+ setHoverEnd(day) {
1701
+ if (this.start && !this.end) {
1702
+ this.hoverEnd = day;
1703
+ this.emit();
1704
+ }
1705
+ }
1706
+ handleGridKeyDown(key) {
1707
+ const snap = this.snapshot;
1708
+ let next = this.focusedDay;
1709
+ switch (key) {
1710
+ case "ArrowLeft":
1711
+ next = this.focusedDay.subtract(1, "day");
1712
+ break;
1713
+ case "ArrowRight":
1714
+ next = this.focusedDay.add(1, "day");
1715
+ break;
1716
+ case "ArrowUp":
1717
+ next = this.focusedDay.subtract(7, "day");
1718
+ break;
1719
+ case "ArrowDown":
1720
+ next = this.focusedDay.add(7, "day");
1721
+ break;
1722
+ case "Home":
1723
+ next = startOfWeek(this.focusedDay, snap.weekStartsOn);
1724
+ break;
1725
+ case "End":
1726
+ next = endOfWeek(this.focusedDay, snap.weekStartsOn);
1727
+ break;
1728
+ case "PageUp":
1729
+ next = this.focusedDay.subtract(1, "month");
1730
+ this.viewMonth = next.startOf("month");
1731
+ break;
1732
+ case "PageDown":
1733
+ next = this.focusedDay.add(1, "month");
1734
+ this.viewMonth = next.startOf("month");
1735
+ break;
1736
+ case "Enter":
1737
+ case " ": {
1738
+ const cell = snap.weeks.flat().find((d) => d.date.isSame(this.focusedDay, "day"));
1739
+ if (cell && !cell.isDisabled && cell.isCurrentMonth) {
1740
+ this.pickDay(cell.date);
1741
+ }
1742
+ return true;
1743
+ }
1744
+ default:
1745
+ return false;
1746
+ }
1747
+ this.focusedDay = next;
1748
+ if (!next.isSame(this.viewMonth, "month")) {
1749
+ this.viewMonth = next.startOf("month");
1750
+ }
1751
+ this.emit();
1752
+ return true;
1753
+ }
1754
+ confirm() {
1755
+ if (!this.start || !this.end) {
1756
+ return null;
1757
+ }
1758
+ this.emitRange(this.start, this.end);
1759
+ this.close();
1760
+ return {
1761
+ start: this.start.toDate(),
1762
+ end: this.end.toDate()
1763
+ };
1764
+ }
1765
+ clear() {
1766
+ this.start = null;
1767
+ this.end = null;
1768
+ this.hoverEnd = null;
1769
+ this.emitRange(null, null);
1770
+ this.emit();
1771
+ this.close();
1772
+ }
1773
+ };
1774
+ function cx4(...parts) {
1775
+ return parts.filter(Boolean).join(" ");
1776
+ }
1777
+ function DateTimeRange(props) {
1778
+ const {
1779
+ value,
1780
+ defaultValue = null,
1781
+ onChange,
1782
+ asString,
1783
+ format,
1784
+ minDate,
1785
+ maxDate,
1786
+ disablePastDates = false,
1787
+ disableFutureDates = false,
1788
+ weekStartsOn = 0,
1789
+ locale = "en",
1790
+ labels: labelsProp,
1791
+ inline = false,
1792
+ className,
1793
+ style,
1794
+ open: openProp,
1795
+ defaultOpen = true,
1796
+ onOpenChange
1797
+ } = props;
1798
+ const titleId = react.useId();
1799
+ const dialogRef = react.useRef(null);
1800
+ const [open, setOpen] = useControllableState({
1801
+ value: openProp,
1802
+ defaultValue: inline ? true : defaultOpen,
1803
+ onChange: onOpenChange
1804
+ });
1805
+ const controllerRef = react.useRef(null);
1806
+ if (!controllerRef.current) {
1807
+ controllerRef.current = new RangeController({
1808
+ value,
1809
+ defaultValue,
1810
+ onChange,
1811
+ asString,
1812
+ format,
1813
+ minDate,
1814
+ maxDate,
1815
+ disablePastDates,
1816
+ disableFutureDates,
1817
+ weekStartsOn,
1818
+ locale,
1819
+ labels: labelsProp,
1820
+ inline,
1821
+ className,
1822
+ open,
1823
+ onOpenChange: setOpen
1824
+ });
1825
+ }
1826
+ const controller = controllerRef.current;
1827
+ react.useEffect(() => {
1828
+ controller.setOptions({
1829
+ value,
1830
+ onChange,
1831
+ asString,
1832
+ format,
1833
+ minDate,
1834
+ maxDate,
1835
+ disablePastDates,
1836
+ disableFutureDates,
1837
+ weekStartsOn,
1838
+ locale,
1839
+ labels: labelsProp,
1840
+ inline,
1841
+ className,
1842
+ open,
1843
+ onOpenChange: setOpen
1844
+ });
1845
+ }, [
1846
+ controller,
1847
+ value,
1848
+ onChange,
1849
+ asString,
1850
+ format,
1851
+ minDate,
1852
+ maxDate,
1853
+ disablePastDates,
1854
+ disableFutureDates,
1855
+ weekStartsOn,
1856
+ locale,
1857
+ labelsProp,
1858
+ inline,
1859
+ className,
1860
+ open,
1861
+ setOpen
1862
+ ]);
1863
+ const snap = react.useSyncExternalStore(
1864
+ controller.subscribe,
1865
+ controller.getSnapshot,
1866
+ controller.getServerSnapshot
1867
+ );
1868
+ const close = react.useCallback(() => {
1869
+ if (!inline) {
1870
+ setOpen(false);
1871
+ }
1872
+ }, [inline, setOpen]);
1873
+ react.useEffect(() => {
1874
+ if (!(open && !inline)) {
1875
+ return;
1876
+ }
1877
+ return attachEscape(close);
1878
+ }, [open, inline, close]);
1879
+ react.useEffect(() => {
1880
+ if (!(open && !inline) || !dialogRef.current) {
1881
+ return;
1882
+ }
1883
+ return attachFocusTrap(dialogRef.current);
1884
+ }, [open, inline]);
1885
+ const onGridKeyDown = (event) => {
1886
+ if (controller.handleGridKeyDown(event.key)) {
1887
+ event.preventDefault();
1888
+ }
1889
+ };
1890
+ if (!open && !inline) {
1891
+ return null;
1892
+ }
1893
+ const statusText = snap.start && !snap.end ? snap.labels.selectEnd : `${snap.start ? formatLocalized(snap.start, "MMM D, YYYY", snap.locale) : snap.labels.start} \u2014 ${snap.end ? formatLocalized(snap.end, "MMM D, YYYY", snap.locale) : snap.labels.end}`;
1894
+ const picker = /* @__PURE__ */ jsxRuntime.jsxs(
1895
+ "div",
1896
+ {
1897
+ ref: dialogRef,
1898
+ className: cx4("ctp-calendar-time-picker", className),
1899
+ style,
1900
+ role: inline ? void 0 : "dialog",
1901
+ "aria-modal": inline ? void 0 : true,
1902
+ "aria-labelledby": titleId,
1903
+ children: [
1904
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ctp-header", children: /* @__PURE__ */ jsxRuntime.jsx("span", { id: titleId, className: "ctp-range-title", children: statusText }) }),
1905
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ctp-body ctp-body-calendar-date", children: [
1906
+ /* @__PURE__ */ jsxRuntime.jsx(
1907
+ CalendarHeader,
1908
+ {
1909
+ viewMonth: snap.viewMonth,
1910
+ setViewMonth: (next) => controller.setViewMonth(next),
1911
+ panel: snap.calPanel,
1912
+ onPanelChange: (p) => controller.setCalPanel(p),
1913
+ locale: snap.locale,
1914
+ labels: snap.labels
1915
+ }
1916
+ ),
1917
+ snap.calPanel === "day" && /* @__PURE__ */ jsxRuntime.jsxs(
1918
+ "div",
1919
+ {
1920
+ className: "ctp-main-calendar",
1921
+ role: "grid",
1922
+ "aria-label": snap.labels.chooseDateRange,
1923
+ tabIndex: 0,
1924
+ onKeyDown: onGridKeyDown,
1925
+ children: [
1926
+ snap.weekdayLabels.map((label) => /* @__PURE__ */ jsxRuntime.jsx(
1927
+ "div",
1928
+ {
1929
+ className: "ctp-box ctp-box-days",
1930
+ role: "columnheader",
1931
+ children: label
1932
+ },
1933
+ label
1934
+ )),
1935
+ snap.weeks.map(
1936
+ (week) => week.map((dayData) => {
1937
+ const focused = dayData.date.isSame(snap.focusedDay, "day");
1938
+ const disabled = !dayData.isCurrentMonth || dayData.isDisabled;
1939
+ return /* @__PURE__ */ jsxRuntime.jsx(
1940
+ "button",
1941
+ {
1942
+ type: "button",
1943
+ role: "gridcell",
1944
+ tabIndex: focused ? 0 : -1,
1945
+ "aria-selected": Boolean(
1946
+ dayData.isRangeStart || dayData.isRangeEnd
1947
+ ),
1948
+ "aria-disabled": disabled,
1949
+ disabled,
1950
+ "aria-label": formatLocalized(
1951
+ dayData.date,
1952
+ "dddd, MMMM D, YYYY",
1953
+ snap.locale
1954
+ ),
1955
+ className: cx4(
1956
+ "ctp-box",
1957
+ "ctp-box-date",
1958
+ !dayData.isCurrentMonth && "not-current-month",
1959
+ dayData.isDisabled && "disabled-date",
1960
+ dayData.isWeekend && "weekend-day",
1961
+ dayData.isCurrentDate && "ctp-today",
1962
+ dayData.isInRange && "ctp-in-range",
1963
+ dayData.isRangeStart && "ctp-range-start",
1964
+ dayData.isRangeEnd && "ctp-range-end"
1965
+ ),
1966
+ onClick: () => {
1967
+ if (!disabled) {
1968
+ controller.pickDay(dayData.date);
1969
+ }
1970
+ },
1971
+ onMouseEnter: () => {
1972
+ if (!disabled && snap.start && !snap.end) {
1973
+ controller.setHoverEnd(dayData.date);
1974
+ }
1975
+ },
1976
+ children: dayData.date.format("D")
1977
+ },
1978
+ dayData.date.format("YYYY-MM-DD")
1979
+ );
1980
+ })
1981
+ )
1982
+ ]
1983
+ }
1984
+ )
1985
+ ] }),
1986
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ctp-footer", children: [
1987
+ /* @__PURE__ */ jsxRuntime.jsx(
1988
+ "button",
1989
+ {
1990
+ type: "button",
1991
+ className: "close-button",
1992
+ onClick: () => {
1993
+ controller.clear();
1994
+ close();
1995
+ },
1996
+ children: snap.labels.clear
1997
+ }
1998
+ ),
1999
+ !inline && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "ctp-cancel", onClick: close, children: snap.labels.close }),
2000
+ /* @__PURE__ */ jsxRuntime.jsx(
2001
+ "button",
2002
+ {
2003
+ type: "button",
2004
+ onClick: () => {
2005
+ controller.confirm();
2006
+ close();
2007
+ },
2008
+ disabled: !snap.start || !snap.end,
2009
+ children: snap.labels.ok
2010
+ }
2011
+ )
2012
+ ] })
2013
+ ]
2014
+ }
2015
+ );
2016
+ if (inline) {
2017
+ return picker;
2018
+ }
2019
+ return reactDom.createPortal(
2020
+ /* @__PURE__ */ jsxRuntime.jsx(
2021
+ "div",
2022
+ {
2023
+ className: "ctp-calendar-time-picker-absolute-container",
2024
+ onClick: (event) => {
2025
+ if (event.target === event.currentTarget) {
2026
+ close();
2027
+ }
2028
+ },
2029
+ children: picker
2030
+ }
2031
+ ),
2032
+ document.body
2033
+ );
2034
+ }
2035
+
2036
+ // src/index.ts
2037
+ var DateTimeWithExtras = DateTime;
2038
+ DateTimeWithExtras.Input = DateTimeInput;
2039
+ DateTimeWithExtras.Range = DateTimeRange;
2040
+ var src_default = DateTimeWithExtras;
2041
+
2042
+ Object.defineProperty(exports, "dayjs", {
2043
+ enumerable: true,
2044
+ get: function () { return dayjs__default.default; }
2045
+ });
2046
+ exports.DATE_FORMAT = DATE_FORMAT;
2047
+ exports.DEFAULT_FORMAT = DEFAULT_FORMAT;
2048
+ exports.DEFAULT_LABELS = DEFAULT_LABELS;
2049
+ exports.DateTime = DateTime;
2050
+ exports.DateTimeInput = DateTimeInput;
2051
+ exports.DateTimeRange = DateTimeRange;
2052
+ exports.TIME_FORMAT = TIME_FORMAT;
2053
+ exports.buildCalendarMonth = buildCalendarMonth;
2054
+ exports.default = src_default;