Signature input
A signature drawn with a finger, a stylus or the mouse, kept as a small SVG.
On this page
The page receives: no signature yet
Code
import { SignatureInput } from "@bosunkit/components/signature-input";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const description: string =
"The outgoing watch officer signs the handover: a finger, a stylus or the mouse draws on the line, Undo, Redo and " +
"Clear sit at the box's end, and the page receives the signature as a small SVG.";
export default function Showcase(): ReactElement {
const [signature, setSignature]: [string | null, Dispatch<SetStateAction<string | null>>] = useState<string | null>(
null,
);
return (
<div style={{ width: "100%", maxWidth: 480, display: "grid", gap: "var(--space-sm)" }}>
<SignatureInput
label="Outgoing watch officer"
required
value={signature}
onValueChange={setSignature}
hint="Sign to hand the watch over."
/>
<p style={{ margin: 0, color: "var(--text-dim)" }}>
The page receives: {signature === null ? "no signature yet" : `an SVG of ${String(signature.length)} characters`}
</p>
</div>
);
}
A signature input is a pad in an outlined box, with the label on its edge and a line to sign on, for a watch
handover, a delivery or a guest's waiver. A finger, a stylus or the mouse draws on it; the page does not scroll under
a finger while it draws. Each stroke is smoothed through the middle of the points the pointer reports, and kept as SVG
path data, so the value is a standalone SVG of a few kilobytes that a form stores and a log or a crew list prints. The
ink is currentColor, the color of the page that shows it, in every theme.
Undo, Redo and Clear sit at the box's end, each disabled while there is nothing to do; on the focused pad, Ctrl+Z
undoes and Ctrl+Shift+Z or Ctrl+Y redoes (Command on a Mac), and a Clear can be undone too. pointerTypes limits the
pointers that draw: ["pen"] keeps a hand resting on a tablet from drawing while the crew sign with a stylus. The pad
is a group named by the label, described as signed or not signed; a required pad left unsigned asks for a signature.
Drawing needs a pointer, so the box offers the other way itself: its keyboard button, "Type the name instead", puts
an input for the name in the pad's place, and the value is then the name as SVG text, marked
data-signature="typed" so whoever reads it later tells it from one drawn (typedSignatureName reads it back). The
pen button draws again. A signature is drawn or typed, never both, so switching clears the other kind; a stored typed
signature opens as its name, and a required one left empty asks for the name.
Examples
Stylus only
Code
import { SignatureInput } from "@bosunkit/components/signature-input";
import type { ReactElement } from "react";
export const description: string =
"A pad that takes only a stylus, so a hand resting on the tablet while the skipper signs draws nothing.";
export default function StylusOnly(): ReactElement {
return (
<div style={{ width: "100%", maxWidth: 480 }}>
<SignatureInput label="Skipper" pointerTypes={["pen"]} hint="Sign with the stylus." />
</div>
);
}
Typed
Code
import { SignatureInput, typedSignatureSvg } from "@bosunkit/components/signature-input";
import type { ReactElement } from "react";
export const description: string =
"A watch handover signed by typing the name, for a crew member who cannot draw one; the keyboard button switches back.";
export default function Typed(): ReactElement {
return (
<div style={{ width: "100%", maxWidth: 480 }}>
<SignatureInput label="Incoming watch officer" defaultValue={typedSignatureSvg("Mara Quist")} />
</div>
);
}
Read-only
Code
import { SignatureInput, signatureSvg } from "@bosunkit/components/signature-input";
import type { ReactElement } from "react";
export const description: string =
"A signed handover, read back: the stored SVG drawn in the page's ink color, with nothing to draw on or clear.";
// A signature as a form stored it: the SVG signatureSvg writes from the pad's strokes.
const STORED: string | null = signatureSvg([
"M72 128Q90 60 104 96Q118 132 132 88Q146 44 160 112Q170 140 186 104L200 98",
"M214 116Q232 82 250 110Q262 128 280 100Q298 72 316 108Q330 132 352 96L370 90",
"M392 120Q420 104 452 112L480 108",
]);
export default function ReadOnly(): ReactElement {
return (
<div style={{ width: "100%", maxWidth: 480 }}>
<SignatureInput label="Outgoing watch officer" readOnly defaultValue={STORED} />
</div>
);
}
Invalid
Code
import { SignatureInput } from "@bosunkit/components/signature-input";
import type { ReactElement } from "react";
export const description: string = "A required signature the crew left without signing, as the pad shows it.";
export default function Invalid(): ReactElement {
return (
<div style={{ width: "100%", maxWidth: 480 }}>
<SignatureInput label="Incoming watch officer" required error="Sign in the box with a finger, a pen or the mouse." />
</div>
);
}
Disabled
Code
import { SignatureInput } from "@bosunkit/components/signature-input";
import type { ReactElement } from "react";
export const description: string = "A pad that cannot be used until the handover checklist is complete.";
export default function Disabled(): ReactElement {
return (
<div style={{ width: "100%", maxWidth: 480 }}>
<SignatureInput label="Outgoing watch officer" disabled hint="Complete the checklist first." />
</div>
);
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| labelrequired | string | – | The field's name, drawn on the outline's top edge, and the pad's accessible name. |
| value | string | null | undefined | – | The signature as SVG (see signatureSvg), or null for none, when the caller holds it. |
| defaultValue | string | null | undefined | – | The signature the pad starts with when it holds it itself. |
| onValueChange | ((svg: string | null) => void) | undefined | – | Called with the signature as SVG after each stroke, undo, redo or clear; null once it is cleared. |
| pointerTypes | readonly SignaturePointer[] | undefined | ALL_POINTERS | The pointers that draw: all three when unset. ["pen"] keeps a palm resting on a tablet from drawing while the
crew sign with a stylus. |
| hint | ReactNode | – | A hint under the pad. |
| error | string | undefined | – | The caller's own problem with the signature, shown in place of the hint. |
| required | boolean | undefined | – | |
| disabled | boolean | undefined | – | |
| readOnly | boolean | undefined | – | Shows the signature without letting the crew change it: no ink, no buttons, not in the tab order. |
| name | string | undefined | – | Submits the signature's SVG with a form under this name. |
| id | string | undefined | – | The pad's id; one is made when unset. |
| className | string | undefined | – | |
| testId | string | undefined | – | Becomes data-testid on the root; the pad is ${testId}--pad. |
| motion | MotionProp | undefined | – | How the field's message moves in and out, as OutlinedField takes it. |
Related
- Outlined field: the box every maritime input draws in.
- Inline edit: a value changed where it is shown.