@algodomain/smart-forms 0.1.8 → 0.1.10
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 +696 -15
- package/dist/{chunk-ONWU4O23.js → chunk-4XK6HAJ2.js} +3 -3
- package/dist/{chunk-ONWU4O23.js.map → chunk-4XK6HAJ2.js.map} +1 -1
- package/dist/{chunk-S4UZYPMB.js → chunk-6VKQ7EMR.js} +3 -9
- package/dist/chunk-6VKQ7EMR.js.map +1 -0
- package/dist/{chunk-I5HYLSDJ.cjs → chunk-DRMVY7TX.cjs} +91 -91
- package/dist/{chunk-I5HYLSDJ.cjs.map → chunk-DRMVY7TX.cjs.map} +1 -1
- package/dist/{chunk-VOLW7NZ4.cjs → chunk-WUYS7DMR.cjs} +3 -9
- package/dist/chunk-WUYS7DMR.cjs.map +1 -0
- package/dist/fields.cjs +101 -101
- package/dist/fields.js +4 -4
- package/dist/index.cjs +492 -8332
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -1
- package/dist/index.d.ts +36 -1
- package/dist/index.js +414 -8255
- package/dist/index.js.map +1 -1
- package/dist/opinionated.cjs +18 -18
- package/dist/opinionated.js +2 -2
- package/dist/style.css +2 -2
- package/package.json +8 -7
- package/dist/chunk-S4UZYPMB.js.map +0 -1
- package/dist/chunk-VOLW7NZ4.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -19,8 +19,11 @@ Built with TypeScript, Tailwind CSS, Zod validation, and shadcn/ui components.
|
|
|
19
19
|
- [Opinionated Fields](#-opinionated-fields)
|
|
20
20
|
- [Configuration Reference](#-configuration-reference)
|
|
21
21
|
- [Advanced Features](#-advanced-features)
|
|
22
|
+
- [UI Components](#-ui-components)
|
|
23
|
+
- [Button Components](#-button-components)
|
|
22
24
|
- [Theming](#-theming--customization)
|
|
23
25
|
- [Hooks & Context](#-hooks--context)
|
|
26
|
+
- [Context Providers](#-context-providers)
|
|
24
27
|
- [Examples](#-examples)
|
|
25
28
|
- [Troubleshooting](#-troubleshooting)
|
|
26
29
|
- [Contributing](#-contributing)
|
|
@@ -199,16 +202,35 @@ Multi-step form with tabs and progress tracking.
|
|
|
199
202
|
api="/api/submit"
|
|
200
203
|
showProgressBar={true}
|
|
201
204
|
showTabNumbers={true}
|
|
205
|
+
animateTabs={false}
|
|
206
|
+
tabType="default"
|
|
202
207
|
>
|
|
203
|
-
<Tab title="Step 1">
|
|
208
|
+
<Tab title="Step 1" onNext={async () => { /* custom logic */ }}>
|
|
204
209
|
{/* Fields */}
|
|
205
210
|
</Tab>
|
|
206
|
-
<Tab title="Step 2">
|
|
211
|
+
<Tab title="Step 2" processingOverlay={<LoadingSpinner />}>
|
|
207
212
|
{/* Fields */}
|
|
208
213
|
</Tab>
|
|
209
214
|
</MultiTabSmartForm>
|
|
210
215
|
```
|
|
211
216
|
|
|
217
|
+
**Additional Props:**
|
|
218
|
+
|
|
219
|
+
| Prop | Type | Default | Description |
|
|
220
|
+
|------|------|---------|-------------|
|
|
221
|
+
| `animateTabs` | `boolean` | `false` | Enable tab transition animations |
|
|
222
|
+
| `tabType` | `'underline' \| 'default'` | `'default'` | Tab styling variant |
|
|
223
|
+
| `disableManualTabSwitch` | `boolean` | `false` | Prevent manual clicking on future tabs. Users can still navigate forward via Next button and go back to previous tabs. |
|
|
224
|
+
|
|
225
|
+
**Tab Component Props:**
|
|
226
|
+
|
|
227
|
+
| Prop | Type | Description |
|
|
228
|
+
|------|------|-------------|
|
|
229
|
+
| `title` | `string` | **Required.** Tab title |
|
|
230
|
+
| `children` | `ReactNode` | Tab content |
|
|
231
|
+
| `onNext` | `() => Promise<void> \| void` | Callback executed before moving to next tab. Can be async. If it throws, navigation is prevented. |
|
|
232
|
+
| `processingOverlay` | `ReactNode` | Overlay shown while `onNext` is processing |
|
|
233
|
+
|
|
212
234
|
### BaseSmartForm
|
|
213
235
|
|
|
214
236
|
Low-level form component for custom layouts.
|
|
@@ -224,6 +246,63 @@ Low-level form component for custom layouts.
|
|
|
224
246
|
</BaseSmartForm>
|
|
225
247
|
```
|
|
226
248
|
|
|
249
|
+
### FormFieldGroup
|
|
250
|
+
|
|
251
|
+
A layout component for grouping form fields horizontally with responsive spacing. Fields automatically wrap to multiple rows on smaller screens.
|
|
252
|
+
|
|
253
|
+
**Usage:**
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
import { FormFieldGroup } from '@algodomain/smart-forms'
|
|
257
|
+
import { SmartInput, SmartSelect } from '@algodomain/smart-forms/fields'
|
|
258
|
+
|
|
259
|
+
<SmartForm api="/api/submit">
|
|
260
|
+
<FormFieldGroup>
|
|
261
|
+
<SmartInput field="firstName" label="First Name" />
|
|
262
|
+
<SmartInput field="lastName" label="Last Name" />
|
|
263
|
+
</FormFieldGroup>
|
|
264
|
+
|
|
265
|
+
<FormFieldGroup>
|
|
266
|
+
<SmartInput field="city" label="City" />
|
|
267
|
+
<SmartSelect field="state" label="State" options={stateOptions} />
|
|
268
|
+
<SmartInput field="zipCode" label="Zip Code" />
|
|
269
|
+
</FormFieldGroup>
|
|
270
|
+
</SmartForm>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Props:**
|
|
274
|
+
|
|
275
|
+
| Prop | Type | Default | Description |
|
|
276
|
+
|------|------|---------|-------------|
|
|
277
|
+
| `children` | `ReactNode` | - | **Required.** Form field components to group |
|
|
278
|
+
| `className` | `string` | `''` | Additional CSS classes to apply |
|
|
279
|
+
|
|
280
|
+
**Features:**
|
|
281
|
+
- Responsive flex layout with automatic wrapping
|
|
282
|
+
- Spacing: `gap-2` on mobile, `gap-4` on medium screens and above
|
|
283
|
+
- Bottom margin: `mb-4` for consistent spacing between groups
|
|
284
|
+
|
|
285
|
+
**Example: Address Form with Grouped Fields**
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
<SmartForm api="/api/submit">
|
|
289
|
+
<FormFieldGroup>
|
|
290
|
+
<SmartInput field="streetAddress" label="Street Address" required />
|
|
291
|
+
</FormFieldGroup>
|
|
292
|
+
|
|
293
|
+
<FormFieldGroup>
|
|
294
|
+
<SmartInput field="city" label="City" required />
|
|
295
|
+
<SmartSelect field="state" label="State" options={states} required />
|
|
296
|
+
<SmartInput field="zipCode" label="Zip Code" required />
|
|
297
|
+
</FormFieldGroup>
|
|
298
|
+
|
|
299
|
+
<FormFieldGroup className="gap-6">
|
|
300
|
+
<SmartInput field="country" label="Country" required />
|
|
301
|
+
<SmartSelect field="phoneCode" label="Phone Code" options={phoneCodes} />
|
|
302
|
+
</FormFieldGroup>
|
|
303
|
+
</SmartForm>
|
|
304
|
+
```
|
|
305
|
+
|
|
227
306
|
---
|
|
228
307
|
|
|
229
308
|
## 🎯 Field Components
|
|
@@ -477,6 +556,9 @@ All form components (`SmartForm`, `MultiTabSmartForm`, `BaseSmartForm`) support
|
|
|
477
556
|
| `showReset` | `boolean` | `false` | Show reset button |
|
|
478
557
|
| `showProgressBar` | `boolean` | `true` | Display progress bar (MultiTab only) |
|
|
479
558
|
| `showTabNumbers` | `boolean` | `true` | Show tab numbers (MultiTab only) |
|
|
559
|
+
| `animateTabs` | `boolean` | `false` | Enable tab animations (MultiTab only) |
|
|
560
|
+
| `tabType` | `'underline' \| 'default'` | `'default'` | Tab styling variant (MultiTab only) |
|
|
561
|
+
| `disableManualTabSwitch` | `boolean` | `false` | Prevent manual clicking on future tabs (MultiTab only) |
|
|
480
562
|
| `authentication` | `AuthenticationConfig` | - | Authentication configuration |
|
|
481
563
|
| `includeQueryParams` | `boolean` | `false` | Include URL query parameters |
|
|
482
564
|
| `queryParamsToInclude` | `string[]` | - | Filter specific query params |
|
|
@@ -598,6 +680,214 @@ All field components support these props:
|
|
|
598
680
|
|
|
599
681
|
## 🚀 Advanced Features
|
|
600
682
|
|
|
683
|
+
### Disable Manual Tab Switching
|
|
684
|
+
|
|
685
|
+
Control whether users can manually click on future tabs. When enabled, users must complete each step sequentially using the Next button, but can still go back to previous tabs.
|
|
686
|
+
|
|
687
|
+
**Usage:**
|
|
688
|
+
|
|
689
|
+
```tsx
|
|
690
|
+
<MultiTabSmartForm
|
|
691
|
+
api="/api/submit"
|
|
692
|
+
disableManualTabSwitch={true}
|
|
693
|
+
>
|
|
694
|
+
<Tab title="Step 1">
|
|
695
|
+
<SmartInput field="name" label="Name" required />
|
|
696
|
+
</Tab>
|
|
697
|
+
|
|
698
|
+
<Tab title="Step 2">
|
|
699
|
+
<SmartInput field="email" label="Email" required />
|
|
700
|
+
</Tab>
|
|
701
|
+
|
|
702
|
+
<Tab title="Step 3">
|
|
703
|
+
<SmartInput field="phone" label="Phone" required />
|
|
704
|
+
</Tab>
|
|
705
|
+
</MultiTabSmartForm>
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
**Behavior:**
|
|
709
|
+
- When `disableManualTabSwitch={true}`:
|
|
710
|
+
- Users **cannot** click on tabs ahead of the current tab
|
|
711
|
+
- Future tabs appear disabled (grayed out)
|
|
712
|
+
- Users **can** click on previous tabs to go back
|
|
713
|
+
- Users **can** navigate forward using the Next button (after validation)
|
|
714
|
+
- When `disableManualTabSwitch={false}` (default):
|
|
715
|
+
- Users can freely click on any tab
|
|
716
|
+
- All tabs are clickable
|
|
717
|
+
|
|
718
|
+
**Use Cases:**
|
|
719
|
+
- Enforce sequential completion of multi-step forms
|
|
720
|
+
- Ensure users complete each step before proceeding
|
|
721
|
+
- Prevent skipping validation steps
|
|
722
|
+
- Guide users through a structured workflow
|
|
723
|
+
|
|
724
|
+
### Tab Callbacks and Processing Overlays
|
|
725
|
+
|
|
726
|
+
In MultiTabSmartForm, you can add custom logic before moving to the next tab using the `onNext` callback. This is useful for async operations like API calls or data processing.
|
|
727
|
+
|
|
728
|
+
**Basic Usage:**
|
|
729
|
+
|
|
730
|
+
```tsx
|
|
731
|
+
<MultiTabSmartForm api="/api/submit">
|
|
732
|
+
<Tab
|
|
733
|
+
title="Step 1"
|
|
734
|
+
onNext={async () => {
|
|
735
|
+
// Custom validation or API call
|
|
736
|
+
const response = await fetch('/api/validate-step1')
|
|
737
|
+
if (!response.ok) {
|
|
738
|
+
throw new Error('Validation failed')
|
|
739
|
+
}
|
|
740
|
+
}}
|
|
741
|
+
>
|
|
742
|
+
<SmartInput field="name" label="Name" required />
|
|
743
|
+
</Tab>
|
|
744
|
+
|
|
745
|
+
<Tab title="Step 2">
|
|
746
|
+
<SmartInput field="email" label="Email" required />
|
|
747
|
+
</Tab>
|
|
748
|
+
</MultiTabSmartForm>
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
**With Processing Overlay:**
|
|
752
|
+
|
|
753
|
+
```tsx
|
|
754
|
+
import { LoadingSpinner } from '@algodomain/smart-forms'
|
|
755
|
+
|
|
756
|
+
<MultiTabSmartForm api="/api/submit">
|
|
757
|
+
<Tab
|
|
758
|
+
title="Processing Step"
|
|
759
|
+
onNext={async () => {
|
|
760
|
+
// Long-running operation
|
|
761
|
+
await processData()
|
|
762
|
+
}}
|
|
763
|
+
processingOverlay={
|
|
764
|
+
<div className="flex flex-col items-center gap-2">
|
|
765
|
+
<LoadingSpinner className="h-8 w-8" />
|
|
766
|
+
<p>Processing your data...</p>
|
|
767
|
+
</div>
|
|
768
|
+
}
|
|
769
|
+
>
|
|
770
|
+
<SmartInput field="data" label="Data" required />
|
|
771
|
+
</Tab>
|
|
772
|
+
</MultiTabSmartForm>
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
**Error Handling:**
|
|
776
|
+
|
|
777
|
+
If `onNext` throws an error, navigation to the next tab is prevented and the error message is displayed:
|
|
778
|
+
|
|
779
|
+
```tsx
|
|
780
|
+
<Tab
|
|
781
|
+
title="Step 1"
|
|
782
|
+
onNext={async () => {
|
|
783
|
+
const result = await validateData()
|
|
784
|
+
if (!result.valid) {
|
|
785
|
+
throw new Error(result.message) // Navigation prevented, error shown
|
|
786
|
+
}
|
|
787
|
+
}}
|
|
788
|
+
>
|
|
789
|
+
{/* Fields */}
|
|
790
|
+
</Tab>
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
### External Forms Integration
|
|
794
|
+
|
|
795
|
+
You can integrate external forms (forms outside the MultiTabSmartForm component) with the tab system for validation and navigation.
|
|
796
|
+
|
|
797
|
+
**Method 1: Manual Registration (Recommended for known fields)**
|
|
798
|
+
|
|
799
|
+
```tsx
|
|
800
|
+
import { useExternalFormFields } from '@algodomain/smart-forms'
|
|
801
|
+
|
|
802
|
+
function ExternalForm() {
|
|
803
|
+
// Register fields for tab index 0
|
|
804
|
+
useExternalFormFields(['name', 'email', 'phone'], 0)
|
|
805
|
+
|
|
806
|
+
return (
|
|
807
|
+
<div>
|
|
808
|
+
<input name="name" />
|
|
809
|
+
<input name="email" />
|
|
810
|
+
<input name="phone" />
|
|
811
|
+
</div>
|
|
812
|
+
)
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// In your MultiTabSmartForm
|
|
816
|
+
<MultiTabSmartForm api="/api/submit">
|
|
817
|
+
<Tab title="Step 1">
|
|
818
|
+
<ExternalForm />
|
|
819
|
+
</Tab>
|
|
820
|
+
</MultiTabSmartForm>
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
**Method 2: Auto-Detection (Recommended for dynamic fields)**
|
|
824
|
+
|
|
825
|
+
```tsx
|
|
826
|
+
import { useExternalTab, ExternalFieldProvider } from '@algodomain/smart-forms'
|
|
827
|
+
import { SmartInput } from '@algodomain/smart-forms/fields'
|
|
828
|
+
|
|
829
|
+
function ExternalForm() {
|
|
830
|
+
const { registerField, ExternalFieldProvider } = useExternalTab()
|
|
831
|
+
|
|
832
|
+
return (
|
|
833
|
+
<ExternalFieldProvider registerField={registerField}>
|
|
834
|
+
<SmartInput field="name" label="Name" />
|
|
835
|
+
<SmartInput field="email" label="Email" />
|
|
836
|
+
</ExternalFieldProvider>
|
|
837
|
+
)
|
|
838
|
+
}
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
**Method 3: Using TabIndexProvider**
|
|
842
|
+
|
|
843
|
+
```tsx
|
|
844
|
+
import { TabIndexProvider, useExternalFormFields } from '@algodomain/smart-forms'
|
|
845
|
+
|
|
846
|
+
function ExternalForm() {
|
|
847
|
+
const tabIndexContext = useTabIndex()
|
|
848
|
+
const tabIndex = tabIndexContext?.tabIndex ?? 0
|
|
849
|
+
|
|
850
|
+
useExternalFormFields(['field1', 'field2'], tabIndex)
|
|
851
|
+
|
|
852
|
+
return <div>{/* Your form */}</div>
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// Wrap with TabIndexProvider
|
|
856
|
+
<TabIndexProvider tabIndex={0}>
|
|
857
|
+
<ExternalForm />
|
|
858
|
+
</TabIndexProvider>
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
### Submit Hooks
|
|
862
|
+
|
|
863
|
+
Register custom hooks to execute before form submission. Useful for file uploads or other async operations.
|
|
864
|
+
|
|
865
|
+
```tsx
|
|
866
|
+
import { useSmartForm } from '@algodomain/smart-forms'
|
|
867
|
+
|
|
868
|
+
function FileUploadField() {
|
|
869
|
+
const { registerSubmitHook, unregisterSubmitHook } = useSmartForm()
|
|
870
|
+
const field = 'resume'
|
|
871
|
+
|
|
872
|
+
useEffect(() => {
|
|
873
|
+
const key = `upload-${field}`
|
|
874
|
+
|
|
875
|
+
const uploadHook = async () => {
|
|
876
|
+
// Upload file before form submission
|
|
877
|
+
await uploadFile(file)
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
registerSubmitHook(key, uploadHook)
|
|
881
|
+
|
|
882
|
+
return () => {
|
|
883
|
+
unregisterSubmitHook(key)
|
|
884
|
+
}
|
|
885
|
+
}, [field, file])
|
|
886
|
+
|
|
887
|
+
return <input type="file" />
|
|
888
|
+
}
|
|
889
|
+
```
|
|
890
|
+
|
|
601
891
|
### Conditional Field Disabling
|
|
602
892
|
|
|
603
893
|
Disable fields or submit buttons based on form data values. Supports both static boolean values and dynamic functions.
|
|
@@ -946,6 +1236,146 @@ The library will:
|
|
|
946
1236
|
|
|
947
1237
|
---
|
|
948
1238
|
|
|
1239
|
+
## 🧩 UI Components
|
|
1240
|
+
|
|
1241
|
+
### FormHeader
|
|
1242
|
+
|
|
1243
|
+
Display form title, subtitle, and logo.
|
|
1244
|
+
|
|
1245
|
+
```tsx
|
|
1246
|
+
import { FormHeader } from '@algodomain/smart-forms'
|
|
1247
|
+
|
|
1248
|
+
<FormHeader
|
|
1249
|
+
title="Registration Form"
|
|
1250
|
+
subTitle="Please fill in your details"
|
|
1251
|
+
logo={<img src="/logo.png" alt="Logo" />}
|
|
1252
|
+
/>
|
|
1253
|
+
```
|
|
1254
|
+
|
|
1255
|
+
### Section
|
|
1256
|
+
|
|
1257
|
+
Section divider with optional text.
|
|
1258
|
+
|
|
1259
|
+
```tsx
|
|
1260
|
+
import { Section } from '@algodomain/smart-forms'
|
|
1261
|
+
|
|
1262
|
+
<Section text="Personal Information" />
|
|
1263
|
+
```
|
|
1264
|
+
|
|
1265
|
+
### Footer
|
|
1266
|
+
|
|
1267
|
+
Footer component for form content.
|
|
1268
|
+
|
|
1269
|
+
```tsx
|
|
1270
|
+
import { Footer } from '@algodomain/smart-forms'
|
|
1271
|
+
|
|
1272
|
+
<Footer>
|
|
1273
|
+
<p className="text-sm text-gray-500">
|
|
1274
|
+
By submitting, you agree to our terms and conditions.
|
|
1275
|
+
</p>
|
|
1276
|
+
</Footer>
|
|
1277
|
+
```
|
|
1278
|
+
|
|
1279
|
+
### ToastContainerWrapper
|
|
1280
|
+
|
|
1281
|
+
Toast notification container (automatically included in forms).
|
|
1282
|
+
|
|
1283
|
+
```tsx
|
|
1284
|
+
import { ToastContainerWrapper } from '@algodomain/smart-forms'
|
|
1285
|
+
|
|
1286
|
+
<ToastContainerWrapper />
|
|
1287
|
+
```
|
|
1288
|
+
|
|
1289
|
+
## 🔘 Button Components
|
|
1290
|
+
|
|
1291
|
+
### LoadingSpinner
|
|
1292
|
+
|
|
1293
|
+
Loading spinner component.
|
|
1294
|
+
|
|
1295
|
+
```tsx
|
|
1296
|
+
import { LoadingSpinner } from '@algodomain/smart-forms'
|
|
1297
|
+
|
|
1298
|
+
<LoadingSpinner className="h-6 w-6" />
|
|
1299
|
+
```
|
|
1300
|
+
|
|
1301
|
+
### SubmitButton
|
|
1302
|
+
|
|
1303
|
+
Submit button with loading state.
|
|
1304
|
+
|
|
1305
|
+
```tsx
|
|
1306
|
+
import { SubmitButton } from '@algodomain/smart-forms'
|
|
1307
|
+
|
|
1308
|
+
<SubmitButton
|
|
1309
|
+
onClick={handleSubmit}
|
|
1310
|
+
disabled={isLoading}
|
|
1311
|
+
isLoading={isLoading}
|
|
1312
|
+
>
|
|
1313
|
+
Submit Form
|
|
1314
|
+
</SubmitButton>
|
|
1315
|
+
```
|
|
1316
|
+
|
|
1317
|
+
### DraftSaveButton
|
|
1318
|
+
|
|
1319
|
+
Draft save button.
|
|
1320
|
+
|
|
1321
|
+
```tsx
|
|
1322
|
+
import { DraftSaveButton } from '@algodomain/smart-forms'
|
|
1323
|
+
|
|
1324
|
+
<DraftSaveButton
|
|
1325
|
+
onClick={handleSaveDraft}
|
|
1326
|
+
disabled={isDraftSaving}
|
|
1327
|
+
/>
|
|
1328
|
+
```
|
|
1329
|
+
|
|
1330
|
+
### ResetButton
|
|
1331
|
+
|
|
1332
|
+
Reset form button.
|
|
1333
|
+
|
|
1334
|
+
```tsx
|
|
1335
|
+
import { ResetButton } from '@algodomain/smart-forms'
|
|
1336
|
+
|
|
1337
|
+
<ResetButton onClick={handleReset} />
|
|
1338
|
+
```
|
|
1339
|
+
|
|
1340
|
+
### NavigationButtons
|
|
1341
|
+
|
|
1342
|
+
Navigation buttons for multi-tab forms.
|
|
1343
|
+
|
|
1344
|
+
```tsx
|
|
1345
|
+
import { NavigationButtons } from '@algodomain/smart-forms'
|
|
1346
|
+
|
|
1347
|
+
<NavigationButtons
|
|
1348
|
+
onPrevious={handlePrevious}
|
|
1349
|
+
onNext={handleNext}
|
|
1350
|
+
onSubmit={handleSubmit}
|
|
1351
|
+
onSaveDraft={handleSaveDraft}
|
|
1352
|
+
onReset={handleReset}
|
|
1353
|
+
isLoading={isLoading}
|
|
1354
|
+
isDraftSaving={isDraftSaving}
|
|
1355
|
+
config={config}
|
|
1356
|
+
isFirstTab={false}
|
|
1357
|
+
isLastTab={false}
|
|
1358
|
+
disabled={false}
|
|
1359
|
+
/>
|
|
1360
|
+
```
|
|
1361
|
+
|
|
1362
|
+
### SimpleFormButtons
|
|
1363
|
+
|
|
1364
|
+
Simple button group for single-page forms.
|
|
1365
|
+
|
|
1366
|
+
```tsx
|
|
1367
|
+
import { SimpleFormButtons } from '@algodomain/smart-forms'
|
|
1368
|
+
|
|
1369
|
+
<SimpleFormButtons
|
|
1370
|
+
onSubmit={handleSubmit}
|
|
1371
|
+
onSaveDraft={handleSaveDraft}
|
|
1372
|
+
onReset={handleReset}
|
|
1373
|
+
isLoading={isLoading}
|
|
1374
|
+
isDraftSaving={isDraftSaving}
|
|
1375
|
+
config={config}
|
|
1376
|
+
/>
|
|
1377
|
+
```
|
|
1378
|
+
|
|
949
1379
|
## 🎨 Theming & Customization
|
|
950
1380
|
|
|
951
1381
|
### CSS Variables
|
|
@@ -1025,21 +1455,36 @@ import { useSmartForm } from '@algodomain/smart-forms'
|
|
|
1025
1455
|
|
|
1026
1456
|
function MyComponent() {
|
|
1027
1457
|
const {
|
|
1028
|
-
formData,
|
|
1029
|
-
errors,
|
|
1030
|
-
isLoading,
|
|
1031
|
-
isDraftSaving,
|
|
1032
|
-
submitForm,
|
|
1033
|
-
saveDraft,
|
|
1034
|
-
resetForm,
|
|
1035
|
-
updateField,
|
|
1036
|
-
|
|
1458
|
+
formData, // Current form values
|
|
1459
|
+
errors, // Validation errors
|
|
1460
|
+
isLoading, // Submission loading state
|
|
1461
|
+
isDraftSaving, // Draft saving state
|
|
1462
|
+
submitForm, // Submit function
|
|
1463
|
+
saveDraft, // Save draft function
|
|
1464
|
+
resetForm, // Reset to initial values
|
|
1465
|
+
updateField, // Update specific field
|
|
1466
|
+
validateField, // Validate a single field
|
|
1467
|
+
validateFields, // Validate multiple fields
|
|
1468
|
+
validateFormAndGetResult, // Validate entire form and return boolean
|
|
1469
|
+
registerSubmitHook, // Register a hook to run before submission
|
|
1470
|
+
unregisterSubmitHook, // Unregister a submit hook
|
|
1471
|
+
setErrors, // Manually set validation errors
|
|
1472
|
+
config // Form configuration
|
|
1037
1473
|
} = useSmartForm()
|
|
1038
1474
|
|
|
1039
1475
|
return <button onClick={submitForm}>Submit</button>
|
|
1040
1476
|
}
|
|
1041
1477
|
```
|
|
1042
1478
|
|
|
1479
|
+
**Methods:**
|
|
1480
|
+
|
|
1481
|
+
- `validateField(field: string, value: any): boolean` - Validate a single field
|
|
1482
|
+
- `validateFields(fields: string[]): boolean` - Validate multiple fields
|
|
1483
|
+
- `validateFormAndGetResult(): boolean` - Validate entire form and return result
|
|
1484
|
+
- `registerSubmitHook(key: string, hook: () => Promise<void>): void` - Register a hook to execute before form submission
|
|
1485
|
+
- `unregisterSubmitHook(key: string): void` - Unregister a submit hook
|
|
1486
|
+
- `setErrors(errors: Record<string, string>): void` - Manually set validation errors
|
|
1487
|
+
|
|
1043
1488
|
### useFormField
|
|
1044
1489
|
|
|
1045
1490
|
Access individual field state (used internally by field components).
|
|
@@ -1059,6 +1504,170 @@ function CustomField({ field }) {
|
|
|
1059
1504
|
}
|
|
1060
1505
|
```
|
|
1061
1506
|
|
|
1507
|
+
### useFormWrapper
|
|
1508
|
+
|
|
1509
|
+
Alias for `useSmartForm` (for convenience).
|
|
1510
|
+
|
|
1511
|
+
```tsx
|
|
1512
|
+
import { useFormWrapper } from '@algodomain/smart-forms'
|
|
1513
|
+
|
|
1514
|
+
function MyComponent() {
|
|
1515
|
+
const { formData, submitForm } = useFormWrapper()
|
|
1516
|
+
// Same as useSmartForm
|
|
1517
|
+
}
|
|
1518
|
+
```
|
|
1519
|
+
|
|
1520
|
+
### useTabIndex
|
|
1521
|
+
|
|
1522
|
+
Get the current tab index in a MultiTabSmartForm.
|
|
1523
|
+
|
|
1524
|
+
```tsx
|
|
1525
|
+
import { useTabIndex } from '@algodomain/smart-forms'
|
|
1526
|
+
|
|
1527
|
+
function MyComponent() {
|
|
1528
|
+
const tabIndexContext = useTabIndex()
|
|
1529
|
+
const tabIndex = tabIndexContext?.tabIndex ?? 0
|
|
1530
|
+
|
|
1531
|
+
return <div>Current tab: {tabIndex + 1}</div>
|
|
1532
|
+
}
|
|
1533
|
+
```
|
|
1534
|
+
|
|
1535
|
+
### useExternalFormRegistration
|
|
1536
|
+
|
|
1537
|
+
Register external forms with MultiTabSmartForm for validation and tab navigation.
|
|
1538
|
+
|
|
1539
|
+
```tsx
|
|
1540
|
+
import { useExternalFormRegistration } from '@algodomain/smart-forms'
|
|
1541
|
+
|
|
1542
|
+
function ExternalForm() {
|
|
1543
|
+
const { registerTabFields, currentTabIndex } = useExternalFormRegistration()
|
|
1544
|
+
|
|
1545
|
+
useEffect(() => {
|
|
1546
|
+
registerTabFields(currentTabIndex, ['field1', 'field2'])
|
|
1547
|
+
}, [])
|
|
1548
|
+
|
|
1549
|
+
return <div>{/* Your form fields */}</div>
|
|
1550
|
+
}
|
|
1551
|
+
```
|
|
1552
|
+
|
|
1553
|
+
### useExternalFormFields
|
|
1554
|
+
|
|
1555
|
+
Register fields from external forms with a specific tab index.
|
|
1556
|
+
|
|
1557
|
+
```tsx
|
|
1558
|
+
import { useExternalFormFields } from '@algodomain/smart-forms'
|
|
1559
|
+
|
|
1560
|
+
function ExternalForm() {
|
|
1561
|
+
useExternalFormFields(['field1', 'field2'], 0) // Register fields for tab 0
|
|
1562
|
+
|
|
1563
|
+
return <div>{/* Your form fields */}</div>
|
|
1564
|
+
}
|
|
1565
|
+
```
|
|
1566
|
+
|
|
1567
|
+
### useAutoDetectFields
|
|
1568
|
+
|
|
1569
|
+
Automatically detect and register fields in external forms.
|
|
1570
|
+
|
|
1571
|
+
```tsx
|
|
1572
|
+
import { useAutoDetectFields, ExternalFieldProvider } from '@algodomain/smart-forms'
|
|
1573
|
+
|
|
1574
|
+
function ExternalForm() {
|
|
1575
|
+
const { registerField, ExternalFieldProvider } = useAutoDetectFields(0)
|
|
1576
|
+
|
|
1577
|
+
return (
|
|
1578
|
+
<ExternalFieldProvider registerField={registerField}>
|
|
1579
|
+
<SmartInput field="name" label="Name" />
|
|
1580
|
+
<SmartInput field="email" label="Email" />
|
|
1581
|
+
</ExternalFieldProvider>
|
|
1582
|
+
)
|
|
1583
|
+
}
|
|
1584
|
+
```
|
|
1585
|
+
|
|
1586
|
+
### useExternalTab
|
|
1587
|
+
|
|
1588
|
+
Automatically detect both tab index and fields in external forms (recommended).
|
|
1589
|
+
|
|
1590
|
+
```tsx
|
|
1591
|
+
import { useExternalTab, ExternalFieldProvider } from '@algodomain/smart-forms'
|
|
1592
|
+
|
|
1593
|
+
function ExternalForm() {
|
|
1594
|
+
const { registerField, ExternalFieldProvider } = useExternalTab()
|
|
1595
|
+
|
|
1596
|
+
return (
|
|
1597
|
+
<ExternalFieldProvider registerField={registerField}>
|
|
1598
|
+
<SmartInput field="name" label="Name" />
|
|
1599
|
+
<SmartInput field="email" label="Email" />
|
|
1600
|
+
</ExternalFieldProvider>
|
|
1601
|
+
)
|
|
1602
|
+
}
|
|
1603
|
+
```
|
|
1604
|
+
|
|
1605
|
+
### useFieldDetection
|
|
1606
|
+
|
|
1607
|
+
Access field detection context (used internally by field components).
|
|
1608
|
+
|
|
1609
|
+
```tsx
|
|
1610
|
+
import { useFieldDetection } from '@algodomain/smart-forms'
|
|
1611
|
+
|
|
1612
|
+
function CustomField({ field }) {
|
|
1613
|
+
const context = useFieldDetection()
|
|
1614
|
+
|
|
1615
|
+
useEffect(() => {
|
|
1616
|
+
if (context) {
|
|
1617
|
+
context.registerField(field)
|
|
1618
|
+
}
|
|
1619
|
+
}, [field, context])
|
|
1620
|
+
|
|
1621
|
+
return <input />
|
|
1622
|
+
}
|
|
1623
|
+
```
|
|
1624
|
+
|
|
1625
|
+
## 🔌 Context Providers
|
|
1626
|
+
|
|
1627
|
+
### TabIndexProvider
|
|
1628
|
+
|
|
1629
|
+
Provide tab index context to child components.
|
|
1630
|
+
|
|
1631
|
+
```tsx
|
|
1632
|
+
import { TabIndexProvider, useTabIndex } from '@algodomain/smart-forms'
|
|
1633
|
+
|
|
1634
|
+
function MyComponent() {
|
|
1635
|
+
return (
|
|
1636
|
+
<TabIndexProvider tabIndex={0}>
|
|
1637
|
+
<ChildComponent />
|
|
1638
|
+
</TabIndexProvider>
|
|
1639
|
+
)
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
function ChildComponent() {
|
|
1643
|
+
const context = useTabIndex()
|
|
1644
|
+
const tabIndex = context?.tabIndex ?? 0
|
|
1645
|
+
return <div>Tab: {tabIndex}</div>
|
|
1646
|
+
}
|
|
1647
|
+
```
|
|
1648
|
+
|
|
1649
|
+
### ExternalFieldProvider
|
|
1650
|
+
|
|
1651
|
+
Provider for automatic field detection in external forms.
|
|
1652
|
+
|
|
1653
|
+
```tsx
|
|
1654
|
+
import { ExternalFieldProvider } from '@algodomain/smart-forms'
|
|
1655
|
+
|
|
1656
|
+
function ExternalForm() {
|
|
1657
|
+
const registerField = (fieldName: string) => {
|
|
1658
|
+
// Register field with parent form
|
|
1659
|
+
console.log('Registering field:', fieldName)
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
return (
|
|
1663
|
+
<ExternalFieldProvider registerField={registerField}>
|
|
1664
|
+
<SmartInput field="name" label="Name" />
|
|
1665
|
+
<SmartInput field="email" label="Email" />
|
|
1666
|
+
</ExternalFieldProvider>
|
|
1667
|
+
)
|
|
1668
|
+
}
|
|
1669
|
+
```
|
|
1670
|
+
|
|
1062
1671
|
---
|
|
1063
1672
|
|
|
1064
1673
|
## 💡 Examples
|
|
@@ -1066,7 +1675,7 @@ function CustomField({ field }) {
|
|
|
1066
1675
|
### Complete Registration Form
|
|
1067
1676
|
|
|
1068
1677
|
```tsx
|
|
1069
|
-
import { SmartForm } from '@algodomain/smart-forms'
|
|
1678
|
+
import { SmartForm, FormHeader, Section, Footer } from '@algodomain/smart-forms'
|
|
1070
1679
|
import { SmartInput, SmartSelect, SmartCheckbox, SmartDatePicker } from '@algodomain/smart-forms/fields'
|
|
1071
1680
|
import { FieldEmail, FieldPassword } from '@algodomain/smart-forms/opinionated'
|
|
1072
1681
|
import { z } from 'zod'
|
|
@@ -1086,6 +1695,13 @@ export function RegistrationForm() {
|
|
|
1086
1695
|
toast.error('Registration failed.')
|
|
1087
1696
|
}}
|
|
1088
1697
|
>
|
|
1698
|
+
<FormHeader
|
|
1699
|
+
title="Create Your Account"
|
|
1700
|
+
subTitle="Join us today and get started"
|
|
1701
|
+
/>
|
|
1702
|
+
|
|
1703
|
+
<Section text="Personal Information" />
|
|
1704
|
+
|
|
1089
1705
|
<SmartInput
|
|
1090
1706
|
field="fullName"
|
|
1091
1707
|
label="Full Name"
|
|
@@ -1096,6 +1712,8 @@ export function RegistrationForm() {
|
|
|
1096
1712
|
<FieldEmail field="email" required />
|
|
1097
1713
|
<FieldPassword field="password" required />
|
|
1098
1714
|
|
|
1715
|
+
<Section text="Location" />
|
|
1716
|
+
|
|
1099
1717
|
<SmartSelect
|
|
1100
1718
|
field="country"
|
|
1101
1719
|
label="Country"
|
|
@@ -1121,12 +1739,20 @@ export function RegistrationForm() {
|
|
|
1121
1739
|
required
|
|
1122
1740
|
/>
|
|
1123
1741
|
|
|
1742
|
+
<Section text="Agreements" />
|
|
1743
|
+
|
|
1124
1744
|
<SmartCheckbox
|
|
1125
1745
|
field="terms"
|
|
1126
1746
|
label="I agree to the Terms and Conditions"
|
|
1127
1747
|
validation={z.boolean().refine(val => val === true)}
|
|
1128
1748
|
required
|
|
1129
1749
|
/>
|
|
1750
|
+
|
|
1751
|
+
<Footer>
|
|
1752
|
+
<p className="text-sm text-gray-500 text-center">
|
|
1753
|
+
By submitting, you agree to our terms and conditions.
|
|
1754
|
+
</p>
|
|
1755
|
+
</Footer>
|
|
1130
1756
|
</SmartForm>
|
|
1131
1757
|
)
|
|
1132
1758
|
}
|
|
@@ -1135,7 +1761,7 @@ export function RegistrationForm() {
|
|
|
1135
1761
|
### Multi-Tab Job Application
|
|
1136
1762
|
|
|
1137
1763
|
```tsx
|
|
1138
|
-
import { MultiTabSmartForm, Tab } from '@algodomain/smart-forms'
|
|
1764
|
+
import { MultiTabSmartForm, Tab, LoadingSpinner } from '@algodomain/smart-forms'
|
|
1139
1765
|
import { SmartInput, SmartTags, SmartDualRangeSlider } from '@algodomain/smart-forms/fields'
|
|
1140
1766
|
import { FieldEmail, FieldPhone } from '@algodomain/smart-forms/opinionated'
|
|
1141
1767
|
import { z } from 'zod'
|
|
@@ -1147,11 +1773,23 @@ export function JobApplicationForm() {
|
|
|
1147
1773
|
method="POST"
|
|
1148
1774
|
showProgressBar={true}
|
|
1149
1775
|
showTabNumbers={true}
|
|
1776
|
+
animateTabs={true}
|
|
1777
|
+
tabType="underline"
|
|
1778
|
+
disableManualTabSwitch={true}
|
|
1150
1779
|
allowSaveDraft={true}
|
|
1151
1780
|
enableLocalStorage={true}
|
|
1152
1781
|
storageKey="job-application"
|
|
1153
1782
|
>
|
|
1154
|
-
<Tab
|
|
1783
|
+
<Tab
|
|
1784
|
+
title="Personal Info"
|
|
1785
|
+
onNext={async () => {
|
|
1786
|
+
// Validate with external API before proceeding
|
|
1787
|
+
const response = await fetch('/api/validate-personal-info')
|
|
1788
|
+
if (!response.ok) {
|
|
1789
|
+
throw new Error('Personal information validation failed')
|
|
1790
|
+
}
|
|
1791
|
+
}}
|
|
1792
|
+
>
|
|
1155
1793
|
<SmartInput
|
|
1156
1794
|
field="fullName"
|
|
1157
1795
|
label="Full Name"
|
|
@@ -1162,7 +1800,15 @@ export function JobApplicationForm() {
|
|
|
1162
1800
|
<FieldPhone field="phone" required />
|
|
1163
1801
|
</Tab>
|
|
1164
1802
|
|
|
1165
|
-
<Tab
|
|
1803
|
+
<Tab
|
|
1804
|
+
title="Experience"
|
|
1805
|
+
processingOverlay={
|
|
1806
|
+
<div className="flex flex-col items-center gap-2">
|
|
1807
|
+
<LoadingSpinner className="h-8 w-8" />
|
|
1808
|
+
<p>Processing experience data...</p>
|
|
1809
|
+
</div>
|
|
1810
|
+
}
|
|
1811
|
+
>
|
|
1166
1812
|
<SmartDualRangeSlider
|
|
1167
1813
|
minField="minExperience"
|
|
1168
1814
|
maxField="maxExperience"
|
|
@@ -1183,6 +1829,41 @@ export function JobApplicationForm() {
|
|
|
1183
1829
|
}
|
|
1184
1830
|
```
|
|
1185
1831
|
|
|
1832
|
+
### Form with External Components
|
|
1833
|
+
|
|
1834
|
+
```tsx
|
|
1835
|
+
import { MultiTabSmartForm, Tab, useExternalTab, ExternalFieldProvider } from '@algodomain/smart-forms'
|
|
1836
|
+
import { SmartInput } from '@algodomain/smart-forms/fields'
|
|
1837
|
+
|
|
1838
|
+
// External form component
|
|
1839
|
+
function ExternalAddressForm() {
|
|
1840
|
+
const { registerField, ExternalFieldProvider } = useExternalTab()
|
|
1841
|
+
|
|
1842
|
+
return (
|
|
1843
|
+
<ExternalFieldProvider registerField={registerField}>
|
|
1844
|
+
<SmartInput field="street" label="Street" required />
|
|
1845
|
+
<SmartInput field="city" label="City" required />
|
|
1846
|
+
<SmartInput field="zipCode" label="Zip Code" required />
|
|
1847
|
+
</ExternalFieldProvider>
|
|
1848
|
+
)
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
// Main form
|
|
1852
|
+
export function FormWithExternalComponents() {
|
|
1853
|
+
return (
|
|
1854
|
+
<MultiTabSmartForm api="/api/submit">
|
|
1855
|
+
<Tab title="Basic Info">
|
|
1856
|
+
<SmartInput field="name" label="Name" required />
|
|
1857
|
+
</Tab>
|
|
1858
|
+
|
|
1859
|
+
<Tab title="Address">
|
|
1860
|
+
<ExternalAddressForm />
|
|
1861
|
+
</Tab>
|
|
1862
|
+
</MultiTabSmartForm>
|
|
1863
|
+
)
|
|
1864
|
+
}
|
|
1865
|
+
```
|
|
1866
|
+
|
|
1186
1867
|
### Form with Query Parameters
|
|
1187
1868
|
|
|
1188
1869
|
```tsx
|