@dmitryvim/form-builder 0.1.22 → 0.1.25

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.
@@ -7,18 +7,26 @@ Complete guide for integrating Form Builder into your application.
7
7
  ### Basic Iframe Embedding
8
8
 
9
9
  ```html
10
- <iframe src="https://picazru.github.io/form-builder/dist/index.html"
11
- width="100%" height="600px" frameborder="0"></iframe>
10
+ <iframe
11
+ src="https://picazru.github.io/form-builder/dist/index.html"
12
+ width="100%"
13
+ height="600px"
14
+ frameborder="0"
15
+ ></iframe>
12
16
  ```
13
17
 
14
18
  ### With Custom Schema
15
19
 
16
20
  ```html
17
- <iframe src="https://picazru.github.io/form-builder/dist/index.html?schema=BASE64_SCHEMA"
18
- width="100%" height="600px"></iframe>
21
+ <iframe
22
+ src="https://picazru.github.io/form-builder/dist/index.html?schema=BASE64_SCHEMA"
23
+ width="100%"
24
+ height="600px"
25
+ ></iframe>
19
26
  ```
20
27
 
21
28
  **Encoding Schema:**
29
+
22
30
  ```javascript
23
31
  const schema = { version: "0.3", title: "My Form", elements: [...] };
24
32
  const encodedSchema = btoa(JSON.stringify(schema));
@@ -38,10 +46,10 @@ npm install @dmitryvim/form-builder
38
46
  Copy the form builder HTML directly into your application:
39
47
 
40
48
  ```javascript
41
- import formBuilderHTML from '@dmitryvim/form-builder/dist/index.html';
49
+ import formBuilderHTML from "@dmitryvim/form-builder/dist/index.html";
42
50
 
43
51
  // Insert into your page
44
- document.getElementById('form-container').innerHTML = formBuilderHTML;
52
+ document.getElementById("form-container").innerHTML = formBuilderHTML;
45
53
  ```
46
54
 
47
55
  ## Configuration
@@ -51,35 +59,38 @@ document.getElementById('form-container').innerHTML = formBuilderHTML;
51
59
  The upload handler **must return a resource ID** that can be used for downloads and form submission:
52
60
 
53
61
  ```javascript
54
- window.addEventListener('message', (event) => {
55
- if (event.origin !== 'https://picazru.github.io') return;
56
-
57
- if (event.data.type === 'formBuilderReady') {
58
- const iframe = document.getElementById('formBuilder');
59
- iframe.contentWindow.postMessage({
60
- type: 'configure',
61
- config: {
62
- uploadHandler: async (file) => {
63
- const formData = new FormData();
64
- formData.append('file', file);
65
-
66
- const response = await fetch('/api/upload', {
67
- method: 'POST',
68
- body: formData
69
- });
70
-
71
- const result = await response.json();
72
- return result.resourceId; // Return ID, not URL
73
- },
74
- downloadHandler: async (resourceId) => {
75
- window.open(`/api/download/${resourceId}`, '_blank');
76
- },
77
- thumbnailHandler: async (resourceId) => {
78
- return `/api/thumbnail/${resourceId}`;
79
- }
80
- }
81
- }, 'https://picazru.github.io');
82
- }
62
+ window.addEventListener("message", (event) => {
63
+ if (event.origin !== "https://picazru.github.io") return;
64
+
65
+ if (event.data.type === "formBuilderReady") {
66
+ const iframe = document.getElementById("formBuilder");
67
+ iframe.contentWindow.postMessage(
68
+ {
69
+ type: "configure",
70
+ config: {
71
+ uploadHandler: async (file) => {
72
+ const formData = new FormData();
73
+ formData.append("file", file);
74
+
75
+ const response = await fetch("/api/upload", {
76
+ method: "POST",
77
+ body: formData,
78
+ });
79
+
80
+ const result = await response.json();
81
+ return result.resourceId; // Return ID, not URL
82
+ },
83
+ downloadHandler: async (resourceId) => {
84
+ window.open(`/api/download/${resourceId}`, "_blank");
85
+ },
86
+ thumbnailHandler: async (resourceId) => {
87
+ return `/api/thumbnail/${resourceId}`;
88
+ },
89
+ },
90
+ },
91
+ "https://picazru.github.io",
92
+ );
93
+ }
83
94
  });
84
95
  ```
85
96
 
@@ -88,12 +99,15 @@ window.addEventListener('message', (event) => {
88
99
  Display form data without editing capabilities:
89
100
 
90
101
  ```javascript
91
- iframe.contentWindow.postMessage({
92
- type: 'configure',
102
+ iframe.contentWindow.postMessage(
103
+ {
104
+ type: "configure",
93
105
  options: {
94
- readonly: true
95
- }
96
- }, 'https://picazru.github.io');
106
+ readonly: true,
107
+ },
108
+ },
109
+ "https://picazru.github.io",
110
+ );
97
111
  ```
98
112
 
99
113
  ### Tailwind CSS Integration
@@ -103,14 +117,14 @@ Always uses Tailwind CSS styling. Add to your CSS:
103
117
  ```css
104
118
  /* Custom properties for consistent styling */
105
119
  :root {
106
- --form-primary: 59 130 246; /* Blue */
107
- --form-secondary: 100 116 139; /* Gray */
108
- --form-accent: 16 185 129; /* Green */
120
+ --form-primary: 59 130 246; /* Blue */
121
+ --form-secondary: 100 116 139; /* Gray */
122
+ --form-accent: 16 185 129; /* Green */
109
123
  }
110
124
 
111
125
  /* Override form builder styles if needed */
112
126
  .form-builder-container {
113
- @apply max-w-none; /* Remove max-width constraints */
127
+ @apply max-w-none; /* Remove max-width constraints */
114
128
  }
115
129
  ```
116
130
 
@@ -121,14 +135,14 @@ Always uses Tailwind CSS styling. Add to your CSS:
121
135
  Listen for form submissions:
122
136
 
123
137
  ```javascript
124
- window.addEventListener('message', (event) => {
125
- if (event.data.type === 'formSubmit') {
126
- const { data, schema } = event.data;
127
- console.log('Form submitted:', data);
128
-
129
- // Handle the submission
130
- handleFormSubmission(data);
131
- }
138
+ window.addEventListener("message", (event) => {
139
+ if (event.data.type === "formSubmit") {
140
+ const { data, schema } = event.data;
141
+ console.log("Form submitted:", data);
142
+
143
+ // Handle the submission
144
+ handleFormSubmission(data);
145
+ }
132
146
  });
133
147
  ```
134
148
 
@@ -137,14 +151,14 @@ window.addEventListener('message', (event) => {
137
151
  Listen for draft save events:
138
152
 
139
153
  ```javascript
140
- window.addEventListener('message', (event) => {
141
- if (event.data.type === 'draftSave') {
142
- const { data, schema } = event.data;
143
- console.log('Draft saved:', data);
144
-
145
- // Save draft without validation
146
- saveDraft(data);
147
- }
154
+ window.addEventListener("message", (event) => {
155
+ if (event.data.type === "draftSave") {
156
+ const { data, schema } = event.data;
157
+ console.log("Draft saved:", data);
158
+
159
+ // Save draft without validation
160
+ saveDraft(data);
161
+ }
148
162
  });
149
163
  ```
150
164
 
@@ -166,28 +180,34 @@ iframe.contentWindow.postMessage({
166
180
  ### Prefill Form Data
167
181
 
168
182
  ```javascript
169
- iframe.contentWindow.postMessage({
170
- type: 'setData',
183
+ iframe.contentWindow.postMessage(
184
+ {
185
+ type: "setData",
171
186
  data: {
172
- name: "John Doe",
173
- email: "john@example.com"
174
- }
175
- }, 'https://picazru.github.io');
187
+ name: "John Doe",
188
+ email: "john@example.com",
189
+ },
190
+ },
191
+ "https://picazru.github.io",
192
+ );
176
193
  ```
177
194
 
178
195
  ### Get Current Data
179
196
 
180
197
  ```javascript
181
198
  // Request data
182
- iframe.contentWindow.postMessage({
183
- type: 'getData'
184
- }, 'https://picazru.github.io');
199
+ iframe.contentWindow.postMessage(
200
+ {
201
+ type: "getData",
202
+ },
203
+ "https://picazru.github.io",
204
+ );
185
205
 
186
206
  // Listen for response
187
- window.addEventListener('message', (event) => {
188
- if (event.data.type === 'currentData') {
189
- console.log('Current form data:', event.data.data);
190
- }
207
+ window.addEventListener("message", (event) => {
208
+ if (event.data.type === "currentData") {
209
+ console.log("Current form data:", event.data.data);
210
+ }
191
211
  });
192
212
  ```
193
213
 
@@ -195,15 +215,18 @@ window.addEventListener('message', (event) => {
195
215
 
196
216
  ```javascript
197
217
  // Request validation
198
- iframe.contentWindow.postMessage({
199
- type: 'validate'
200
- }, 'https://picazru.github.io');
218
+ iframe.contentWindow.postMessage(
219
+ {
220
+ type: "validate",
221
+ },
222
+ "https://picazru.github.io",
223
+ );
201
224
 
202
225
  // Listen for result
203
- window.addEventListener('message', (event) => {
204
- if (event.data.type === 'validationResult') {
205
- console.log('Form is valid:', event.data.isValid);
206
- }
226
+ window.addEventListener("message", (event) => {
227
+ if (event.data.type === "validationResult") {
228
+ console.log("Form is valid:", event.data.isValid);
229
+ }
207
230
  });
208
231
  ```
209
232
 
@@ -215,58 +238,61 @@ Your backend must handle file uploads and return resource IDs:
215
238
 
216
239
  ```javascript
217
240
  // Express.js example
218
- app.post('/api/upload', upload.single('file'), (req, res) => {
219
- const file = req.file;
220
-
221
- // Store file (S3, local storage, etc.)
222
- const resourceId = generateResourceId();
223
- storeFile(resourceId, file);
224
-
225
- res.json({
226
- resourceId: resourceId,
227
- filename: file.originalname,
228
- size: file.size,
229
- mimetype: file.mimetype
230
- });
241
+ app.post("/api/upload", upload.single("file"), (req, res) => {
242
+ const file = req.file;
243
+
244
+ // Store file (S3, local storage, etc.)
245
+ const resourceId = generateResourceId();
246
+ storeFile(resourceId, file);
247
+
248
+ res.json({
249
+ resourceId: resourceId,
250
+ filename: file.originalname,
251
+ size: file.size,
252
+ mimetype: file.mimetype,
253
+ });
231
254
  });
232
255
  ```
233
256
 
234
257
  ### File Download Endpoint
235
258
 
236
259
  ```javascript
237
- app.get('/api/download/:resourceId', (req, res) => {
238
- const { resourceId } = req.params;
239
-
240
- // Retrieve file info
241
- const fileInfo = getFileInfo(resourceId);
242
- if (!fileInfo) {
243
- return res.status(404).json({ error: 'File not found' });
244
- }
245
-
246
- // Stream file to client
247
- const fileStream = getFileStream(resourceId);
248
- res.setHeader('Content-Disposition', `attachment; filename="${fileInfo.filename}"`);
249
- res.setHeader('Content-Type', fileInfo.mimetype);
250
- fileStream.pipe(res);
260
+ app.get("/api/download/:resourceId", (req, res) => {
261
+ const { resourceId } = req.params;
262
+
263
+ // Retrieve file info
264
+ const fileInfo = getFileInfo(resourceId);
265
+ if (!fileInfo) {
266
+ return res.status(404).json({ error: "File not found" });
267
+ }
268
+
269
+ // Stream file to client
270
+ const fileStream = getFileStream(resourceId);
271
+ res.setHeader(
272
+ "Content-Disposition",
273
+ `attachment; filename="${fileInfo.filename}"`,
274
+ );
275
+ res.setHeader("Content-Type", fileInfo.mimetype);
276
+ fileStream.pipe(res);
251
277
  });
252
278
  ```
253
279
 
254
280
  ### Form Submission Handler
255
281
 
256
282
  ```javascript
257
- app.post('/api/forms/submit', (req, res) => {
258
- const { schema, data } = req.body;
259
-
260
- // Process form data
261
- // File fields will contain resource IDs
262
-
263
- console.log('Received form:', data);
264
- // Example: { name: "John", avatar: "res_abc123", documents: ["res_def456", "res_ghi789"] }
265
-
266
- // Validate and save
267
- const result = processFormSubmission(schema, data);
268
-
269
- res.json({ success: true, id: result.id });
283
+ app.post("/api/forms/submit", (req, res) => {
284
+ const { schema, data } = req.body;
285
+
286
+ // Process form data
287
+ // File fields will contain resource IDs
288
+
289
+ console.log("Received form:", data);
290
+ // Example: { name: "John", avatar: "res_abc123", documents: ["res_def456", "res_ghi789"] }
291
+
292
+ // Validate and save
293
+ const result = processFormSubmission(schema, data);
294
+
295
+ res.json({ success: true, id: result.id });
270
296
  });
271
297
  ```
272
298
 
@@ -275,39 +301,42 @@ app.post('/api/forms/submit', (req, res) => {
275
301
  ### React Integration
276
302
 
277
303
  ```jsx
278
- import { useEffect, useRef } from 'react';
304
+ import { useEffect, useRef } from "react";
279
305
 
280
306
  function FormBuilder({ schema, onSubmit }) {
281
- const iframeRef = useRef(null);
282
-
283
- useEffect(() => {
284
- const handleMessage = (event) => {
285
- if (event.data.type === 'formBuilderReady') {
286
- // Configure form builder
287
- iframeRef.current.contentWindow.postMessage({
288
- type: 'setSchema',
289
- schema: schema
290
- }, 'https://picazru.github.io');
291
- }
292
-
293
- if (event.data.type === 'formSubmit') {
294
- onSubmit(event.data.data);
295
- }
296
- };
297
-
298
- window.addEventListener('message', handleMessage);
299
- return () => window.removeEventListener('message', handleMessage);
300
- }, [schema, onSubmit]);
301
-
302
- return (
303
- <iframe
304
- ref={iframeRef}
305
- src="https://picazru.github.io/form-builder/dist/index.html"
306
- width="100%"
307
- height="600px"
308
- frameBorder="0"
309
- />
310
- );
307
+ const iframeRef = useRef(null);
308
+
309
+ useEffect(() => {
310
+ const handleMessage = (event) => {
311
+ if (event.data.type === "formBuilderReady") {
312
+ // Configure form builder
313
+ iframeRef.current.contentWindow.postMessage(
314
+ {
315
+ type: "setSchema",
316
+ schema: schema,
317
+ },
318
+ "https://picazru.github.io",
319
+ );
320
+ }
321
+
322
+ if (event.data.type === "formSubmit") {
323
+ onSubmit(event.data.data);
324
+ }
325
+ };
326
+
327
+ window.addEventListener("message", handleMessage);
328
+ return () => window.removeEventListener("message", handleMessage);
329
+ }, [schema, onSubmit]);
330
+
331
+ return (
332
+ <iframe
333
+ ref={iframeRef}
334
+ src="https://picazru.github.io/form-builder/dist/index.html"
335
+ width="100%"
336
+ height="600px"
337
+ frameBorder="0"
338
+ />
339
+ );
311
340
  }
312
341
  ```
313
342
 
@@ -315,42 +344,45 @@ function FormBuilder({ schema, onSubmit }) {
315
344
 
316
345
  ```vue
317
346
  <template>
318
- <iframe
319
- ref="formBuilder"
320
- src="https://picazru.github.io/form-builder/dist/index.html"
321
- width="100%"
322
- height="600px"
323
- frameborder="0"
324
- @load="onIframeLoad"
325
- />
347
+ <iframe
348
+ ref="formBuilder"
349
+ src="https://picazru.github.io/form-builder/dist/index.html"
350
+ width="100%"
351
+ height="600px"
352
+ frameborder="0"
353
+ @load="onIframeLoad"
354
+ />
326
355
  </template>
327
356
 
328
357
  <script>
329
358
  export default {
330
- props: ['schema'],
331
-
332
- mounted() {
333
- window.addEventListener('message', this.handleMessage);
359
+ props: ["schema"],
360
+
361
+ mounted() {
362
+ window.addEventListener("message", this.handleMessage);
363
+ },
364
+
365
+ beforeUnmount() {
366
+ window.removeEventListener("message", this.handleMessage);
367
+ },
368
+
369
+ methods: {
370
+ handleMessage(event) {
371
+ if (event.data.type === "formBuilderReady") {
372
+ this.$refs.formBuilder.contentWindow.postMessage(
373
+ {
374
+ type: "setSchema",
375
+ schema: this.schema,
376
+ },
377
+ "https://picazru.github.io",
378
+ );
379
+ }
380
+
381
+ if (event.data.type === "formSubmit") {
382
+ this.$emit("submit", event.data.data);
383
+ }
334
384
  },
335
-
336
- beforeUnmount() {
337
- window.removeEventListener('message', this.handleMessage);
338
- },
339
-
340
- methods: {
341
- handleMessage(event) {
342
- if (event.data.type === 'formBuilderReady') {
343
- this.$refs.formBuilder.contentWindow.postMessage({
344
- type: 'setSchema',
345
- schema: this.schema
346
- }, 'https://picazru.github.io');
347
- }
348
-
349
- if (event.data.type === 'formSubmit') {
350
- this.$emit('submit', event.data.data);
351
- }
352
- }
353
- }
385
+ },
354
386
  };
355
387
  </script>
356
388
  ```
@@ -362,20 +394,20 @@ export default {
362
394
  Always validate message origins:
363
395
 
364
396
  ```javascript
365
- window.addEventListener('message', (event) => {
366
- // Only accept messages from form builder origin
367
- if (event.origin !== 'https://picazru.github.io') {
368
- return;
369
- }
370
-
371
- // Process message...
397
+ window.addEventListener("message", (event) => {
398
+ // Only accept messages from form builder origin
399
+ if (event.origin !== "https://picazru.github.io") {
400
+ return;
401
+ }
402
+
403
+ // Process message...
372
404
  });
373
405
  ```
374
406
 
375
407
  ### File Upload Security
376
408
 
377
409
  - **Validate file types** on server
378
- - **Limit file sizes** appropriately
410
+ - **Limit file sizes** appropriately
379
411
  - **Scan for viruses** if possible
380
412
  - **Use secure file storage** (S3 with proper permissions)
381
413
  - **Generate secure resource IDs** (UUID, crypto.randomUUID())
@@ -385,9 +417,9 @@ window.addEventListener('message', (event) => {
385
417
  Add to your CSP headers:
386
418
 
387
419
  ```
388
- Content-Security-Policy:
389
- default-src 'self';
390
- frame-src https://picazru.github.io;
420
+ Content-Security-Policy:
421
+ default-src 'self';
422
+ frame-src https://picazru.github.io;
391
423
  connect-src 'self' https://your-api.com;
392
424
  ```
393
425
 
@@ -408,12 +440,15 @@ Content-Security-Policy:
408
440
  Enable console logging in iframe:
409
441
 
410
442
  ```javascript
411
- iframe.contentWindow.postMessage({
412
- type: 'configure',
443
+ iframe.contentWindow.postMessage(
444
+ {
445
+ type: "configure",
413
446
  config: {
414
- debug: true
415
- }
416
- }, 'https://picazru.github.io');
447
+ debug: true,
448
+ },
449
+ },
450
+ "https://picazru.github.io",
451
+ );
417
452
  ```
418
453
 
419
454
  This will log all form builder events to the browser console.
@@ -431,15 +466,15 @@ This will log all form builder events to the browser console.
431
466
  ```javascript
432
467
  // Old (v0.1.x)
433
468
  uploadHandler: async (file) => {
434
- // ... upload logic
435
- return result.fileUrl; // URL
436
- }
469
+ // ... upload logic
470
+ return result.fileUrl; // URL
471
+ };
437
472
 
438
- // New (v0.2.x)
473
+ // New (v0.2.x)
439
474
  uploadHandler: async (file) => {
440
- // ... upload logic
441
- return result.resourceId; // ID
442
- }
475
+ // ... upload logic
476
+ return result.resourceId; // ID
477
+ };
443
478
  ```
444
479
 
445
- This integration guide covers all aspects of using Form Builder in production applications.
480
+ This integration guide covers all aspects of using Form Builder in production applications.