Time Picker
A segmented time input paired with a popover of scrollable listbox columns: type a time, or pick it from the hour, minute and AM/PM wheels.
Value:
<NaviusTimePicker @bind-Value="_time" HourCycle="12">
<div class="inline-flex items-center gap-1 rounded-md border px-3 py-2">
<NaviusTimePickerInput />
<NaviusTimePickerTrigger aria-label="Open time picker"><ClockIcon /></NaviusTimePickerTrigger>
</div>
<NaviusTimePickerContent class="z-50 flex gap-1 rounded-md border bg-popover p-1 shadow-md">
<NaviusTimePickerColumn Unit="hour" />
<NaviusTimePickerColumn Unit="minute" />
<NaviusTimePickerColumn Unit="dayPeriod" />
</NaviusTimePickerContent>
</NaviusTimePicker>
@code { private TimeOnly? _time = new(9, 30); }Features
- Wraps an embedded Time Input for keyboard entry (which works whether or not the popup is open) plus a listbox column per unit.
- Reuses the Popover engine for anchoring and the dismissable layer; the root owns a single open state.
- Each column auto-generates its options for its unit, roves with a single tabindex, and commits the chosen unit while preserving the others.
- Owns a
TimeOnly?and an open flag: controlled via@bind-Value/@bind-Open, uncontrolled viaDefaultValue/DefaultOpen.
Installation
Install the brain, or copy just this primitive in with the CLI.
navius add time-pickerAnatomy
Import the parts and assemble them. The root renders no DOM of its own; it cascades state through the wrapped popover.
@using Navius.Primitives.Components.TimePicker
<NaviusTimePicker>
<NaviusTimePickerInput />
<NaviusTimePickerTrigger />
<NaviusTimePickerContent>
<NaviusTimePickerColumn Unit="hour" />
<NaviusTimePickerColumn Unit="minute" />
<NaviusTimePickerColumn Unit="dayPeriod" />
</NaviusTimePickerContent>
</NaviusTimePicker>API Reference
Root
NaviusTimePicker. Owns the value and open state and cascades them to the input, trigger and columns. Renders no DOM (wraps a Popover). Value and Open are two-way bindable.
| Prop | Type | Default |
|---|---|---|
| Value | TimeOnly? | - |
| ValueChanged | EventCallback<TimeOnly?> | - |
| DefaultValue | TimeOnly? | - |
| Open | bool | false |
| OpenChanged | EventCallback<bool> | - |
| DefaultOpen | bool | false |
| Granularity | string | "minute" |
| HourCycle | int? | culture |
| MinuteStep | int | 1 |
| SecondStep | int | 1 |
| Disabled | bool | false |
| ChildContent | RenderFragment? | - |
Input
NaviusTimePickerInput. The typed, segmented surface: an embedded Time Input bound to the picker's value. Keyboard entry here works with the popup closed. Renders the Time Input group.
| Prop | Type | Default |
|---|---|---|
| …attributes | IDictionary<string, object>? | - |
Data attributes
| Data attribute | Values |
|---|---|
| [data-navius-time-picker-input] | Present on the embedded time input |
Trigger
NaviusTimePickerTrigger. The clock button that toggles the popup. Delegates to the Popover trigger (aria-haspopup=dialog, aria-expanded, aria-controls). Renders a button.
| Prop | Type | Default |
|---|---|---|
| ChildContent | RenderFragment? | - |
| …attributes | IDictionary<string, object>? | - |
Data attributes
| Data attribute | Values |
|---|---|
| [data-navius-time-picker-trigger] | Present on the trigger button |
| [data-popup-open] | Present while the popup is open (from the Popover trigger) |
Content
NaviusTimePickerContent. Portals and anchors the popup (Popover Portal > Positioner > Popup) and hosts the columns. The Popup is role=dialog. Renders the popover popup.
| Prop | Type | Default |
|---|---|---|
| Side | string | "bottom" |
| Align | string | "start" |
| SideOffset | double | 4 |
| ChildContent | RenderFragment? | - |
| …attributes | IDictionary<string, object>? | - |
Data attributes
| Data attribute | Values |
|---|---|
| [data-navius-time-picker-popup] | Present on the popup (role=dialog) |
| [data-open] / [data-closed] | Open/closed lifecycle, from the Popover engine |
| [data-starting-style] / [data-ending-style] | Enter/exit animation hooks, from the Popover engine |
Column
NaviusTimePickerColumn. One scrollable listbox for a single unit; it auto-generates its options and commits through the shared context. Renders a div with role=listbox.
| Prop | Type | Default |
|---|---|---|
| Unit | string | "hour" |
| Class | string? | - |
| OptionClass | string? | - |
Data attributes
| Data attribute | Values |
|---|---|
| [data-navius-time-picker-column] | Present on the listbox |
| [data-unit] | "hour" | "minute" | "second" | "dayPeriod" |
| [data-navius-time-picker-option] | Present on each option button |
| [data-selected] | Present on the option matching the current unit value |
| [data-highlighted] | Present on the roving-active option |
Accessibility
The trigger carries aria-haspopup="dialog" and aria-expanded; the popup is role="dialog". Each column is a role="listbox" of role="option" buttons with a roving tabindex. The segmented input keeps its own spinbutton semantics (see Time Input).
Keyboard interactions (columns)
| Key | Description |
|---|---|
| Arrow Up / Arrow Down | Moves the roving highlight within the column. |
| Home / End | Moves to the first / last option. |
| 0-9 | Type-ahead: jumps to the option whose value starts with the typed digits. |
| Enter / Space | Commits the highlighted option (via its native button activation). |