@kopexa/date-picker 1.2.1 → 1.4.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.
@@ -1,6 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { DatePickerValueChangeDetails, DatePickerRootProps } from '@ark-ui/react/date-picker';
3
3
  import { DateValue } from '@internationalized/date';
4
+ import { ReactElement } from 'react';
4
5
 
5
6
  type DatePickerFieldProps = {
6
7
  /** Label text */
@@ -35,7 +36,43 @@ type DatePickerFieldProps = {
35
36
  className?: string;
36
37
  /** Additional Ark UI Root props */
37
38
  rootProps?: Partial<DatePickerRootProps>;
39
+ /**
40
+ * Visual variant.
41
+ * - `"input"` (default): bordered input + calendar button + optional inline time/clear
42
+ * - `"trigger"`: a single ghost-style button that opens the popover
43
+ * with the calendar (and time picker, if `showTime`). Use `trigger`
44
+ * to provide a custom trigger element.
45
+ */
46
+ variant?: "input" | "trigger";
47
+ /**
48
+ * Custom trigger element rendered in `variant="trigger"` mode. Wrapped
49
+ * with `DatePickerTrigger asChild`, so any clickable element works
50
+ * (sight Button, Chip, etc.). The element should display the value —
51
+ * pass `<DatePickerValueText placeholder={…} />` as its children, or
52
+ * use the `formatValue` prop for default formatting.
53
+ */
54
+ trigger?: ReactElement;
55
+ /**
56
+ * Custom formatter for the trigger label (only used by the default
57
+ * `variant="trigger"` button). Receives the selected `DateValue` and
58
+ * returns a display string. Defaults to `Intl.DateTimeFormat` with the
59
+ * picker's locale, including time when `showTime` is true.
60
+ */
61
+ formatValue?: (value: DateValue) => string;
62
+ /**
63
+ * When provided, a "Save" button is rendered in the popover footer.
64
+ * `onValueChange` still fires per interaction so the picker stays
65
+ * controllable, but `onSave` is the explicit commit signal — callers
66
+ * that need draft-then-commit semantics (e.g. avoiding a save on every
67
+ * minute increment of the time picker) should listen here instead of
68
+ * `onValueChange`. Clicking Save also closes the popover.
69
+ */
70
+ onSave?: (details: DatePickerValueChangeDetails) => void;
71
+ /**
72
+ * Override the Save button label. Defaults to the i18n message.
73
+ */
74
+ saveLabel?: string;
38
75
  };
39
- declare function DatePickerField({ label, value, defaultValue, onValueChange, showTime, clearable, locale: localeProp, min, max, disabled, readOnly, placeholder: placeholderProp, todayLabel: todayLabelProp, clearLabel: clearLabelProp, className, rootProps, }: DatePickerFieldProps): react_jsx_runtime.JSX.Element;
76
+ declare function DatePickerField({ label, value, defaultValue, onValueChange, showTime, clearable, locale: localeProp, min, max, disabled, readOnly, placeholder: placeholderProp, todayLabel: todayLabelProp, clearLabel: clearLabelProp, className, rootProps, variant, trigger, formatValue, onSave, saveLabel: saveLabelProp, }: DatePickerFieldProps): react_jsx_runtime.JSX.Element;
40
77
 
41
78
  export { DatePickerField, type DatePickerFieldProps };
@@ -58,6 +58,11 @@ var datePickerMessages = (0, import_i18n.defineMessages)({
58
58
  id: "date-picker.select_date_and_time",
59
59
  defaultMessage: "Select date and time",
60
60
  description: "Placeholder for date+time input"
61
+ },
62
+ save: {
63
+ id: "date-picker.save",
64
+ defaultMessage: "Save",
65
+ description: "Button to commit the staged date selection (only shown when an onSave callback is provided)"
61
66
  }
62
67
  });
63
68
 
@@ -139,6 +144,55 @@ function XIcon({ className }) {
139
144
  }
140
145
  );
141
146
  }
147
+ function getDatePartOrder(locale) {
148
+ try {
149
+ const fmt = new Intl.DateTimeFormat(locale, {
150
+ day: "2-digit",
151
+ month: "2-digit",
152
+ year: "numeric"
153
+ });
154
+ return fmt.formatToParts(new Date(2024, 0, 1)).filter(
155
+ (p) => p.type === "day" || p.type === "month" || p.type === "year"
156
+ ).map((p) => p.type);
157
+ } catch {
158
+ return ["day", "month", "year"];
159
+ }
160
+ }
161
+ function finalizeDateParts(parts) {
162
+ var _a, _b, _c;
163
+ const day = (_a = parts.day) != null ? _a : 1;
164
+ const month = (_b = parts.month) != null ? _b : 1;
165
+ let year = (_c = parts.year) != null ? _c : (/* @__PURE__ */ new Date()).getFullYear();
166
+ if (year < 100) year = year < 50 ? 2e3 + year : 1900 + year;
167
+ if (month < 1 || month > 12) return void 0;
168
+ if (day < 1 || day > 31) return void 0;
169
+ return { year, month, day };
170
+ }
171
+ function parseLocalizedDate(value, locale) {
172
+ const trimmed = value.trim();
173
+ if (!trimmed) return void 0;
174
+ const order = getDatePartOrder(locale);
175
+ const segments = trimmed.split(/[./\-\s]+/).filter(Boolean);
176
+ if (segments.length === 3 && segments.every((s) => /^\d+$/.test(s))) {
177
+ const map2 = {};
178
+ order.forEach((field, i) => {
179
+ map2[field] = Number.parseInt(segments[i], 10);
180
+ });
181
+ return finalizeDateParts(map2);
182
+ }
183
+ const digits = trimmed.replace(/\D/g, "");
184
+ const widths = digits.length === 8 ? { day: 2, month: 2, year: 4 } : digits.length === 6 ? { day: 2, month: 2, year: 2 } : digits.length === 4 ? { day: 2, month: 2, year: 0 } : null;
185
+ if (!widths) return void 0;
186
+ let pos = 0;
187
+ const map = {};
188
+ for (const field of order) {
189
+ const w = widths[field];
190
+ if (w === 0) continue;
191
+ map[field] = Number.parseInt(digits.slice(pos, pos + w), 10);
192
+ pos += w;
193
+ }
194
+ return finalizeDateParts(map);
195
+ }
142
196
  var styles = {
143
197
  control: "relative flex items-center",
144
198
  input: "w-full h-9 rounded-md border bg-transparent pl-3 pr-9 text-sm outline-none focus:ring-2 focus:ring-ring",
@@ -162,9 +216,14 @@ var styles = {
162
216
  yearCellTrigger: "inline-flex items-center justify-center w-full py-2 text-sm rounded-md transition-colors hover:bg-muted data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[disabled]:text-muted-foreground/30 data-[disabled]:pointer-events-none",
163
217
  footer: "flex items-center gap-1 pt-2 mt-2 border-t",
164
218
  footerButton: "text-sm px-2 py-1 rounded-md hover:bg-muted transition-colors",
219
+ footerSaveButton: "ml-auto text-sm px-3 py-1 rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors",
165
220
  timeInput: "h-9 rounded-md border bg-transparent px-3 text-sm outline-none focus:ring-2 focus:ring-ring",
166
221
  label: "text-sm font-medium",
167
- timeTrigger: "flex-1 h-9 rounded-md border bg-transparent px-3 text-sm text-left hover:bg-muted transition-colors flex items-center justify-between"
222
+ timeTrigger: "flex-1 h-9 rounded-md border bg-transparent px-3 text-sm text-left hover:bg-muted transition-colors flex items-center justify-between",
223
+ defaultGhostTrigger: "inline-flex items-center gap-2 h-9 rounded-md px-2.5 text-sm font-normal text-foreground transition-colors hover:bg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[placeholder]:text-muted-foreground",
224
+ defaultGhostPlaceholder: "text-muted-foreground",
225
+ timeRow: "mt-2 pt-2 border-t flex items-center gap-2",
226
+ timeRowLabel: "text-xs text-muted-foreground"
168
227
  };
169
228
  function DayView() {
170
229
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerView, { view: "day", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerContext, { children: (api) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
@@ -269,9 +328,19 @@ function YearView() {
269
328
  )) }) })
270
329
  ] }) }) });
271
330
  }
331
+ function CalendarPanel() {
332
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
333
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DayView, {}),
334
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MonthView, {}),
335
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(YearView, {})
336
+ ] });
337
+ }
272
338
  function CalendarFooter({
273
339
  todayLabel,
274
- clearLabel
340
+ clearLabel,
341
+ saveLabel,
342
+ clearable = true,
343
+ onSave
275
344
  }) {
276
345
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerContext, { children: (api) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: styles.footer, children: [
277
346
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -283,7 +352,7 @@ function CalendarFooter({
283
352
  children: todayLabel
284
353
  }
285
354
  ),
286
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
355
+ clearable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
287
356
  "button",
288
357
  {
289
358
  type: "button",
@@ -291,6 +360,64 @@ function CalendarFooter({
291
360
  className: `${styles.footerButton} text-destructive`,
292
361
  children: clearLabel
293
362
  }
363
+ ),
364
+ onSave && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
365
+ "button",
366
+ {
367
+ type: "button",
368
+ onClick: () => {
369
+ onSave({
370
+ value: api.value,
371
+ valueAsString: api.valueAsString,
372
+ view: api.view
373
+ });
374
+ api.setOpen(false);
375
+ },
376
+ className: styles.footerSaveButton,
377
+ children: saveLabel
378
+ }
379
+ )
380
+ ] }) });
381
+ }
382
+ function DateTimeFooter({
383
+ todayLabel,
384
+ clearLabel,
385
+ saveLabel,
386
+ clearable = true,
387
+ onSelectNow,
388
+ onClear,
389
+ onSave
390
+ }) {
391
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerContext, { children: (api) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: styles.footer, children: [
392
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
393
+ "button",
394
+ {
395
+ type: "button",
396
+ onClick: onSelectNow,
397
+ className: `${styles.footerButton} text-foreground`,
398
+ children: todayLabel
399
+ }
400
+ ),
401
+ clearable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
402
+ "button",
403
+ {
404
+ type: "button",
405
+ onClick: onClear,
406
+ className: `${styles.footerButton} text-destructive`,
407
+ children: clearLabel
408
+ }
409
+ ),
410
+ onSave && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
411
+ "button",
412
+ {
413
+ type: "button",
414
+ onClick: () => {
415
+ onSave();
416
+ api.setOpen(false);
417
+ },
418
+ className: styles.footerSaveButton,
419
+ children: saveLabel
420
+ }
294
421
  )
295
422
  ] }) });
296
423
  }
@@ -310,16 +437,49 @@ function DatePickerField({
310
437
  todayLabel: todayLabelProp,
311
438
  clearLabel: clearLabelProp,
312
439
  className,
313
- rootProps
440
+ rootProps,
441
+ variant = "input",
442
+ trigger,
443
+ formatValue,
444
+ onSave,
445
+ saveLabel: saveLabelProp
314
446
  }) {
315
447
  var _a;
316
448
  const intl = (0, import_i18n2.useSafeIntl)();
317
449
  const locale = (_a = localeProp != null ? localeProp : intl.locale) != null ? _a : "en-US";
318
450
  const todayLabel = todayLabelProp != null ? todayLabelProp : intl.formatMessage(datePickerMessages.today);
319
451
  const clearLabel = clearLabelProp != null ? clearLabelProp : intl.formatMessage(datePickerMessages.clear);
452
+ const saveLabel = saveLabelProp != null ? saveLabelProp : intl.formatMessage(datePickerMessages.save);
320
453
  const placeholder = placeholderProp != null ? placeholderProp : intl.formatMessage(
321
454
  showTime ? datePickerMessages.select_date_and_time : datePickerMessages.select_date
322
455
  );
456
+ if (variant === "trigger") {
457
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
458
+ DateTriggerPickerField,
459
+ {
460
+ label,
461
+ value,
462
+ defaultValue,
463
+ onValueChange,
464
+ showTime,
465
+ clearable,
466
+ locale,
467
+ min,
468
+ max,
469
+ disabled,
470
+ readOnly,
471
+ placeholder,
472
+ todayLabel,
473
+ clearLabel,
474
+ saveLabel,
475
+ className,
476
+ rootProps,
477
+ trigger,
478
+ formatValue,
479
+ onSave
480
+ }
481
+ );
482
+ }
323
483
  if (showTime) {
324
484
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
325
485
  DateTimePickerField,
@@ -337,8 +497,10 @@ function DatePickerField({
337
497
  placeholder,
338
498
  todayLabel,
339
499
  clearLabel,
500
+ saveLabel,
340
501
  className,
341
- rootProps
502
+ rootProps,
503
+ onSave
342
504
  }
343
505
  );
344
506
  }
@@ -356,6 +518,10 @@ function DatePickerField({
356
518
  selectionMode: "single",
357
519
  outsideDaySelectable: true,
358
520
  closeOnSelect: true,
521
+ parse: (input, details) => {
522
+ const parts = parseLocalizedDate(input, details.locale);
523
+ return parts ? new import_date.CalendarDate(parts.year, parts.month, parts.day) : void 0;
524
+ },
359
525
  className,
360
526
  ...rootProps,
361
527
  children: [
@@ -374,10 +540,17 @@ function DatePickerField({
374
540
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerTrigger, { className: styles.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CalendarIcon, { className: "size-4" }) })
375
541
  ] }),
376
542
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_portal.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerPositioner, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_date_picker.DatePickerContent, { className: styles.content, children: [
377
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DayView, {}),
378
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MonthView, {}),
379
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(YearView, {}),
380
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CalendarFooter, { todayLabel, clearLabel })
543
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CalendarPanel, {}),
544
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
545
+ CalendarFooter,
546
+ {
547
+ todayLabel,
548
+ clearLabel,
549
+ saveLabel,
550
+ clearable,
551
+ onSave
552
+ }
553
+ )
381
554
  ] }) }) })
382
555
  ]
383
556
  }
@@ -397,14 +570,17 @@ function DateTimePickerField({
397
570
  placeholder: placeholderProp,
398
571
  todayLabel: todayLabelProp,
399
572
  clearLabel: clearLabelProp,
573
+ saveLabel: saveLabelProp,
400
574
  className,
401
- rootProps
575
+ rootProps,
576
+ onSave
402
577
  }) {
403
578
  var _a;
404
579
  const intl = (0, import_i18n2.useSafeIntl)();
405
580
  const locale = (_a = localeProp != null ? localeProp : intl.locale) != null ? _a : "en-US";
406
581
  const todayLabel = todayLabelProp != null ? todayLabelProp : intl.formatMessage(datePickerMessages.today);
407
582
  const clearLabel = clearLabelProp != null ? clearLabelProp : intl.formatMessage(datePickerMessages.clear);
583
+ const saveLabel = saveLabelProp != null ? saveLabelProp : intl.formatMessage(datePickerMessages.save);
408
584
  const placeholder = placeholderProp != null ? placeholderProp : intl.formatMessage(datePickerMessages.select_date_and_time);
409
585
  const [internalValue, setInternalValue] = (0, import_react.useState)(() => {
410
586
  const initial = valueProp != null ? valueProp : defaultValue;
@@ -475,6 +651,22 @@ function DateTimePickerField({
475
651
  view: "day"
476
652
  });
477
653
  }, [onValueChange]);
654
+ const handleSelectNow = (0, import_react.useCallback)(() => {
655
+ const now = /* @__PURE__ */ new Date();
656
+ const nowValue = new import_date.CalendarDateTime(
657
+ now.getFullYear(),
658
+ now.getMonth() + 1,
659
+ now.getDate(),
660
+ now.getHours(),
661
+ now.getMinutes()
662
+ );
663
+ setInternalValue([nowValue]);
664
+ onValueChange == null ? void 0 : onValueChange({
665
+ value: [nowValue],
666
+ valueAsString: [nowValue.toString()],
667
+ view: "day"
668
+ });
669
+ }, [onValueChange]);
478
670
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
479
671
  import_date_picker.DatePickerRoot,
480
672
  {
@@ -488,13 +680,33 @@ function DateTimePickerField({
488
680
  selectionMode: "single",
489
681
  outsideDaySelectable: true,
490
682
  closeOnSelect: false,
683
+ parse: (input, details) => {
684
+ const parts = parseLocalizedDate(input, details.locale);
685
+ if (!parts) return void 0;
686
+ const prev = currentValue[0];
687
+ const hour = prev && "hour" in prev ? prev.hour : 0;
688
+ const minute = prev && "minute" in prev ? prev.minute : 0;
689
+ return new import_date.CalendarDateTime(
690
+ parts.year,
691
+ parts.month,
692
+ parts.day,
693
+ hour,
694
+ minute
695
+ );
696
+ },
491
697
  className,
492
698
  ...rootProps,
493
699
  children: [
494
700
  label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerLabel, { className: styles.label, children: label }),
495
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_date_picker.DatePickerControl, { className: "flex items-center gap-1", children: [
701
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_date_picker.DatePickerControl, { className: "flex items-center", children: [
496
702
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative flex-1 flex items-center", children: [
497
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerInput, { className: styles.input, placeholder }),
703
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
704
+ import_date_picker.DatePickerInput,
705
+ {
706
+ className: "w-full h-9 rounded-l-md border border-r-0 bg-transparent pl-3 pr-9 text-sm outline-none focus:ring-2 focus:ring-ring",
707
+ placeholder
708
+ }
709
+ ),
498
710
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerTrigger, { className: styles.trigger, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CalendarIcon, { className: "size-4" }) })
499
711
  ] }),
500
712
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -505,23 +717,215 @@ function DateTimePickerField({
505
717
  onChange: handleTimeChange,
506
718
  disabled,
507
719
  readOnly,
508
- className: styles.timeInput
720
+ className: `h-9 border bg-transparent px-3 text-sm outline-none focus:ring-2 focus:ring-ring ${clearable && !disabled && !readOnly ? "border-r-0" : "rounded-r-md"}`
509
721
  }
510
722
  ),
511
723
  clearable && !disabled && !readOnly && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
512
- import_date_picker.DatePickerClearTrigger,
724
+ "button",
513
725
  {
514
- className: "inline-flex items-center justify-center size-9 rounded-md border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
726
+ type: "button",
727
+ className: "inline-flex items-center justify-center size-9 rounded-r-md border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
515
728
  onClick: handleClear,
516
729
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(XIcon, { className: "size-4" })
517
730
  }
518
731
  )
519
732
  ] }),
520
733
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_portal.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerPositioner, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_date_picker.DatePickerContent, { className: styles.content, children: [
521
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DayView, {}),
522
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MonthView, {}),
523
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(YearView, {}),
524
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CalendarFooter, { todayLabel, clearLabel })
734
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CalendarPanel, {}),
735
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
736
+ DateTimeFooter,
737
+ {
738
+ todayLabel,
739
+ clearLabel,
740
+ saveLabel,
741
+ clearable,
742
+ onSelectNow: handleSelectNow,
743
+ onClear: handleClear,
744
+ onSave: onSave ? () => onSave({
745
+ value: currentValue,
746
+ valueAsString: currentValue.map((v) => v.toString()),
747
+ view: "day"
748
+ }) : void 0
749
+ }
750
+ )
751
+ ] }) }) })
752
+ ]
753
+ }
754
+ );
755
+ }
756
+ function DateTriggerPickerField({
757
+ label,
758
+ value: valueProp,
759
+ defaultValue,
760
+ onValueChange,
761
+ showTime = false,
762
+ clearable = true,
763
+ locale: localeProp,
764
+ min,
765
+ max,
766
+ disabled,
767
+ readOnly,
768
+ placeholder: placeholderProp,
769
+ todayLabel: todayLabelProp,
770
+ clearLabel: clearLabelProp,
771
+ saveLabel: saveLabelProp,
772
+ className,
773
+ rootProps,
774
+ trigger,
775
+ formatValue,
776
+ onSave
777
+ }) {
778
+ var _a;
779
+ const intl = (0, import_i18n2.useSafeIntl)();
780
+ const locale = (_a = localeProp != null ? localeProp : intl.locale) != null ? _a : "en-US";
781
+ const todayLabel = todayLabelProp != null ? todayLabelProp : intl.formatMessage(datePickerMessages.today);
782
+ const clearLabel = clearLabelProp != null ? clearLabelProp : intl.formatMessage(datePickerMessages.clear);
783
+ const saveLabel = saveLabelProp != null ? saveLabelProp : intl.formatMessage(datePickerMessages.save);
784
+ const placeholder = placeholderProp != null ? placeholderProp : intl.formatMessage(
785
+ showTime ? datePickerMessages.select_date_and_time : datePickerMessages.select_date
786
+ );
787
+ const [draft, setDraft] = (0, import_react.useState)(
788
+ () => {
789
+ var _a2;
790
+ return (_a2 = valueProp != null ? valueProp : defaultValue) != null ? _a2 : [];
791
+ }
792
+ );
793
+ const [committed, setCommitted] = (0, import_react.useState)(
794
+ () => {
795
+ var _a2;
796
+ return (_a2 = valueProp != null ? valueProp : defaultValue) != null ? _a2 : [];
797
+ }
798
+ );
799
+ (0, import_react.useEffect)(() => {
800
+ if (valueProp !== void 0) {
801
+ setCommitted(valueProp);
802
+ if (!onSave) setDraft(valueProp);
803
+ }
804
+ }, [valueProp, onSave]);
805
+ const currentValue = onSave ? draft : valueProp != null ? valueProp : draft;
806
+ const handleDateChange = (0, import_react.useCallback)(
807
+ (details) => {
808
+ const next = details.value[0];
809
+ if (!showTime || !next) {
810
+ setDraft(details.value);
811
+ onValueChange == null ? void 0 : onValueChange(details);
812
+ return;
813
+ }
814
+ const prev = currentValue[0];
815
+ const prevHour = prev && "hour" in prev ? prev.hour : 0;
816
+ const prevMinute = prev && "minute" in prev ? prev.minute : 0;
817
+ const merged = new import_date.CalendarDateTime(
818
+ next.year,
819
+ next.month,
820
+ next.day,
821
+ prevHour,
822
+ prevMinute
823
+ );
824
+ setDraft([merged]);
825
+ onValueChange == null ? void 0 : onValueChange({ ...details, value: [merged] });
826
+ },
827
+ [currentValue, onValueChange, showTime]
828
+ );
829
+ const handleTimeChange = (0, import_react.useCallback)(
830
+ (e) => {
831
+ const [hours, minutes] = e.currentTarget.value.split(":").map(Number);
832
+ const prev = currentValue[0];
833
+ const base = prev && "hour" in prev ? prev : prev ? new import_date.CalendarDateTime(prev.year, prev.month, prev.day, 0, 0) : (() => {
834
+ const now = /* @__PURE__ */ new Date();
835
+ return new import_date.CalendarDateTime(
836
+ now.getFullYear(),
837
+ now.getMonth() + 1,
838
+ now.getDate(),
839
+ 0,
840
+ 0
841
+ );
842
+ })();
843
+ const updated = base.set({ hour: hours, minute: minutes });
844
+ setDraft([updated]);
845
+ onValueChange == null ? void 0 : onValueChange({
846
+ value: [updated],
847
+ valueAsString: [updated.toString()],
848
+ view: "day"
849
+ });
850
+ },
851
+ [currentValue, onValueChange]
852
+ );
853
+ const userOnOpenChange = rootProps == null ? void 0 : rootProps.onOpenChange;
854
+ const handleOpenChange = (0, import_react.useCallback)(
855
+ (details) => {
856
+ if (onSave && details.open) {
857
+ setDraft(committed);
858
+ }
859
+ userOnOpenChange == null ? void 0 : userOnOpenChange(details);
860
+ },
861
+ [committed, onSave, userOnOpenChange]
862
+ );
863
+ const handleSaveCommit = (0, import_react.useCallback)(
864
+ (details) => {
865
+ setCommitted(details.value);
866
+ onSave == null ? void 0 : onSave(details);
867
+ },
868
+ [onSave]
869
+ );
870
+ const formatter = (0, import_react.useMemo)(() => {
871
+ if (formatValue) return formatValue;
872
+ const fmt = new Intl.DateTimeFormat(
873
+ locale,
874
+ showTime ? { dateStyle: "medium", timeStyle: "short" } : { dateStyle: "medium" }
875
+ );
876
+ return (v) => fmt.format(v.toDate((0, import_date.getLocalTimeZone)()));
877
+ }, [formatValue, locale, showTime]);
878
+ const timeValue = currentValue[0] && "hour" in currentValue[0] ? `${String(currentValue[0].hour).padStart(2, "0")}:${String(currentValue[0].minute).padStart(2, "0")}` : "";
879
+ const triggerElement = trigger != null ? trigger : onSave ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: styles.defaultGhostTrigger, children: [
880
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CalendarIcon, { className: "size-4 shrink-0 opacity-70" }),
881
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: committed[0] ? formatter(committed[0]) : placeholder })
882
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: styles.defaultGhostTrigger, children: [
883
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CalendarIcon, { className: "size-4 shrink-0 opacity-70" }),
884
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerValueText, { placeholder, children: ({ value }) => formatter(value) })
885
+ ] });
886
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
887
+ import_date_picker.DatePickerRoot,
888
+ {
889
+ value: currentValue,
890
+ defaultValue,
891
+ onValueChange: handleDateChange,
892
+ locale,
893
+ min,
894
+ max,
895
+ disabled,
896
+ readOnly,
897
+ selectionMode: "single",
898
+ outsideDaySelectable: true,
899
+ closeOnSelect: !showTime && !onSave,
900
+ className,
901
+ ...rootProps,
902
+ onOpenChange: handleOpenChange,
903
+ children: [
904
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerLabel, { className: styles.label, children: label }),
905
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerControl, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerTrigger, { asChild: true, children: triggerElement }) }),
906
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_portal.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_date_picker.DatePickerPositioner, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_date_picker.DatePickerContent, { className: styles.content, children: [
907
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CalendarPanel, {}),
908
+ showTime && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.timeRow, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
909
+ "input",
910
+ {
911
+ type: "time",
912
+ value: timeValue,
913
+ onChange: handleTimeChange,
914
+ disabled,
915
+ readOnly,
916
+ className: styles.timeInput
917
+ }
918
+ ) }),
919
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
920
+ CalendarFooter,
921
+ {
922
+ todayLabel,
923
+ clearLabel,
924
+ saveLabel,
925
+ clearable,
926
+ onSave: onSave ? handleSaveCommit : void 0
927
+ }
928
+ )
525
929
  ] }) }) })
526
930
  ]
527
931
  }
@@ -2,8 +2,8 @@
2
2
  "use client";
3
3
  import {
4
4
  DatePickerField
5
- } from "./chunk-VYQ6BFFN.mjs";
6
- import "./chunk-HPM5Y2V6.mjs";
5
+ } from "./chunk-BA6CJAOH.mjs";
6
+ import "./chunk-6IFLG64O.mjs";
7
7
  import "./chunk-C7GZJJIK.mjs";
8
8
  export {
9
9
  DatePickerField
@@ -24,6 +24,11 @@ declare const datePickerMessages: {
24
24
  defaultMessage: string;
25
25
  description: string;
26
26
  };
27
+ save: {
28
+ id: string;
29
+ defaultMessage: string;
30
+ description: string;
31
+ };
27
32
  };
28
33
 
29
34
  export { datePickerMessages };
@@ -24,6 +24,11 @@ declare const datePickerMessages: {
24
24
  defaultMessage: string;
25
25
  description: string;
26
26
  };
27
+ save: {
28
+ id: string;
29
+ defaultMessage: string;
30
+ description: string;
31
+ };
27
32
  };
28
33
 
29
34
  export { datePickerMessages };
@@ -50,6 +50,11 @@ var datePickerMessages = (0, import_i18n.defineMessages)({
50
50
  id: "date-picker.select_date_and_time",
51
51
  defaultMessage: "Select date and time",
52
52
  description: "Placeholder for date+time input"
53
+ },
54
+ save: {
55
+ id: "date-picker.save",
56
+ defaultMessage: "Save",
57
+ description: "Button to commit the staged date selection (only shown when an onSave callback is provided)"
53
58
  }
54
59
  });
55
60
  // Annotate the CommonJS export names for ESM import in node:
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  datePickerMessages
4
- } from "./chunk-HPM5Y2V6.mjs";
4
+ } from "./chunk-6IFLG64O.mjs";
5
5
  import "./chunk-C7GZJJIK.mjs";
6
6
  export {
7
7
  datePickerMessages