suprform 1.1.3 → 1.1.4

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 CHANGED
@@ -1,499 +1,499 @@
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).
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).