@dmitryvim/form-builder 0.2.9 → 0.2.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/README.md CHANGED
@@ -42,7 +42,7 @@ This package ships with complete TypeScript definitions. Ensure your `tsconfig.j
42
42
  ```json
43
43
  {
44
44
  "compilerOptions": {
45
- "moduleResolution": "NodeNext", // or "Bundler" for Vite/Webpack
45
+ "moduleResolution": "NodeNext", // or "Bundler" for Vite/Webpack
46
46
  "esModuleInterop": true,
47
47
  "skipLibCheck": false
48
48
  }
@@ -77,6 +77,33 @@ This package ships with complete TypeScript definitions. Ensure your `tsconfig.j
77
77
 
78
78
  ## Quick Examples
79
79
 
80
+ ### Root-Level Properties (v0.2.9+)
81
+
82
+ The root schema now supports container-like properties:
83
+
84
+ - **`columns`** - Grid layout for root-level fields (1-4 columns)
85
+ - **`prefillHints`** - Quick-fill templates for entire form
86
+
87
+ **Example:**
88
+
89
+ ```json
90
+ {
91
+ "version": "0.3",
92
+ "title": "Contact Form",
93
+ "columns": 2,
94
+ "prefillHints": [
95
+ {"label": "Work", "values": {"email": "work@company.com"}, "icon": "💼"},
96
+ {"label": "Personal", "values": {"email": "personal@email.com"}, "icon": "🏠"}
97
+ ],
98
+ "elements": [
99
+ {"type": "text", "key": "name", "label": "Name"},
100
+ {"type": "text", "key": "email", "label": "Email"}
101
+ ]
102
+ }
103
+ ```
104
+
105
+ See [CLAUDE.md](CLAUDE.md) for complete documentation.
106
+
80
107
  ### Simple Contact Form
81
108
 
82
109
  ```json
@@ -178,7 +205,7 @@ const formBuilder = createFormBuilder({
178
205
  },
179
206
  downloadFile: (resourceId, fileName) => {
180
207
  // Handle file download
181
- window.open(`/api/files/${resourceId}`, '_blank');
208
+ window.open(`/api/files/${resourceId}`, "_blank");
182
209
  },
183
210
  getThumbnail: async (resourceId) => {
184
211
  // v0.2.0: Async-only thumbnail loading
@@ -196,9 +223,9 @@ const formBuilder = createFormBuilder({
196
223
  },
197
224
  // v0.2.0: CSS theming
198
225
  theme: {
199
- primaryColor: '#0066cc',
200
- borderRadius: '4px',
201
- }
226
+ primaryColor: "#0066cc",
227
+ borderRadius: "4px",
228
+ },
202
229
  });
203
230
 
204
231
  // Render form
@@ -212,8 +239,8 @@ if (result.valid) {
212
239
  }
213
240
 
214
241
  // v0.2.0: Update fields without re-rendering
215
- formBuilder.updateField('email', 'newemail@example.com');
216
- formBuilder.setFormData({ name: 'John', email: 'john@example.com' });
242
+ formBuilder.updateField("email", "newemail@example.com");
243
+ formBuilder.setFormData({ name: "John", email: "john@example.com" });
217
244
  ```
218
245
 
219
246
  ### 2. CDN Integration
@@ -226,7 +253,9 @@ formBuilder.setFormData({ name: 'John', email: 'john@example.com' });
226
253
 
227
254
  const formBuilder = createFormBuilder({
228
255
  uploadFile: async (file) => "resource-id",
229
- downloadFile: (resourceId) => { /* download */ }
256
+ downloadFile: (resourceId) => {
257
+ /* download */
258
+ },
230
259
  });
231
260
 
232
261
  const rootElement = document.getElementById("form-container");
@@ -343,6 +372,7 @@ form.destroy();
343
372
  **Breaking Changes in v0.2.0:**
344
373
 
345
374
  1. **Instance-Only API** - Global static methods removed
375
+
346
376
  ```typescript
347
377
  // OLD (v0.1.x)
348
378
  FormBuilder.configure({ uploadFile: async (file) => "id" });
@@ -354,12 +384,13 @@ form.renderForm(root, schema);
354
384
  ```
355
385
 
356
386
  2. **Async getThumbnail** - Must return Promise
387
+
357
388
  ```typescript
358
389
  // OLD (v0.1.x)
359
- getThumbnail: (resourceId) => `/thumbs/${resourceId}.jpg`
390
+ getThumbnail: (resourceId) => `/thumbs/${resourceId}.jpg`;
360
391
 
361
392
  // NEW (v0.2.0)
362
- getThumbnail: async (resourceId) => `/thumbs/${resourceId}.jpg`
393
+ getThumbnail: async (resourceId) => `/thumbs/${resourceId}.jpg`;
363
394
  ```
364
395
 
365
396
  See [CHANGELOG.md](CHANGELOG.md) for complete migration details.
@@ -372,26 +403,27 @@ Customize appearance with 43 theme properties:
372
403
  const form = createFormBuilder({
373
404
  theme: {
374
405
  // Colors
375
- primaryColor: '#0066cc',
376
- borderColor: '#e0e0e0',
377
- errorColor: '#d32f2f',
406
+ primaryColor: "#0066cc",
407
+ borderColor: "#e0e0e0",
408
+ errorColor: "#d32f2f",
378
409
 
379
410
  // Typography
380
- fontSize: '16px',
411
+ fontSize: "16px",
381
412
  fontFamily: '"Roboto", sans-serif',
382
413
 
383
414
  // Spacing
384
- borderRadius: '4px',
385
- inputPaddingX: '12px',
415
+ borderRadius: "4px",
416
+ inputPaddingX: "12px",
386
417
 
387
418
  // Buttons
388
- buttonBgColor: '#0066cc',
389
- buttonTextColor: '#ffffff',
390
- }
419
+ buttonBgColor: "#0066cc",
420
+ buttonTextColor: "#ffffff",
421
+ },
391
422
  });
392
423
  ```
393
424
 
394
425
  **Most Common Properties:**
426
+
395
427
  - `primaryColor` - Primary brand color
396
428
  - `borderRadius` - Border radius for inputs/buttons
397
429
  - `fontSize` - Base font size
@@ -409,10 +441,10 @@ const form = createFormBuilder({
409
441
  // Required: Upload handler
410
442
  uploadFile: async (file) => {
411
443
  const formData = new FormData();
412
- formData.append('file', file);
413
- const response = await fetch('/api/upload', {
414
- method: 'POST',
415
- body: formData
444
+ formData.append("file", file);
445
+ const response = await fetch("/api/upload", {
446
+ method: "POST",
447
+ body: formData,
416
448
  });
417
449
  const data = await response.json();
418
450
  return data.resourceId; // Return unique resource ID
@@ -421,14 +453,14 @@ const form = createFormBuilder({
421
453
  // Required: Download handler
422
454
  downloadFile: (resourceId, fileName) => {
423
455
  // Option 1: Direct download
424
- window.open(`/api/files/${resourceId}/download`, '_blank');
456
+ window.open(`/api/files/${resourceId}/download`, "_blank");
425
457
 
426
458
  // Option 2: Programmatic download
427
459
  fetch(`/api/files/${resourceId}/download`)
428
- .then(response => response.blob())
429
- .then(blob => {
460
+ .then((response) => response.blob())
461
+ .then((blob) => {
430
462
  const url = URL.createObjectURL(blob);
431
- const a = document.createElement('a');
463
+ const a = document.createElement("a");
432
464
  a.href = url;
433
465
  a.download = fileName;
434
466
  a.click();
@@ -446,7 +478,7 @@ const form = createFormBuilder({
446
478
  // Optional: Full file download URL (used instead of getThumbnail for downloads)
447
479
  getDownloadUrl: (resourceId) => {
448
480
  return `/api/files/${resourceId}/download`;
449
- }
481
+ },
450
482
  });
451
483
  ```
452
484
 
@@ -470,17 +502,25 @@ When a user clicks the download button in readonly mode, the form builder uses t
470
502
 
471
503
  ### Conditional Field Visibility
472
504
 
473
- Show or hide fields based on form data values using the `displayIf` property:
505
+ Show or hide fields based on form data values using the `enableIf` property:
474
506
 
475
507
  ```typescript
476
- interface DisplayCondition {
477
- key: string; // Field key to check (supports nested paths)
478
- equals?: any; // Value to compare (initial operator)
508
+ interface EnableCondition {
509
+ key: string; // Field key to check (supports nested paths)
510
+ equals?: any; // Value to compare (initial operator)
511
+ scope?: "relative" | "absolute"; // v0.2.9: Evaluation context (default: "relative")
479
512
  // Future: notEquals, in, exists, greaterThan, lessThan, and/or
480
513
  }
481
514
  ```
482
515
 
516
+ **Scope Behavior (v0.2.9+):**
517
+
518
+ - **`scope: "relative"`** (default): Checks fields within current container
519
+ - **`scope: "absolute"`**: Checks fields in root-level form data
520
+ - Use absolute scope when referencing root-level fields from inside containers
521
+
483
522
  **Simple Condition:**
523
+
484
524
  ```json
485
525
  {
486
526
  "type": "select",
@@ -495,7 +535,7 @@ interface DisplayCondition {
495
535
  "type": "text",
496
536
  "key": "background_image",
497
537
  "label": "Background Image URL",
498
- "displayIf": {
538
+ "enableIf": {
499
539
  "key": "background_mode",
500
540
  "equals": "use_image"
501
541
  }
@@ -503,12 +543,13 @@ interface DisplayCondition {
503
543
  ```
504
544
 
505
545
  **Nested Path:**
546
+
506
547
  ```json
507
548
  {
508
549
  "type": "text",
509
550
  "key": "city_details",
510
551
  "label": "City-Specific Information",
511
- "displayIf": {
552
+ "enableIf": {
512
553
  "key": "address.city",
513
554
  "equals": "New York"
514
555
  }
@@ -516,25 +557,48 @@ interface DisplayCondition {
516
557
  ```
517
558
 
518
559
  **Array Indices:**
560
+
519
561
  ```json
520
562
  {
521
563
  "type": "text",
522
564
  "key": "first_item_note",
523
565
  "label": "Note for First Item",
524
- "displayIf": {
566
+ "enableIf": {
525
567
  "key": "items[0].quantity",
526
568
  "equals": 1
527
569
  }
528
570
  }
529
571
  ```
530
572
 
573
+ **Absolute Scope (v0.2.9+):**
574
+
575
+ ```json
576
+ {
577
+ "type": "container",
578
+ "key": "shipping_options",
579
+ "elements": [
580
+ {
581
+ "type": "text",
582
+ "key": "express_note",
583
+ "label": "Express Shipping Note",
584
+ "enableIf": {
585
+ "key": "shipping_type",
586
+ "equals": "express",
587
+ "scope": "absolute"
588
+ }
589
+ }
590
+ ]
591
+ }
592
+ ```
593
+
531
594
  **Hide Entire Sections:**
595
+
532
596
  ```json
533
597
  {
534
598
  "type": "container",
535
599
  "key": "advanced_settings",
536
600
  "label": "Advanced Settings",
537
- "displayIf": {
601
+ "enableIf": {
538
602
  "key": "mode",
539
603
  "equals": "advanced"
540
604
  },
@@ -560,19 +624,19 @@ interface DisplayCondition {
560
624
 
561
625
  **Future Extensibility:**
562
626
 
563
- The `displayIf` system is designed for future expansion:
627
+ The `enableIf` system is designed for future expansion:
564
628
 
565
629
  ```typescript
566
630
  // Future operators (planned)
567
631
  {
568
- "displayIf": {
632
+ "enableIf": {
569
633
  "key": "age",
570
634
  "greaterThan": 18
571
635
  }
572
636
  }
573
637
 
574
638
  {
575
- "displayIf": {
639
+ "enableIf": {
576
640
  "key": "status",
577
641
  "in": ["active", "pending"]
578
642
  }
@@ -580,7 +644,7 @@ The `displayIf` system is designed for future expansion:
580
644
 
581
645
  // Future complex conditions (planned)
582
646
  {
583
- "displayIf": {
647
+ "enableIf": {
584
648
  "and": [
585
649
  { "key": "role", "equals": "admin" },
586
650
  { "key": "verified", "equals": true }