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.
@@ -1,31 +1,61 @@
1
1
  ---
2
2
  title: FormBuilder
3
- description: A compound, composition-based form. Author forms as JSX children (FormBuilder.Text, FormBuilder.Select, …) with steps-as-components, a drawer wrapper, and any react-hook-form resolver.
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
- [form-builder, form, compound, composition, react-hook-form, resolver, stepper, drawer, fields]
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. Steps are components and the drawer is a wrapper.
26
+ for you.
15
27
 
16
28
  ```tsx
17
29
  <FormBuilder onSubmit={save} resolver={zodResolver(schema)} defaultValues={d}>
18
- <FormBuilder.Section title="Identity" color="Blue">
19
- <FormBuilder.Text name="name" label="Name" required />
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
- > Need the same form to render as a page _or_ a drawer, with a title header and an
27
- > `actions` slot for the Save? Wrap these same children in
28
- > [FormRenderer](./form-renderer.md).
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 a `conclusion` panel must read the same values. |
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's outermost element — e.g. `"min-w-0 flex-1"` to fill the space beside a `conclusion` panel. |
89
+ | `className` | `string` | Lands on the `<form>` element itself — e.g. `"min-w-0 flex-1"` to fill a flex parent. |
61
90
 
62
- **Adaptive layout.** FormBuilder lays out responsively: a `FormBuilder.Stepper` puts its nav rail in
63
- a **left** column beside the fields (both inside the form surface, under the title header), and a
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 `FormBuilder.Section` — nesting produces a card inside a card. When you need 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
- `FormBuilder.Section` (props `title`, `color`, `icon`, `action`, `variant`) groups fields in a Glare
172
- `SectionBlock`. `action` puts buttons on the title row; `variant="Table"` switches to the
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
- ## Stepper (steps are components)
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
- Wrap steps in `FormBuilder.Stepper`; each `FormBuilder.Step` holds a step's fields.
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
- `FormBuilder.Back` / `FormBuilder.Next` are **chevron step-nav buttons** (previous / next,
214
- disabled at the ends; `Next` validates the current step first). Place them in a
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
- > **In a drawer?** `FormBuilder` is drawer-unaware — it only displays inputs. To
236
- > show a form in a drawer, wrap it in `FormDrawer` from
237
- > [FormRenderer](./form-renderer.md) (pass `fieldDirection="vertical"` to the
238
- > form), or use `FormRenderer` with `display="drawer"`.
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
- > Building a **detail page** with a sidebar of tabs (not a form)? That lives on `FormRenderer`
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: A thin wrapper around FormBuilder. Author fields as JSX children; FormRenderer owns page-vs-drawer display, the absolute title header, and drawer field layout. You compose the Save and pass it via the actions prop.
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: [form-renderer, form, drawer, header, display, actions, react-hook-form]
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
- A thin wrapper around the compound [FormBuilder](./form-builder.md). You author the
12
- fields as **JSX children** exactly as you would with `FormBuilder`; `FormRenderer`
13
- takes care of the surrounding concerns:
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** + action bar (page display),
17
- - **vertical field layout** inside a drawer.
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 `FormBuilder.Section` panels (no `<form>`, no submit — see [Detail tabs](#detail-tabs-sidebar)).
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
- <FormBuilder.Section title="Identity" color="Blue">
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
- </FormBuilder.Section>
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 — `FormBuilder.Section` / field / `FormBuilder.Stepper` JSX. |
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 `FormBuilder.Stepper` lays out as three columns — stepper nav · fields · summary. |
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 `FormBuilder.Stepper` in as the child. The Save lives in the header `actions` and
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
- <FormBuilder.Stepper>
119
- <FormBuilder.Step title="Basics">
151
+ <FormRenderer.Stepper>
152
+ <FormRenderer.Step title="Basics">
120
153
  <FormBuilder.Text name="name" label="Name" required />
121
- </FormBuilder.Step>
122
- </FormBuilder.Stepper>
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 `FormBuilder.Section` blocks; `FormRenderer.Grid` + `FormRenderer.Row` lay
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
- <FormBuilder.Section title="Main Information" color="Blue">
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
- </FormBuilder.Section>
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 works.
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
- <FormBuilder.Section title="Line items">…fields…</FormBuilder.Section>
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 required when rendered outside the `<FormBuilder>`. |
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#formbuildertable), which renders exactly this shell
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)