torch-glare 2.4.0 → 2.4.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/README.md +8 -0
- package/apps/lib/components/ActionButton.tsx +7 -12
- package/apps/lib/components/ActionsGroup.tsx +4 -15
- package/apps/lib/components/AlertDialog.tsx +173 -191
- package/apps/lib/components/Avatar.tsx +13 -16
- package/apps/lib/components/Badge.tsx +1 -3
- package/apps/lib/components/BadgeField.tsx +76 -71
- package/apps/lib/components/Breadcrumb.tsx +49 -85
- package/apps/lib/components/Button.tsx +21 -20
- package/apps/lib/components/ButtonGroup.tsx +31 -41
- package/apps/lib/components/Calendar.tsx +51 -29
- package/apps/lib/components/Card.tsx +40 -24
- package/apps/lib/components/Checkbox.tsx +9 -16
- package/apps/lib/components/ColorPicker.tsx +441 -0
- package/apps/lib/components/ConclusionHeader.tsx +148 -0
- package/apps/lib/components/ContextMenu.tsx +97 -96
- package/apps/lib/components/CountBadge.tsx +4 -6
- package/apps/lib/components/DataTable.tsx +196 -216
- package/apps/lib/components/DataViews/DataViewRadio.tsx +1 -6
- package/apps/lib/components/DataViews/DataViewsConfigPanel.tsx +13 -47
- package/apps/lib/components/DataViews/DataViewsHeader.tsx +1 -3
- package/apps/lib/components/DataViews/DataViewsLayout.tsx +5 -12
- package/apps/lib/components/DataViews/FilterPanel.tsx +155 -149
- package/apps/lib/components/DataViews/HeaderSearch.tsx +1 -5
- package/apps/lib/components/DataViews/InboxView.tsx +22 -68
- package/apps/lib/components/DataViews/InboxViewCard.tsx +68 -77
- package/apps/lib/components/DataViews/KanbanView.tsx +8 -25
- package/apps/lib/components/DataViews/PanelControls.tsx +3 -13
- package/apps/lib/components/DataViews/SettingsPanel.tsx +83 -89
- package/apps/lib/components/DataViews/TableView.tsx +8 -28
- package/apps/lib/components/DataViews/TreeView.tsx +12 -40
- package/apps/lib/components/DataViews/badgeAdapter.ts +32 -28
- package/apps/lib/components/DataViews/fieldRenderers.tsx +106 -141
- package/apps/lib/components/DataViews/filters/DatePickerRangeFilter.tsx +30 -23
- package/apps/lib/components/DataViews/filters/DateRangePopover.tsx +39 -32
- package/apps/lib/components/DataViews/filters/PresetChips.tsx +15 -15
- package/apps/lib/components/DataViews/filters/RangeSliderWithInputs.tsx +69 -58
- package/apps/lib/components/DataViews/index.ts +26 -26
- package/apps/lib/components/DataViews/tree/TreeDrawer.tsx +12 -16
- package/apps/lib/components/DataViews/tree/TreeSidebar.tsx +28 -31
- package/apps/lib/components/DataViews/types.ts +104 -115
- package/apps/lib/components/DatePicker.tsx +200 -196
- package/apps/lib/components/Dialog.tsx +92 -105
- package/apps/lib/components/Divider.tsx +21 -24
- package/apps/lib/components/Drawer.tsx +124 -142
- package/apps/lib/components/DropdownMenu.tsx +131 -171
- package/apps/lib/components/FieldHint.tsx +4 -11
- package/apps/lib/components/Form.tsx +146 -155
- package/apps/lib/components/FormBuilder/DisplayField.tsx +40 -0
- package/apps/lib/components/FormBuilder/context.ts +71 -0
- package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +65 -0
- package/apps/lib/components/FormBuilder/fields/ColorField.tsx +76 -0
- package/apps/lib/components/FormBuilder/fields/CustomField.tsx +16 -0
- package/apps/lib/components/FormBuilder/fields/DateField.tsx +34 -0
- package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +79 -0
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +174 -0
- package/apps/lib/components/FormBuilder/fields/FileField.tsx +40 -0
- package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +139 -0
- package/apps/lib/components/FormBuilder/fields/OtpField.tsx +32 -0
- package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +82 -0
- package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +37 -0
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +101 -0
- package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +175 -0
- package/apps/lib/components/FormBuilder/fields/SliderField.tsx +72 -0
- package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +42 -0
- package/apps/lib/components/FormBuilder/fields/TableField.tsx +312 -0
- package/apps/lib/components/FormBuilder/fields/TextField.tsx +222 -0
- package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +49 -0
- package/apps/lib/components/FormBuilder/fields/countries.ts +303 -0
- package/apps/lib/components/FormBuilder/fields/index.ts +25 -0
- package/apps/lib/components/FormBuilder/form-builder.tsx +292 -0
- package/apps/lib/components/FormBuilder/header.tsx +105 -0
- package/apps/lib/components/FormBuilder/index.ts +37 -0
- package/apps/lib/components/FormBuilder/numberFormat.ts +16 -0
- package/apps/lib/components/FormBuilder/stepper.tsx +217 -0
- package/apps/lib/components/FormBuilder/submit.tsx +41 -0
- package/apps/lib/components/FormBuilder/types.ts +264 -0
- package/apps/lib/components/FormBuilder/viewFormat.tsx +137 -0
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +121 -0
- package/apps/lib/components/FormRenderer/form-renderer.tsx +113 -0
- package/apps/lib/components/FormRenderer/index.ts +9 -0
- package/apps/lib/components/FormRenderer/types.ts +79 -0
- package/apps/lib/components/FormStepper.tsx +9 -39
- package/apps/lib/components/FormSummary.tsx +282 -0
- package/apps/lib/components/HeaderBar.tsx +1 -9
- package/apps/lib/components/ImageAttachment.tsx +106 -93
- package/apps/lib/components/InnerLabelField.tsx +20 -16
- package/apps/lib/components/Input.tsx +171 -172
- package/apps/lib/components/InputField.tsx +44 -45
- package/apps/lib/components/InputOTP.tsx +68 -69
- package/apps/lib/components/Label.tsx +61 -58
- package/apps/lib/components/LabelField.tsx +4 -5
- package/apps/lib/components/LabeledCheckBox.tsx +8 -12
- package/apps/lib/components/LabeledRadio.tsx +7 -5
- package/apps/lib/components/LinkButton.tsx +2 -9
- package/apps/lib/components/LoginButton.tsx +4 -9
- package/apps/lib/components/PasswordLevel.tsx +5 -5
- package/apps/lib/components/Popover.tsx +15 -33
- package/apps/lib/components/ProfileMenu.tsx +67 -72
- package/apps/lib/components/Radio.tsx +1 -5
- package/apps/lib/components/RadioCard.tsx +11 -28
- package/apps/lib/components/ScrollArea.tsx +45 -43
- package/apps/lib/components/SearchField.tsx +49 -39
- package/apps/lib/components/SearchableSelect.tsx +16 -16
- package/apps/lib/components/SearchableTable.tsx +108 -118
- package/apps/lib/components/SearchableTree.tsx +469 -0
- package/apps/lib/components/SearchableTreeDialog.tsx +493 -0
- package/apps/lib/components/SectionBlock.tsx +19 -23
- package/apps/lib/components/Select.tsx +80 -186
- package/apps/lib/components/SimpleSelect.tsx +196 -187
- package/apps/lib/components/Skeleton.tsx +9 -12
- package/apps/lib/components/SlideDatePicker.tsx +215 -166
- package/apps/lib/components/SpinLoading.tsx +33 -49
- package/apps/lib/components/Stepper.tsx +13 -56
- package/apps/lib/components/Switch.tsx +8 -10
- package/apps/lib/components/TabFormItem.tsx +3 -12
- package/apps/lib/components/TabSwitch.tsx +7 -34
- package/apps/lib/components/Table.tsx +60 -75
- package/apps/lib/components/{ChartBlockTool.ts → TextEditor/ChartBlockTool.ts} +17 -52
- package/apps/lib/components/TextEditor/RichTextField.tsx +46 -0
- package/apps/lib/components/{TableDnDWrapper.ts → TextEditor/TableDnDWrapper.ts} +15 -48
- package/apps/lib/components/{TextEditor.tsx → TextEditor/TextEditor.tsx} +156 -113
- package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +429 -0
- package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +70 -0
- package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +50 -0
- package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +48 -0
- package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +98 -0
- package/apps/lib/{types → components/TextEditor}/editorjs.d.ts +19 -0
- package/apps/lib/components/TextEditor/index.ts +7 -0
- package/apps/lib/{utils → components/TextEditor}/markdownParser.ts +2 -7
- package/apps/lib/components/Textarea.tsx +9 -13
- package/apps/lib/components/Timeline.tsx +21 -41
- package/apps/lib/components/Toast.tsx +83 -83
- package/apps/lib/components/Toggle.tsx +112 -114
- package/apps/lib/components/ToggleButton.tsx +1 -1
- package/apps/lib/components/Tooltip.tsx +24 -19
- package/apps/lib/components/TransparentLabel.tsx +61 -62
- package/apps/lib/components/TreeDropDown.tsx +86 -52
- package/apps/lib/components/TreeFolder/TreeFolder.tsx +15 -48
- package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +16 -21
- package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +9 -20
- package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +3 -3
- package/apps/lib/components/TreeFolder/icons.tsx +14 -17
- package/apps/lib/components/TreeFolder/index.ts +15 -8
- package/apps/lib/components/TreeFolder/treeFolderUtils.ts +47 -52
- package/apps/lib/components/TreeFolder/types.ts +32 -32
- package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +117 -123
- package/apps/lib/hooks/useActiveTreeItem.ts +52 -55
- package/apps/lib/hooks/useClickOutside.ts +21 -20
- package/apps/lib/hooks/useDataViewsState.ts +86 -80
- package/apps/lib/hooks/useIsMobile.ts +12 -12
- package/apps/lib/hooks/useResize.ts +54 -65
- package/apps/lib/hooks/useTagSelection.ts +201 -201
- package/apps/lib/layouts/CNLayout.tsx +181 -140
- package/apps/lib/layouts/DataViewCard.tsx +4 -18
- package/apps/lib/layouts/FieldSection.tsx +2 -6
- package/apps/lib/layouts/TreeSubLayout.tsx +24 -38
- package/apps/lib/providers/ThemeProvider.tsx +8 -23
- package/apps/lib/registry.json +997 -0
- package/apps/lib/tsconfig.json +9 -0
- package/apps/lib/tsconfig.tsbuildinfo +1 -0
- package/apps/lib/utils/cn.ts +1 -1
- package/apps/lib/utils/color.ts +175 -0
- package/apps/lib/utils/dataViews/columnUtils.ts +46 -44
- package/apps/lib/utils/dataViews/fieldUtils.ts +70 -71
- package/apps/lib/utils/dataViews/nestedDataUtils.tsx +129 -122
- package/apps/lib/utils/dataViews/pathUtils.ts +74 -67
- package/apps/lib/utils/dataViews/rangeUtils.ts +121 -112
- package/apps/lib/utils/dataViews/treeUtils.ts +204 -211
- package/apps/lib/utils/dateFormat.ts +64 -54
- package/apps/lib/utils/resize.ts +21 -25
- package/apps/lib/utils/types.ts +13 -13
- package/dist/bin/index.js +8 -2
- package/dist/bin/index.js.map +1 -1
- package/dist/src/shared/getAvailableFiles.d.ts.map +1 -1
- package/dist/src/shared/getAvailableFiles.js +6 -1
- package/dist/src/shared/getAvailableFiles.js.map +1 -1
- package/dist/src/shared/loadRegistry.d.ts +7 -0
- package/dist/src/shared/loadRegistry.d.ts.map +1 -0
- package/dist/src/shared/loadRegistry.js +31 -0
- package/dist/src/shared/loadRegistry.js.map +1 -0
- package/dist/src/shared/resolveInstallPlan.d.ts +12 -0
- package/dist/src/shared/resolveInstallPlan.d.ts.map +1 -0
- package/dist/src/shared/resolveInstallPlan.js +39 -0
- package/dist/src/shared/resolveInstallPlan.js.map +1 -0
- package/dist/src/types/main.d.ts +24 -0
- package/dist/src/types/main.d.ts.map +1 -1
- package/docs/README.md +10 -2
- package/docs/components/action-button.md +30 -24
- package/docs/components/actions-group.md +26 -20
- package/docs/components/alert-dialog.md +45 -45
- package/docs/components/avatar.md +6 -2
- package/docs/components/badge-field.md +6 -2
- package/docs/components/badge.md +7 -1
- package/docs/components/breadcrumb.md +15 -40
- package/docs/components/button-group.md +13 -10
- package/docs/components/button.md +58 -33
- package/docs/components/calendar.md +26 -19
- package/docs/components/card.md +23 -18
- package/docs/components/chart-block-tool.md +129 -0
- package/docs/components/checkbox.md +16 -8
- package/docs/components/cn-layout.md +7 -1
- package/docs/components/color-picker.md +101 -0
- package/docs/components/conclusion-header.md +80 -0
- package/docs/components/context-menu.md +18 -25
- package/docs/components/count-badge.md +7 -1
- package/docs/components/data-table.md +45 -33
- package/docs/components/data-views-config-panel.md +15 -11
- package/docs/components/data-views-layout.md +13 -16
- package/docs/components/date-picker.md +30 -23
- package/docs/components/dialog.md +43 -42
- package/docs/components/divider.md +10 -4
- package/docs/components/drawer.md +161 -135
- package/docs/components/dropdown-menu.md +25 -34
- package/docs/components/field-hint.md +37 -25
- package/docs/components/field-section.md +9 -3
- package/docs/components/form-builder.md +270 -0
- package/docs/components/form-renderer.md +228 -0
- package/docs/components/form-stepper.md +6 -0
- package/docs/components/form-summary.md +123 -0
- package/docs/components/form.md +17 -20
- package/docs/components/header-bar.md +6 -0
- package/docs/components/image-attachment.md +43 -45
- package/docs/components/inbox-view.md +16 -5
- package/docs/components/inner-label-field.md +34 -24
- package/docs/components/input-field.md +18 -10
- package/docs/components/input-otp.md +14 -11
- package/docs/components/input.md +22 -16
- package/docs/components/kanban-view.md +15 -5
- package/docs/components/label-field.md +32 -22
- package/docs/components/label.md +43 -32
- package/docs/components/labeled-check-box.md +16 -8
- package/docs/components/labeled-radio.md +18 -11
- package/docs/components/link-button.md +7 -1
- package/docs/components/login-button.md +8 -2
- package/docs/components/password-level.md +37 -24
- package/docs/components/popover.md +26 -23
- package/docs/components/profile-menu.md +38 -27
- package/docs/components/radio-card.md +16 -9
- package/docs/components/radio.md +16 -8
- package/docs/components/scroll-area.md +6 -2
- package/docs/components/search-field.md +14 -6
- package/docs/components/searchable-select.md +19 -14
- package/docs/components/searchable-table.md +18 -13
- package/docs/components/searchable-tree-dialog.md +190 -0
- package/docs/components/searchable-tree.md +200 -0
- package/docs/components/section-block.md +12 -4
- package/docs/components/select.md +23 -18
- package/docs/components/simple-select.md +14 -10
- package/docs/components/skeleton.md +43 -33
- package/docs/components/slide-date-picker.md +37 -29
- package/docs/components/spin-loading.md +32 -23
- package/docs/components/stepper.md +6 -0
- package/docs/components/switch.md +14 -6
- package/docs/components/tab-form-item.md +7 -1
- package/docs/components/tab-switch.md +13 -6
- package/docs/components/table-dnd-wrapper.md +107 -0
- package/docs/components/table-view.md +15 -5
- package/docs/components/table.md +28 -28
- package/docs/components/text-editor.md +37 -13
- package/docs/components/textarea.md +15 -7
- package/docs/components/timeline.md +6 -0
- package/docs/components/toast.md +35 -26
- package/docs/components/toggle-button.md +24 -21
- package/docs/components/toggle.md +23 -15
- package/docs/components/tooltip.md +42 -30
- package/docs/components/transparent-label.md +29 -20
- package/docs/components/tree-drop-down.md +31 -23
- package/docs/components/tree-sub-layout.md +6 -2
- package/docs/components/tree-view.md +16 -5
- package/docs/explanation/architecture.md +54 -0
- package/docs/explanation/design-system.md +52 -0
- package/docs/how-to/data-views-from-backend-response.md +6 -4
- package/docs/how-to/form-and-list-recipes.md +26 -19
- package/docs/how-to/forms-with-form-builder.md +408 -0
- package/docs/how-to/guides.md +177 -197
- package/docs/migration/changelog.md +38 -0
- package/docs/reference/cli.md +87 -0
- package/docs/reference/components.md +11 -12
- package/docs/reference/hooks.md +8 -8
- package/docs/reference/providers.md +22 -22
- package/docs/reference/tailwind-plugins.md +2 -2
- package/docs/reference/theme.md +85 -0
- package/docs/reference/types.md +4 -4
- package/docs/reference/utilities.md +19 -26
- package/docs/tutorials/building-first-form.md +170 -172
- package/docs/tutorials/component-composition.md +34 -50
- package/docs/tutorials/getting-started.md +2 -2
- package/docs/tutorials/theming-basics.md +13 -10
- package/package.json +31 -3
- package/dist/src/commands/block.d.ts +0 -5
- package/dist/src/commands/block.d.ts.map +0 -1
- package/dist/src/commands/block.js +0 -255
- package/dist/src/commands/block.js.map +0 -1
- package/dist/src/shared/configureFonts.d.ts +0 -6
- package/dist/src/shared/configureFonts.d.ts.map +0 -1
- package/dist/src/shared/configureFonts.js +0 -106
- package/dist/src/shared/configureFonts.js.map +0 -1
- package/dist/src/shared/configureGlobalCss.d.ts +0 -6
- package/dist/src/shared/configureGlobalCss.d.ts.map +0 -1
- package/dist/src/shared/configureGlobalCss.js +0 -154
- package/dist/src/shared/configureGlobalCss.js.map +0 -1
- package/dist/src/shared/configureTailwind.d.ts +0 -7
- package/dist/src/shared/configureTailwind.d.ts.map +0 -1
- package/dist/src/shared/configureTailwind.js +0 -163
- package/dist/src/shared/configureTailwind.js.map +0 -1
- package/dist/src/shared/detectFramework.d.ts +0 -23
- package/dist/src/shared/detectFramework.d.ts.map +0 -1
- package/dist/src/shared/detectFramework.js +0 -119
- package/dist/src/shared/detectFramework.js.map +0 -1
- package/dist/src/shared/installBaseUtils.d.ts +0 -5
- package/dist/src/shared/installBaseUtils.d.ts.map +0 -1
- package/dist/src/shared/installBaseUtils.js +0 -79
- package/dist/src/shared/installBaseUtils.js.map +0 -1
- package/dist/src/shared/resolveAliases.d.ts +0 -24
- package/dist/src/shared/resolveAliases.d.ts.map +0 -1
- package/dist/src/shared/resolveAliases.js +0 -98
- package/dist/src/shared/resolveAliases.js.map +0 -1
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
---
|
|
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, edit/view, and a full invoice example combining all three.
|
|
4
|
+
keywords:
|
|
5
|
+
[
|
|
6
|
+
form,
|
|
7
|
+
forms,
|
|
8
|
+
formbuilder,
|
|
9
|
+
formrenderer,
|
|
10
|
+
formsummary,
|
|
11
|
+
form-builder,
|
|
12
|
+
form-renderer,
|
|
13
|
+
form-summary,
|
|
14
|
+
validation,
|
|
15
|
+
zod,
|
|
16
|
+
resolver,
|
|
17
|
+
stepper,
|
|
18
|
+
drawer,
|
|
19
|
+
totals,
|
|
20
|
+
invoice,
|
|
21
|
+
calculation,
|
|
22
|
+
react-hook-form,
|
|
23
|
+
]
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Forms with FormBuilder, FormRenderer & FormSummary
|
|
27
|
+
|
|
28
|
+
**This is the way to build forms in TORCH Glare.** Do not hand-wire
|
|
29
|
+
`FormField` → `FormItem` → `FormControl` → `InputField` rows, and never track field state
|
|
30
|
+
with `useState` — that boilerplate is exactly what `FormBuilder` exists to remove.
|
|
31
|
+
|
|
32
|
+
Three components, layered:
|
|
33
|
+
|
|
34
|
+
| Component | Use it for | When |
|
|
35
|
+
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
|
|
36
|
+
| **`FormBuilder`** | The form itself — fields authored as JSX children. Owns react-hook-form, validation, edit/view, sections, steppers. | Always. This is the base. |
|
|
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
|
+
| **`FormSummary`** | A read-only **calculation panel beside the form** — totals that recompute live as the user types. | Invoices, orders, anything with a "conclusion". |
|
|
39
|
+
|
|
40
|
+
Validation is **resolver-agnostic**: pass any react-hook-form resolver
|
|
41
|
+
(`zodResolver(schema)` is typical). The library never depends on zod.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 1. A single-page form
|
|
46
|
+
|
|
47
|
+
Each `FormBuilder.*` field is one JSX child taking at least a `name`, plus `label`,
|
|
48
|
+
`placeholder`, `required`, `disabled`, `hidden`, `fullWidth`. Wrap them in `FormRenderer`,
|
|
49
|
+
give it a `header` for the title bar, and pass the Save via `actions`.
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
"use client";
|
|
53
|
+
|
|
54
|
+
import { z } from "zod";
|
|
55
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
56
|
+
import { FormBuilder } from "@/components/FormBuilder";
|
|
57
|
+
import { FormRenderer } from "@/components/FormRenderer";
|
|
58
|
+
|
|
59
|
+
const schema = z.object({
|
|
60
|
+
name: z.string().min(1, "Name is required"),
|
|
61
|
+
description: z.string(),
|
|
62
|
+
category: z.string().min(1, "Pick a category"),
|
|
63
|
+
price: z.number().positive("Must be positive").optional(),
|
|
64
|
+
active: z.boolean(),
|
|
65
|
+
agree: z.boolean().refine((v) => v === true, "You must agree to continue"),
|
|
66
|
+
});
|
|
67
|
+
type Values = z.infer<typeof schema>;
|
|
68
|
+
|
|
69
|
+
const DEFAULTS: Values = {
|
|
70
|
+
name: "",
|
|
71
|
+
description: "",
|
|
72
|
+
category: "",
|
|
73
|
+
price: undefined,
|
|
74
|
+
active: true,
|
|
75
|
+
agree: false,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const CATEGORY = [
|
|
79
|
+
{ label: "Hardware", value: "hardware" },
|
|
80
|
+
{ label: "Software", value: "software" },
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
export function ItemForm({
|
|
84
|
+
onSave,
|
|
85
|
+
saving,
|
|
86
|
+
}: {
|
|
87
|
+
onSave: (v: Values) => Promise<void>;
|
|
88
|
+
saving?: boolean;
|
|
89
|
+
}) {
|
|
90
|
+
return (
|
|
91
|
+
<FormRenderer<Values>
|
|
92
|
+
onSubmit={onSave}
|
|
93
|
+
loading={saving}
|
|
94
|
+
resolver={zodResolver(schema)}
|
|
95
|
+
defaultValues={DEFAULTS}
|
|
96
|
+
header={{ title: "New item", variant: "new" }}
|
|
97
|
+
actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}
|
|
98
|
+
>
|
|
99
|
+
<FormBuilder.Section title="Identity" color="Blue">
|
|
100
|
+
<FormBuilder.Text name="name" label="Name" required placeholder="e.g. Acme Widget" />
|
|
101
|
+
<FormBuilder.Textarea name="description" label="Description" fullWidth />
|
|
102
|
+
</FormBuilder.Section>
|
|
103
|
+
|
|
104
|
+
<FormBuilder.Section title="Classification" color="Red">
|
|
105
|
+
<FormBuilder.Select name="category" label="Category" required options={CATEGORY} />
|
|
106
|
+
<FormBuilder.Currency name="price" label="Base price" currencySymbol="$" />
|
|
107
|
+
</FormBuilder.Section>
|
|
108
|
+
|
|
109
|
+
<FormBuilder.Section title="Settings" color="Purple">
|
|
110
|
+
<FormBuilder.SwitchBox name="active" label="Active" subLabel="Enabled" />
|
|
111
|
+
<FormBuilder.Checkbox name="agree" label="I agree to the terms" required />
|
|
112
|
+
</FormBuilder.Section>
|
|
113
|
+
</FormRenderer>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`FormBuilder.Section` groups fields in a `SectionBlock` (`color` is one of `Blue`, `Yellow`,
|
|
119
|
+
`Green`, `Red`, `Orange`, `Purple`, `Pink`, `Gray`). Pass the Save via `actions` — a
|
|
120
|
+
`FormBuilder.Submit`, which is loading-aware and auto-hides in view mode; it renders in the
|
|
121
|
+
header action pill. (With raw `FormBuilder`, put the same `FormBuilder.Submit` in a
|
|
122
|
+
`FormBuilder.Header`.)
|
|
123
|
+
|
|
124
|
+
### Field types
|
|
125
|
+
|
|
126
|
+
Every field, its underlying control, and the value your `onSubmit` receives:
|
|
127
|
+
|
|
128
|
+
| Component | Value |
|
|
129
|
+
| ---------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
130
|
+
| `.Text` / `.Email` / `.Password` (`strengthMeter`) | `string` |
|
|
131
|
+
| `.Number` | `number` |
|
|
132
|
+
| `.Currency` (`currencySymbol`) | `number` |
|
|
133
|
+
| `.Textarea` | `string` |
|
|
134
|
+
| `.Select` / `.SearchableSelect` (`options`) | `string` |
|
|
135
|
+
| `.MultiSelect` / `.Tags` (`options`) | `string[]` |
|
|
136
|
+
| `.RadioList` (`options`, optional per-option `description`) | `string` |
|
|
137
|
+
| `.CheckboxGroup` (`options`) | `string[]` |
|
|
138
|
+
| `.RadioCards` (`options` with `description`) | `string` |
|
|
139
|
+
| `.Checkbox` (`subLabel`) | `boolean` |
|
|
140
|
+
| `.SwitchBox` (`subLabel`) | `boolean` |
|
|
141
|
+
| `.Otp` (`length`) | `string` |
|
|
142
|
+
| `.Slider` (`min`, `max`, `step`, `range`, `suffix`) | `number` (or `[number, number]` with `range`) |
|
|
143
|
+
| `.Color` (`presets`, `alpha`) | hex `string` |
|
|
144
|
+
| `.Phone` (`defaultCountry`, defaults to `+964`) | `string` (`"+<dial> <number>"`) |
|
|
145
|
+
| `.Date` | `Date` |
|
|
146
|
+
| `.DateRange` | `{ from, to }` |
|
|
147
|
+
| `.DateMultiple` | `Date[]` |
|
|
148
|
+
| `.DateTime` | `Date` |
|
|
149
|
+
| `.TreeSelect` (`nodes`, `getNodeId`, `getNodeLabel`) | node id (`string`) |
|
|
150
|
+
| `.File` / `.Image` (`accept`, `multiple`) | `File \| File[]` |
|
|
151
|
+
| `.RichText` | EditorJS `OutputData` |
|
|
152
|
+
| `.Signature` (`penColor`) | PNG data-URL `string` |
|
|
153
|
+
| `.FieldArray` (`children` render fn, `defaultItem`) | `object[]` |
|
|
154
|
+
| `.Table` (`columns`, `selectable`, `reorderable`, `defaultItem`) | `object[]` — editable grid; top-level child (not in a Section) |
|
|
155
|
+
| `.Custom` (`render`, `formatView`) | anything |
|
|
156
|
+
|
|
157
|
+
See the [FormBuilder](../components/form-builder.md) doc for the full prop tables.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 2. Title header + action bar
|
|
162
|
+
|
|
163
|
+
`FormRenderer`'s `header` prop renders a title pill on the left: `header={{ title: 'Acme Widget
|
|
164
|
+
Pro', variant: 'edit' }}` (variants: `new`, `edit`, `detail`). The **action pill** on the right is
|
|
165
|
+
whatever you pass to `actions` — put the Save there:
|
|
166
|
+
`actions={<FormBuilder.Submit>Save invoice</FormBuilder.Submit>}`. A bare `FormBuilder.Submit`
|
|
167
|
+
auto-targets the form (via a form-id context), so it submits even though the header renders
|
|
168
|
+
_outside_ the `<form>`.
|
|
169
|
+
|
|
170
|
+
If you use raw `FormBuilder`, the same bar is `FormBuilder.Header` with a `FormBuilder.Submit`
|
|
171
|
+
child.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 3. Stepper (steps are components)
|
|
176
|
+
|
|
177
|
+
Every step's fields stay **mounted and registered** — the whole form is live regardless of
|
|
178
|
+
which step shows; the stepper only toggles visibility. **Navigation is the step buttons
|
|
179
|
+
themselves**: backward is free, clicking forward validates the steps in between and stops at
|
|
180
|
+
the first one with errors. The Save is the header `actions` — it submits every step's fields at
|
|
181
|
+
once, from any step.
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
<FormRenderer<Values>
|
|
185
|
+
onSubmit={save}
|
|
186
|
+
resolver={zodResolver(schema)}
|
|
187
|
+
defaultValues={DEFAULTS}
|
|
188
|
+
header={{ title: "New item", variant: "new" }}
|
|
189
|
+
actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}
|
|
190
|
+
>
|
|
191
|
+
<FormBuilder.Stepper>
|
|
192
|
+
<FormBuilder.Step title="Identity">
|
|
193
|
+
<FormBuilder.Section title="Identity" color="Blue">
|
|
194
|
+
<FormBuilder.Text name="name" label="Name" required />
|
|
195
|
+
</FormBuilder.Section>
|
|
196
|
+
</FormBuilder.Step>
|
|
197
|
+
|
|
198
|
+
<FormBuilder.Step title="Classification">
|
|
199
|
+
<FormBuilder.Section title="Classification" color="Red">
|
|
200
|
+
<FormBuilder.Select name="category" label="Category" required options={CATEGORY} />
|
|
201
|
+
</FormBuilder.Section>
|
|
202
|
+
</FormBuilder.Step>
|
|
203
|
+
</FormBuilder.Stepper>
|
|
204
|
+
</FormRenderer>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 4. Edit and read-only view — one markup, both modes
|
|
210
|
+
|
|
211
|
+
- **Edit**: pass `values` (not just `defaultValues`) — the form repopulates when the data
|
|
212
|
+
loads. Add a remount `key` so initial-only inputs (date, rich text) re-seed.
|
|
213
|
+
- **View**: `mode="view"` renders the same children read-only, with no Submit.
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
<FormRenderer<Values>
|
|
217
|
+
key={record?.id ?? "new"}
|
|
218
|
+
mode={mode} // 'edit' | 'view'
|
|
219
|
+
values={record} // arrives async
|
|
220
|
+
onSubmit={save}
|
|
221
|
+
resolver={zodResolver(schema)}
|
|
222
|
+
defaultValues={DEFAULTS}
|
|
223
|
+
header={{ title: record?.name ?? "New", variant: mode === "view" ? "detail" : "edit" }}
|
|
224
|
+
>
|
|
225
|
+
…the same fields…
|
|
226
|
+
</FormRenderer>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 5. A form in a drawer
|
|
232
|
+
|
|
233
|
+
Set `display="drawer"` and drive it with `open` / `onOpenChange`. Pass the Save via `actions` —
|
|
234
|
+
it renders in the drawer header, with no manual `id` / `form={id}` wiring:
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
<FormRenderer<Values>
|
|
238
|
+
display="drawer"
|
|
239
|
+
open={open}
|
|
240
|
+
onOpenChange={setOpen}
|
|
241
|
+
header={{ title: "New item", label: "New", variant: "new" }}
|
|
242
|
+
onSubmit={save}
|
|
243
|
+
resolver={zodResolver(schema)}
|
|
244
|
+
defaultValues={DEFAULTS}
|
|
245
|
+
actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}
|
|
246
|
+
>
|
|
247
|
+
<FormBuilder.Section title="Identity" color="Blue">
|
|
248
|
+
…
|
|
249
|
+
</FormBuilder.Section>
|
|
250
|
+
</FormRenderer>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Inside a drawer the fields default to a vertical (narrow) layout automatically.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 6. The conclusion panel — live totals beside the form
|
|
258
|
+
|
|
259
|
+
`FormSummary` is a read-only panel of computed rows. Each `FormSummary.Row` declares a
|
|
260
|
+
**`compute(values)`** that runs against the **live** form values, so totals recalculate as the
|
|
261
|
+
user types. Pass it to `FormRenderer`'s `summary` prop.
|
|
262
|
+
|
|
263
|
+
Both the form and the panel must read the same values, so **hoist `useForm`** and hand the
|
|
264
|
+
instance to `FormRenderer` **and** `FormSummary` via their `form` prop:
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
"use client";
|
|
268
|
+
|
|
269
|
+
import { useForm } from "react-hook-form";
|
|
270
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
271
|
+
import { FormBuilder } from "@/components/FormBuilder";
|
|
272
|
+
import { FormRenderer } from "@/components/FormRenderer";
|
|
273
|
+
import { FormSummary } from "@/components/FormSummary";
|
|
274
|
+
|
|
275
|
+
// The calculations — plain functions of the form values. Keep them out of the JSX.
|
|
276
|
+
const subTotal = (v: Invoice) => v.items.reduce((s, i) => s + (i.qty ?? 0) * (i.price ?? 0), 0);
|
|
277
|
+
const totalDiscount = (v: Invoice) => v.items.reduce((s, i) => s + (i.discount ?? 0), 0);
|
|
278
|
+
const taxable = (v: Invoice) => Math.max(0, subTotal(v) - totalDiscount(v));
|
|
279
|
+
const totalTax = (v: Invoice) => taxable(v) * ((v.taxRate ?? 0) / 100);
|
|
280
|
+
const overallTotal = (v: Invoice) => taxable(v) + totalTax(v);
|
|
281
|
+
|
|
282
|
+
export function InvoiceForm({ save }: { save: (v: Invoice) => Promise<void> }) {
|
|
283
|
+
// Hoisted so both the form and the summary beside it share the same values.
|
|
284
|
+
const form = useForm<Invoice>({ resolver: zodResolver(schema), defaultValues: DEFAULTS });
|
|
285
|
+
|
|
286
|
+
return (
|
|
287
|
+
<FormRenderer<Invoice>
|
|
288
|
+
form={form}
|
|
289
|
+
onSubmit={save}
|
|
290
|
+
fieldDirection="vertical"
|
|
291
|
+
header={{ title: "Invoice", variant: "new" }}
|
|
292
|
+
actions={<FormBuilder.Submit>Save invoice</FormBuilder.Submit>}
|
|
293
|
+
summary={
|
|
294
|
+
<FormSummary form={form} title="Invoice" subtitle="Summary">
|
|
295
|
+
<FormSummary.Group title="Total">
|
|
296
|
+
<FormSummary.Row label="Total Discount" compute={totalDiscount} />
|
|
297
|
+
<FormSummary.Row label="Overall Total" emphasized compute={overallTotal} />
|
|
298
|
+
<FormSummary.Row
|
|
299
|
+
label="Overall Total"
|
|
300
|
+
currency="IQD"
|
|
301
|
+
tone="success"
|
|
302
|
+
decimals={0}
|
|
303
|
+
compute={(v) => overallTotal(v) * (v.iqdRate ?? 0)}
|
|
304
|
+
/>
|
|
305
|
+
<FormSummary.Row
|
|
306
|
+
label="Overall Total"
|
|
307
|
+
currency="USD"
|
|
308
|
+
tone="info"
|
|
309
|
+
compute={overallTotal}
|
|
310
|
+
/>
|
|
311
|
+
</FormSummary.Group>
|
|
312
|
+
|
|
313
|
+
<FormSummary.Group title="Tax">
|
|
314
|
+
<FormSummary.Row label="Sub Total" compute={subTotal} />
|
|
315
|
+
<FormSummary.Row label="Total Tax" compute={totalTax} />
|
|
316
|
+
</FormSummary.Group>
|
|
317
|
+
</FormSummary>
|
|
318
|
+
}
|
|
319
|
+
>
|
|
320
|
+
<FormBuilder.Section title="Line items" color="Green">
|
|
321
|
+
<FormBuilder.FieldArray
|
|
322
|
+
name="items"
|
|
323
|
+
label="Items"
|
|
324
|
+
addLabel="Add item"
|
|
325
|
+
defaultItem={{ name: "", qty: 1, price: 0, discount: 0 }}
|
|
326
|
+
>
|
|
327
|
+
{(rowName) => (
|
|
328
|
+
<>
|
|
329
|
+
<FormBuilder.Text name={`${rowName}.name`} label="Item" />
|
|
330
|
+
<FormBuilder.Number name={`${rowName}.qty`} label="Qty" />
|
|
331
|
+
<FormBuilder.Currency name={`${rowName}.price`} label="Price" currencySymbol="$" />
|
|
332
|
+
<FormBuilder.Currency
|
|
333
|
+
name={`${rowName}.discount`}
|
|
334
|
+
label="Discount"
|
|
335
|
+
currencySymbol="$"
|
|
336
|
+
/>
|
|
337
|
+
</>
|
|
338
|
+
)}
|
|
339
|
+
</FormBuilder.FieldArray>
|
|
340
|
+
</FormBuilder.Section>
|
|
341
|
+
|
|
342
|
+
<FormBuilder.Section title="Rates" color="Purple">
|
|
343
|
+
<FormBuilder.Number name="taxRate" label="Tax rate (%)" />
|
|
344
|
+
<FormBuilder.Number name="iqdRate" label="USD → IQD rate" />
|
|
345
|
+
</FormBuilder.Section>
|
|
346
|
+
</FormRenderer>
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Row options: `emphasized` (the primary result), `currency` + `tone`
|
|
352
|
+
(`'neutral' | 'success' | 'info'`), `decimals` (default `2` → `0.00`), `action` (a trailing
|
|
353
|
+
button), `format` (override the number formatting), `value` (a static row).
|
|
354
|
+
|
|
355
|
+
The panel is **read-only** — it contributes nothing to the submitted values. On a page it sits
|
|
356
|
+
beside the form (a `summary` **plus** a `FormBuilder.Stepper` becomes three columns: nav ·
|
|
357
|
+
fields · summary).
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## 7. Conclusion panel + drawer
|
|
362
|
+
|
|
363
|
+
The same `summary` prop works with `display="drawer"` — `FormRenderer` moves the panel into the
|
|
364
|
+
drawer's tray, beside the form:
|
|
365
|
+
|
|
366
|
+
```tsx
|
|
367
|
+
<FormRenderer<Invoice>
|
|
368
|
+
display="drawer"
|
|
369
|
+
open={open}
|
|
370
|
+
onOpenChange={setOpen}
|
|
371
|
+
form={form}
|
|
372
|
+
onSubmit={save}
|
|
373
|
+
header={{ title: "New item", badge: "New", variant: "new" }}
|
|
374
|
+
summary={
|
|
375
|
+
<FormSummary form={form} title="Item" subtitle="Summary">
|
|
376
|
+
<FormSummary.Group title="Pricing">
|
|
377
|
+
<FormSummary.Row label="Base price" compute={basePrice} />
|
|
378
|
+
<FormSummary.Row label="Overall Total" emphasized compute={overallTotal} />
|
|
379
|
+
</FormSummary.Group>
|
|
380
|
+
</FormSummary>
|
|
381
|
+
}
|
|
382
|
+
>
|
|
383
|
+
<FormBuilder.Section title="Identity" color="Blue">
|
|
384
|
+
…
|
|
385
|
+
</FormBuilder.Section>
|
|
386
|
+
</FormRenderer>
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Gotchas
|
|
392
|
+
|
|
393
|
+
- **Hoisting `useForm` disables the remount-`key` reset.** Once the form instance lives in
|
|
394
|
+
your component, a changing `key` no longer resets it. Call `form.reset(DEFAULTS)` instead
|
|
395
|
+
(e.g. when opening a drawer).
|
|
396
|
+
- **`FormSummary` needs the same hoisted `form`** — that's the react-hook-form context it reads
|
|
397
|
+
live values from. Pass the identical instance to `FormRenderer` and `FormSummary`.
|
|
398
|
+
- **Number/currency values are real numbers**, not strings — `onSubmit` receives
|
|
399
|
+
`price: 1299`, not `"1299"`.
|
|
400
|
+
- **`required` is cosmetic.** It renders the "(Required)" tag; actual enforcement is your
|
|
401
|
+
resolver — keep the two in sync.
|
|
402
|
+
- **Never use `variant="SystemStyle"` or `*-system-*` tokens** in app code.
|
|
403
|
+
|
|
404
|
+
## Related
|
|
405
|
+
|
|
406
|
+
- [FormBuilder](../components/form-builder.md) — every field type and its value shape
|
|
407
|
+
- [FormRenderer](../components/form-renderer.md) — display, header, `actions`, `summary`
|
|
408
|
+
- [FormSummary](../components/form-summary.md) — the calculation panel
|