@aatulwork/customform-renderer 1.0.0

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 ADDED
@@ -0,0 +1,653 @@
1
+ # @custom-form/renderer
2
+
3
+ A powerful, reusable form renderer component for React with Material-UI support. This package provides a dynamic form rendering system that can generate forms from JSON schemas with support for multiple field types, validation, file uploads, and more.
4
+
5
+ ## Features
6
+
7
+ - 🎨 **Material-UI Integration** - Built with Material-UI components
8
+ - 📝 **13 Field Types** - Text, Email, Number, Select, Checkbox, Radio, DatePicker, File, CKEditor, Toggle, Color, FormReference, ApiReference
9
+ - ✅ **Form Validation** - Built-in validation with react-hook-form
10
+ - 📱 **Responsive Design** - Mobile-friendly layouts
11
+ - 🎯 **Multiple Layouts** - Accordion panels, stepper, and grid layouts
12
+ - 👁️ **View Mode** - Read-only view mode for displaying form data
13
+ - 🔌 **Service Injection** - Extensible architecture with injectable services
14
+ - 📦 **TypeScript** - Fully typed with TypeScript
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @custom-form/renderer
20
+ ```
21
+
22
+ ### Peer Dependencies
23
+
24
+ Make sure you have these peer dependencies installed:
25
+
26
+ ```bash
27
+ npm install react react-dom @mui/material @mui/icons-material @mui/x-date-pickers react-hook-form @tanstack/react-query dayjs @ckeditor/ckeditor5-react
28
+ ```
29
+
30
+ ### CKEditor Setup
31
+
32
+ The package includes CKEditor in the `lib/ckeditor/` directory. You need to load it before using CKEditor fields.
33
+
34
+ **Option 1: Include in HTML (Recommended)**
35
+ ```html
36
+ <script src="/lib/ckeditor/ckeditor.js"></script>
37
+ ```
38
+
39
+ **Option 2: Copy to your public directory**
40
+ ```bash
41
+ cp node_modules/@custom-form/renderer/lib/ckeditor/ckeditor.js public/lib/ckeditor/
42
+ ```
43
+
44
+ **Option 3: Use dynamic loading**
45
+ ```tsx
46
+ import { useCKEditor } from '@custom-form/renderer';
47
+
48
+ const { isReady } = useCKEditor({ autoLoad: true });
49
+ ```
50
+
51
+ See [CKEditor Setup Guide](#ckeditor-setup) for more details.
52
+
53
+ ## Quick Start
54
+
55
+ ```tsx
56
+ import { FormRenderer } from '@custom-form/renderer';
57
+ import { FormSchema } from '@custom-form/renderer';
58
+
59
+ const formSchema: FormSchema = {
60
+ title: 'User Registration',
61
+ name: 'user-registration',
62
+ sections: [
63
+ {
64
+ id: 'personal-info',
65
+ title: 'Personal Information',
66
+ fields: [
67
+ {
68
+ type: 'text',
69
+ name: 'firstName',
70
+ label: 'First Name',
71
+ required: true,
72
+ },
73
+ {
74
+ type: 'email',
75
+ name: 'email',
76
+ label: 'Email',
77
+ required: true,
78
+ },
79
+ ],
80
+ },
81
+ ],
82
+ };
83
+
84
+ function App() {
85
+ const handleSubmit = async (data: Record<string, any>) => {
86
+ console.log('Form data:', data);
87
+ // Handle form submission
88
+ };
89
+
90
+ return (
91
+ <FormRenderer
92
+ formSchema={formSchema}
93
+ onSubmit={handleSubmit}
94
+ onSuccess={() => console.log('Form submitted successfully!')}
95
+ />
96
+ );
97
+ }
98
+ ```
99
+
100
+ ## Form Schema Structure
101
+
102
+ ### Basic Schema
103
+
104
+ ```typescript
105
+ interface FormSchema {
106
+ title: string;
107
+ name: string; // Unique identifier
108
+ sections?: FormSection[];
109
+ fields?: FormField[]; // Legacy support
110
+ settings?: {
111
+ sectionDisplayMode?: 'panel' | 'stepper';
112
+ fieldsPerRow?: 1 | 2 | 3;
113
+ };
114
+ }
115
+ ```
116
+
117
+ ### Form Section
118
+
119
+ ```typescript
120
+ interface FormSection {
121
+ id: string;
122
+ title: string;
123
+ description?: string;
124
+ fields: FormField[];
125
+ }
126
+ ```
127
+
128
+ ### Form Field
129
+
130
+ ```typescript
131
+ interface FormField {
132
+ type: 'text' | 'email' | 'number' | 'select' | 'checkbox' | 'radio' |
133
+ 'datepicker' | 'file' | 'ckeditor' | 'toggle' | 'color' |
134
+ 'formReference' | 'apiReference';
135
+ name: string;
136
+ label: string;
137
+ required?: boolean;
138
+ placeholder?: string;
139
+ options?: OptionItem[] | string[]; // For select/radio
140
+ validation?: {
141
+ min?: number;
142
+ max?: number;
143
+ pattern?: string;
144
+ maxFileSize?: number; // For file fields
145
+ allowedFileTypes?: string[]; // For file fields
146
+ };
147
+ allowMultiple?: boolean; // For select/file fields
148
+ datePickerMode?: 'date' | 'datetime' | 'time'; // For datepicker
149
+ // ... other field-specific properties
150
+ }
151
+ ```
152
+
153
+ ## Field Types
154
+
155
+ ### Text Fields
156
+
157
+ ```typescript
158
+ {
159
+ type: 'text' | 'email' | 'number',
160
+ name: 'fieldName',
161
+ label: 'Field Label',
162
+ required: true,
163
+ placeholder: 'Enter value',
164
+ validation: {
165
+ min: 0,
166
+ max: 100,
167
+ pattern: '^[A-Za-z]+$'
168
+ }
169
+ }
170
+ ```
171
+
172
+ ### Select Field
173
+
174
+ ```typescript
175
+ {
176
+ type: 'select',
177
+ name: 'country',
178
+ label: 'Country',
179
+ required: true,
180
+ allowMultiple: false,
181
+ options: [
182
+ { label: 'United States', value: 'us' },
183
+ { label: 'Canada', value: 'ca' },
184
+ ]
185
+ }
186
+ ```
187
+
188
+ ### Date Picker
189
+
190
+ ```typescript
191
+ {
192
+ type: 'datepicker',
193
+ name: 'birthDate',
194
+ label: 'Birth Date',
195
+ datePickerMode: 'date', // 'date' | 'datetime' | 'time'
196
+ }
197
+ ```
198
+
199
+ ### File Upload
200
+
201
+ ```typescript
202
+ {
203
+ type: 'file',
204
+ name: 'documents',
205
+ label: 'Upload Documents',
206
+ allowMultiple: true,
207
+ validation: {
208
+ maxFileSize: 5242880, // 5MB in bytes
209
+ allowedFileTypes: ['pdf', 'doc', 'docx']
210
+ }
211
+ }
212
+ ```
213
+
214
+ ### CKEditor (Rich Text)
215
+
216
+ ```typescript
217
+ {
218
+ type: 'ckeditor',
219
+ name: 'description',
220
+ label: 'Description',
221
+ required: true
222
+ }
223
+ ```
224
+
225
+ ### Reference Fields
226
+
227
+ ```typescript
228
+ // Form Reference
229
+ {
230
+ type: 'formReference',
231
+ name: 'relatedForm',
232
+ label: 'Related Form Entry',
233
+ referenceFormName: 'users',
234
+ referenceFieldName: 'name',
235
+ allowMultiple: false
236
+ }
237
+
238
+ // API Reference
239
+ {
240
+ type: 'apiReference',
241
+ name: 'role',
242
+ label: 'Role',
243
+ apiEndpoint: '/api/roles',
244
+ apiLabelField: 'name',
245
+ apiValueField: '_id',
246
+ allowMultiple: false
247
+ }
248
+ ```
249
+
250
+ ## Service Injection
251
+
252
+ The package uses a service injection pattern to handle external dependencies. You can provide custom services for file uploads, form references, API references, and more.
253
+
254
+ ### Providing Services
255
+
256
+ ```tsx
257
+ import { FormRenderer, FormServices } from '@custom-form/renderer';
258
+
259
+ const services: FormServices = {
260
+ // File upload service
261
+ fileUpload: {
262
+ uploadFiles: async (formName, fieldName, files) => {
263
+ // Your upload implementation
264
+ const formData = new FormData();
265
+ files.forEach(file => formData.append('files', file));
266
+
267
+ const response = await fetch('/api/upload', {
268
+ method: 'POST',
269
+ body: formData,
270
+ });
271
+
272
+ return response.json();
273
+ },
274
+ },
275
+
276
+ // Form reference service
277
+ formReference: {
278
+ fetchOptions: async (formName, fieldName) => {
279
+ const response = await fetch(`/api/forms/${formName}/entries`);
280
+ const data = await response.json();
281
+ return data.map(entry => ({
282
+ label: entry.payload[fieldName],
283
+ value: entry._id,
284
+ }));
285
+ },
286
+ },
287
+
288
+ // API reference service
289
+ apiReference: {
290
+ fetchOptions: async (endpoint, labelField, valueField = '_id') => {
291
+ const response = await fetch(endpoint);
292
+ const data = await response.json();
293
+ return data.map(item => ({
294
+ label: item[labelField],
295
+ value: item[valueField],
296
+ }));
297
+ },
298
+ },
299
+
300
+ // Date formatter
301
+ dateFormatter: {
302
+ format: (value, options) => {
303
+ // Your date formatting logic
304
+ return new Date(value).toLocaleDateString();
305
+ },
306
+ },
307
+
308
+ // File base URL for displaying uploaded files
309
+ fileBaseUrl: 'https://your-cdn.com/uploads/',
310
+
311
+ // CKEditor license key
312
+ ckEditorLicenseKey: 'your-license-key',
313
+ };
314
+
315
+ function App() {
316
+ return (
317
+ <FormRenderer
318
+ formSchema={formSchema}
319
+ onSubmit={handleSubmit}
320
+ services={services}
321
+ />
322
+ );
323
+ }
324
+ ```
325
+
326
+ ## Props
327
+
328
+ ### FormRenderer Props
329
+
330
+ ```typescript
331
+ interface FormRendererProps {
332
+ formSchema: FormSchema;
333
+ onSubmit?: (data: Record<string, any>) => void | Promise<void>;
334
+ onCancel?: () => void;
335
+ isLoading?: boolean;
336
+ onSuccess?: () => void;
337
+ initialValues?: Record<string, any>;
338
+ hideTitle?: boolean;
339
+ allowResetOnValuesChange?: boolean;
340
+ mode?: 'edit' | 'view';
341
+ services?: FormServices;
342
+ }
343
+ ```
344
+
345
+ ## View Mode
346
+
347
+ You can render forms in read-only view mode:
348
+
349
+ ```tsx
350
+ <FormRenderer
351
+ formSchema={formSchema}
352
+ initialValues={{
353
+ firstName: 'John',
354
+ email: 'john@example.com',
355
+ }}
356
+ mode="view"
357
+ services={services}
358
+ />
359
+ ```
360
+
361
+ ## Custom Components
362
+
363
+ You can provide custom components for specific use cases:
364
+
365
+ ```typescript
366
+ const services: FormServices = {
367
+ // Custom select component (e.g., searchable select)
368
+ SelectComponent: MyCustomSelectComponent,
369
+
370
+ // Custom file display component
371
+ FileDisplayComponent: MyFileDisplayComponent,
372
+
373
+ // Custom CKEditor display component
374
+ CKEditorDisplayComponent: MyCKEditorDisplayComponent,
375
+ };
376
+ ```
377
+
378
+ ## Examples
379
+
380
+ ### Basic Form
381
+
382
+ ```tsx
383
+ const schema: FormSchema = {
384
+ title: 'Contact Form',
385
+ name: 'contact',
386
+ sections: [
387
+ {
388
+ id: 'contact-info',
389
+ title: 'Contact Information',
390
+ fields: [
391
+ { type: 'text', name: 'name', label: 'Name', required: true },
392
+ { type: 'email', name: 'email', label: 'Email', required: true },
393
+ { type: 'text', name: 'phone', label: 'Phone' },
394
+ ],
395
+ },
396
+ ],
397
+ };
398
+ ```
399
+
400
+ ### Form with Stepper
401
+
402
+ ```tsx
403
+ const schema: FormSchema = {
404
+ title: 'Multi-Step Form',
405
+ name: 'multistep',
406
+ settings: {
407
+ sectionDisplayMode: 'stepper',
408
+ },
409
+ sections: [
410
+ {
411
+ id: 'step1',
412
+ title: 'Step 1',
413
+ fields: [/* fields */],
414
+ },
415
+ {
416
+ id: 'step2',
417
+ title: 'Step 2',
418
+ fields: [/* fields */],
419
+ },
420
+ ],
421
+ };
422
+ ```
423
+
424
+ ### Form with Grid Layout
425
+
426
+ ```tsx
427
+ const schema: FormSchema = {
428
+ title: 'Grid Form',
429
+ name: 'grid-form',
430
+ settings: {
431
+ fieldsPerRow: 2, // 1, 2, or 3 columns
432
+ },
433
+ sections: [
434
+ {
435
+ id: 'grid-section',
436
+ title: 'Grid Section',
437
+ fields: [/* fields will be displayed in 2 columns */],
438
+ },
439
+ ],
440
+ };
441
+ ```
442
+
443
+ ## Building
444
+
445
+ To build the package:
446
+
447
+ ```bash
448
+ npm run build
449
+ ```
450
+
451
+ This will generate:
452
+ - `dist/index.js` - CommonJS build
453
+ - `dist/index.esm.js` - ES Module build
454
+ - `dist/index.d.ts` - TypeScript definitions
455
+
456
+ ### Build Commands
457
+
458
+ ```bash
459
+ # Standard build
460
+ npm run build
461
+
462
+ # Clean build (removes dist first)
463
+ npm run build:clean
464
+
465
+ # Watch mode (development)
466
+ npm run dev
467
+ # or
468
+ npm run build:watch
469
+
470
+ # Type check only
471
+ npm run lint:check
472
+ ```
473
+
474
+ ## Publishing
475
+
476
+ ### Quick Publish
477
+
478
+ ```bash
479
+ # Patch version (1.0.0 -> 1.0.1) + build + publish
480
+ npm run publish:patch
481
+
482
+ # Minor version (1.0.0 -> 1.1.0) + build + publish
483
+ npm run publish:minor
484
+
485
+ # Major version (1.0.0 -> 2.0.0) + build + publish
486
+ npm run publish:major
487
+ ```
488
+
489
+ ### Publish with Tags
490
+
491
+ ```bash
492
+ # Publish as beta
493
+ npm run publish:beta
494
+
495
+ # Publish as next
496
+ npm run publish:next
497
+
498
+ # Publish as public (for scoped packages)
499
+ npm run publish:public
500
+ ```
501
+
502
+ ### Dry Run (Test)
503
+
504
+ ```bash
505
+ npm run publish:dry-run
506
+ ```
507
+
508
+ ### Interactive Publish Scripts
509
+
510
+ **Linux/Mac:**
511
+ ```bash
512
+ chmod +x scripts/publish.sh
513
+ ./scripts/publish.sh
514
+ ```
515
+
516
+ **Windows:**
517
+ ```powershell
518
+ .\scripts\publish.ps1
519
+ ```
520
+
521
+ ### Manual Publish Steps
522
+
523
+ 1. **Bump version** (if needed):
524
+ ```bash
525
+ npm run version:patch # or :minor or :major
526
+ ```
527
+
528
+ 2. **Build**:
529
+ ```bash
530
+ npm run build
531
+ ```
532
+
533
+ 3. **Publish**:
534
+ ```bash
535
+ npm publish --access public
536
+ ```
537
+
538
+ For detailed publishing instructions, see [PUBLISH_GUIDE.md](./PUBLISH_GUIDE.md).
539
+
540
+ ## Development
541
+
542
+ ```bash
543
+ # Install dependencies
544
+ npm install
545
+
546
+ # Build in watch mode
547
+ npm run dev
548
+
549
+ # Build for production
550
+ npm run build
551
+
552
+ # Type check
553
+ npm run lint:check
554
+ ```
555
+
556
+ ## CKEditor Setup
557
+
558
+ CKEditor is required for the `ckeditor` field type. The package includes the CKEditor build file.
559
+
560
+ ### Setup Methods
561
+
562
+ #### Method 1: HTML Script Tag (Recommended)
563
+
564
+ Add to your `index.html` or main HTML file:
565
+
566
+ ```html
567
+ <script src="/lib/ckeditor/ckeditor.js"></script>
568
+ ```
569
+
570
+ Make sure the file is accessible at `/lib/ckeditor/ckeditor.js` (or copy it to your public directory).
571
+
572
+ #### Method 2: Copy to Public Directory
573
+
574
+ If using Vite, Create React App, or similar:
575
+
576
+ ```bash
577
+ # Copy CKEditor to your public directory
578
+ cp node_modules/@custom-form/renderer/lib/ckeditor/ckeditor.js public/lib/ckeditor/ckeditor.js
579
+ ```
580
+
581
+ Then include in HTML:
582
+ ```html
583
+ <script src="/lib/ckeditor/ckeditor.js"></script>
584
+ ```
585
+
586
+ #### Method 3: Dynamic Loading
587
+
588
+ Use the provided hook to load CKEditor dynamically:
589
+
590
+ ```tsx
591
+ import { useCKEditor } from '@custom-form/renderer';
592
+
593
+ function App() {
594
+ const { isReady, isLoading, error } = useCKEditor({
595
+ scriptPath: '/lib/ckeditor/ckeditor.js',
596
+ autoLoad: true,
597
+ });
598
+
599
+ if (isLoading) {
600
+ return <div>Loading editor...</div>;
601
+ }
602
+
603
+ if (error) {
604
+ return <div>Error loading editor: {error.message}</div>;
605
+ }
606
+
607
+ return <FormRenderer formSchema={schema} />;
608
+ }
609
+ ```
610
+
611
+ #### Method 4: Custom Script Path
612
+
613
+ If CKEditor is hosted elsewhere:
614
+
615
+ ```tsx
616
+ const services: FormServices = {
617
+ ckEditorScriptPath: 'https://cdn.example.com/ckeditor.js',
618
+ // ... other services
619
+ };
620
+ ```
621
+
622
+ ### CKEditor Utilities
623
+
624
+ ```tsx
625
+ import {
626
+ loadCKEditor,
627
+ isCKEditorAvailable,
628
+ waitForCKEditor,
629
+ useCKEditor
630
+ } from '@custom-form/renderer';
631
+
632
+ // Check if available
633
+ if (isCKEditorAvailable()) {
634
+ // CKEditor is ready
635
+ }
636
+
637
+ // Load manually
638
+ await loadCKEditor('/lib/ckeditor/ckeditor.js');
639
+
640
+ // Wait for it to become available
641
+ await waitForCKEditor(10000); // 10 second timeout
642
+
643
+ // React hook
644
+ const { isReady, isLoading, error, load } = useCKEditor();
645
+ ```
646
+
647
+ ## License
648
+
649
+ MIT
650
+
651
+ ## Contributing
652
+
653
+ Contributions are welcome! Please feel free to submit a Pull Request.