@dmitryvim/form-builder 0.1.35 → 0.1.37

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/demo.js CHANGED
@@ -113,7 +113,7 @@ const EXAMPLE_SCHEMA = {
113
113
  maxCount: 5,
114
114
  minLength: 2,
115
115
  maxLength: 20,
116
- default: "popular"
116
+ default: "popular",
117
117
  },
118
118
  {
119
119
  type: "textarea",
@@ -127,13 +127,14 @@ const EXAMPLE_SCHEMA = {
127
127
  maxCount: 4,
128
128
  minLength: 10,
129
129
  maxLength: 200,
130
- rows: 3
130
+ rows: 3,
131
131
  },
132
132
  {
133
133
  type: "number",
134
134
  key: "dimensions",
135
135
  label: "Размеры (см)",
136
- description: "Укажите размеры товара в сантиметрах: длина, ширина, высота",
136
+ description:
137
+ "Укажите размеры товара в сантиметрах: длина, ширина, высота",
137
138
  placeholder: "0",
138
139
  required: false,
139
140
  multiple: true,
@@ -142,7 +143,7 @@ const EXAMPLE_SCHEMA = {
142
143
  min: 0,
143
144
  max: 500,
144
145
  step: 0.1,
145
- decimals: 1
146
+ decimals: 1,
146
147
  },
147
148
  {
148
149
  type: "select",
@@ -159,9 +160,9 @@ const EXAMPLE_SCHEMA = {
159
160
  { value: "red", label: "Красный" },
160
161
  { value: "blue", label: "Синий" },
161
162
  { value: "green", label: "Зеленый" },
162
- { value: "gray", label: "Серый" }
163
+ { value: "gray", label: "Серый" },
163
164
  ],
164
- default: "white"
165
+ default: "white",
165
166
  },
166
167
  {
167
168
  type: "file",
@@ -214,7 +215,7 @@ const EXAMPLE_SCHEMA = {
214
215
  label: "Session ID",
215
216
  description: "Hidden session identifier for tracking purposes",
216
217
  hidden: true,
217
- default: "session_" + Math.random().toString(36).substr(2, 9),
218
+ default: `session_${Math.random().toString(36).substr(2, 9)}`,
218
219
  },
219
220
  ],
220
221
  };
@@ -328,10 +329,10 @@ let actionLabelMap = new Map();
328
329
  // Build action value -> label mapping from schema for efficient lookup
329
330
  function buildActionLabelMap(schema) {
330
331
  const map = new Map();
331
-
332
+
332
333
  function processElements(elements) {
333
334
  if (!Array.isArray(elements)) return;
334
-
335
+
335
336
  for (const element of elements) {
336
337
  if (element.actions && Array.isArray(element.actions)) {
337
338
  for (const action of element.actions) {
@@ -340,18 +341,18 @@ function buildActionLabelMap(schema) {
340
341
  }
341
342
  }
342
343
  }
343
-
344
+
344
345
  // Process nested group elements
345
346
  if (element.elements && Array.isArray(element.elements)) {
346
347
  processElements(element.elements);
347
348
  }
348
349
  }
349
350
  }
350
-
351
+
351
352
  if (schema && schema.elements) {
352
353
  processElements(schema.elements);
353
354
  }
354
-
355
+
355
356
  return map;
356
357
  }
357
358
 
@@ -372,6 +373,10 @@ const el = {
372
373
  copyDataBtn: document.getElementById("copyDataBtn"),
373
374
  downloadDataBtn: document.getElementById("downloadDataBtn"),
374
375
  dataErrors: document.getElementById("dataErrors"),
376
+ actionsTextarea: document.getElementById("actionsTextarea"),
377
+ formatActionsBtn: document.getElementById("formatActionsBtn"),
378
+ clearActionsBtn: document.getElementById("clearActionsBtn"),
379
+ actionsErrors: document.getElementById("actionsErrors"),
375
380
  };
376
381
 
377
382
  // Utility functions
@@ -405,6 +410,44 @@ function downloadFile(filename, content) {
405
410
  URL.revokeObjectURL(url);
406
411
  }
407
412
 
413
+ // Parse and validate external actions
414
+ function parseActions(actionsText) {
415
+ if (!actionsText || actionsText.trim() === "") {
416
+ return [];
417
+ }
418
+
419
+ try {
420
+ const actions = JSON.parse(actionsText);
421
+
422
+ if (!Array.isArray(actions)) {
423
+ throw new Error("Actions must be an array");
424
+ }
425
+
426
+ // Validate each action
427
+ for (let i = 0; i < actions.length; i++) {
428
+ const action = actions[i];
429
+ if (!action || typeof action !== "object") {
430
+ throw new Error(`Action at index ${i} must be an object`);
431
+ }
432
+ if (!action.related_field || typeof action.related_field !== "string") {
433
+ throw new Error(
434
+ `Action at index ${i} missing valid 'related_field' property`,
435
+ );
436
+ }
437
+ if (!action.value || typeof action.value !== "string") {
438
+ throw new Error(`Action at index ${i} missing valid 'value' property`);
439
+ }
440
+ if (!action.label || typeof action.label !== "string") {
441
+ throw new Error(`Action at index ${i} missing valid 'label' property`);
442
+ }
443
+ }
444
+
445
+ return actions;
446
+ } catch (error) {
447
+ throw new Error(`Actions JSON error: ${error.message}`);
448
+ }
449
+ }
450
+
408
451
  // Configure FormBuilder with in-memory handlers
409
452
  function setupFormBuilder() {
410
453
  // Set form container
@@ -437,17 +480,44 @@ function setupFormBuilder() {
437
480
  });
438
481
 
439
482
  // Action handler - display message when action button is clicked
440
- FormBuilder.setActionHandler((value) => {
441
- // Use cached action map for O(1) lookup instead of re-parsing schema
442
- const actionLabel = actionLabelMap.get(value) || value; // fallback to value
483
+ // Updated to support both old system (1 param) and new system (2 params)
484
+ FormBuilder.setActionHandler((relatedFieldOrValue, value) => {
485
+ let actionLabel, actionValue, relatedField;
486
+
487
+ // Determine if this is the old system (1 param) or new system (2 params)
488
+ if (arguments.length === 1) {
489
+ // Old system: only value parameter
490
+ actionValue = relatedFieldOrValue;
491
+ actionLabel = actionLabelMap.get(actionValue) || actionValue;
492
+ relatedField = null;
493
+ } else {
494
+ // New system: related_field and value parameters
495
+ relatedField = relatedFieldOrValue;
496
+ actionValue = value;
497
+ actionLabel = `Action for ${relatedField}`;
498
+ }
499
+
500
+ console.log("Action clicked:", {
501
+ label: actionLabel,
502
+ value: actionValue,
503
+ relatedField,
504
+ system: arguments.length === 1 ? "schema-based" : "external",
505
+ });
443
506
 
444
- console.log("Action clicked:", { label: actionLabel, value });
445
-
446
507
  // Show message to user (compatible with all environments)
447
508
  if (typeof window !== "undefined" && window.alert) {
448
- window.alert(`${actionLabel} clicked: ${value}`);
509
+ if (relatedField) {
510
+ window.alert(
511
+ `External Action: "${actionLabel}" clicked for field "${relatedField}" with value: ${actionValue}`,
512
+ );
513
+ } else {
514
+ window.alert(`Schema Action: "${actionLabel}" clicked: ${actionValue}`);
515
+ }
449
516
  } else {
450
- console.log(`Demo action: ${actionLabel} clicked: ${value}`);
517
+ console.log(
518
+ `Demo action: ${actionLabel} clicked: ${actionValue}`,
519
+ relatedField ? ` for field: ${relatedField}` : "",
520
+ );
451
521
  }
452
522
  });
453
523
 
@@ -460,6 +530,7 @@ function setupFormBuilder() {
460
530
  function applyCurrentSchema() {
461
531
  clearError(el.schemaErrors);
462
532
  clearError(el.formErrors);
533
+ clearError(el.actionsErrors);
463
534
 
464
535
  try {
465
536
  const schema = JSON.parse(el.schemaInput.value);
@@ -473,6 +544,15 @@ function applyCurrentSchema() {
473
544
  return false;
474
545
  }
475
546
 
547
+ // Parse external actions
548
+ let externalActions = [];
549
+ try {
550
+ externalActions = parseActions(el.actionsTextarea.value);
551
+ } catch (error) {
552
+ showError(el.actionsErrors, error.message);
553
+ return false;
554
+ }
555
+
476
556
  // Build action value -> label map for efficient lookup
477
557
  actionLabelMap = buildActionLabelMap(schema);
478
558
 
@@ -491,10 +571,12 @@ function applyCurrentSchema() {
491
571
  // Ignore errors when getting current data
492
572
  }
493
573
 
494
- // Render form with current data
495
- FormBuilder.renderForm(schema, currentData);
574
+ // Render form with current data and external actions
575
+ FormBuilder.renderForm(schema, currentData, externalActions);
496
576
 
497
577
  console.log(`Form rendered in ${isReadOnly ? "readonly" : "edit"} mode`);
578
+ console.log(`External actions:`, externalActions);
579
+
498
580
  return true;
499
581
  } catch (e) {
500
582
  showError(el.schemaErrors, `JSON parse error: ${e.message}`);
@@ -512,7 +594,9 @@ el.resetSchemaBtn.addEventListener("click", () => {
512
594
  clearError(el.schemaErrors);
513
595
  clearError(el.formErrors);
514
596
  clearError(el.dataErrors);
597
+ clearError(el.actionsErrors);
515
598
  el.dataTextarea.value = "";
599
+ el.actionsTextarea.value = "";
516
600
 
517
601
  // Clear file storage
518
602
  fileStorage.clear();
@@ -617,6 +701,15 @@ el.prefillBtn.addEventListener("click", () => {
617
701
  const prefillData = JSON.parse(el.dataTextarea.value || "{}");
618
702
  const currentSchema = JSON.parse(el.schemaInput.value);
619
703
 
704
+ // Parse external actions
705
+ let externalActions = [];
706
+ try {
707
+ externalActions = parseActions(el.actionsTextarea.value);
708
+ } catch (error) {
709
+ showError(el.actionsErrors, error.message);
710
+ return;
711
+ }
712
+
620
713
  // Convert enhanced data back to raw format for prefilling
621
714
  const processedData = JSON.parse(JSON.stringify(prefillData));
622
715
 
@@ -657,9 +750,10 @@ el.prefillBtn.addEventListener("click", () => {
657
750
  const isReadOnly = el.readOnlyToggle.checked;
658
751
  FormBuilder.setMode(isReadOnly ? "readonly" : "edit");
659
752
 
660
- FormBuilder.renderForm(currentSchema, processedData);
753
+ FormBuilder.renderForm(currentSchema, processedData, externalActions);
661
754
  console.log("Form prefilled with data");
662
755
  console.log("Processed prefill data:", processedData);
756
+ console.log("External actions:", externalActions);
663
757
  } catch (e) {
664
758
  showError(el.dataErrors, `JSON parse error: ${e.message}`);
665
759
  console.error("Prefill error:", e);
@@ -684,6 +778,51 @@ el.downloadDataBtn.addEventListener("click", () => {
684
778
  console.log("Data downloaded");
685
779
  });
686
780
 
781
+ // Actions management handlers
782
+ el.formatActionsBtn.addEventListener("click", () => {
783
+ try {
784
+ const actions = parseActions(el.actionsTextarea.value);
785
+ el.actionsTextarea.value = pretty(actions);
786
+ clearError(el.actionsErrors);
787
+ console.log("Actions formatted");
788
+ } catch (e) {
789
+ showError(el.actionsErrors, e.message);
790
+ console.error("Format actions error:", e);
791
+ }
792
+ });
793
+
794
+ el.clearActionsBtn.addEventListener("click", () => {
795
+ el.actionsTextarea.value = "";
796
+ clearError(el.actionsErrors);
797
+ // Re-apply schema to remove external actions from the form
798
+ applyCurrentSchema();
799
+ console.log("Actions cleared");
800
+ });
801
+
802
+ // Example external actions for demonstration
803
+ const EXAMPLE_ACTIONS = [
804
+ {
805
+ related_field: "title[0]",
806
+ value: "generate-title",
807
+ label: "🤖 Generate Title",
808
+ },
809
+ {
810
+ related_field: "description",
811
+ value: "improve-description",
812
+ label: "✨ Improve Description",
813
+ },
814
+ {
815
+ related_field: "slides[0].title",
816
+ value: "optimize-slide-title",
817
+ label: "🎯 Optimize Slide Title",
818
+ },
819
+ {
820
+ related_field: "cover",
821
+ value: "analyze-image",
822
+ label: "🔍 Analyze Image",
823
+ },
824
+ ];
825
+
687
826
  // Initialize demo application
688
827
  function initDemo() {
689
828
  // Set up FormBuilder
@@ -692,6 +831,9 @@ function initDemo() {
692
831
  // Initialize with example schema
693
832
  el.schemaInput.value = pretty(EXAMPLE_SCHEMA);
694
833
 
834
+ // Initialize with example actions
835
+ el.actionsTextarea.value = pretty(EXAMPLE_ACTIONS);
836
+
695
837
  // Apply initial schema
696
838
  applyCurrentSchema();
697
839