TimeField
HeroUIA time of day, typed or stepped segment by segment.
Solidtype_heroui.Time_field is Kobalte’s TimeField.Root, with the classes
HeroUI’s own timeFieldVariants gives, and its group, input and segments
HeroUI’s dateInputGroupVariants. Each part of the time – the hour, the
minute, the second and AM or PM – is a segment, typed into or stepped by the
arrow keys. Its label names it, its description describes it, its error shows
while it is invalid, and it submits its value with a form, by Kobalte’s hidden
input.
Its value is Kobalte’s Types.Time, an hour, a minute and a second, made by
Solidtype_kobalte.Types.Time.make ~hour:14. ~minute:30. ().
HeroUI’s examples, with HeroUI’s icons from @gravity-ui/icons.
module S = Solidtype.Solidmodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_field
let make () = <Time_field class_="w-[256px]" name="time"> <Label>"Time"</Label> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> </Time_field>Anatomy
Section titled “Anatomy”<Time_field> <Label /> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Description /> <Field_error /></Time_field>Examples
Section titled “Examples”With Icons
Section titled “With Icons”module S = Solidtype.Solidmodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_field
let make () = <Time_field class_="w-[256px]" name="time"> <Label>"Time"</Label> <Time_field.Group> <Time_field.Prefix> <Gravity.Clock class_="size-4 text-muted" /> </Time_field.Prefix> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> </Time_field>module S = Solidtype.Solidmodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_field
let make () = <Time_field class_="w-[256px]" name="time"> <Label>"Time"</Label> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> <Time_field.Suffix> <Gravity.Clock class_="size-4 text-muted" /> </Time_field.Suffix> </Time_field.Group> </Time_field>module S = Solidtype.Solidmodule Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_field
let make () = <Time_field class_="w-[256px]" name="time"> <Label>"Time"</Label> <Time_field.Group> <Time_field.Prefix> <Gravity.Clock class_="size-4 text-muted" /> </Time_field.Prefix> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> <Time_field.Suffix> <Gravity.Chevron_down class_="size-4 text-muted" /> </Time_field.Suffix> </Time_field.Group> <Description>"Enter a time"</Description> </Time_field>On Surface
Section titled “On Surface”module S = Solidtype.Solidmodule Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Surface = Solidtype_heroui.Surfacemodule Time_field = Solidtype_heroui.Time_field
let make () = <Surface class_="flex w-full max-w-sm flex-col gap-4 rounded-3xl p-6"> <Time_field class_="w-full" name="time"> <Label>"Time"</Label> <Time_field.Group variant=`secondary> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Description>"Enter a time"</Description> </Time_field> <Time_field class_="w-full" name="time-2"> <Label>"Appointment time"</Label> <Time_field.Group variant=`secondary> <Time_field.Prefix> <Gravity.Clock class_="size-4 text-muted" /> </Time_field.Prefix> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Description>"Enter a time for your appointment"</Description> </Time_field> </Surface>With Description
Section titled “With Description”module S = Solidtype.Solidmodule Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_field
let make () = <div class_="flex flex-col gap-4"> <Time_field class_="w-[256px]" name="time"> <Label>"Start time"</Label> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Description>"Enter the start time"</Description> </Time_field> <Time_field class_="w-[256px]" name="end-time"> <Label>"End time"</Label> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Description>"Enter the end time"</Description> </Time_field> </div>Required Field
Section titled “Required Field”module S = Solidtype.Solidmodule Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_field
let make () = <div class_="flex flex-col gap-4"> <Time_field is_required=true class_="w-[256px]" name="time"> <Label>"Time"</Label> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> </Time_field> <Time_field is_required=true class_="w-[256px]" name="appointment-time"> <Label>"Appointment time"</Label> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Description>"Required field"</Description> </Time_field> </div>Disabled State
Section titled “Disabled State”module S = Solidtype.Solidmodule Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_fieldmodule Time = Solidtype_kobalte.Types.Time
(* The time now, in the reader's time zone. *)let now () = let d = Js.Date.make () in Time.make ~hour:(Js.Date.getHours d) ~minute:(Js.Date.getMinutes d) ~second:(Js.Date.getSeconds d) ()
let make () = let time_value = now () in <div class_="flex flex-col gap-4"> <Time_field is_disabled=true class_="w-[256px]" name="time" value=(fun () -> Js.Nullable.return time_value)> <Label>"Time"</Label> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Description>"This time field is disabled"</Description> </Time_field> <Time_field is_disabled=true class_="w-[256px]" name="time-empty"> <Label>"Time"</Label> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Description>"This time field is disabled"</Description> </Time_field> </div>Full Width
Section titled “Full Width”module S = Solidtype.Solidmodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_field
let make () = <div class_="w-[400px] space-y-4"> <Time_field full_width=true name="time"> <Label>"Time"</Label> <Time_field.Group full_width=true> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> </Time_field> <Time_field full_width=true name="time-icons"> <Label>"Time"</Label> <Time_field.Group full_width=true> <Time_field.Prefix> <Gravity.Clock class_="size-4 text-muted" /> </Time_field.Prefix> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> <Time_field.Suffix> <Gravity.Chevron_down class_="size-4 text-muted" /> </Time_field.Suffix> </Time_field.Group> </Time_field> </div>Validation
Section titled “Validation”module S = Solidtype.Solidmodule Field_error = Solidtype_heroui.Field_errormodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_field
let make () = <div class_="flex flex-col gap-4"> <Time_field is_invalid=true is_required=true class_="w-[256px]" name="time"> <Label>"Time"</Label> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Field_error>"Please enter a valid time"</Field_error> </Time_field> <Time_field is_invalid=true class_="w-[256px]" name="invalid-time"> <Label>"Time"</Label> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Field_error>"Time must be within business hours"</Field_error> </Time_field> </div>Controlled
Section titled “Controlled”module S = Solidtype.Solidmodule Jsx = Solidtype.Jsxmodule Button = Solidtype_heroui.Buttonmodule Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_fieldmodule Time = Solidtype_kobalte.Types.Time
(* The time now, in the reader's time zone. *)let now () = let d = Js.Date.make () in Time.make ~hour:(Js.Date.getHours d) ~minute:(Js.Date.getMinutes d) ~second:(Js.Date.getSeconds d) ()
(* A time as @internationalized/date's [Time] writes it, 09:45:00. *)let to_string t = let two part = let n = Option.value (part t) ~default:0. in (if n < 10. then "0" else "") ^ Js.Float.toString n in String.concat ":" [ two Time.hour; two Time.minute; two Time.second ]
let make () = let value, set_value = S.create_signal None in <div class_="flex flex-col gap-4"> <Time_field class_="w-[256px]" name="time" value=(fun () -> Js.Nullable.fromOption (S.get value)) on_change=(S.set set_value)> <Label>"Time"</Label> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Description> (fun () -> Jsx.string ("Current value: " ^ match S.get value with Some t -> to_string t | None -> "(empty)")) </Description> </Time_field> <div class_="flex gap-2"> <Button variant=`tertiary on_click=(fun _ -> S.set set_value (Some (now ())))> "Set now" </Button> <Button variant=`tertiary on_click=(fun _ -> S.set set_value None)> "Clear" </Button> </div> </div>Form Example
Section titled “Form Example”HeroUI’s button takes isPending while the time is sent; the port’s button has no pending state yet.
module S = Solidtype.Solidmodule Show = Solidtype.Showmodule Button = Solidtype_heroui.Buttonmodule Description = Solidtype_heroui.Descriptionmodule Field_error = Solidtype_heroui.Field_errormodule Form = Solidtype_heroui.Formmodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_fieldmodule Time = Solidtype_kobalte.Types.Time
external prevent_default : Dom.event -> unit = "preventDefault" [@@mel.send]
let min_time = Time.make ~hour:9. ~minute:0. ()let max_time = Time.make ~hour:17. ~minute:0. ()
(* A time's seconds since midnight, which order two times. *)let seconds t = let part p = Option.value (p t) ~default:0. in (part Time.hour *. 3600.) +. (part Time.minute *. 60.) +. part Time.second
let make () = let value, set_value = S.create_signal None in let is_submitting, set_is_submitting = S.create_signal false in let is_invalid () = match S.get value with | Some t -> seconds t < seconds min_time || seconds t > seconds max_time | None -> false in let handle_submit e = prevent_default e; match S.get value with | Some t when not (is_invalid ()) -> S.set set_is_submitting true; (* Simulate API call *) ignore (Js.Global.setTimeout ~f:(fun () -> Js.log2 "Time submitted:" t; S.set set_value None; S.set set_is_submitting false) 1500 : Js.Global.timeoutId) | Some _ | None -> () in <Form class_="flex w-[280px] flex-col gap-4" on_submit=handle_submit> <Time_field is_required=true class_="w-full" is_invalid=(fun () -> is_invalid ()) name="time" min_value=(fun () -> min_time) max_value=(fun () -> max_time) value=(fun () -> Js.Nullable.fromOption (S.get value)) on_change=(S.set set_value)> <Label>"Appointment time"</Label> <Time_field.Group> <Time_field.Prefix> <Gravity.Clock class_="size-4 text-muted" /> </Time_field.Prefix> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Show when_=(fun () -> is_invalid ()) fallback=(<Description> "Enter a time between 9:00 AM and 5:00 PM" </Description>)> <Field_error>"Time must be between 9:00 AM and 5:00 PM"</Field_error> </Show> </Time_field> <Button class_="w-full" is_disabled=(fun () -> Option.is_none (S.get value) || is_invalid ()) type_=`submit variant=`primary> <Show when_=(fun () -> not (S.get is_submitting))>"Submit"</Show> <Show when_=(fun () -> S.get is_submitting)>"Submitting..."</Show> </Button> </Form>With Validation
Section titled “With Validation”module S = Solidtype.Solidmodule Show = Solidtype.Showmodule Description = Solidtype_heroui.Descriptionmodule Field_error = Solidtype_heroui.Field_errormodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_fieldmodule Time = Solidtype_kobalte.Types.Time
let min_time = Time.make ~hour:9. ~minute:0. ()let max_time = Time.make ~hour:17. ~minute:0. ()
(* A time's seconds since midnight, which order two times. *)let seconds t = let part p = Option.value (p t) ~default:0. in (part Time.hour *. 3600.) +. (part Time.minute *. 60.) +. part Time.second
let make () = let value, set_value = S.create_signal None in let is_invalid () = match S.get value with | Some t -> seconds t < seconds min_time || seconds t > seconds max_time | None -> false in <div class_="flex flex-col gap-4"> <Time_field is_required=true class_="w-[256px]" is_invalid=(fun () -> is_invalid ()) name="time" min_value=(fun () -> min_time) max_value=(fun () -> max_time) value=(fun () -> Js.Nullable.fromOption (S.get value)) on_change=(S.set set_value)> <Label>"Time"</Label> <Time_field.Group> <Time_field.Input> (fun segment -> <Time_field.Segment segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> <Show when_=(fun () -> is_invalid ()) fallback=(<Description> "Enter a time between 9:00 AM and 5:00 PM" </Description>)> <Field_error>"Time must be between 9:00 AM and 5:00 PM"</Field_error> </Show> </Time_field> </div>Render Function
Section titled “Render Function”HeroUI’s Render Function example gives the field render, React’s way to swap the element it renders, which a Solid component has no need of.
Customization
Section titled “Customization”Tailwind CSS
Section titled “Tailwind CSS”class_ is added after HeroUI’s classes.
module S = Solidtype.Solidmodule Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Time_field = Solidtype_heroui.Time_field
let make () = <Time_field class_="w-full max-w-48 gap-1.5" name="reminder"> <Label class_="font-medium text-foreground">"Reminder time"</Label> <Description>"Daily check-in notification."</Description> <Time_field.Group class_="rounded-xl border border-border/80 bg-surface px-2 py-1 \ shadow-sm ring-1 ring-accent/5 focus-within:border-accent/25 \ focus-within:ring-2 focus-within:ring-accent/15"> <Time_field.Input> (fun segment -> <Time_field.Segment class_="text-foreground" segment=(fun () -> S.get segment) />) </Time_field.Input> </Time_field.Group> </Time_field>Styling Reference
Section titled “Styling Reference”CSS Classes
Section titled “CSS Classes”.time-field, and its full width.time-field--full-width.date-input-group,.date-input-group--primary,.date-input-group--secondary,.date-input-group__input,.date-input-group__segment,.date-input-group__prefix,.date-input-group__suffix
Interactive States
Section titled “Interactive States”- the field:
[data-invalid="true"],[data-required="true"],[data-disabled="true"] - the group:
[data-hovered="true"],[data-focus-within="true"],[data-focus-visible="true"] - a segment:
[data-focused="true"],[data-placeholder="true"],[data-type="literal"]
API Reference
Section titled “API Reference”Each prop beside the name HeroUI’s React component gives it.
TimeField
Section titled “TimeField”| React | solidtype | Type | Default | Description |
|---|---|---|---|---|
value |
value |
Types.Time.t Js.Nullable.t |
Its value, controlled; null controls it empty | |
defaultValue |
default_value |
Types.Time.t |
Its value at first, kept by itself | |
onChange |
on_change |
Types.Time.t option -> unit |
Called once every segment has its part, and with None once every segment is erased |
|
placeholderValue |
placeholder_value |
Types.Time.t |
What an arrow key steps an empty segment from | |
minValue |
min_value |
Types.Time.t |
The earliest time | |
maxValue |
max_value |
Types.Time.t |
The latest time | |
granularity |
granularity |
[ `hour | `minute | `second ] |
`minute |
The smallest part it shows |
hourCycle |
hour_cycle |
[ `h12 | `h24 ] |
the locale’s | Its clock |
shouldForceLeadingZeros |
should_force_leading_zeros |
bool |
the locale’s | Whether an hour of one digit has a zero before it |
isRequired |
is_required |
bool |
false |
Whether a form requires it |
isInvalid |
is_invalid |
bool |
false |
Whether it is invalid, its error shown |
isDisabled |
is_disabled |
bool |
false |
Whether it is disabled |
isReadOnly |
is_read_only |
bool |
false |
Whether it keeps its value |
name |
name |
string |
Its name in a form | |
aria-label |
aria_label |
string |
Its name where no label names it | |
fullWidth |
full_width |
bool |
false |
Whether it takes its container’s width |
className |
class_ |
string |
Added after HeroUI’s classes | |
onBlur |
on_blur |
Dom.focusEvent -> unit |
Called once focus leaves it for an element outside it | |
validate, validationBehavior |
— | A field validates nothing itself: it is invalid as the application says | ||
hideTimeZone |
— | Not yet |
TimeField.Group
Section titled “TimeField.Group”| React | solidtype | Type | Default | Description |
|---|---|---|---|---|
variant |
variant |
[ `primary | `secondary ] |
`primary |
Its look |
fullWidth |
full_width |
bool |
its field’s | Whether it takes its container’s width |
className |
class_ |
string |
Added after HeroUI’s classes |
TimeField.Input, TimeField.Segment, TimeField.Prefix, TimeField.Suffix
Section titled “TimeField.Input, TimeField.Segment, TimeField.Prefix, TimeField.Suffix”Each takes class_, added after HeroUI’s class; the input, a function of each
segment, rendering it; a segment, the segment it is; a prefix and a suffix,
their children.
Beside React
Section titled “Beside React”Original HeroUI documentation: TimeField.
The same HeroUI TimeField twice, under the same props: in solidtype on the left, in React on the right. Below them, every attribute of the two fields: a marked row is one they disagree on.
| attribute | solidtype | React |
|---|
