Time Field
Enter a time segment by segment.
use herogpui::components::time_field::{TimeField, TimeState};TimeField on docs.rsSource at v0.14.0
Usage
Showing 24-hour, live: HeroGPUI compiled to WebAssembly. The canvas has no accessibility tree; each example's description and code below carry the same information.
Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{HourCycle, Time, TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![TimeField::new(self.demo_time("tmf-24-hour", cx))
.label("Start time")
.hour_cycle(HourCycle::H24)
.description("Click a segment, then use the steppers.")
.on_change(
cx.listener(|_, _t: &Option<Time>, _, cx| cx.notify()),
)
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{HourCycle, Time, TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![TimeField::new(self.demo_time("tmf-12-hour-seconds", cx))
.label("Reminder")
.hour_cycle(HourCycle::H12)
.show_seconds(true)
.on_change(
cx.listener(|_, _t: &Option<Time>, _, cx| cx.notify()),
)
.into_any_element()])The system locale controls numeric padding; this prop only forces the hour to two digits. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{HourCycle, TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![
TimeField::new(self.demo_time("tmf-leading-locale", cx))
.label("Locale default")
.hour_cycle(HourCycle::H12)
.show_seconds(true)
.into_any_element(),
TimeField::new(self.demo_time("tmf-leading-forced", cx))
.label("Forced two-digit hour")
.hour_cycle(HourCycle::H12)
.show_seconds(true)
.should_force_leading_zeros(true)
.into_any_element(),
])Uses your system regional segment order, separators, padding, day-period names, and 12- or 24-hour cycle. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![
TimeField::new(self.demo_time("tmf-usage", cx))
.label("Time")
.radius(px(4.))
.into_any_element(),
])The same box seam as the other fields: `height`, `padding_x` and `is_bare`. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ActiveTheme, TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![TimeField::new(self.demo_time("tmf-custom-box", cx))
.label("Compact")
.height(px(28.))
.padding_x(px(8.))
.is_bare(true)
.stepper_hover_bg(cx.colors().accent.soft())
.font_family(MONO_FONT)
.into_any_element()])Also calls MONO_FONT (included when you copy).
Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ActiveTheme, TimeField, TimeState, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![TimeField::new(self.demo_time("tmf-icon", cx))
.label("Time")
.prefix(gpui::svg()
.size(px(16.))
.path(icons::SUN)
.text_color(cx.colors().foreground)
.into_any_element())
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{FieldVariant, Surface, TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![Surface::new()
.padding(px(24.))
.gap(px(16.))
.child(
TimeField::new(self.demo_time("tmf-surface", cx))
.label("Time")
.variant(FieldVariant::Secondary),
)
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![TimeField::new(self.demo_time("tmf-desc", cx))
.label("Time")
.description("Hour and minute")
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![TimeField::new(self.demo_time("tmf-req", cx))
.label("Time")
.is_required(true)
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![TimeField::new(self.demo_time("tmf-dis", cx))
.label("Time")
.is_disabled(true)
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.w(px(400.))
.child(
TimeField::new(self.demo_time("tmf-full", cx))
.label("Time")
.full_width(true),
)Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{Time, TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![TimeField::new(self.demo_time("tmf-invalid", cx))
.label("Time")
// `minValue`/`maxValue` clamp what the segments accept.
.min_value(Time::new(9, 0))
.max_value(Time::new(17, 30))
.is_required(true)
.is_invalid(true)
.error_message("Pick a time")
.into_any_element()])The field owns the value; `on_change` reports each edit. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{Time, TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
TimeField::new(self.demo_time("tmf-ctl", cx))
.label("Time")
.on_change(
cx.listener(|_, _t: &Option<Time>, _, cx| cx.notify()),
)
.into_any_element(),
])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![TimeField::new(self.demo_time("tmf-validate", cx))
.label("Meeting time")
.description("Office hours are 09:00 to 17:00")
.validate(|value| {
value
.filter(|t| t.hour < 9 || t.hour >= 17)
.map(|_| "Pick a time inside office hours".into())
})
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{Button, Form, TimeField, TimeState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![{
let state = self.demo_time("tmf-form", cx);
let field = TimeField::new(state)
.label("Start time")
.name("start_time")
.is_required(true);
Form::new()
.child(field)
.child(Button::new("time-field-form-submit").label("Save"))
.into_any_element()
}])Helpers used by these examples
Plain functions over the public API, defined once here and copied with each example that calls them.
const MONO_FONT: &str = if cfg!(target_arch = "wasm32") {
// The web shell bundles JetBrains Mono; the literal family "monospace"
// resolves to nothing there and panics text resolution.
"JetBrains Mono"
} else if cfg!(target_os = "macos") {
"Menlo"
} else {
"Consolas"
};Anatomy
Rust types
Time Field is assembled from these builders. The API reference lists each one.
Customization
Shared appearance belongs on ThemeBuilder::components and .recipe("name"). Typed builders change how Time Field looks at one call site. Every builder also carries sx for placement, width and flex: it takes GPUI's own styling methods and refines them over the component's root after the theme, so an instance override wins. Behaviour builders live in the API reference below.
Styling
| Method | Values | Description |
|---|---|---|
full_width(bool) | true | false | Expands the root and group to the available width. |
variant(FieldVariant) | FieldVariant::Primary | FieldVariant::Secondary | The monolithic group and input share one field variant. |
// The one slot for caller-owned low-level styling: GPUI's
// styling methods, refined over the root element last.
.sx(|el| {
el.bg(gpui::rgba(0xffa500ff)) // background
.text_color(gpui::rgba(0x000000ff)) // text
.w(gpui::px(13.)) // size
.h(gpui::px(12.))
})API reference
Against HeroUI v3.2.6: 34 entries ported, 10 partial, 6 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.
Builders
| Builder | Values | Default | Description |
|---|---|---|---|
content(Partial | AnyElement | | — | Built-in field composition or replacement content with complete field state. |
full_width( | true | | false | Expands the root and group to the available width. |
focus_ring( | true | | true | GPUI extension: hides only the segmented field's visual focus ring while retaining focus and editing. |
new(Partial | string | — | The bound TimeState entity supplies internal identity rather than a caller element id. Simultaneous fields need distinct TimeState owners. |
value(Partial | TimeValue | | — | Controlled plain-time value held on the builder and synced into the state when it changes; calendar date-time and zoned types are not represented. |
default_value(Partial | TimeValue | | — | Seeds an uncontrolled plain-time value once. |
on_change(Partial | Fn( | — | Reports a plain Time value or null. |
placeholder_value( | TimeValue | | 12:00 AM / 00:00 | Seeds the first edit and defaults to midnight in the active hour cycle. |
is_required( | true | | false | Marks the field required and blocks empty native submission. |
is_invalid( | true | | false | Forces invalid chrome and validation reporting. |
min_value(Partial | TimeValue | | — | Inclusive minimum plain time. |
max_value(Partial | TimeValue | | — | Inclusive maximum plain time. |
validate(Partial | Fn( | — | Custom validation returns one optional GPUI message. |
validation_behavior( | ValidationBehavior:: | ValidationBehavior:: | Chooses blocking native or non-blocking allow behavior. |
granularity( | TimeGranularity:: | TimeGranularity:: | Controls the smallest visible and submitted unit. |
hour_cycle( | 12 | | HourCycle:: | Defaults to the system regional time format; an explicit cycle overrides it. |
should_force_leading_zeros( | true | | locale | Forces a two-digit hour when true; otherwise numeric padding follows the system regional time pattern. |
is_disabled( | true | | false | Dims the whole field, removes interaction and omits form data. |
is_read_only( | true | | false | Keeps segment navigation and focus while blocking edits. |
name( | string | — | Registers a live ISO time FormField. |
auto_focus( | true | | false | Focuses the segmented input on first render, including replacement content. |
variant( | FieldVariant:: | FieldVariant:: | The monolithic group and input share one field variant. |
segment( | DateSegment | — | Hands custom content each time segment and its system-regional generated text. |
prefix( | AnyElement | — | Inert content before the segments. |
suffix( | AnyElement | — | Inert content after the segments. |
Not ported6 HeroUI entries not ported
| HeroUI v3 | Why it is not ported |
|---|---|
TimeField.className | Browser styles are unavailable. |
TimeField.style | Browser inline styles are unavailable. |
TimeField.render | Root-element substitution has no GPUI equivalent. |
TimeField.hideTimeZone | Has no zoned time value or zone segment. |
TimeField.aria-label / aria-labelledby / aria-describedby / aria-details | Gpui-pre 0.3.3 exposes no accessibility attribute tree. |
Composition parts.className | Per-part browser styles are unavailable. |
Parts
| Part | Description |
|---|---|
TimeField | Field state, validation and form owner. |
TimeField | Optional label with required, invalid and disabled state. |
TimeField | Focusable field chrome around input, prefix and suffix. |
TimeFieldPartial | Built-in segmented input rather than an independently composable part. |
TimeField | Keyboard-editable time segment with custom content support. |
TimeField | Inert leading content. |
TimeField | Inert trailing content. |
TimeField | Optional help text hidden when invalid. |
TimeFieldPartial | Resolved validation message; the built-in equivalent is ErrorMessage. |
States
| State | Builder | Description |
|---|---|---|
Hovered | hover field background | Interactive group uses its variant hover fill outside focus. |
Focus within | focus_handle | Focused segments ring the field and use focus chrome. |
Invalid | validation::resolve | Danger field styling replaces description with the resolved error. |
Disabled | is_disabled | The whole field is dimmed and inert. |
Read only | is_read_only gates edits | Segments remain navigable but cannot change. |
Required | is_required | Label marker and native form validation reflect required state. |
Segment focused | TimeState::focused | Active segment uses accent-soft selection chrome. |
Segment placeholder | TimeState cleared/display_value | Incomplete segments use placeholder text. |