@baseplate-dev/ui-components
Version:
Shared UI component library
100 lines • 5.29 kB
JavaScript
'use client';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { format, parseISO, set } from 'date-fns';
import { useId, useState } from 'react';
import { MdSchedule } from 'react-icons/md';
import { useControllerMerged } from '#src/hooks/use-controller-merged.js';
import { cn } from '#src/utils/index.js';
import { Button } from '../button/button.js';
import { Calendar } from '../calendar/calendar.js';
import { FormControl, FormDescription, FormItem, FormLabel, FormMessage, } from '../form-item/form-item.js';
import { Input } from '../input/input.js';
import { Popover, PopoverContent, PopoverTrigger } from '../popover/popover.js';
/**
* Field with label and error states that wraps a Calendar and time input for date-time selection.
*/
function DateTimePickerField({ className, wrapperClassName, disabled, placeholder = 'Pick date and time', onChange, value, label, error, description, dateTimeFormat = 'PPP pp', showSeconds = false, calendarProps, ref, }) {
const addWrapper = label ?? error ?? description;
const id = useId();
const [open, setOpen] = useState(false);
// Parse string value to Date for internal operations
const dateTimeValue = value ? parseISO(value) : undefined;
const handleDateSelect = (date) => {
if (!date) {
onChange?.(undefined);
return;
}
let newDate;
if (dateTimeValue) {
// Preserve existing time when changing date
newDate = set(date, {
hours: dateTimeValue.getHours(),
minutes: dateTimeValue.getMinutes(),
seconds: showSeconds ? dateTimeValue.getSeconds() : 0,
});
}
else {
// Set default time to current time or noon if no current time
const now = new Date();
newDate = set(date, {
hours: now.getHours(),
minutes: now.getMinutes(),
seconds: showSeconds ? now.getSeconds() : 0,
});
}
// Convert to ISO string
onChange?.(newDate.toISOString());
};
const handleTimeChange = (timeString) => {
if (!dateTimeValue) {
// If no date selected, use today's date
const today = new Date();
const [hours, minutes, seconds = 0] = timeString
.split(':')
.map((n) => (n ? Number(n) : 0));
const newDate = set(today, {
hours,
minutes,
seconds: showSeconds ? seconds : 0,
});
onChange?.(newDate.toISOString());
return;
}
const [hours, minutes, seconds = 0] = timeString
.split(':')
.map((n) => (n ? Number(n) : 0));
const newDate = set(dateTimeValue, {
hours,
minutes,
seconds: showSeconds ? seconds : 0,
});
onChange?.(newDate.toISOString());
};
const formatTimeValue = (date) => {
if (!date)
return '';
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
const seconds = date.getSeconds().toString().padStart(2, '0');
if (showSeconds) {
return `${hours}:${minutes}:${seconds}`;
}
return `${hours}:${minutes}`;
};
const inputComponent = (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", "data-empty": !dateTimeValue, disabled: disabled, id: id, ref: ref, className: cn('w-[280px] justify-start text-left font-normal data-[empty=true]:text-muted-foreground', className), children: [_jsx(MdSchedule, {}), dateTimeValue ? (format(dateTimeValue, dateTimeFormat)) : (_jsx("span", { children: placeholder }))] }) }), _jsx(PopoverContent, { className: "w-auto p-0", align: "start", children: _jsxs("div", { className: "space-y-3 p-3", children: [_jsx(Calendar, { mode: "single", selected: dateTimeValue, onSelect: handleDateSelect, captionLayout: "dropdown", ...calendarProps }), _jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-sm font-medium", children: "Time" }), _jsx(Input, { type: "time", step: showSeconds ? 1 : 60, value: formatTimeValue(dateTimeValue), onChange: (e) => {
handleTimeChange(e.target.value);
}, className: "w-full" })] })] }) })] }));
if (addWrapper) {
return (_jsxs(FormItem, { error: error, className: cn('flex gap-2', wrapperClassName), children: [_jsx(FormLabel, { children: label }), _jsx(FormControl, { children: inputComponent }), _jsx(FormDescription, { children: description }), _jsx(FormMessage, {})] }));
}
return inputComponent;
}
function DateTimePickerFieldController({ control, name, ref, ...rest }) {
const { field: fieldProps, fieldState: { error }, } = useControllerMerged({
control,
name,
}, rest, ref);
return (_jsx(DateTimePickerField, { error: error?.message, ...rest, ...fieldProps }));
}
export { DateTimePickerField, DateTimePickerFieldController };
//# sourceMappingURL=date-time-picker-field.js.map