@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 +653 -0
- package/dist/index.d.mts +352 -0
- package/dist/index.d.ts +352 -0
- package/dist/index.js +1840 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1796 -0
- package/dist/index.mjs.map +1 -0
- package/lib/ckeditor/README.md +56 -0
- package/lib/ckeditor/ckeditor.js +6 -0
- package/package.json +66 -0
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.
|