@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.
- package/dist/{chunk-HPM5Y2V6.mjs → chunk-6IFLG64O.mjs} +5 -0
- package/dist/{chunk-VYQ6BFFN.mjs → chunk-BA6CJAOH.mjs} +431 -22
- package/dist/date-picker-field.d.mts +38 -1
- package/dist/date-picker-field.d.ts +38 -1
- package/dist/date-picker-field.js +423 -19
- package/dist/date-picker-field.mjs +2 -2
- package/dist/date-picker-messages.d.mts +5 -0
- package/dist/date-picker-messages.d.ts +5 -0
- package/dist/date-picker-messages.js +5 -0
- package/dist/date-picker-messages.mjs +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +423 -19
- package/dist/index.mjs +2 -2
- package/package.json +2 -2
|
@@ -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)(
|
|
378
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
379
|
-
|
|
380
|
-
|
|
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
|
|
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)(
|
|
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:
|
|
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
|
-
|
|
724
|
+
"button",
|
|
513
725
|
{
|
|
514
|
-
|
|
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)(
|
|
522
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
523
|
-
|
|
524
|
-
|
|
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
|
}
|
|
@@ -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:
|