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.
Files changed (318) hide show
  1. package/README.md +8 -0
  2. package/apps/lib/components/ActionButton.tsx +7 -12
  3. package/apps/lib/components/ActionsGroup.tsx +4 -15
  4. package/apps/lib/components/AlertDialog.tsx +173 -191
  5. package/apps/lib/components/Avatar.tsx +13 -16
  6. package/apps/lib/components/Badge.tsx +1 -3
  7. package/apps/lib/components/BadgeField.tsx +76 -71
  8. package/apps/lib/components/Breadcrumb.tsx +49 -85
  9. package/apps/lib/components/Button.tsx +21 -20
  10. package/apps/lib/components/ButtonGroup.tsx +31 -41
  11. package/apps/lib/components/Calendar.tsx +51 -29
  12. package/apps/lib/components/Card.tsx +40 -24
  13. package/apps/lib/components/Checkbox.tsx +9 -16
  14. package/apps/lib/components/ColorPicker.tsx +441 -0
  15. package/apps/lib/components/ConclusionHeader.tsx +148 -0
  16. package/apps/lib/components/ContextMenu.tsx +97 -96
  17. package/apps/lib/components/CountBadge.tsx +4 -6
  18. package/apps/lib/components/DataTable.tsx +196 -216
  19. package/apps/lib/components/DataViews/DataViewRadio.tsx +1 -6
  20. package/apps/lib/components/DataViews/DataViewsConfigPanel.tsx +13 -47
  21. package/apps/lib/components/DataViews/DataViewsHeader.tsx +1 -3
  22. package/apps/lib/components/DataViews/DataViewsLayout.tsx +5 -12
  23. package/apps/lib/components/DataViews/FilterPanel.tsx +155 -149
  24. package/apps/lib/components/DataViews/HeaderSearch.tsx +1 -5
  25. package/apps/lib/components/DataViews/InboxView.tsx +22 -68
  26. package/apps/lib/components/DataViews/InboxViewCard.tsx +68 -77
  27. package/apps/lib/components/DataViews/KanbanView.tsx +8 -25
  28. package/apps/lib/components/DataViews/PanelControls.tsx +3 -13
  29. package/apps/lib/components/DataViews/SettingsPanel.tsx +83 -89
  30. package/apps/lib/components/DataViews/TableView.tsx +8 -28
  31. package/apps/lib/components/DataViews/TreeView.tsx +12 -40
  32. package/apps/lib/components/DataViews/badgeAdapter.ts +32 -28
  33. package/apps/lib/components/DataViews/fieldRenderers.tsx +106 -141
  34. package/apps/lib/components/DataViews/filters/DatePickerRangeFilter.tsx +30 -23
  35. package/apps/lib/components/DataViews/filters/DateRangePopover.tsx +39 -32
  36. package/apps/lib/components/DataViews/filters/PresetChips.tsx +15 -15
  37. package/apps/lib/components/DataViews/filters/RangeSliderWithInputs.tsx +69 -58
  38. package/apps/lib/components/DataViews/index.ts +26 -26
  39. package/apps/lib/components/DataViews/tree/TreeDrawer.tsx +12 -16
  40. package/apps/lib/components/DataViews/tree/TreeSidebar.tsx +28 -31
  41. package/apps/lib/components/DataViews/types.ts +104 -115
  42. package/apps/lib/components/DatePicker.tsx +200 -196
  43. package/apps/lib/components/Dialog.tsx +92 -105
  44. package/apps/lib/components/Divider.tsx +21 -24
  45. package/apps/lib/components/Drawer.tsx +124 -142
  46. package/apps/lib/components/DropdownMenu.tsx +131 -171
  47. package/apps/lib/components/FieldHint.tsx +4 -11
  48. package/apps/lib/components/Form.tsx +146 -155
  49. package/apps/lib/components/FormBuilder/DisplayField.tsx +40 -0
  50. package/apps/lib/components/FormBuilder/context.ts +71 -0
  51. package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +65 -0
  52. package/apps/lib/components/FormBuilder/fields/ColorField.tsx +76 -0
  53. package/apps/lib/components/FormBuilder/fields/CustomField.tsx +16 -0
  54. package/apps/lib/components/FormBuilder/fields/DateField.tsx +34 -0
  55. package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +79 -0
  56. package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +174 -0
  57. package/apps/lib/components/FormBuilder/fields/FileField.tsx +40 -0
  58. package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +139 -0
  59. package/apps/lib/components/FormBuilder/fields/OtpField.tsx +32 -0
  60. package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +82 -0
  61. package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +37 -0
  62. package/apps/lib/components/FormBuilder/fields/SelectField.tsx +101 -0
  63. package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +175 -0
  64. package/apps/lib/components/FormBuilder/fields/SliderField.tsx +72 -0
  65. package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +42 -0
  66. package/apps/lib/components/FormBuilder/fields/TableField.tsx +312 -0
  67. package/apps/lib/components/FormBuilder/fields/TextField.tsx +222 -0
  68. package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +49 -0
  69. package/apps/lib/components/FormBuilder/fields/countries.ts +303 -0
  70. package/apps/lib/components/FormBuilder/fields/index.ts +25 -0
  71. package/apps/lib/components/FormBuilder/form-builder.tsx +292 -0
  72. package/apps/lib/components/FormBuilder/header.tsx +105 -0
  73. package/apps/lib/components/FormBuilder/index.ts +37 -0
  74. package/apps/lib/components/FormBuilder/numberFormat.ts +16 -0
  75. package/apps/lib/components/FormBuilder/stepper.tsx +217 -0
  76. package/apps/lib/components/FormBuilder/submit.tsx +41 -0
  77. package/apps/lib/components/FormBuilder/types.ts +264 -0
  78. package/apps/lib/components/FormBuilder/viewFormat.tsx +137 -0
  79. package/apps/lib/components/FormRenderer/FormDrawer.tsx +121 -0
  80. package/apps/lib/components/FormRenderer/form-renderer.tsx +113 -0
  81. package/apps/lib/components/FormRenderer/index.ts +9 -0
  82. package/apps/lib/components/FormRenderer/types.ts +79 -0
  83. package/apps/lib/components/FormStepper.tsx +9 -39
  84. package/apps/lib/components/FormSummary.tsx +282 -0
  85. package/apps/lib/components/HeaderBar.tsx +1 -9
  86. package/apps/lib/components/ImageAttachment.tsx +106 -93
  87. package/apps/lib/components/InnerLabelField.tsx +20 -16
  88. package/apps/lib/components/Input.tsx +171 -172
  89. package/apps/lib/components/InputField.tsx +44 -45
  90. package/apps/lib/components/InputOTP.tsx +68 -69
  91. package/apps/lib/components/Label.tsx +61 -58
  92. package/apps/lib/components/LabelField.tsx +4 -5
  93. package/apps/lib/components/LabeledCheckBox.tsx +8 -12
  94. package/apps/lib/components/LabeledRadio.tsx +7 -5
  95. package/apps/lib/components/LinkButton.tsx +2 -9
  96. package/apps/lib/components/LoginButton.tsx +4 -9
  97. package/apps/lib/components/PasswordLevel.tsx +5 -5
  98. package/apps/lib/components/Popover.tsx +15 -33
  99. package/apps/lib/components/ProfileMenu.tsx +67 -72
  100. package/apps/lib/components/Radio.tsx +1 -5
  101. package/apps/lib/components/RadioCard.tsx +11 -28
  102. package/apps/lib/components/ScrollArea.tsx +45 -43
  103. package/apps/lib/components/SearchField.tsx +49 -39
  104. package/apps/lib/components/SearchableSelect.tsx +16 -16
  105. package/apps/lib/components/SearchableTable.tsx +108 -118
  106. package/apps/lib/components/SearchableTree.tsx +469 -0
  107. package/apps/lib/components/SearchableTreeDialog.tsx +493 -0
  108. package/apps/lib/components/SectionBlock.tsx +19 -23
  109. package/apps/lib/components/Select.tsx +80 -186
  110. package/apps/lib/components/SimpleSelect.tsx +196 -187
  111. package/apps/lib/components/Skeleton.tsx +9 -12
  112. package/apps/lib/components/SlideDatePicker.tsx +215 -166
  113. package/apps/lib/components/SpinLoading.tsx +33 -49
  114. package/apps/lib/components/Stepper.tsx +13 -56
  115. package/apps/lib/components/Switch.tsx +8 -10
  116. package/apps/lib/components/TabFormItem.tsx +3 -12
  117. package/apps/lib/components/TabSwitch.tsx +7 -34
  118. package/apps/lib/components/Table.tsx +60 -75
  119. package/apps/lib/components/{ChartBlockTool.ts → TextEditor/ChartBlockTool.ts} +17 -52
  120. package/apps/lib/components/TextEditor/RichTextField.tsx +46 -0
  121. package/apps/lib/components/{TableDnDWrapper.ts → TextEditor/TableDnDWrapper.ts} +15 -48
  122. package/apps/lib/components/{TextEditor.tsx → TextEditor/TextEditor.tsx} +156 -113
  123. package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +429 -0
  124. package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +70 -0
  125. package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +50 -0
  126. package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +48 -0
  127. package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +98 -0
  128. package/apps/lib/{types → components/TextEditor}/editorjs.d.ts +19 -0
  129. package/apps/lib/components/TextEditor/index.ts +7 -0
  130. package/apps/lib/{utils → components/TextEditor}/markdownParser.ts +2 -7
  131. package/apps/lib/components/Textarea.tsx +9 -13
  132. package/apps/lib/components/Timeline.tsx +21 -41
  133. package/apps/lib/components/Toast.tsx +83 -83
  134. package/apps/lib/components/Toggle.tsx +112 -114
  135. package/apps/lib/components/ToggleButton.tsx +1 -1
  136. package/apps/lib/components/Tooltip.tsx +24 -19
  137. package/apps/lib/components/TransparentLabel.tsx +61 -62
  138. package/apps/lib/components/TreeDropDown.tsx +86 -52
  139. package/apps/lib/components/TreeFolder/TreeFolder.tsx +15 -48
  140. package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +16 -21
  141. package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +9 -20
  142. package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +3 -3
  143. package/apps/lib/components/TreeFolder/icons.tsx +14 -17
  144. package/apps/lib/components/TreeFolder/index.ts +15 -8
  145. package/apps/lib/components/TreeFolder/treeFolderUtils.ts +47 -52
  146. package/apps/lib/components/TreeFolder/types.ts +32 -32
  147. package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +117 -123
  148. package/apps/lib/hooks/useActiveTreeItem.ts +52 -55
  149. package/apps/lib/hooks/useClickOutside.ts +21 -20
  150. package/apps/lib/hooks/useDataViewsState.ts +86 -80
  151. package/apps/lib/hooks/useIsMobile.ts +12 -12
  152. package/apps/lib/hooks/useResize.ts +54 -65
  153. package/apps/lib/hooks/useTagSelection.ts +201 -201
  154. package/apps/lib/layouts/CNLayout.tsx +181 -140
  155. package/apps/lib/layouts/DataViewCard.tsx +4 -18
  156. package/apps/lib/layouts/FieldSection.tsx +2 -6
  157. package/apps/lib/layouts/TreeSubLayout.tsx +24 -38
  158. package/apps/lib/providers/ThemeProvider.tsx +8 -23
  159. package/apps/lib/registry.json +997 -0
  160. package/apps/lib/tsconfig.json +9 -0
  161. package/apps/lib/tsconfig.tsbuildinfo +1 -0
  162. package/apps/lib/utils/cn.ts +1 -1
  163. package/apps/lib/utils/color.ts +175 -0
  164. package/apps/lib/utils/dataViews/columnUtils.ts +46 -44
  165. package/apps/lib/utils/dataViews/fieldUtils.ts +70 -71
  166. package/apps/lib/utils/dataViews/nestedDataUtils.tsx +129 -122
  167. package/apps/lib/utils/dataViews/pathUtils.ts +74 -67
  168. package/apps/lib/utils/dataViews/rangeUtils.ts +121 -112
  169. package/apps/lib/utils/dataViews/treeUtils.ts +204 -211
  170. package/apps/lib/utils/dateFormat.ts +64 -54
  171. package/apps/lib/utils/resize.ts +21 -25
  172. package/apps/lib/utils/types.ts +13 -13
  173. package/dist/bin/index.js +8 -2
  174. package/dist/bin/index.js.map +1 -1
  175. package/dist/src/shared/getAvailableFiles.d.ts.map +1 -1
  176. package/dist/src/shared/getAvailableFiles.js +6 -1
  177. package/dist/src/shared/getAvailableFiles.js.map +1 -1
  178. package/dist/src/shared/loadRegistry.d.ts +7 -0
  179. package/dist/src/shared/loadRegistry.d.ts.map +1 -0
  180. package/dist/src/shared/loadRegistry.js +31 -0
  181. package/dist/src/shared/loadRegistry.js.map +1 -0
  182. package/dist/src/shared/resolveInstallPlan.d.ts +12 -0
  183. package/dist/src/shared/resolveInstallPlan.d.ts.map +1 -0
  184. package/dist/src/shared/resolveInstallPlan.js +39 -0
  185. package/dist/src/shared/resolveInstallPlan.js.map +1 -0
  186. package/dist/src/types/main.d.ts +24 -0
  187. package/dist/src/types/main.d.ts.map +1 -1
  188. package/docs/README.md +10 -2
  189. package/docs/components/action-button.md +30 -24
  190. package/docs/components/actions-group.md +26 -20
  191. package/docs/components/alert-dialog.md +45 -45
  192. package/docs/components/avatar.md +6 -2
  193. package/docs/components/badge-field.md +6 -2
  194. package/docs/components/badge.md +7 -1
  195. package/docs/components/breadcrumb.md +15 -40
  196. package/docs/components/button-group.md +13 -10
  197. package/docs/components/button.md +58 -33
  198. package/docs/components/calendar.md +26 -19
  199. package/docs/components/card.md +23 -18
  200. package/docs/components/chart-block-tool.md +129 -0
  201. package/docs/components/checkbox.md +16 -8
  202. package/docs/components/cn-layout.md +7 -1
  203. package/docs/components/color-picker.md +101 -0
  204. package/docs/components/conclusion-header.md +80 -0
  205. package/docs/components/context-menu.md +18 -25
  206. package/docs/components/count-badge.md +7 -1
  207. package/docs/components/data-table.md +45 -33
  208. package/docs/components/data-views-config-panel.md +15 -11
  209. package/docs/components/data-views-layout.md +13 -16
  210. package/docs/components/date-picker.md +30 -23
  211. package/docs/components/dialog.md +43 -42
  212. package/docs/components/divider.md +10 -4
  213. package/docs/components/drawer.md +161 -135
  214. package/docs/components/dropdown-menu.md +25 -34
  215. package/docs/components/field-hint.md +37 -25
  216. package/docs/components/field-section.md +9 -3
  217. package/docs/components/form-builder.md +270 -0
  218. package/docs/components/form-renderer.md +228 -0
  219. package/docs/components/form-stepper.md +6 -0
  220. package/docs/components/form-summary.md +123 -0
  221. package/docs/components/form.md +17 -20
  222. package/docs/components/header-bar.md +6 -0
  223. package/docs/components/image-attachment.md +43 -45
  224. package/docs/components/inbox-view.md +16 -5
  225. package/docs/components/inner-label-field.md +34 -24
  226. package/docs/components/input-field.md +18 -10
  227. package/docs/components/input-otp.md +14 -11
  228. package/docs/components/input.md +22 -16
  229. package/docs/components/kanban-view.md +15 -5
  230. package/docs/components/label-field.md +32 -22
  231. package/docs/components/label.md +43 -32
  232. package/docs/components/labeled-check-box.md +16 -8
  233. package/docs/components/labeled-radio.md +18 -11
  234. package/docs/components/link-button.md +7 -1
  235. package/docs/components/login-button.md +8 -2
  236. package/docs/components/password-level.md +37 -24
  237. package/docs/components/popover.md +26 -23
  238. package/docs/components/profile-menu.md +38 -27
  239. package/docs/components/radio-card.md +16 -9
  240. package/docs/components/radio.md +16 -8
  241. package/docs/components/scroll-area.md +6 -2
  242. package/docs/components/search-field.md +14 -6
  243. package/docs/components/searchable-select.md +19 -14
  244. package/docs/components/searchable-table.md +18 -13
  245. package/docs/components/searchable-tree-dialog.md +190 -0
  246. package/docs/components/searchable-tree.md +200 -0
  247. package/docs/components/section-block.md +12 -4
  248. package/docs/components/select.md +23 -18
  249. package/docs/components/simple-select.md +14 -10
  250. package/docs/components/skeleton.md +43 -33
  251. package/docs/components/slide-date-picker.md +37 -29
  252. package/docs/components/spin-loading.md +32 -23
  253. package/docs/components/stepper.md +6 -0
  254. package/docs/components/switch.md +14 -6
  255. package/docs/components/tab-form-item.md +7 -1
  256. package/docs/components/tab-switch.md +13 -6
  257. package/docs/components/table-dnd-wrapper.md +107 -0
  258. package/docs/components/table-view.md +15 -5
  259. package/docs/components/table.md +28 -28
  260. package/docs/components/text-editor.md +37 -13
  261. package/docs/components/textarea.md +15 -7
  262. package/docs/components/timeline.md +6 -0
  263. package/docs/components/toast.md +35 -26
  264. package/docs/components/toggle-button.md +24 -21
  265. package/docs/components/toggle.md +23 -15
  266. package/docs/components/tooltip.md +42 -30
  267. package/docs/components/transparent-label.md +29 -20
  268. package/docs/components/tree-drop-down.md +31 -23
  269. package/docs/components/tree-sub-layout.md +6 -2
  270. package/docs/components/tree-view.md +16 -5
  271. package/docs/explanation/architecture.md +54 -0
  272. package/docs/explanation/design-system.md +52 -0
  273. package/docs/how-to/data-views-from-backend-response.md +6 -4
  274. package/docs/how-to/form-and-list-recipes.md +26 -19
  275. package/docs/how-to/forms-with-form-builder.md +408 -0
  276. package/docs/how-to/guides.md +177 -197
  277. package/docs/migration/changelog.md +38 -0
  278. package/docs/reference/cli.md +87 -0
  279. package/docs/reference/components.md +11 -12
  280. package/docs/reference/hooks.md +8 -8
  281. package/docs/reference/providers.md +22 -22
  282. package/docs/reference/tailwind-plugins.md +2 -2
  283. package/docs/reference/theme.md +85 -0
  284. package/docs/reference/types.md +4 -4
  285. package/docs/reference/utilities.md +19 -26
  286. package/docs/tutorials/building-first-form.md +170 -172
  287. package/docs/tutorials/component-composition.md +34 -50
  288. package/docs/tutorials/getting-started.md +2 -2
  289. package/docs/tutorials/theming-basics.md +13 -10
  290. package/package.json +31 -3
  291. package/dist/src/commands/block.d.ts +0 -5
  292. package/dist/src/commands/block.d.ts.map +0 -1
  293. package/dist/src/commands/block.js +0 -255
  294. package/dist/src/commands/block.js.map +0 -1
  295. package/dist/src/shared/configureFonts.d.ts +0 -6
  296. package/dist/src/shared/configureFonts.d.ts.map +0 -1
  297. package/dist/src/shared/configureFonts.js +0 -106
  298. package/dist/src/shared/configureFonts.js.map +0 -1
  299. package/dist/src/shared/configureGlobalCss.d.ts +0 -6
  300. package/dist/src/shared/configureGlobalCss.d.ts.map +0 -1
  301. package/dist/src/shared/configureGlobalCss.js +0 -154
  302. package/dist/src/shared/configureGlobalCss.js.map +0 -1
  303. package/dist/src/shared/configureTailwind.d.ts +0 -7
  304. package/dist/src/shared/configureTailwind.d.ts.map +0 -1
  305. package/dist/src/shared/configureTailwind.js +0 -163
  306. package/dist/src/shared/configureTailwind.js.map +0 -1
  307. package/dist/src/shared/detectFramework.d.ts +0 -23
  308. package/dist/src/shared/detectFramework.d.ts.map +0 -1
  309. package/dist/src/shared/detectFramework.js +0 -119
  310. package/dist/src/shared/detectFramework.js.map +0 -1
  311. package/dist/src/shared/installBaseUtils.d.ts +0 -5
  312. package/dist/src/shared/installBaseUtils.d.ts.map +0 -1
  313. package/dist/src/shared/installBaseUtils.js +0 -79
  314. package/dist/src/shared/installBaseUtils.js.map +0 -1
  315. package/dist/src/shared/resolveAliases.d.ts +0 -24
  316. package/dist/src/shared/resolveAliases.d.ts.map +0 -1
  317. package/dist/src/shared/resolveAliases.js +0 -98
  318. 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