/// Inputs
DateTimePicker
Sunken field that opens a Calendar above a TimeInput in one overlay, so a date and a time are chosen together and returned as a single Date.
import { DateTimePicker } from "@protocore/pds";Basics
The trigger shows a formatted date-and-time; opening it reveals a Calendar above a TimeInput. Choosing a day keeps the current time, changing the time keeps the day. Controllable via value / defaultValue / onValueChange with a single Date.
Bounded, 12-hour & clearable
Wrap it in a Field for a label and hint. Constrain the calendar with min / max, switch the time to hour12, add withSeconds for precision, and set clearable to expose a reset once a value is chosen.
Within the next 60 days.
When to use it
Use DateTimePicker when a single instant matters — a scheduled deploy, an event start. It composes Calendar and TimeInput so the two parts stay in sync as one Date. When only the date matters use DatePicker; when only the time matters use TimePicker.
Usage
Do
- Give the trigger an accessible name via a `Field` label or `aria-label`.
- Set `min` / `max` to the valid window so out-of-range days can't be picked.
- Match `hour12` and `locale` to the surrounding UI's conventions.
- Read the combined `Date` from `onValueChange` — it carries both parts.
Don't
- Don't pair a separate DatePicker and TimePicker when the two must move as one instant.
- Don't leave the trigger unlabelled when no visible field label wraps it.
- Don't reconstruct the value from the field string — use the `Date`.
- Don't nest it inside another popover trigger where overlays contend for focus.
Accessibility
| Keys | Action |
|---|---|
| Enter / Space | Open the overlay from the focused trigger. |
| Arrow keys | Move between days in the calendar grid. |
| Tab | Move from the calendar into the time segments. |
| Esc | Close the overlay without changing the value. |
- The overlay is a Radix Popover: portalled, dismissed on outside-click and Escape, and returns focus to the trigger on close.
- The calendar is a `role="grid"`; the time field is a `role="group"` of spinbutton segments.
- `invalid` sets `aria-invalid` on the trigger; the trigger exposes `aria-expanded` for its open state.
DateTimePicker props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | |
clearable | boolean | false | Show a clear button when a value is selected. |
defaultOpen | boolean | — | Initial open state in uncontrolled mode. |
defaultValue | Date | null | — | Initial date-time in uncontrolled mode. |
disabled | boolean | — | Disable the whole control. |
format | DateTimeFormatOptions | ((date: Date) => string) | — | How the value renders in the field. `Intl.DateTimeFormatOptions` or a function. |
hour12 | boolean | false | Use a 12-hour clock for the time field. |
invalid | boolean | — | Mark the field invalid — danger border plus `aria-invalid`. |
isDateDisabled | ((date: Date) => boolean) | — | Predicate to disable arbitrary days. |
locale | string | — | BCP-47 locale for the field label and calendar. |
max | Date | — | Latest selectable day (inclusive). |
min | Date | — | Earliest selectable day (inclusive). |
onOpenChange | ((open: boolean) => void) | — | Fires when the overlay opens or closes. |
onValueChange | ((value: Date | null) => void) | — | Fires with the chosen `Date` (or `null` when cleared). |
open | boolean | — | Controlled open state of the overlay. |
placeholder | string | Select date & time | Text shown when no value is selected. |
size | enum | md | Field height: `sm` (32) · `md` (36, default) · `lg` (40). |
style | CSSProperties | — | |
value | Date | null | — | Controlled selected date-time. Pass `null` for no selection. |
weekStartsOn | enum | 1 | First column of the week: 0 = Sunday … 6 = Saturday. |
withSeconds | boolean | false | Include a seconds segment in the time field. |