@dmitryvim/form-builder 0.1.37 → 0.1.38

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
@@ -105,9 +105,9 @@ npm install @dmitryvim/form-builder
105
105
  "extensions": ["jpg", "png", "webp"]
106
106
  },
107
107
  "actions": [
108
- { "value": "enhance", "label": "Enhance Quality" },
109
- { "value": "crop", "label": "Auto Crop" },
110
- { "value": "retry", "label": "Try Again" }
108
+ { "key": "enhance", "label": "Enhance Quality" },
109
+ { "key": "crop", "label": "Auto Crop" },
110
+ { "key": "retry", "label": "Try Again" }
111
111
  ]
112
112
  },
113
113
  {
@@ -152,9 +152,14 @@ FormBuilder.setUploadHandler(async (file) => {
152
152
  // Your upload logic - return resource ID
153
153
  return "resource-123";
154
154
  });
155
- FormBuilder.setActionHandler((value) => {
155
+ FormBuilder.setActionHandler((value, key, relatedField) => {
156
156
  // Handle action button clicks
157
- console.log("Action clicked:", value);
157
+ console.log("Action clicked:", { value, key, relatedField });
158
+ if (relatedField) {
159
+ // Field-level action
160
+ } else {
161
+ // Form-level action
162
+ }
158
163
  });
159
164
  FormBuilder.renderForm(schema, prefillData);
160
165
  ```
package/dist/demo.js CHANGED
@@ -19,9 +19,9 @@ const EXAMPLE_SCHEMA = {
19
19
  },
20
20
  maxSizeMB: 10,
21
21
  actions: [
22
- { value: "cover1.retry", label: "А давай ещё разок" },
23
- { value: "cover1.enhance", label: "Улучшить качество" },
24
- { value: "cover1.crop", label: "Обрезать изображение" },
22
+ { key: "retry", label: "А давай ещё разок" },
23
+ { key: "enhance", label: "Улучшить качество" },
24
+ { key: "crop", label: "Обрезать изображение" },
25
25
  ],
26
26
  },
27
27
  {
@@ -323,38 +323,6 @@ class InMemoryFileStorage {
323
323
  // Initialize file storage
324
324
  const fileStorage = new InMemoryFileStorage();
325
325
 
326
- // Cache for action value -> label mapping for efficient lookup
327
- let actionLabelMap = new Map();
328
-
329
- // Build action value -> label mapping from schema for efficient lookup
330
- function buildActionLabelMap(schema) {
331
- const map = new Map();
332
-
333
- function processElements(elements) {
334
- if (!Array.isArray(elements)) return;
335
-
336
- for (const element of elements) {
337
- if (element.actions && Array.isArray(element.actions)) {
338
- for (const action of element.actions) {
339
- if (action.value && action.label) {
340
- map.set(action.value, action.label);
341
- }
342
- }
343
- }
344
-
345
- // Process nested group elements
346
- if (element.elements && Array.isArray(element.elements)) {
347
- processElements(element.elements);
348
- }
349
- }
350
- }
351
-
352
- if (schema && schema.elements) {
353
- processElements(schema.elements);
354
- }
355
-
356
- return map;
357
- }
358
326
 
359
327
  // DOM element references
360
328
  const el = {
@@ -429,16 +397,21 @@ function parseActions(actionsText) {
429
397
  if (!action || typeof action !== "object") {
430
398
  throw new Error(`Action at index ${i} must be an object`);
431
399
  }
432
- if (!action.related_field || typeof action.related_field !== "string") {
400
+ if (!action.key || typeof action.key !== "string") {
433
401
  throw new Error(
434
- `Action at index ${i} missing valid 'related_field' property`,
402
+ `Action at index ${i} missing valid 'key' property`,
435
403
  );
436
404
  }
437
405
  if (!action.value || typeof action.value !== "string") {
438
406
  throw new Error(`Action at index ${i} missing valid 'value' property`);
439
407
  }
440
- if (!action.label || typeof action.label !== "string") {
441
- throw new Error(`Action at index ${i} missing valid 'label' property`);
408
+ // related_field is optional - if not provided, action is form-level
409
+ if (action.related_field && typeof action.related_field !== "string") {
410
+ throw new Error(`Action at index ${i} has invalid 'related_field' property type`);
411
+ }
412
+ // Label is optional - will be resolved from schema or key
413
+ if (action.label && typeof action.label !== "string") {
414
+ throw new Error(`Action at index ${i} has invalid 'label' property type`);
442
415
  }
443
416
  }
444
417
 
@@ -480,43 +453,28 @@ function setupFormBuilder() {
480
453
  });
481
454
 
482
455
  // Action handler - display message when action button is clicked
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
-
456
+ // New system: value, key, related_field parameters
457
+ FormBuilder.setActionHandler((value, key, relatedField) => {
500
458
  console.log("Action clicked:", {
501
- label: actionLabel,
502
- value: actionValue,
459
+ value,
460
+ key,
503
461
  relatedField,
504
- system: arguments.length === 1 ? "schema-based" : "external",
462
+ type: relatedField ? "field-level" : "form-level",
505
463
  });
506
464
 
507
465
  // Show message to user (compatible with all environments)
508
466
  if (typeof window !== "undefined" && window.alert) {
509
467
  if (relatedField) {
510
468
  window.alert(
511
- `External Action: "${actionLabel}" clicked for field "${relatedField}" with value: ${actionValue}`,
469
+ `Field Action: "${key}" clicked for field "${relatedField}" with value: ${value}`,
512
470
  );
513
471
  } else {
514
- window.alert(`Schema Action: "${actionLabel}" clicked: ${actionValue}`);
472
+ window.alert(`Form Action: "${key}" clicked with value: ${value}`);
515
473
  }
516
474
  } else {
517
475
  console.log(
518
- `Demo action: ${actionLabel} clicked: ${actionValue}`,
519
- relatedField ? ` for field: ${relatedField}` : "",
476
+ `Demo action: ${key} clicked with value: ${value}`,
477
+ relatedField ? ` for field: ${relatedField}` : " (form-level)",
520
478
  );
521
479
  }
522
480
  });
@@ -553,8 +511,6 @@ function applyCurrentSchema() {
553
511
  return false;
554
512
  }
555
513
 
556
- // Build action value -> label map for efficient lookup
557
- actionLabelMap = buildActionLabelMap(schema);
558
514
 
559
515
  // Set mode based on toggle
560
516
  const isReadOnly = el.readOnlyToggle.checked;
@@ -801,25 +757,55 @@ el.clearActionsBtn.addEventListener("click", () => {
801
757
 
802
758
  // Example external actions for demonstration
803
759
  const EXAMPLE_ACTIONS = [
760
+ // Field-level actions using predefined labels from schema
761
+ {
762
+ related_field: "cover",
763
+ key: "retry",
764
+ value: "regenerate_cover_image", // Specific action value for handler
765
+ },
766
+ {
767
+ related_field: "cover",
768
+ key: "enhance",
769
+ value: "enhance_cover_quality", // Specific action value for handler
770
+ },
771
+ {
772
+ related_field: "cover",
773
+ key: "crop",
774
+ value: "auto_crop_cover", // Specific action value for handler
775
+ },
776
+ // Field-level actions with custom labels
804
777
  {
805
778
  related_field: "title[0]",
806
- value: "generate-title",
807
- label: "🤖 Generate Title",
779
+ key: "generate",
780
+ value: "ai_generate_title",
781
+ label: "🤖 Generate Title", // Custom label overrides schema
808
782
  },
809
783
  {
810
784
  related_field: "description",
811
- value: "improve-description",
812
- label: " Improve Description",
785
+ key: "improve",
786
+ value: "ai_improve_description", // Key will be used as fallback label
813
787
  },
814
788
  {
815
789
  related_field: "slides[0].title",
816
- value: "optimize-slide-title",
817
- label: "🎯 Optimize Slide Title",
790
+ key: "optimize",
791
+ value: "ai_optimize_slide_title",
792
+ label: "🎯 Optimize Slide Title", // Custom label
818
793
  },
794
+ // Form-level actions (no related_field)
819
795
  {
820
- related_field: "cover",
821
- value: "analyze-image",
822
- label: "🔍 Analyze Image",
796
+ key: "save-draft",
797
+ value: "save_form_draft",
798
+ label: "💾 Save Draft",
799
+ },
800
+ {
801
+ key: "preview",
802
+ value: "preview_infographic",
803
+ label: "👁️ Preview",
804
+ },
805
+ {
806
+ key: "export",
807
+ value: "export_json_data",
808
+ label: "📄 Export JSON",
823
809
  },
824
810
  ];
825
811
 
@@ -290,50 +290,8 @@ function renderElement(element, ctx) {
290
290
  }
291
291
  }
292
292
 
293
- // Add action buttons in readonly mode
294
- if (
295
- state.config.readonly &&
296
- element.actions &&
297
- Array.isArray(element.actions) &&
298
- element.actions.length > 0
299
- ) {
300
- const actionsContainer = document.createElement("div");
301
- actionsContainer.className = "mt-3 flex flex-wrap gap-2";
302
-
303
- element.actions.forEach((action) => {
304
- if (action.value && action.label) {
305
- const actionBtn = document.createElement("button");
306
- actionBtn.type = "button";
307
- actionBtn.className =
308
- "px-3 py-2 text-sm border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors";
309
- actionBtn.textContent = action.label;
310
-
311
- actionBtn.addEventListener("click", (e) => {
312
- e.preventDefault();
313
- e.stopPropagation();
314
-
315
- if (
316
- state.config.actionHandler &&
317
- typeof state.config.actionHandler === "function"
318
- ) {
319
- // For schema-based actions (old system), call with just the value for backward compatibility
320
- // Check if the handler expects 2 parameters (new system) or 1 parameter (old system)
321
- if (state.config.actionHandler.length > 1) {
322
- // New system: pass related_field (element key) and value
323
- state.config.actionHandler(element.key, action.value);
324
- } else {
325
- // Old system: pass only value for backward compatibility
326
- state.config.actionHandler(action.value);
327
- }
328
- }
329
- });
330
-
331
- actionsContainer.appendChild(actionBtn);
332
- }
333
- });
334
-
335
- wrapper.appendChild(actionsContainer);
336
- }
293
+ // Actions are now only rendered via external actions system in renderExternalActions()
294
+ // element.actions are only used for label lookup, not direct rendering
337
295
 
338
296
  return wrapper;
339
297
  }
@@ -597,6 +555,23 @@ async function renderFilePreview(container, resourceId, options = {}) {
597
555
  }
598
556
  }
599
557
 
558
+ function renderThumbnailForResource(slot, rid, meta) {
559
+ const url = state.config.getThumbnail(rid);
560
+ if (url) {
561
+ const img = document.createElement("img");
562
+ img.className = "w-full h-full object-contain";
563
+ img.alt = meta.name;
564
+ img.src = url;
565
+ slot.appendChild(img);
566
+ } else {
567
+ slot.innerHTML = `<div class="flex flex-col items-center justify-center h-full text-gray-400">
568
+ <svg class="w-12 h-12" fill="currentColor" viewBox="0 0 24 24">
569
+ <path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
570
+ </svg>
571
+ </div>`;
572
+ }
573
+ }
574
+
600
575
  function renderResourcePills(container, rids, onRemove) {
601
576
  clear(container);
602
577
 
@@ -721,21 +696,7 @@ function renderResourcePills(container, rids, onRemove) {
721
696
  slot.appendChild(img);
722
697
  } else if (state.config.getThumbnail) {
723
698
  // Use getThumbnail for uploaded files
724
- const img = document.createElement("img");
725
- img.className = "w-full h-full object-contain";
726
- img.alt = meta.name;
727
-
728
- const url = state.config.getThumbnail(rid);
729
- if (url) {
730
- img.src = url;
731
- slot.appendChild(img);
732
- } else {
733
- slot.innerHTML = `<div class="flex flex-col items-center justify-center h-full text-gray-400">
734
- <svg class="w-12 h-12" fill="currentColor" viewBox="0 0 24 24">
735
- <path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
736
- </svg>
737
- </div>`;
738
- }
699
+ renderThumbnailForResource(slot, rid, meta);
739
700
  } else {
740
701
  slot.innerHTML = `<div class="flex flex-col items-center justify-center h-full text-gray-400">
741
702
  <svg class="w-12 h-12" fill="currentColor" viewBox="0 0 24 24">
@@ -985,21 +946,48 @@ function findFormElementByFieldPath(fieldPath) {
985
946
 
986
947
  if (!state.formRoot) return null;
987
948
 
988
- // Try exact match first
989
- let element = state.formRoot.querySelector(`[name="${fieldPath}"]`);
990
- if (element) return element;
949
+ // In edit mode, try to find input elements with name attributes
950
+ if (!state.config.readonly) {
951
+ // Try exact match first
952
+ let element = state.formRoot.querySelector(`[name="${fieldPath}"]`);
953
+ if (element) return element;
991
954
 
992
- // Try with array notation variations
993
- const variations = [
994
- fieldPath,
995
- fieldPath.replace(/\[(\d+)\]/g, "[$1]"), // normalize array notation
996
- fieldPath.replace(/\./g, "[") +
997
- "]".repeat((fieldPath.match(/\./g) || []).length), // convert dots to brackets
998
- ];
955
+ // Try with array notation variations
956
+ const variations = [
957
+ fieldPath,
958
+ fieldPath.replace(/\[(\d+)\]/g, "[$1]"), // normalize array notation
959
+ fieldPath.replace(/\./g, "[") +
960
+ "]".repeat((fieldPath.match(/\./g) || []).length), // convert dots to brackets
961
+ ];
962
+
963
+ for (const variation of variations) {
964
+ element = state.formRoot.querySelector(`[name="${variation}"]`);
965
+ if (element) return element;
966
+ }
967
+ }
999
968
 
1000
- for (const variation of variations) {
1001
- element = state.formRoot.querySelector(`[name="${variation}"]`);
1002
- if (element) return element;
969
+ // In readonly mode, or if no input found, look for field wrappers using data attributes
970
+ // Find the schema element for this field path to match against rendered fields
971
+ const schemaElement = findSchemaElement(fieldPath);
972
+ if (!schemaElement) return null;
973
+
974
+ // Look for field wrappers that contain the field key
975
+ const fieldWrappers = state.formRoot.querySelectorAll('.fb-field-wrapper');
976
+ for (const wrapper of fieldWrappers) {
977
+ // Try to find a label or element that matches this field
978
+ const labelText = schemaElement.label || schemaElement.key;
979
+ const labelElement = wrapper.querySelector('label');
980
+ if (labelElement && (labelElement.textContent === labelText || labelElement.textContent === `${labelText}*`)) {
981
+ // Create a dummy element for the field so actions can attach
982
+ let fieldElement = wrapper.querySelector('.field-placeholder');
983
+ if (!fieldElement) {
984
+ fieldElement = document.createElement('div');
985
+ fieldElement.className = 'field-placeholder';
986
+ fieldElement.style.display = 'none';
987
+ wrapper.appendChild(fieldElement);
988
+ }
989
+ return fieldElement;
990
+ }
1003
991
  }
1004
992
 
1005
993
  return null;
@@ -1008,14 +996,32 @@ function findFormElementByFieldPath(fieldPath) {
1008
996
  function renderExternalActions() {
1009
997
  if (!state.externalActions || !Array.isArray(state.externalActions)) return;
1010
998
 
999
+ // Group actions by related_field (null for form-level actions)
1000
+ const actionsByField = new Map();
1001
+ const formLevelActions = [];
1002
+
1011
1003
  state.externalActions.forEach((action) => {
1012
- if (!action.related_field || !action.value || !action.label) return;
1004
+ if (!action.key || !action.value) return;
1013
1005
 
1006
+ if (!action.related_field) {
1007
+ // Form-level action
1008
+ formLevelActions.push(action);
1009
+ } else {
1010
+ // Field-level action
1011
+ if (!actionsByField.has(action.related_field)) {
1012
+ actionsByField.set(action.related_field, []);
1013
+ }
1014
+ actionsByField.get(action.related_field).push(action);
1015
+ }
1016
+ });
1017
+
1018
+ // Render field-level actions
1019
+ actionsByField.forEach((actions, fieldPath) => {
1014
1020
  // Find the form element for this related field
1015
- const fieldElement = findFormElementByFieldPath(action.related_field);
1021
+ const fieldElement = findFormElementByFieldPath(fieldPath);
1016
1022
  if (!fieldElement) {
1017
1023
  console.warn(
1018
- `External action: Could not find form element for field "${action.related_field}"`,
1024
+ `External action: Could not find form element for field "${fieldPath}"`,
1019
1025
  );
1020
1026
  return;
1021
1027
  }
@@ -1028,25 +1034,91 @@ function renderExternalActions() {
1028
1034
 
1029
1035
  if (!wrapper) {
1030
1036
  console.warn(
1031
- `External action: Could not find wrapper for field "${action.related_field}"`,
1037
+ `External action: Could not find wrapper for field "${fieldPath}"`,
1032
1038
  );
1033
1039
  return;
1034
1040
  }
1035
1041
 
1036
- // Check if we already added external actions to this wrapper
1037
- if (wrapper.querySelector(".external-actions-container")) return;
1042
+ // Remove any existing actions container
1043
+ const existingContainer = wrapper.querySelector(".external-actions-container");
1044
+ if (existingContainer) {
1045
+ existingContainer.remove();
1046
+ }
1038
1047
 
1039
1048
  // Create actions container
1040
1049
  const actionsContainer = document.createElement("div");
1041
1050
  actionsContainer.className =
1042
- "external-actions-container mt-4 flex flex-wrap gap-2";
1051
+ "external-actions-container mt-3 flex flex-wrap gap-2";
1052
+
1053
+ // Find the corresponding schema element for label lookup
1054
+ const schemaElement = findSchemaElement(fieldPath);
1055
+
1056
+ // Create action buttons
1057
+ actions.forEach((action) => {
1058
+ const actionBtn = document.createElement("button");
1059
+ actionBtn.type = "button";
1060
+ actionBtn.className =
1061
+ "bg-white text-gray-700 border border-gray-200 px-3 py-2 text-sm rounded-lg hover:bg-gray-50 hover:border-gray-300 transition-all duration-200 shadow-sm";
1062
+
1063
+ // Resolve action label with priority:
1064
+ // 1. Use explicit label from action if provided
1065
+ // 2. Try to find label from schema element labels using key
1066
+ // 3. Fall back to using key as label
1067
+ const resolvedLabel = resolveActionLabel(action.key, action.label, schemaElement);
1068
+ actionBtn.textContent = resolvedLabel;
1069
+
1070
+ actionBtn.addEventListener("click", (e) => {
1071
+ e.preventDefault();
1072
+ e.stopPropagation();
1073
+
1074
+ if (
1075
+ state.config.actionHandler &&
1076
+ typeof state.config.actionHandler === "function"
1077
+ ) {
1078
+ // Call with value, key, and related_field for the new actions system
1079
+ state.config.actionHandler(action.value, action.key, action.related_field);
1080
+ }
1081
+ });
1082
+
1083
+ actionsContainer.appendChild(actionBtn);
1084
+ });
1085
+
1086
+ wrapper.appendChild(actionsContainer);
1087
+ });
1088
+
1089
+ // Render form-level actions at the bottom of the form
1090
+ if (formLevelActions.length > 0) {
1091
+ renderFormLevelActions(formLevelActions);
1092
+ }
1093
+ }
1094
+
1095
+ function renderFormLevelActions(actions) {
1096
+ if (!state.formRoot) return;
1043
1097
 
1044
- // Create action button
1098
+ // Remove any existing form-level actions container
1099
+ const existingContainer = state.formRoot.querySelector(".form-level-actions-container");
1100
+ if (existingContainer) {
1101
+ existingContainer.remove();
1102
+ }
1103
+
1104
+ // Create form-level actions container
1105
+ const actionsContainer = document.createElement("div");
1106
+ actionsContainer.className =
1107
+ "form-level-actions-container mt-6 pt-4 border-t border-gray-200 flex flex-wrap gap-3 justify-center";
1108
+
1109
+ // Create action buttons
1110
+ actions.forEach((action) => {
1045
1111
  const actionBtn = document.createElement("button");
1046
1112
  actionBtn.type = "button";
1047
1113
  actionBtn.className =
1048
- "px-3 py-2 text-sm border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors";
1049
- actionBtn.textContent = action.label;
1114
+ "bg-white text-gray-700 border border-gray-200 px-4 py-2 text-sm font-medium rounded-lg hover:bg-gray-50 hover:border-gray-300 transition-all duration-200 shadow-sm";
1115
+
1116
+ // Resolve action label with priority:
1117
+ // 1. Use explicit label from action if provided
1118
+ // 2. Try to find label from schema element labels using key
1119
+ // 3. Fall back to using key as label
1120
+ const resolvedLabel = resolveActionLabel(action.key, action.label, null);
1121
+ actionBtn.textContent = resolvedLabel;
1050
1122
 
1051
1123
  actionBtn.addEventListener("click", (e) => {
1052
1124
  e.preventDefault();
@@ -1056,14 +1128,58 @@ function renderExternalActions() {
1056
1128
  state.config.actionHandler &&
1057
1129
  typeof state.config.actionHandler === "function"
1058
1130
  ) {
1059
- // Call with both related_field and value for the new actions system
1060
- state.config.actionHandler(action.related_field, action.value);
1131
+ // Call with value, key, and null related_field for form-level actions
1132
+ state.config.actionHandler(action.value, action.key, null);
1061
1133
  }
1062
1134
  });
1063
1135
 
1064
1136
  actionsContainer.appendChild(actionBtn);
1065
- wrapper.appendChild(actionsContainer);
1066
1137
  });
1138
+
1139
+ // Append to form root
1140
+ state.formRoot.appendChild(actionsContainer);
1141
+ }
1142
+
1143
+ // Helper function to resolve action label
1144
+ function resolveActionLabel(actionKey, externalLabel, schemaElement) {
1145
+ // 1. Try to find label from predefined actions in schema using key (highest priority)
1146
+ if (schemaElement && schemaElement.actions && Array.isArray(schemaElement.actions)) {
1147
+ const predefinedAction = schemaElement.actions.find(a => a.key === actionKey);
1148
+ if (predefinedAction && predefinedAction.label) {
1149
+ return predefinedAction.label;
1150
+ }
1151
+ }
1152
+
1153
+ // 2. Use explicit label from external action if provided
1154
+ if (externalLabel) {
1155
+ return externalLabel;
1156
+ }
1157
+
1158
+ // 3. Fall back to using key as label
1159
+ return actionKey;
1160
+ }
1161
+
1162
+ // Helper function to find schema element by field path
1163
+ function findSchemaElement(fieldPath) {
1164
+ if (!state.schema || !state.schema.elements) return null;
1165
+
1166
+ let currentElements = state.schema.elements;
1167
+ let foundElement = null;
1168
+
1169
+ // Handle paths like 'a.b' or 'a[0].b' by looking for keys in sequence
1170
+ const keys = fieldPath.replace(/\[\d+\]/g, '').split('.').filter(Boolean);
1171
+
1172
+ for (const key of keys) {
1173
+ foundElement = currentElements.find(el => el.key === key);
1174
+ if (!foundElement) {
1175
+ return null; // Key not found at this level
1176
+ }
1177
+ if (foundElement.elements) {
1178
+ currentElements = foundElement.elements;
1179
+ }
1180
+ }
1181
+
1182
+ return foundElement;
1067
1183
  }
1068
1184
 
1069
1185
  function showTooltip(tooltipId, button) {
package/dist/index.html CHANGED
@@ -296,7 +296,7 @@
296
296
  id="actionsTextarea"
297
297
  class="w-full 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 resize-none flex-1"
298
298
  spellcheck="false"
299
- placeholder='[{"related_field": "fieldName", "value": "action-key", "label": "Button Label"}]'
299
+ placeholder='[{"key": "action-key", "value": "specific-value", "related_field": "fieldName", "label": "Button Label"}]'
300
300
  ></textarea>
301
301
  <div
302
302
  id="actionsErrors"
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.1.37",
6
+ "version": "0.1.38",
7
7
  "description": "A reusable JSON schema form builder library",
8
8
  "main": "dist/form-builder.js",
9
9
  "module": "dist/form-builder.js",