NumberField
HeroUIA number, typed or stepped by its buttons and the arrow keys.
Solidtype_heroui.Number_field is Kobalte’s NumberField.Root, with the
classes HeroUI’s own numberFieldVariants gives, and the parts inside it as
Kobalte’s. Its value is a number, controlled or its own, kept on its step’s
grid within its range as HeroUI’s field keeps it: Kobalte snaps nothing, so
the field snaps each value, a value halfway to the step away from the
minimum, written with its step’s decimals, so a tenth added to 0.2 is 0.3;
and it disables a button its step would not move. Its
input is Kobalte’s spinbutton.
HeroUI’s examples, with HeroUI’s icons from @gravity-ui/icons.
module Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_field
let make () = <Number_field class_="w-full max-w-64" default_value=1024. min_value=0. name="width"> <Label>"Width"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> </Number_field>Anatomy
Section titled “Anatomy”<Number_field> <Label>"Width"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input /> <Number_field.Increment_button /> </Number_field.Group> <Description>"In pixels"</Description> <Field_error>"Too wide"</Field_error></Number_field>Examples
Section titled “Examples”Variants
Section titled “Variants”module Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_field
let make () = <div class_="flex flex-col gap-4"> <Number_field default_value=100. min_value=0. name="primary-width" variant=`primary> <Label>"Primary variant"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> </Number_field> <Number_field default_value=100. min_value=0. name="secondary-width" variant=`secondary> <Label>"Secondary variant"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> </Number_field> </div>In Surface
Section titled “In Surface”module Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_fieldmodule Surface = Solidtype_heroui.Surfacemodule Format = Solidtype_kobalte.Types.NumberFormatOptions
let make () = <Surface class_="flex w-full max-w-[280px] flex-col gap-4 rounded-3xl p-6"> <Number_field default_value=1024. min_value=0. name="width" variant=`secondary> <Label>"Width"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-full" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Enter the width in pixels"</Description> </Number_field> <Number_field default_value=0.5 format_options=(fun () -> Format.make ~style:`percent ()) max_value=1. min_value=0. name="percentage" step=0.1 variant=`secondary> <Label>"Percentage"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-full" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Value must be between 0 and 100"</Description> </Number_field> </Surface>With Description
Section titled “With Description”module Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_fieldmodule Format = Solidtype_kobalte.Types.NumberFormatOptions
let make () = <div class_="flex w-full max-w-64 flex-col gap-4"> <Number_field default_value=1024. min_value=0. name="width"> <Label>"Width"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Enter the width in pixels"</Description> </Number_field> <Number_field default_value=0.5 format_options=(fun () -> Format.make ~style:`percent ()) max_value=1. min_value=0. name="percentage" step=0.1> <Label>"Percentage"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Value must be between 0 and 100"</Description> </Number_field> </div>Required Field
Section titled “Required Field”module Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_field
let make () = <div class_="flex w-full max-w-64 flex-col gap-4"> <Number_field is_required=true min_value=0. name="quantity"> <Label>"Quantity"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> </Number_field> <Number_field is_required=true default_value=1. max_value=10. min_value=1. name="rating"> <Label>"Rating"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Rate from 1 to 10"</Description> </Number_field> </div>Disabled State
Section titled “Disabled State”module Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_fieldmodule Format = Solidtype_kobalte.Types.NumberFormatOptions
let make () = <div class_="flex w-full max-w-64 flex-col gap-4"> <Number_field is_disabled=true default_value=1024. min_value=0. name="width"> <Label>"Width"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Enter the width in pixels"</Description> </Number_field> <Number_field is_disabled=true default_value=0.5 format_options=(fun () -> Format.make ~style:`percent ()) max_value=1. min_value=0. name="percentage" step=0.1> <Label>"Percentage"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Value must be between 0 and 100"</Description> </Number_field> </div>Full Width
Section titled “Full Width”module Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_field
let make () = <div class_="w-[400px] space-y-4"> <Number_field full_width=true default_value=1024. min_value=0. name="width"> <Label>"Width"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input /> <Number_field.Increment_button /> </Number_field.Group> </Number_field> </div>Validation
Section titled “Validation”module Field_error = Solidtype_heroui.Field_errormodule Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_fieldmodule Format = Solidtype_kobalte.Types.NumberFormatOptions
let make () = <div class_="flex w-full max-w-64 flex-col gap-4"> <Number_field is_invalid=true is_required=true min_value=0. name="quantity" value=(-5.)> <Label>"Quantity"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Field_error>"Quantity must be greater than or equal to 0"</Field_error> </Number_field> <Number_field is_invalid=true format_options=(fun () -> Format.make ~style:`percent ()) max_value=1. min_value=0. name="percentage" step=0.1 value=1.5> <Label>"Percentage"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Field_error>"Percentage must be between 0 and 100"</Field_error> </Number_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 Number_field = Solidtype_heroui.Number_field
let make () = let value, set_value = S.create_signal 1024. in <div class_="flex w-full max-w-64 flex-col gap-4"> <Number_field min_value=0. name="width" value=(fun () -> S.get value) on_change=(S.set set_value)> <Label>"Width"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description> (fun () -> Jsx.string ("Current value: " ^ Js.Float.toString (S.get value))) </Description> </Number_field> <div class_="flex gap-2"> <Button variant=`tertiary on_click=(fun _ -> S.set set_value 0.)> "Reset to 0" </Button> <Button variant=`tertiary on_click=(fun _ -> S.set set_value 2048.)> "Set to 2048" </Button> </div> </div>Step Values
Section titled “Step Values”module Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_field
let make () = <div class_="flex w-full max-w-64 flex-col gap-4"> <Number_field default_value=0. max_value=100. min_value=0. name="step1" step=1.> <Label>"Step: 1"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Increments by 1"</Description> </Number_field> <Number_field default_value=0. max_value=100. min_value=0. name="step5" step=5.> <Label>"Step: 5"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Increments by 5"</Description> </Number_field> <Number_field default_value=0. max_value=100. min_value=0. name="step10" step=10.> <Label>"Step: 10"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Increments by 10"</Description> </Number_field> </div>Format Options
Section titled “Format Options”format_options is Kobalte’s own: the input shows and reads its value as a currency, a percent or a unit, in the reader’s locale, and tells a reader the value as it shows it.
module Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_fieldmodule Format = Solidtype_kobalte.Types.NumberFormatOptions
let make () = <div class_="flex w-full max-w-64 flex-col gap-4"> <Number_field default_value=99. min_value=0. name="currency-eur" format_options=(fun () -> Format.make ~currency:"EUR" ~currency_sign:`accounting ~style:`currency ())> <Label>"Currency (EUR - Accounting)"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Accounting format with EUR currency"</Description> </Number_field> <Number_field default_value=99.99 min_value=0. name="currency-usd" format_options=(fun () -> Format.make ~currency:"USD" ~style:`currency ())> <Label>"Currency (USD)"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Standard USD currency format"</Description> </Number_field> <Number_field default_value=0.5 format_options=(fun () -> Format.make ~style:`percent ()) max_value=1. min_value=0. name="percentage" step=0.01> <Label>"Percentage"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Percentage format (0-1, where 0.5 = 50%)"</Description> </Number_field> <Number_field default_value=1234.56 min_value=0. name="decimal" format_options=(fun () -> Format.make ~maximum_fraction_digits:2. ~minimum_fraction_digits:2. ~style:`decimal ())> <Label>"Decimal (2 decimal places)"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Decimal format with 2 decimal places"</Description> </Number_field> <Number_field default_value=1000. min_value=0. name="unit" format_options=(fun () -> Format.make ~style:`unit ~unit:"kilogram" ~unit_display:`short ())> <Label>"Unit (Kilograms)"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Description>"Unit format with kilograms"</Description> </Number_field> </div>Form Example
Section titled “Form Example”HeroUI’s button takes isPending while the order is sent; the port’s button has no pending state yet, so this one shows the spinner alone.
module S = Solidtype.Solidmodule Jsx = Solidtype.Jsxmodule 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 Number_field = Solidtype_heroui.Number_fieldmodule Spinner = Solidtype_heroui.Spinner
external prevent_default : Dom.event -> unit = "preventDefault" [@@mel.send]
let stock_available = 3.let stock = Js.Float.toString stock_available
let make () = (* Empty is not a number. *) let value, set_value = S.create_signal Float.nan in let is_submitting, set_is_submitting = S.create_signal false in let is_out_of_stock () = (not (Float.is_nan (S.get value))) && S.get value > stock_available in let unavailable () = let v = S.get value in Float.is_nan v || v < 1. || v > stock_available in let handle_submit e = prevent_default e; if not (unavailable ()) then ( S.set set_is_submitting true; (* Simulate API call *) ignore (Js.Global.setTimeout ~f:(fun () -> Js.log2 "Order submitted, quantity:" (S.get value); S.set set_value Float.nan; S.set set_is_submitting false) 1500 : Js.Global.timeoutId)) in <Form class_="flex w-[280px] flex-col gap-4" on_submit=handle_submit> <Number_field is_required=true is_invalid=(fun () -> is_out_of_stock ()) max_value=5. min_value=1. name="quantity" value=(fun () -> S.get value) on_change=(S.set set_value)> <Label>"Order quantity"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Show when_=(fun () -> is_out_of_stock ()) fallback=(<Description> (fun () -> Jsx.string ("Only " ^ stock ^ " items available")) </Description>)> <Field_error> (fun () -> Jsx.string ("Only " ^ stock ^ " items left in stock")) </Field_error> </Show> </Number_field> <Button class_="w-full" is_disabled=(fun () -> unavailable ()) type_=`submit variant=`primary> <Show when_=(fun () -> not (S.get is_submitting))>"Place Order"</Show> <Show when_=(fun () -> S.get is_submitting)> <Spinner color=`current size=`sm /> "Processing..." </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 Number_field = Solidtype_heroui.Number_fieldmodule Format = Solidtype_kobalte.Types.NumberFormatOptions
let make () = (* Empty is not a number. *) let value, set_value = S.create_signal Float.nan in let is_invalid () = let v = S.get value in (not (Float.is_nan v)) && (v < 0. || v > 100.) in <div class_="flex w-full max-w-64 flex-col gap-4"> <Number_field is_required=true format_options=(fun () -> Format.make ~style:`percent ()) is_invalid=(fun () -> is_invalid ()) max_value=1. min_value=0. name="percentage" step=0.1 value=(fun () -> S.get value) on_change=(S.set set_value)> <Label>"Percentage"</Label> <Number_field.Group> <Number_field.Decrement_button /> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button /> </Number_field.Group> <Show when_=(fun () -> is_invalid ()) fallback=(<Description>"Enter a value between 0 and 100"</Description>)> <Field_error>"Percentage must be between 0 and 100"</Field_error> </Show> </Number_field> </div>Custom Icons
Section titled “Custom Icons”module Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_field
let make () = <div class_="flex w-full max-w-64 flex-col gap-4"> <Number_field default_value=1024. min_value=0. name="width"> <Label>"Width (Custom Icons)"</Label> <Number_field.Group> <Number_field.Decrement_button> <Gravity.Magnifier_minus /> </Number_field.Decrement_button> <Number_field.Input class_="w-[120px]" /> <Number_field.Increment_button> <Gravity.Magnifier_plus /> </Number_field.Increment_button> </Number_field.Group> <Description>"Custom icon children"</Description> </Number_field> </div>With Chevrons
Section titled “With Chevrons”module Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_fieldmodule Format = Solidtype_kobalte.Types.NumberFormatOptions
let make () = <Number_field class_="w-full max-w-64" default_value=99. min_value=0. name="amount" format_options=(fun () -> Format.make ~currency:"EUR" ~currency_sign:`accounting ~style:`currency ())> <Label>"Number field with chevrons"</Label> <Number_field.Group class_="flex"> <Number_field.Input class_="flex-1" /> <div class_="flex h-full flex-col border-s border-field-placeholder/15"> <Number_field.Increment_button class_="flex h-1/2 w-6 items-center justify-center rounded-none \ border-0 pt-0.5 text-sm"> <Gravity.Chevron_up width=11 height=11 /> </Number_field.Increment_button> <Number_field.Decrement_button class_="flex h-1/2 w-6 items-center justify-center rounded-none \ border-0 pb-0.5 text-sm"> <Gravity.Chevron_down width=11 height=11 /> </Number_field.Decrement_button> </div> </Number_field.Group> </Number_field>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 Label = Solidtype_heroui.Labelmodule Number_field = Solidtype_heroui.Number_field
let make () = <Number_field class_="w-full max-w-48" default_value=2. min_value=1. name="guests" variant=`secondary> <Label class_="font-medium text-foreground">"Guests"</Label> <Number_field.Group class_="rounded-xl bg-default"> <Number_field.Decrement_button class_="text-muted hover:text-foreground" /> <Number_field.Input class_="text-center tabular-nums" /> <Number_field.Increment_button class_="text-muted hover:text-foreground" /> </Number_field.Group> </Number_field>Styling Reference
Section titled “Styling Reference”CSS Classes
Section titled “CSS Classes”.number-field, and.number-field--primary,.number-field--secondary,.number-field--full-width.number-field__group, and.number-field__group--full-width.number-field__input.number-field__increment-button,.number-field__decrement-button
Interactive States
Section titled “Interactive States”- Hover:
[data-hovered="true"]on the group, the input and the buttons - Focus within:
[data-focus-within="true"]on the group - Focus visible:
[data-focus-visible="true"]on the input and buttons - Disabled:
[data-disabled="true"] - Invalid:
[data-invalid="true"]
API Reference
Section titled “API Reference”Each prop beside the name HeroUI’s React component gives it.
NumberField
Section titled “NumberField”| React | solidtype | Type | Default | Description |
|---|---|---|---|---|
variant |
variant |
[ `primary | `secondary ] |
`primary |
Its look |
fullWidth |
full_width |
bool |
false |
Whether it takes its container’s width |
value |
value |
float |
Its value, controlled | |
defaultValue |
default_value |
float |
Its value at first, kept by itself | |
onChange |
on_change |
float -> unit |
Told each change of its value | |
minValue |
min_value |
float |
The least value | |
maxValue |
max_value |
float |
The greatest value | |
step |
step |
float |
1. |
How far a button or an arrow key steps |
isDisabled |
is_disabled |
bool |
Whether it is disabled, else as its fieldset is | |
isInvalid |
is_invalid |
bool |
false |
Whether it is invalid, its error shown |
isReadOnly |
is_read_only |
bool |
false |
Whether its value is kept |
isRequired |
is_required |
bool |
false |
Whether it is required |
name |
name |
string |
Its name in a form | |
aria-label |
aria_label |
string |
Its name where no label names it | |
className |
class_ |
string |
Added after HeroUI’s classes | |
onBlur |
on_blur |
Dom.focusEvent -> unit |
Called once focus leaves it for an element outside it | |
formatOptions |
format_options |
Solidtype_kobalte.Types.NumberFormatOptions.t |
How its value is shown and read | |
validate |
— | A field validates nothing itself: its is_invalid and Field_error come from the application’s state |
NumberField.Group, Input, IncrementButton, DecrementButton
Section titled “NumberField.Group, Input, IncrementButton, DecrementButton”Each takes class_, added after HeroUI’s class; the input a placeholder;
a button children, drawn in place of HeroUI’s plus or minus.
Beside React
Section titled “Beside React”Original HeroUI documentation: NumberField.
The same HeroUI NumberField twice, under the same props: in solidtype on the left, in React on the right. Below them, every attribute of the two groups: a marked row is one they disagree on.
| attribute | solidtype | React |
|---|
