@dmitryvim/form-builder 0.1.9 → 0.1.11
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/dist/demo.js +574 -0
- package/dist/form-builder.js +1252 -1202
- package/dist/images/final_video.mp4 +0 -0
- package/dist/images/infographic_draft.jpg +0 -0
- package/dist/index.html +120 -1500
- package/package.json +3 -5
- package/dist/README.md +0 -284
- package/dist/example.html +0 -108
- package/dist/sample.html +0 -1703
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.11",
|
|
7
7
|
"description": "A reusable JSON schema form builder library",
|
|
8
8
|
"main": "dist/form-builder.js",
|
|
9
9
|
"files": [
|
|
@@ -11,10 +11,8 @@
|
|
|
11
11
|
"docs"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "mkdir -p dist && cp public/index.html dist/
|
|
15
|
-
"
|
|
16
|
-
"dev": "serve public",
|
|
17
|
-
"dev:sample": "serve dist --single",
|
|
14
|
+
"build": "mkdir -p dist && cp public/index.html dist/index.html && cp src/form-builder.js dist/form-builder.js && cp public/demo.js dist/demo.js && cp -r public/images dist/",
|
|
15
|
+
"dev": "npm run build && serve dist --single",
|
|
18
16
|
"test": "jest",
|
|
19
17
|
"format": "prettier --write ."
|
|
20
18
|
},
|
package/dist/README.md
DELETED
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
# Form Builder Library
|
|
2
|
-
|
|
3
|
-
A reusable JavaScript library for generating dynamic forms from JSON schemas.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
### 1. Include the Library
|
|
8
|
-
|
|
9
|
-
```html
|
|
10
|
-
<script src="https://unpkg.com/@dmitryvim/form-builder@latest/dist/form-builder.js"></script>
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Or via npm:
|
|
14
|
-
```bash
|
|
15
|
-
npm install @dmitryvim/form-builder
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
### 2. Basic Usage
|
|
19
|
-
|
|
20
|
-
```javascript
|
|
21
|
-
// Define your schema
|
|
22
|
-
const schema = {
|
|
23
|
-
"version": "0.3",
|
|
24
|
-
"title": "User Registration",
|
|
25
|
-
"elements": [
|
|
26
|
-
{
|
|
27
|
-
"type": "text",
|
|
28
|
-
"key": "name",
|
|
29
|
-
"label": "Full Name",
|
|
30
|
-
"required": true
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"type": "text",
|
|
34
|
-
"key": "email",
|
|
35
|
-
"label": "Email",
|
|
36
|
-
"required": true
|
|
37
|
-
}
|
|
38
|
-
]
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// Get container element
|
|
42
|
-
const container = document.getElementById('form-container');
|
|
43
|
-
|
|
44
|
-
// Render the form
|
|
45
|
-
const form = FormBuilder.renderForm(schema, container, {
|
|
46
|
-
onSubmit: (data) => {
|
|
47
|
-
console.log('Form submitted:', data);
|
|
48
|
-
},
|
|
49
|
-
onDraft: (data) => {
|
|
50
|
-
console.log('Draft saved:', data);
|
|
51
|
-
},
|
|
52
|
-
onError: (errors) => {
|
|
53
|
-
console.log('Validation errors:', errors);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## API Reference
|
|
59
|
-
|
|
60
|
-
### FormBuilder.renderForm(schema, container, options)
|
|
61
|
-
|
|
62
|
-
Renders a form in the specified container.
|
|
63
|
-
|
|
64
|
-
**Parameters:**
|
|
65
|
-
- `schema` - JSON schema object (v0.3)
|
|
66
|
-
- `container` - DOM element to render form in
|
|
67
|
-
- `options` - Configuration object (optional)
|
|
68
|
-
|
|
69
|
-
**Options:**
|
|
70
|
-
- `prefill` - Object with prefilled values
|
|
71
|
-
- `readonly` - Boolean, renders form in read-only mode (hides buttons)
|
|
72
|
-
- `debug` - Boolean, enables debug logging to console
|
|
73
|
-
- `onSubmit` - Callback for form submission `(data) => {}`
|
|
74
|
-
- `onDraft` - Callback for draft saving `(data) => {}`
|
|
75
|
-
- `onError` - Callback for validation errors `(errors) => {}`
|
|
76
|
-
- `buttons` - Custom button text: `{ submit: "Start Workflow", draft: "Save Progress" }`
|
|
77
|
-
|
|
78
|
-
**Important:** Upload handlers must return a **string** resourceId, not an object.
|
|
79
|
-
|
|
80
|
-
### FormBuilder.validateSchema(schema)
|
|
81
|
-
|
|
82
|
-
Validates a JSON schema and returns array of errors.
|
|
83
|
-
|
|
84
|
-
**Returns:** `string[]` - Array of error messages (empty if valid)
|
|
85
|
-
|
|
86
|
-
### FormBuilder.collectAndValidate(schema, formElement, skipValidation)
|
|
87
|
-
|
|
88
|
-
⚠️ **Direct API for advanced users only**
|
|
89
|
-
|
|
90
|
-
Collects form data and validates it. Most users should use `onSubmit`/`onDraft` callbacks instead.
|
|
91
|
-
|
|
92
|
-
**Parameters:**
|
|
93
|
-
- `schema` - The form schema
|
|
94
|
-
- `formElement` - The form DOM element
|
|
95
|
-
- `skipValidation` - Boolean, skip validation (for drafts)
|
|
96
|
-
|
|
97
|
-
**Returns:** `{ result: object, errors: string[] }`
|
|
98
|
-
|
|
99
|
-
**Important:** This is the correct destructuring format - there is no `.valid` or `.data` property.
|
|
100
|
-
|
|
101
|
-
### FormBuilder.setUploadHandler(uploadFn)
|
|
102
|
-
|
|
103
|
-
Sets custom file upload handler:
|
|
104
|
-
```javascript
|
|
105
|
-
FormBuilder.setUploadHandler(async (file) => {
|
|
106
|
-
// Upload file to your backend
|
|
107
|
-
const response = await fetch('/upload', {
|
|
108
|
-
method: 'POST',
|
|
109
|
-
body: file
|
|
110
|
-
});
|
|
111
|
-
const result = await response.json();
|
|
112
|
-
return result.resourceId; // Return resource ID
|
|
113
|
-
});
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### FormBuilder.setDownloadHandler(downloadFn)
|
|
117
|
-
|
|
118
|
-
Sets custom file download handler:
|
|
119
|
-
```javascript
|
|
120
|
-
FormBuilder.setDownloadHandler(async (resourceId, fileName) => {
|
|
121
|
-
window.open(`/download/${resourceId}`, '_blank');
|
|
122
|
-
});
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## Supported Field Types
|
|
126
|
-
|
|
127
|
-
- `text` - Single line text input
|
|
128
|
-
- `textarea` - Multi-line text input
|
|
129
|
-
- `number` - Numeric input with constraints
|
|
130
|
-
- `select` - Dropdown selection
|
|
131
|
-
- `file` - Single file upload
|
|
132
|
-
- `files` - Multiple file upload
|
|
133
|
-
- `group` - Nested objects with optional repeat and custom element titles
|
|
134
|
-
|
|
135
|
-
## Troubleshooting Common Issues
|
|
136
|
-
|
|
137
|
-
### ❌ Upload Handler Returns Object
|
|
138
|
-
```javascript
|
|
139
|
-
// Wrong - causes "resourceId.slice is not a function"
|
|
140
|
-
FormBuilder.setUploadHandler(async (file) => {
|
|
141
|
-
const response = await uploadFile(file);
|
|
142
|
-
return { reference: response.id, name: file.name }; // ❌ Object
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Correct - return string only
|
|
146
|
-
FormBuilder.setUploadHandler(async (file) => {
|
|
147
|
-
const response = await uploadFile(file);
|
|
148
|
-
return response.id; // ✅ String resourceId
|
|
149
|
-
});
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### ❌ Wrong API Usage
|
|
153
|
-
```javascript
|
|
154
|
-
// Wrong - no .valid or .data properties
|
|
155
|
-
const result = FormBuilder.collectAndValidate(schema, form);
|
|
156
|
-
if (result.valid) { // ❌ Property doesn't exist
|
|
157
|
-
console.log(result.data); // ❌ Property doesn't exist
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Correct - use destructuring
|
|
161
|
-
const { result, errors } = FormBuilder.collectAndValidate(schema, form);
|
|
162
|
-
if (errors.length === 0) { // ✅ Check errors array
|
|
163
|
-
console.log(result); // ✅ Data is in result
|
|
164
|
-
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### ❌ Group Elements Missing Elements Array
|
|
168
|
-
```javascript
|
|
169
|
-
// Wrong - causes "group.elements must be array"
|
|
170
|
-
{
|
|
171
|
-
type: 'group',
|
|
172
|
-
key: 'address',
|
|
173
|
-
// Missing elements array ❌
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Correct - always include elements
|
|
177
|
-
{
|
|
178
|
-
type: 'group',
|
|
179
|
-
key: 'address',
|
|
180
|
-
label: 'Address Information',
|
|
181
|
-
element_label: 'Address #$index', // ✅ Optional custom title
|
|
182
|
-
elements: [ // ✅ Required array
|
|
183
|
-
{ type: 'text', key: 'street', label: 'Street' }
|
|
184
|
-
]
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### ❌ Readonly Mode Confusion
|
|
189
|
-
```javascript
|
|
190
|
-
// This only hides the default buttons, fields remain editable
|
|
191
|
-
FormBuilder.renderForm(schema, container, { readonly: true });
|
|
192
|
-
|
|
193
|
-
// For true read-only, you need to disable inputs manually or use a different approach
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
## Complete Example
|
|
197
|
-
|
|
198
|
-
```javascript
|
|
199
|
-
const schema = {
|
|
200
|
-
"version": "0.3",
|
|
201
|
-
"title": "Video Cover Generation",
|
|
202
|
-
"elements": [
|
|
203
|
-
{
|
|
204
|
-
"type": "text",
|
|
205
|
-
"key": "concept",
|
|
206
|
-
"label": "Animation Concept (Optional)",
|
|
207
|
-
"required": false,
|
|
208
|
-
"maxLength": 100,
|
|
209
|
-
"placeholder": "e.g., fun, elegant, dynamic, cozy..."
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
"type": "group",
|
|
213
|
-
"key": "slides_input",
|
|
214
|
-
"label": "Video Slides",
|
|
215
|
-
"element_label": "Slide $index", // 🆕 Custom title for each item
|
|
216
|
-
"repeat": {
|
|
217
|
-
"min": 1,
|
|
218
|
-
"max": 10
|
|
219
|
-
},
|
|
220
|
-
"elements": [
|
|
221
|
-
{
|
|
222
|
-
"type": "file",
|
|
223
|
-
"key": "main_image",
|
|
224
|
-
"label": "Main Image",
|
|
225
|
-
"required": true,
|
|
226
|
-
"accept": {
|
|
227
|
-
"extensions": ["png", "jpg", "jpeg", "webp"],
|
|
228
|
-
"mime": ["image/png", "image/jpeg", "image/webp"]
|
|
229
|
-
},
|
|
230
|
-
"maxSizeMB": 25
|
|
231
|
-
},
|
|
232
|
-
{
|
|
233
|
-
"type": "files",
|
|
234
|
-
"key": "elements",
|
|
235
|
-
"label": "Element Images (Optional)",
|
|
236
|
-
"required": false,
|
|
237
|
-
"accept": {
|
|
238
|
-
"extensions": ["png", "jpg", "jpeg", "webp"],
|
|
239
|
-
"mime": ["image/png", "image/jpeg", "image/webp"]
|
|
240
|
-
},
|
|
241
|
-
"minCount": 0,
|
|
242
|
-
"maxCount": 10,
|
|
243
|
-
"maxSizeMB": 10
|
|
244
|
-
}
|
|
245
|
-
]
|
|
246
|
-
}
|
|
247
|
-
]
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
// Configure file upload
|
|
251
|
-
FormBuilder.setUploadHandler(async (file) => {
|
|
252
|
-
const formData = new FormData();
|
|
253
|
-
formData.append('file', file);
|
|
254
|
-
|
|
255
|
-
const response = await fetch('/api/upload', {
|
|
256
|
-
method: 'POST',
|
|
257
|
-
body: formData
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
const result = await response.json();
|
|
261
|
-
return result.resourceId;
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// Render form
|
|
265
|
-
FormBuilder.renderForm(schema, document.getElementById('form'), {
|
|
266
|
-
onSubmit: async (data) => {
|
|
267
|
-
const response = await fetch('/api/submit', {
|
|
268
|
-
method: 'POST',
|
|
269
|
-
headers: { 'Content-Type': 'application/json' },
|
|
270
|
-
body: JSON.stringify(data)
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
if (response.ok) {
|
|
274
|
-
alert('Form submitted successfully!');
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
});
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
## Files
|
|
281
|
-
|
|
282
|
-
- `form-builder.js` - Main library file
|
|
283
|
-
- `sample.html` - Interactive demo page
|
|
284
|
-
- Complete documentation at `/docs/`
|
package/dist/example.html
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Form Builder - Simple Example</title>
|
|
7
|
-
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
-
</head>
|
|
9
|
-
<body class="bg-gray-50 p-8">
|
|
10
|
-
<div class="max-w-2xl mx-auto">
|
|
11
|
-
<h1 class="text-3xl font-bold mb-8">Form Builder Library Example</h1>
|
|
12
|
-
|
|
13
|
-
<div class="bg-white rounded-lg shadow p-6 mb-8">
|
|
14
|
-
<h2 class="text-xl font-semibold mb-4">Generated Form</h2>
|
|
15
|
-
<div id="form-container"></div>
|
|
16
|
-
</div>
|
|
17
|
-
|
|
18
|
-
<div class="bg-white rounded-lg shadow p-6">
|
|
19
|
-
<h2 class="text-xl font-semibold mb-4">Form Output</h2>
|
|
20
|
-
<pre id="output" class="bg-gray-100 p-4 rounded text-sm overflow-auto">Submit the form to see output...</pre>
|
|
21
|
-
</div>
|
|
22
|
-
</div>
|
|
23
|
-
|
|
24
|
-
<script src="form-builder.js"></script>
|
|
25
|
-
<script>
|
|
26
|
-
// Example schema demonstrating element_label feature
|
|
27
|
-
const schema = {
|
|
28
|
-
"version": "0.3",
|
|
29
|
-
"title": "Video Cover Generation",
|
|
30
|
-
"elements": [
|
|
31
|
-
{
|
|
32
|
-
"type": "text",
|
|
33
|
-
"key": "concept",
|
|
34
|
-
"label": "Animation Concept (Optional)",
|
|
35
|
-
"required": false,
|
|
36
|
-
"maxLength": 100,
|
|
37
|
-
"placeholder": "e.g., fun, elegant, dynamic, cozy..."
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"type": "group",
|
|
41
|
-
"key": "slides_input",
|
|
42
|
-
"label": "Video Slides",
|
|
43
|
-
"element_label": "Slide $index",
|
|
44
|
-
"repeat": {
|
|
45
|
-
"min": 1,
|
|
46
|
-
"max": 5
|
|
47
|
-
},
|
|
48
|
-
"elements": [
|
|
49
|
-
{
|
|
50
|
-
"type": "file",
|
|
51
|
-
"key": "main_image",
|
|
52
|
-
"label": "Main Image",
|
|
53
|
-
"required": true,
|
|
54
|
-
"accept": {
|
|
55
|
-
"extensions": ["png", "jpg", "jpeg", "webp"],
|
|
56
|
-
"mime": ["image/png", "image/jpeg", "image/webp"]
|
|
57
|
-
},
|
|
58
|
-
"maxSizeMB": 25
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"type": "files",
|
|
62
|
-
"key": "elements",
|
|
63
|
-
"label": "Element Images (Optional)",
|
|
64
|
-
"required": false,
|
|
65
|
-
"accept": {
|
|
66
|
-
"extensions": ["png", "jpg", "jpeg", "webp"],
|
|
67
|
-
"mime": ["image/png", "image/jpeg", "image/webp"]
|
|
68
|
-
},
|
|
69
|
-
"minCount": 0,
|
|
70
|
-
"maxCount": 5,
|
|
71
|
-
"maxSizeMB": 10
|
|
72
|
-
}
|
|
73
|
-
]
|
|
74
|
-
}
|
|
75
|
-
]
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Configure file upload simulation
|
|
79
|
-
FormBuilder.setUploadHandler(async (file) => {
|
|
80
|
-
// Simulate upload delay
|
|
81
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
82
|
-
|
|
83
|
-
// Generate fake resource ID
|
|
84
|
-
const resourceId = 'res_' + Math.random().toString(36).substr(2, 12);
|
|
85
|
-
console.log(`File "${file.name}" uploaded with ID: ${resourceId}`);
|
|
86
|
-
return resourceId;
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Render the form
|
|
90
|
-
const container = document.getElementById('form-container');
|
|
91
|
-
const output = document.getElementById('output');
|
|
92
|
-
|
|
93
|
-
FormBuilder.renderForm(schema, container, {
|
|
94
|
-
onSubmit: (data) => {
|
|
95
|
-
output.textContent = JSON.stringify(data, null, 2);
|
|
96
|
-
alert('Form submitted successfully! Check the output below.');
|
|
97
|
-
},
|
|
98
|
-
onDraft: (data) => {
|
|
99
|
-
output.textContent = 'DRAFT:\n' + JSON.stringify(data, null, 2);
|
|
100
|
-
console.log('Draft saved:', data);
|
|
101
|
-
},
|
|
102
|
-
onError: (errors) => {
|
|
103
|
-
alert('Validation errors: ' + errors.join(', '));
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
</script>
|
|
107
|
-
</body>
|
|
108
|
-
</html>
|