@dmitryvim/form-builder 0.1.31 → 0.1.33

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.
@@ -0,0 +1,450 @@
1
+ // Elements Documentation Page Logic
2
+
3
+ // Initialize FormBuilder with demo handlers
4
+ function initializeFormBuilder() {
5
+ // Simple in-memory file storage for demo
6
+ const fileStorage = new Map();
7
+ let fileCounter = 0;
8
+
9
+ // Mock upload handler
10
+ FormBuilder.setUploadHandler(async (file) => {
11
+ const resourceId = `file-${++fileCounter}`;
12
+ const fileUrl = URL.createObjectURL(file);
13
+
14
+ fileStorage.set(resourceId, {
15
+ file: file,
16
+ url: fileUrl,
17
+ name: file.name,
18
+ type: file.type,
19
+ size: file.size,
20
+ uploadDate: new Date().toISOString()
21
+ });
22
+
23
+ return resourceId;
24
+ });
25
+
26
+ // Mock download handler
27
+ FormBuilder.setDownloadHandler((resourceId, fileName) => {
28
+ const fileData = fileStorage.get(resourceId);
29
+ if (fileData && fileData.url) {
30
+ const a = document.createElement('a');
31
+ a.href = fileData.url;
32
+ a.download = fileName || fileData.name;
33
+ document.body.appendChild(a);
34
+ a.click();
35
+ document.body.removeChild(a);
36
+ }
37
+ });
38
+
39
+ // Mock thumbnail handler
40
+ FormBuilder.setThumbnailHandler((resourceId) => {
41
+ const fileData = fileStorage.get(resourceId);
42
+ return fileData ? fileData.url : null;
43
+ });
44
+
45
+ // Mock action handler
46
+ FormBuilder.setActionHandler((actionValue) => {
47
+ alert(`Action triggered: ${actionValue}`);
48
+ });
49
+ }
50
+
51
+ // Element data and demo schemas
52
+ const elementSchemas = {
53
+ text: {
54
+ schema: {
55
+ version: "0.3",
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
+ }]
72
+ },
73
+ prefill: { username: "john_doe" }
74
+ },
75
+
76
+ 'text-multiple': {
77
+ schema: {
78
+ version: "0.3",
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
+ }]
93
+ },
94
+ prefill: { keywords: ["javascript", "react", "nodejs"] }
95
+ },
96
+
97
+ textarea: {
98
+ schema: {
99
+ version: "0.3",
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
+ }]
111
+ },
112
+ prefill: { description: "This is a sample project description that demonstrates the textarea field with multiple lines of text." }
113
+ },
114
+
115
+ 'textarea-multiple': {
116
+ schema: {
117
+ version: "0.3",
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
+ }]
132
+ },
133
+ prefill: {
134
+ features: [
135
+ "High-quality materials ensure durability and long-lasting performance",
136
+ "User-friendly interface makes it easy to operate for all skill levels"
137
+ ]
138
+ }
139
+ },
140
+
141
+ number: {
142
+ schema: {
143
+ version: "0.3",
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
+ }]
156
+ },
157
+ prefill: { price: 149.99 }
158
+ },
159
+
160
+ 'number-multiple': {
161
+ schema: {
162
+ version: "0.3",
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
+ }]
179
+ },
180
+ prefill: { dimensions: [25.5, 30.0, 15.2] }
181
+ },
182
+
183
+ select: {
184
+ schema: {
185
+ version: "0.3",
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
+ }]
201
+ },
202
+ prefill: { category: "books" }
203
+ },
204
+
205
+ 'select-multiple': {
206
+ schema: {
207
+ version: "0.3",
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
+ }]
227
+ },
228
+ prefill: { tags: ["popular", "featured"] }
229
+ },
230
+
231
+ file: {
232
+ schema: {
233
+ version: "0.3",
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
242
+ },
243
+ description: "Upload your profile picture (max 5MB, images only)"
244
+ }]
245
+ },
246
+ prefill: { avatar: null }
247
+ },
248
+
249
+ files: {
250
+ schema: {
251
+ version: "0.3",
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
262
+ },
263
+ description: "Upload project files (max 10MB each, 1-10 files)"
264
+ }]
265
+ },
266
+ prefill: { attachments: [] }
267
+ },
268
+
269
+ group: {
270
+ schema: {
271
+ version: "0.3",
272
+ elements: [{
273
+ type: "group",
274
+ key: "contacts",
275
+ label: "Contact Information",
276
+ repeat: {
277
+ min: 1,
278
+ max: 5
279
+ },
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
+ }]
308
+ },
309
+ prefill: {
310
+ contacts: [
311
+ { name: "John Smith", email: "john@company.com", role: "primary" },
312
+ { name: "Jane Doe", email: "jane@company.com", role: "technical" }
313
+ ]
314
+ }
315
+ }
316
+ };
317
+
318
+ // Update preview for an element
319
+ function updatePreview(elementType) {
320
+ const elementData = elementSchemas[elementType];
321
+ if (!elementData) return;
322
+
323
+ const previewContainer = document.getElementById(`${elementType}-preview`);
324
+ const readonlyToggle = document.querySelector(`input[data-element="${elementType}"]`);
325
+
326
+ if (!previewContainer) return;
327
+
328
+ // Set form root and mode
329
+ FormBuilder.setFormRoot(previewContainer);
330
+ FormBuilder.setMode(readonlyToggle?.checked ? 'readonly' : 'edit');
331
+
332
+ // Render the form
333
+ FormBuilder.renderForm(elementData.schema, elementData.prefill);
334
+ }
335
+
336
+ // Update schema textarea from element data
337
+ function updateSchemaTextarea(elementType) {
338
+ const elementData = elementSchemas[elementType];
339
+ const textarea = document.getElementById(`${elementType}-schema`);
340
+
341
+ if (elementData && textarea) {
342
+ textarea.value = JSON.stringify(elementData.schema.elements[0], null, 2);
343
+ }
344
+ }
345
+
346
+ // Apply schema changes from textarea
347
+ function applySchemaChanges(elementType) {
348
+ const textarea = document.getElementById(`${elementType}-schema`);
349
+ if (!textarea) return;
350
+
351
+ try {
352
+ const elementSchema = JSON.parse(textarea.value);
353
+ elementSchemas[elementType].schema.elements[0] = elementSchema;
354
+ updatePreview(elementType);
355
+ } catch (error) {
356
+ console.error('Invalid JSON in schema:', error);
357
+ // Reset to original if invalid
358
+ updateSchemaTextarea(elementType);
359
+ }
360
+ }
361
+
362
+ // Initialize navigation
363
+ function initializeNavigation() {
364
+ const navItems = document.querySelectorAll('.nav-item');
365
+
366
+ // Set initial active state
367
+ if (navItems.length > 0) {
368
+ navItems[0].classList.add('active');
369
+ }
370
+
371
+ navItems.forEach(item => {
372
+ item.addEventListener('click', (e) => {
373
+ e.preventDefault();
374
+
375
+ // Remove active class from all items
376
+ navItems.forEach(nav => nav.classList.remove('active'));
377
+
378
+ // Add active class to clicked item
379
+ item.classList.add('active');
380
+
381
+ // Scroll to element
382
+ const elementId = item.getAttribute('href').substring(1);
383
+ const element = document.getElementById(elementId);
384
+ if (element) {
385
+ element.scrollIntoView({ behavior: 'smooth', block: 'start' });
386
+ }
387
+ });
388
+ });
389
+
390
+ // Handle scroll-based active states
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
+ });
408
+
409
+ sections.forEach(section => {
410
+ observer.observe(section);
411
+ });
412
+ }
413
+
414
+ // Initialize all elements
415
+ function initializeElements() {
416
+ Object.keys(elementSchemas).forEach(elementType => {
417
+ // Update textarea with schema
418
+ updateSchemaTextarea(elementType);
419
+
420
+ // Setup textarea change listener
421
+ const textarea = document.getElementById(`${elementType}-schema`);
422
+ if (textarea) {
423
+ let debounceTimer;
424
+ textarea.addEventListener('input', () => {
425
+ clearTimeout(debounceTimer);
426
+ debounceTimer = setTimeout(() => {
427
+ applySchemaChanges(elementType);
428
+ }, 1000); // 1 second delay
429
+ });
430
+ }
431
+
432
+ // Setup readonly toggle
433
+ const readonlyToggle = document.querySelector(`input[data-element="${elementType}"]`);
434
+ if (readonlyToggle) {
435
+ readonlyToggle.addEventListener('change', () => {
436
+ updatePreview(elementType);
437
+ });
438
+ }
439
+
440
+ // Initial preview render
441
+ updatePreview(elementType);
442
+ });
443
+ }
444
+
445
+ // Initialize everything when DOM is loaded
446
+ document.addEventListener('DOMContentLoaded', () => {
447
+ initializeFormBuilder();
448
+ initializeNavigation();
449
+ initializeElements();
450
+ });