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

Forms

Number Field

A numeric field with steppers, range and step.

Rust
use herogpui::components::number_field::{NumberField, NumberState};

NumberField on docs.rsSource at v0.14.0

Usage

Showing Usage, live: HeroGPUI compiled to WebAssembly. The canvas has no accessibility tree; each example's description and code below carry the same information.

Usage

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::{NumberField, NumberState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![NumberField::new(self.demo_number(
        "nf-usage",
        1024.,
        0.,
        4096.,
        1.,
        cx,
    ))
    // v3's basic example seeds the field with `defaultValue`.
    .default_value(1024.)
    .min_value(0.)
    .name("width")
    .full_width(true)
    .label("Width")
    .into_any_element()])

Box Customisation

`height` resizes the group and the inner field together; `padding_x` moves the input's inset without touching the steppers; `is_bare` drops the group's paint. 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::{NumberField, NumberState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![NumberField::new(
        self.demo_number("nf-custom-box", 5., 0., 20., 1., cx),
    )
    .label("Compact")
    .height(px(28.))
    .padding_x(px(8.))
    .is_bare(true)
    .font_family(MONO_FONT)
    .into_any_element()])

Also calls MONO_FONT (included when you copy).

Hover Colour

`group_hover_bg` names the fill the hovered group fades to, in place of `--field-hover`; a focused, invalid or disabled group keeps its own chrome. 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, NumberField, NumberState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .w(px(256.))
    .gap(px(12.))
    .children(vec![NumberField::new(
        self.demo_number("nf-hover-colour", 5., 0., 20., 1., cx),
    )
    .label("Quantity")
    .group_hover_bg(cx.colors().accent.soft())
    .into_any_element()])

Without steppers

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::{NumberField, NumberState};
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![NumberField::new(self.number.clone())
        .label("Quantity")
        .hide_steppers(true)
        .into_any_element()])

Format Options

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::{NumberField, NumberFormat, NumberState};
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![NumberField::new(self.price.clone())
        .label("Price")
        .format_options(NumberFormat::currency("USD"))
        .into_any_element()])

Variants

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, NumberField, NumberState};
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![
        NumberField::new(self.demo_number("nf-primary", 5., 0., 20., 1., cx))
            .label("Primary")
            .into_any_element(),
        NumberField::new(self.demo_number("nf-secondary", 5., 0., 20., 1., cx))
            .label("Secondary")
            .variant(FieldVariant::Secondary)
            .into_any_element(),
    ])

In 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, NumberField, NumberState, Surface};
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(
            NumberField::new(self.demo_number(
                "nf-surface",
                2.,
                0.,
                10.,
                1.,
                cx,
            ))
            .label("Seats")
            .variant(FieldVariant::Secondary)
            .description("The secondary variant, for use on a surface"),
        )
        .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::{NumberField, NumberState};
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![NumberField::new(
        self.demo_number("nf-desc", 1., 0., 99., 1., cx),
    )
    .label("Quantity")
    .description("How many licences to buy")
    .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::{NumberField, NumberState};
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![NumberField::new(
        self.demo_number("nf-req", 1., 0., 99., 1., cx),
    )
    .label("Quantity")
    .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::{NumberField, NumberState};
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![NumberField::new(
        self.demo_number("nf-dis", 8., 0., 99., 1., cx),
    )
    .label("Quantity")
    .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::{NumberField, NumberState};
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![NumberField::new(
        self.demo_number("nf-full", 3., 0., 99., 1., cx),
    )
    .label("Quantity")
    .full_width(true)
    .into_any_element()])

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::{NumberField, NumberState};
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![NumberField::new(self.demo_number(
        "nf-invalid",
        0.,
        0.,
        99.,
        1.,
        cx,
    ))
    .label("Quantity")
    // `minValue`/`maxValue` on the component, which is what
    // clamps the steppers and the typed value.
    .min_value(1.)
    .max_value(99.)
    .is_required(true)
    .is_invalid(true)
    .validation_errors(["Order at least one"])
    .into_any_element()])

Controlled

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, NumberField, NumberState};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, px};
use herogpui::gpui;

let nf_controlled = self
    .demo_number("nf-ctl", 5., 0., 20., 1., cx)
    .read(cx)
    .value();

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        NumberField::new(self.demo_number("nf-ctl", 5., 0., 20., 1., cx))
            .label("Quantity")
            .on_change(cx.listener(|_, _v: &f64, _, cx| cx.notify()))
            .into_any_element(),
        para(&format!("Value: {nf_controlled}"), cx),
    ])

Also calls para (included when you copy).

Step Values

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::{NumberField, NumberState};
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![
        NumberField::new(self.demo_number("nf-step-5", 10., 0., 100., 5., cx))
            .label("Step 5")
            .into_any_element(),
        NumberField::new(self.demo_number(
            "nf-step-tenth",
            1.5,
            0.,
            10.,
            0.1,
            cx,
        ))
        .label("Step 0.1")
        .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, FormField, NumberField, NumberState};
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 seats = self.demo_number("nf-form", 1., 1., 99., 1., cx);
        Form::new()
            .field(FormField::number(seats.clone()).name("seats"))
            .child(
                NumberField::new(seats)
                    .label("Seats")
                    .name("seats")
                    .is_required(true),
            )
            .child(Button::new("number-field-form-submit").label("Buy"))
            .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::{NumberField, NumberState};
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![NumberField::new(self.demo_number(
        "nf-validate",
        200.,
        0.,
        1000.,
        10.,
        cx,
    ))
    .label("Budget")
    .description("At least 100")
    .validate(|value| (*value < 100.).then(|| "Budget must be at least 100".into()))
    .into_any_element()])

Custom 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, NumberField, NumberState, icons};
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![NumberField::new(
        self.demo_number("nf-icons", 1024., 0., 4096., 1., cx,)
    )
    .label("Width (Custom Icons)")
    .description("Custom icon children")
    .decrement_icon(gpui::svg()
                        .size(px(16.))
                        .path(icons::CHEVRON_LEFT)
                        .text_color(cx.colors().foreground)
                        .into_any_element())
    .increment_icon(gpui::svg()
                        .size(px(16.))
                        .path(icons::CHEVRON_RIGHT)
                        .text_color(cx.colors().foreground)
                        .into_any_element())
    .into_any_element()])

With Chevrons

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, NumberField, NumberFormat, NumberState, icons};
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![NumberField::new(
        self.demo_number("nf-chev", 99., 0., 999., 1., cx),
    )
    .label("Amount")
    .format_options(NumberFormat::currency("EUR"))
    .vertical_steppers(true)
    .increment_icon(
        gpui::svg()
            .size(px(11.))
            .path(icons::CHEVRON_UP)
            .text_color(cx.colors().foreground),
    )
    .decrement_icon(
        gpui::svg()
            .size(px(11.))
            .path(icons::CHEVRON_DOWN)
            .text_color(cx.colors().foreground),
    )
    .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"
};
para
fn para(text: &str, cx: &App) -> gpui::AnyElement {
    let colors = cx.colors();
    gpui::div()
        .text_size(px(14.5))
        .line_height(px(24.))
        .text_color(colors.foreground)
        .max_w(px(720.))
        .child(text.to_owned())
        .into_any_element()
}

Anatomy

Rust types

NumberField

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

Number Field styling
MethodValuesDescription
full_width(bool)true | falseExpands both the root and number group to the available width.
variant(FieldVariant)FieldVariant::Primary | FieldVariant::SecondarySelects elevated or lower-emphasis field chrome.
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: 29 entries ported, 20 partial, 6 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.

Builders

Number Field builders
BuilderValuesDefaultDescription
content(render)PartialAnyElement | Fn(values: NumberFieldRenderProps) -> AnyElement—Built-in field anatomy or replacement content receiving the complete resolved number-field state.
full_width(bool)true | falsefalseExpands both the root and number group to the available width.
focus_ring(bool)true | falsetrueGPUI extension: hides only the group's visual focus ring while retaining focus, editing and stepper behavior.
new(state)Partialstring—The bound NumberState entity supplies stable GPUI identity rather than a element id.
variant(FieldVariant)FieldVariant::Primary | FieldVariant::SecondaryFieldVariant::PrimarySelects elevated or lower-emphasis field chrome.
default_value(value)number—Seeds the uncontrolled NumberState once before render content observes it.
on_change(callback)PartialFn(value: number | undefined) -> ()—Reports each changed numeric value; the local state cannot represent undefined empty value.
format_options(NumberFormat)PartialIntl.NumberFormatOptions—Supports decimal, currency and percentage formatting without the complete Intl option surface.
is_required(bool)true | falsefalseMarks the label and live FormField required and blocks invalid native submission.
is_invalid(bool)true | falsefalseForces invalid chrome and validation reporting.
validate(callback)PartialFn(value: number) -> ValidationError | true | null | undefined—Custom validation returns one optional GPUI message.
validation_behavior(ValidationBehavior)ValidationBehavior::Native | ValidationBehavior::AriaValidationBehavior::NativeChooses blocking native or non-blocking allow behavior.
validation_errors(errors)string[]—Server messages take precedence over the custom validator.
min_value(value)number—Inclusive lower bound for typing, keys, wheel input and steppers.
max_value(value)number—Inclusive upper bound for typing, keys, wheel input and steppers.
step(value)number1Positive finite increment lattice shared by every input path.
is_disabled(bool)true | falsefalseDims the whole field, removes interaction and omits form data.
is_read_only(bool)true | falsefalseKeeps the field focusable while blocking text, key, wheel and stepper edits.
name(value)string—Registers the current number under this form field name.
auto_focus(bool)true | falsefalseFocuses the numeric input once, including when replacement content is rendered.
vertical_steppers(bool)Partialtrue | false—Builds the group monolithically, including either horizontal buttons or the documented vertical-chevron composition.
increment_icon(icon)AnyElement<IconPlus />Replaces the increment glyph while preserving the button's spin behavior.
decrement_icon(icon)AnyElement<IconMinus />Replaces the decrement glyph while preserving the button's spin behavior.
Not ported6 HeroUI entries not ported
Number Field builders not ported
HeroUI v3Why it is not ported
NumberField.classNameBrowser styles are unavailable.
NumberField.styleBrowser inline styles are unavailable.
NumberField.renderRoot-element substitution has no GPUI equivalent.
NumberField.localeLocale-sensitive separators, digit systems and currency placement require CLDR data not bundled by this port.
NumberField.aria-label / aria-labelledby / aria-describedby / aria-detailsGpui-pre 0.3.3 exposes no accessibility attribute tree.
NumberField.Input.classNameArbitrary input classes are unavailable.

Parts

Number Field parts
PartDescription
NumberFieldRoot stack owning numeric state, validation, formatting, focus and form registration.
NumberFieldPartialUnified 36px field shell; composition is selected through typed builders rather than arbitrary children.
NumberFieldPartialInner Input bound to NumberState and stripped of independent group-edge chrome.
NumberFieldPartialBuilt-in repeatable spin button with caller-replaceable content.
NumberFieldRequired- and invalid-aware field label above the group.
NumberFieldHelp text shown below the group while the field is valid.
NumberFieldResolved server, custom or controlled validation message replacing Description.

States

Number Field states
StateBuilderDescription
Invalidvalidation::resolveInvalid chrome and FieldError replace the normal description.
Disabledis_disabledThe whole field is dimmed, inert, omitted from form data and outside the tab order.
Read onlyis_read_only interaction gatesFocus remains available while every numeric mutation path is disabled.
Focus withinshared Input focus_handleFocus on the input or either spin button uses the unified field focus treatment.
Focus visiblePartialfocus_visibleReplacement content receives keyboard-visible focus state; the built-in group uses focus-within chrome.
HoveredPartialgroup hover_fade_with_duration_and_easing(150ms, EaseSmooth)The enabled unfocused group uses the hover fill and border; the fill interpolates over 150ms ease-smooth while the border endpoint remains immediate. Button hover intentionally has no additional rule body.
PressedPartialpressed_with_backgroundPress fills and geometrically scales the spin button while starting the repeat schedule; arbitrary icon children retain their own fixed size.
Requiredis_requiredRequired state reaches label, validation, form submission and replacement content.