Quantity input
A length, a volume or a speed typed in the unit the crew think in and held in the base unit, rounded the way that keeps it safe.
On this page
Code
import { QuantityInput } from "@bosunkit/components/quantity-input";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const description: string =
"A vessel's draft and air draft typed in feet and held in meters, each rounded up so it never understates the " +
"vessel, and a fuel tank typed in US gallons and held in liters. The unit button switches the unit.";
export default function Showcase(): ReactElement {
const [draft, setDraft]: [number | null, Dispatch<SetStateAction<number | null>>] = useState<number | null>(1.99);
const [air, setAir]: [number | null, Dispatch<SetStateAction<number | null>>] = useState<number | null>(19.2);
const [fuel, setFuel]: [number | null, Dispatch<SetStateAction<number | null>>] = useState<number | null>(400);
return (
<div style={{ width: "100%", display: "flex", flexWrap: "wrap", gap: "var(--space-lg)" }}>
<QuantityInput
label="Draft"
kind="length"
defaultUnit="ft"
rounding="up"
max={50}
value={draft}
onValueChange={setDraft}
/>
<QuantityInput
label="Air draft"
kind="length"
defaultUnit="ft"
rounding="up"
value={air}
onValueChange={setAir}
/>
<QuantityInput
label="Fuel tank"
kind="volume"
defaultUnit="us_gal"
decimals={0}
value={fuel}
onValueChange={setFuel}
/>
</div>
);
}
A quantity input holds a length, a volume or a speed in its base unit, meters, liters or knots, the units bosund's schemas name, while the crew type it in the unit they think in. A length can be typed in feet, a volume in US or imperial gallons, and a speed in km/h or m/s. The unit button after the value switches the unit and writes the same value in it; the value the page holds does not change. Under the field, a value typed in another unit says what it is saved as, "Saved as 1.99 m", so a skipper who types a draft of 6.5 sees that it is feet and not meters. The factors are exact by definition: the foot is 0.3048 m, the US gallon 3.785411784 L, the imperial gallon 4.54609 L, and the knot 1.852 km/h. A value pasted or typed with the unit the field shows after it ("12.5 kn") is read as its figure; one in another unit is not converted, and the field says it cannot read it, so the crew switch the unit.
A value is held to decimals in the base unit and written to the same decimals in every unit, cut the way
rounding says. A depth or a clearance under the keel rounds down, so it never overstates the water. A draft or an
air draft rounds up, so it never understates the vessel. A value the field wrote is never read back through that
rounding again, so switching units back and forth, or leaving the field twice, does not move it. min and max are
in the base unit, and a value outside them is named in the unit shown, rounded inward so the limit stated is one the
field takes. While the field shows text it does not take, a value outside the bounds or one it cannot read, it keeps
the text and reports no value, so a form never saves the one that stood before.
Examples
In the base unit
Code
import { QuantityInput } from "@bosunkit/components/quantity-input";
import type { ReactElement } from "react";
export const description: string = "In the base unit, the value is written to its decimals and nothing is converted.";
export default function BaseUnit(): ReactElement {
return <QuantityInput label="Length overall" kind="length" defaultValue={13.95} />;
}
Which way it rounds
Code
import { QuantityInput } from "@bosunkit/components/quantity-input";
import type { ReactElement } from "react";
export const description: string =
"`rounding` decides which way a converted value is cut to its decimals: down for a depth or a clearance under the " +
"keel, so it never overstates the water; up for a draft or an air draft, so it never understates the vessel.";
export default function Rounding(): ReactElement {
return (
<div style={{ display: "flex", flexWrap: "wrap", gap: "var(--space-lg)" }}>
<QuantityInput label="Least depth on the bar" kind="length" defaultUnit="ft" rounding="down" defaultValue={2.4} />
<QuantityInput label="Draft" kind="length" defaultUnit="ft" rounding="up" defaultValue={2.4} />
</div>
);
}
Speed
Code
import { QuantityInput } from "@bosunkit/components/quantity-input";
import type { ReactElement } from "react";
export const description: string =
"A speed is held in knots and can be typed in km/h or m/s, as a river or canal authority posts its limit.";
export default function Speed(): ReactElement {
return <QuantityInput label="Canal speed limit" kind="speed" defaultUnit="kmh" decimals={1} defaultValue={4.3} />;
}
Least and greatest
Code
import { QuantityInput } from "@bosunkit/components/quantity-input";
import type { ReactElement } from "react";
export const description: string =
"`min` and `max` are in the base unit, and the error names them in the unit shown, rounded inward so the limit it " +
"states is one the field takes.";
export default function Bounds(): ReactElement {
return (
<QuantityInput
label="Draft"
kind="length"
defaultUnit="ft"
max={50}
error="Enter at most 164.04 ft."
defaultValue={null}
/>
);
}
Read-only
Code
import { QuantityInput } from "@bosunkit/components/quantity-input";
import type { ReactElement } from "react";
export const description: string = "Read-only: the value and its unit written, the unit not offered.";
export default function ReadOnly(): ReactElement {
return <QuantityInput label="Water tank" kind="volume" readOnly decimals={0} defaultValue={300} />;
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | undefined | – | The field's name on the outline, translated by the caller; omit it inside a FormField. |
| kindrequired | QuantityKind | – | What the value measures: length (held in m), volume (in L) or speed (in kn). |
| value | number | null | undefined | – | The value in the kind's base unit, or null for none. |
| defaultValue | number | null | undefined | – | The value it starts with when the caller does not hold value. |
| onValueChange | ((base: number | null) => void) | undefined | – | Called with the value in the base unit when the crew type one and leave the field; null when they clear it. |
| unit | QuantityUnit | undefined | – | The unit the field shows and reads, for a caller that holds it (the crew's preference); the base unit unless set. |
| defaultUnit | QuantityUnit | undefined | – | The unit it starts in when the caller does not hold unit. |
| onUnitChange | ((unit: QuantityUnit) => void) | undefined | – | Called with the unit the crew switch to. |
| decimals | number | undefined | 2 | The decimals the value is held to in the base unit, and written to in every unit; 2 unless set. |
| rounding | QuantityRounding | undefined | "nearest" | Which way a value is rounded to its decimals: nearest unless set. |
| min | number | undefined | – | The least value, in the base unit. |
| max | number | undefined | – | The greatest value, in the base unit. |
| hint | string | undefined | – | Text under the field while the base unit is shown, translated by the caller. |
| error | string | undefined | – | The caller's own error, shown in place of the field's. |
| required | boolean | undefined | – | Whether the field must hold a value. |
| disabled | boolean | undefined | – | Whether the field takes no input. |
| readOnly | boolean | undefined | – | Whether the field shows its value without letting the crew change it. |
| id | string | undefined | – | The input's id, for a Label's htmlFor. |
| name | string | undefined | – | The form field name; the form receives the value in the base unit. |
| aria-label | string | undefined | – | Names the input when there is no label. |
| className | string | undefined | – | Classes added to the field's root. |
| testId | string | undefined | – | data-testid on the field. |
| ref | Ref<HTMLInputElement> | undefined | – | The input element. |
| motion | MotionProp | undefined | – | How the error line moves; as FormField's, an error that appears rises in by default. |
Related
- Number input: a plain number with no unit to convert.
- Duration input: a length of time in its parts.
- Reading: a live value from the instruments, which is not typed.