suprform 1.0.6 → 1.0.7
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 +499 -363
- package/dist/components/{condition-checker/ConditionChecker.d.ts → ConditionChecker.d.ts} +1 -1
- package/dist/components/ConditionChecker.d.ts.map +1 -0
- package/dist/components/SuprForm.d.ts +4 -0
- package/dist/components/SuprForm.d.ts.map +1 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/suprform.cjs.js +1 -1
- package/dist/suprform.cjs.js.map +1 -1
- package/dist/suprform.es.js +1 -1333
- package/dist/suprform.es.js.map +1 -1
- package/dist/type.d.ts +7 -1
- package/dist/type.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/components/condition-checker/ConditionChecker.d.ts.map +0 -1
- package/dist/components/condition-checker/index.d.ts +0 -2
- package/dist/components/condition-checker/index.d.ts.map +0 -1
- package/dist/components/controlled-field/ControlledField.d.ts +0 -4
- package/dist/components/controlled-field/ControlledField.d.ts.map +0 -1
- package/dist/components/controlled-field/index.d.ts +0 -2
- package/dist/components/controlled-field/index.d.ts.map +0 -1
- package/dist/components/form/SuprForm.d.ts +0 -4
- package/dist/components/form/SuprForm.d.ts.map +0 -1
- package/dist/components/form/index.d.ts.map +0 -1
- /package/dist/components/{form/index.d.ts → index.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,363 +1,499 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
</
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
required: '
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
>
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
1
|
+
# SuprForm
|
|
2
|
+
|
|
3
|
+
A **headless React form library** that wraps [react-hook-form](https://react-hook-form.com) with a composable API for effortless form management. Design system agnostic, TypeScript-first, and built for developer experience.
|
|
4
|
+
|
|
5
|
+
## Core Features
|
|
6
|
+
|
|
7
|
+
### 🎨 **Design System Agnostic**
|
|
8
|
+
|
|
9
|
+
Use with any UI framework—Material-UI, Ant Design, Tailwind, shadcn/ui, or plain HTML. SuprForm provides the logic, you control the design.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
// Works with Material-UI
|
|
13
|
+
<SuprForm.Control name="email" rules={{ required: true }}>
|
|
14
|
+
<TextField variant="outlined" />
|
|
15
|
+
</SuprForm.Control>
|
|
16
|
+
|
|
17
|
+
// Works with plain HTML
|
|
18
|
+
<SuprForm.Control name="email" rules={{ required: true }}>
|
|
19
|
+
<input type="email" />
|
|
20
|
+
</SuprForm.Control>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 🎯 **Composable Field Control**
|
|
24
|
+
|
|
25
|
+
The `SuprForm.Control` component wraps your inputs with automatic label rendering, error display, and validation—no configuration needed.
|
|
26
|
+
|
|
27
|
+
- Automatic label and error rendering
|
|
28
|
+
- Field state tracking (touched, dirty, valid)
|
|
29
|
+
- Seamless integration with any input component
|
|
30
|
+
- Full TypeScript inference for field names and values
|
|
31
|
+
|
|
32
|
+
### 🔒 **TypeScript-First Architecture**
|
|
33
|
+
|
|
34
|
+
Complete type safety with intelligent inference throughout your forms. Field names, validation rules, and form values are all fully typed.
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
interface FormData {
|
|
38
|
+
email: string;
|
|
39
|
+
age: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
<SuprForm<FormData>
|
|
43
|
+
onSubmit={(values) => {
|
|
44
|
+
// values is typed as FormData
|
|
45
|
+
console.log(values.email); // ✓ Type-safe
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
<SuprForm.Control name='email' /> {/* ✓ Type-checked */}
|
|
49
|
+
</SuprForm>;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 👁️ **Conditional Field Visibility**
|
|
53
|
+
|
|
54
|
+
Advanced conditional rendering with declarative visibility rules. Show/hide fields based on other field values with AND/OR logic.
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<SuprForm.Control
|
|
58
|
+
name='creditCard'
|
|
59
|
+
visibility={{
|
|
60
|
+
operator: 'AND',
|
|
61
|
+
conditions: [
|
|
62
|
+
{ name: 'paymentMethod', operator: 'EQUALS', value: 'card' },
|
|
63
|
+
{ name: 'amount', operator: 'GREATER_THAN', value: 0 },
|
|
64
|
+
],
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
<input />
|
|
68
|
+
</SuprForm.Control>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Supported operators:** EQUALS, NOT_EQUALS, GREATER_THAN, LESS_THAN, STARTS_WITH, ENDS_WITH, INCLUDES
|
|
72
|
+
|
|
73
|
+
### ✅ **Declarative Validation**
|
|
74
|
+
|
|
75
|
+
Powered by react-hook-form's validation system with support for sync and async validators.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
<SuprForm.Control
|
|
79
|
+
name='username'
|
|
80
|
+
rules={{
|
|
81
|
+
required: 'Username is required',
|
|
82
|
+
minLength: { value: 3, message: 'Min 3 characters' },
|
|
83
|
+
validate: async (value) => {
|
|
84
|
+
const available = await checkAvailability(value);
|
|
85
|
+
return available || 'Username taken';
|
|
86
|
+
},
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
<input />
|
|
90
|
+
</SuprForm.Control>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 🎛️ **Imperative Form Control**
|
|
94
|
+
|
|
95
|
+
Access react-hook-form methods via ref for programmatic form manipulation.
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
const formRef = useRef();
|
|
99
|
+
|
|
100
|
+
<SuprForm ref={formRef} onSubmit={handleSubmit}>
|
|
101
|
+
{/* ... */}
|
|
102
|
+
</SuprForm>;
|
|
103
|
+
|
|
104
|
+
// Later:
|
|
105
|
+
formRef.current.setValue('email', 'test@example.com');
|
|
106
|
+
formRef.current.trigger('email'); // Manually validate
|
|
107
|
+
formRef.current.reset(); // Reset form
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Available methods:** `setValue`, `setError`, `clearErrors`, `getValues`, `reset`, `setFocus`, `resetField`, `trigger`, `unregister`, `watch`
|
|
111
|
+
|
|
112
|
+
### 📦 **Zero UI Dependencies**
|
|
113
|
+
|
|
114
|
+
Only React as a peer dependency. No CSS framework lock-in, no component library coupling. Bring your own design system.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Quick Start
|
|
119
|
+
|
|
120
|
+
### Installation
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npm install suprform
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Basic Example
|
|
127
|
+
|
|
128
|
+
The composable `SuprForm.Control` component handles everything for you:
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import SuprForm from 'suprform';
|
|
132
|
+
|
|
133
|
+
function LoginForm() {
|
|
134
|
+
return (
|
|
135
|
+
<SuprForm
|
|
136
|
+
onSubmit={(values) => {
|
|
137
|
+
console.log('Form submitted:', values);
|
|
138
|
+
}}
|
|
139
|
+
>
|
|
140
|
+
<SuprForm.Control
|
|
141
|
+
name='email'
|
|
142
|
+
label='Email Address'
|
|
143
|
+
rules={{
|
|
144
|
+
required: 'Email is required',
|
|
145
|
+
pattern: {
|
|
146
|
+
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
|
147
|
+
message: 'Invalid email address',
|
|
148
|
+
},
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
<input type='email' placeholder='you@example.com' />
|
|
152
|
+
</SuprForm.Control>
|
|
153
|
+
|
|
154
|
+
<SuprForm.Control
|
|
155
|
+
name='password'
|
|
156
|
+
label='Password'
|
|
157
|
+
rules={{
|
|
158
|
+
required: 'Password is required',
|
|
159
|
+
minLength: { value: 8, message: 'Password must be at least 8 characters' },
|
|
160
|
+
}}
|
|
161
|
+
>
|
|
162
|
+
<input type='password' placeholder='••••••••' />
|
|
163
|
+
</SuprForm.Control>
|
|
164
|
+
|
|
165
|
+
<button type='submit'>Login</button>
|
|
166
|
+
</SuprForm>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
SuprForm.Control **automatically handles**:
|
|
172
|
+
|
|
173
|
+
- Label rendering with proper `htmlFor` linking
|
|
174
|
+
- Validation on blur and change
|
|
175
|
+
- Error message display
|
|
176
|
+
- Field state management (value, touched, dirty, error)
|
|
177
|
+
- Works with any input component (HTML, Material-UI, custom, etc.)
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Advanced Usage
|
|
182
|
+
|
|
183
|
+
### With UI Component Libraries
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import SuprForm from 'suprform';
|
|
187
|
+
import { TextField, Button } from '@mui/material'; // or your own
|
|
188
|
+
|
|
189
|
+
function SignupForm() {
|
|
190
|
+
return (
|
|
191
|
+
<SuprForm
|
|
192
|
+
onSubmit={(values) => {
|
|
193
|
+
console.log('User signed up:', values);
|
|
194
|
+
}}
|
|
195
|
+
>
|
|
196
|
+
<SuprForm.Control
|
|
197
|
+
name='username'
|
|
198
|
+
label='Username'
|
|
199
|
+
rules={{
|
|
200
|
+
required: 'Username is required',
|
|
201
|
+
minLength: { value: 3, message: 'Min 3 characters' },
|
|
202
|
+
pattern: {
|
|
203
|
+
value: /^[a-zA-Z0-9_]+$/,
|
|
204
|
+
message: 'Only letters, numbers, and underscores',
|
|
205
|
+
},
|
|
206
|
+
}}
|
|
207
|
+
>
|
|
208
|
+
<TextField variant='outlined' fullWidth />
|
|
209
|
+
</SuprForm.Control>
|
|
210
|
+
|
|
211
|
+
<SuprForm.Control
|
|
212
|
+
name='email'
|
|
213
|
+
label='Email'
|
|
214
|
+
rules={{
|
|
215
|
+
required: 'Email is required',
|
|
216
|
+
pattern: {
|
|
217
|
+
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
|
218
|
+
message: 'Invalid email',
|
|
219
|
+
},
|
|
220
|
+
}}
|
|
221
|
+
>
|
|
222
|
+
<TextField type='email' variant='outlined' fullWidth />
|
|
223
|
+
</SuprForm.Control>
|
|
224
|
+
|
|
225
|
+
<Button variant='contained' type='submit'>
|
|
226
|
+
Sign Up
|
|
227
|
+
</Button>
|
|
228
|
+
</SuprForm>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Conditional Field Visibility
|
|
234
|
+
|
|
235
|
+
Show/hide fields based on other field values:
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
<SuprForm onSubmit={handleSubmit}>
|
|
239
|
+
<SuprForm.Control name='hasDiscount' label='Apply Discount?'>
|
|
240
|
+
<input type='checkbox' />
|
|
241
|
+
</SuprForm.Control>
|
|
242
|
+
|
|
243
|
+
{/* Only show when hasDiscount is checked */}
|
|
244
|
+
<SuprForm.Control
|
|
245
|
+
name='discountCode'
|
|
246
|
+
label='Discount Code'
|
|
247
|
+
visibility={{
|
|
248
|
+
operator: 'AND',
|
|
249
|
+
conditions: [{ name: 'hasDiscount', operator: 'EQUALS', value: true }],
|
|
250
|
+
}}
|
|
251
|
+
rules={{ required: 'Discount code required' }}
|
|
252
|
+
>
|
|
253
|
+
<input type='text' />
|
|
254
|
+
</SuprForm.Control>
|
|
255
|
+
</SuprForm>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Async Validation
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
<SuprForm.Control
|
|
262
|
+
name='username'
|
|
263
|
+
rules={{
|
|
264
|
+
required: 'Username is required',
|
|
265
|
+
validate: async (value) => {
|
|
266
|
+
const response = await fetch(`/api/check-username/${value}`);
|
|
267
|
+
const data = await response.json();
|
|
268
|
+
return data.available || 'Username already taken';
|
|
269
|
+
},
|
|
270
|
+
}}
|
|
271
|
+
>
|
|
272
|
+
<input />
|
|
273
|
+
</SuprForm.Control>
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Programmatic Form Control
|
|
277
|
+
|
|
278
|
+
```tsx
|
|
279
|
+
function MyForm() {
|
|
280
|
+
const formRef = useRef();
|
|
281
|
+
|
|
282
|
+
const prefillForm = () => {
|
|
283
|
+
formRef.current.setValue('email', 'user@example.com');
|
|
284
|
+
formRef.current.setFocus('password');
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
return (
|
|
288
|
+
<>
|
|
289
|
+
<button onClick={prefillForm}>Prefill</button>
|
|
290
|
+
<SuprForm ref={formRef} onSubmit={handleSubmit}>
|
|
291
|
+
<SuprForm.Control name='email'>
|
|
292
|
+
<input />
|
|
293
|
+
</SuprForm.Control>
|
|
294
|
+
<SuprForm.Control name='password'>
|
|
295
|
+
<input type='password' />
|
|
296
|
+
</SuprForm.Control>
|
|
297
|
+
</SuprForm>
|
|
298
|
+
</>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## API Reference
|
|
306
|
+
|
|
307
|
+
### `<SuprForm>`
|
|
308
|
+
|
|
309
|
+
Root form component that wraps your form fields.
|
|
310
|
+
|
|
311
|
+
**Props:**
|
|
312
|
+
|
|
313
|
+
| Prop | Type | Description |
|
|
314
|
+
| -------------- | --------------------- | -------------------------------------------- |
|
|
315
|
+
| `children` | `ReactNode` | Form fields and elements |
|
|
316
|
+
| `onSubmit` | `(values: T) => void` | Called with form values when valid |
|
|
317
|
+
| `onError` | `(errors) => void` | Called when form submission fails validation |
|
|
318
|
+
| `formOptions` | `UseFormProps` | Options for react-hook-form's `useForm()` |
|
|
319
|
+
| `className` | `string` | CSS class for `<form>` element |
|
|
320
|
+
| `style` | `CSSProperties` | Inline styles for `<form>` |
|
|
321
|
+
| `showAsterisk` | `boolean` | Show asterisk on required field labels |
|
|
322
|
+
| `ref` | `Ref` | Access form methods imperatively |
|
|
323
|
+
|
|
324
|
+
**Example:**
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
<SuprForm onSubmit={(values) => console.log(values)} formOptions={{ mode: 'onBlur' }} showAsterisk>
|
|
328
|
+
{/* fields */}
|
|
329
|
+
</SuprForm>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### `<SuprForm.Control>`
|
|
333
|
+
|
|
334
|
+
Composable field wrapper that handles labels, errors, and validation.
|
|
335
|
+
|
|
336
|
+
**Props:**
|
|
337
|
+
|
|
338
|
+
| Prop | Type | Description |
|
|
339
|
+
| ------------------ | ----------------------- | ----------------------------------------------------------- |
|
|
340
|
+
| `name` | `string` | **Required.** Field name (type-checked against form values) |
|
|
341
|
+
| `children` | `ReactElement` | **Required.** Your input component |
|
|
342
|
+
| `rules` | `RegisterOptions` | Validation rules (react-hook-form format) |
|
|
343
|
+
| `label` | `string` | Label text (rendered above input) |
|
|
344
|
+
| `className` | `string` | CSS class for wrapper div |
|
|
345
|
+
| `id` | `string` | HTML id (auto-generated if not provided) |
|
|
346
|
+
| `disabled` | `boolean` | Disable the field |
|
|
347
|
+
| `visibility` | `Visibility \| boolean` | Conditional visibility rules |
|
|
348
|
+
| `shouldUnregister` | `boolean` | Unregister field when unmounted |
|
|
349
|
+
|
|
350
|
+
**Example:**
|
|
351
|
+
|
|
352
|
+
```tsx
|
|
353
|
+
<SuprForm.Control
|
|
354
|
+
name='email'
|
|
355
|
+
label='Email Address'
|
|
356
|
+
rules={{
|
|
357
|
+
required: 'Email is required',
|
|
358
|
+
pattern: { value: /^\S+@\S+$/, message: 'Invalid email' },
|
|
359
|
+
}}
|
|
360
|
+
className='mb-4'
|
|
361
|
+
>
|
|
362
|
+
<input type='email' />
|
|
363
|
+
</SuprForm.Control>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Validation Rules
|
|
367
|
+
|
|
368
|
+
SuprForm uses [react-hook-form validation rules](https://react-hook-form.com/docs/useform/register#options):
|
|
369
|
+
|
|
370
|
+
| Rule | Type | Description |
|
|
371
|
+
| ----------- | ----------------------------------------- | -------------------------- |
|
|
372
|
+
| `required` | `string \| boolean` | Field is required |
|
|
373
|
+
| `min` | `{ value: number, message: string }` | Minimum numeric value |
|
|
374
|
+
| `max` | `{ value: number, message: string }` | Maximum numeric value |
|
|
375
|
+
| `minLength` | `{ value: number, message: string }` | Minimum string length |
|
|
376
|
+
| `maxLength` | `{ value: number, message: string }` | Maximum string length |
|
|
377
|
+
| `pattern` | `{ value: RegExp, message: string }` | Regex pattern match |
|
|
378
|
+
| `validate` | `(value) => boolean \| string \| Promise` | Custom validation function |
|
|
379
|
+
|
|
380
|
+
### Conditional Visibility
|
|
381
|
+
|
|
382
|
+
The `visibility` prop accepts:
|
|
383
|
+
|
|
384
|
+
```tsx
|
|
385
|
+
{
|
|
386
|
+
operator: 'AND' | 'OR',
|
|
387
|
+
conditions: [
|
|
388
|
+
{
|
|
389
|
+
name: 'fieldName',
|
|
390
|
+
operator: 'EQUALS' | 'NOT_EQUALS' | 'GREATER_THAN' | 'LESS_THAN' |
|
|
391
|
+
'GREATER_THAN_OR_EQUAL' | 'LESS_THAN_OR_EQUAL' |
|
|
392
|
+
'STARTS_WITH' | 'ENDS_WITH' | 'INCLUDES' | 'NOT_INCLUDES',
|
|
393
|
+
value: any
|
|
394
|
+
}
|
|
395
|
+
]
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**Example:**
|
|
400
|
+
|
|
401
|
+
```tsx
|
|
402
|
+
<SuprForm.Control
|
|
403
|
+
name='promoCode'
|
|
404
|
+
visibility={{
|
|
405
|
+
operator: 'OR',
|
|
406
|
+
conditions: [
|
|
407
|
+
{ name: 'isVip', operator: 'EQUALS', value: true },
|
|
408
|
+
{ name: 'orderTotal', operator: 'GREATER_THAN', value: 100 },
|
|
409
|
+
],
|
|
410
|
+
}}
|
|
411
|
+
>
|
|
412
|
+
<input />
|
|
413
|
+
</SuprForm.Control>
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Form Ref Methods
|
|
417
|
+
|
|
418
|
+
When you pass a `ref` to `<SuprForm>`, you get access to:
|
|
419
|
+
|
|
420
|
+
```tsx
|
|
421
|
+
formRef.current.setValue(name, value) // Set field value
|
|
422
|
+
formRef.current.setError(name, error) // Set field error
|
|
423
|
+
formRef.current.clearErrors(name?) // Clear errors
|
|
424
|
+
formRef.current.getValues(name?) // Get field value(s)
|
|
425
|
+
formRef.current.reset(values?) // Reset form
|
|
426
|
+
formRef.current.setFocus(name) // Focus field
|
|
427
|
+
formRef.current.resetField(name) // Reset specific field
|
|
428
|
+
formRef.current.trigger(name?) // Trigger validation
|
|
429
|
+
formRef.current.unregister(name) // Unregister field
|
|
430
|
+
formRef.current.watch(name?) // Watch field value(s)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## Styling
|
|
438
|
+
|
|
439
|
+
SuprForm is **design system agnostic**. Style using CSS classes:
|
|
440
|
+
|
|
441
|
+
```css
|
|
442
|
+
.controlled-field {
|
|
443
|
+
/* Field wrapper */
|
|
444
|
+
}
|
|
445
|
+
.controlled-field-label {
|
|
446
|
+
/* Label element */
|
|
447
|
+
}
|
|
448
|
+
.controlled-field-error {
|
|
449
|
+
/* Error message (defaults: red, 14px, margin-top 4px) */
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
**Tailwind Example:**
|
|
454
|
+
|
|
455
|
+
```tsx
|
|
456
|
+
<SuprForm.Control name='email' className='mb-4'>
|
|
457
|
+
<input className='w-full px-4 py-2 border rounded-lg focus:ring-2' />
|
|
458
|
+
</SuprForm.Control>
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
**styled-components Example:**
|
|
462
|
+
|
|
463
|
+
```tsx
|
|
464
|
+
const StyledField = styled.div`
|
|
465
|
+
.controlled-field-label {
|
|
466
|
+
font-weight: 600;
|
|
467
|
+
}
|
|
468
|
+
.controlled-field-error {
|
|
469
|
+
color: #d32f2f;
|
|
470
|
+
}
|
|
471
|
+
`;
|
|
472
|
+
|
|
473
|
+
<SuprForm.Control className={StyledField} name='email'>
|
|
474
|
+
<input />
|
|
475
|
+
</SuprForm.Control>;
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## Publishing
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
npm login
|
|
484
|
+
npm run build
|
|
485
|
+
npm version patch # or minor/major
|
|
486
|
+
npm publish --access public --otp=XXXXXX
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
The published package contains only compiled `dist/` files (ESM/CJS + TypeScript declarations).
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## License
|
|
494
|
+
|
|
495
|
+
MIT
|
|
496
|
+
|
|
497
|
+
## Contributing
|
|
498
|
+
|
|
499
|
+
Contributions welcome! Submit a Pull Request on [GitHub](https://github.com/Albinbritto/suprform).
|