torch-glare 2.4.2 → 2.4.4
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/FormBuilder/context.ts +2 -5
- package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +2 -7
- package/apps/lib/components/FormBuilder/fields/ColorField.tsx +1 -18
- package/apps/lib/components/FormBuilder/fields/CustomField.tsx +1 -6
- package/apps/lib/components/FormBuilder/fields/DateField.tsx +1 -3
- package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +10 -15
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +6 -32
- package/apps/lib/components/FormBuilder/fields/FileField.tsx +1 -2
- package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +2 -9
- package/apps/lib/components/FormBuilder/fields/OtpField.tsx +1 -2
- package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +24 -23
- package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +1 -7
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +3 -13
- package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +1 -19
- package/apps/lib/components/FormBuilder/fields/SliderField.tsx +1 -6
- package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +1 -2
- package/apps/lib/components/FormBuilder/fields/TableField.tsx +22 -26
- package/apps/lib/components/FormBuilder/fields/TextField.tsx +5 -14
- package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +1 -7
- package/apps/lib/components/FormBuilder/form-builder.tsx +22 -36
- package/apps/lib/components/FormBuilder/header.tsx +2 -6
- package/apps/lib/components/FormBuilder/index.ts +1 -5
- package/apps/lib/components/FormBuilder/stepper.tsx +88 -15
- package/apps/lib/components/FormBuilder/submit.tsx +1 -4
- package/apps/lib/components/FormBuilder/types.ts +2 -5
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +9 -2
- package/apps/lib/components/FormRenderer/detail.tsx +207 -0
- package/apps/lib/components/FormRenderer/form-renderer.tsx +55 -14
- package/apps/lib/components/FormRenderer/index.ts +7 -5
- package/apps/lib/components/FormRenderer/types.ts +2 -3
- package/apps/lib/components/Popover.tsx +6 -2
- package/apps/lib/components/SearchableSelect.tsx +7 -2
- package/apps/lib/layouts/FieldSection.tsx +25 -22
- package/apps/lib/registry.json +1 -1
- package/apps/lib/tsconfig.tsbuildinfo +1 -1
- package/dist/src/shared/tailwindInit.d.ts.map +1 -1
- package/dist/src/shared/tailwindInit.js +3 -0
- package/dist/src/shared/tailwindInit.js.map +1 -1
- package/docs/components/form-builder.md +22 -29
- package/docs/components/form-renderer.md +72 -11
- package/docs/components/searchable-select.md +50 -46
- package/docs/how-to/forms-with-form-builder.md +68 -11
- package/docs/reference/tailwind-plugins.md +11 -1
- package/docs/tutorials/getting-started.md +9 -0
- package/package.json +1 -1
- package/apps/lib/components/FormBuilder/DisplayField.tsx +0 -40
- package/apps/lib/components/FormBuilder/viewFormat.tsx +0 -137
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tailwindInit.d.ts","sourceRoot":"","sources":["../../../cli/src/shared/tailwindInit.ts"],"names":[],"mappings":"AAIA,wBAAgB,YAAY,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"tailwindInit.d.ts","sourceRoot":"","sources":["../../../cli/src/shared/tailwindInit.ts"],"names":[],"mappings":"AAIA,wBAAgB,YAAY,IAAI,IAAI,CAanC"}
|
|
@@ -11,6 +11,9 @@ export function tailwindInit() {
|
|
|
11
11
|
LessThanV4 ? "mapping-color-system" : "mapping-color-system-v4",
|
|
12
12
|
"glare-torch-mode",
|
|
13
13
|
];
|
|
14
|
+
if (LessThanV4) {
|
|
15
|
+
dependencies.push("@tailwindcss/container-queries");
|
|
16
|
+
}
|
|
14
17
|
installDependencies(dependencies);
|
|
15
18
|
}
|
|
16
19
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tailwindInit.js","sourceRoot":"","sources":["../../../cli/src/shared/tailwindInit.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tailwindInit.js","sourceRoot":"","sources":["../../../cli/src/shared/tailwindInit.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,29 +1,17 @@
|
|
|
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,
|
|
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.
|
|
4
4
|
component: true
|
|
5
5
|
group: Forms
|
|
6
6
|
keywords:
|
|
7
|
-
[
|
|
8
|
-
form-builder,
|
|
9
|
-
form,
|
|
10
|
-
compound,
|
|
11
|
-
composition,
|
|
12
|
-
react-hook-form,
|
|
13
|
-
resolver,
|
|
14
|
-
stepper,
|
|
15
|
-
drawer,
|
|
16
|
-
fields,
|
|
17
|
-
view-mode,
|
|
18
|
-
]
|
|
7
|
+
[form-builder, form, compound, composition, react-hook-form, resolver, stepper, drawer, fields]
|
|
19
8
|
---
|
|
20
9
|
|
|
21
10
|
# FormBuilder
|
|
22
11
|
|
|
23
12
|
A **compound, composition-based** form. You author a form as JSX children — each
|
|
24
13
|
field is a `FormBuilder.*` component wired to [react-hook-form](https://react-hook-form.com/)
|
|
25
|
-
for you. Steps are components
|
|
26
|
-
editable or read-only.
|
|
14
|
+
for you. Steps are components and the drawer is a wrapper.
|
|
27
15
|
|
|
28
16
|
```tsx
|
|
29
17
|
<FormBuilder onSubmit={save} resolver={zodResolver(schema)} defaultValues={d}>
|
|
@@ -63,7 +51,6 @@ import { FormBuilder } from "@/components/FormBuilder";
|
|
|
63
51
|
| `resolver` | `Resolver` | Any react-hook-form resolver, e.g. `zodResolver(schema)`. |
|
|
64
52
|
| `defaultValues` | `DefaultValues` | Initial values (create). |
|
|
65
53
|
| `values` | `T` | Controlled values (edit) — the form re-syncs when this changes. |
|
|
66
|
-
| `mode` | `'edit' \| 'view'` | `'view'` renders every field read-only. |
|
|
67
54
|
| `loading` | `boolean` | Submit shows a spinner; inputs disable. |
|
|
68
55
|
| `fieldDirection` | `'horizontal' \| 'vertical'` | Row layout (auto-vertical inside a drawer). |
|
|
69
56
|
| `resetOnSuccess` | `boolean` | Reset to defaults after a successful submit. |
|
|
@@ -125,7 +112,7 @@ Each column pairs a `header` with a `cell(rowName, index)` renderer — name the
|
|
|
125
112
|
`${rowName}.<key>`. The section header carries the actions: a **Add New** button (also repeated as the
|
|
126
113
|
bottom footer) and a **Delete Row** button that's disabled until rows are checkbox-selected. Rows support
|
|
127
114
|
**checkbox selection** (+ select-all), **drag-drop reordering**, and — per column, via `sortKey` — a
|
|
128
|
-
sort toggle in the header.
|
|
115
|
+
sort toggle in the header. Cells render
|
|
129
116
|
"bare" (control only) with validation errors shown as a tooltip on the control, so a row stays one line
|
|
130
117
|
tall, and each field passes `onTable` so it's borderless and blends into the grid. Practical cell fields
|
|
131
118
|
are the compact ones — `Text`, `Number`, `Currency`, `Select`, `SearchableSelect`, `Date`, `Phone`,
|
|
@@ -172,7 +159,7 @@ renders like any other field — the `label` sits in the normal label column —
|
|
|
172
159
|
an optional inline `subLabel`, a vertical divider, and the switch.
|
|
173
160
|
|
|
174
161
|
`FormBuilder.Section` (props `title`, `color`, `icon`) groups fields in a Glare
|
|
175
|
-
`SectionBlock`. `FormBuilder.Submit` is a loading-aware submit button
|
|
162
|
+
`SectionBlock`. `FormBuilder.Submit` is a loading-aware submit button. It
|
|
176
163
|
**auto-associates with the enclosing form** (via context), so it submits even when placed in a
|
|
177
164
|
header / action bar that renders _outside_ the `<form>` — no manual `form={id}` wiring.
|
|
178
165
|
|
|
@@ -197,10 +184,9 @@ child of `<FormBuilder>`; the root then switches to the scroll-shell layout.
|
|
|
197
184
|
```
|
|
198
185
|
|
|
199
186
|
- `title` — plain text (uppercased). `label` — badge text (defaults from `variant`).
|
|
200
|
-
- `variant` — `"new" | "edit" | "detail"` (badge color
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
and auto-hides in view mode.
|
|
187
|
+
- `variant` — `"new" | "edit" | "detail"` (badge color; default `"new"`). `children` are
|
|
188
|
+
the action buttons; `FormBuilder.Submit` submits the form (it auto-associates with it, even
|
|
189
|
+
though the header sits outside `<form>`).
|
|
204
190
|
|
|
205
191
|
## Stepper (steps are components)
|
|
206
192
|
|
|
@@ -209,9 +195,15 @@ Wrap steps in `FormBuilder.Stepper`; each `FormBuilder.Step` holds a step's fiel
|
|
|
209
195
|
regardless of which step is showing; the stepper only toggles _visibility_.
|
|
210
196
|
**Navigation is the step buttons themselves**: click a step to go there. Backward is
|
|
211
197
|
free; clicking forward validates the steps in between and stops at the first one with
|
|
212
|
-
errors (shown with a red indicator).
|
|
198
|
+
errors (shown with a red indicator). A step that **passes validation stays checked** even
|
|
199
|
+
after you navigate back to it. Put the **Submit** in the `FormBuilder.Header` (or the
|
|
213
200
|
`FormRenderer` `actions`) — it submits every step's fields at once, from any step.
|
|
214
201
|
|
|
202
|
+
`FormBuilder.Back` / `FormBuilder.Next` are **chevron step-nav buttons** (previous / next,
|
|
203
|
+
disabled at the ends; `Next` validates the current step first). Place them in a
|
|
204
|
+
`FormBuilder.Header`, or use `FormRenderer` — it prepends them before your Submit for a stepper
|
|
205
|
+
automatically.
|
|
206
|
+
|
|
215
207
|
```tsx
|
|
216
208
|
<FormBuilder onSubmit={save} resolver={r} defaultValues={d}>
|
|
217
209
|
<FormBuilder.Header title="Item" variant="new">
|
|
@@ -234,6 +226,9 @@ errors (shown with a red indicator). Put the **Submit** in the `FormBuilder.Head
|
|
|
234
226
|
> [FormRenderer](./form-renderer.md) (pass `fieldDirection="vertical"` to the
|
|
235
227
|
> form), or use `FormRenderer` with `display="drawer"`.
|
|
236
228
|
|
|
229
|
+
> Building a **detail page** with a sidebar of tabs (not a form)? That lives on `FormRenderer`
|
|
230
|
+
> (`FormRenderer.Sidebar` / `.Tab`) — see the FormRenderer docs. `FormBuilder` itself stays form-only.
|
|
231
|
+
|
|
237
232
|
## Calculation panel
|
|
238
233
|
|
|
239
234
|
To show computed totals **beside** the form, render a [FormSummary](./form-summary.md)
|
|
@@ -251,15 +246,13 @@ const form = useForm({ resolver: r, defaultValues: d })
|
|
|
251
246
|
`form` makes FormBuilder use an existing react-hook-form instance instead of creating its
|
|
252
247
|
own — that's what lets something outside the form read the same live values.
|
|
253
248
|
|
|
254
|
-
##
|
|
249
|
+
## Editing a record
|
|
255
250
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
- **View**: `mode="view"` renders the same children read-only (label + formatted value)
|
|
259
|
-
with no submit — one markup drives both edit and detail views.
|
|
251
|
+
Pass `values` (not just `defaultValues`); the form repopulates when the data loads. Use a
|
|
252
|
+
remount `key` so initial-only inputs (date, rich text) re-seed.
|
|
260
253
|
|
|
261
254
|
```tsx
|
|
262
|
-
<FormBuilder
|
|
255
|
+
<FormBuilder key={entity.id} values={entity} onSubmit={save} resolver={r} defaultValues={d}>
|
|
263
256
|
…the same fields…
|
|
264
257
|
</FormBuilder>
|
|
265
258
|
```
|
|
@@ -3,7 +3,7 @@ title: FormRenderer
|
|
|
3
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.
|
|
4
4
|
component: true
|
|
5
5
|
group: Forms
|
|
6
|
-
keywords: [form-renderer, form, drawer, header, display,
|
|
6
|
+
keywords: [form-renderer, form, drawer, header, display, actions, react-hook-form]
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# FormRenderer
|
|
@@ -16,6 +16,10 @@ takes care of the surrounding concerns:
|
|
|
16
16
|
- the **absolute title header** + action bar (page display),
|
|
17
17
|
- **vertical field layout** inside a drawer.
|
|
18
18
|
|
|
19
|
+
FormRenderer has **two modes**: a **form** (author fields as children, as below), or a display-only
|
|
20
|
+
**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)).
|
|
22
|
+
|
|
19
23
|
FormRenderer never manufactures a Submit — **you compose the Save and hand it to `actions`**.
|
|
20
24
|
It renders in the form's header action pill (page) or the drawer header (drawer), and a bare
|
|
21
25
|
`<FormBuilder.Submit>` auto-targets this form (even though the header sits outside the `<form>`).
|
|
@@ -60,10 +64,9 @@ import { FormBuilder } from "@/components/FormBuilder";
|
|
|
60
64
|
| Prop | Type | Notes |
|
|
61
65
|
| -------------------------------------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
62
66
|
| `children` | `ReactNode` | The form body — `FormBuilder.Section` / field / `FormBuilder.Stepper` JSX. |
|
|
63
|
-
| `onSubmit
|
|
67
|
+
| `onSubmit?` / `onInvalid?` | fns | Submit / validation-fail callbacks. Optional — a detail-tabs view has no form, so omit them there. |
|
|
64
68
|
| `resolver` | `Resolver` | Any react-hook-form resolver, e.g. `zodResolver(schema)`. |
|
|
65
69
|
| `defaultValues` / `values` | `DefaultValues` / `T` | Initial values; `values` re-syncs on change (edit). |
|
|
66
|
-
| `mode` | `'edit' \| 'view'` | `'view'` renders read-only, no Submit. |
|
|
67
70
|
| `loading` / `resetOnSuccess` | `boolean` | Forwarded to `FormBuilder`. |
|
|
68
71
|
| `fieldDirection` | `'horizontal' \| 'vertical'` | Defaults to vertical inside a drawer. |
|
|
69
72
|
| `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. |
|
|
@@ -74,22 +77,23 @@ import { FormBuilder } from "@/components/FormBuilder";
|
|
|
74
77
|
| `id` | `string` | `id` on the underlying `<form>`. Optional — FormRenderer generates and wires one otherwise. |
|
|
75
78
|
| `open` / `onOpenChange` / `title` / `badge` / `onOpenInNewTab` | — | Drawer control (when `display="drawer"`). `title` / `badge` are strings that override `header.title` / `header.label`. |
|
|
76
79
|
|
|
77
|
-
## Drawer
|
|
80
|
+
## Drawer
|
|
78
81
|
|
|
79
82
|
```tsx
|
|
80
83
|
// drawer — pass the Save via `actions`; it renders in the drawer header
|
|
81
84
|
<FormRenderer
|
|
82
|
-
display="drawer"
|
|
83
|
-
|
|
85
|
+
display="drawer"
|
|
86
|
+
open={open}
|
|
87
|
+
onOpenChange={setOpen}
|
|
88
|
+
title="New item"
|
|
89
|
+
badge="New"
|
|
90
|
+
onSubmit={save}
|
|
91
|
+
resolver={r}
|
|
92
|
+
defaultValues={d}
|
|
84
93
|
actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}
|
|
85
94
|
>
|
|
86
95
|
{fields}
|
|
87
96
|
</FormRenderer>
|
|
88
|
-
|
|
89
|
-
// read-only view — same children, omit `actions` (a view has no Save)
|
|
90
|
-
<FormRenderer mode="view" values={entity} onSubmit={() => {}}>
|
|
91
|
-
{fields}
|
|
92
|
-
</FormRenderer>
|
|
93
97
|
```
|
|
94
98
|
|
|
95
99
|
## Stepper
|
|
@@ -97,6 +101,12 @@ import { FormBuilder } from "@/components/FormBuilder";
|
|
|
97
101
|
Drop a `FormBuilder.Stepper` in as the child. The Save lives in the header `actions` and
|
|
98
102
|
submits every step's registered fields at once (steps stay mounted, so the whole form is live).
|
|
99
103
|
|
|
104
|
+
For a stepper, FormRenderer **automatically prepends chevron Back/Next nav + a divider** before
|
|
105
|
+
your Submit, so the action bar reads `[◀] [▶] │ Save`. Back is disabled on the first step; Next
|
|
106
|
+
validates the current step, then advances (disabled on the last step). A step that passes
|
|
107
|
+
validation **stays checked** in the rail — even after you navigate back — while a live validation
|
|
108
|
+
error overrides it to red. You still pass just the Submit; the nav is wired for you:
|
|
109
|
+
|
|
100
110
|
```tsx
|
|
101
111
|
<FormRenderer
|
|
102
112
|
onSubmit={save}
|
|
@@ -113,6 +123,57 @@ submits every step's registered fields at once (steps stay mounted, so the whole
|
|
|
113
123
|
</FormRenderer>
|
|
114
124
|
```
|
|
115
125
|
|
|
126
|
+
## Detail tabs (sidebar)
|
|
127
|
+
|
|
128
|
+
Give FormRenderer `FormRenderer.Sidebar` + `FormRenderer.Tab` children (instead of fields) and it
|
|
129
|
+
switches to a **display-only detail page**: a left **sidebar** where each item swaps in its matching
|
|
130
|
+
tab panel — no `<form>`, no submit. The sidebar sits **where a stepper's rail would**, and only the
|
|
131
|
+
active panel shows (built on the same Radix Tabs primitive shadcn uses, so it's keyboard-accessible).
|
|
132
|
+
Pair it with `header` (`variant="detail"` → a "View" badge) + `actions` (Print / Approve / …).
|
|
133
|
+
|
|
134
|
+
Each `Tab` holds read-only `FormBuilder.Section` blocks; `FormRenderer.Grid` + `FormRenderer.Row` lay
|
|
135
|
+
out the label/value display cells (the display counterpart of form fields).
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
<FormRenderer
|
|
139
|
+
header={{ title: "Order DE-344", variant: "detail" }}
|
|
140
|
+
actions={<Button variant="BorderStyle">Print</Button>}
|
|
141
|
+
>
|
|
142
|
+
{/* The rail — one Item per tab, tied to a Tab by `value`. */}
|
|
143
|
+
<FormRenderer.Sidebar>
|
|
144
|
+
<FormRenderer.Sidebar.Item value="overview" icon={<i className="ri-layout-grid-line" />}>
|
|
145
|
+
Overview
|
|
146
|
+
</FormRenderer.Sidebar.Item>
|
|
147
|
+
<FormRenderer.Sidebar.Item value="items" icon={<i className="ri-table-line" />}>
|
|
148
|
+
Items Table
|
|
149
|
+
</FormRenderer.Sidebar.Item>
|
|
150
|
+
</FormRenderer.Sidebar>
|
|
151
|
+
|
|
152
|
+
{/* One panel per tab — read-only Section blocks. */}
|
|
153
|
+
<FormRenderer.Tab value="overview">
|
|
154
|
+
<FormBuilder.Section title="Main Information" color="Blue">
|
|
155
|
+
<FormRenderer.Grid>
|
|
156
|
+
<FormRenderer.Row label="PO Number" value="PO-000123" />
|
|
157
|
+
<FormRenderer.Row label="Status" value={<Badge label="Submitted" color="yellow" />} />
|
|
158
|
+
</FormRenderer.Grid>
|
|
159
|
+
</FormBuilder.Section>
|
|
160
|
+
</FormRenderer.Tab>
|
|
161
|
+
|
|
162
|
+
<FormRenderer.Tab value="items">…</FormRenderer.Tab>
|
|
163
|
+
</FormRenderer>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
| Component | Props | Renders |
|
|
167
|
+
| -------------- | -------------------------- | ------------------------------------------------------------------ |
|
|
168
|
+
| `Sidebar` | `children` | The tab rail (a Radix `Tabs.List`), fixed at the stepper's place. |
|
|
169
|
+
| `Sidebar.Item` | `value`, `icon?`, children | A rail nav row (a `Tabs.Trigger`); the active one is a black pill. |
|
|
170
|
+
| `Tab` | `value`, children | A content panel (a `Tabs.Content`) shown when its tab is active. |
|
|
171
|
+
| `Grid` | `columns?` (1–3), children | A padded grid of display `Row`s (default 2 columns). |
|
|
172
|
+
| `Row` | `label`, `value` | A read-only label/value display cell. |
|
|
173
|
+
|
|
174
|
+
The first `Tab` is active by default. Only the active panel is visible; the rail stays fixed while the
|
|
175
|
+
content column scrolls.
|
|
176
|
+
|
|
116
177
|
## Summary panel
|
|
117
178
|
|
|
118
179
|
Pass a hoisted `useForm` as `form` and a `FormSummary` as `summary` — FormRenderer binds
|
|
@@ -168,70 +168,73 @@ function ArabicPicker({ options, value, onValueChange }) {
|
|
|
168
168
|
|
|
169
169
|
### SearchableSelect
|
|
170
170
|
|
|
171
|
-
| Prop
|
|
172
|
-
|
|
173
|
-
| `options`
|
|
174
|
-
| `value`
|
|
175
|
-
| `onValueChange`
|
|
176
|
-
| `placeholder`
|
|
177
|
-
| `size`
|
|
178
|
-
| `variant`
|
|
179
|
-
| `icon`
|
|
180
|
-
| `theme`
|
|
181
|
-
| `dir`
|
|
182
|
-
| `className`
|
|
183
|
-
| `filterClientSide` | `boolean`
|
|
184
|
-
| `onSearchChange`
|
|
185
|
-
| `searchDebounceMs` | `number`
|
|
186
|
-
| `hasMore`
|
|
187
|
-
| `loading`
|
|
188
|
-
| `onLoadMore`
|
|
189
|
-
| `maxVisibleItems`
|
|
171
|
+
| Prop | Type | Default | Description |
|
|
172
|
+
| ------------------ | --------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
173
|
+
| `options` | `SearchableSelectOption[]` | Required | The list of selectable options. In server mode (`filterClientSide={false}`) these are rendered as-is. |
|
|
174
|
+
| `value` | `string \| null` | - | Controlled selected value. The matching option's label is shown as solid text in the input. |
|
|
175
|
+
| `onValueChange` | `(value: string, option: SearchableSelectOption) => void` | - | Called when an option is selected. Receives the value and the full option object. |
|
|
176
|
+
| `placeholder` | `string` | `'Search…'` | Placeholder text for the search input. |
|
|
177
|
+
| `size` | `'XS' \| 'S' \| 'M'` | `'M'` | Field size. |
|
|
178
|
+
| `variant` | `'PresentationStyle'` | `'PresentationStyle'` | Visual style variant for the field and dropdown surface. |
|
|
179
|
+
| `icon` | `ReactNode` | - | Optional leading icon rendered inside the field. |
|
|
180
|
+
| `theme` | `'dark' \| 'light' \| 'default'` | - | Theme variant, applied via `data-theme`. |
|
|
181
|
+
| `dir` | `string` | - | Text direction (e.g. `'rtl'`) for the field and dropdown. |
|
|
182
|
+
| `className` | `string` | - | Additional CSS classes for the field group. |
|
|
183
|
+
| `filterClientSide` | `boolean` | `true` | When `true`, filters `options` locally by label. Set `false` for server-side search — `options` are rendered as-is and refetched via `onSearchChange`. |
|
|
184
|
+
| `onSearchChange` | `(query: string) => void` | - | Called (debounced) with the trimmed query as the user types. Refetch your data here for server-side search. |
|
|
185
|
+
| `searchDebounceMs` | `number` | `300` | Debounce delay (ms) before `onSearchChange` fires. |
|
|
186
|
+
| `hasMore` | `boolean` | `false` | Whether more pages are available; gates the infinite-scroll loader. |
|
|
187
|
+
| `loading` | `boolean` | `false` | Whether a fetch is in flight; shows a loading row and blocks `onLoadMore`. |
|
|
188
|
+
| `onLoadMore` | `() => void` | - | Called when the scroll viewport nears the bottom and `hasMore && !loading`. |
|
|
189
|
+
| `maxVisibleItems` | `number` | `5` | Maximum rows visible before the list scrolls internally. |
|
|
190
190
|
|
|
191
191
|
### SearchableSelectOption
|
|
192
192
|
|
|
193
|
-
| Prop
|
|
194
|
-
|
|
195
|
-
| `value`
|
|
196
|
-
| `label`
|
|
197
|
-
| `icon`
|
|
193
|
+
| Prop | Type | Default | Description |
|
|
194
|
+
| ------------ | ----------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
195
|
+
| `value` | `string` | Required | Unique value for the option; matched against the `value` prop. |
|
|
196
|
+
| `label` | `string` | Required | Display text; also the default target for client-side filtering. |
|
|
197
|
+
| `icon` | `ReactNode` | - | Optional leading icon rendered in the row. |
|
|
198
|
+
| `searchText` | `string` | `label` | Text used for client-side filtering. Set it to match on a **subset** of the label — e.g. a country name when the label also shows a dial code, so typing the dial doesn't match. |
|
|
198
199
|
|
|
199
200
|
## TypeScript
|
|
200
201
|
|
|
201
202
|
### Type Definitions
|
|
202
203
|
|
|
203
204
|
```typescript
|
|
204
|
-
import { ReactNode } from
|
|
205
|
+
import { ReactNode } from "react";
|
|
205
206
|
|
|
206
207
|
export interface SearchableSelectOption {
|
|
207
|
-
value: string
|
|
208
|
-
label: string
|
|
209
|
-
icon?: ReactNode
|
|
208
|
+
value: string;
|
|
209
|
+
label: string;
|
|
210
|
+
icon?: ReactNode;
|
|
211
|
+
/** Text used for client-side filtering. Defaults to `label`. */
|
|
212
|
+
searchText?: string;
|
|
210
213
|
}
|
|
211
214
|
|
|
212
215
|
interface SearchableSelectProps {
|
|
213
|
-
options: SearchableSelectOption[]
|
|
214
|
-
value?: string | null
|
|
215
|
-
onValueChange?: (value: string, option: SearchableSelectOption) => void
|
|
216
|
-
placeholder?: string
|
|
217
|
-
size?:
|
|
218
|
-
variant?:
|
|
219
|
-
icon?: ReactNode
|
|
220
|
-
theme?:
|
|
221
|
-
dir?: string
|
|
222
|
-
className?: string
|
|
216
|
+
options: SearchableSelectOption[];
|
|
217
|
+
value?: string | null;
|
|
218
|
+
onValueChange?: (value: string, option: SearchableSelectOption) => void;
|
|
219
|
+
placeholder?: string;
|
|
220
|
+
size?: "XS" | "S" | "M";
|
|
221
|
+
variant?: "PresentationStyle";
|
|
222
|
+
icon?: ReactNode;
|
|
223
|
+
theme?: "dark" | "light" | "default";
|
|
224
|
+
dir?: string;
|
|
225
|
+
className?: string;
|
|
223
226
|
|
|
224
227
|
// Async / backend pagination (all optional; static `options` still work)
|
|
225
|
-
filterClientSide?: boolean
|
|
226
|
-
onSearchChange?: (query: string) => void
|
|
227
|
-
searchDebounceMs?: number
|
|
228
|
-
hasMore?: boolean
|
|
229
|
-
loading?: boolean
|
|
230
|
-
onLoadMore?: () => void
|
|
231
|
-
maxVisibleItems?: number
|
|
228
|
+
filterClientSide?: boolean;
|
|
229
|
+
onSearchChange?: (query: string) => void;
|
|
230
|
+
searchDebounceMs?: number;
|
|
231
|
+
hasMore?: boolean;
|
|
232
|
+
loading?: boolean;
|
|
233
|
+
onLoadMore?: () => void;
|
|
234
|
+
maxVisibleItems?: number;
|
|
232
235
|
}
|
|
233
236
|
|
|
234
|
-
export function SearchableSelect(props: SearchableSelectProps): JSX.Element
|
|
237
|
+
export function SearchableSelect(props: SearchableSelectProps): JSX.Element;
|
|
235
238
|
```
|
|
236
239
|
|
|
237
240
|
## Common Patterns
|
|
@@ -340,6 +343,7 @@ function UserPicker() {
|
|
|
340
343
|
## Best Practices
|
|
341
344
|
|
|
342
345
|
1. **Always control `value`** — pass `value` and `onValueChange` so the input can display the selected label.
|
|
346
|
+
|
|
343
347
|
```typescript
|
|
344
348
|
<SearchableSelect value={value} onValueChange={setValue} options={options} />
|
|
345
349
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Forms with FormBuilder, FormRenderer & FormSummary
|
|
3
|
-
description: The canonical way to build forms in TORCH Glare. Author fields as JSX with FormBuilder, add page/drawer chrome with FormRenderer, and show live computed totals with FormSummary. Covers single, stepper, drawer,
|
|
3
|
+
description: The canonical way to build forms in TORCH Glare. Author fields as JSX with FormBuilder, add page/drawer chrome with FormRenderer, and show live computed totals with FormSummary. Covers single, stepper, drawer, editing a record, and a full invoice example combining all three.
|
|
4
4
|
keywords:
|
|
5
5
|
[
|
|
6
6
|
form,
|
|
@@ -33,7 +33,7 @@ Three components, layered:
|
|
|
33
33
|
|
|
34
34
|
| Component | Use it for | When |
|
|
35
35
|
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
|
|
36
|
-
| **`FormBuilder`** | The form itself — fields authored as JSX children. Owns react-hook-form, validation,
|
|
36
|
+
| **`FormBuilder`** | The form itself — fields authored as JSX children. Owns react-hook-form, validation, sections, steppers. | Always. This is the base. |
|
|
37
37
|
| **`FormRenderer`** | Wraps `FormBuilder` to add **chrome**: page-vs-drawer display, the title header, an `actions` slot for the Save, and a `summary` slot. | Real forms — prefer it over raw `FormBuilder`. |
|
|
38
38
|
| **`FormSummary`** | A read-only **calculation panel beside the form** — totals that recompute live as the user types. | Invoices, orders, anything with a "conclusion". |
|
|
39
39
|
|
|
@@ -117,9 +117,8 @@ export function ItemForm({
|
|
|
117
117
|
|
|
118
118
|
`FormBuilder.Section` groups fields in a `SectionBlock` (`color` is one of `Blue`, `Yellow`,
|
|
119
119
|
`Green`, `Red`, `Orange`, `Purple`, `Pink`, `Gray`). Pass the Save via `actions` — a
|
|
120
|
-
`FormBuilder.Submit`, which is loading-aware
|
|
121
|
-
|
|
122
|
-
`FormBuilder.Header`.)
|
|
120
|
+
`FormBuilder.Submit`, which is loading-aware; it renders in the header action pill. (With raw
|
|
121
|
+
`FormBuilder`, put the same `FormBuilder.Submit` in a `FormBuilder.Header`.)
|
|
123
122
|
|
|
124
123
|
### Field types
|
|
125
124
|
|
|
@@ -180,6 +179,11 @@ themselves**: backward is free, clicking forward validates the steps in between
|
|
|
180
179
|
the first one with errors. The Save is the header `actions` — it submits every step's fields at
|
|
181
180
|
once, from any step.
|
|
182
181
|
|
|
182
|
+
You pass just the Submit as `actions`; for a stepper, FormRenderer **auto-prepends chevron
|
|
183
|
+
Back/Next controls + a divider** before it (`[◀] [▶] │ Save`). Back is disabled on the first
|
|
184
|
+
step; Next validates then advances (disabled on the last). A step that **passes validation stays
|
|
185
|
+
checked** in the rail even after you navigate back — a live error still shows it red.
|
|
186
|
+
|
|
183
187
|
```tsx
|
|
184
188
|
<FormRenderer<Values>
|
|
185
189
|
onSubmit={save}
|
|
@@ -206,21 +210,20 @@ once, from any step.
|
|
|
206
210
|
|
|
207
211
|
---
|
|
208
212
|
|
|
209
|
-
## 4.
|
|
213
|
+
## 4. Editing a record
|
|
210
214
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
- **View**: `mode="view"` renders the same children read-only, with no Submit.
|
|
215
|
+
Pass `values` (not just `defaultValues`) — the form repopulates when the data loads. Add a
|
|
216
|
+
remount `key` so initial-only inputs (date, rich text) re-seed.
|
|
214
217
|
|
|
215
218
|
```tsx
|
|
216
219
|
<FormRenderer<Values>
|
|
217
220
|
key={record?.id ?? "new"}
|
|
218
|
-
mode={mode} // 'edit' | 'view'
|
|
219
221
|
values={record} // arrives async
|
|
220
222
|
onSubmit={save}
|
|
221
223
|
resolver={zodResolver(schema)}
|
|
222
224
|
defaultValues={DEFAULTS}
|
|
223
|
-
header={{ title: record?.name ?? "New", variant:
|
|
225
|
+
header={{ title: record?.name ?? "New", variant: record ? "edit" : "new" }}
|
|
226
|
+
actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}
|
|
224
227
|
>
|
|
225
228
|
…the same fields…
|
|
226
229
|
</FormRenderer>
|
|
@@ -388,6 +391,60 @@ drawer's tray, beside the form:
|
|
|
388
391
|
|
|
389
392
|
---
|
|
390
393
|
|
|
394
|
+
## 8. A detail (view) page — sidebar tabs, not a form
|
|
395
|
+
|
|
396
|
+
Sometimes you want to **display** a record, not edit it. Give `FormRenderer` `FormRenderer.Sidebar`
|
|
397
|
+
|
|
398
|
+
- `FormRenderer.Tab` children (instead of fields) and it switches to a display-only detail page: a
|
|
399
|
+
left **sidebar** where each item swaps in its matching tab panel — no `<form>`, no submit. The
|
|
400
|
+
sidebar sits where a stepper's rail would; only the active panel shows.
|
|
401
|
+
|
|
402
|
+
**Every tab's content is `FormBuilder.Section` blocks.** Inside a Section, use the default
|
|
403
|
+
`FormRenderer.Grid` + `FormRenderer.Row` display cells (the read-only counterpart of form fields), or
|
|
404
|
+
render **your own component** — anything goes inside a Section.
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
<FormRenderer
|
|
408
|
+
header={{ title: "Order DE-344", variant: "detail" }} // "View" badge
|
|
409
|
+
actions={<Button variant="BorderStyle">Print</Button>}
|
|
410
|
+
>
|
|
411
|
+
{/* Rail — each Item's `value` ties to a Tab. */}
|
|
412
|
+
<FormRenderer.Sidebar>
|
|
413
|
+
<FormRenderer.Sidebar.Item value="overview" icon={<i className="ri-layout-grid-line" />}>
|
|
414
|
+
Overview
|
|
415
|
+
</FormRenderer.Sidebar.Item>
|
|
416
|
+
<FormRenderer.Sidebar.Item value="activity" icon={<i className="ri-pulse-line" />}>
|
|
417
|
+
Activity log
|
|
418
|
+
</FormRenderer.Sidebar.Item>
|
|
419
|
+
</FormRenderer.Sidebar>
|
|
420
|
+
|
|
421
|
+
{/* Default display cells */}
|
|
422
|
+
<FormRenderer.Tab value="overview">
|
|
423
|
+
<FormBuilder.Section title="Main Information" color="Blue">
|
|
424
|
+
<FormRenderer.Grid columns={2}>
|
|
425
|
+
<FormRenderer.Row label="PO Number" value={record.poNumber} />
|
|
426
|
+
<FormRenderer.Row label="Status" value={<Badge label="Submitted" color="yellow" />} />
|
|
427
|
+
</FormRenderer.Grid>
|
|
428
|
+
</FormBuilder.Section>
|
|
429
|
+
</FormRenderer.Tab>
|
|
430
|
+
|
|
431
|
+
{/* Or bring your own component — still inside a Section */}
|
|
432
|
+
<FormRenderer.Tab value="activity">
|
|
433
|
+
<FormBuilder.Section title="Activity log" color="Green">
|
|
434
|
+
<YourTimeline items={record.activity} />
|
|
435
|
+
</FormBuilder.Section>
|
|
436
|
+
</FormRenderer.Tab>
|
|
437
|
+
</FormRenderer>
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
`FormRenderer.Grid` takes `columns` (1–3, default 2) and spans the full section width; `FormRenderer.Row`
|
|
441
|
+
takes `label` + `value` (any node). The first `Tab` is active by default.
|
|
442
|
+
|
|
443
|
+
> Generating one? Call the `create-form` tool with `layout="detail"` — it returns this exact wiring
|
|
444
|
+
> with your fields pre-filled as display rows.
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
391
448
|
## Gotchas
|
|
392
449
|
|
|
393
450
|
- **Hoisting `useForm` disables the remount-`key` reset.** Once the form instance lives in
|
|
@@ -599,7 +599,16 @@ export default {
|
|
|
599
599
|
"./src/**/*.{js,ts,jsx,tsx}",
|
|
600
600
|
],
|
|
601
601
|
theme: {
|
|
602
|
-
extend: {
|
|
602
|
+
extend: {
|
|
603
|
+
containers: {
|
|
604
|
+
xs: "320px",
|
|
605
|
+
sm: "600px",
|
|
606
|
+
md: "768px",
|
|
607
|
+
lg: "1024px",
|
|
608
|
+
xl: "1280px",
|
|
609
|
+
"2xl": "1536px",
|
|
610
|
+
},
|
|
611
|
+
},
|
|
603
612
|
},
|
|
604
613
|
plugins: [
|
|
605
614
|
// Color system (required first)
|
|
@@ -612,6 +621,7 @@ export default {
|
|
|
612
621
|
require('glare-typography'),
|
|
613
622
|
|
|
614
623
|
// Additional Tailwind plugins
|
|
624
|
+
require('@tailwindcss/container-queries'),
|
|
615
625
|
require('tailwindcss-animate'),
|
|
616
626
|
require('tailwind-scrollbar-hide'),
|
|
617
627
|
],
|
|
@@ -132,10 +132,19 @@ module.exports = {
|
|
|
132
132
|
theme: {
|
|
133
133
|
extend: {
|
|
134
134
|
colors: mappingVars,
|
|
135
|
+
containers: {
|
|
136
|
+
xs: "320px",
|
|
137
|
+
sm: "600px",
|
|
138
|
+
md: "768px",
|
|
139
|
+
lg: "1024px",
|
|
140
|
+
xl: "1280px",
|
|
141
|
+
"2xl": "1536px",
|
|
142
|
+
},
|
|
135
143
|
},
|
|
136
144
|
},
|
|
137
145
|
plugins: [
|
|
138
146
|
plugin,
|
|
147
|
+
require('@tailwindcss/container-queries'),
|
|
139
148
|
require('tailwindcss-animate'),
|
|
140
149
|
require('tailwind-scrollbar-hide'),
|
|
141
150
|
require('glare-typography'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "torch-glare",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "A copy-in React component library (TypeScript + Radix UI + Tailwind CSS). Its CLI copies component source directly into your project — you own the code.",
|