@dmitryvim/form-builder 0.1.34 → 0.1.35

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/elements.js CHANGED
@@ -17,7 +17,7 @@ function initializeFormBuilder() {
17
17
  name: file.name,
18
18
  type: file.type,
19
19
  size: file.size,
20
- uploadDate: new Date().toISOString(),
20
+ uploadDate: new Date().toISOString()
21
21
  });
22
22
 
23
23
  return resourceId;
@@ -27,7 +27,7 @@ function initializeFormBuilder() {
27
27
  FormBuilder.setDownloadHandler((resourceId, fileName) => {
28
28
  const fileData = fileStorage.get(resourceId);
29
29
  if (fileData && fileData.url) {
30
- const a = document.createElement("a");
30
+ const a = document.createElement('a');
31
31
  a.href = fileData.url;
32
32
  a.download = fileName || fileData.name;
33
33
  document.body.appendChild(a);
@@ -53,297 +53,266 @@ const elementSchemas = {
53
53
  text: {
54
54
  schema: {
55
55
  version: "0.3",
56
- elements: [
57
- {
58
- type: "text",
59
- key: "username",
60
- label: "Username",
61
- placeholder: "Enter your username",
62
- required: true,
63
- minLength: 3,
64
- maxLength: 20,
65
- pattern: "^[a-zA-Z0-9_]+$",
66
- description:
67
- "Username must be 3-20 characters, alphanumeric and underscore only",
68
- default: "",
69
- actions: [
70
- {
71
- value: "check_availability",
72
- label: "Check Availability",
73
- },
74
- ],
75
- },
76
- ],
56
+ elements: [{
57
+ type: "text",
58
+ key: "username",
59
+ label: "Username",
60
+ placeholder: "Enter your username",
61
+ required: true,
62
+ minLength: 3,
63
+ maxLength: 20,
64
+ pattern: "^[a-zA-Z0-9_]+$",
65
+ description: "Username must be 3-20 characters, alphanumeric and underscore only",
66
+ default: "",
67
+ actions: [{
68
+ value: "check_availability",
69
+ label: "Check Availability"
70
+ }]
71
+ }]
77
72
  },
78
- prefill: { username: "john_doe" },
73
+ prefill: { username: "john_doe" }
79
74
  },
80
75
 
81
- "text-multiple": {
76
+ 'text-multiple': {
82
77
  schema: {
83
78
  version: "0.3",
84
- elements: [
85
- {
86
- type: "text",
87
- key: "keywords",
88
- label: "Keywords",
89
- placeholder: "Enter keyword",
90
- required: true,
91
- multiple: true,
92
- minCount: 2,
93
- maxCount: 5,
94
- minLength: 2,
95
- maxLength: 30,
96
- description:
97
- "Add multiple keywords, minimum 2 required, maximum 5 allowed",
98
- default: "example",
99
- },
100
- ],
79
+ elements: [{
80
+ type: "text",
81
+ key: "keywords",
82
+ label: "Keywords",
83
+ placeholder: "Enter keyword",
84
+ required: true,
85
+ multiple: true,
86
+ minCount: 2,
87
+ maxCount: 5,
88
+ minLength: 2,
89
+ maxLength: 30,
90
+ description: "Add multiple keywords, minimum 2 required, maximum 5 allowed",
91
+ default: "example"
92
+ }]
101
93
  },
102
- prefill: { keywords: ["javascript", "react", "nodejs"] },
94
+ prefill: { keywords: ["javascript", "react", "nodejs"] }
103
95
  },
104
96
 
105
97
  textarea: {
106
98
  schema: {
107
99
  version: "0.3",
108
- elements: [
109
- {
110
- type: "textarea",
111
- key: "description",
112
- label: "Description",
113
- placeholder: "Enter detailed description...",
114
- rows: 6,
115
- required: false,
116
- minLength: 10,
117
- maxLength: 500,
118
- description: "Provide a detailed description of your project",
119
- },
120
- ],
121
- },
122
- prefill: {
123
- description:
124
- "This is a sample project description that demonstrates the textarea field with multiple lines of text.",
100
+ elements: [{
101
+ type: "textarea",
102
+ key: "description",
103
+ label: "Description",
104
+ placeholder: "Enter detailed description...",
105
+ rows: 6,
106
+ required: false,
107
+ minLength: 10,
108
+ maxLength: 500,
109
+ description: "Provide a detailed description of your project"
110
+ }]
125
111
  },
112
+ prefill: { description: "This is a sample project description that demonstrates the textarea field with multiple lines of text." }
126
113
  },
127
114
 
128
- "textarea-multiple": {
115
+ 'textarea-multiple': {
129
116
  schema: {
130
117
  version: "0.3",
131
- elements: [
132
- {
133
- type: "textarea",
134
- key: "features",
135
- label: "Product Features",
136
- placeholder: "Describe a feature...",
137
- rows: 4,
138
- required: true,
139
- multiple: true,
140
- minCount: 2,
141
- maxCount: 4,
142
- minLength: 20,
143
- maxLength: 200,
144
- description: "List key product features, minimum 2 required",
145
- },
146
- ],
118
+ elements: [{
119
+ type: "textarea",
120
+ key: "features",
121
+ label: "Product Features",
122
+ placeholder: "Describe a feature...",
123
+ rows: 4,
124
+ required: true,
125
+ multiple: true,
126
+ minCount: 2,
127
+ maxCount: 4,
128
+ minLength: 20,
129
+ maxLength: 200,
130
+ description: "List key product features, minimum 2 required"
131
+ }]
147
132
  },
148
133
  prefill: {
149
134
  features: [
150
135
  "High-quality materials ensure durability and long-lasting performance",
151
- "User-friendly interface makes it easy to operate for all skill levels",
152
- ],
153
- },
136
+ "User-friendly interface makes it easy to operate for all skill levels"
137
+ ]
138
+ }
154
139
  },
155
140
 
156
141
  number: {
157
142
  schema: {
158
143
  version: "0.3",
159
- elements: [
160
- {
161
- type: "number",
162
- key: "price",
163
- label: "Price (USD)",
164
- placeholder: "0.00",
165
- required: true,
166
- min: 0,
167
- max: 10000,
168
- step: 0.01,
169
- default: 99.99,
170
- description: "Product price in US dollars",
171
- },
172
- ],
144
+ elements: [{
145
+ type: "number",
146
+ key: "price",
147
+ label: "Price (USD)",
148
+ placeholder: "0.00",
149
+ required: true,
150
+ min: 0,
151
+ max: 10000,
152
+ step: 0.01,
153
+ default: 99.99,
154
+ description: "Product price in US dollars"
155
+ }]
173
156
  },
174
- prefill: { price: 149.99 },
157
+ prefill: { price: 149.99 }
175
158
  },
176
159
 
177
- "number-multiple": {
160
+ 'number-multiple': {
178
161
  schema: {
179
162
  version: "0.3",
180
- elements: [
181
- {
182
- type: "number",
183
- key: "dimensions",
184
- label: "Dimensions (cm)",
185
- placeholder: "0.0",
186
- required: true,
187
- multiple: true,
188
- minCount: 2,
189
- maxCount: 3,
190
- min: 0.1,
191
- max: 500,
192
- step: 0.1,
193
- decimals: 1,
194
- default: 10.0,
195
- description:
196
- "Enter product dimensions: width, height, and optionally depth",
197
- },
198
- ],
163
+ elements: [{
164
+ type: "number",
165
+ key: "dimensions",
166
+ label: "Dimensions (cm)",
167
+ placeholder: "0.0",
168
+ required: true,
169
+ multiple: true,
170
+ minCount: 2,
171
+ maxCount: 3,
172
+ min: 0.1,
173
+ max: 500,
174
+ step: 0.1,
175
+ decimals: 1,
176
+ default: 10.0,
177
+ description: "Enter product dimensions: width, height, and optionally depth"
178
+ }]
199
179
  },
200
- prefill: { dimensions: [25.5, 30.0, 15.2] },
180
+ prefill: { dimensions: [25.5, 30.0, 15.2] }
201
181
  },
202
182
 
203
183
  select: {
204
184
  schema: {
205
185
  version: "0.3",
206
- elements: [
207
- {
208
- type: "select",
209
- key: "category",
210
- label: "Product Category",
211
- required: true,
212
- options: [
213
- { value: "electronics", label: "Electronics" },
214
- { value: "clothing", label: "Clothing & Apparel" },
215
- { value: "books", label: "Books & Media" },
216
- { value: "home", label: "Home & Garden" },
217
- { value: "sports", label: "Sports & Recreation" },
218
- ],
219
- default: "electronics",
220
- description: "Select the primary category for your product",
221
- },
222
- ],
186
+ elements: [{
187
+ type: "select",
188
+ key: "category",
189
+ label: "Product Category",
190
+ required: true,
191
+ options: [
192
+ {value: "electronics", label: "Electronics"},
193
+ {value: "clothing", label: "Clothing & Apparel"},
194
+ {value: "books", label: "Books & Media"},
195
+ {value: "home", label: "Home & Garden"},
196
+ {value: "sports", label: "Sports & Recreation"}
197
+ ],
198
+ default: "electronics",
199
+ description: "Select the primary category for your product"
200
+ }]
223
201
  },
224
- prefill: { category: "books" },
202
+ prefill: { category: "books" }
225
203
  },
226
204
 
227
- "select-multiple": {
205
+ 'select-multiple': {
228
206
  schema: {
229
207
  version: "0.3",
230
- elements: [
231
- {
232
- type: "select",
233
- key: "tags",
234
- label: "Product Tags",
235
- required: true,
236
- multiple: true,
237
- minCount: 1,
238
- maxCount: 3,
239
- options: [
240
- { value: "new", label: "New" },
241
- { value: "popular", label: "Popular" },
242
- { value: "sale", label: "On Sale" },
243
- { value: "featured", label: "Featured" },
244
- { value: "limited", label: "Limited Edition" },
245
- { value: "bestseller", label: "Bestseller" },
246
- ],
247
- default: "new",
248
- description:
249
- "Select relevant tags for the product, maximum 3 allowed",
250
- },
251
- ],
208
+ elements: [{
209
+ type: "select",
210
+ key: "tags",
211
+ label: "Product Tags",
212
+ required: true,
213
+ multiple: true,
214
+ minCount: 1,
215
+ maxCount: 3,
216
+ options: [
217
+ {value: "new", label: "New"},
218
+ {value: "popular", label: "Popular"},
219
+ {value: "sale", label: "On Sale"},
220
+ {value: "featured", label: "Featured"},
221
+ {value: "limited", label: "Limited Edition"},
222
+ {value: "bestseller", label: "Bestseller"}
223
+ ],
224
+ default: "new",
225
+ description: "Select relevant tags for the product, maximum 3 allowed"
226
+ }]
252
227
  },
253
- prefill: { tags: ["popular", "featured"] },
228
+ prefill: { tags: ["popular", "featured"] }
254
229
  },
255
230
 
256
231
  file: {
257
232
  schema: {
258
233
  version: "0.3",
259
- elements: [
260
- {
261
- type: "file",
262
- key: "avatar",
263
- label: "Profile Picture",
264
- required: false,
265
- accept: {
266
- extensions: ["jpg", "jpeg", "png", "gif", "webp"],
267
- maxSize: 5242880,
268
- },
269
- description: "Upload your profile picture (max 5MB, images only)",
234
+ elements: [{
235
+ type: "file",
236
+ key: "avatar",
237
+ label: "Profile Picture",
238
+ required: false,
239
+ accept: {
240
+ extensions: ["jpg", "jpeg", "png", "gif", "webp"],
241
+ maxSize: 5242880
270
242
  },
271
- ],
243
+ description: "Upload your profile picture (max 5MB, images only)"
244
+ }]
272
245
  },
273
- prefill: { avatar: null },
246
+ prefill: { avatar: null }
274
247
  },
275
248
 
276
249
  files: {
277
250
  schema: {
278
251
  version: "0.3",
279
- elements: [
280
- {
281
- type: "files",
282
- key: "attachments",
283
- label: "Project Attachments",
284
- required: false,
285
- min: 1,
286
- max: 10,
287
- accept: {
288
- extensions: ["jpg", "png", "pdf", "doc", "docx", "mp4"],
289
- maxSize: 10485760,
290
- },
291
- description: "Upload project files (max 10MB each, 1-10 files)",
252
+ elements: [{
253
+ type: "files",
254
+ key: "attachments",
255
+ label: "Project Attachments",
256
+ required: false,
257
+ min: 1,
258
+ max: 10,
259
+ accept: {
260
+ extensions: ["jpg", "png", "pdf", "doc", "docx", "mp4"],
261
+ maxSize: 10485760
292
262
  },
293
- ],
263
+ description: "Upload project files (max 10MB each, 1-10 files)"
264
+ }]
294
265
  },
295
- prefill: { attachments: [] },
266
+ prefill: { attachments: [] }
296
267
  },
297
268
 
298
269
  group: {
299
270
  schema: {
300
271
  version: "0.3",
301
- elements: [
302
- {
303
- type: "group",
304
- key: "contacts",
305
- label: "Contact Information",
306
- repeat: {
307
- min: 1,
308
- max: 5,
309
- },
310
- elements: [
311
- {
312
- type: "text",
313
- key: "name",
314
- label: "Contact Name",
315
- required: true,
316
- },
317
- {
318
- type: "text",
319
- key: "email",
320
- label: "Email Address",
321
- required: true,
322
- pattern: "^[^@]+@[^@]+\\.[^@]+$",
323
- },
324
- {
325
- type: "select",
326
- key: "role",
327
- label: "Role",
328
- options: [
329
- { value: "primary", label: "Primary Contact" },
330
- { value: "secondary", label: "Secondary Contact" },
331
- { value: "technical", label: "Technical Contact" },
332
- ],
333
- default: "primary",
334
- },
335
- ],
336
- description: "Add contact information for your project",
272
+ elements: [{
273
+ type: "group",
274
+ key: "contacts",
275
+ label: "Contact Information",
276
+ repeat: {
277
+ min: 1,
278
+ max: 5
337
279
  },
338
- ],
280
+ elements: [
281
+ {
282
+ type: "text",
283
+ key: "name",
284
+ label: "Contact Name",
285
+ required: true
286
+ },
287
+ {
288
+ type: "text",
289
+ key: "email",
290
+ label: "Email Address",
291
+ required: true,
292
+ pattern: "^[^@]+@[^@]+\\.[^@]+$"
293
+ },
294
+ {
295
+ type: "select",
296
+ key: "role",
297
+ label: "Role",
298
+ options: [
299
+ {value: "primary", label: "Primary Contact"},
300
+ {value: "secondary", label: "Secondary Contact"},
301
+ {value: "technical", label: "Technical Contact"}
302
+ ],
303
+ default: "primary"
304
+ }
305
+ ],
306
+ description: "Add contact information for your project"
307
+ }]
339
308
  },
340
309
  prefill: {
341
310
  contacts: [
342
311
  { name: "John Smith", email: "john@company.com", role: "primary" },
343
- { name: "Jane Doe", email: "jane@company.com", role: "technical" },
344
- ],
345
- },
346
- },
312
+ { name: "Jane Doe", email: "jane@company.com", role: "technical" }
313
+ ]
314
+ }
315
+ }
347
316
  };
348
317
 
349
318
  // Update preview for an element
@@ -352,15 +321,13 @@ function updatePreview(elementType) {
352
321
  if (!elementData) return;
353
322
 
354
323
  const previewContainer = document.getElementById(`${elementType}-preview`);
355
- const readonlyToggle = document.querySelector(
356
- `input[data-element="${elementType}"]`,
357
- );
324
+ const readonlyToggle = document.querySelector(`input[data-element="${elementType}"]`);
358
325
 
359
326
  if (!previewContainer) return;
360
327
 
361
328
  // Set form root and mode
362
329
  FormBuilder.setFormRoot(previewContainer);
363
- FormBuilder.setMode(readonlyToggle?.checked ? "readonly" : "edit");
330
+ FormBuilder.setMode(readonlyToggle?.checked ? 'readonly' : 'edit');
364
331
 
365
332
  // Render the form
366
333
  FormBuilder.renderForm(elementData.schema, elementData.prefill);
@@ -386,7 +353,7 @@ function applySchemaChanges(elementType) {
386
353
  elementSchemas[elementType].schema.elements[0] = elementSchema;
387
354
  updatePreview(elementType);
388
355
  } catch (error) {
389
- console.error("Invalid JSON in schema:", error);
356
+ console.error('Invalid JSON in schema:', error);
390
357
  // Reset to original if invalid
391
358
  updateSchemaTextarea(elementType);
392
359
  }
@@ -394,62 +361,59 @@ function applySchemaChanges(elementType) {
394
361
 
395
362
  // Initialize navigation
396
363
  function initializeNavigation() {
397
- const navItems = document.querySelectorAll(".nav-item");
364
+ const navItems = document.querySelectorAll('.nav-item');
398
365
 
399
366
  // Set initial active state
400
367
  if (navItems.length > 0) {
401
- navItems[0].classList.add("active");
368
+ navItems[0].classList.add('active');
402
369
  }
403
370
 
404
- navItems.forEach((item) => {
405
- item.addEventListener("click", (e) => {
371
+ navItems.forEach(item => {
372
+ item.addEventListener('click', (e) => {
406
373
  e.preventDefault();
407
374
 
408
375
  // Remove active class from all items
409
- navItems.forEach((nav) => nav.classList.remove("active"));
376
+ navItems.forEach(nav => nav.classList.remove('active'));
410
377
 
411
378
  // Add active class to clicked item
412
- item.classList.add("active");
379
+ item.classList.add('active');
413
380
 
414
381
  // Scroll to element
415
- const elementId = item.getAttribute("href").substring(1);
382
+ const elementId = item.getAttribute('href').substring(1);
416
383
  const element = document.getElementById(elementId);
417
384
  if (element) {
418
- element.scrollIntoView({ behavior: "smooth", block: "start" });
385
+ element.scrollIntoView({ behavior: 'smooth', block: 'start' });
419
386
  }
420
387
  });
421
388
  });
422
389
 
423
390
  // Handle scroll-based active states
424
- const sections = document.querySelectorAll(".element-section");
425
- const observer = new IntersectionObserver(
426
- (entries) => {
427
- entries.forEach((entry) => {
428
- if (entry.isIntersecting) {
429
- const id = entry.target.id;
430
- navItems.forEach((nav) => {
431
- nav.classList.remove("active");
432
- if (nav.getAttribute("href") === `#${id}`) {
433
- nav.classList.add("active");
434
- }
435
- });
436
- }
437
- });
438
- },
439
- {
440
- rootMargin: "-20% 0% -20% 0%",
441
- threshold: 0.1,
442
- },
443
- );
391
+ const sections = document.querySelectorAll('.element-section');
392
+ const observer = new IntersectionObserver((entries) => {
393
+ entries.forEach(entry => {
394
+ if (entry.isIntersecting) {
395
+ const id = entry.target.id;
396
+ navItems.forEach(nav => {
397
+ nav.classList.remove('active');
398
+ if (nav.getAttribute('href') === `#${id}`) {
399
+ nav.classList.add('active');
400
+ }
401
+ });
402
+ }
403
+ });
404
+ }, {
405
+ rootMargin: '-20% 0% -20% 0%',
406
+ threshold: 0.1
407
+ });
444
408
 
445
- sections.forEach((section) => {
409
+ sections.forEach(section => {
446
410
  observer.observe(section);
447
411
  });
448
412
  }
449
413
 
450
414
  // Initialize all elements
451
415
  function initializeElements() {
452
- Object.keys(elementSchemas).forEach((elementType) => {
416
+ Object.keys(elementSchemas).forEach(elementType => {
453
417
  // Update textarea with schema
454
418
  updateSchemaTextarea(elementType);
455
419
 
@@ -457,7 +421,7 @@ function initializeElements() {
457
421
  const textarea = document.getElementById(`${elementType}-schema`);
458
422
  if (textarea) {
459
423
  let debounceTimer;
460
- textarea.addEventListener("input", () => {
424
+ textarea.addEventListener('input', () => {
461
425
  clearTimeout(debounceTimer);
462
426
  debounceTimer = setTimeout(() => {
463
427
  applySchemaChanges(elementType);
@@ -466,11 +430,9 @@ function initializeElements() {
466
430
  }
467
431
 
468
432
  // Setup readonly toggle
469
- const readonlyToggle = document.querySelector(
470
- `input[data-element="${elementType}"]`,
471
- );
433
+ const readonlyToggle = document.querySelector(`input[data-element="${elementType}"]`);
472
434
  if (readonlyToggle) {
473
- readonlyToggle.addEventListener("change", () => {
435
+ readonlyToggle.addEventListener('change', () => {
474
436
  updatePreview(elementType);
475
437
  });
476
438
  }
@@ -481,8 +443,8 @@ function initializeElements() {
481
443
  }
482
444
 
483
445
  // Initialize everything when DOM is loaded
484
- document.addEventListener("DOMContentLoaded", () => {
446
+ document.addEventListener('DOMContentLoaded', () => {
485
447
  initializeFormBuilder();
486
448
  initializeNavigation();
487
449
  initializeElements();
488
- });
450
+ });