@dmitryvim/form-builder 0.2.29 → 0.2.30

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.
@@ -159,12 +159,14 @@ function validateSchema(schema) {
159
159
  allOutputKeys.add(textKey);
160
160
  allOutputKeys.add(filesKey);
161
161
  } else {
162
- if (allOutputKeys.has(el.key)) {
163
- errors.push(
164
- `${scopePath}: Element key "${el.key}" collides with a flatOutput richinput key`
165
- );
162
+ if (el.key) {
163
+ if (allOutputKeys.has(el.key)) {
164
+ errors.push(
165
+ `${scopePath}: Element key "${el.key}" collides with a flatOutput richinput key`
166
+ );
167
+ }
168
+ allOutputKeys.add(el.key);
166
169
  }
167
- allOutputKeys.add(el.key);
168
170
  }
169
171
  }
170
172
  }
@@ -174,9 +176,17 @@ function validateSchema(schema) {
174
176
  if (!element.type) {
175
177
  errors.push(`${elementPath}: missing type`);
176
178
  }
177
- if (!element.key) {
179
+ if (!element.key && element.type !== "markdown") {
178
180
  errors.push(`${elementPath}: missing key`);
179
181
  }
182
+ if (element.type === "markdown") {
183
+ const content = element.content;
184
+ if (typeof content !== "string") {
185
+ errors.push(
186
+ `${elementPath}: markdown element requires "content" to be a string (got ${content === null ? "null" : typeof content})`
187
+ );
188
+ }
189
+ }
180
190
  if (element.enableIf) {
181
191
  const enableIf = element.enableIf;
182
192
  if (!enableIf.key || typeof enableIf.key !== "string") {
@@ -269,6 +279,18 @@ function escapeHtml(text) {
269
279
  div.textContent = text;
270
280
  return div.innerHTML;
271
281
  }
282
+ function getElementLookupKey(element, state) {
283
+ if (element.key) {
284
+ return element.key;
285
+ }
286
+ const cached = state.syntheticElementIds.get(element);
287
+ if (cached !== void 0) {
288
+ return cached;
289
+ }
290
+ const id = `fb-synthetic-${state.syntheticElementIdCounter++}`;
291
+ state.syntheticElementIds.set(element, id);
292
+ return id;
293
+ }
272
294
  function pathJoin(base, key) {
273
295
  return base ? `${base}.${key}` : key;
274
296
  }
@@ -5531,7 +5553,7 @@ function updateSliderField(element, fieldPath, value, context) {
5531
5553
  function extractChildDefaults(elements) {
5532
5554
  const defaults = {};
5533
5555
  for (const child of elements) {
5534
- if ("default" in child && child.default !== void 0) {
5556
+ if (child.key && "default" in child && child.default !== void 0) {
5535
5557
  defaults[child.key] = child.default;
5536
5558
  }
5537
5559
  }
@@ -5628,8 +5650,8 @@ function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
5628
5650
  };
5629
5651
  element.elements.forEach((child) => {
5630
5652
  var _a2, _b2;
5631
- if (child.hidden || child.type === "hidden") {
5632
- const prefillVal = (_b2 = (_a2 = containerPrefill[child.key]) != null ? _a2 : child.default) != null ? _b2 : null;
5653
+ if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
5654
+ const prefillVal = (_b2 = (_a2 = containerPrefill[child.key]) != null ? _a2 : "default" in child ? child.default : null) != null ? _b2 : null;
5633
5655
  itemsWrap.appendChild(
5634
5656
  createHiddenInput(pathJoin(subCtx.path, child.key), prefillVal)
5635
5657
  );
@@ -5705,11 +5727,11 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
5705
5727
  }
5706
5728
  element.elements.forEach((child) => {
5707
5729
  var _a2;
5708
- if (child.hidden || child.type === "hidden") {
5730
+ if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
5709
5731
  childWrapper.appendChild(
5710
5732
  createHiddenInput(
5711
5733
  pathJoin(subCtx.path, child.key),
5712
- (_a2 = child.default) != null ? _a2 : null
5734
+ (_a2 = "default" in child ? child.default : null) != null ? _a2 : null
5713
5735
  )
5714
5736
  );
5715
5737
  } else {
@@ -5784,8 +5806,8 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
5784
5806
  }
5785
5807
  element.elements.forEach((child) => {
5786
5808
  var _a3, _b2;
5787
- if (child.hidden || child.type === "hidden") {
5788
- const prefillVal = (_b2 = (_a3 = prefillObj == null ? void 0 : prefillObj[child.key]) != null ? _a3 : child.default) != null ? _b2 : null;
5809
+ if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
5810
+ const prefillVal = (_b2 = (_a3 = prefillObj == null ? void 0 : prefillObj[child.key]) != null ? _a3 : "default" in child ? child.default : null) != null ? _b2 : null;
5789
5811
  childWrapper.appendChild(
5790
5812
  createHiddenInput(pathJoin(subCtx.path, child.key), prefillVal)
5791
5813
  );
@@ -5841,11 +5863,11 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
5841
5863
  }
5842
5864
  element.elements.forEach((child) => {
5843
5865
  var _a2;
5844
- if (child.hidden || child.type === "hidden") {
5866
+ if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
5845
5867
  childWrapper.appendChild(
5846
5868
  createHiddenInput(
5847
5869
  pathJoin(subCtx.path, child.key),
5848
- (_a2 = child.default) != null ? _a2 : null
5870
+ (_a2 = "default" in child ? child.default : null) != null ? _a2 : null
5849
5871
  )
5850
5872
  );
5851
5873
  } else {
@@ -6035,6 +6057,7 @@ function updateContainerField(element, fieldPath, value, context) {
6035
6057
  if (isPlainObject(itemValue)) {
6036
6058
  element.elements.forEach((childElement) => {
6037
6059
  var _a, _b;
6060
+ if (childElement.type === "markdown" || !childElement.key) return;
6038
6061
  const childPath = `${fieldPath}[${index}].${childElement.key}`;
6039
6062
  if (childElement.type === "richinput" && childElement.flatOutput) {
6040
6063
  const richChild = childElement;
@@ -6075,6 +6098,7 @@ function updateContainerField(element, fieldPath, value, context) {
6075
6098
  }
6076
6099
  element.elements.forEach((childElement) => {
6077
6100
  var _a, _b;
6101
+ if (childElement.type === "markdown" || !childElement.key) return;
6078
6102
  const childPath = `${fieldPath}.${childElement.key}`;
6079
6103
  if (childElement.type === "richinput" && childElement.flatOutput) {
6080
6104
  const richChild = childElement;
@@ -8842,6 +8866,250 @@ function updateRichInputField(element, fieldPath, value, context) {
8842
8866
  }
8843
8867
  }
8844
8868
 
8869
+ // src/components/markdown/snarkdown.ts
8870
+ var TAGS = {
8871
+ "": ["<em>", "</em>"],
8872
+ _: ["<strong>", "</strong>"],
8873
+ "*": ["<strong>", "</strong>"],
8874
+ "~": ["<s>", "</s>"],
8875
+ "\n": ["<br />"],
8876
+ " ": ["<br />"],
8877
+ "-": ["<hr />"]
8878
+ };
8879
+ function outdent(str) {
8880
+ return str.replace(
8881
+ RegExp("^" + (str.match(/^(\t| )+/) || "")[0], "gm"),
8882
+ ""
8883
+ );
8884
+ }
8885
+ function encodeAttr(str) {
8886
+ return (str + "").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
8887
+ }
8888
+ function parse(md, prevLinks) {
8889
+ const tokenizer = /((?:^|\n+)(?:\n---+|\*[ ]\*(?:[ ]\*)+)\n)|(?:^```[ ]*(\w*)\n([\s\S]*?)\n```$)|((?:(?:^|\n+)(?:\t|[ ]{2,}).+)+\n*)|((?:(?:^|\n)([>*+-]|\d+\.)\s+.*)+)|(?:!\[([^\]]*?)\]\(([^)]+?)\))|(\[)|(\](?:\(([^)]+?)\))?)|(?:(?:^|\n+)([^\s].*)\n(-{3,}|={3,})(?:\n+|$))|(?:(?:^|\n+)(#{1,6})\s*(.+)(?:\n+|$))|(?:`([^`].*?)`)|([ ]{2}\n\n*|\n{2,}|__|\*\*|[_*]|~~)/gm;
8890
+ const context = [];
8891
+ let out = "";
8892
+ const links = prevLinks || {};
8893
+ let last = 0;
8894
+ let chunk;
8895
+ let prev;
8896
+ let token;
8897
+ let inner;
8898
+ let t2;
8899
+ function tag(token2) {
8900
+ const desc = TAGS[token2[1] || ""];
8901
+ const end = context[context.length - 1] === token2;
8902
+ if (!desc) return token2;
8903
+ if (!desc[1]) return desc[0];
8904
+ if (end) context.pop();
8905
+ else context.push(token2);
8906
+ return desc[end ? 1 : 0];
8907
+ }
8908
+ function flush() {
8909
+ let str = "";
8910
+ while (context.length) str += tag(context[context.length - 1]);
8911
+ return str;
8912
+ }
8913
+ md = md.replace(/^\[(.+?)\]:\s*(.+)$/gm, (_s, name, url) => {
8914
+ links[name.toLowerCase()] = url;
8915
+ return "";
8916
+ }).replace(/^\n+|\n+$/g, "");
8917
+ while (token = tokenizer.exec(md)) {
8918
+ prev = md.substring(last, token.index);
8919
+ last = tokenizer.lastIndex;
8920
+ chunk = token[0];
8921
+ if (prev.match(/[^\\](\\\\)*\\$/)) ; else if (t2 = token[3] || token[4]) {
8922
+ chunk = '<pre class="code ' + (token[4] ? "poetry" : token[2].toLowerCase()) + '"><code' + (token[2] ? ` class="language-${token[2].toLowerCase()}"` : "") + ">" + outdent(encodeAttr(t2).replace(/^\n+|\n+$/g, "")) + "</code></pre>";
8923
+ } else if (t2 = token[6]) {
8924
+ if (t2.match(/\./)) {
8925
+ token[5] = token[5].replace(/^\d+/gm, "");
8926
+ }
8927
+ inner = parse(outdent(token[5].replace(/^\s*[>*+.-]/gm, "")));
8928
+ if (t2 === ">") t2 = "blockquote";
8929
+ else {
8930
+ t2 = t2.match(/\./) ? "ol" : "ul";
8931
+ inner = inner.replace(/^(.*)(\n|$)/gm, "<li>$1</li>");
8932
+ }
8933
+ chunk = "<" + t2 + ">" + inner + "</" + t2 + ">";
8934
+ } else if (token[8]) {
8935
+ chunk = `<img src="${encodeAttr(token[8])}" alt="${encodeAttr(token[7])}">`;
8936
+ } else if (token[10]) {
8937
+ out = out.replace(
8938
+ "<a>",
8939
+ `<a href="${encodeAttr(token[11] || links[prev.toLowerCase()])}">`
8940
+ );
8941
+ chunk = flush() + "</a>";
8942
+ } else if (token[9]) {
8943
+ chunk = "<a>";
8944
+ } else if (token[12] || token[14]) {
8945
+ t2 = "h" + (token[14] ? token[14].length : token[13] > "=" ? 1 : 2);
8946
+ chunk = "<" + t2 + ">" + parse(token[12] || token[15], links) + "</" + t2 + ">";
8947
+ } else if (token[16]) {
8948
+ chunk = "<code>" + encodeAttr(token[16]) + "</code>";
8949
+ } else if (token[17] || token[1]) {
8950
+ chunk = tag(token[17] || "--");
8951
+ }
8952
+ out += prev;
8953
+ out += chunk;
8954
+ }
8955
+ return (out + md.substring(last) + flush()).replace(/^\n+|\n+$/g, "");
8956
+ }
8957
+
8958
+ // src/components/markdown/render.ts
8959
+ var STYLE_ID2 = "fb-markdown-styles";
8960
+ function ensureMarkdownStyles() {
8961
+ if (typeof document === "undefined") return;
8962
+ if (document.getElementById(STYLE_ID2)) return;
8963
+ const style = document.createElement("style");
8964
+ style.id = STYLE_ID2;
8965
+ style.setAttribute("data-fb-markdown-styles", "true");
8966
+ style.textContent = `
8967
+ .fb-markdown {
8968
+ font-family: var(--fb-font-family, inherit);
8969
+ font-size: var(--fb-font-size, 1rem);
8970
+ color: var(--fb-text-color, #1f2937);
8971
+ line-height: 1.6;
8972
+ }
8973
+ .fb-markdown h1,
8974
+ .fb-markdown h2,
8975
+ .fb-markdown h3,
8976
+ .fb-markdown h4,
8977
+ .fb-markdown h5,
8978
+ .fb-markdown h6 {
8979
+ font-weight: var(--fb-font-weight-medium, 600);
8980
+ color: var(--fb-text-color, #1f2937);
8981
+ margin-top: 0.75em;
8982
+ margin-bottom: 0.25em;
8983
+ }
8984
+ .fb-markdown h1 { font-size: 1.5rem; }
8985
+ .fb-markdown h2 { font-size: 1.25rem; }
8986
+ .fb-markdown h3 { font-size: 1.1rem; }
8987
+ .fb-markdown h4,
8988
+ .fb-markdown h5,
8989
+ .fb-markdown h6 { font-size: 1rem; }
8990
+ .fb-markdown p {
8991
+ margin-top: 0;
8992
+ margin-bottom: 0.5em;
8993
+ }
8994
+ .fb-markdown ul,
8995
+ .fb-markdown ol {
8996
+ margin: 0.25em 0 0.5em 1.5em;
8997
+ padding: 0;
8998
+ }
8999
+ .fb-markdown li {
9000
+ margin-bottom: 0.15em;
9001
+ }
9002
+ .fb-markdown a {
9003
+ color: var(--fb-primary-color, #2563eb);
9004
+ text-decoration: underline;
9005
+ }
9006
+ .fb-markdown a:hover {
9007
+ opacity: 0.8;
9008
+ }
9009
+ .fb-markdown code {
9010
+ background: var(--fb-input-background-color, #f3f4f6);
9011
+ border-radius: 3px;
9012
+ padding: 0.1em 0.35em;
9013
+ font-size: 0.9em;
9014
+ font-family: monospace;
9015
+ }
9016
+ .fb-markdown pre {
9017
+ background: var(--fb-input-background-color, #f3f4f6);
9018
+ border-radius: var(--fb-border-radius, 0.375rem);
9019
+ padding: 0.75em 1em;
9020
+ overflow-x: auto;
9021
+ margin: 0.5em 0;
9022
+ }
9023
+ .fb-markdown pre code {
9024
+ background: none;
9025
+ padding: 0;
9026
+ border-radius: 0;
9027
+ font-size: inherit;
9028
+ }
9029
+ .fb-markdown blockquote {
9030
+ border-left: 3px solid var(--fb-border-color, #d1d5db);
9031
+ margin: 0.5em 0;
9032
+ padding-left: 1em;
9033
+ color: var(--fb-text-secondary-color, #6b7280);
9034
+ }
9035
+ .fb-markdown hr {
9036
+ border: none;
9037
+ border-top: 1px solid var(--fb-border-color, #d1d5db);
9038
+ margin: 0.75em 0;
9039
+ }
9040
+ .fb-markdown strong { font-weight: var(--fb-font-weight-medium, 600); }
9041
+ .fb-markdown em { font-style: italic; }
9042
+ .fb-markdown s { text-decoration: line-through; }
9043
+ `;
9044
+ document.head.appendChild(style);
9045
+ }
9046
+ var ANCHOR_DANGEROUS_SCHEMES = [
9047
+ "javascript:",
9048
+ "data:",
9049
+ "vbscript:",
9050
+ "blob:"
9051
+ ];
9052
+ var IMG_DANGEROUS_SCHEMES = ["javascript:", "vbscript:", "blob:"];
9053
+ function isImgSrcDangerous(normalized) {
9054
+ if (IMG_DANGEROUS_SCHEMES.some((scheme) => normalized.startsWith(scheme))) {
9055
+ return true;
9056
+ }
9057
+ if (normalized.startsWith("data:") && !normalized.startsWith("data:image/")) {
9058
+ return true;
9059
+ }
9060
+ return false;
9061
+ }
9062
+ function escapeRawHtml(content) {
9063
+ return content.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
9064
+ }
9065
+ function sanitizeElements(container) {
9066
+ const anchors = container.querySelectorAll("a[href]");
9067
+ anchors.forEach((a) => {
9068
+ var _a;
9069
+ const href = (_a = a.getAttribute("href")) != null ? _a : "";
9070
+ const normalized = href.trim().toLowerCase();
9071
+ const isDangerous = ANCHOR_DANGEROUS_SCHEMES.some(
9072
+ (scheme) => normalized.startsWith(scheme)
9073
+ );
9074
+ if (isDangerous) {
9075
+ a.setAttribute("href", "#");
9076
+ }
9077
+ a.setAttribute("target", "_blank");
9078
+ a.setAttribute("rel", "noopener noreferrer");
9079
+ });
9080
+ const images = container.querySelectorAll("img[src]");
9081
+ images.forEach((img) => {
9082
+ var _a;
9083
+ const src = (_a = img.getAttribute("src")) != null ? _a : "";
9084
+ const normalized = src.trim().toLowerCase();
9085
+ if (isImgSrcDangerous(normalized)) {
9086
+ img.setAttribute("src", "");
9087
+ }
9088
+ });
9089
+ }
9090
+ function renderMarkdown(element, _ctx, parent) {
9091
+ if (typeof element.content !== "string") {
9092
+ throw new Error(
9093
+ `renderMarkdown: markdown element${element.key ? ` "${element.key}"` : ""} requires "content" to be a string (got ${element.content === null ? "null" : typeof element.content})`
9094
+ );
9095
+ }
9096
+ ensureMarkdownStyles();
9097
+ const wrapper = document.createElement("div");
9098
+ wrapper.className = "fb-markdown";
9099
+ const escaped = escapeRawHtml(element.content);
9100
+ wrapper.innerHTML = parse(escaped);
9101
+ sanitizeElements(wrapper);
9102
+ parent.appendChild(wrapper);
9103
+ return wrapper;
9104
+ }
9105
+
9106
+ // src/components/markdown/index.ts
9107
+ function validateMarkdown(_element, _key, _context) {
9108
+ return { value: void 0, errors: [], skip: true };
9109
+ }
9110
+ function updateMarkdown(_element, _fieldPath, _value, _context) {
9111
+ }
9112
+
8845
9113
  // src/components/index.ts
8846
9114
  function showTooltip(tooltipId, button) {
8847
9115
  const tooltip = document.getElementById(tooltipId);
@@ -9073,9 +9341,10 @@ function setupEnableIfListeners(wrapper, element, ctx) {
9073
9341
  });
9074
9342
  }
9075
9343
  function createFieldLabel(element) {
9344
+ var _a, _b;
9076
9345
  const title = document.createElement("label");
9077
9346
  title.className = "text-sm font-medium text-gray-900";
9078
- title.textContent = element.label || element.key;
9347
+ title.textContent = (_b = (_a = element.label) != null ? _a : element.key) != null ? _b : "";
9079
9348
  if (element.required) {
9080
9349
  const req = document.createElement("span");
9081
9350
  req.className = "text-red-500 ml-1";
@@ -9203,6 +9472,29 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
9203
9472
  }
9204
9473
  }
9205
9474
  function renderElement2(element, ctx) {
9475
+ if (element.type === "markdown") {
9476
+ if (element.hidden === true) {
9477
+ const placeholder = document.createElement("div");
9478
+ placeholder.style.display = "none";
9479
+ placeholder.setAttribute("data-fb-hidden-markdown", "true");
9480
+ return placeholder;
9481
+ }
9482
+ const initiallyDisabled2 = shouldDisableElement(element, ctx);
9483
+ const outerWrapper = document.createElement("div");
9484
+ outerWrapper.className = "mb-6 fb-field-wrapper fb-markdown-wrapper";
9485
+ outerWrapper.setAttribute(
9486
+ "data-field-key",
9487
+ getElementLookupKey(element, ctx.state)
9488
+ );
9489
+ renderMarkdown(element, ctx, outerWrapper);
9490
+ if (initiallyDisabled2) {
9491
+ outerWrapper.style.display = "none";
9492
+ outerWrapper.classList.add("fb-field-wrapper-disabled");
9493
+ outerWrapper.setAttribute("data-conditionally-disabled", "true");
9494
+ }
9495
+ setupEnableIfListeners(outerWrapper, element, ctx);
9496
+ return outerWrapper;
9497
+ }
9206
9498
  const initiallyDisabled = shouldDisableElement(element, ctx);
9207
9499
  const wrapper = document.createElement("div");
9208
9500
  wrapper.className = "mb-6 fb-field-wrapper";
@@ -9413,7 +9705,9 @@ function createInstanceState(config) {
9413
9705
  translations: mergedTranslations
9414
9706
  },
9415
9707
  debounceTimer: null,
9416
- prefill: {}
9708
+ prefill: {},
9709
+ syntheticElementIds: /* @__PURE__ */ new WeakMap(),
9710
+ syntheticElementIdCounter: 0
9417
9711
  };
9418
9712
  }
9419
9713
  function generateInstanceId() {
@@ -9691,6 +9985,11 @@ var componentRegistry = {
9691
9985
  // Legacy type: `type: "hidden"` — reads/writes DOM <input type="hidden"> element
9692
9986
  validate: validateHiddenElement,
9693
9987
  update: updateHiddenField
9988
+ },
9989
+ markdown: {
9990
+ // Display-only element — no value, no errors, skip from form data
9991
+ validate: validateMarkdown,
9992
+ update: updateMarkdown
9694
9993
  }
9695
9994
  };
9696
9995
  function getComponentOperations(elementType) {
@@ -10119,7 +10418,7 @@ var FormBuilderInstance = class {
10119
10418
  }
10120
10419
  schema.elements.forEach((element) => {
10121
10420
  var _a, _b;
10122
- if (element.hidden || element.type === "hidden") {
10421
+ if (element.type !== "markdown" && (element.hidden || element.type === "hidden")) {
10123
10422
  const val = (_b = (_a = prefill == null ? void 0 : prefill[element.key]) != null ? _a : element.default) != null ? _b : null;
10124
10423
  fieldsWrapper.appendChild(createHiddenInput(element.key, val));
10125
10424
  return;
@@ -10154,7 +10453,8 @@ var FormBuilderInstance = class {
10154
10453
  const errors = [];
10155
10454
  const data = {};
10156
10455
  const validateElement2 = (element, ctx, customScopeRoot = null) => {
10157
- const key = element.key;
10456
+ var _a;
10457
+ const key = (_a = element.key) != null ? _a : "";
10158
10458
  const scopeRoot = customScopeRoot || this.state.formRoot;
10159
10459
  const componentContext = {
10160
10460
  scopeRoot,
@@ -10172,7 +10472,8 @@ var FormBuilderInstance = class {
10172
10472
  errors.push(...componentResult.errors);
10173
10473
  return {
10174
10474
  value: componentResult.value,
10175
- spread: !!componentResult.spread
10475
+ spread: !!componentResult.spread,
10476
+ skip: !!componentResult.skip
10176
10477
  };
10177
10478
  }
10178
10479
  console.warn(`Unknown field type "${element.type}" for key "${key}"`);
@@ -10194,6 +10495,9 @@ var FormBuilderInstance = class {
10194
10495
  );
10195
10496
  }
10196
10497
  }
10498
+ if (element.type === "markdown") {
10499
+ return;
10500
+ }
10197
10501
  if (element.hidden || element.type === "hidden") {
10198
10502
  const hiddenInput = this.state.formRoot.querySelector(
10199
10503
  `input[type="hidden"][data-hidden-field="true"][name="${element.key}"]`
@@ -10206,9 +10510,10 @@ var FormBuilderInstance = class {
10206
10510
  }
10207
10511
  } else {
10208
10512
  const result = validateElement2(element, { path: "" });
10513
+ if (result.skip) return;
10209
10514
  if (result.spread && result.value !== null && typeof result.value === "object") {
10210
10515
  Object.assign(data, result.value);
10211
- } else {
10516
+ } else if (element.key) {
10212
10517
  data[element.key] = result.value;
10213
10518
  }
10214
10519
  }
@@ -10284,6 +10589,7 @@ var FormBuilderInstance = class {
10284
10589
  buildHiddenFieldsData(elements) {
10285
10590
  const data = {};
10286
10591
  for (const element of elements) {
10592
+ if (element.type === "markdown" || !element.key) continue;
10287
10593
  const key = element.key;
10288
10594
  if (element.hidden && element.default !== void 0) {
10289
10595
  data[key] = element.default;
@@ -10405,6 +10711,72 @@ var FormBuilderInstance = class {
10405
10711
  );
10406
10712
  }
10407
10713
  }
10714
+ /**
10715
+ * Find the field wrapper DOM element for a given element+path combo.
10716
+ * Used by reevaluateConditionalFields.
10717
+ */
10718
+ findFieldWrapper(element, currentPath) {
10719
+ const formRoot = this.state.formRoot;
10720
+ const lookupKey = getElementLookupKey(element, this.state);
10721
+ if (!currentPath) {
10722
+ return formRoot.querySelector(`[data-field-key="${lookupKey}"]`);
10723
+ }
10724
+ const pathMatch = currentPath.match(/^(.+)\[(\d+)\]$/);
10725
+ if (pathMatch) {
10726
+ const containerEl2 = formRoot.querySelector(
10727
+ `[data-container-item="${pathMatch[1]}[${pathMatch[2]}]"]`
10728
+ );
10729
+ return containerEl2 ? containerEl2.querySelector(`[data-field-key="${lookupKey}"]`) : null;
10730
+ }
10731
+ const containerEl = formRoot.querySelector(
10732
+ `[data-container="${currentPath}"]`
10733
+ );
10734
+ return containerEl ? containerEl.querySelector(`[data-field-key="${lookupKey}"]`) : null;
10735
+ }
10736
+ /**
10737
+ * Apply enableIf show/hide logic to a single field wrapper.
10738
+ * Extracted to reduce cyclomatic complexity of checkElements.
10739
+ */
10740
+ applyEnableIfVisibility(element, wrapper, currentPath, fullPath, formData) {
10741
+ var _a, _b, _c, _d;
10742
+ try {
10743
+ const scope = (_a = element.enableIf.scope) != null ? _a : "relative";
10744
+ const containerData = scope === "relative" && currentPath ? getValueByPath(formData, currentPath) : void 0;
10745
+ const shouldEnable = evaluateEnableCondition(
10746
+ element.enableIf,
10747
+ formData,
10748
+ containerData
10749
+ );
10750
+ const isCurrentlyDisabled = wrapper.getAttribute("data-conditionally-disabled") === "true";
10751
+ if (shouldEnable && isCurrentlyDisabled) {
10752
+ const containerPrefill = currentPath ? getValueByPath(formData, currentPath) : formData;
10753
+ const prefillContext = containerPrefill && typeof containerPrefill === "object" ? containerPrefill : {};
10754
+ const newWrapper = renderElement2(element, {
10755
+ path: currentPath,
10756
+ prefill: prefillContext,
10757
+ formData,
10758
+ state: this.state,
10759
+ instance: this
10760
+ });
10761
+ (_b = wrapper.parentNode) == null ? void 0 : _b.replaceChild(newWrapper, wrapper);
10762
+ } else if (!shouldEnable && !isCurrentlyDisabled) {
10763
+ const disabledWrapper = document.createElement("div");
10764
+ disabledWrapper.className = "fb-field-wrapper-disabled";
10765
+ disabledWrapper.style.display = "none";
10766
+ disabledWrapper.setAttribute(
10767
+ "data-field-key",
10768
+ getElementLookupKey(element, this.state)
10769
+ );
10770
+ disabledWrapper.setAttribute("data-conditionally-disabled", "true");
10771
+ (_c = wrapper.parentNode) == null ? void 0 : _c.replaceChild(disabledWrapper, wrapper);
10772
+ }
10773
+ } catch (error) {
10774
+ console.error(
10775
+ `Error re-evaluating enableIf for field "${(_d = element.key) != null ? _d : "<no key>"}" at path "${fullPath}":`,
10776
+ error
10777
+ );
10778
+ }
10779
+ }
10408
10780
  /**
10409
10781
  * Re-evaluate all conditional fields (enableIf) based on current form data
10410
10782
  * This is called automatically when form data changes (via onChange events)
@@ -10414,98 +10786,32 @@ var FormBuilderInstance = class {
10414
10786
  const formData = this.validateForm(true).data;
10415
10787
  const checkElements = (elements, currentPath) => {
10416
10788
  elements.forEach((element) => {
10417
- var _a, _b, _c;
10418
- const fullPath = currentPath ? `${currentPath}.${element.key}` : element.key;
10789
+ var _a, _b;
10790
+ const fullPath = currentPath ? `${currentPath}.${(_a = element.key) != null ? _a : ""}` : (_b = element.key) != null ? _b : "";
10419
10791
  if (element.enableIf) {
10420
- let fieldWrapper = null;
10421
- if (currentPath) {
10422
- const pathMatch = currentPath.match(/^(.+)\[(\d+)\]$/);
10423
- if (pathMatch) {
10424
- const containerKey = pathMatch[1];
10425
- const containerIndex = pathMatch[2];
10426
- const containerElement = this.state.formRoot.querySelector(
10427
- `[data-container-item="${containerKey}[${containerIndex}]"]`
10428
- );
10429
- if (containerElement) {
10430
- fieldWrapper = containerElement.querySelector(
10431
- `[data-field-key="${element.key}"]`
10432
- );
10433
- }
10434
- } else {
10435
- const containerElement = this.state.formRoot.querySelector(
10436
- `[data-container="${currentPath}"]`
10437
- );
10438
- if (containerElement) {
10439
- fieldWrapper = containerElement.querySelector(
10440
- `[data-field-key="${element.key}"]`
10441
- );
10442
- }
10443
- }
10444
- } else {
10445
- fieldWrapper = this.state.formRoot.querySelector(
10446
- `[data-field-key="${element.key}"]`
10447
- );
10448
- }
10792
+ const fieldWrapper = this.findFieldWrapper(element, currentPath);
10449
10793
  if (fieldWrapper) {
10450
- const wrapper = fieldWrapper;
10451
- try {
10452
- let containerData = void 0;
10453
- const scope = (_a = element.enableIf.scope) != null ? _a : "relative";
10454
- if (scope === "relative" && currentPath) {
10455
- containerData = getValueByPath(formData, currentPath);
10456
- }
10457
- const shouldEnable = evaluateEnableCondition(
10458
- element.enableIf,
10459
- formData,
10460
- // Use complete formData for absolute scope
10461
- containerData
10462
- // Use container-specific data for relative scope
10463
- );
10464
- const isCurrentlyDisabled = wrapper.getAttribute("data-conditionally-disabled") === "true";
10465
- if (shouldEnable && isCurrentlyDisabled) {
10466
- const containerPrefill = currentPath ? getValueByPath(formData, currentPath) : formData;
10467
- const prefillContext = containerPrefill && typeof containerPrefill === "object" ? containerPrefill : {};
10468
- const newWrapper = renderElement2(element, {
10469
- path: currentPath,
10470
- // Use container path (empty string for root-level)
10471
- prefill: prefillContext,
10472
- formData,
10473
- // Pass complete formData for enableIf evaluation
10474
- state: this.state,
10475
- instance: this
10476
- });
10477
- (_b = wrapper.parentNode) == null ? void 0 : _b.replaceChild(newWrapper, wrapper);
10478
- } else if (!shouldEnable && !isCurrentlyDisabled) {
10479
- const disabledWrapper = document.createElement("div");
10480
- disabledWrapper.className = "fb-field-wrapper-disabled";
10481
- disabledWrapper.style.display = "none";
10482
- disabledWrapper.setAttribute("data-field-key", element.key);
10483
- disabledWrapper.setAttribute(
10484
- "data-conditionally-disabled",
10485
- "true"
10486
- );
10487
- (_c = wrapper.parentNode) == null ? void 0 : _c.replaceChild(disabledWrapper, wrapper);
10488
- }
10489
- } catch (error) {
10490
- console.error(
10491
- `Error re-evaluating enableIf for field "${element.key}" at path "${fullPath}":`,
10492
- error
10493
- );
10494
- }
10794
+ this.applyEnableIfVisibility(
10795
+ element,
10796
+ fieldWrapper,
10797
+ currentPath,
10798
+ fullPath,
10799
+ formData
10800
+ );
10495
10801
  }
10496
10802
  }
10497
10803
  if ((element.type === "container" || element.type === "group") && "elements" in element && element.elements) {
10498
- const containerData = formData == null ? void 0 : formData[element.key];
10804
+ const containerData = element.key ? formData == null ? void 0 : formData[element.key] : void 0;
10499
10805
  if (Array.isArray(containerData)) {
10500
10806
  const containerItems = this.state.formRoot.querySelectorAll(
10501
10807
  `[data-container-item]`
10502
10808
  );
10503
- const directItems = Array.from(containerItems).filter((el) => {
10809
+ const directItems = fullPath ? Array.from(containerItems).filter((el) => {
10504
10810
  const attr = el.getAttribute("data-container-item") || "";
10505
10811
  if (!attr.startsWith(`${fullPath}[`)) return false;
10506
10812
  const suffix = attr.slice(fullPath.length);
10507
10813
  return /^\[\d+\]$/.test(suffix);
10508
- });
10814
+ }) : [];
10509
10815
  directItems.forEach((el) => {
10510
10816
  const attr = el.getAttribute("data-container-item") || "";
10511
10817
  checkElements(element.elements, attr);