Primitives
Number, password and code inputs
Numbers clamp when you leave the field, not while typing; Caps Lock is flagged; a pasted code fills every box.
cars
Cars the site can hold. ↑ ↓ to step, Shift for ten.
৳
Caps Lock is flagged while it is on.
Strength
Verification code
Paste a code to fill every box.
split · 3–3
underline · a PIN, masked
error
number-password-pin.tsx
"use client";
import { FormField, NumberInput, PasswordInput, PinInput } from "@waveinno/ui";
import * as React from "react";
export default function FieldsDemo() {
const [cap, setCap] = React.useState<number | null>(120);
const [rate, setRate] = React.useState<number | null>(30);
const [code, setCode] = React.useState("");
const [done, setDone] = React.useState<string | null>(null);
return (
<div className="grid gap-x-8 gap-y-6 md:grid-cols-2">
<FormField label="Capacity" hint="Cars the site can hold. ↑ ↓ to step, Shift for ten.">
<NumberInput value={cap} onValueChange={setCap} min={0} max={2000} suffix="cars" />
</FormField>
<FormField label="Hourly rate">
<NumberInput value={rate} onValueChange={setRate} min={0} step={5} prefix="৳" stepper="stack" />
</FormField>
<FormField label="Password" hint="Caps Lock is flagged while it is on.">
<PasswordInput placeholder="Your password" />
</FormField>
<FormField label="New password">
<PasswordInput strength placeholder="At least 8 characters" />
</FormField>
<div className="space-y-2">
<p className="text-[13px] font-medium text-text">Verification code</p>
<PinInput value={code} onValueChange={setCode} onComplete={setDone} />
<p className="text-[12px] text-muted">{done ? `Entered ${done}` : "Paste a code to fill every box."}</p>
</div>
<div className="space-y-3">
<div className="space-y-2">
<p className="text-[12px] text-muted">split · 3–3</p>
<PinInput variant="split" defaultValue="482" />
</div>
<div className="flex flex-wrap gap-6">
<div className="space-y-2">
<p className="text-[12px] text-muted">underline · a PIN, masked</p>
<PinInput variant="underline" length={4} mask defaultValue="12" aria-label="PIN" />
</div>
<div className="space-y-2">
<p className="text-[12px] text-muted">error</p>
<PinInput length={4} error defaultValue="1234" />
</div>
</div>
</div>
</div>
);
}Import#
Everything comes from the package root. The example above uses:
your-component.tsx
import { FormField, NumberInput, PasswordInput, PinInput } from "@waveinno/ui";Props#
Read from the source of this version, so they match the code exactly.
<FormField>
src/components/ui/form-field.tsx| Prop | Type | Default | Description |
|---|---|---|---|
label | React.ReactNode | — | |
htmlFor | string | — | The control's id, so clicking the label focuses it. |
required | boolean | — | |
hint | React.ReactNode | — | |
error | React.ReactNode | — | |
warning | React.ReactNode | — | |
success | React.ReactNode | — | |
count | { value: number; max: number } | — | Character counter, e.g. { value: note.length, max: 240 }. |
className | string | — | |
childrenrequired | React.ReactNode | — |
<NumberInput>
src/components/ui/number-input.tsx| Prop | Type | Default | Description |
|---|---|---|---|
value | number | null | — | |
defaultValue | number | null | null | |
onValueChange | (value: number | null) => void | — | |
min | number | -Infinity | |
max | number | Infinity | |
step | number | 1 | |
precision | number | — | Decimal places kept. Default: as many as `step` has. |
prefix | React.ReactNode | — | Inside the box, before the number: "৳", "+880". |
suffix | React.ReactNode | — | After it: "min", "cars", "%". |
stepper | "split" | "stack" | "none" | "split" | |
state | InputState | "default" |
Also takes Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue" | "onChange" | "type" | "min" | "max" | "step" | "prefix">.
<PasswordInput>
src/components/ui/password-input.tsx| Prop | Type | Default | Description |
|---|---|---|---|
state | InputState | — | |
strength | boolean | — | A meter under the field: for choosing a new password. |
Also takes Omit<React.InputHTMLAttributes<HTMLInputElement>, "type">.
<PinInput>
src/components/ui/pin-input.tsx| Prop | Type | Default | Description |
|---|---|---|---|
length | number | 6 | |
value | string | — | |
defaultValue | string | "" | |
onValueChange | (value: string) => void | — | |
onComplete | (value: string) => void | — | |
type | "numeric" | "alphanumeric" | "numeric" | `numeric` (default) brings up the number pad; `alphanumeric` takes letters too. |
mask | boolean | — | Dots instead of characters: PINs, not one-time codes. |
variant | "boxed" | "underline" | "split" | "boxed" | |
size | "md" | "lg" | "md" | |
error | boolean | — | |
disabled | boolean | — | |
autoFocus | boolean | — | |
aria-label | string | "Verification code" | Read aloud as the group's name. |
className | string | — |
Install#
If the project does not have the package yet, add it, then follow the setup for Next.js or Vite once.
$ npm install @waveinno/ui