Skip to content
Component installs need the registry setup
VegaStack Design

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
stable
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-input

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/formula-input

Run 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 full TextEdit would 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

PropTypeDefaultDescription
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-describedbystring—Ids of more elements that describe it (a hint); the preview and the error are added.
aria-labelstring—Accessible name, when no FieldLabel names it.
aria-labelledbystring—Id of the element that names it.
classNamestring—Classes for the root.
disabledbooleanfalseRead-only and dimmed.
errorReact.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.
idstring—The editable surface's id (an enclosing Field supplies one).
invalidFieldsreadonly string[]—Keys whose chips show as invalid (a field the formula may not use).
operatorsreadonly TemplateEditorReplacement[]`*` → ×, `/` → ÷Typing replacements, applied as you type.
pickerLabelstring"Insert field"The field picker's accessible name.
placeholderstring"Type { to add a field"Shown while the formula is empty.
previewReact.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.
previewLabelstring"First row"The words before the preview.
refReact.Ref<HTMLDivElement>—Ref forwarded to the root (data-slot="formula-input").

Data attributes and CSS variables on FormulaInput

AttributeValues
data-invalid""
data-slot"formula-input" | "formula-input-error" | "formula-input-preview" | "formula-input-status"

FormulaInputField

PropTypeDefaultDescription
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.
typestring—The field's type ("Number", "Currency"), muted beside its name in the picker.

Accessibility

  • The input is a textbox named by its FieldLabel (or aria-label); the picker is a listbox it points at with aria-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.
KeyAction
{Opens the field picker
ArrowUp / ArrowDownMoves through the fields
Enter / TabInserts the highlighted field as a chip
EscapeCloses the picker
ContractStates tested
Behaviourdefault, empty, menu-open, preview, error, disabled
Accessibilitylabeled, described, invalid, keyboard, browser-accessibility-test
Visualdefault, empty, preview, error, invalid-token

Do / Don't

Do
Offer only the fields a formula can use, and show the first row's result as the formula is typed.
Don't
Ask for a formula in a plain text box with field names typed in braces and no check until save.

On this page