Formula Input
A calculated field's formula on one line — field chips from a typed brace, × and ÷ as you type, the first row's result under it and its error in its place.
- Status
- Since
0.25.89- Accessibility pattern
- labelled textbox, listbox picker, polite status line
Last updated
First row = 1,240
Type { to add a field.
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Formula Input before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify formula-inputInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/formula-inputRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The same command installs the registry items it composes: @vegastack/template-editor.
Usage
import { FormulaInput } from "@/components/ui/formula-input";
<Field>
<FieldLabel>Formula</FieldLabel>
<FormulaInput
value={formula}
onChange={setFormula}
fields={numberFields.map((f) => ({
key: f.id,
label: f.name,
type: f.typeLabel,
}))}
preview={check.ok ? format(check.firstRow) : undefined}
error={check.ok ? undefined : check.message}
/>
</Field>;A calculated field's formula as one line. Typing { opens a picker of the table's fields and
inserts the chosen one as a chip; * and / read as × and ÷. Under the input, the first row's
result ("First row = 1,240") — or, while the formula is wrong, what is wrong (Unknown field {Qyt}), which marks the input invalid. The value is text with each field written as {{key}}
({{qty}} × {{price}}); parsing, checking and evaluating it are the host's.
Scope
- Owns: the one-line editor with field chips, the
{picker, the operator replacements, the status line (preview or error) and its announcement. - Does not own: the grammar, the evaluation or which fields a formula may use — pass only those as
fields. - Built on:
TemplateEditor(singleLine,triggers={["{"]}). A fullTextEditwould bring blocks, a toolbar and collaboration a formula has no use for.
Examples
With a live preview
Edit the formula: the result under it follows the first row (Qty 4, Unit price 310).
First row = 1,240
Type { to add a field.
Error
An unknown field's chip shows invalid (invalidFields) and the error takes the preview's place.
Unknown field {qyt}
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
fields* | readonly FormulaInputField[] | — | The fields { offers, in the picker's order. |
onChange* | (value: string) => void | — | Called with the new formula on every edit. |
value* | string | — | The formula: text with each field written as {{key}}. |
aria-describedby | string | — | Ids of more elements that describe it (a hint); the preview and the error are added. |
aria-label | string | — | Accessible name, when no FieldLabel names it. |
aria-labelledby | string | — | Id of the element that names it. |
className | string | — | Classes for the root. |
disabled | boolean | false | Read-only and dimmed. |
error | React.ReactNode | — | What is wrong with the formula ("Unknown field {Qyt}"), under the input in the destructive ink; it marks the input invalid and is announced politely. |
id | string | — | The editable surface's id (an enclosing Field supplies one). |
invalidFields | readonly string[] | — | Keys whose chips show as invalid (a field the formula may not use). |
operators | readonly TemplateEditorReplacement[] | `*` → ×, `/` → ÷ | Typing replacements, applied as you type. |
pickerLabel | string | "Insert field" | The field picker's accessible name. |
placeholder | string | "Type { to add a field" | Shown while the formula is empty. |
preview | React.ReactNode | — | The first row's result, worked out by the host ("1,240"), shown under the input as
"First row = 1,240". Hidden while error shows. |
previewLabel | string | "First row" | The words before the preview. |
ref | React.Ref<HTMLDivElement> | — | Ref forwarded to the root (data-slot="formula-input"). |
Data attributes and CSS variables on FormulaInput
| Attribute | Values |
|---|---|
data-invalid | "" |
data-slot | "formula-input" | "formula-input-error" | "formula-input-preview" | "formula-input-status" |
FormulaInputField
| Prop | Type | Default | Description |
|---|---|---|---|
key* | string | — | The field's id — written into the value as {{key}}. |
label* | string | — | The field's name, on its chip and in the picker. |
type | string | — | The field's type ("Number", "Currency"), muted beside its name in the picker. |
Accessibility
- The input is a
textboxnamed by itsFieldLabel(oraria-label); the picker is a listbox it points at witharia-activedescendant. - The status line describes the input and is a polite live region: a fixed or broken formula is
heard once, after the keystroke. An error sets
aria-invalid. - A chip is one character for the caret: Backspace removes it whole.
| Key | Action |
|---|---|
| { | Opens the field picker |
| ArrowUp / ArrowDown | Moves through the fields |
| Enter / Tab | Inserts the highlighted field as a chip |
| Escape | Closes the picker |
| Contract | States tested |
|---|---|
| Behaviour | default, empty, menu-open, preview, error, disabled |
| Accessibility | labeled, described, invalid, keyboard, browser-accessibility-test |
| Visual | default, empty, preview, error, invalid-token |
Do / Don't
Template Editor
A plain-paragraph editor for text with placeholders — @ opens a searchable token list, a picked token becomes a chip, and the value stays plain text.
Field
The form-field scaffold — label, description, error, legend, separator and choice-card layouts in vertical, horizontal or responsive orientation.