suprform 1.1.3 → 1.1.5

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