@dmitryvim/form-builder 0.1.28 → 0.1.31
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 +178 -22
- package/dist/demo.js +59 -1
- package/dist/form-builder.js +45 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
JSON Schema → Dynamic Forms → Structured Output
|
|
4
4
|
|
|
5
|
-
A
|
|
5
|
+
A comprehensive, zero-dependency form generation library that converts JSON Schema v0.3 into dynamic, interactive HTML forms with advanced file handling, real-time validation, internationalization, and extensive field type support.
|
|
6
6
|
|
|
7
7
|
## Live Demo
|
|
8
8
|
|
|
@@ -37,15 +37,21 @@ npm install @dmitryvim/form-builder
|
|
|
37
37
|
|
|
38
38
|
## Core Features
|
|
39
39
|
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
40
|
+
- **🎯 Schema-driven forms**: JSON Schema v0.3 → Interactive forms with live preview
|
|
41
|
+
- **📁 Advanced file handling**: Images, videos, documents with drag-and-drop and grid preview
|
|
42
|
+
- **✅ Real-time validation**: Client-side validation with visual feedback and error display
|
|
43
|
+
- **🌍 Internationalization**: Built-in English/Russian support with extensible translation system
|
|
44
|
+
- **🎨 Rich field types**: Text, textarea, number, select, file, files, and nested groups
|
|
45
|
+
- **👁️ Read-only mode**: Display form data without editing capabilities
|
|
46
|
+
- **🔘 Action buttons**: Configurable buttons in readonly mode for custom interactions
|
|
47
|
+
- **💾 Draft saving**: Save incomplete forms without validation
|
|
48
|
+
- **🔧 Framework agnostic**: Works with any web stack (React, Vue, Angular, vanilla JS)
|
|
49
|
+
- **📦 Zero dependencies**: Self-contained HTML/CSS/JavaScript
|
|
50
|
+
- **📱 Responsive design**: Mobile-friendly with Tailwind CSS styling
|
|
47
51
|
|
|
48
|
-
##
|
|
52
|
+
## Quick Examples
|
|
53
|
+
|
|
54
|
+
### Simple Contact Form
|
|
49
55
|
|
|
50
56
|
```json
|
|
51
57
|
{
|
|
@@ -56,36 +62,186 @@ npm install @dmitryvim/form-builder
|
|
|
56
62
|
"type": "text",
|
|
57
63
|
"key": "name",
|
|
58
64
|
"label": "Full Name",
|
|
59
|
-
"required": true
|
|
65
|
+
"required": true,
|
|
66
|
+
"minLength": 2,
|
|
67
|
+
"maxLength": 50
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"type": "textarea",
|
|
71
|
+
"key": "message",
|
|
72
|
+
"label": "Message",
|
|
73
|
+
"placeholder": "Your message here...",
|
|
74
|
+
"required": true,
|
|
75
|
+
"rows": 4
|
|
60
76
|
},
|
|
61
77
|
{
|
|
62
78
|
"type": "files",
|
|
63
79
|
"key": "attachments",
|
|
64
80
|
"label": "Attachments",
|
|
81
|
+
"description": "Upload supporting documents or images",
|
|
65
82
|
"maxCount": 3,
|
|
83
|
+
"maxSizeMB": 10,
|
|
66
84
|
"accept": {
|
|
67
|
-
"extensions": ["pdf", "jpg", "png"]
|
|
85
|
+
"extensions": ["pdf", "jpg", "png", "docx"]
|
|
68
86
|
}
|
|
69
87
|
}
|
|
70
88
|
]
|
|
71
89
|
}
|
|
72
90
|
```
|
|
73
91
|
|
|
74
|
-
|
|
92
|
+
### Advanced Product Form with Actions
|
|
75
93
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"version": "0.3",
|
|
97
|
+
"title": "Product Registration",
|
|
98
|
+
"elements": [
|
|
99
|
+
{
|
|
100
|
+
"type": "file",
|
|
101
|
+
"key": "mainImage",
|
|
102
|
+
"label": "Product Image",
|
|
103
|
+
"required": true,
|
|
104
|
+
"accept": {
|
|
105
|
+
"extensions": ["jpg", "png", "webp"]
|
|
106
|
+
},
|
|
107
|
+
"actions": [
|
|
108
|
+
{ "value": "enhance", "label": "Enhance Quality" },
|
|
109
|
+
{ "value": "crop", "label": "Auto Crop" },
|
|
110
|
+
{ "value": "retry", "label": "Try Again" }
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"type": "group",
|
|
115
|
+
"key": "specifications",
|
|
116
|
+
"label": "Product Specifications",
|
|
117
|
+
"repeat": { "min": 1, "max": 10 },
|
|
118
|
+
"elements": [
|
|
119
|
+
{
|
|
120
|
+
"type": "text",
|
|
121
|
+
"key": "name",
|
|
122
|
+
"label": "Specification Name",
|
|
123
|
+
"required": true
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"type": "text",
|
|
127
|
+
"key": "value",
|
|
128
|
+
"label": "Value",
|
|
129
|
+
"required": true
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Integration Methods
|
|
138
|
+
|
|
139
|
+
### 1. NPM Package (Recommended)
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npm install @dmitryvim/form-builder
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
// ES6 imports
|
|
147
|
+
import FormBuilder from "@dmitryvim/form-builder";
|
|
148
|
+
|
|
149
|
+
// Configure and use
|
|
150
|
+
FormBuilder.setFormRoot(document.getElementById("form-container"));
|
|
151
|
+
FormBuilder.setUploadHandler(async (file) => {
|
|
152
|
+
// Your upload logic - return resource ID
|
|
153
|
+
return "resource-123";
|
|
154
|
+
});
|
|
155
|
+
FormBuilder.setActionHandler((value) => {
|
|
156
|
+
// Handle action button clicks
|
|
157
|
+
console.log("Action clicked:", value);
|
|
158
|
+
});
|
|
159
|
+
FormBuilder.renderForm(schema, prefillData);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 2. CDN Integration
|
|
79
163
|
|
|
80
|
-
|
|
164
|
+
```html
|
|
165
|
+
<!-- Direct script include (npm CDN) -->
|
|
166
|
+
<script src="https://cdn.jsdelivr.net/npm/@dmitryvim/form-builder@latest/dist/form-builder.js"></script>
|
|
167
|
+
<script>
|
|
168
|
+
window.FormBuilder.renderForm(schema);
|
|
169
|
+
</script>
|
|
170
|
+
|
|
171
|
+
<!-- Or use our S3 CDN -->
|
|
172
|
+
<script src="https://picaz-form-builder.website.yandexcloud.net/form-builder/latest/form-builder.js"></script>
|
|
173
|
+
|
|
174
|
+
<!-- Embed complete demo -->
|
|
175
|
+
<iframe
|
|
176
|
+
src="https://picaz-form-builder.website.yandexcloud.net/form-builder/latest/index.html"
|
|
177
|
+
width="100%"
|
|
178
|
+
height="600px"
|
|
179
|
+
frameborder="0"
|
|
180
|
+
>
|
|
181
|
+
</iframe>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 3. Self-Hosted Deployment
|
|
185
|
+
|
|
186
|
+
Download and serve the `dist/` folder contents.
|
|
187
|
+
|
|
188
|
+
See [Integration Guide](docs/integration.md) for detailed setup instructions.
|
|
189
|
+
|
|
190
|
+
## Complete Feature Set
|
|
191
|
+
|
|
192
|
+
### Field Types
|
|
193
|
+
|
|
194
|
+
- **Text**: Single-line with pattern validation, length limits
|
|
195
|
+
- **Textarea**: Multi-line with configurable rows
|
|
196
|
+
- **Number**: Numeric input with min/max/step/decimals
|
|
197
|
+
- **Select**: Dropdown with options and default values
|
|
198
|
+
- **File**: Single file upload with preview and type restrictions
|
|
199
|
+
- **Files**: Multiple file upload with grid layout and drag-and-drop
|
|
200
|
+
- **Group**: Nested objects with repeatable array support
|
|
201
|
+
|
|
202
|
+
### File Handling
|
|
203
|
+
|
|
204
|
+
- **Supported formats**: Images (jpg, png, gif, webp), Videos (mp4, webm, mov), Documents (pdf, docx, etc.)
|
|
205
|
+
- **Preview system**: Thumbnails for images, video players, document icons
|
|
206
|
+
- **Drag-and-drop**: Visual feedback and multi-file support
|
|
207
|
+
- **Validation**: File size, type, and count restrictions
|
|
208
|
+
- **Resource management**: Metadata tracking and automatic cleanup
|
|
209
|
+
|
|
210
|
+
### Validation & UX
|
|
211
|
+
|
|
212
|
+
- **Real-time validation**: As-you-type with visual feedback
|
|
213
|
+
- **Schema validation**: Comprehensive error reporting
|
|
214
|
+
- **Internationalization**: English/Russian built-in, extensible
|
|
215
|
+
- **Tooltips**: Field descriptions and hints
|
|
216
|
+
- **Responsive design**: Mobile-friendly interface
|
|
217
|
+
- **Read-only mode**: Display data without editing
|
|
218
|
+
- **Action buttons**: Custom buttons in readonly mode with configurable handlers
|
|
81
219
|
|
|
82
220
|
## Documentation
|
|
83
221
|
|
|
84
|
-
- [Integration Guide](docs/integration.md) -
|
|
85
|
-
- [Schema Reference](docs/schema.md) -
|
|
86
|
-
- [
|
|
87
|
-
- [Development Guide](
|
|
222
|
+
- [Integration Guide](docs/integration.md) - Complete setup and usage
|
|
223
|
+
- [Schema Reference](docs/schema.md) - Field types and validation rules
|
|
224
|
+
- [API Documentation](docs/api.md) - Method reference and examples
|
|
225
|
+
- [Development Guide](CLAUDE.md) - Architecture and development workflow
|
|
226
|
+
|
|
227
|
+
## Live Demo
|
|
228
|
+
|
|
229
|
+
**Try it now**: [https://picaz-form-builder.website.yandexcloud.net/form-builder/latest/index.html](https://picaz-form-builder.website.yandexcloud.net/form-builder/latest/index.html)
|
|
230
|
+
|
|
231
|
+
**Version Index**: [https://picaz-form-builder.website.yandexcloud.net/index.html](https://picaz-form-builder.website.yandexcloud.net/index.html)
|
|
232
|
+
|
|
233
|
+
Features a 3-column interface:
|
|
234
|
+
|
|
235
|
+
- **Schema Editor**: Edit JSON schema with validation
|
|
236
|
+
- **Live Preview**: See form render in real-time
|
|
237
|
+
- **Data Output**: View/export form data as JSON
|
|
238
|
+
|
|
239
|
+
## Support & Contributing
|
|
88
240
|
|
|
89
|
-
|
|
241
|
+
- **GitHub**: [picazru/form-builder](https://github.com/picazru/form-builder)
|
|
242
|
+
- **NPM Package**: [@dmitryvim/form-builder](https://www.npmjs.com/package/@dmitryvim/form-builder)
|
|
243
|
+
- **CDN**: [picaz-form-builder.website.yandexcloud.net](https://picaz-form-builder.website.yandexcloud.net/)
|
|
244
|
+
- **Version**: 0.1.28
|
|
245
|
+
- **License**: MIT - see [LICENSE](LICENSE) file
|
|
90
246
|
|
|
91
|
-
|
|
247
|
+
Built with ❤️ for the web development community.
|
package/dist/demo.js
CHANGED
|
@@ -18,6 +18,11 @@ const EXAMPLE_SCHEMA = {
|
|
|
18
18
|
mime: ["image/png", "image/jpeg", "image/gif"],
|
|
19
19
|
},
|
|
20
20
|
maxSizeMB: 10,
|
|
21
|
+
actions: [
|
|
22
|
+
{ value: "cover1.retry", label: "А давай ещё разок" },
|
|
23
|
+
{ value: "cover1.enhance", label: "Улучшить качество" },
|
|
24
|
+
{ value: "cover1.crop", label: "Обрезать изображение" },
|
|
25
|
+
],
|
|
21
26
|
},
|
|
22
27
|
{
|
|
23
28
|
type: "files",
|
|
@@ -241,6 +246,39 @@ class InMemoryFileStorage {
|
|
|
241
246
|
// Initialize file storage
|
|
242
247
|
const fileStorage = new InMemoryFileStorage();
|
|
243
248
|
|
|
249
|
+
// Cache for action value -> label mapping for efficient lookup
|
|
250
|
+
let actionLabelMap = new Map();
|
|
251
|
+
|
|
252
|
+
// Build action value -> label mapping from schema for efficient lookup
|
|
253
|
+
function buildActionLabelMap(schema) {
|
|
254
|
+
const map = new Map();
|
|
255
|
+
|
|
256
|
+
function processElements(elements) {
|
|
257
|
+
if (!Array.isArray(elements)) return;
|
|
258
|
+
|
|
259
|
+
for (const element of elements) {
|
|
260
|
+
if (element.actions && Array.isArray(element.actions)) {
|
|
261
|
+
for (const action of element.actions) {
|
|
262
|
+
if (action.value && action.label) {
|
|
263
|
+
map.set(action.value, action.label);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Process nested group elements
|
|
269
|
+
if (element.elements && Array.isArray(element.elements)) {
|
|
270
|
+
processElements(element.elements);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (schema && schema.elements) {
|
|
276
|
+
processElements(schema.elements);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return map;
|
|
280
|
+
}
|
|
281
|
+
|
|
244
282
|
// DOM element references
|
|
245
283
|
const el = {
|
|
246
284
|
schemaInput: document.getElementById("schemaInput"),
|
|
@@ -322,7 +360,24 @@ function setupFormBuilder() {
|
|
|
322
360
|
return thumbnailUrl;
|
|
323
361
|
});
|
|
324
362
|
|
|
325
|
-
|
|
363
|
+
// Action handler - display message when action button is clicked
|
|
364
|
+
FormBuilder.setActionHandler((value) => {
|
|
365
|
+
// Use cached action map for O(1) lookup instead of re-parsing schema
|
|
366
|
+
const actionLabel = actionLabelMap.get(value) || value; // fallback to value
|
|
367
|
+
|
|
368
|
+
console.log("Action clicked:", { label: actionLabel, value });
|
|
369
|
+
|
|
370
|
+
// Show message to user (compatible with all environments)
|
|
371
|
+
if (typeof window !== "undefined" && window.alert) {
|
|
372
|
+
window.alert(`${actionLabel} clicked: ${value}`);
|
|
373
|
+
} else {
|
|
374
|
+
console.log(`Demo action: ${actionLabel} clicked: ${value}`);
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
console.log(
|
|
379
|
+
"FormBuilder configured with in-memory file handlers and action handler",
|
|
380
|
+
);
|
|
326
381
|
}
|
|
327
382
|
|
|
328
383
|
// Schema management functions
|
|
@@ -342,6 +397,9 @@ function applyCurrentSchema() {
|
|
|
342
397
|
return false;
|
|
343
398
|
}
|
|
344
399
|
|
|
400
|
+
// Build action value -> label map for efficient lookup
|
|
401
|
+
actionLabelMap = buildActionLabelMap(schema);
|
|
402
|
+
|
|
345
403
|
// Set mode based on toggle
|
|
346
404
|
const isReadOnly = el.readOnlyToggle.checked;
|
|
347
405
|
FormBuilder.setMode(isReadOnly ? "readonly" : "edit");
|
package/dist/form-builder.js
CHANGED
|
@@ -11,6 +11,8 @@ const state = {
|
|
|
11
11
|
downloadFile: null,
|
|
12
12
|
getThumbnail: null,
|
|
13
13
|
getDownloadUrl: null,
|
|
14
|
+
// Action handler
|
|
15
|
+
actionHandler: null,
|
|
14
16
|
// Default implementations
|
|
15
17
|
enableFilePreview: true,
|
|
16
18
|
maxPreviewSize: "200px",
|
|
@@ -242,6 +244,43 @@ function renderElement(element, ctx) {
|
|
|
242
244
|
}
|
|
243
245
|
}
|
|
244
246
|
|
|
247
|
+
// Add action buttons in readonly mode
|
|
248
|
+
if (
|
|
249
|
+
state.config.readonly &&
|
|
250
|
+
element.actions &&
|
|
251
|
+
Array.isArray(element.actions) &&
|
|
252
|
+
element.actions.length > 0
|
|
253
|
+
) {
|
|
254
|
+
const actionsContainer = document.createElement("div");
|
|
255
|
+
actionsContainer.className = "mt-3 flex flex-wrap gap-2";
|
|
256
|
+
|
|
257
|
+
element.actions.forEach((action) => {
|
|
258
|
+
if (action.value && action.label) {
|
|
259
|
+
const actionBtn = document.createElement("button");
|
|
260
|
+
actionBtn.type = "button";
|
|
261
|
+
actionBtn.className =
|
|
262
|
+
"px-3 py-1.5 text-sm bg-blue-50 border border-blue-200 text-blue-700 rounded-lg hover:bg-blue-100 transition-colors";
|
|
263
|
+
actionBtn.textContent = action.label;
|
|
264
|
+
|
|
265
|
+
actionBtn.addEventListener("click", (e) => {
|
|
266
|
+
e.preventDefault();
|
|
267
|
+
e.stopPropagation();
|
|
268
|
+
|
|
269
|
+
if (
|
|
270
|
+
state.config.actionHandler &&
|
|
271
|
+
typeof state.config.actionHandler === "function"
|
|
272
|
+
) {
|
|
273
|
+
state.config.actionHandler(action.value);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
actionsContainer.appendChild(actionBtn);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
wrapper.appendChild(actionsContainer);
|
|
282
|
+
}
|
|
283
|
+
|
|
245
284
|
return wrapper;
|
|
246
285
|
}
|
|
247
286
|
|
|
@@ -1817,6 +1856,10 @@ function setThumbnailHandler(thumbnailFn) {
|
|
|
1817
1856
|
state.config.getThumbnail = thumbnailFn;
|
|
1818
1857
|
}
|
|
1819
1858
|
|
|
1859
|
+
function setActionHandler(actionFn) {
|
|
1860
|
+
state.config.actionHandler = actionFn;
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1820
1863
|
function setMode(mode) {
|
|
1821
1864
|
state.config.readonly = mode === "readonly";
|
|
1822
1865
|
}
|
|
@@ -1879,6 +1922,7 @@ const formBuilderAPI = {
|
|
|
1879
1922
|
setUploadHandler,
|
|
1880
1923
|
setDownloadHandler,
|
|
1881
1924
|
setThumbnailHandler,
|
|
1925
|
+
setActionHandler,
|
|
1882
1926
|
setMode,
|
|
1883
1927
|
setLocale,
|
|
1884
1928
|
getFormData,
|
|
@@ -1903,6 +1947,7 @@ export {
|
|
|
1903
1947
|
setUploadHandler,
|
|
1904
1948
|
setDownloadHandler,
|
|
1905
1949
|
setThumbnailHandler,
|
|
1950
|
+
setActionHandler,
|
|
1906
1951
|
setMode,
|
|
1907
1952
|
setLocale,
|
|
1908
1953
|
getFormData,
|