Skip to content
design
Components

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
Saved as 1.99 m
Saved as 19.20 m
Saved as 400 L
A draft and an air draft typed in feet and held in meters, rounded up, and a fuel tank typed in US gallons and held in liters.
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

In the base unit, the value is written to its decimals and nothing is converted.
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

Saved as 2.40 m
Saved as 2.40 m
`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.
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

Saved as 4.3 kn
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.
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

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

L
Read-only: the value and its unit written, the unit not offered.
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

PropTypeDefaultDescription
labelstring | undefined–The field's name on the outline, translated by the caller; omit it inside a FormField.
kindrequiredQuantityKind–What the value measures: length (held in m), volume (in L) or speed (in kn).
valuenumber | null | undefined–The value in the kind's base unit, or null for none.
defaultValuenumber | 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.
unitQuantityUnit | undefined–The unit the field shows and reads, for a caller that holds it (the crew's preference); the base unit unless set.
defaultUnitQuantityUnit | 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.
decimalsnumber | undefined2The decimals the value is held to in the base unit, and written to in every unit; 2 unless set.
roundingQuantityRounding | undefined"nearest"Which way a value is rounded to its decimals: nearest unless set.
minnumber | undefined–The least value, in the base unit.
maxnumber | undefined–The greatest value, in the base unit.
hintstring | undefined–Text under the field while the base unit is shown, translated by the caller.
errorstring | undefined–The caller's own error, shown in place of the field's.
requiredboolean | undefined–Whether the field must hold a value.
disabledboolean | undefined–Whether the field takes no input.
readOnlyboolean | undefined–Whether the field shows its value without letting the crew change it.
idstring | undefined–The input's id, for a Label's htmlFor.
namestring | undefined–The form field name; the form receives the value in the base unit.
aria-labelstring | undefined–Names the input when there is no label.
classNamestring | undefined–Classes added to the field's root.
testIdstring | undefined–data-testid on the field.
refRef<HTMLInputElement> | undefined–The input element.
motionMotionProp | undefined–How the error line moves; as FormField's, an error that appears rises in by default.
  • 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.