Skip to content
PorabuildHeroGPUI component library
v0.14.0 · Apache-2.0

Date and time

Time Field

Enter a time segment by segment.

Rust
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.

24-hour

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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()])

12-hour with seconds

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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()])

Forced Leading Zeros

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>.

Rust
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(),
    ])

Usage

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>.

Rust
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(),
    ])

Box Customisation

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>.

Rust
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).

With Icons

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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()])

On Surface

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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()])

With Description

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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()])

Required Field

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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()])

Disabled State

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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()])

Full Width

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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),
    )

Validation

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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()])

Controlled

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>.

Rust
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(),
    ])

With Validation

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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()])

Form Example

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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.

MONO_FONT
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

TimeField

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

Time Field styling
MethodValuesDescription
full_width(bool)true | falseExpands the root and group to the available width.
variant(FieldVariant)FieldVariant::Primary | FieldVariant::SecondaryThe monolithic group and input share one field variant.
Rust
// 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

Time Field builders
BuilderValuesDefaultDescription
content(render)PartialAnyElement | Fn(values: TimeFieldRenderProps) -> AnyElement—Built-in field composition or replacement content with complete field state.
full_width(bool)true | falsefalseExpands the root and group to the available width.
focus_ring(bool)true | falsetrueGPUI extension: hides only the segmented field's visual focus ring while retaining focus and editing.
new(state)Partialstring—The bound TimeState entity supplies internal identity rather than a caller element id. Simultaneous fields need distinct TimeState owners.
value(Option<Time>)PartialTimeValue | null—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(Time)PartialTimeValue | null—Seeds an uncontrolled plain-time value once.
on_change(callback)PartialFn(value: TimeValue | null) -> ()—Reports a plain Time value or null.
placeholder_value(Time)TimeValue | null12:00 AM / 00:00Seeds the first edit and defaults to midnight in the active hour cycle.
is_required(bool)true | falsefalseMarks the field required and blocks empty native submission.
is_invalid(bool)true | falsefalseForces invalid chrome and validation reporting.
min_value(Time)PartialTimeValue | null—Inclusive minimum plain time.
max_value(Time)PartialTimeValue | null—Inclusive maximum plain time.
validate(callback)PartialFn(value: TimeValue) -> ValidationError | true | null—Custom validation returns one optional GPUI message.
validation_behavior(ValidationBehavior)ValidationBehavior::Native | ValidationBehavior::AriaValidationBehavior::NativeChooses blocking native or non-blocking allow behavior.
granularity(TimeGranularity)TimeGranularity::Hour | TimeGranularity::Minute | TimeGranularity::SecondTimeGranularity::MinuteControls the smallest visible and submitted unit.
hour_cycle(HourCycle)12 | 24HourCycle::LocaleDefaults to the system regional time format; an explicit cycle overrides it.
should_force_leading_zeros(bool)true | falselocaleForces a two-digit hour when true; otherwise numeric padding follows the system regional time pattern.
is_disabled(bool)true | falsefalseDims the whole field, removes interaction and omits form data.
is_read_only(bool)true | falsefalseKeeps segment navigation and focus while blocking edits.
name(value) + form_field(cx)string—Registers a live ISO time FormField.
auto_focus(bool)true | falsefalseFocuses the segmented input on first render, including replacement content.
variant(FieldVariant)FieldVariant::Primary | FieldVariant::SecondaryFieldVariant::PrimaryThe monolithic group and input share one field variant.
segment(render)DateSegment—Hands custom content each time segment and its system-regional generated text.
prefix(element)AnyElement—Inert content before the segments.
suffix(element)AnyElement—Inert content after the segments.
Not ported6 HeroUI entries not ported
Time Field builders not ported
HeroUI v3Why it is not ported
TimeField.classNameBrowser styles are unavailable.
TimeField.styleBrowser inline styles are unavailable.
TimeField.renderRoot-element substitution has no GPUI equivalent.
TimeField.hideTimeZoneHas no zoned time value or zone segment.
TimeField.aria-label / aria-labelledby / aria-describedby / aria-detailsGpui-pre 0.3.3 exposes no accessibility attribute tree.
Composition parts.classNamePer-part browser styles are unavailable.

Parts

Time Field parts
PartDescription
TimeFieldField state, validation and form owner.
TimeFieldOptional label with required, invalid and disabled state.
TimeFieldFocusable field chrome around input, prefix and suffix.
TimeFieldPartialBuilt-in segmented input rather than an independently composable part.
TimeFieldKeyboard-editable time segment with custom content support.
TimeFieldInert leading content.
TimeFieldInert trailing content.
TimeFieldOptional help text hidden when invalid.
TimeFieldPartialResolved validation message; the built-in equivalent is ErrorMessage.

States

Time Field states
StateBuilderDescription
Hoveredhover field backgroundInteractive group uses its variant hover fill outside focus.
Focus withinfocus_handleFocused segments ring the field and use focus chrome.
Invalidvalidation::resolveDanger field styling replaces description with the resolved error.
Disabledis_disabledThe whole field is dimmed and inert.
Read onlyis_read_only gates editsSegments remain navigable but cannot change.
Requiredis_requiredLabel marker and native form validation reflect required state.
Segment focusedTimeState::focusedActive segment uses accent-soft selection chrome.
Segment placeholderTimeState cleared/display_valueIncomplete segments use placeholder text.