Monet
@etched/monet 0.0.1

Number field

Ported

A number with − and + buttons. Arrow Up and Down step by step, with Shift by largeStep and with Alt by smallStep; Home and End go to min and max. format takes Intl.NumberFormat options (currency, percent, units), and the user may type in that format. Native <input> attributes and ref pass through to the field.

import { NumberField } from "@etched/monet/number-field";

export function Preview() {
  return <NumberField aria-label="Benches" defaultValue={4} min={0} max={12} />;
}

Installation

import { NumberField } from "@etched/monet/number-field";

Examples

Steps and limits

min, max and step. Arrow Up and Down step; Shift steps by largeStep (10 by default). A button turns off at its limit.

Up to 12 per run.

import { NumberField } from "@etched/monet/number-field";

export function StepsAndLimits() {
  return (
    <div className="grid">
      <NumberField label="Benches" defaultValue={4} min={0} max={12} description="Up to 12 per run." />
      <NumberField label="Sample interval (ms)" defaultValue={250} min={50} max={5000} step={50} largeStep={500} />
    </div>
  );
}

Sizes

import { NumberField } from "@etched/monet/number-field";

export function Sizes() {
  return (
    <div className="grid">
      <NumberField size="sm" aria-label="Retries (small)" defaultValue={3} min={0} />
      <NumberField aria-label="Retries" defaultValue={3} min={0} />
    </div>
  );
}

Formats

format takes Intl.NumberFormat options; the user may type in that format. A percent is a fraction: 0.25 shows 25%.

import { NumberField } from "@etched/monet/number-field";

export function Formats() {
  return (
    <div className="grid">
      <NumberField label="Base salary" defaultValue={185000} min={0} step={1000} format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }} />
      <NumberField label="Sampling rate" defaultValue={0.25} min={0} max={1} step={0.05} format={{ style: "percent" }} />
      <NumberField label="Die temperature" defaultValue={71.5} step={0.5} format={{ style: "unit", unit: "celsius", minimumFractionDigits: 1 }} />
    </div>
  );
}

Controlled

onValueChange gets the number, or null when the field is empty.

3 openings on the job page.

import { useState } from "react";
import { NumberField } from "@etched/monet/number-field";

export function Controlled() {
  const [headcount, setHeadcount] = useState<number | null>(3);

  return (
    <div className="grid">
      <NumberField
        label="Headcount"
        value={headcount}
        onValueChange={setHeadcount}
        min={1}
        description={headcount === null ? "Enter a number." : `${headcount} ${headcount === 1 ? "opening" : "openings"} on the job page.`}
      />
    </div>
  );
}

States

A read-only field shows only its value; an error comes from error.

Set by the offer.

Plan at least one interview.
import { NumberField } from "@etched/monet/number-field";

export function States() {
  return (
    <div className="grid">
      <NumberField label="Level" defaultValue={5} readOnly description="Set by the offer." />
      <NumberField label="Interviews" defaultValue={0} min={0} error="Plan at least one interview." />
      <NumberField label="Seats" defaultValue={8} disabled />
      <NumberField label="Referral bonus" placeholder="$0" required={false} format={{ style: "currency", currency: "USD" }} />
    </div>
  );
}

Accessibility

  • ArrowUp/ArrowDown step by step; with Shift by largeStep, with Alt by smallStep. With min and max set, Home/End go to them.
  • A textbox with aria-roledescription "Number field", named by label, in a group. The buttons are named "Decrease" and "Increase", or by decrementLabel and incrementLabel.
  • The − and + buttons are out of the tab order, as the arrow keys do the same. They turn off at a limit.
  • description and error describe the field through aria-describedby, and an error sets aria-invalid.
  • Without label, give it an aria-label. Put the unit in format or in the label, such as "Sample interval (ms)".

API reference

PropTypeDefaultDescription
labelReactNode–Visible label. Without one, give the control an aria-label.
labelTooltipReactNode–More about the field, behind an info button after the label.
labelTooltipLabelstring"More information"Names the info button.
descriptionReactNode–Help text below the control. Hidden while an error shows.
errorFieldError–
size"sm" | "base""base"
incrementLabelstring"Increase"Names the + button.
decrementLabelstring"Decrease"Names the − button.
classNamestring–Class on the outer element, for layout.