@algodomain/smart-forms 0.1.8 → 0.1.9
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 +652 -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 +475 -8326
- 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 +397 -8249
- package/dist/index.js.map +1 -1
- package/dist/opinionated.cjs +18 -18
- package/dist/opinionated.js +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,34 @@ 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
|
+
|
|
224
|
+
**Tab Component Props:**
|
|
225
|
+
|
|
226
|
+
| Prop | Type | Description |
|
|
227
|
+
|------|------|-------------|
|
|
228
|
+
| `title` | `string` | **Required.** Tab title |
|
|
229
|
+
| `children` | `ReactNode` | Tab content |
|
|
230
|
+
| `onNext` | `() => Promise<void> \| void` | Callback executed before moving to next tab. Can be async. If it throws, navigation is prevented. |
|
|
231
|
+
| `processingOverlay` | `ReactNode` | Overlay shown while `onNext` is processing |
|
|
232
|
+
|
|
212
233
|
### BaseSmartForm
|
|
213
234
|
|
|
214
235
|
Low-level form component for custom layouts.
|
|
@@ -224,6 +245,63 @@ Low-level form component for custom layouts.
|
|
|
224
245
|
</BaseSmartForm>
|
|
225
246
|
```
|
|
226
247
|
|
|
248
|
+
### FormFieldGroup
|
|
249
|
+
|
|
250
|
+
A layout component for grouping form fields horizontally with responsive spacing. Fields automatically wrap to multiple rows on smaller screens.
|
|
251
|
+
|
|
252
|
+
**Usage:**
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
import { FormFieldGroup } from '@algodomain/smart-forms'
|
|
256
|
+
import { SmartInput, SmartSelect } from '@algodomain/smart-forms/fields'
|
|
257
|
+
|
|
258
|
+
<SmartForm api="/api/submit">
|
|
259
|
+
<FormFieldGroup>
|
|
260
|
+
<SmartInput field="firstName" label="First Name" />
|
|
261
|
+
<SmartInput field="lastName" label="Last Name" />
|
|
262
|
+
</FormFieldGroup>
|
|
263
|
+
|
|
264
|
+
<FormFieldGroup>
|
|
265
|
+
<SmartInput field="city" label="City" />
|
|
266
|
+
<SmartSelect field="state" label="State" options={stateOptions} />
|
|
267
|
+
<SmartInput field="zipCode" label="Zip Code" />
|
|
268
|
+
</FormFieldGroup>
|
|
269
|
+
</SmartForm>
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Props:**
|
|
273
|
+
|
|
274
|
+
| Prop | Type | Default | Description |
|
|
275
|
+
|------|------|---------|-------------|
|
|
276
|
+
| `children` | `ReactNode` | - | **Required.** Form field components to group |
|
|
277
|
+
| `className` | `string` | `''` | Additional CSS classes to apply |
|
|
278
|
+
|
|
279
|
+
**Features:**
|
|
280
|
+
- Responsive flex layout with automatic wrapping
|
|
281
|
+
- Spacing: `gap-2` on mobile, `gap-4` on medium screens and above
|
|
282
|
+
- Bottom margin: `mb-4` for consistent spacing between groups
|
|
283
|
+
|
|
284
|
+
**Example: Address Form with Grouped Fields**
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
<SmartForm api="/api/submit">
|
|
288
|
+
<FormFieldGroup>
|
|
289
|
+
<SmartInput field="streetAddress" label="Street Address" required />
|
|
290
|
+
</FormFieldGroup>
|
|
291
|
+
|
|
292
|
+
<FormFieldGroup>
|
|
293
|
+
<SmartInput field="city" label="City" required />
|
|
294
|
+
<SmartSelect field="state" label="State" options={states} required />
|
|
295
|
+
<SmartInput field="zipCode" label="Zip Code" required />
|
|
296
|
+
</FormFieldGroup>
|
|
297
|
+
|
|
298
|
+
<FormFieldGroup className="gap-6">
|
|
299
|
+
<SmartInput field="country" label="Country" required />
|
|
300
|
+
<SmartSelect field="phoneCode" label="Phone Code" options={phoneCodes} />
|
|
301
|
+
</FormFieldGroup>
|
|
302
|
+
</SmartForm>
|
|
303
|
+
```
|
|
304
|
+
|
|
227
305
|
---
|
|
228
306
|
|
|
229
307
|
## 🎯 Field Components
|
|
@@ -477,6 +555,8 @@ All form components (`SmartForm`, `MultiTabSmartForm`, `BaseSmartForm`) support
|
|
|
477
555
|
| `showReset` | `boolean` | `false` | Show reset button |
|
|
478
556
|
| `showProgressBar` | `boolean` | `true` | Display progress bar (MultiTab only) |
|
|
479
557
|
| `showTabNumbers` | `boolean` | `true` | Show tab numbers (MultiTab only) |
|
|
558
|
+
| `animateTabs` | `boolean` | `false` | Enable tab animations (MultiTab only) |
|
|
559
|
+
| `tabType` | `'underline' \| 'default'` | `'default'` | Tab styling variant (MultiTab only) |
|
|
480
560
|
| `authentication` | `AuthenticationConfig` | - | Authentication configuration |
|
|
481
561
|
| `includeQueryParams` | `boolean` | `false` | Include URL query parameters |
|
|
482
562
|
| `queryParamsToInclude` | `string[]` | - | Filter specific query params |
|
|
@@ -598,6 +678,173 @@ All field components support these props:
|
|
|
598
678
|
|
|
599
679
|
## 🚀 Advanced Features
|
|
600
680
|
|
|
681
|
+
### Tab Callbacks and Processing Overlays
|
|
682
|
+
|
|
683
|
+
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.
|
|
684
|
+
|
|
685
|
+
**Basic Usage:**
|
|
686
|
+
|
|
687
|
+
```tsx
|
|
688
|
+
<MultiTabSmartForm api="/api/submit">
|
|
689
|
+
<Tab
|
|
690
|
+
title="Step 1"
|
|
691
|
+
onNext={async () => {
|
|
692
|
+
// Custom validation or API call
|
|
693
|
+
const response = await fetch('/api/validate-step1')
|
|
694
|
+
if (!response.ok) {
|
|
695
|
+
throw new Error('Validation failed')
|
|
696
|
+
}
|
|
697
|
+
}}
|
|
698
|
+
>
|
|
699
|
+
<SmartInput field="name" label="Name" required />
|
|
700
|
+
</Tab>
|
|
701
|
+
|
|
702
|
+
<Tab title="Step 2">
|
|
703
|
+
<SmartInput field="email" label="Email" required />
|
|
704
|
+
</Tab>
|
|
705
|
+
</MultiTabSmartForm>
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
**With Processing Overlay:**
|
|
709
|
+
|
|
710
|
+
```tsx
|
|
711
|
+
import { LoadingSpinner } from '@algodomain/smart-forms'
|
|
712
|
+
|
|
713
|
+
<MultiTabSmartForm api="/api/submit">
|
|
714
|
+
<Tab
|
|
715
|
+
title="Processing Step"
|
|
716
|
+
onNext={async () => {
|
|
717
|
+
// Long-running operation
|
|
718
|
+
await processData()
|
|
719
|
+
}}
|
|
720
|
+
processingOverlay={
|
|
721
|
+
<div className="flex flex-col items-center gap-2">
|
|
722
|
+
<LoadingSpinner className="h-8 w-8" />
|
|
723
|
+
<p>Processing your data...</p>
|
|
724
|
+
</div>
|
|
725
|
+
}
|
|
726
|
+
>
|
|
727
|
+
<SmartInput field="data" label="Data" required />
|
|
728
|
+
</Tab>
|
|
729
|
+
</MultiTabSmartForm>
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
**Error Handling:**
|
|
733
|
+
|
|
734
|
+
If `onNext` throws an error, navigation to the next tab is prevented and the error message is displayed:
|
|
735
|
+
|
|
736
|
+
```tsx
|
|
737
|
+
<Tab
|
|
738
|
+
title="Step 1"
|
|
739
|
+
onNext={async () => {
|
|
740
|
+
const result = await validateData()
|
|
741
|
+
if (!result.valid) {
|
|
742
|
+
throw new Error(result.message) // Navigation prevented, error shown
|
|
743
|
+
}
|
|
744
|
+
}}
|
|
745
|
+
>
|
|
746
|
+
{/* Fields */}
|
|
747
|
+
</Tab>
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
### External Forms Integration
|
|
751
|
+
|
|
752
|
+
You can integrate external forms (forms outside the MultiTabSmartForm component) with the tab system for validation and navigation.
|
|
753
|
+
|
|
754
|
+
**Method 1: Manual Registration (Recommended for known fields)**
|
|
755
|
+
|
|
756
|
+
```tsx
|
|
757
|
+
import { useExternalFormFields } from '@algodomain/smart-forms'
|
|
758
|
+
|
|
759
|
+
function ExternalForm() {
|
|
760
|
+
// Register fields for tab index 0
|
|
761
|
+
useExternalFormFields(['name', 'email', 'phone'], 0)
|
|
762
|
+
|
|
763
|
+
return (
|
|
764
|
+
<div>
|
|
765
|
+
<input name="name" />
|
|
766
|
+
<input name="email" />
|
|
767
|
+
<input name="phone" />
|
|
768
|
+
</div>
|
|
769
|
+
)
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// In your MultiTabSmartForm
|
|
773
|
+
<MultiTabSmartForm api="/api/submit">
|
|
774
|
+
<Tab title="Step 1">
|
|
775
|
+
<ExternalForm />
|
|
776
|
+
</Tab>
|
|
777
|
+
</MultiTabSmartForm>
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
**Method 2: Auto-Detection (Recommended for dynamic fields)**
|
|
781
|
+
|
|
782
|
+
```tsx
|
|
783
|
+
import { useExternalTab, ExternalFieldProvider } from '@algodomain/smart-forms'
|
|
784
|
+
import { SmartInput } from '@algodomain/smart-forms/fields'
|
|
785
|
+
|
|
786
|
+
function ExternalForm() {
|
|
787
|
+
const { registerField, ExternalFieldProvider } = useExternalTab()
|
|
788
|
+
|
|
789
|
+
return (
|
|
790
|
+
<ExternalFieldProvider registerField={registerField}>
|
|
791
|
+
<SmartInput field="name" label="Name" />
|
|
792
|
+
<SmartInput field="email" label="Email" />
|
|
793
|
+
</ExternalFieldProvider>
|
|
794
|
+
)
|
|
795
|
+
}
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
**Method 3: Using TabIndexProvider**
|
|
799
|
+
|
|
800
|
+
```tsx
|
|
801
|
+
import { TabIndexProvider, useExternalFormFields } from '@algodomain/smart-forms'
|
|
802
|
+
|
|
803
|
+
function ExternalForm() {
|
|
804
|
+
const tabIndexContext = useTabIndex()
|
|
805
|
+
const tabIndex = tabIndexContext?.tabIndex ?? 0
|
|
806
|
+
|
|
807
|
+
useExternalFormFields(['field1', 'field2'], tabIndex)
|
|
808
|
+
|
|
809
|
+
return <div>{/* Your form */}</div>
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Wrap with TabIndexProvider
|
|
813
|
+
<TabIndexProvider tabIndex={0}>
|
|
814
|
+
<ExternalForm />
|
|
815
|
+
</TabIndexProvider>
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
### Submit Hooks
|
|
819
|
+
|
|
820
|
+
Register custom hooks to execute before form submission. Useful for file uploads or other async operations.
|
|
821
|
+
|
|
822
|
+
```tsx
|
|
823
|
+
import { useSmartForm } from '@algodomain/smart-forms'
|
|
824
|
+
|
|
825
|
+
function FileUploadField() {
|
|
826
|
+
const { registerSubmitHook, unregisterSubmitHook } = useSmartForm()
|
|
827
|
+
const field = 'resume'
|
|
828
|
+
|
|
829
|
+
useEffect(() => {
|
|
830
|
+
const key = `upload-${field}`
|
|
831
|
+
|
|
832
|
+
const uploadHook = async () => {
|
|
833
|
+
// Upload file before form submission
|
|
834
|
+
await uploadFile(file)
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
registerSubmitHook(key, uploadHook)
|
|
838
|
+
|
|
839
|
+
return () => {
|
|
840
|
+
unregisterSubmitHook(key)
|
|
841
|
+
}
|
|
842
|
+
}, [field, file])
|
|
843
|
+
|
|
844
|
+
return <input type="file" />
|
|
845
|
+
}
|
|
846
|
+
```
|
|
847
|
+
|
|
601
848
|
### Conditional Field Disabling
|
|
602
849
|
|
|
603
850
|
Disable fields or submit buttons based on form data values. Supports both static boolean values and dynamic functions.
|
|
@@ -946,6 +1193,146 @@ The library will:
|
|
|
946
1193
|
|
|
947
1194
|
---
|
|
948
1195
|
|
|
1196
|
+
## 🧩 UI Components
|
|
1197
|
+
|
|
1198
|
+
### FormHeader
|
|
1199
|
+
|
|
1200
|
+
Display form title, subtitle, and logo.
|
|
1201
|
+
|
|
1202
|
+
```tsx
|
|
1203
|
+
import { FormHeader } from '@algodomain/smart-forms'
|
|
1204
|
+
|
|
1205
|
+
<FormHeader
|
|
1206
|
+
title="Registration Form"
|
|
1207
|
+
subTitle="Please fill in your details"
|
|
1208
|
+
logo={<img src="/logo.png" alt="Logo" />}
|
|
1209
|
+
/>
|
|
1210
|
+
```
|
|
1211
|
+
|
|
1212
|
+
### Section
|
|
1213
|
+
|
|
1214
|
+
Section divider with optional text.
|
|
1215
|
+
|
|
1216
|
+
```tsx
|
|
1217
|
+
import { Section } from '@algodomain/smart-forms'
|
|
1218
|
+
|
|
1219
|
+
<Section text="Personal Information" />
|
|
1220
|
+
```
|
|
1221
|
+
|
|
1222
|
+
### Footer
|
|
1223
|
+
|
|
1224
|
+
Footer component for form content.
|
|
1225
|
+
|
|
1226
|
+
```tsx
|
|
1227
|
+
import { Footer } from '@algodomain/smart-forms'
|
|
1228
|
+
|
|
1229
|
+
<Footer>
|
|
1230
|
+
<p className="text-sm text-gray-500">
|
|
1231
|
+
By submitting, you agree to our terms and conditions.
|
|
1232
|
+
</p>
|
|
1233
|
+
</Footer>
|
|
1234
|
+
```
|
|
1235
|
+
|
|
1236
|
+
### ToastContainerWrapper
|
|
1237
|
+
|
|
1238
|
+
Toast notification container (automatically included in forms).
|
|
1239
|
+
|
|
1240
|
+
```tsx
|
|
1241
|
+
import { ToastContainerWrapper } from '@algodomain/smart-forms'
|
|
1242
|
+
|
|
1243
|
+
<ToastContainerWrapper />
|
|
1244
|
+
```
|
|
1245
|
+
|
|
1246
|
+
## 🔘 Button Components
|
|
1247
|
+
|
|
1248
|
+
### LoadingSpinner
|
|
1249
|
+
|
|
1250
|
+
Loading spinner component.
|
|
1251
|
+
|
|
1252
|
+
```tsx
|
|
1253
|
+
import { LoadingSpinner } from '@algodomain/smart-forms'
|
|
1254
|
+
|
|
1255
|
+
<LoadingSpinner className="h-6 w-6" />
|
|
1256
|
+
```
|
|
1257
|
+
|
|
1258
|
+
### SubmitButton
|
|
1259
|
+
|
|
1260
|
+
Submit button with loading state.
|
|
1261
|
+
|
|
1262
|
+
```tsx
|
|
1263
|
+
import { SubmitButton } from '@algodomain/smart-forms'
|
|
1264
|
+
|
|
1265
|
+
<SubmitButton
|
|
1266
|
+
onClick={handleSubmit}
|
|
1267
|
+
disabled={isLoading}
|
|
1268
|
+
isLoading={isLoading}
|
|
1269
|
+
>
|
|
1270
|
+
Submit Form
|
|
1271
|
+
</SubmitButton>
|
|
1272
|
+
```
|
|
1273
|
+
|
|
1274
|
+
### DraftSaveButton
|
|
1275
|
+
|
|
1276
|
+
Draft save button.
|
|
1277
|
+
|
|
1278
|
+
```tsx
|
|
1279
|
+
import { DraftSaveButton } from '@algodomain/smart-forms'
|
|
1280
|
+
|
|
1281
|
+
<DraftSaveButton
|
|
1282
|
+
onClick={handleSaveDraft}
|
|
1283
|
+
disabled={isDraftSaving}
|
|
1284
|
+
/>
|
|
1285
|
+
```
|
|
1286
|
+
|
|
1287
|
+
### ResetButton
|
|
1288
|
+
|
|
1289
|
+
Reset form button.
|
|
1290
|
+
|
|
1291
|
+
```tsx
|
|
1292
|
+
import { ResetButton } from '@algodomain/smart-forms'
|
|
1293
|
+
|
|
1294
|
+
<ResetButton onClick={handleReset} />
|
|
1295
|
+
```
|
|
1296
|
+
|
|
1297
|
+
### NavigationButtons
|
|
1298
|
+
|
|
1299
|
+
Navigation buttons for multi-tab forms.
|
|
1300
|
+
|
|
1301
|
+
```tsx
|
|
1302
|
+
import { NavigationButtons } from '@algodomain/smart-forms'
|
|
1303
|
+
|
|
1304
|
+
<NavigationButtons
|
|
1305
|
+
onPrevious={handlePrevious}
|
|
1306
|
+
onNext={handleNext}
|
|
1307
|
+
onSubmit={handleSubmit}
|
|
1308
|
+
onSaveDraft={handleSaveDraft}
|
|
1309
|
+
onReset={handleReset}
|
|
1310
|
+
isLoading={isLoading}
|
|
1311
|
+
isDraftSaving={isDraftSaving}
|
|
1312
|
+
config={config}
|
|
1313
|
+
isFirstTab={false}
|
|
1314
|
+
isLastTab={false}
|
|
1315
|
+
disabled={false}
|
|
1316
|
+
/>
|
|
1317
|
+
```
|
|
1318
|
+
|
|
1319
|
+
### SimpleFormButtons
|
|
1320
|
+
|
|
1321
|
+
Simple button group for single-page forms.
|
|
1322
|
+
|
|
1323
|
+
```tsx
|
|
1324
|
+
import { SimpleFormButtons } from '@algodomain/smart-forms'
|
|
1325
|
+
|
|
1326
|
+
<SimpleFormButtons
|
|
1327
|
+
onSubmit={handleSubmit}
|
|
1328
|
+
onSaveDraft={handleSaveDraft}
|
|
1329
|
+
onReset={handleReset}
|
|
1330
|
+
isLoading={isLoading}
|
|
1331
|
+
isDraftSaving={isDraftSaving}
|
|
1332
|
+
config={config}
|
|
1333
|
+
/>
|
|
1334
|
+
```
|
|
1335
|
+
|
|
949
1336
|
## 🎨 Theming & Customization
|
|
950
1337
|
|
|
951
1338
|
### CSS Variables
|
|
@@ -1025,21 +1412,36 @@ import { useSmartForm } from '@algodomain/smart-forms'
|
|
|
1025
1412
|
|
|
1026
1413
|
function MyComponent() {
|
|
1027
1414
|
const {
|
|
1028
|
-
formData,
|
|
1029
|
-
errors,
|
|
1030
|
-
isLoading,
|
|
1031
|
-
isDraftSaving,
|
|
1032
|
-
submitForm,
|
|
1033
|
-
saveDraft,
|
|
1034
|
-
resetForm,
|
|
1035
|
-
updateField,
|
|
1036
|
-
|
|
1415
|
+
formData, // Current form values
|
|
1416
|
+
errors, // Validation errors
|
|
1417
|
+
isLoading, // Submission loading state
|
|
1418
|
+
isDraftSaving, // Draft saving state
|
|
1419
|
+
submitForm, // Submit function
|
|
1420
|
+
saveDraft, // Save draft function
|
|
1421
|
+
resetForm, // Reset to initial values
|
|
1422
|
+
updateField, // Update specific field
|
|
1423
|
+
validateField, // Validate a single field
|
|
1424
|
+
validateFields, // Validate multiple fields
|
|
1425
|
+
validateFormAndGetResult, // Validate entire form and return boolean
|
|
1426
|
+
registerSubmitHook, // Register a hook to run before submission
|
|
1427
|
+
unregisterSubmitHook, // Unregister a submit hook
|
|
1428
|
+
setErrors, // Manually set validation errors
|
|
1429
|
+
config // Form configuration
|
|
1037
1430
|
} = useSmartForm()
|
|
1038
1431
|
|
|
1039
1432
|
return <button onClick={submitForm}>Submit</button>
|
|
1040
1433
|
}
|
|
1041
1434
|
```
|
|
1042
1435
|
|
|
1436
|
+
**Methods:**
|
|
1437
|
+
|
|
1438
|
+
- `validateField(field: string, value: any): boolean` - Validate a single field
|
|
1439
|
+
- `validateFields(fields: string[]): boolean` - Validate multiple fields
|
|
1440
|
+
- `validateFormAndGetResult(): boolean` - Validate entire form and return result
|
|
1441
|
+
- `registerSubmitHook(key: string, hook: () => Promise<void>): void` - Register a hook to execute before form submission
|
|
1442
|
+
- `unregisterSubmitHook(key: string): void` - Unregister a submit hook
|
|
1443
|
+
- `setErrors(errors: Record<string, string>): void` - Manually set validation errors
|
|
1444
|
+
|
|
1043
1445
|
### useFormField
|
|
1044
1446
|
|
|
1045
1447
|
Access individual field state (used internally by field components).
|
|
@@ -1059,6 +1461,170 @@ function CustomField({ field }) {
|
|
|
1059
1461
|
}
|
|
1060
1462
|
```
|
|
1061
1463
|
|
|
1464
|
+
### useFormWrapper
|
|
1465
|
+
|
|
1466
|
+
Alias for `useSmartForm` (for convenience).
|
|
1467
|
+
|
|
1468
|
+
```tsx
|
|
1469
|
+
import { useFormWrapper } from '@algodomain/smart-forms'
|
|
1470
|
+
|
|
1471
|
+
function MyComponent() {
|
|
1472
|
+
const { formData, submitForm } = useFormWrapper()
|
|
1473
|
+
// Same as useSmartForm
|
|
1474
|
+
}
|
|
1475
|
+
```
|
|
1476
|
+
|
|
1477
|
+
### useTabIndex
|
|
1478
|
+
|
|
1479
|
+
Get the current tab index in a MultiTabSmartForm.
|
|
1480
|
+
|
|
1481
|
+
```tsx
|
|
1482
|
+
import { useTabIndex } from '@algodomain/smart-forms'
|
|
1483
|
+
|
|
1484
|
+
function MyComponent() {
|
|
1485
|
+
const tabIndexContext = useTabIndex()
|
|
1486
|
+
const tabIndex = tabIndexContext?.tabIndex ?? 0
|
|
1487
|
+
|
|
1488
|
+
return <div>Current tab: {tabIndex + 1}</div>
|
|
1489
|
+
}
|
|
1490
|
+
```
|
|
1491
|
+
|
|
1492
|
+
### useExternalFormRegistration
|
|
1493
|
+
|
|
1494
|
+
Register external forms with MultiTabSmartForm for validation and tab navigation.
|
|
1495
|
+
|
|
1496
|
+
```tsx
|
|
1497
|
+
import { useExternalFormRegistration } from '@algodomain/smart-forms'
|
|
1498
|
+
|
|
1499
|
+
function ExternalForm() {
|
|
1500
|
+
const { registerTabFields, currentTabIndex } = useExternalFormRegistration()
|
|
1501
|
+
|
|
1502
|
+
useEffect(() => {
|
|
1503
|
+
registerTabFields(currentTabIndex, ['field1', 'field2'])
|
|
1504
|
+
}, [])
|
|
1505
|
+
|
|
1506
|
+
return <div>{/* Your form fields */}</div>
|
|
1507
|
+
}
|
|
1508
|
+
```
|
|
1509
|
+
|
|
1510
|
+
### useExternalFormFields
|
|
1511
|
+
|
|
1512
|
+
Register fields from external forms with a specific tab index.
|
|
1513
|
+
|
|
1514
|
+
```tsx
|
|
1515
|
+
import { useExternalFormFields } from '@algodomain/smart-forms'
|
|
1516
|
+
|
|
1517
|
+
function ExternalForm() {
|
|
1518
|
+
useExternalFormFields(['field1', 'field2'], 0) // Register fields for tab 0
|
|
1519
|
+
|
|
1520
|
+
return <div>{/* Your form fields */}</div>
|
|
1521
|
+
}
|
|
1522
|
+
```
|
|
1523
|
+
|
|
1524
|
+
### useAutoDetectFields
|
|
1525
|
+
|
|
1526
|
+
Automatically detect and register fields in external forms.
|
|
1527
|
+
|
|
1528
|
+
```tsx
|
|
1529
|
+
import { useAutoDetectFields, ExternalFieldProvider } from '@algodomain/smart-forms'
|
|
1530
|
+
|
|
1531
|
+
function ExternalForm() {
|
|
1532
|
+
const { registerField, ExternalFieldProvider } = useAutoDetectFields(0)
|
|
1533
|
+
|
|
1534
|
+
return (
|
|
1535
|
+
<ExternalFieldProvider registerField={registerField}>
|
|
1536
|
+
<SmartInput field="name" label="Name" />
|
|
1537
|
+
<SmartInput field="email" label="Email" />
|
|
1538
|
+
</ExternalFieldProvider>
|
|
1539
|
+
)
|
|
1540
|
+
}
|
|
1541
|
+
```
|
|
1542
|
+
|
|
1543
|
+
### useExternalTab
|
|
1544
|
+
|
|
1545
|
+
Automatically detect both tab index and fields in external forms (recommended).
|
|
1546
|
+
|
|
1547
|
+
```tsx
|
|
1548
|
+
import { useExternalTab, ExternalFieldProvider } from '@algodomain/smart-forms'
|
|
1549
|
+
|
|
1550
|
+
function ExternalForm() {
|
|
1551
|
+
const { registerField, ExternalFieldProvider } = useExternalTab()
|
|
1552
|
+
|
|
1553
|
+
return (
|
|
1554
|
+
<ExternalFieldProvider registerField={registerField}>
|
|
1555
|
+
<SmartInput field="name" label="Name" />
|
|
1556
|
+
<SmartInput field="email" label="Email" />
|
|
1557
|
+
</ExternalFieldProvider>
|
|
1558
|
+
)
|
|
1559
|
+
}
|
|
1560
|
+
```
|
|
1561
|
+
|
|
1562
|
+
### useFieldDetection
|
|
1563
|
+
|
|
1564
|
+
Access field detection context (used internally by field components).
|
|
1565
|
+
|
|
1566
|
+
```tsx
|
|
1567
|
+
import { useFieldDetection } from '@algodomain/smart-forms'
|
|
1568
|
+
|
|
1569
|
+
function CustomField({ field }) {
|
|
1570
|
+
const context = useFieldDetection()
|
|
1571
|
+
|
|
1572
|
+
useEffect(() => {
|
|
1573
|
+
if (context) {
|
|
1574
|
+
context.registerField(field)
|
|
1575
|
+
}
|
|
1576
|
+
}, [field, context])
|
|
1577
|
+
|
|
1578
|
+
return <input />
|
|
1579
|
+
}
|
|
1580
|
+
```
|
|
1581
|
+
|
|
1582
|
+
## 🔌 Context Providers
|
|
1583
|
+
|
|
1584
|
+
### TabIndexProvider
|
|
1585
|
+
|
|
1586
|
+
Provide tab index context to child components.
|
|
1587
|
+
|
|
1588
|
+
```tsx
|
|
1589
|
+
import { TabIndexProvider, useTabIndex } from '@algodomain/smart-forms'
|
|
1590
|
+
|
|
1591
|
+
function MyComponent() {
|
|
1592
|
+
return (
|
|
1593
|
+
<TabIndexProvider tabIndex={0}>
|
|
1594
|
+
<ChildComponent />
|
|
1595
|
+
</TabIndexProvider>
|
|
1596
|
+
)
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
function ChildComponent() {
|
|
1600
|
+
const context = useTabIndex()
|
|
1601
|
+
const tabIndex = context?.tabIndex ?? 0
|
|
1602
|
+
return <div>Tab: {tabIndex}</div>
|
|
1603
|
+
}
|
|
1604
|
+
```
|
|
1605
|
+
|
|
1606
|
+
### ExternalFieldProvider
|
|
1607
|
+
|
|
1608
|
+
Provider for automatic field detection in external forms.
|
|
1609
|
+
|
|
1610
|
+
```tsx
|
|
1611
|
+
import { ExternalFieldProvider } from '@algodomain/smart-forms'
|
|
1612
|
+
|
|
1613
|
+
function ExternalForm() {
|
|
1614
|
+
const registerField = (fieldName: string) => {
|
|
1615
|
+
// Register field with parent form
|
|
1616
|
+
console.log('Registering field:', fieldName)
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
return (
|
|
1620
|
+
<ExternalFieldProvider registerField={registerField}>
|
|
1621
|
+
<SmartInput field="name" label="Name" />
|
|
1622
|
+
<SmartInput field="email" label="Email" />
|
|
1623
|
+
</ExternalFieldProvider>
|
|
1624
|
+
)
|
|
1625
|
+
}
|
|
1626
|
+
```
|
|
1627
|
+
|
|
1062
1628
|
---
|
|
1063
1629
|
|
|
1064
1630
|
## 💡 Examples
|
|
@@ -1066,7 +1632,7 @@ function CustomField({ field }) {
|
|
|
1066
1632
|
### Complete Registration Form
|
|
1067
1633
|
|
|
1068
1634
|
```tsx
|
|
1069
|
-
import { SmartForm } from '@algodomain/smart-forms'
|
|
1635
|
+
import { SmartForm, FormHeader, Section, Footer } from '@algodomain/smart-forms'
|
|
1070
1636
|
import { SmartInput, SmartSelect, SmartCheckbox, SmartDatePicker } from '@algodomain/smart-forms/fields'
|
|
1071
1637
|
import { FieldEmail, FieldPassword } from '@algodomain/smart-forms/opinionated'
|
|
1072
1638
|
import { z } from 'zod'
|
|
@@ -1086,6 +1652,13 @@ export function RegistrationForm() {
|
|
|
1086
1652
|
toast.error('Registration failed.')
|
|
1087
1653
|
}}
|
|
1088
1654
|
>
|
|
1655
|
+
<FormHeader
|
|
1656
|
+
title="Create Your Account"
|
|
1657
|
+
subTitle="Join us today and get started"
|
|
1658
|
+
/>
|
|
1659
|
+
|
|
1660
|
+
<Section text="Personal Information" />
|
|
1661
|
+
|
|
1089
1662
|
<SmartInput
|
|
1090
1663
|
field="fullName"
|
|
1091
1664
|
label="Full Name"
|
|
@@ -1096,6 +1669,8 @@ export function RegistrationForm() {
|
|
|
1096
1669
|
<FieldEmail field="email" required />
|
|
1097
1670
|
<FieldPassword field="password" required />
|
|
1098
1671
|
|
|
1672
|
+
<Section text="Location" />
|
|
1673
|
+
|
|
1099
1674
|
<SmartSelect
|
|
1100
1675
|
field="country"
|
|
1101
1676
|
label="Country"
|
|
@@ -1121,12 +1696,20 @@ export function RegistrationForm() {
|
|
|
1121
1696
|
required
|
|
1122
1697
|
/>
|
|
1123
1698
|
|
|
1699
|
+
<Section text="Agreements" />
|
|
1700
|
+
|
|
1124
1701
|
<SmartCheckbox
|
|
1125
1702
|
field="terms"
|
|
1126
1703
|
label="I agree to the Terms and Conditions"
|
|
1127
1704
|
validation={z.boolean().refine(val => val === true)}
|
|
1128
1705
|
required
|
|
1129
1706
|
/>
|
|
1707
|
+
|
|
1708
|
+
<Footer>
|
|
1709
|
+
<p className="text-sm text-gray-500 text-center">
|
|
1710
|
+
By submitting, you agree to our terms and conditions.
|
|
1711
|
+
</p>
|
|
1712
|
+
</Footer>
|
|
1130
1713
|
</SmartForm>
|
|
1131
1714
|
)
|
|
1132
1715
|
}
|
|
@@ -1135,7 +1718,7 @@ export function RegistrationForm() {
|
|
|
1135
1718
|
### Multi-Tab Job Application
|
|
1136
1719
|
|
|
1137
1720
|
```tsx
|
|
1138
|
-
import { MultiTabSmartForm, Tab } from '@algodomain/smart-forms'
|
|
1721
|
+
import { MultiTabSmartForm, Tab, LoadingSpinner } from '@algodomain/smart-forms'
|
|
1139
1722
|
import { SmartInput, SmartTags, SmartDualRangeSlider } from '@algodomain/smart-forms/fields'
|
|
1140
1723
|
import { FieldEmail, FieldPhone } from '@algodomain/smart-forms/opinionated'
|
|
1141
1724
|
import { z } from 'zod'
|
|
@@ -1147,11 +1730,22 @@ export function JobApplicationForm() {
|
|
|
1147
1730
|
method="POST"
|
|
1148
1731
|
showProgressBar={true}
|
|
1149
1732
|
showTabNumbers={true}
|
|
1733
|
+
animateTabs={true}
|
|
1734
|
+
tabType="underline"
|
|
1150
1735
|
allowSaveDraft={true}
|
|
1151
1736
|
enableLocalStorage={true}
|
|
1152
1737
|
storageKey="job-application"
|
|
1153
1738
|
>
|
|
1154
|
-
<Tab
|
|
1739
|
+
<Tab
|
|
1740
|
+
title="Personal Info"
|
|
1741
|
+
onNext={async () => {
|
|
1742
|
+
// Validate with external API before proceeding
|
|
1743
|
+
const response = await fetch('/api/validate-personal-info')
|
|
1744
|
+
if (!response.ok) {
|
|
1745
|
+
throw new Error('Personal information validation failed')
|
|
1746
|
+
}
|
|
1747
|
+
}}
|
|
1748
|
+
>
|
|
1155
1749
|
<SmartInput
|
|
1156
1750
|
field="fullName"
|
|
1157
1751
|
label="Full Name"
|
|
@@ -1162,7 +1756,15 @@ export function JobApplicationForm() {
|
|
|
1162
1756
|
<FieldPhone field="phone" required />
|
|
1163
1757
|
</Tab>
|
|
1164
1758
|
|
|
1165
|
-
<Tab
|
|
1759
|
+
<Tab
|
|
1760
|
+
title="Experience"
|
|
1761
|
+
processingOverlay={
|
|
1762
|
+
<div className="flex flex-col items-center gap-2">
|
|
1763
|
+
<LoadingSpinner className="h-8 w-8" />
|
|
1764
|
+
<p>Processing experience data...</p>
|
|
1765
|
+
</div>
|
|
1766
|
+
}
|
|
1767
|
+
>
|
|
1166
1768
|
<SmartDualRangeSlider
|
|
1167
1769
|
minField="minExperience"
|
|
1168
1770
|
maxField="maxExperience"
|
|
@@ -1183,6 +1785,41 @@ export function JobApplicationForm() {
|
|
|
1183
1785
|
}
|
|
1184
1786
|
```
|
|
1185
1787
|
|
|
1788
|
+
### Form with External Components
|
|
1789
|
+
|
|
1790
|
+
```tsx
|
|
1791
|
+
import { MultiTabSmartForm, Tab, useExternalTab, ExternalFieldProvider } from '@algodomain/smart-forms'
|
|
1792
|
+
import { SmartInput } from '@algodomain/smart-forms/fields'
|
|
1793
|
+
|
|
1794
|
+
// External form component
|
|
1795
|
+
function ExternalAddressForm() {
|
|
1796
|
+
const { registerField, ExternalFieldProvider } = useExternalTab()
|
|
1797
|
+
|
|
1798
|
+
return (
|
|
1799
|
+
<ExternalFieldProvider registerField={registerField}>
|
|
1800
|
+
<SmartInput field="street" label="Street" required />
|
|
1801
|
+
<SmartInput field="city" label="City" required />
|
|
1802
|
+
<SmartInput field="zipCode" label="Zip Code" required />
|
|
1803
|
+
</ExternalFieldProvider>
|
|
1804
|
+
)
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
// Main form
|
|
1808
|
+
export function FormWithExternalComponents() {
|
|
1809
|
+
return (
|
|
1810
|
+
<MultiTabSmartForm api="/api/submit">
|
|
1811
|
+
<Tab title="Basic Info">
|
|
1812
|
+
<SmartInput field="name" label="Name" required />
|
|
1813
|
+
</Tab>
|
|
1814
|
+
|
|
1815
|
+
<Tab title="Address">
|
|
1816
|
+
<ExternalAddressForm />
|
|
1817
|
+
</Tab>
|
|
1818
|
+
</MultiTabSmartForm>
|
|
1819
|
+
)
|
|
1820
|
+
}
|
|
1821
|
+
```
|
|
1822
|
+
|
|
1186
1823
|
### Form with Query Parameters
|
|
1187
1824
|
|
|
1188
1825
|
```tsx
|