torch-glare 2.5.1 → 2.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/lib/components/DataViews/filters/filters.tsx +7 -11
- package/apps/lib/components/FormBuilder/context.ts +8 -25
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +1 -1
- package/apps/lib/components/FormBuilder/form-builder.tsx +32 -172
- package/apps/lib/components/FormBuilder/index.ts +0 -4
- package/apps/lib/components/FormBuilder/types.ts +6 -16
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +2 -2
- package/apps/lib/components/FormRenderer/detail.tsx +6 -6
- package/apps/lib/components/FormRenderer/form-renderer.tsx +155 -35
- package/apps/lib/components/{FormBuilder → FormRenderer}/header.tsx +12 -25
- package/apps/lib/components/FormRenderer/index.ts +4 -0
- package/apps/lib/components/FormRenderer/section.tsx +39 -0
- package/apps/lib/components/{FormBuilder → FormRenderer}/stepper.tsx +47 -19
- package/apps/lib/components/FormRenderer/types.ts +6 -6
- package/apps/lib/registry.json +5 -3
- package/apps/lib/tsconfig.tsbuildinfo +1 -1
- package/docs/components/form-builder.md +62 -88
- package/docs/components/form-renderer.md +61 -25
- package/docs/components/form-summary.md +18 -3
- package/docs/components/section-block.md +1 -1
- package/docs/how-to/forms-with-form-builder.md +44 -41
- package/docs/migration/changelog.md +3 -0
- package/docs/migration/form-builder-2.5.2.md +113 -0
- package/package.json +1 -1
|
@@ -1,31 +1,61 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: FormBuilder
|
|
3
|
-
description: A compound, composition-based form
|
|
3
|
+
description: The fields. A compound, composition-based form — author fields as JSX children (FormBuilder.Text, FormBuilder.Select, …) wired to any react-hook-form resolver. All chrome around the fields lives in FormRenderer.
|
|
4
4
|
component: true
|
|
5
5
|
group: Forms
|
|
6
6
|
keywords:
|
|
7
|
-
[
|
|
7
|
+
[
|
|
8
|
+
form-builder,
|
|
9
|
+
form,
|
|
10
|
+
compound,
|
|
11
|
+
composition,
|
|
12
|
+
react-hook-form,
|
|
13
|
+
resolver,
|
|
14
|
+
fields,
|
|
15
|
+
validation,
|
|
16
|
+
table,
|
|
17
|
+
field-array,
|
|
18
|
+
submit,
|
|
19
|
+
]
|
|
8
20
|
---
|
|
9
21
|
|
|
10
22
|
# FormBuilder
|
|
11
23
|
|
|
12
24
|
A **compound, composition-based** form. You author a form as JSX children — each
|
|
13
25
|
field is a `FormBuilder.*` component wired to [react-hook-form](https://react-hook-form.com/)
|
|
14
|
-
for you.
|
|
26
|
+
for you.
|
|
15
27
|
|
|
16
28
|
```tsx
|
|
17
29
|
<FormBuilder onSubmit={save} resolver={zodResolver(schema)} defaultValues={d}>
|
|
18
|
-
<FormBuilder.
|
|
19
|
-
|
|
20
|
-
<FormBuilder.Currency name="price" label="Price" currencySymbol="$" />
|
|
21
|
-
</FormBuilder.Section>
|
|
22
|
-
<FormBuilder.Submit>Save</FormBuilder.Submit>
|
|
30
|
+
<FormBuilder.Text name="name" label="Name" required />
|
|
31
|
+
<FormBuilder.Currency name="price" label="Price" currencySymbol="$" />
|
|
23
32
|
</FormBuilder>
|
|
24
33
|
```
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
## FormBuilder is the fields — nothing else
|
|
36
|
+
|
|
37
|
+
FormBuilder renders a `<form>`, its react-hook-form context, and your fields. Each field draws
|
|
38
|
+
its own row (label, required marker, hint). Beyond that it draws **no frame at all**: no titled
|
|
39
|
+
section cards, no page gutters, no scroll shell, no title header, no stepper rail, no summary
|
|
40
|
+
column. Rendered bare it simply fills its container — which is what an embedded form wants (a
|
|
41
|
+
settings rail, a `DataViews` filter panel).
|
|
42
|
+
|
|
43
|
+
Everything drawn *around* the fields lives in **[FormRenderer](./form-renderer.md)**:
|
|
44
|
+
|
|
45
|
+
| You want | Use |
|
|
46
|
+
| ------------------------------------- | ---------------------------------------------------- |
|
|
47
|
+
| A titled card grouping fields | `FormRenderer.Section` |
|
|
48
|
+
| A page title header + Save action bar | `FormRenderer`'s `header` and `actions` props |
|
|
49
|
+
| A wizard | `FormRenderer.Stepper` + `FormRenderer.Step` |
|
|
50
|
+
| A drawer | `FormRenderer` with `display="drawer"` |
|
|
51
|
+
| Live totals beside the form | `FormRenderer`'s `summary` prop + [FormSummary](./form-summary.md) |
|
|
52
|
+
| A read-only detail page | `FormRenderer.Sidebar` + `FormRenderer.Tab` |
|
|
53
|
+
|
|
54
|
+
> **For real forms, reach for [FormRenderer](./form-renderer.md), not raw FormBuilder.** You still
|
|
55
|
+
> author the fields as `FormBuilder.*` children — FormRenderer just wraps them.
|
|
56
|
+
|
|
57
|
+
`FormBuilder.Submit` is the one non-field part that stays here — see
|
|
58
|
+
[Field components](#field-components) for what it does.
|
|
29
59
|
|
|
30
60
|
## Installation
|
|
31
61
|
|
|
@@ -54,18 +84,12 @@ import { FormBuilder } from "@/components/FormBuilder";
|
|
|
54
84
|
| `loading` | `boolean` | Submit shows a spinner; inputs disable. |
|
|
55
85
|
| `fieldDirection` | `'horizontal' \| 'vertical'` | Row layout (auto-vertical inside a drawer). |
|
|
56
86
|
| `resetOnSuccess` | `boolean` | Reset to defaults after a successful submit. |
|
|
57
|
-
| `form` | `UseFormReturn` | A hoisted `useForm` to bind to — pass when
|
|
58
|
-
| `conclusion` | `ReactNode` | A live panel (e.g. `FormSummary`) rendered **outside** the `<form>` as the grid's right column. |
|
|
87
|
+
| `form` | `UseFormReturn` | A hoisted `useForm` to bind to — pass when something outside the form must read the same values. |
|
|
59
88
|
| `id` | `string` | Sets the underlying `<form id>`, so a button outside the form can submit it via `form={id}` (e.g. a drawer header's Save). |
|
|
60
|
-
| `className` | `string` | Lands on the form
|
|
89
|
+
| `className` | `string` | Lands on the `<form>` element itself — e.g. `"min-w-0 flex-1"` to fill a flex parent. |
|
|
61
90
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
`conclusion` renders as its own panel **outside** the form surface, beside it — the same structure
|
|
65
|
-
`FormDrawer` uses in the drawer tray (6px gutter). So a stepper + conclusion reads as three columns
|
|
66
|
-
(nav · fields · conclusion), a conclusion alone as two, and a plain form as one. The fields column
|
|
67
|
-
caps at 1100px and centers. The conclusion is outside the `<form>` and reads values via its own
|
|
68
|
-
`form` prop.
|
|
91
|
+
There is no `layout` prop and no `conclusion` prop: the fields always fill their container, and a
|
|
92
|
+
panel beside the form is `FormRenderer`'s `summary`.
|
|
69
93
|
|
|
70
94
|
## Field components
|
|
71
95
|
|
|
@@ -109,7 +133,7 @@ are named `${rowName}.field`.
|
|
|
109
133
|
`FormBuilder.Table` is the **table-shaped** counterpart of `FieldArray` (value `object[]`): an editable
|
|
110
134
|
grid where each row is a record and each column cell is any `FormBuilder.*` field. It renders its own
|
|
111
135
|
`SectionBlock` (`variant="Table"` — the full-bleed table shell), so place it as a **top-level child** of
|
|
112
|
-
the form, **not** inside a `
|
|
136
|
+
the form, **not** inside a `FormRenderer.Section` — nesting produces a card inside a card. When you need a
|
|
113
137
|
table that isn't a form field, compose the shell yourself with `SectionBlock variant="Table"` (see
|
|
114
138
|
[SectionBlock → Table Variant](./section-block.md#table-variant)).
|
|
115
139
|
|
|
@@ -168,77 +192,27 @@ clickable. Multi-select is also available as `.MultiSelect` / `.Tags` (a tag-chi
|
|
|
168
192
|
renders like any other field — the `label` sits in the normal label column — and the box holds
|
|
169
193
|
an optional inline `subLabel`, a vertical divider, and the switch.
|
|
170
194
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
full-bleed table shell (what `FormBuilder.Table` uses internally). `FormBuilder.Submit` is a loading-aware submit button. It
|
|
174
|
-
**auto-associates with the enclosing form** (via context), so it submits even when placed in a
|
|
175
|
-
header / action bar that renders _outside_ the `<form>` — no manual `form={id}` wiring.
|
|
176
|
-
|
|
177
|
-
## Title header + action bar
|
|
178
|
-
|
|
179
|
-
`FormBuilder.Header` renders a **title pill** (Glare `HeaderBar`) on the left and an
|
|
180
|
-
**action bar** (your buttons) on the right, **absolutely positioned** over a
|
|
181
|
-
scrollable form body — the products-services item-edit look. Place it as a direct
|
|
182
|
-
child of `<FormBuilder>`; the root then switches to the scroll-shell layout.
|
|
183
|
-
|
|
184
|
-
```tsx
|
|
185
|
-
<FormBuilder onSubmit={save} resolver={r} defaultValues={d}>
|
|
186
|
-
<FormBuilder.Header title="Item" variant="new">
|
|
187
|
-
<FormBuilder.Submit>Save</FormBuilder.Submit>
|
|
188
|
-
{/* add more actions, e.g. a "Save as draft" Button */}
|
|
189
|
-
</FormBuilder.Header>
|
|
190
|
-
|
|
191
|
-
<FormBuilder.Section title="Identity" color="Blue">
|
|
192
|
-
<FormBuilder.Text name="name" label="Name" required />
|
|
193
|
-
</FormBuilder.Section>
|
|
194
|
-
</FormBuilder>
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
- `title` — plain text (uppercased). `label` — badge text (defaults from `variant`).
|
|
198
|
-
- `variant` — `"new" | "edit" | "detail"` (badge color; default `"new"`). `children` are
|
|
199
|
-
the action buttons; `FormBuilder.Submit` submits the form (it auto-associates with it, even
|
|
200
|
-
though the header sits outside `<form>`).
|
|
195
|
+
To group fields into a titled card, use [`FormRenderer.Section`](./form-renderer.md) — it is
|
|
196
|
+
presentation, so it lives there.
|
|
201
197
|
|
|
202
|
-
|
|
198
|
+
`FormBuilder.Submit` is a loading-aware submit button. It **auto-associates with the enclosing
|
|
199
|
+
form** (via context), so it submits even when placed in a header / action bar that renders
|
|
200
|
+
_outside_ the `<form>` — no manual `form={id}` wiring.
|
|
203
201
|
|
|
204
|
-
|
|
205
|
-
**Every step's fields are always mounted and registered** — the whole form is live
|
|
206
|
-
regardless of which step is showing; the stepper only toggles _visibility_.
|
|
207
|
-
**Navigation is the step buttons themselves**: click a step to go there. Backward is
|
|
208
|
-
free; clicking forward validates the steps in between and stops at the first one with
|
|
209
|
-
errors (shown with a red indicator). A step that **passes validation stays checked** even
|
|
210
|
-
after you navigate back to it. Put the **Submit** in the `FormBuilder.Header` (or the
|
|
211
|
-
`FormRenderer` `actions`) — it submits every step's fields at once, from any step.
|
|
202
|
+
## Moved to FormRenderer
|
|
212
203
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
`FormBuilder.Header`, or use `FormRenderer` — it prepends them before your Submit for a stepper
|
|
216
|
-
automatically.
|
|
217
|
-
|
|
218
|
-
```tsx
|
|
219
|
-
<FormBuilder onSubmit={save} resolver={r} defaultValues={d}>
|
|
220
|
-
<FormBuilder.Header title="Item" variant="new">
|
|
221
|
-
<FormBuilder.Submit>Save</FormBuilder.Submit>
|
|
222
|
-
</FormBuilder.Header>
|
|
223
|
-
|
|
224
|
-
<FormBuilder.Stepper>
|
|
225
|
-
<FormBuilder.Step title="Basics">
|
|
226
|
-
<FormBuilder.Text name="name" label="Name" required />
|
|
227
|
-
</FormBuilder.Step>
|
|
228
|
-
<FormBuilder.Step title="Details">
|
|
229
|
-
<FormBuilder.Select name="category" label="Category" required options={cats} />
|
|
230
|
-
</FormBuilder.Step>
|
|
231
|
-
</FormBuilder.Stepper>
|
|
232
|
-
</FormBuilder>
|
|
233
|
-
```
|
|
204
|
+
These all used to live here. They are chrome, so they now live on
|
|
205
|
+
[FormRenderer](./form-renderer.md):
|
|
234
206
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
207
|
+
| Was | Now |
|
|
208
|
+
| --- | --- |
|
|
209
|
+
| `FormBuilder.Section` | `FormRenderer.Section` |
|
|
210
|
+
| `FormBuilder.Stepper` / `.Step` / `.Back` / `.Next` | `FormRenderer.Stepper` / `.Step` / `.Back` / `.Next` |
|
|
211
|
+
| `FormBuilder.Header` | `FormRenderer`'s `header` prop |
|
|
212
|
+
| the `layout` prop | gone — the fields always fill their container |
|
|
213
|
+
| the `conclusion` prop | `FormRenderer`'s `summary` prop |
|
|
239
214
|
|
|
240
|
-
|
|
241
|
-
> (`FormRenderer.Sidebar` / `.Tab`) — see the FormRenderer docs. `FormBuilder` itself stays form-only.
|
|
215
|
+
See [the migration note](../migration/form-builder-2.5.2.md) for the full upgrade path.
|
|
242
216
|
|
|
243
217
|
## Calculation panel
|
|
244
218
|
|
|
@@ -1,33 +1,39 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: FormRenderer
|
|
3
|
-
description:
|
|
3
|
+
description: The chrome around a FormBuilder. Author fields as JSX children; FormRenderer owns page-vs-drawer display, the title header, the actions bar, titled Sections, the stepper, and the summary panel. Prefer it over raw FormBuilder for real forms.
|
|
4
4
|
component: true
|
|
5
5
|
group: Forms
|
|
6
|
-
keywords:
|
|
6
|
+
keywords:
|
|
7
|
+
[form-renderer, form, drawer, header, display, actions, section, stepper, summary, react-hook-form]
|
|
7
8
|
---
|
|
8
9
|
|
|
9
10
|
# FormRenderer
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
The **chrome** around a [FormBuilder](./form-builder.md). You author the fields as **JSX
|
|
13
|
+
children** (`FormBuilder.Text`, `FormBuilder.Select`, …); `FormRenderer` draws everything
|
|
14
|
+
around them:
|
|
14
15
|
|
|
15
16
|
- **page vs drawer** display (`display="drawer"` hosts the form in a `FormDrawer`),
|
|
16
|
-
- the **absolute title header** +
|
|
17
|
-
-
|
|
17
|
+
- the **absolute title header** + `actions` bar,
|
|
18
|
+
- **`FormRenderer.Section`** — the titled cards that group fields,
|
|
19
|
+
- the **stepper** (`FormRenderer.Stepper` / `.Step`) and its nav rail,
|
|
20
|
+
- the **`summary`** panel beside the form,
|
|
21
|
+
- the page gutters and scroll shell, and **vertical field layout** inside a drawer.
|
|
22
|
+
|
|
23
|
+
`FormBuilder` on its own is the fields and nothing else — no card, no header, no frame — so it
|
|
24
|
+
fills whatever it is placed in. **For real forms, use `FormRenderer`**; reach for bare
|
|
25
|
+
`FormBuilder` only when you are embedding fields inside something that already provides its own
|
|
26
|
+
chrome (a settings rail, a filter panel).
|
|
18
27
|
|
|
19
28
|
FormRenderer has **two modes**: a **form** (author fields as children, as below), or a display-only
|
|
20
29
|
**detail page** — give it `FormRenderer.Sidebar` + `FormRenderer.Tab` children instead of fields and the
|
|
21
|
-
sidebar swaps `
|
|
30
|
+
sidebar swaps `FormRenderer.Tab` panels, whose content is `FormRenderer.Section` blocks (no `<form>`,
|
|
31
|
+
no submit — see [Detail tabs](#detail-tabs-sidebar)).
|
|
22
32
|
|
|
23
33
|
FormRenderer never manufactures a Submit — **you compose the Save and hand it to `actions`**.
|
|
24
34
|
It renders in the form's header action pill (page) or the drawer header (drawer), and a bare
|
|
25
35
|
`<FormBuilder.Submit>` auto-targets this form (even though the header sits outside the `<form>`).
|
|
26
36
|
|
|
27
|
-
Reach for `FormBuilder` directly when you just want the form. Reach for
|
|
28
|
-
`FormRenderer` when you want the same form to render as a page _or_ a drawer with
|
|
29
|
-
the standard chrome.
|
|
30
|
-
|
|
31
37
|
```tsx
|
|
32
38
|
<FormRenderer
|
|
33
39
|
onSubmit={save}
|
|
@@ -36,13 +42,39 @@ the standard chrome.
|
|
|
36
42
|
header={{ title: "Item", variant: "new" }}
|
|
37
43
|
actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}
|
|
38
44
|
>
|
|
39
|
-
<
|
|
45
|
+
<FormRenderer.Section title="Identity" color="Blue">
|
|
40
46
|
<FormBuilder.Text name="name" label="Name" required />
|
|
41
47
|
<FormBuilder.Currency name="price" label="Price" currencySymbol="$" />
|
|
42
|
-
</
|
|
48
|
+
</FormRenderer.Section>
|
|
43
49
|
</FormRenderer>
|
|
44
50
|
```
|
|
45
51
|
|
|
52
|
+
## Compound parts
|
|
53
|
+
|
|
54
|
+
| Part | What it is |
|
|
55
|
+
| ------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
56
|
+
| `FormRenderer.Section` | A titled `SectionBlock` card grouping fields (or display rows). |
|
|
57
|
+
| `FormRenderer.Stepper` / `.Step` | The wizard — see [Stepper](#stepper). |
|
|
58
|
+
| `FormRenderer.Back` / `.Next` | Chevron step controls. The header's action bar prepends them for you. |
|
|
59
|
+
| `FormRenderer.Sidebar` / `.Tab` | The display-only [detail-tabs](#detail-tabs-sidebar) view. |
|
|
60
|
+
| `FormRenderer.Grid` / `.Row` | Read-only display cells inside a detail tab. |
|
|
61
|
+
|
|
62
|
+
### `FormRenderer.Section`
|
|
63
|
+
|
|
64
|
+
Props `title`, `color`, `icon`, `action`, `variant`. It groups fields in a Glare `SectionBlock`:
|
|
65
|
+
`color` is one of `Blue`, `Yellow`, `Green`, `Red`, `Orange`, `Purple`, `Pink`, `Gray`; `action`
|
|
66
|
+
puts buttons on the title row; `variant="Table"` switches to the full-bleed table shell (what
|
|
67
|
+
`FormBuilder.Table` uses internally).
|
|
68
|
+
|
|
69
|
+
It holds no form state, which is why it lives here and not on `FormBuilder` — the same component
|
|
70
|
+
groups editable fields on a form and read-only `FormRenderer.Row` cells on a detail page.
|
|
71
|
+
|
|
72
|
+
Because it reads no context, it also works **outside** `<FormRenderer>` — e.g. wrapping fields in a
|
|
73
|
+
bare `<FormBuilder>` that you are laying out yourself. It is the one compound part that does.
|
|
74
|
+
|
|
75
|
+
> Not to be confused with `FieldSection`, the per-field row layout (label · control) that every
|
|
76
|
+
> field draws for itself. That one stays inside FormBuilder.
|
|
77
|
+
|
|
46
78
|
## Installation
|
|
47
79
|
|
|
48
80
|
```bash
|
|
@@ -63,16 +95,17 @@ import { FormBuilder } from "@/components/FormBuilder";
|
|
|
63
95
|
|
|
64
96
|
| Prop | Type | Notes |
|
|
65
97
|
| -------------------------------------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
66
|
-
| `children` | `ReactNode` | The form body — `
|
|
98
|
+
| `children` | `ReactNode` | The form body — `FormRenderer.Section` / field / `FormRenderer.Stepper` JSX. |
|
|
67
99
|
| `onSubmit?` / `onInvalid?` | fns | Submit / validation-fail callbacks. Optional — a detail-tabs view has no form, so omit them there. |
|
|
68
100
|
| `resolver` | `Resolver` | Any react-hook-form resolver, e.g. `zodResolver(schema)`. |
|
|
69
101
|
| `defaultValues` / `values` | `DefaultValues` / `T` | Initial values; `values` re-syncs on change (edit). |
|
|
70
102
|
| `loading` / `resetOnSuccess` | `boolean` | Forwarded to `FormBuilder`. |
|
|
71
103
|
| `fieldDirection` | `'horizontal' \| 'vertical'` | Defaults to vertical inside a drawer. |
|
|
72
104
|
| `form` | `UseFormReturn<T>` | A hoisted `useForm` to bind to — pass when a sibling (e.g. a `summary` `FormSummary`) must read the same live values; the caller owns `resolver`/`defaultValues` on it. Omit to let FormRenderer create its own. |
|
|
105
|
+
| `className` | `string` | Lands on FormRenderer's outermost element (page display) — e.g. `"min-h-0 flex-1"` to fill a flex column. Note this is the page frame, not the `<form>`; `FormBuilder`'s own `className` lands on the `<form>` element itself. |
|
|
73
106
|
| `display` | `'page' \| 'drawer'` | `'drawer'` wraps the form in `FormDrawer`. |
|
|
74
107
|
| `header` | `{ title; label?; variant? }` | Absolute title header + action bar (page display); `actions` render in it. |
|
|
75
|
-
| `summary` | `ReactNode` | A live panel (typically `FormSummary`) rendered beside the form (page) or in the drawer tray (drawer). Give the same hoisted `form` so it reads live values. On a page, a `summary` **plus** a `
|
|
108
|
+
| `summary` | `ReactNode` | A live panel (typically `FormSummary`) rendered beside the form (page) or in the drawer tray (drawer). Give the same hoisted `form` so it reads live values. On a page, a `summary` **plus** a `FormRenderer.Stepper` lays out as three columns — stepper nav · fields · summary. |
|
|
76
109
|
| `actions` | `ReactNode` | The form's action bar — rendered in the header action pill (page) or drawer header (drawer). Put the Save here: `actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}`. A bare `FormBuilder.Submit` auto-targets this form. |
|
|
77
110
|
| `id` | `string` | `id` on the underlying `<form>`. Optional — FormRenderer generates and wires one otherwise. |
|
|
78
111
|
| `open` / `onOpenChange` / `title` / `badge` / `onOpenInNewTab` | — | Drawer control (when `display="drawer"`). `title` / `badge` are strings that override `header.title` / `header.label`. |
|
|
@@ -98,7 +131,7 @@ import { FormBuilder } from "@/components/FormBuilder";
|
|
|
98
131
|
|
|
99
132
|
## Stepper
|
|
100
133
|
|
|
101
|
-
Drop a `
|
|
134
|
+
Drop a `FormRenderer.Stepper` in as the child. The Save lives in the header `actions` and
|
|
102
135
|
submits every step's registered fields at once (steps stay mounted, so the whole form is live).
|
|
103
136
|
|
|
104
137
|
For a stepper, FormRenderer **automatically prepends chevron Back/Next nav + a divider** before
|
|
@@ -115,11 +148,11 @@ error overrides it to red. You still pass just the Submit; the nav is wired for
|
|
|
115
148
|
header={{ title: "New item", variant: "new" }}
|
|
116
149
|
actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}
|
|
117
150
|
>
|
|
118
|
-
<
|
|
119
|
-
<
|
|
151
|
+
<FormRenderer.Stepper>
|
|
152
|
+
<FormRenderer.Step title="Basics">
|
|
120
153
|
<FormBuilder.Text name="name" label="Name" required />
|
|
121
|
-
</
|
|
122
|
-
</
|
|
154
|
+
</FormRenderer.Step>
|
|
155
|
+
</FormRenderer.Stepper>
|
|
123
156
|
</FormRenderer>
|
|
124
157
|
```
|
|
125
158
|
|
|
@@ -131,7 +164,7 @@ tab panel — no `<form>`, no submit. The sidebar sits **where a stepper's rail
|
|
|
131
164
|
active panel shows (built on the same Radix Tabs primitive shadcn uses, so it's keyboard-accessible).
|
|
132
165
|
Pair it with `header` (`variant="detail"` → a "View" badge) + `actions` (Print / Approve / …).
|
|
133
166
|
|
|
134
|
-
Each `Tab` holds read-only `
|
|
167
|
+
Each `Tab` holds read-only `FormRenderer.Section` blocks; `FormRenderer.Grid` + `FormRenderer.Row` lay
|
|
135
168
|
out the label/value display cells (the display counterpart of form fields).
|
|
136
169
|
|
|
137
170
|
```tsx
|
|
@@ -151,12 +184,12 @@ out the label/value display cells (the display counterpart of form fields).
|
|
|
151
184
|
|
|
152
185
|
{/* One panel per tab — read-only Section blocks. */}
|
|
153
186
|
<FormRenderer.Tab value="overview">
|
|
154
|
-
<
|
|
187
|
+
<FormRenderer.Section title="Main Information" color="Blue">
|
|
155
188
|
<FormRenderer.Grid>
|
|
156
189
|
<FormRenderer.Row label="PO Number" value="PO-000123" />
|
|
157
190
|
<FormRenderer.Row label="Status" value={<Badge label="Submitted" color="yellow" />} />
|
|
158
191
|
</FormRenderer.Grid>
|
|
159
|
-
</
|
|
192
|
+
</FormRenderer.Section>
|
|
160
193
|
</FormRenderer.Tab>
|
|
161
194
|
|
|
162
195
|
<FormRenderer.Tab value="items">…</FormRenderer.Tab>
|
|
@@ -284,7 +317,10 @@ const form = useForm({ resolver, defaultValues })
|
|
|
284
317
|
</FormDrawer>
|
|
285
318
|
```
|
|
286
319
|
|
|
287
|
-
> `childrenOutside` is the deprecated former name for `summary`. It still
|
|
320
|
+
> `childrenOutside` is the deprecated former name for `FormDrawer`'s `summary`. It still
|
|
321
|
+
> works — it long predates the FormBuilder/FormRenderer split and is unrelated to it (that
|
|
322
|
+
> move ships no aliases; see the
|
|
323
|
+
> [migration note](../migration/form-builder-2.5.2.md)).
|
|
288
324
|
|
|
289
325
|
`FormRenderer` forwards `summary` straight into the drawer tray, so you rarely need
|
|
290
326
|
`FormDrawer` directly — reach for it only when you want the drawer without a form.
|
|
@@ -16,14 +16,19 @@ Each `FormSummary.Row` declares a **`compute(values)`** that runs against the **
|
|
|
16
16
|
form values, so every total recalculates as the user types.
|
|
17
17
|
|
|
18
18
|
The panel renders **outside** the form, beside it. Both read the same form, so
|
|
19
|
-
**hoist `useForm`** and hand the instance to each
|
|
19
|
+
**hoist `useForm`** and hand the instance to each.
|
|
20
|
+
|
|
21
|
+
> **Normally you do not lay this out by hand.** Pass the panel to
|
|
22
|
+
> [`FormRenderer`](./form-renderer.md)'s `summary` prop and it places it for you — beside the form
|
|
23
|
+
> on a page, in the tray in a drawer. The manual version below is what `summary` does, and is what
|
|
24
|
+
> you want only when you are composing a bare `<FormBuilder>` yourself.
|
|
20
25
|
|
|
21
26
|
```tsx
|
|
22
27
|
const form = useForm({ resolver: zodResolver(schema), defaultValues })
|
|
23
28
|
|
|
24
29
|
<div className="flex gap-4">
|
|
25
30
|
<FormBuilder form={form} onSubmit={save} className="min-w-0 flex-1">
|
|
26
|
-
<
|
|
31
|
+
<FormRenderer.Section title="Line items">…fields…</FormRenderer.Section>
|
|
27
32
|
</FormBuilder>
|
|
28
33
|
|
|
29
34
|
<FormSummary form={form} title="Invoice" subtitle="Summary">
|
|
@@ -71,6 +76,10 @@ npx torch-glare@latest add FormSummary
|
|
|
71
76
|
## Imports
|
|
72
77
|
|
|
73
78
|
```tsx
|
|
79
|
+
import { useForm } from 'react-hook-form'
|
|
80
|
+
|
|
81
|
+
import { FormBuilder } from '@/components/FormBuilder'
|
|
82
|
+
import { FormRenderer } from '@/components/FormRenderer'
|
|
74
83
|
import { FormSummary } from '@/components/FormSummary'
|
|
75
84
|
```
|
|
76
85
|
|
|
@@ -80,7 +89,7 @@ import { FormSummary } from '@/components/FormSummary'
|
|
|
80
89
|
|---|---|---|
|
|
81
90
|
| `title` | `ReactNode` | Panel title, e.g. `"Invoice"`. |
|
|
82
91
|
| `subtitle` | `ReactNode` | Muted text beside the title, e.g. `"Summary"`. |
|
|
83
|
-
| `form` | `UseFormReturn` | The form to read values from
|
|
92
|
+
| `form` | `UseFormReturn` | The form to read values from. The panel always renders outside the `<form>`, so pass the same hoisted instance you gave `FormRenderer` / `FormBuilder`. |
|
|
84
93
|
| `width` | `number \| string` | Panel width; default `228`. |
|
|
85
94
|
| `children` | `ReactNode` | `FormSummary.Group` children. |
|
|
86
95
|
|
|
@@ -121,3 +130,9 @@ const overallTotal = v => taxable(v) + totalTax(v)
|
|
|
121
130
|
```
|
|
122
131
|
|
|
123
132
|
The panel is **read-only** — it contributes nothing to the submitted values.
|
|
133
|
+
|
|
134
|
+
## Related
|
|
135
|
+
|
|
136
|
+
- [FormRenderer](./form-renderer.md) — its `summary` prop places this panel for you
|
|
137
|
+
- [FormBuilder](./form-builder.md) — the fields the totals read from
|
|
138
|
+
- [ConclusionHeader](./conclusion-header.md) — the header this panel uses
|
|
@@ -435,7 +435,7 @@ import {
|
|
|
435
435
|
```
|
|
436
436
|
|
|
437
437
|
For a table that edits form values, don't hand-compose this — use
|
|
438
|
-
[`FormBuilder.Table`](./form-builder.md#
|
|
438
|
+
[`FormBuilder.Table`](./form-builder.md#field-components), which renders exactly this shell
|
|
439
439
|
and wires rows to `react-hook-form`.
|
|
440
440
|
|
|
441
441
|
### Custom Layout (override defaults)
|