@dmitryvim/form-builder 0.1.31 → 0.1.34

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,1151 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Form Builder - Element Types Documentation</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <!-- Development only - replace with local Tailwind build for production -->
8
+ <script>
9
+ if (
10
+ location.hostname === "localhost" ||
11
+ location.hostname === "127.0.0.1" ||
12
+ location.protocol === "file:"
13
+ ) {
14
+ // Development environment - load Tailwind CDN
15
+ const script = document.createElement("script");
16
+ script.src = "https://cdn.tailwindcss.com";
17
+ document.head.appendChild(script);
18
+ } else {
19
+ // Production environment - load local CSS (implement local build)
20
+ console.warn(
21
+ "Production mode: Please implement local Tailwind CSS build",
22
+ );
23
+ const link = document.createElement("link");
24
+ link.rel = "stylesheet";
25
+ link.href = "./tailwind.min.css"; // Local build file
26
+ document.head.appendChild(link);
27
+ }
28
+ </script>
29
+ <script>
30
+ tailwind.config = {
31
+ darkMode: "media",
32
+ theme: {
33
+ extend: {
34
+ fontFamily: {
35
+ mono: [
36
+ "ui-monospace",
37
+ "SFMono-Regular",
38
+ "Menlo",
39
+ "Monaco",
40
+ "Consolas",
41
+ '"Liberation Mono"',
42
+ '"Courier New"',
43
+ "monospace",
44
+ ],
45
+ },
46
+ },
47
+ },
48
+ };
49
+ </script>
50
+ <style>
51
+ /* Custom styles for form validation states */
52
+ .invalid {
53
+ @apply border-red-500 !important;
54
+ }
55
+ .field-hint {
56
+ @apply text-gray-500 text-xs mt-1;
57
+ }
58
+ .error-message {
59
+ @apply text-red-500 text-xs mt-1;
60
+ }
61
+ .file-preview-container {
62
+ @apply mb-3 p-3 border border-dashed border-gray-300 rounded-lg min-h-[60px] flex items-center justify-center bg-blue-50;
63
+ }
64
+ .dark .file-preview-container {
65
+ @apply bg-blue-900/20 border-gray-600;
66
+ }
67
+ .resource-pill {
68
+ @apply inline-flex items-center gap-1.5 bg-blue-50 border border-gray-300 rounded-full px-2.5 py-1 font-mono text-xs m-0.5;
69
+ }
70
+ .dark .resource-pill {
71
+ @apply bg-blue-900/20 border-gray-600;
72
+ }
73
+
74
+ /* Toggle switch styles */
75
+ .toggle-switch {
76
+ position: relative;
77
+ display: inline-block;
78
+ width: 60px;
79
+ height: 34px;
80
+ }
81
+
82
+ .toggle-switch input {
83
+ opacity: 0;
84
+ width: 0;
85
+ height: 0;
86
+ }
87
+
88
+ .toggle-slider {
89
+ position: absolute;
90
+ cursor: pointer;
91
+ top: 0;
92
+ left: 0;
93
+ right: 0;
94
+ bottom: 0;
95
+ background-color: #ccc;
96
+ transition: 0.4s;
97
+ border-radius: 34px;
98
+ }
99
+
100
+ .toggle-slider:before {
101
+ position: absolute;
102
+ content: "";
103
+ height: 26px;
104
+ width: 26px;
105
+ left: 4px;
106
+ bottom: 4px;
107
+ background-color: white;
108
+ transition: 0.4s;
109
+ border-radius: 50%;
110
+ }
111
+
112
+ input:checked + .toggle-slider {
113
+ background-color: #2563eb;
114
+ }
115
+
116
+ input:checked + .toggle-slider:before {
117
+ transform: translateX(26px);
118
+ }
119
+
120
+ /* Navigation styles */
121
+ .nav-item {
122
+ @apply block px-3 py-2 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900 transition-colors;
123
+ }
124
+ .nav-item.active {
125
+ @apply bg-blue-100 text-blue-900;
126
+ }
127
+ </style>
128
+ </head>
129
+ <body class="bg-gray-50">
130
+ <!-- Header -->
131
+ <div class="bg-white border-b border-gray-200 px-6 py-4">
132
+ <div class="flex items-center justify-between">
133
+ <div class="flex items-center space-x-3">
134
+ <div
135
+ class="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center"
136
+ >
137
+ <span class="text-white font-bold text-sm">FB</span>
138
+ </div>
139
+ <div>
140
+ <h1 class="text-lg font-bold text-gray-800">
141
+ Form Builder - Element Types
142
+ </h1>
143
+ <p class="text-xs text-gray-500">
144
+ Complete documentation for all form field types
145
+ </p>
146
+ </div>
147
+ </div>
148
+ <div class="flex items-center space-x-4">
149
+ <a
150
+ href="./index.html"
151
+ class="text-sm text-blue-600 hover:text-blue-800"
152
+ >← Back to Demo</a
153
+ >
154
+ </div>
155
+ </div>
156
+ </div>
157
+
158
+ <!-- Main Content -->
159
+ <div class="flex">
160
+ <!-- Left Navigation -->
161
+ <div class="w-64 bg-white border-r border-gray-200 min-h-screen">
162
+ <div class="p-4">
163
+ <h3 class="text-sm font-semibold text-gray-900 mb-3">
164
+ Element Types
165
+ </h3>
166
+ <nav class="space-y-1">
167
+ <a href="#text" class="nav-item" data-element="text">Text Input</a>
168
+ <a
169
+ href="#text-multiple"
170
+ class="nav-item"
171
+ data-element="text-multiple"
172
+ >Text Input (Multiple)</a
173
+ >
174
+ <a href="#textarea" class="nav-item" data-element="textarea"
175
+ >Textarea</a
176
+ >
177
+ <a
178
+ href="#textarea-multiple"
179
+ class="nav-item"
180
+ data-element="textarea-multiple"
181
+ >Textarea (Multiple)</a
182
+ >
183
+ <a href="#number" class="nav-item" data-element="number"
184
+ >Number Input</a
185
+ >
186
+ <a
187
+ href="#number-multiple"
188
+ class="nav-item"
189
+ data-element="number-multiple"
190
+ >Number Input (Multiple)</a
191
+ >
192
+ <a href="#select" class="nav-item" data-element="select"
193
+ >Select Dropdown</a
194
+ >
195
+ <a
196
+ href="#select-multiple"
197
+ class="nav-item"
198
+ data-element="select-multiple"
199
+ >Select Dropdown (Multiple)</a
200
+ >
201
+ <a href="#file" class="nav-item" data-element="file">File Upload</a>
202
+ <a href="#files" class="nav-item" data-element="files"
203
+ >Multiple Files (Legacy)</a
204
+ >
205
+ <a href="#container" class="nav-item" data-element="container"
206
+ >Container</a
207
+ >
208
+ <a href="#group" class="nav-item" data-element="group"
209
+ >Group (Legacy)</a
210
+ >
211
+ </nav>
212
+ </div>
213
+ </div>
214
+
215
+ <!-- Main Content Area -->
216
+ <div class="flex-1 p-6">
217
+ <!-- Text Element -->
218
+ <div id="text" class="element-section mb-16">
219
+ <h2 class="text-2xl font-bold text-gray-900 mb-4">Text Input</h2>
220
+ <p class="text-gray-700 mb-4">
221
+ Single line text input with pattern validation and length
222
+ constraints.
223
+ </p>
224
+ <div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
225
+ <p class="text-sm text-blue-800">
226
+ <strong>Hidden Fields:</strong> Set <code>hidden: true</code> to
227
+ hide the field from display while including its value in form
228
+ data. Hidden fields use their <code>default</code> value and are
229
+ not rendered in the form.
230
+ </p>
231
+ </div>
232
+
233
+ <div class="grid grid-cols-2 gap-6">
234
+ <!-- Schema Editor -->
235
+ <div>
236
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
237
+ Schema Definition
238
+ </h3>
239
+ <textarea
240
+ id="text-schema"
241
+ class="w-full h-80 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
242
+ spellcheck="false"
243
+ >
244
+ {
245
+ "type": "text",
246
+ "key": "username",
247
+ "label": "Username",
248
+ "placeholder": "Enter your username",
249
+ "required": true,
250
+ "minLength": 3,
251
+ "maxLength": 20,
252
+ "pattern": "^[a-zA-Z0-9_]+$",
253
+ "description": "Username must be 3-20 characters, alphanumeric and underscore only",
254
+ "default": "",
255
+ "actions": [
256
+ {
257
+ "value": "check_availability",
258
+ "label": "Check Availability"
259
+ }
260
+ ],
261
+ "hidden": false
262
+ }</textarea
263
+ >
264
+ </div>
265
+
266
+ <!-- Live Preview -->
267
+ <div>
268
+ <div class="flex items-center justify-between mb-3">
269
+ <h3 class="text-lg font-semibold text-gray-900">
270
+ Live Preview
271
+ </h3>
272
+ <div class="flex items-center space-x-3">
273
+ <span class="text-sm font-medium text-gray-700"
274
+ >Read Only</span
275
+ >
276
+ <label class="toggle-switch">
277
+ <input
278
+ type="checkbox"
279
+ class="readonly-toggle"
280
+ data-element="text"
281
+ />
282
+ <span class="toggle-slider"></span>
283
+ </label>
284
+ </div>
285
+ </div>
286
+ <div
287
+ id="text-preview"
288
+ class="border border-gray-300 rounded-lg p-4 bg-white min-h-80"
289
+ ></div>
290
+ </div>
291
+ </div>
292
+
293
+ <div class="mt-6">
294
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
295
+ Available Properties
296
+ </h3>
297
+ <div class="bg-gray-50 rounded-lg p-4">
298
+ <ul class="space-y-2 text-sm">
299
+ <li><strong>type:</strong> "text" (required)</li>
300
+ <li><strong>key:</strong> Field identifier (required)</li>
301
+ <li><strong>label:</strong> Display label for the field</li>
302
+ <li><strong>placeholder:</strong> Placeholder text</li>
303
+ <li>
304
+ <strong>required:</strong> Whether field is required (boolean)
305
+ </li>
306
+ <li>
307
+ <strong>minLength:</strong> Minimum character length (number)
308
+ </li>
309
+ <li>
310
+ <strong>maxLength:</strong> Maximum character length (number)
311
+ </li>
312
+ <li>
313
+ <strong>pattern:</strong> Regular expression for validation
314
+ </li>
315
+ <li><strong>default:</strong> Default value</li>
316
+ <li>
317
+ <strong>description:</strong> Field description (shows in
318
+ tooltip)
319
+ </li>
320
+ <li>
321
+ <strong>hint:</strong> Additional hint text (shows in tooltip)
322
+ </li>
323
+ <li>
324
+ <strong>actions:</strong> Array of action buttons for readonly
325
+ mode
326
+ </li>
327
+ <li>
328
+ <strong>hidden:</strong> Hide field from form display but
329
+ include in data (boolean)
330
+ </li>
331
+ </ul>
332
+ </div>
333
+ </div>
334
+ </div>
335
+
336
+ <!-- Textarea Element -->
337
+ <div id="textarea" class="element-section mb-16">
338
+ <h2 class="text-2xl font-bold text-gray-900 mb-4">Textarea</h2>
339
+ <p class="text-gray-700 mb-6">
340
+ Multi-line text input with configurable row height.
341
+ </p>
342
+
343
+ <div class="grid grid-cols-2 gap-6">
344
+ <div>
345
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
346
+ Schema Definition
347
+ </h3>
348
+ <textarea
349
+ id="textarea-schema"
350
+ class="w-full h-80 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
351
+ spellcheck="false"
352
+ >
353
+ {
354
+ "type": "textarea",
355
+ "key": "description",
356
+ "label": "Description",
357
+ "placeholder": "Enter detailed description...",
358
+ "rows": 6,
359
+ "required": false,
360
+ "minLength": 10,
361
+ "maxLength": 500,
362
+ "description": "Provide a detailed description of your project"
363
+ }</textarea
364
+ >
365
+ </div>
366
+
367
+ <div>
368
+ <div class="flex items-center justify-between mb-3">
369
+ <h3 class="text-lg font-semibold text-gray-900">
370
+ Live Preview
371
+ </h3>
372
+ <div class="flex items-center space-x-3">
373
+ <span class="text-sm font-medium text-gray-700"
374
+ >Read Only</span
375
+ >
376
+ <label class="toggle-switch">
377
+ <input
378
+ type="checkbox"
379
+ class="readonly-toggle"
380
+ data-element="textarea"
381
+ />
382
+ <span class="toggle-slider"></span>
383
+ </label>
384
+ </div>
385
+ </div>
386
+ <div
387
+ id="textarea-preview"
388
+ class="border border-gray-300 rounded-lg p-4 bg-white min-h-80"
389
+ ></div>
390
+ </div>
391
+ </div>
392
+
393
+ <div class="mt-6">
394
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
395
+ Available Properties
396
+ </h3>
397
+ <div class="bg-gray-50 rounded-lg p-4">
398
+ <ul class="space-y-2 text-sm">
399
+ <li><strong>type:</strong> "textarea" (required)</li>
400
+ <li><strong>key:</strong> Field identifier (required)</li>
401
+ <li><strong>label:</strong> Display label for the field</li>
402
+ <li><strong>placeholder:</strong> Placeholder text</li>
403
+ <li>
404
+ <strong>rows:</strong> Number of visible rows (default: 4)
405
+ </li>
406
+ <li>
407
+ <strong>required:</strong> Whether field is required (boolean)
408
+ </li>
409
+ <li>
410
+ <strong>minLength:</strong> Minimum character length (number)
411
+ </li>
412
+ <li>
413
+ <strong>maxLength:</strong> Maximum character length (number)
414
+ </li>
415
+ <li><strong>default:</strong> Default value</li>
416
+ <li>
417
+ <strong>description:</strong> Field description (shows in
418
+ tooltip)
419
+ </li>
420
+ <li>
421
+ <strong>hint:</strong> Additional hint text (shows in tooltip)
422
+ </li>
423
+ <li>
424
+ <strong>actions:</strong> Array of action buttons for readonly
425
+ mode
426
+ </li>
427
+ <li>
428
+ <strong>hidden:</strong> Hide field from form display but
429
+ include in data (boolean)
430
+ </li>
431
+ </ul>
432
+ </div>
433
+ </div>
434
+ </div>
435
+
436
+ <!-- Number Element -->
437
+ <div id="number" class="element-section mb-16">
438
+ <h2 class="text-2xl font-bold text-gray-900 mb-4">Number Input</h2>
439
+ <p class="text-gray-700 mb-6">
440
+ Numeric input with min/max/step validation.
441
+ </p>
442
+
443
+ <div class="grid grid-cols-2 gap-6">
444
+ <div>
445
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
446
+ Schema Definition
447
+ </h3>
448
+ <textarea
449
+ id="number-schema"
450
+ class="w-full h-80 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
451
+ spellcheck="false"
452
+ >
453
+ {
454
+ "type": "number",
455
+ "key": "price",
456
+ "label": "Price (USD)",
457
+ "placeholder": "0.00",
458
+ "required": true,
459
+ "min": 0,
460
+ "max": 10000,
461
+ "step": 0.01,
462
+ "default": 99.99,
463
+ "description": "Product price in US dollars"
464
+ }</textarea
465
+ >
466
+ </div>
467
+
468
+ <div>
469
+ <div class="flex items-center justify-between mb-3">
470
+ <h3 class="text-lg font-semibold text-gray-900">
471
+ Live Preview
472
+ </h3>
473
+ <div class="flex items-center space-x-3">
474
+ <span class="text-sm font-medium text-gray-700"
475
+ >Read Only</span
476
+ >
477
+ <label class="toggle-switch">
478
+ <input
479
+ type="checkbox"
480
+ class="readonly-toggle"
481
+ data-element="number"
482
+ />
483
+ <span class="toggle-slider"></span>
484
+ </label>
485
+ </div>
486
+ </div>
487
+ <div
488
+ id="number-preview"
489
+ class="border border-gray-300 rounded-lg p-4 bg-white min-h-80"
490
+ ></div>
491
+ </div>
492
+ </div>
493
+
494
+ <div class="mt-6">
495
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
496
+ Available Properties
497
+ </h3>
498
+ <div class="bg-gray-50 rounded-lg p-4">
499
+ <ul class="space-y-2 text-sm">
500
+ <li><strong>type:</strong> "number" (required)</li>
501
+ <li><strong>key:</strong> Field identifier (required)</li>
502
+ <li><strong>label:</strong> Display label for the field</li>
503
+ <li><strong>placeholder:</strong> Placeholder text</li>
504
+ <li>
505
+ <strong>required:</strong> Whether field is required (boolean)
506
+ </li>
507
+ <li><strong>min:</strong> Minimum value (number)</li>
508
+ <li><strong>max:</strong> Maximum value (number)</li>
509
+ <li>
510
+ <strong>step:</strong> Step increment for input controls
511
+ </li>
512
+ <li><strong>default:</strong> Default value</li>
513
+ <li>
514
+ <strong>description:</strong> Field description (shows in
515
+ tooltip)
516
+ </li>
517
+ <li>
518
+ <strong>hint:</strong> Additional hint text (shows in tooltip)
519
+ </li>
520
+ <li>
521
+ <strong>actions:</strong> Array of action buttons for readonly
522
+ mode
523
+ </li>
524
+ <li>
525
+ <strong>hidden:</strong> Hide field from form display but
526
+ include in data (boolean)
527
+ </li>
528
+ </ul>
529
+ </div>
530
+ </div>
531
+ </div>
532
+
533
+ <!-- Select Element -->
534
+ <div id="select" class="element-section mb-16">
535
+ <h2 class="text-2xl font-bold text-gray-900 mb-4">Select Dropdown</h2>
536
+ <p class="text-gray-700 mb-6">
537
+ Dropdown selection with predefined options.
538
+ </p>
539
+
540
+ <div class="grid grid-cols-2 gap-6">
541
+ <div>
542
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
543
+ Schema Definition
544
+ </h3>
545
+ <textarea
546
+ id="select-schema"
547
+ class="w-full h-80 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
548
+ spellcheck="false"
549
+ >
550
+ {
551
+ "type": "select",
552
+ "key": "category",
553
+ "label": "Product Category",
554
+ "required": true,
555
+ "options": [
556
+ {"value": "electronics", "label": "Electronics"},
557
+ {"value": "clothing", "label": "Clothing & Apparel"},
558
+ {"value": "books", "label": "Books & Media"},
559
+ {"value": "home", "label": "Home & Garden"},
560
+ {"value": "sports", "label": "Sports & Recreation"}
561
+ ],
562
+ "default": "electronics",
563
+ "description": "Select the primary category for your product"
564
+ }</textarea
565
+ >
566
+ </div>
567
+
568
+ <div>
569
+ <div class="flex items-center justify-between mb-3">
570
+ <h3 class="text-lg font-semibold text-gray-900">
571
+ Live Preview
572
+ </h3>
573
+ <div class="flex items-center space-x-3">
574
+ <span class="text-sm font-medium text-gray-700"
575
+ >Read Only</span
576
+ >
577
+ <label class="toggle-switch">
578
+ <input
579
+ type="checkbox"
580
+ class="readonly-toggle"
581
+ data-element="select"
582
+ />
583
+ <span class="toggle-slider"></span>
584
+ </label>
585
+ </div>
586
+ </div>
587
+ <div
588
+ id="select-preview"
589
+ class="border border-gray-300 rounded-lg p-4 bg-white min-h-80"
590
+ ></div>
591
+ </div>
592
+ </div>
593
+
594
+ <div class="mt-6">
595
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
596
+ Available Properties
597
+ </h3>
598
+ <div class="bg-gray-50 rounded-lg p-4">
599
+ <ul class="space-y-2 text-sm">
600
+ <li><strong>type:</strong> "select" (required)</li>
601
+ <li><strong>key:</strong> Field identifier (required)</li>
602
+ <li><strong>label:</strong> Display label for the field</li>
603
+ <li>
604
+ <strong>required:</strong> Whether field is required (boolean)
605
+ </li>
606
+ <li>
607
+ <strong>options:</strong> Array of {value, label} option
608
+ objects (required)
609
+ </li>
610
+ <li>
611
+ <strong>default:</strong> Default selected value (must match
612
+ an option value)
613
+ </li>
614
+ <li>
615
+ <strong>description:</strong> Field description (shows in
616
+ tooltip)
617
+ </li>
618
+ <li>
619
+ <strong>hint:</strong> Additional hint text (shows in tooltip)
620
+ </li>
621
+ <li>
622
+ <strong>actions:</strong> Array of action buttons for readonly
623
+ mode
624
+ </li>
625
+ <li>
626
+ <strong>hidden:</strong> Hide field from form display but
627
+ include in data (boolean)
628
+ </li>
629
+ </ul>
630
+ </div>
631
+ </div>
632
+ </div>
633
+
634
+ <!-- File Element -->
635
+ <div id="file" class="element-section mb-16">
636
+ <h2 class="text-2xl font-bold text-gray-900 mb-4">File Upload</h2>
637
+ <p class="text-gray-700 mb-4">
638
+ File upload with preview, drag-and-drop, and file type restrictions.
639
+ Supports both single and multiple files.
640
+ </p>
641
+ <div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
642
+ <p class="text-sm text-blue-800">
643
+ <strong>Multiple Files:</strong> Set
644
+ <code>multiple: true</code> to allow multiple file selection. Use
645
+ <code>min</code> and <code>max</code> properties to control file
646
+ count limits.
647
+ </p>
648
+ </div>
649
+
650
+ <div class="grid grid-cols-2 gap-6">
651
+ <div>
652
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
653
+ Schema Definition
654
+ </h3>
655
+ <textarea
656
+ id="file-schema"
657
+ class="w-full h-80 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
658
+ spellcheck="false"
659
+ >
660
+ {
661
+ "type": "file",
662
+ "key": "avatar",
663
+ "label": "Profile Picture",
664
+ "required": false,
665
+ "accept": {
666
+ "extensions": ["jpg", "jpeg", "png", "gif", "webp"],
667
+ "maxSize": 5242880
668
+ },
669
+ "description": "Upload your profile picture (max 5MB, images only)"
670
+ }</textarea
671
+ >
672
+ </div>
673
+
674
+ <div>
675
+ <div class="flex items-center justify-between mb-3">
676
+ <h3 class="text-lg font-semibold text-gray-900">
677
+ Live Preview
678
+ </h3>
679
+ <div class="flex items-center space-x-3">
680
+ <span class="text-sm font-medium text-gray-700"
681
+ >Read Only</span
682
+ >
683
+ <label class="toggle-switch">
684
+ <input
685
+ type="checkbox"
686
+ class="readonly-toggle"
687
+ data-element="file"
688
+ />
689
+ <span class="toggle-slider"></span>
690
+ </label>
691
+ </div>
692
+ </div>
693
+ <div
694
+ id="file-preview"
695
+ class="border border-gray-300 rounded-lg p-4 bg-white min-h-80"
696
+ ></div>
697
+ </div>
698
+ </div>
699
+
700
+ <div class="mt-6">
701
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
702
+ Available Properties
703
+ </h3>
704
+ <div class="bg-gray-50 rounded-lg p-4">
705
+ <ul class="space-y-2 text-sm">
706
+ <li><strong>type:</strong> "file" (required)</li>
707
+ <li><strong>key:</strong> Field identifier (required)</li>
708
+ <li><strong>label:</strong> Display label for the field</li>
709
+ <li>
710
+ <strong>required:</strong> Whether field is required (boolean)
711
+ </li>
712
+ <li><strong>accept:</strong> Object with file restrictions</li>
713
+ <li>
714
+ <strong>accept.extensions:</strong> Array of allowed file
715
+ extensions
716
+ </li>
717
+ <li>
718
+ <strong>accept.maxSize:</strong> Maximum file size in bytes
719
+ </li>
720
+ <li>
721
+ <strong>multiple:</strong> Allow multiple file selection
722
+ (boolean)
723
+ </li>
724
+ <li>
725
+ <strong>minCount:</strong> Minimum number of files (when
726
+ multiple is true)
727
+ </li>
728
+ <li>
729
+ <strong>maxCount:</strong> Maximum number of files (when
730
+ multiple is true)
731
+ </li>
732
+ <li>
733
+ <strong>default:</strong> Default file resource ID or array
734
+ for multiple
735
+ </li>
736
+ <li>
737
+ <strong>description:</strong> Field description (shows in
738
+ tooltip)
739
+ </li>
740
+ <li>
741
+ <strong>hint:</strong> Additional hint text (shows in tooltip)
742
+ </li>
743
+ <li>
744
+ <strong>actions:</strong> Array of action buttons for readonly
745
+ mode
746
+ </li>
747
+ <li>
748
+ <strong>hidden:</strong> Hide field from form display but
749
+ include in data (boolean)
750
+ </li>
751
+ </ul>
752
+ </div>
753
+ </div>
754
+ </div>
755
+
756
+ <!-- Files Element -->
757
+ <div id="files" class="element-section mb-16">
758
+ <h2 class="text-2xl font-bold text-gray-900 mb-4">
759
+ Multiple Files (Legacy)
760
+ </h2>
761
+ <p class="text-gray-700 mb-4">
762
+ Multiple file uploads with grid preview, count limits, and
763
+ drag-and-drop.
764
+ </p>
765
+ <div
766
+ class="bg-orange-50 border border-orange-200 rounded-lg p-4 mb-6"
767
+ >
768
+ <p class="text-sm text-orange-800">
769
+ <strong>⚠️ Deprecated:</strong> The <code>files</code> type is
770
+ deprecated. Use <code>file</code> with
771
+ <code>multiple: true</code> instead for consistent API design.
772
+ </p>
773
+ </div>
774
+
775
+ <div class="grid grid-cols-2 gap-6">
776
+ <div>
777
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
778
+ Schema Definition
779
+ </h3>
780
+ <textarea
781
+ id="files-schema"
782
+ class="w-full h-80 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
783
+ spellcheck="false"
784
+ >
785
+ {
786
+ "type": "files",
787
+ "key": "attachments",
788
+ "label": "Project Attachments",
789
+ "required": false,
790
+ "min": 1,
791
+ "max": 10,
792
+ "accept": {
793
+ "extensions": ["jpg", "png", "pdf", "doc", "docx", "mp4"],
794
+ "maxSize": 10485760
795
+ },
796
+ "description": "Upload project files (max 10MB each, 1-10 files)"
797
+ }</textarea
798
+ >
799
+ </div>
800
+
801
+ <div>
802
+ <div class="flex items-center justify-between mb-3">
803
+ <h3 class="text-lg font-semibold text-gray-900">
804
+ Live Preview
805
+ </h3>
806
+ <div class="flex items-center space-x-3">
807
+ <span class="text-sm font-medium text-gray-700"
808
+ >Read Only</span
809
+ >
810
+ <label class="toggle-switch">
811
+ <input
812
+ type="checkbox"
813
+ class="readonly-toggle"
814
+ data-element="files"
815
+ />
816
+ <span class="toggle-slider"></span>
817
+ </label>
818
+ </div>
819
+ </div>
820
+ <div
821
+ id="files-preview"
822
+ class="border border-gray-300 rounded-lg p-4 bg-white min-h-80"
823
+ ></div>
824
+ </div>
825
+ </div>
826
+
827
+ <div class="mt-6">
828
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
829
+ Available Properties
830
+ </h3>
831
+ <div class="bg-gray-50 rounded-lg p-4">
832
+ <ul class="space-y-2 text-sm">
833
+ <li><strong>type:</strong> "files" (required)</li>
834
+ <li><strong>key:</strong> Field identifier (required)</li>
835
+ <li><strong>label:</strong> Display label for the field</li>
836
+ <li>
837
+ <strong>required:</strong> Whether field is required (boolean)
838
+ </li>
839
+ <li><strong>min:</strong> Minimum number of files (number)</li>
840
+ <li><strong>max:</strong> Maximum number of files (number)</li>
841
+ <li><strong>accept:</strong> Object with file restrictions</li>
842
+ <li>
843
+ <strong>accept.extensions:</strong> Array of allowed file
844
+ extensions
845
+ </li>
846
+ <li>
847
+ <strong>accept.maxSize:</strong> Maximum file size in bytes
848
+ </li>
849
+ <li>
850
+ <strong>default:</strong> Array of default file resource IDs
851
+ </li>
852
+ <li>
853
+ <strong>description:</strong> Field description (shows in
854
+ tooltip)
855
+ </li>
856
+ <li>
857
+ <strong>hint:</strong> Additional hint text (shows in tooltip)
858
+ </li>
859
+ <li>
860
+ <strong>actions:</strong> Array of action buttons for readonly
861
+ mode
862
+ </li>
863
+ <li>
864
+ <strong>hidden:</strong> Hide field from form display but
865
+ include in data (boolean)
866
+ </li>
867
+ </ul>
868
+ </div>
869
+ </div>
870
+ </div>
871
+
872
+ <!-- Container Element -->
873
+ <div id="container" class="element-section mb-16">
874
+ <h2 class="text-2xl font-bold text-gray-900 mb-4">Container</h2>
875
+ <p class="text-gray-700 mb-4">
876
+ Nested object container with support for both single objects and
877
+ repeatable arrays.
878
+ </p>
879
+ <div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
880
+ <p class="text-sm text-blue-800">
881
+ <strong>Multiple Containers:</strong> Set
882
+ <code>multiple: true</code> to create repeatable container arrays.
883
+ Use <code>minCount</code> and <code>maxCount</code> to control
884
+ item limits.
885
+ </p>
886
+ </div>
887
+
888
+ <div class="grid grid-cols-2 gap-6">
889
+ <!-- Schema Editor -->
890
+ <div>
891
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
892
+ Schema Definition
893
+ </h3>
894
+ <textarea
895
+ id="container-schema"
896
+ class="w-full h-80 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
897
+ spellcheck="false"
898
+ >
899
+ {
900
+ "type": "container",
901
+ "key": "contacts",
902
+ "label": "Contact Information",
903
+ "multiple": true,
904
+ "minCount": 1,
905
+ "maxCount": 5,
906
+ "elements": [
907
+ {
908
+ "type": "text",
909
+ "key": "name",
910
+ "label": "Contact Name",
911
+ "required": true
912
+ },
913
+ {
914
+ "type": "text",
915
+ "key": "email",
916
+ "label": "Email Address",
917
+ "required": true,
918
+ "pattern": "^[^@]+@[^@]+\\.[^@]+$"
919
+ },
920
+ {
921
+ "type": "select",
922
+ "key": "role",
923
+ "label": "Role",
924
+ "options": [
925
+ {"value": "primary", "label": "Primary Contact"},
926
+ {"value": "secondary", "label": "Secondary Contact"},
927
+ {"value": "technical", "label": "Technical Contact"}
928
+ ],
929
+ "default": "primary"
930
+ }
931
+ ],
932
+ "description": "Add contact information for your project"
933
+ }</textarea
934
+ >
935
+ </div>
936
+
937
+ <!-- Live Preview -->
938
+ <div>
939
+ <div class="flex items-center justify-between mb-3">
940
+ <h3 class="text-lg font-semibold text-gray-900">
941
+ Live Preview
942
+ </h3>
943
+ <div class="flex items-center space-x-3">
944
+ <span class="text-sm font-medium text-gray-700"
945
+ >Read Only</span
946
+ >
947
+ <label class="toggle-switch">
948
+ <input
949
+ type="checkbox"
950
+ class="readonly-toggle"
951
+ data-element="container"
952
+ />
953
+ <span class="toggle-slider"></span>
954
+ </label>
955
+ </div>
956
+ </div>
957
+ <div
958
+ id="container-preview"
959
+ class="border border-gray-300 rounded-lg p-4 bg-white min-h-80"
960
+ ></div>
961
+ </div>
962
+ </div>
963
+
964
+ <div class="mt-6">
965
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
966
+ Available Properties
967
+ </h3>
968
+ <div class="bg-gray-50 rounded-lg p-4">
969
+ <ul class="space-y-2 text-sm">
970
+ <li><strong>type:</strong> "container" (required)</li>
971
+ <li><strong>key:</strong> Field identifier (required)</li>
972
+ <li><strong>label:</strong> Display label for the container</li>
973
+ <li>
974
+ <strong>elements:</strong> Array of nested form elements
975
+ (required)
976
+ </li>
977
+ <li>
978
+ <strong>multiple:</strong> Make container repeatable (boolean)
979
+ </li>
980
+ <li>
981
+ <strong>minCount:</strong> Minimum number of container
982
+ instances (when multiple is true)
983
+ </li>
984
+ <li>
985
+ <strong>maxCount:</strong> Maximum number of container
986
+ instances (when multiple is true)
987
+ </li>
988
+ <li>
989
+ <strong>description:</strong> Container description (shows in
990
+ tooltip)
991
+ </li>
992
+ <li>
993
+ <strong>hint:</strong> Additional hint text (shows in tooltip)
994
+ </li>
995
+ <li>
996
+ <strong>actions:</strong> Array of action buttons for readonly
997
+ mode
998
+ </li>
999
+ <li>
1000
+ <strong>hidden:</strong> Hide field from form display but
1001
+ include in data (boolean)
1002
+ </li>
1003
+ </ul>
1004
+ </div>
1005
+ </div>
1006
+ </div>
1007
+
1008
+ <!-- Group Element -->
1009
+ <div id="group" class="element-section mb-16">
1010
+ <h2 class="text-2xl font-bold text-gray-900 mb-4">
1011
+ Group Container (Legacy)
1012
+ </h2>
1013
+ <p class="text-gray-700 mb-4">
1014
+ Nested object container with optional repeatable arrays (min/max
1015
+ items).
1016
+ </p>
1017
+ <div
1018
+ class="bg-orange-50 border border-orange-200 rounded-lg p-4 mb-6"
1019
+ >
1020
+ <p class="text-sm text-orange-800">
1021
+ <strong>⚠️ Deprecated:</strong> The <code>group</code> type is
1022
+ deprecated. Use <code>container</code> with
1023
+ <code>multiple: true</code> instead. Groups use
1024
+ <code>repeat: {min, max}</code> syntax which is being replaced by
1025
+ <code>minCount</code> and <code>maxCount</code> properties.
1026
+ </p>
1027
+ </div>
1028
+
1029
+ <div class="grid grid-cols-2 gap-6">
1030
+ <div>
1031
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
1032
+ Schema Definition
1033
+ </h3>
1034
+ <textarea
1035
+ id="group-schema"
1036
+ class="w-full h-80 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
1037
+ spellcheck="false"
1038
+ >
1039
+ {
1040
+ "type": "group",
1041
+ "key": "contacts",
1042
+ "label": "Contact Information",
1043
+ "repeat": {
1044
+ "min": 1,
1045
+ "max": 5
1046
+ },
1047
+ "elements": [
1048
+ {
1049
+ "type": "text",
1050
+ "key": "name",
1051
+ "label": "Contact Name",
1052
+ "required": true
1053
+ },
1054
+ {
1055
+ "type": "text",
1056
+ "key": "email",
1057
+ "label": "Email Address",
1058
+ "required": true,
1059
+ "pattern": "^[^@]+@[^@]+\\.[^@]+$"
1060
+ },
1061
+ {
1062
+ "type": "select",
1063
+ "key": "role",
1064
+ "label": "Role",
1065
+ "options": [
1066
+ {"value": "primary", "label": "Primary Contact"},
1067
+ {"value": "secondary", "label": "Secondary Contact"},
1068
+ {"value": "technical", "label": "Technical Contact"}
1069
+ ],
1070
+ "default": "primary"
1071
+ }
1072
+ ],
1073
+ "description": "Add contact information for your project"
1074
+ }</textarea
1075
+ >
1076
+ </div>
1077
+
1078
+ <div>
1079
+ <div class="flex items-center justify-between mb-3">
1080
+ <h3 class="text-lg font-semibold text-gray-900">
1081
+ Live Preview
1082
+ </h3>
1083
+ <div class="flex items-center space-x-3">
1084
+ <span class="text-sm font-medium text-gray-700"
1085
+ >Read Only</span
1086
+ >
1087
+ <label class="toggle-switch">
1088
+ <input
1089
+ type="checkbox"
1090
+ class="readonly-toggle"
1091
+ data-element="group"
1092
+ />
1093
+ <span class="toggle-slider"></span>
1094
+ </label>
1095
+ </div>
1096
+ </div>
1097
+ <div
1098
+ id="group-preview"
1099
+ class="border border-gray-300 rounded-lg p-4 bg-white min-h-80"
1100
+ ></div>
1101
+ </div>
1102
+ </div>
1103
+
1104
+ <div class="mt-6">
1105
+ <h3 class="text-lg font-semibold text-gray-900 mb-3">
1106
+ Available Properties
1107
+ </h3>
1108
+ <div class="bg-gray-50 rounded-lg p-4">
1109
+ <ul class="space-y-2 text-sm">
1110
+ <li><strong>type:</strong> "group" (required)</li>
1111
+ <li><strong>key:</strong> Field identifier (required)</li>
1112
+ <li><strong>label:</strong> Display label for the group</li>
1113
+ <li>
1114
+ <strong>elements:</strong> Array of nested form elements
1115
+ (required)
1116
+ </li>
1117
+ <li>
1118
+ <strong>repeat:</strong> Object to make group repeatable
1119
+ </li>
1120
+ <li>
1121
+ <strong>repeat.min:</strong> Minimum number of group instances
1122
+ </li>
1123
+ <li>
1124
+ <strong>repeat.max:</strong> Maximum number of group instances
1125
+ </li>
1126
+ <li>
1127
+ <strong>description:</strong> Group description (shows in
1128
+ tooltip)
1129
+ </li>
1130
+ <li>
1131
+ <strong>hint:</strong> Additional hint text (shows in tooltip)
1132
+ </li>
1133
+ <li>
1134
+ <strong>actions:</strong> Array of action buttons for readonly
1135
+ mode
1136
+ </li>
1137
+ <li>
1138
+ <strong>hidden:</strong> Hide field from form display but
1139
+ include in data (boolean)
1140
+ </li>
1141
+ </ul>
1142
+ </div>
1143
+ </div>
1144
+ </div>
1145
+ </div>
1146
+ </div>
1147
+
1148
+ <script type="module" src="./form-builder.js"></script>
1149
+ <script type="module" src="./elements.js"></script>
1150
+ </body>
1151
+ </html>