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,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: FormRenderer
|
|
3
|
+
description: A thin wrapper around FormBuilder. Author fields as JSX children; FormRenderer owns page-vs-drawer display, the absolute title header, and drawer field layout. You compose the Save and pass it via the actions prop.
|
|
4
|
+
component: true
|
|
5
|
+
group: Forms
|
|
6
|
+
keywords: [form-renderer, form, drawer, header, display, view-mode, react-hook-form]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# FormRenderer
|
|
10
|
+
|
|
11
|
+
A thin wrapper around the compound [FormBuilder](./form-builder.md). You author the
|
|
12
|
+
fields as **JSX children** exactly as you would with `FormBuilder`; `FormRenderer`
|
|
13
|
+
takes care of the surrounding concerns:
|
|
14
|
+
|
|
15
|
+
- **page vs drawer** display (`display="drawer"` hosts the form in a `FormDrawer`),
|
|
16
|
+
- the **absolute title header** + action bar (page display),
|
|
17
|
+
- **vertical field layout** inside a drawer.
|
|
18
|
+
|
|
19
|
+
FormRenderer never manufactures a Submit — **you compose the Save and hand it to `actions`**.
|
|
20
|
+
It renders in the form's header action pill (page) or the drawer header (drawer), and a bare
|
|
21
|
+
`<FormBuilder.Submit>` auto-targets this form (even though the header sits outside the `<form>`).
|
|
22
|
+
|
|
23
|
+
Reach for `FormBuilder` directly when you just want the form. Reach for
|
|
24
|
+
`FormRenderer` when you want the same form to render as a page _or_ a drawer with
|
|
25
|
+
the standard chrome.
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<FormRenderer
|
|
29
|
+
onSubmit={save}
|
|
30
|
+
resolver={zodResolver(schema)}
|
|
31
|
+
defaultValues={d}
|
|
32
|
+
header={{ title: "Item", variant: "new" }}
|
|
33
|
+
actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}
|
|
34
|
+
>
|
|
35
|
+
<FormBuilder.Section title="Identity" color="Blue">
|
|
36
|
+
<FormBuilder.Text name="name" label="Name" required />
|
|
37
|
+
<FormBuilder.Currency name="price" label="Price" currencySymbol="$" />
|
|
38
|
+
</FormBuilder.Section>
|
|
39
|
+
</FormRenderer>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npx torch-glare@latest init
|
|
46
|
+
npx torch-glare@latest add FormRenderer
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`add` also pulls in `FormBuilder`, which FormRenderer renders through.
|
|
50
|
+
|
|
51
|
+
## Imports
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { FormRenderer, FormDrawer } from "@/components/FormRenderer";
|
|
55
|
+
import { FormBuilder } from "@/components/FormBuilder";
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Props
|
|
59
|
+
|
|
60
|
+
| Prop | Type | Notes |
|
|
61
|
+
| -------------------------------------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| `children` | `ReactNode` | The form body — `FormBuilder.Section` / field / `FormBuilder.Stepper` JSX. |
|
|
63
|
+
| `onSubmit` / `onInvalid` | fns | Submit / validation-fail callbacks. |
|
|
64
|
+
| `resolver` | `Resolver` | Any react-hook-form resolver, e.g. `zodResolver(schema)`. |
|
|
65
|
+
| `defaultValues` / `values` | `DefaultValues` / `T` | Initial values; `values` re-syncs on change (edit). |
|
|
66
|
+
| `mode` | `'edit' \| 'view'` | `'view'` renders read-only, no Submit. |
|
|
67
|
+
| `loading` / `resetOnSuccess` | `boolean` | Forwarded to `FormBuilder`. |
|
|
68
|
+
| `fieldDirection` | `'horizontal' \| 'vertical'` | Defaults to vertical inside a drawer. |
|
|
69
|
+
| `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. |
|
|
70
|
+
| `display` | `'page' \| 'drawer'` | `'drawer'` wraps the form in `FormDrawer`. |
|
|
71
|
+
| `header` | `{ title; label?; variant? }` | Absolute title header + action bar (page display); `actions` render in it. |
|
|
72
|
+
| `summary` | `ReactNode` | A live panel (typically `FormSummary`) rendered beside the form (page) or in the drawer tray (drawer). Give the same hoisted `form` so it reads live values. On a page, a `summary` **plus** a `FormBuilder.Stepper` lays out as three columns — stepper nav · fields · summary. |
|
|
73
|
+
| `actions` | `ReactNode` | The form's action bar — rendered in the header action pill (page) or drawer header (drawer). Put the Save here: `actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}`. A bare `FormBuilder.Submit` auto-targets this form. |
|
|
74
|
+
| `id` | `string` | `id` on the underlying `<form>`. Optional — FormRenderer generates and wires one otherwise. |
|
|
75
|
+
| `open` / `onOpenChange` / `title` / `badge` / `onOpenInNewTab` | — | Drawer control (when `display="drawer"`). `title` / `badge` are strings that override `header.title` / `header.label`. |
|
|
76
|
+
|
|
77
|
+
## Drawer & view
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
// drawer — pass the Save via `actions`; it renders in the drawer header
|
|
81
|
+
<FormRenderer
|
|
82
|
+
display="drawer" open={open} onOpenChange={setOpen} title="New item" badge="New"
|
|
83
|
+
onSubmit={save} resolver={r} defaultValues={d}
|
|
84
|
+
actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}
|
|
85
|
+
>
|
|
86
|
+
{fields}
|
|
87
|
+
</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
|
+
```
|
|
94
|
+
|
|
95
|
+
## Stepper
|
|
96
|
+
|
|
97
|
+
Drop a `FormBuilder.Stepper` in as the child. The Save lives in the header `actions` and
|
|
98
|
+
submits every step's registered fields at once (steps stay mounted, so the whole form is live).
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<FormRenderer
|
|
102
|
+
onSubmit={save}
|
|
103
|
+
resolver={r}
|
|
104
|
+
defaultValues={d}
|
|
105
|
+
header={{ title: "New item", variant: "new" }}
|
|
106
|
+
actions={<FormBuilder.Submit>Save</FormBuilder.Submit>}
|
|
107
|
+
>
|
|
108
|
+
<FormBuilder.Stepper>
|
|
109
|
+
<FormBuilder.Step title="Basics">
|
|
110
|
+
<FormBuilder.Text name="name" label="Name" required />
|
|
111
|
+
</FormBuilder.Step>
|
|
112
|
+
</FormBuilder.Stepper>
|
|
113
|
+
</FormRenderer>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Summary panel
|
|
117
|
+
|
|
118
|
+
Pass a hoisted `useForm` as `form` and a `FormSummary` as `summary` — FormRenderer binds
|
|
119
|
+
the form to that instance and lays the panel beside the form (page) or in the drawer tray
|
|
120
|
+
(drawer), so it recomputes live as fields change.
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
const form = useForm<Invoice>({ resolver, defaultValues })
|
|
124
|
+
|
|
125
|
+
<FormRenderer
|
|
126
|
+
form={form}
|
|
127
|
+
onSubmit={save}
|
|
128
|
+
header={{ title: 'Invoice', variant: 'new' }}
|
|
129
|
+
actions={<FormBuilder.Submit>Save invoice</FormBuilder.Submit>}
|
|
130
|
+
summary={
|
|
131
|
+
<FormSummary form={form} title="Invoice" subtitle="Summary">
|
|
132
|
+
<FormSummary.Group title="Total">
|
|
133
|
+
<FormSummary.Row label="Overall Total" emphasized compute={overallTotal} />
|
|
134
|
+
</FormSummary.Group>
|
|
135
|
+
</FormSummary>
|
|
136
|
+
}
|
|
137
|
+
>
|
|
138
|
+
{fields}
|
|
139
|
+
</FormRenderer>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Works with `display="drawer"` too — the summary moves into the drawer's tray.
|
|
143
|
+
|
|
144
|
+
## `FormDrawer` — the drawer wrapper
|
|
145
|
+
|
|
146
|
+
`FormRenderer` uses `FormDrawer` internally for `display: 'drawer'`. It's also exported
|
|
147
|
+
so you can put a hand-composed `FormBuilder` in a drawer directly. Since the header sits
|
|
148
|
+
outside the `<form>`, wire the Save button to the form via `id` / `form={id}`:
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
<FormDrawer
|
|
152
|
+
open={open}
|
|
153
|
+
onOpenChange={setOpen}
|
|
154
|
+
title="New item"
|
|
155
|
+
badge="New"
|
|
156
|
+
actions={
|
|
157
|
+
<Button type="submit" form="item-form" is_loading={saving}>
|
|
158
|
+
Save
|
|
159
|
+
</Button>
|
|
160
|
+
}
|
|
161
|
+
>
|
|
162
|
+
<FormBuilder
|
|
163
|
+
id="item-form"
|
|
164
|
+
onSubmit={save}
|
|
165
|
+
resolver={r}
|
|
166
|
+
defaultValues={d}
|
|
167
|
+
fieldDirection="vertical"
|
|
168
|
+
>
|
|
169
|
+
…fields…
|
|
170
|
+
</FormBuilder>
|
|
171
|
+
</FormDrawer>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`FormDrawer` props: `open`, `onOpenChange`, `title`, `badge`, `variant`, `actions`,
|
|
175
|
+
`onOpenInNewTab`, `children`, `summary`. It owns no form state.
|
|
176
|
+
|
|
177
|
+
### The title
|
|
178
|
+
|
|
179
|
+
The drawer renders the **same floating header as the page form** — a [HeaderBar](./header-bar.md)
|
|
180
|
+
title pill on the left, a dark action pill on the right — so a form's title looks identical in
|
|
181
|
+
either display. `title` is the plain (uppercased) text, `badge` the colored emphasis pill, and
|
|
182
|
+
`variant` (`new` / `edit` / `detail`) picks the badge colors; `badge` defaults from it
|
|
183
|
+
(New / Edit / View).
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
<FormDrawer title="Sales Invoice" badge="New" variant="new" … />
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Driving it through `FormRenderer`, the `header` prop feeds both displays — including
|
|
190
|
+
`header.variant`, which the drawer previously ignored:
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
<FormRenderer display="drawer" header={{ title: "Sales Invoice", variant: "edit" }} … />
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`title` and `badge` are **plain strings** (they were `ReactNode` before this shared-header
|
|
197
|
+
change) because they render through `HeaderBar`'s uppercase text treatment.
|
|
198
|
+
|
|
199
|
+
### A summary beside the form
|
|
200
|
+
|
|
201
|
+
`summary` renders a conclusion panel — typically a [FormSummary](./form-summary.md) — _beside_
|
|
202
|
+
the form rather than inside its scrollable body. `FormDrawer` puts the form in a
|
|
203
|
+
[`DrawerPanel`](./drawer.md#drawerpanel) (the light surface) and the summary next to it with a
|
|
204
|
+
6px gutter; the tray paints nothing, so the summary keeps its own dark background and full
|
|
205
|
+
height, scrolling internally when it outruns the drawer.
|
|
206
|
+
|
|
207
|
+
Hoist the form so both read the same instance:
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
const form = useForm({ resolver, defaultValues })
|
|
211
|
+
|
|
212
|
+
<FormDrawer
|
|
213
|
+
open={open} onOpenChange={setOpen} title="New item" badge="New"
|
|
214
|
+
summary={
|
|
215
|
+
<FormSummary form={form} title="Item" subtitle="Summary">
|
|
216
|
+
<FormSummary.Group title="Pricing">…rows…</FormSummary.Group>
|
|
217
|
+
</FormSummary>
|
|
218
|
+
}
|
|
219
|
+
>
|
|
220
|
+
<FormBuilder form={form} id="item-form" onSubmit={save} fieldDirection="vertical">
|
|
221
|
+
…fields…
|
|
222
|
+
</FormBuilder>
|
|
223
|
+
</FormDrawer>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
> `childrenOutside` is the deprecated former name for `summary`. It still works.
|
|
227
|
+
|
|
228
|
+
`FormRenderer` does **not** forward `summary` — use `FormDrawer` directly when you need one.
|
|
@@ -14,10 +14,16 @@ The component is composed of `FormStepper`, `FormStep`, `FormStepIndicator`, and
|
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
17
|
+
TORCH Glare is a copy-in library: the CLI copies this component's source into your project
|
|
18
|
+
(you do **not** install it from the npm package). Run `init` once, then `add`:
|
|
19
|
+
|
|
17
20
|
```bash
|
|
21
|
+
npx torch-glare@latest init
|
|
18
22
|
npx torch-glare@latest add FormStepper
|
|
19
23
|
```
|
|
20
24
|
|
|
25
|
+
`add` also copies any components, hooks, and utilities that `FormStepper` depends on.
|
|
26
|
+
|
|
21
27
|
## Imports
|
|
22
28
|
|
|
23
29
|
```typescript
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: FormSummary
|
|
3
|
+
description: A read-only calculation panel displayed beside a form. Each row declares a compute(values) that runs against the live form values, so totals recalculate as the user types.
|
|
4
|
+
component: true
|
|
5
|
+
group: Forms
|
|
6
|
+
keywords: [form-summary, summary, totals, calculation, invoice, panel, react-hook-form, useWatch]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# FormSummary
|
|
10
|
+
|
|
11
|
+
A read-only **calculation panel** that sits **beside a form** — the invoice-summary
|
|
12
|
+
look: grouped rows of computed totals, right-aligned, in Glare's dark `SystemStyle`
|
|
13
|
+
fields.
|
|
14
|
+
|
|
15
|
+
Each `FormSummary.Row` declares a **`compute(values)`** that runs against the **live**
|
|
16
|
+
form values, so every total recalculates as the user types.
|
|
17
|
+
|
|
18
|
+
The panel renders **outside** the form, beside it. Both read the same form, so
|
|
19
|
+
**hoist `useForm`** and hand the instance to each:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
const form = useForm({ resolver: zodResolver(schema), defaultValues })
|
|
23
|
+
|
|
24
|
+
<div className="flex gap-4">
|
|
25
|
+
<FormBuilder form={form} onSubmit={save} className="min-w-0 flex-1">
|
|
26
|
+
<FormBuilder.Section title="Line items">…fields…</FormBuilder.Section>
|
|
27
|
+
</FormBuilder>
|
|
28
|
+
|
|
29
|
+
<FormSummary form={form} title="Invoice" subtitle="Summary">
|
|
30
|
+
<FormSummary.Group title="Total">
|
|
31
|
+
<FormSummary.Row label="Total Discount" compute={totalDiscount} />
|
|
32
|
+
<FormSummary.Row label="Overall Total" emphasized compute={overallTotal} />
|
|
33
|
+
<FormSummary.Row label="Overall Total" currency="IQD" tone="success"
|
|
34
|
+
compute={v => overallTotal(v) * v.iqdRate} decimals={0} />
|
|
35
|
+
<FormSummary.Row label="Overall Total" currency="USD" tone="info" compute={overallTotal} />
|
|
36
|
+
</FormSummary.Group>
|
|
37
|
+
</FormSummary>
|
|
38
|
+
</div>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`FormBuilder` stays purely an input renderer — it knows nothing about the panel, and
|
|
42
|
+
**you** own the side-by-side layout. Passing `form` to `FormBuilder` makes it use that
|
|
43
|
+
instance instead of creating its own.
|
|
44
|
+
|
|
45
|
+
### Height
|
|
46
|
+
|
|
47
|
+
The panel is `h-full` and fills whatever height its parent gives it, scrolling its groups
|
|
48
|
+
(header pinned) once they outrun it. That means **the parent has to have a height** — the
|
|
49
|
+
usual mistake is `items-start` on the flex row, which collapses the panel to its content
|
|
50
|
+
instead. Leave the row at its default `items-stretch`, as above.
|
|
51
|
+
|
|
52
|
+
To make the panel own the viewport rather than grow with the form, bound the row:
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
<div className="flex h-[calc(100vh-2rem)] gap-4">
|
|
56
|
+
<FormBuilder … className="min-w-0 flex-1 overflow-y-auto" />
|
|
57
|
+
<FormSummary … />
|
|
58
|
+
</div>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
> Hoisting the form means a remount `key` no longer resets it. Call `form.reset(defaults)`
|
|
62
|
+
> instead (e.g. when opening a drawer).
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx torch-glare@latest init
|
|
68
|
+
npx torch-glare@latest add FormSummary
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Imports
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import { FormSummary } from '@/components/FormSummary'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## `FormSummary` props
|
|
78
|
+
|
|
79
|
+
| Prop | Type | Notes |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `title` | `ReactNode` | Panel title, e.g. `"Invoice"`. |
|
|
82
|
+
| `subtitle` | `ReactNode` | Muted text beside the title, e.g. `"Summary"`. |
|
|
83
|
+
| `form` | `UseFormReturn` | The form to read values from — required when rendered outside the `<FormBuilder>`. |
|
|
84
|
+
| `width` | `number \| string` | Panel width; default `228`. |
|
|
85
|
+
| `children` | `ReactNode` | `FormSummary.Group` children. |
|
|
86
|
+
|
|
87
|
+
## `FormSummary.Group` props
|
|
88
|
+
|
|
89
|
+
| Prop | Type | Notes |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| `title` | `ReactNode` | The group heading (Customer / Total / Tax / …). Groups are separated by a divider. |
|
|
92
|
+
| `collapsible` | `boolean` | When titled, the heading is a [ConclusionHeader](./conclusion-header.md) that opens/closes **this group's** rows; default `true`. |
|
|
93
|
+
| `defaultOpen` | `boolean` | Initial open state when collapsible; default `true`. |
|
|
94
|
+
|
|
95
|
+
The main panel title is a plain header — collapsing happens **per group**, not on the whole panel.
|
|
96
|
+
|
|
97
|
+
## `FormSummary.Row` props
|
|
98
|
+
|
|
99
|
+
| Prop | Type | Notes |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `label` | `ReactNode` | Row label, above the value. |
|
|
102
|
+
| `compute` | `(values) => number \| string` | **The calculation** — receives the live form values. |
|
|
103
|
+
| `value` | `number \| string` | Static value, when there's nothing to compute. |
|
|
104
|
+
| `currency` | `ReactNode` | Currency code, e.g. `"IQD"` — rendered **inside** the field's trailing edge. |
|
|
105
|
+
| `tone` | `'neutral' \| 'success' \| 'info'` | Color of the currency code (green / blue). |
|
|
106
|
+
| `emphasized` | `boolean` | The primary result — lighter, emphasized border. |
|
|
107
|
+
| `decimals` | `number` | Decimal places; default `2` (→ `0.00`). |
|
|
108
|
+
| `action` | `ReactNode` | Trailing slot inside the field, e.g. an `ActionButton`. Shares the slot with `currency`. |
|
|
109
|
+
| `format` | `(value) => string` | Override the default number formatting. |
|
|
110
|
+
|
|
111
|
+
## Computing totals
|
|
112
|
+
|
|
113
|
+
`compute` is a plain function of the form values — keep the maths outside the JSX:
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
const subTotal = v => v.items.reduce((s, i) => s + i.qty * i.price, 0)
|
|
117
|
+
const totalDiscount = v => v.items.reduce((s, i) => s + i.discount, 0)
|
|
118
|
+
const taxable = v => Math.max(0, subTotal(v) - totalDiscount(v))
|
|
119
|
+
const totalTax = v => taxable(v) * (v.taxRate / 100)
|
|
120
|
+
const overallTotal = v => taxable(v) + totalTax(v)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The panel is **read-only** — it contributes nothing to the submitted values.
|
package/docs/components/form.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: Form
|
|
3
|
-
version:
|
|
3
|
+
version: 2.4.0
|
|
4
4
|
status: stable
|
|
5
5
|
category: components/forms
|
|
6
6
|
tags: [form, validation, react-hook-form, accessible, compound]
|
|
@@ -18,25 +18,22 @@ dependencies:
|
|
|
18
18
|
|
|
19
19
|
## Installation
|
|
20
20
|
|
|
21
|
+
TORCH Glare is a copy-in library: the CLI copies this component's source into your project
|
|
22
|
+
(you do **not** install it from the npm package). Run `init` once, then `add`:
|
|
23
|
+
|
|
21
24
|
```bash
|
|
22
|
-
|
|
25
|
+
npx torch-glare@latest init
|
|
26
|
+
npx torch-glare@latest add Form
|
|
23
27
|
```
|
|
24
28
|
|
|
29
|
+
`add` also copies any components, hooks, and utilities that `Form` depends on.
|
|
30
|
+
|
|
25
31
|
## Import
|
|
26
32
|
|
|
27
|
-
|
|
28
|
-
import {
|
|
29
|
-
Form,
|
|
30
|
-
FormField,
|
|
31
|
-
FormItem,
|
|
32
|
-
FormLabel,
|
|
33
|
-
FormControl,
|
|
34
|
-
FormDescription,
|
|
35
|
-
FormMessage
|
|
36
|
-
} from 'torch-glare/lib/components/Form'
|
|
33
|
+
Import from your project's local path — the alias configured in `glare.json` (e.g. `@/*`):
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
import {
|
|
35
|
+
```tsx
|
|
36
|
+
import { Form } from "@/components/Form";
|
|
40
37
|
```
|
|
41
38
|
|
|
42
39
|
## Quick Examples
|
|
@@ -52,9 +49,9 @@ import {
|
|
|
52
49
|
FormLabel,
|
|
53
50
|
FormControl,
|
|
54
51
|
FormMessage
|
|
55
|
-
} from '
|
|
56
|
-
import { Input } from '
|
|
57
|
-
import { Button } from '
|
|
52
|
+
} from '@/components/Form'
|
|
53
|
+
import { Input } from '@/components/Input'
|
|
54
|
+
import { Button } from '@/components/Button'
|
|
58
55
|
|
|
59
56
|
function BasicForm() {
|
|
60
57
|
const form = useForm({
|
|
@@ -213,9 +210,9 @@ function ValidatedForm() {
|
|
|
213
210
|
### With Multiple Field Types
|
|
214
211
|
|
|
215
212
|
```typescript
|
|
216
|
-
import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '
|
|
217
|
-
import { Checkbox } from '
|
|
218
|
-
import { RadioGroup, RadioGroupItem } from '
|
|
213
|
+
import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '@/components/Select'
|
|
214
|
+
import { Checkbox } from '@/components/Checkbox'
|
|
215
|
+
import { RadioGroup, RadioGroupItem } from '@/components/Radio'
|
|
219
216
|
|
|
220
217
|
function MultiFieldForm() {
|
|
221
218
|
const form = useForm({
|
|
@@ -12,10 +12,16 @@ A presentational header chip used at the top of a form, page, or drawer to commu
|
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
15
|
+
TORCH Glare is a copy-in library: the CLI copies this component's source into your project
|
|
16
|
+
(you do **not** install it from the npm package). Run `init` once, then `add`:
|
|
17
|
+
|
|
15
18
|
```bash
|
|
19
|
+
npx torch-glare@latest init
|
|
16
20
|
npx torch-glare@latest add HeaderBar
|
|
17
21
|
```
|
|
18
22
|
|
|
23
|
+
`add` also copies any components, hooks, and utilities that `HeaderBar` depends on.
|
|
24
|
+
|
|
19
25
|
## Imports
|
|
20
26
|
|
|
21
27
|
```typescript
|
|
@@ -11,16 +11,22 @@ keywords: [image-attachment, file-upload, drag-drop, preview, modal, expandable]
|
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
TORCH Glare is a copy-in library: the CLI copies this component's source into your project
|
|
15
|
+
(you do **not** install it from the npm package). Run `init` once, then `add`:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx torch-glare@latest init
|
|
19
|
+
npx torch-glare@latest add ImageAttachment
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`add` also copies any components, hooks, and utilities that `ImageAttachment` depends on.
|
|
15
23
|
|
|
16
24
|
## Import
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
AttachmentImagePreview
|
|
23
|
-
} from '@torch-ui/components'
|
|
26
|
+
Import from your project's local path — the alias configured in `glare.json` (e.g. `@/*`):
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { ImageAttachment } from "@/components/ImageAttachment";
|
|
24
30
|
```
|
|
25
31
|
|
|
26
32
|
## Component Overview
|
|
@@ -36,7 +42,7 @@ The ImageAttachment suite consists of three components:
|
|
|
36
42
|
### Basic File Upload
|
|
37
43
|
|
|
38
44
|
```typescript
|
|
39
|
-
import { ImageAttachment } from
|
|
45
|
+
import { ImageAttachment } from "@/components/ImageAttachment";
|
|
40
46
|
import { useState } from 'react'
|
|
41
47
|
|
|
42
48
|
function Example() {
|
|
@@ -64,7 +70,7 @@ function Example() {
|
|
|
64
70
|
### With Preview
|
|
65
71
|
|
|
66
72
|
```typescript
|
|
67
|
-
import { ImageAttachment, ExpandableImage } from
|
|
73
|
+
import { ImageAttachment, ExpandableImage } from "@/components/ImageAttachment";
|
|
68
74
|
import { useState } from 'react'
|
|
69
75
|
|
|
70
76
|
function WithPreview() {
|
|
@@ -106,12 +112,8 @@ function WithPreview() {
|
|
|
106
112
|
### Complete Upload Flow
|
|
107
113
|
|
|
108
114
|
```typescript
|
|
109
|
-
import {
|
|
110
|
-
|
|
111
|
-
ExpandableImage,
|
|
112
|
-
AttachmentImagePreview
|
|
113
|
-
} from '@torch-ui/components'
|
|
114
|
-
import { Button } from '@torch-ui/components'
|
|
115
|
+
import { ImageAttachment, ExpandableImage, AttachmentImagePreview } from "@/components/ImageAttachment";
|
|
116
|
+
import { Button } from "@/components/Button";
|
|
115
117
|
import { useState } from 'react'
|
|
116
118
|
|
|
117
119
|
function CompleteFlow() {
|
|
@@ -160,7 +162,7 @@ function CompleteFlow() {
|
|
|
160
162
|
header={file?.name || 'Image Preview'}
|
|
161
163
|
>
|
|
162
164
|
<Button onClick={handleUpload}>Upload</Button>
|
|
163
|
-
<Button variant="
|
|
165
|
+
<Button variant="BluSecStyle" onClick={handleRemove}>
|
|
164
166
|
Remove
|
|
165
167
|
</Button>
|
|
166
168
|
</AttachmentImagePreview>
|
|
@@ -174,7 +176,7 @@ function CompleteFlow() {
|
|
|
174
176
|
### With Drag and Drop (React Dropzone)
|
|
175
177
|
|
|
176
178
|
```typescript
|
|
177
|
-
import { ImageAttachment, ExpandableImage } from
|
|
179
|
+
import { ImageAttachment, ExpandableImage } from "@/components/ImageAttachment";
|
|
178
180
|
import { useDropzone } from 'react-dropzone'
|
|
179
181
|
import { useState } from 'react'
|
|
180
182
|
|
|
@@ -221,7 +223,7 @@ function WithDropzone() {
|
|
|
221
223
|
### Multiple File Upload
|
|
222
224
|
|
|
223
225
|
```typescript
|
|
224
|
-
import { ImageAttachment, ExpandableImage } from
|
|
226
|
+
import { ImageAttachment, ExpandableImage } from "@/components/ImageAttachment";
|
|
225
227
|
import { useState } from 'react'
|
|
226
228
|
|
|
227
229
|
function MultipleFiles() {
|
|
@@ -267,8 +269,9 @@ function MultipleFiles() {
|
|
|
267
269
|
### With Validation
|
|
268
270
|
|
|
269
271
|
```typescript
|
|
270
|
-
import { ImageAttachment } from
|
|
271
|
-
import { FieldHint
|
|
272
|
+
import { ImageAttachment } from "@/components/ImageAttachment";
|
|
273
|
+
import { FieldHint } from "@/components/FieldHint";
|
|
274
|
+
import { toast } from "@/components/Toast";
|
|
272
275
|
import { useState } from 'react'
|
|
273
276
|
|
|
274
277
|
function WithValidation() {
|
|
@@ -327,12 +330,9 @@ function WithValidation() {
|
|
|
327
330
|
### Profile Picture Upload
|
|
328
331
|
|
|
329
332
|
```typescript
|
|
330
|
-
import {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
AttachmentImagePreview
|
|
334
|
-
} from '@torch-ui/components'
|
|
335
|
-
import { Avatar, Button } from '@torch-ui/components'
|
|
333
|
+
import { ImageAttachment, ExpandableImage, AttachmentImagePreview } from "@/components/ImageAttachment";
|
|
334
|
+
import { Avatar } from "@/components/Avatar";
|
|
335
|
+
import { Button } from "@/components/Button";
|
|
336
336
|
import { useState } from 'react'
|
|
337
337
|
|
|
338
338
|
function ProfilePictureUpload() {
|
|
@@ -400,8 +400,8 @@ function ProfilePictureUpload() {
|
|
|
400
400
|
### Gallery Upload
|
|
401
401
|
|
|
402
402
|
```typescript
|
|
403
|
-
import { ImageAttachment, ExpandableImage } from
|
|
404
|
-
import { Button } from
|
|
403
|
+
import { ImageAttachment, ExpandableImage } from "@/components/ImageAttachment";
|
|
404
|
+
import { Button } from "@/components/Button";
|
|
405
405
|
import { useState } from 'react'
|
|
406
406
|
|
|
407
407
|
function GalleryUpload() {
|
|
@@ -448,7 +448,7 @@ function GalleryUpload() {
|
|
|
448
448
|
<Button
|
|
449
449
|
size="S"
|
|
450
450
|
buttonType="icon"
|
|
451
|
-
variant="
|
|
451
|
+
variant="BluSecStyle"
|
|
452
452
|
className="absolute top-1 right-1 opacity-0 group-hover:opacity-100"
|
|
453
453
|
onClick={() => handleRemove(image.id)}
|
|
454
454
|
>
|
|
@@ -545,11 +545,7 @@ export const AttachmentImagePreview: React.FC<AttachmentImagePreviewProps>
|
|
|
545
545
|
### Usage with Types
|
|
546
546
|
|
|
547
547
|
```typescript
|
|
548
|
-
import {
|
|
549
|
-
ImageAttachment,
|
|
550
|
-
ExpandableImage,
|
|
551
|
-
AttachmentImagePreview
|
|
552
|
-
} from '@torch-ui/components'
|
|
548
|
+
import { ImageAttachment, ExpandableImage, AttachmentImagePreview } from "@/components/ImageAttachment";
|
|
553
549
|
import { useRef, useState } from 'react'
|
|
554
550
|
|
|
555
551
|
function TypedExample() {
|
|
@@ -585,7 +581,7 @@ function TypedExample() {
|
|
|
585
581
|
### Upload with Progress
|
|
586
582
|
|
|
587
583
|
```typescript
|
|
588
|
-
import { ImageAttachment } from
|
|
584
|
+
import { ImageAttachment } from "@/components/ImageAttachment";
|
|
589
585
|
import { useState } from 'react'
|
|
590
586
|
|
|
591
587
|
function UploadWithProgress() {
|
|
@@ -638,7 +634,7 @@ function UploadWithProgress() {
|
|
|
638
634
|
### Image Cropper Integration
|
|
639
635
|
|
|
640
636
|
```typescript
|
|
641
|
-
import { ImageAttachment, ExpandableImage } from
|
|
637
|
+
import { ImageAttachment, ExpandableImage } from "@/components/ImageAttachment";
|
|
642
638
|
import { useState } from 'react'
|
|
643
639
|
import Cropper from 'react-easy-crop'
|
|
644
640
|
|
|
@@ -688,11 +684,7 @@ function ImageCropper() {
|
|
|
688
684
|
|
|
689
685
|
```typescript
|
|
690
686
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
|
691
|
-
import {
|
|
692
|
-
ImageAttachment,
|
|
693
|
-
ExpandableImage,
|
|
694
|
-
AttachmentImagePreview
|
|
695
|
-
} from '@torch-ui/components'
|
|
687
|
+
import { ImageAttachment, ExpandableImage, AttachmentImagePreview } from "@/components/ImageAttachment";
|
|
696
688
|
|
|
697
689
|
describe('ImageAttachment', () => {
|
|
698
690
|
it('renders labels', () => {
|
|
@@ -883,10 +875,16 @@ describe('AttachmentImagePreview', () => {
|
|
|
883
875
|
|
|
884
876
|
### Default Styling
|
|
885
877
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
-
|
|
889
|
-
|
|
878
|
+
Matches the *Attachment-Field-1.0 / Pic-Container-1.0* design:
|
|
879
|
+
|
|
880
|
+
- **Field box** (`ImageAttachment`): a `#f9f9f9` (`background-presentation-form-field-primary`)
|
|
881
|
+
rounded container holding an optional preview and the drop zone, `gap-[10px]`.
|
|
882
|
+
- **Drop zone**: fills the box (`flex-1`), 65px tall, transparent with a dashed
|
|
883
|
+
`border-presentation-action-borderstyle` border; drag-active tints it gray.
|
|
884
|
+
- **Preview thumbnail** (`ExpandableImage`): a fixed **65×65** square, `object-cover`, rounded.
|
|
885
|
+
- **Placeholder** (no image): ocean fill (`badge-ocean-subtle`) + dashed `blue-purple-300`
|
|
886
|
+
border + paperclip icon; on hover it goes gray (`badge-gray-subtle` / `badge-gray`).
|
|
887
|
+
- **Hover (has image)**: a `black/50` overlay with a fullscreen icon + the `expandLabel`.
|
|
890
888
|
|
|
891
889
|
## Best Practices
|
|
892
890
|
|