reactaform 1.1.4 → 1.1.6

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,18 +1,37 @@
1
1
  # ReactaForm
2
2
 
3
- A powerful, type-safe React form builder library with dynamic field rendering, conditional visibility, multi-language support, and extensible validation. Built with TypeScript and React 19+.
3
+ ReactaForm is a fully dynamic, ultra-customizable form engine for modern React applications. With schema-driven rendering, full TypeScript support, and built-in performance optimizations, it provides everything you need to build powerful forms—without the boilerplate.
4
4
 
5
5
  ## ✨ Features
6
6
 
7
- - 🎯 **Type-Safe**: Full TypeScript support with generic field types
8
- - 🎨 **Themeable**: CSS variables for easy customization (light/dark mode built-in)
9
- - 🌍 **i18n Ready**: Built-in internationalization with translation caching
10
- - 🔌 **Extensible**: Registry pattern for custom components, validators, and handlers
11
- - **Performance**: Virtual scrolling, debounced inputs, RAF-based state management
12
- - 🎭 **Conditional Logic**: Field visibility based on parent field values
13
- - 📱 **Responsive**: Works seamlessly across devices
14
- - ♿ **Accessible**: ARIA attributes and keyboard navigation support
15
- - 🧩 **20+ Field Types**: Text, email, phone, dropdown, checkbox, slider, rating, date, file upload, and more
7
+ ### 🔧 Core Capabilities
8
+
9
+ - **Dynamic Schema-Driven Forms** Generate entire forms from JSON definitions.
10
+ - **Type-Safe by Design** Strongly typed fields, validators, and submission handlers.
11
+ - **20+ Built-In Field Types** Text, email, phone, dropdown, slider, rating, date, file upload, and more.
12
+
13
+ ### 🎨 Customization & Theming
14
+
15
+ - **Themeable via CSS Variables** Customize colors, spacing, borders, typography, and support light/dark modes.
16
+ - **Component Registry** — Register custom field components.
17
+
18
+ ### 🧠 Logic & Validation
19
+
20
+ - **Custom Validation System** — Register validators globally or per field.
21
+ - **Conditional Logic** — Show or hide fields dynamically based on parent values.
22
+
23
+ ### 🌍 Internationalization
24
+
25
+ - **Built-In Multi-Language Support** — i18n with translation caching for fast rendering.
26
+
27
+ ### ⚡ Performance & UX
28
+
29
+ - **Optimized Input Handling** — Debounced updates + requestAnimationFrame-driven state management.
30
+ - **Accessible by Default** — ARIA attributes, keyboard navigation, and focus management.
31
+
32
+ ### 🔌 Flexible Submission Flow
33
+
34
+ - **Custom Submission Handlers** — Integrate any workflow, API, or async logic.
16
35
 
17
36
  ## 📦 Installation
18
37
 
@@ -21,32 +40,20 @@ npm install reactaform react react-dom
21
40
  ```
22
41
 
23
42
  **Peer Dependencies:**
24
- - React ^19.2.0
25
- - React-DOM ^19.2.0
26
-
27
- ## 🌐 Environment Compatibility
28
43
 
29
- ReactaForm is **bundler-agnostic** and works seamlessly across different build tools:
44
+ - React `^18.0.0 || ^19.0.0`
45
+ - React-DOM `^18.0.0 || ^19.0.0`
30
46
 
31
- **Vite** - Fully supported (recommended)
32
- ✅ **Webpack** (Create React App) - Fully supported
33
- ✅ **Next.js** - Fully supported
34
- ✅ **Parcel, esbuild, Rollup** - Fully supported
47
+ ## 🌐 Environment Compatibility
35
48
 
36
- The library gracefully handles environment-specific APIs (`import.meta.env`, `process.env`) with automatic fallbacks. No special configuration needed!
49
+ ReactaForm works seamlessly with:
37
50
 
38
- ### TypeScript Setup (Non-Vite Projects)
51
+ - Vite (recommended)
52
+ - Webpack / CRA
53
+ - Next.js
54
+ - Parcel, esbuild, Rollup
39
55
 
40
- If you see TypeScript errors about CSS imports in non-Vite projects, add a `global.d.ts`:
41
-
42
- ```typescript
43
- // src/global.d.ts
44
- declare module '*.css';
45
- declare module '*.module.css' {
46
- const classes: { readonly [key: string]: string };
47
- export default classes;
48
- }
49
- ```
56
+ The library intelligently handles `import.meta.env` and `process.env` with automatic fallbacks—no config tweaks required.
50
57
 
51
58
  ## 🚀 Quick Start
52
59
 
@@ -54,39 +61,21 @@ declare module '*.module.css' {
54
61
  import { ReactaForm, createInstanceFromDefinition } from 'reactaform';
55
62
  import { useState } from 'react';
56
63
 
64
+ // Define definition, can be load from server
57
65
  const definition = {
58
66
  name: "contactForm",
59
67
  version: "1.0",
60
68
  displayName: "Contact Form",
61
69
  properties: [
62
- {
63
- name: "fullName",
64
- displayName: "Full Name",
65
- type: "string",
66
- defaultValue: "",
67
- required: true,
68
- },
69
- {
70
- name: "email",
71
- displayName: "Email",
72
- type: "email",
73
- defaultValue: "",
74
- required: true,
75
- },
76
- {
77
- name: "message",
78
- displayName: "Message",
79
- type: "text",
80
- defaultValue: "",
81
- required: true,
82
- }
70
+ { name: "fullName", displayName: "Full Name", type: "string", required: true },
71
+ { name: "email", displayName: "Email", type: "email", required: true },
72
+ { name: "message", displayName: "Message", type: "text", required: true }
83
73
  ]
84
74
  };
85
75
 
86
76
  function App() {
87
- // Create an instance from the definition
88
77
  const result = createInstanceFromDefinition(definition, "myForm");
89
- const [instance, setInstance] = useState(result.instance);
78
+ const [instance] = useState(result.instance);
90
79
 
91
80
  return (
92
81
  <ReactaForm
@@ -98,339 +87,221 @@ function App() {
98
87
  }
99
88
  ```
100
89
 
101
- > **Note**: `ReactaForm` manages form state internally. If you need to programmatically update values, use `setInstance()` with a new instance object.
90
+ > **Note:** ReactaForm manages internal form state automatically. Use `setInstance()` only for programmatic overrides.
91
+
102
92
 
103
93
  ## 📖 Core Concepts
104
94
 
105
95
  ### Form Definitions
106
96
 
107
- A form definition is a JSON object that describes the structure of your form:
108
-
109
- ```typescript
97
+ ```ts
110
98
  interface ReactaDefinition {
111
- name: string; // Unique form identifier
112
- version: string; // Version for tracking changes
113
- displayName: string; // Human-readable form title
114
- properties: FieldDefinition[]; // Array of field definitions
99
+ name: string;
100
+ version: string;
101
+ displayName: string;
102
+ properties: FieldDefinition[];
115
103
  }
116
104
  ```
117
105
 
118
- ### Field Types
119
-
120
- ReactaForm supports 20+ field types out of the box:
121
-
122
- | Type | Description | Example |
123
- |------|-------------|---------|
124
- | `string` | Single-line text input | Name, username |
125
- | `text` | Multi-line text area | Comments, descriptions |
126
- | `email` | Email with validation | user@example.com |
127
- | `phone` | Phone number input | +1 (555) 123-4567 |
128
- | `url` | URL with validation | https://example.com |
129
- | `integer` | Whole numbers | Age, quantity |
130
- | `float` | Decimal numbers | Price, weight |
131
- | `boolean` | Checkbox | Accept terms |
132
- | `dropdown` | Select from options | Country, category |
133
- | `radio` | Radio button group | Gender, size |
134
- | `multiSelection` | Multiple checkboxes | Interests, tags |
135
- | `slider` | Range slider | Volume, brightness |
136
- | `rating` | Star rating | Product rating |
137
- | `date` | Date picker | Birth date |
138
- | `dateTime` | Date and time picker | Appointment |
139
- | `time` | Time picker | Meeting time |
140
- | `color` | Color picker | Theme color |
141
- | `file` | File upload | Documents, images |
142
- | `image` | Image display | Preview, thumbnail |
143
- | `unitValue` | Value with unit conversion | 100 cm → 1 m |
144
-
145
- ### Conditional Visibility
146
-
147
- Fields can be conditionally shown/hidden based on parent field values:
148
-
149
- ```typescript
106
+ ### Supported Field Types
107
+
108
+ | Type | Description |
109
+ |------|-------------|
110
+ | `checkbox` | Boolean |
111
+ | `color` | Color picker |
112
+ | `date` | Date Picker |
113
+ | `dropdown` | Select menu |
114
+ | `email` | Email input |
115
+ | `file` | Upload |
116
+ | `float` | Float input |
117
+ | `float-array` | Float array input |
118
+ | `image` | Image preview |
119
+ | `int-array`| Integer array input |
120
+ | `int` | Integer input |
121
+ | `multi-selection` | Multiple selection |
122
+ | `phone` | Phone number input |
123
+ | `radio` | Radio button group |
124
+ | `rating` | Star rating |
125
+ | `slider` | Range slider |
126
+ | `switch` | Boolean |
127
+ | `text` | Single line input |
128
+ | `time` | Time input |
129
+ | `unitValue` | Value + unit conversion |
130
+ | `url` | URL validation |
131
+
132
+ ### 🎭 Conditional Visibility
133
+
134
+ ```json
150
135
  {
151
- name: "country",
152
- displayName: "Country",
153
- type: "dropdown",
154
- options: [
155
- { label: "United States", value: "US" },
156
- { label: "Canada", value: "CA" }
136
+ "name": "country",
137
+ "displayName": "Country",
138
+ "type": "dropdown",
139
+ "options": [
140
+ { "label": "United States", "value": "US" },
141
+ { "label": "Canada", "value": "CA" }
157
142
  ]
158
143
  },
159
144
  {
160
- name: "state",
161
- displayName: "State/Province",
162
- type: "dropdown",
163
- parents: {
164
- country: ["US", "CA"] // Only show when country is US or CA
165
- },
166
- options: [
167
- { label: "California", value: "CA" },
168
- { label: "Ontario", value: "ON" }
169
- ]
145
+ "name": "state",
146
+ "displayName": "State",
147
+ "type": "dropdown",
148
+ "parents": { "country": ["US"] }
149
+ },
150
+ {
151
+ "name": "province",
152
+ "displayName": "Province",
153
+ "type": "dropdown",
154
+ "parents": { "country": ["CA"] }
170
155
  }
171
- ```
172
156
 
173
- ### Validation
157
+ ```
174
158
 
175
- Built-in validation for common patterns:
159
+ ### 🔍 Validation
176
160
 
177
- ```typescript
161
+ ```json
178
162
  {
179
- name: "email",
180
- displayName: "Email Address",
181
- type: "email",
182
- required: true, // Field is required
183
- pattern: "^[a-z]+$", // Custom regex pattern (optional)
184
- minLength: 5, // Minimum length (optional)
185
- maxLength: 100, // Maximum length (optional)
163
+ "name": "email",
164
+ "displayName": "Email",
165
+ "type": "email",
166
+ "required": true,
167
+ "pattern": "^[a-z]+$",
168
+ "minLength": 5,
169
+ "maxLength": 100
186
170
  }
187
171
  ```
188
172
 
189
173
  ## 🎨 Theming
190
174
 
191
- ReactaForm uses CSS variables for easy theming:
175
+ Customize with CSS variables:
192
176
 
193
177
  ```css
194
178
  :root {
195
- /* Colors */
196
179
  --reactaform-color-primary: #2563eb;
197
180
  --reactaform-color-error: #ef4444;
198
- --reactaform-color-success: #10b981;
199
-
200
- /* Typography */
201
- --reactaform-font-family: system-ui, sans-serif;
202
181
  --reactaform-font-size: 1rem;
203
- --reactaform-font-weight: 400;
204
-
205
- /* Spacing */
206
- --reactaform-space-xs: 4px;
207
- --reactaform-space-sm: 8px;
208
- --reactaform-space-md: 16px;
209
-
210
- /* Borders */
211
- --reactaform-border-radius: 4px;
212
- --reactaform-border-color: #d1d5db;
213
-
214
- /* Inputs */
215
182
  --reactaform-input-bg: #ffffff;
216
- --reactaform-input-padding: 8px 12px;
217
183
  }
218
184
 
219
- /* Dark mode */
185
+ /* Dark */
220
186
  [data-reactaform-theme="dark"] {
221
- --reactaform-bg-primary: #1A1A1A;
222
- --reactaform-text-color: #EDEDED;
223
- --reactaform-input-bg: #2A2A2A;
224
- --reactaform-border-color: #3A3A3A;
187
+ --reactaform-bg-primary: #1a1a1a;
188
+ --reactaform-text-color: #ededed;
225
189
  }
226
190
  ```
227
191
 
228
- ### Using Dark Mode
192
+ **Enable Dark Mode:**
229
193
 
230
194
  ```tsx
231
- <ReactaForm
232
- definitionData={definition}
233
- instance={formData}
234
- darkMode={true} // Enable dark mode
235
- />
195
+ <ReactaForm darkMode={true} ... />
236
196
  ```
237
197
 
238
- ## 🌍 Internationalization
239
-
240
- ReactaForm supports multiple languages:
198
+ ## 🌍 Internationalization (i18n)
241
199
 
242
200
  ```tsx
243
- <ReactaForm
244
- definitionData={definition}
245
- instance={formData}
246
- language="fr" // 'en', 'fr', 'de', 'es', 'zh-cn'
247
- />
201
+ <ReactaForm language="fr" ... />
248
202
  ```
249
203
 
250
204
  ### Custom Translations
251
205
 
252
- Provide custom translations for your form:
253
-
254
- ```typescript
206
+ ```json
255
207
  // public/locales/fr/myform.json
256
208
  {
257
209
  "Full Name": "Nom complet",
258
- "Email": "Courriel",
259
- "Submit": "Soumettre"
210
+ "Email": "Courriel"
260
211
  }
261
212
  ```
262
213
 
263
- ## 🔧 Advanced Usage
264
214
 
265
- ### Custom Field Components
215
+ ## 🔧 Advanced Usage
266
216
 
267
- Register custom field components:
217
+ ### Custom Components
268
218
 
269
219
  ```tsx
270
220
  import { registerComponent } from 'reactaform';
271
221
 
272
- const CustomInput = ({ field, value, onChange }) => {
273
- return (
274
- <input
275
- value={value}
276
- onChange={(e) => onChange(e.target.value, null)}
277
- placeholder={field.displayName}
278
- />
279
- );
280
- };
222
+ const CustomInput = ({ value, onChange, field }) => (
223
+ <input
224
+ value={value}
225
+ placeholder={field.displayName}
226
+ onChange={(e) => onChange(e.target.value, null)}
227
+ />
228
+ );
281
229
 
282
- registerComponent('customType', CustomInput);
230
+ registerComponent("customType", CustomInput);
283
231
  ```
284
232
 
285
233
  ### Custom Validation
286
234
 
287
- Register custom validators:
288
-
289
235
  ```tsx
290
236
  import { registerValidationHandler } from 'reactaform';
291
237
 
292
- registerValidationHandler('customType', (value, field) => {
293
- if (value.length < 10) {
294
- return "Value must be at least 10 characters";
295
- }
296
- return null; // No error
297
- });
238
+ registerValidationHandler("customType", (value) =>
239
+ value.length < 10 ? "Must be at least 10 characters" : null
240
+ );
298
241
  ```
299
242
 
300
- ### Form Submission
301
-
302
- Handle form submission with custom logic:
243
+ ### Custom Submission
303
244
 
304
245
  ```tsx
305
246
  import { registerSubmissionHandler } from 'reactaform';
306
247
 
307
- // Register a submission handler for your form
308
- registerSubmissionHandler(
309
- 'mySubmitHandler', // Handler name (referenced in definition.submitHandlerName)
310
- (definition, instanceName, valuesMap, t) => {
311
- // definition: The form definition
312
- // instanceName: Current instance name
313
- // valuesMap: Object with all field values { fieldName: value }
314
- // t: Translation function for error messages
315
-
316
- // Custom validation
317
- if (!valuesMap.email?.includes('@')) {
318
- return [t('Invalid email address')]; // Return array of error strings
319
- }
320
-
321
- // Submit to API
322
- fetch('/api/contact', {
323
- method: 'POST',
324
- headers: { 'Content-Type': 'application/json' },
325
- body: JSON.stringify(valuesMap),
326
- }).catch(err => {
327
- console.error('Submission failed:', err);
328
- });
329
-
330
- // Return undefined for success, or array of error strings for failure
331
- return undefined;
332
- }
333
- );
334
- ```
248
+ registerSubmissionHandler("mySubmitHandler", async (_, __, values, t) => {
249
+ if (!values.email.includes("@")) return [t("Invalid email address")];
335
250
 
336
- **Then reference it in your definition:**
251
+ await fetch("/api/contact", {
252
+ method: "POST",
253
+ body: JSON.stringify(values),
254
+ });
255
+ });
337
256
 
338
- ```tsx
339
257
  const definition = {
340
258
  name: "contactForm",
341
- submitHandlerName: "mySubmitHandler", // Link to registered handler
342
- // ... rest of definition
259
+ submitHandlerName: "mySubmitHandler"
343
260
  };
344
261
  ```
345
262
 
346
- ### Using ReactaFormProvider
347
-
348
- Wrap your app for global configuration:
263
+ ### Provider Usage
349
264
 
350
265
  ```tsx
351
266
  import { ReactaFormProvider, ReactaFormRenderer } from 'reactaform';
352
267
 
353
- function App() {
354
- return (
355
- <ReactaFormProvider
356
- defaultLanguage="en"
357
- defaultDarkMode={false}
358
- definitionName="myForm"
359
- >
360
- <ReactaFormRenderer
361
- properties={definition.properties}
362
- instance={formData}
363
- />
364
- </ReactaFormProvider>
365
- );
366
- }
268
+ <ReactaFormProvider defaultLanguage="en">
269
+ <ReactaFormRenderer properties={definition.properties} instance={formData} />
270
+ </ReactaFormProvider>
367
271
  ```
368
272
 
369
- ### Note: `definitionName` vs renderer-level override
370
-
371
- - `ReactaFormProvider` accepts an optional `definitionName` prop which can be used as a top-level default when you are rendering a single form or when tests/storybook rely on a global active definition.
372
- - `ReactaFormRenderer` injects its own `definitionName` into the React context (it wraps its output in a nested provider). This means multiple renderers with different definitions can safely coexist under the same `ReactaFormProvider`.
373
- - Recommendation: prefer leaving `definitionName` empty at the top-level and let each `ReactaFormRenderer` provide the definitive `definitionName` for the fields it renders. Use the provider-level `definitionName` only for special cases (tests, single-form pages) where you need a global fallback.
374
-
375
-
376
273
  ## 📚 API Reference
377
274
 
378
275
  ### ReactaForm Props
379
276
 
380
277
  | Prop | Type | Required | Description |
381
278
  |------|------|----------|-------------|
382
- | `definitionData` | `ReactaDefinition \| string` | Yes | Form definition object or JSON string |
383
- | `instance` | `ReactaInstance` | No* | Form instance with values and metadata. If not provided, will be auto-created from definition |
384
- | `language` | `string` | No | Language code (default: 'en') |
385
- | `darkMode` | `boolean` | No | Enable dark mode (default: auto-detect from parent theme) |
386
- | `className` | `string` | No | Custom CSS class |
387
- | `style` | `CSSProperties` | No | Custom inline styles |
388
-
389
- *While `instance` is technically optional (auto-created if omitted), providing it is recommended for proper state management.
390
-
391
- ### Field Definition Props
392
-
393
- | Prop | Type | Required | Description |
394
- |------|------|----------|-------------|
395
- | `name` | `string` | Yes | Unique field identifier |
396
- | `displayName` | `string` | Yes | Field label |
397
- | `type` | `string` | Yes | Field type (see Field Types) |
398
- | `defaultValue` | `any` | No | Default field value |
399
- | `required` | `boolean` | No | Field is required |
400
- | `disabled` | `boolean` | No | Field is disabled |
401
- | `readOnly` | `boolean` | No | Field is read-only |
402
- | `tooltip` | `string` | No | Tooltip text |
403
- | `placeholder` | `string` | No | Placeholder text |
404
- | `options` | `Option[]` | No | Options for dropdown/radio/multi-selection |
405
- | `parents` | `Record<string, string[]>` | No | Conditional visibility rules |
406
- | `pattern` | `string` | No | Regex validation pattern |
407
- | `minLength` | `number` | No | Minimum input length |
408
- | `maxLength` | `number` | No | Maximum input length |
409
- | `min` | `number` | No | Minimum numeric value |
410
- | `max` | `number` | No | Maximum numeric value |
411
- | `step` | `number` | No | Numeric step increment |
412
- | `labelLayout` | `'row' \| 'column-left' \| 'column-center'` | No | Label positioning |
279
+ | `definitionData` | `ReactaDefinition \| string` | | Form definition |
280
+ | `instance` | `ReactaInstance` | | Form state instance |
281
+ | `language` | `string` | | e.g. "en", "fr" |
282
+ | `darkMode` | `boolean` | | Force dark mode |
283
+ | `className` | `string` | | Custom CSS class |
284
+ | `style` | `CSSProperties` | | Inline styles |
413
285
 
414
286
  ## 🧪 Testing
415
287
 
416
- ReactaForm uses Vitest for testing:
417
-
418
288
  ```bash
419
- npm run test # Run all tests
420
- npm run typecheck # Type checking
289
+ npm run test
290
+ npm run typecheck
421
291
  ```
422
292
 
423
293
  ## 🏗️ Building
424
294
 
425
295
  ```bash
426
- npm run build:lib # Build library
427
- npm pack # Create .tgz package
296
+ npm run build:lib
297
+ npm pack
428
298
  ```
429
299
 
430
- Output formats:
431
- - **ESM**: `dist/reactaform.es.js`
432
- - **CommonJS**: `dist/reactaform.cjs.js`
433
- - **Types**: `dist/index.d.ts`
300
+ **Outputs:**
301
+
302
+ - ESM: `dist/reactaform.es.js`
303
+ - CJS: `dist/reactaform.cjs.js`
304
+ - Types: `dist/index.d.ts`
434
305
 
435
306
  ## 📄 License
436
307
 
@@ -438,57 +309,16 @@ MIT
438
309
 
439
310
  ## 🤝 Contributing
440
311
 
441
- Contributions are welcome! Please feel free to submit a Pull Request.
442
-
443
- ## 🐛 Known Issues
444
-
445
- - Virtual scrolling optimization in progress for forms with 1000+ fields
446
- - Screen reader testing ongoing for complex conditional fields
312
+ Contributions welcome! Open a pull request anytime.
447
313
 
448
314
  ## 🗺️ Roadmap
449
315
 
450
316
  - [ ] Enhanced accessibility audit
451
- - [ ] More built-in validators
452
- - [ ] Form builder UI
317
+ - [ ] Additional built-in validators
318
+ - [ ] Visual form-builder UI
453
319
  - [ ] Schema migration tools
454
320
  - [ ] Performance profiling dashboard
455
321
 
456
- ## 🔍 Troubleshooting
457
-
458
- ### "Cannot find module './style.css'"
459
-
460
- **Solution**: Add a `global.d.ts` file (see Environment Compatibility section above).
461
-
462
- ### Form not updating when instance changes
463
-
464
- **Solution**: Ensure you're passing a **new instance object** (immutable updates):
465
-
466
- ```tsx
467
- // ✅ Correct - creates new object
468
- setInstance({ ...instance, values: { ...instance.values, name: 'John' } });
469
-
470
- // ❌ Wrong - mutates existing object
471
- instance.values.name = 'John';
472
- setInstance(instance);
473
- ```
474
-
475
- ### Submission handler not called
476
-
477
- **Solution**: Ensure `submitHandlerName` in your definition matches the registered handler name:
478
-
479
- ```tsx
480
- registerSubmissionHandler('myHandler', ...);
481
- // Definition must have: submitHandlerName: "myHandler"
482
- ```
483
-
484
- ### TypeScript errors about `import.meta` or `process`
485
-
486
- **Solution**: The library handles these internally with fallbacks. If you see errors in **your** code, ensure you're not directly importing library internals. If errors persist, update to the latest version.
487
-
488
- ### Dark mode not working
489
-
490
- **Solution**: Either pass `darkMode={true}` prop, or wrap your app with `[data-reactaform-theme="dark"]` attribute on a parent element.
491
-
492
322
  ---
493
323
 
494
- **Built with ❤️ using React and TypeScript**
324
+ Built with ❤️ using React and TypeScript