Rule Builder
ProWrite a rule in plain words and read it back as a sentence of editable conditions, with its action and a backtest of what it would have done.
Office supplies from contracted suppliers
Backtest
Last 90 days, 1,480 requests
- Matches
- 312
- of 1,480
- Share
- 21%
- Amount
- €40.9k
- Hours saved
- 87 h
- Disputed later
- 7
| week | Matched | Still to a person |
|---|---|---|
| W1 | 26 | 97 |
| W2 | 31 | 92 |
| W3 | 25 | 98 |
| W4 | 22 | 101 |
| W5 | 28 | 95 |
| W6 | 30 | 93 |
| W7 | 23 | 100 |
| W8 | 23 | 100 |
| W9 | 30 | 93 |
| W10 | 28 | 95 |
| W11 | 22 | 101 |
| W12 | 25 | 98 |
Installation#
Usage#
The builder parses nothing itself. Hand the text to your own parser in onSubmit, set pending while it runs, and draw what comes back. Keep evaluating rules in deterministic code: the builder only shows them.
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Anatomy#
import {
RuleBuilder,
RuleBuilderHeader,
RuleBuilderTitle,
RuleBuilderMeta,
RuleStatus,
RuleVersion,
RuleBuilderInput,
RuleSentence,
RuleText,
RuleClause,
RuleCondition,
RuleAction,
RuleAddCondition,
RuleMissing,
RulePreview,
RulePreviewHeader,
RulePreviewTitle,
RulePreviewDescription,
RuleStats,
RuleStat,
RuleBuilderFooter,
} from "@/components/ui/rule-builder"From a parser#
Pass the parser's tokens straight to tokens. Each connector stays on the line of the chip after it, and removing a chip moves focus to the next one.
States#
A guessed condition takes a dashed edge, the pieces the parser missed are listed under the sentence, and while the rule is being read the sentence dims in place instead of disappearing.
Partly understood
- "during month end"
Not understood
- a condition
- what should happen
Reading
Action tones#
The action is the one filled chip in the sentence. Keep it in ink, or give an outcome its status color.
Status and version#
API reference#
RuleBuilder
The frame and the context every part reads. Every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| pending | boolean | false | The rule is being read. The submit button spins, and the sentence and the backtest dim until you clear it. |
| variant | "card" | "plain" | "card" | A framed surface, or no chrome for a full page. |
| density | "comfortable" | "compact" | - | Padding and spacing between parts. Reads DensityProvider when unset. |
| Data attribute | Description |
|---|---|
| data-pending | Present while the rule is being read. |
RuleBuilderInput
The plain-language field, built on PromptInput. Enter submits, Shift+Enter breaks a line, and the text stays after you submit. Children are a hint at the start of its toolbar.
| Prop | Type | Default | Description |
|---|---|---|---|
| onSubmit | (text: string) => void | - | Read the text with your parser and set pending while it runs. |
| value | string | - | The text, when you control it. |
| defaultValue | string | "" | The text on the first render. |
| onValueChange | (text: string) => void | - | The text changed. |
| pending | boolean | - | Overrides the pending of RuleBuilder. |
| placeholder | string | "Describe the rule in plain words" | Shown while the field is empty. |
| label | string | "Rule" | Names the field for a screen reader. |
| submitLabel | string | "Read rule" | Names the submit button. |
RuleSentence
The compiled rule as a line of text with chips in it. Compose RuleText, RuleClause, RuleCondition and RuleAction inside it, or pass tokens.
| Prop | Type | Default | Description |
|---|---|---|---|
| tokens | readonly RuleToken[] | - | Draws the sentence from a parser's output: { kind: "text", text }, { kind: "condition", index, field, op, value, uncertain? } or { kind: "action", text }. Each connector is kept on the line of the chip after it. |
| renderEditor | (token: RuleChipToken) => ReactNode | - | The editor for a condition or the action, when you pass tokens. |
| onRemoveCondition | (index: number) => void | - | Shows a remove button on each condition, when you pass tokens. |
| addCondition | ReactNode | - | Placed after the last condition, usually a RuleAddCondition. |
| actionTone | "neutral" | "success" | "warning" | "destructive" | - | The tone of the action chip, when you pass tokens. |
| Data attribute | Description |
|---|---|
| data-pending | Present while the rule is being read. |
RuleClause
Keeps a connector such as where, and or then on one line with the chip that follows it, so a line never ends on a lone word.
RuleCondition
One condition as a chip: the field, the operator and the value. With an editor it is a button that opens a popover.
| Prop | Type | Default | Description |
|---|---|---|---|
| field* | string | - | What is tested, such as Amount. |
| operator | string | - | How, such as is under. |
| value | string | - | Against what, such as €300. Set in tabular numerals. |
| uncertain | boolean | false | The parser guessed. The chip takes a dashed warning edge and says so to a screen reader. |
| onRemove | () => void | - | Shows the remove button. Focus moves to the next chip. |
| editor | ReactNode | - | What the chip's popover holds, usually a Select for the field and the operator and an input for the value. Without it the chip is plain text. |
| open | boolean | - | Whether the editor is open, when you control it. |
| defaultOpen | boolean | false | Opens the editor on the first render. |
| onOpenChange | (open: boolean) => void | - | The editor opened or closed. |
| editorClassName | string | - | Merged onto the popover panel, to size it. |
| editLabel | string | "Edit condition" | Starts the button's accessible name. |
| removeLabel | string | "Remove condition" | Starts the remove button's accessible name. |
| uncertainLabel | string | "not sure" | Read after an uncertain condition. |
| Data attribute | Description |
|---|---|
| data-uncertain | Present on a guessed condition. |
RuleAction
What the rule does, the then of the sentence, as a filled chip. Children are its label.
| Prop | Type | Default | Description |
|---|---|---|---|
| tone | "neutral" | "success" | "warning" | "destructive" | "neutral" | Ink for the default, or a status color for an outcome you want to stand out. |
| editor | ReactNode | - | What the chip's popover holds, usually a Select for the field and the operator and an input for the value. Without it the chip is plain text. |
| open | boolean | - | Whether the editor is open, when you control it. |
| defaultOpen | boolean | false | Opens the editor on the first render. |
| onOpenChange | (open: boolean) => void | - | The editor opened or closed. |
| editorClassName | string | - | Merged onto the popover panel, to size it. |
| editLabel | string | "Edit action" | Starts the button's accessible name. |
| Data attribute | Description |
|---|---|
| data-tone | The tone. |
RuleAddCondition
A dashed chip that adds a condition. Handle onClick, or pass an editor to open a popover from it.
| Prop | Type | Default | Description |
|---|---|---|---|
| editor | ReactNode | - | What the chip's popover holds, usually a Select for the field and the operator and an input for the value. Without it the chip is plain text. |
| open | boolean | - | Whether the editor is open, when you control it. |
| defaultOpen | boolean | false | Opens the editor on the first render. |
| onOpenChange | (open: boolean) => void | - | The editor opened or closed. |
| editorClassName | string | - | Merged onto the popover panel, to size it. |
| children | ReactNode | "Condition" | The label. Without it the button is named Add condition. |
RuleMissing
A quiet warning that lists what the parser could not read. It renders nothing when the list is empty.
| Prop | Type | Default | Description |
|---|---|---|---|
| items | readonly ReactNode[] | [] | The pieces that were not understood. |
| label | ReactNode | "Not understood yet:" | Leads the list. |
| children | ReactNode | - | A hint after the list. |
RulePreview
The backtest under the sentence. Holds RulePreviewHeader, RuleStats and anything else, such as a Chart. Dims while the rule is read.
| Prop | Type | Default | Description |
|---|---|---|---|
| aria-label | string | "Backtest" | Names the region. |
RuleStat
One figure of RuleStats, a description list that sits on one row when there is room and on two columns when there is not.
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | ReactNode | - | What the figure counts. |
| value* | ReactNode | - | The figure, in tabular numerals. |
| detail | ReactNode | - | A small line under the figure, such as of 1,480. |
| tone | "neutral" | "success" | "warning" | "destructive" | "neutral" | Colors the figure, such as warning for false positives. |
| children | ReactNode | - | Anything under the figure, such as a Meter. |
RuleStatus
The state of the rule as a Badge with a dot. Children replace the label.
| Prop | Type | Default | Description |
|---|---|---|---|
| status* | "active" | "shadow" | "draft" | "off" | - | Sets the color and the default label. |
RuleVersion
The version as a Badge, read out in full by a screen reader.
| Prop | Type | Default | Description |
|---|---|---|---|
| version* | number | - | Shown as v3. |
| label | string | "Version" | What a screen reader says before the number. |
RuleBuilderFooter
The row of actions. It stacks on a phone with the last button on top, and sits right-aligned from sm up.