@dmitryvim/form-builder 0.2.28 → 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
  }
@@ -2664,23 +2686,35 @@ function setEmptyFileContainer(fileContainer, state, hint) {
2664
2686
  </div>
2665
2687
  `;
2666
2688
  }
2689
+ var dragDropHandlers = /* @__PURE__ */ new WeakMap();
2667
2690
  function setupDragAndDrop(element, dropHandler) {
2668
- element.addEventListener("dragover", (e) => {
2691
+ const prev = dragDropHandlers.get(element);
2692
+ if (prev) {
2693
+ element.removeEventListener("dragover", prev.dragover);
2694
+ element.removeEventListener("dragleave", prev.dragleave);
2695
+ element.removeEventListener("drop", prev.drop);
2696
+ }
2697
+ const dragover = (e) => {
2669
2698
  e.preventDefault();
2670
2699
  element.classList.add("border-blue-500", "bg-blue-50");
2671
- });
2672
- element.addEventListener("dragleave", (e) => {
2700
+ };
2701
+ const dragleave = (e) => {
2673
2702
  e.preventDefault();
2674
2703
  element.classList.remove("border-blue-500", "bg-blue-50");
2675
- });
2676
- element.addEventListener("drop", (e) => {
2704
+ };
2705
+ const drop = (e) => {
2677
2706
  var _a;
2678
2707
  e.preventDefault();
2679
2708
  element.classList.remove("border-blue-500", "bg-blue-50");
2680
- if ((_a = e.dataTransfer) == null ? void 0 : _a.files) {
2681
- dropHandler(e.dataTransfer.files);
2709
+ const files = (_a = e.dataTransfer) == null ? void 0 : _a.files;
2710
+ if (files) {
2711
+ dropHandler(files);
2682
2712
  }
2683
- });
2713
+ };
2714
+ element.addEventListener("dragover", dragover);
2715
+ element.addEventListener("dragleave", dragleave);
2716
+ element.addEventListener("drop", drop);
2717
+ dragDropHandlers.set(element, { dragover, dragleave, drop });
2684
2718
  }
2685
2719
 
2686
2720
  // src/components/file/preview.ts
@@ -3957,7 +3991,7 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
3957
3991
  releaseLocalFileUrl((_a2 = state.resourceIndex.get(currentRid)) == null ? void 0 : _a2.file);
3958
3992
  }
3959
3993
  if (hiddenInput) hiddenInput.value = "";
3960
- handlers.restoreDropzone();
3994
+ renderEmptySingleState();
3961
3995
  }
3962
3996
  };
3963
3997
  const buildDeps = () => ({
@@ -3972,6 +4006,9 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
3972
4006
  fileContainer.className = "file-preview-container";
3973
4007
  fileContainer.removeAttribute("style");
3974
4008
  fileContainer.onclick = null;
4009
+ while (fileContainer.firstChild) {
4010
+ fileContainer.removeChild(fileContainer.firstChild);
4011
+ }
3975
4012
  const row = document.createElement("div");
3976
4013
  row.className = "fb-file-card-row";
3977
4014
  row.style.cssText = "display:flex;gap:8px;align-items:stretch;";
@@ -5516,7 +5553,7 @@ function updateSliderField(element, fieldPath, value, context) {
5516
5553
  function extractChildDefaults(elements) {
5517
5554
  const defaults = {};
5518
5555
  for (const child of elements) {
5519
- if ("default" in child && child.default !== void 0) {
5556
+ if (child.key && "default" in child && child.default !== void 0) {
5520
5557
  defaults[child.key] = child.default;
5521
5558
  }
5522
5559
  }
@@ -5613,8 +5650,8 @@ function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
5613
5650
  };
5614
5651
  element.elements.forEach((child) => {
5615
5652
  var _a2, _b2;
5616
- if (child.hidden || child.type === "hidden") {
5617
- 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;
5618
5655
  itemsWrap.appendChild(
5619
5656
  createHiddenInput(pathJoin(subCtx.path, child.key), prefillVal)
5620
5657
  );
@@ -5690,11 +5727,11 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
5690
5727
  }
5691
5728
  element.elements.forEach((child) => {
5692
5729
  var _a2;
5693
- if (child.hidden || child.type === "hidden") {
5730
+ if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
5694
5731
  childWrapper.appendChild(
5695
5732
  createHiddenInput(
5696
5733
  pathJoin(subCtx.path, child.key),
5697
- (_a2 = child.default) != null ? _a2 : null
5734
+ (_a2 = "default" in child ? child.default : null) != null ? _a2 : null
5698
5735
  )
5699
5736
  );
5700
5737
  } else {
@@ -5769,8 +5806,8 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
5769
5806
  }
5770
5807
  element.elements.forEach((child) => {
5771
5808
  var _a3, _b2;
5772
- if (child.hidden || child.type === "hidden") {
5773
- 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;
5774
5811
  childWrapper.appendChild(
5775
5812
  createHiddenInput(pathJoin(subCtx.path, child.key), prefillVal)
5776
5813
  );
@@ -5826,11 +5863,11 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
5826
5863
  }
5827
5864
  element.elements.forEach((child) => {
5828
5865
  var _a2;
5829
- if (child.hidden || child.type === "hidden") {
5866
+ if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
5830
5867
  childWrapper.appendChild(
5831
5868
  createHiddenInput(
5832
5869
  pathJoin(subCtx.path, child.key),
5833
- (_a2 = child.default) != null ? _a2 : null
5870
+ (_a2 = "default" in child ? child.default : null) != null ? _a2 : null
5834
5871
  )
5835
5872
  );
5836
5873
  } else {
@@ -6020,6 +6057,7 @@ function updateContainerField(element, fieldPath, value, context) {
6020
6057
  if (isPlainObject(itemValue)) {
6021
6058
  element.elements.forEach((childElement) => {
6022
6059
  var _a, _b;
6060
+ if (childElement.type === "markdown" || !childElement.key) return;
6023
6061
  const childPath = `${fieldPath}[${index}].${childElement.key}`;
6024
6062
  if (childElement.type === "richinput" && childElement.flatOutput) {
6025
6063
  const richChild = childElement;
@@ -6060,6 +6098,7 @@ function updateContainerField(element, fieldPath, value, context) {
6060
6098
  }
6061
6099
  element.elements.forEach((childElement) => {
6062
6100
  var _a, _b;
6101
+ if (childElement.type === "markdown" || !childElement.key) return;
6063
6102
  const childPath = `${fieldPath}.${childElement.key}`;
6064
6103
  if (childElement.type === "richinput" && childElement.flatOutput) {
6065
6104
  const richChild = childElement;
@@ -8827,6 +8866,250 @@ function updateRichInputField(element, fieldPath, value, context) {
8827
8866
  }
8828
8867
  }
8829
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
+
8830
9113
  // src/components/index.ts
8831
9114
  function showTooltip(tooltipId, button) {
8832
9115
  const tooltip = document.getElementById(tooltipId);
@@ -9058,9 +9341,10 @@ function setupEnableIfListeners(wrapper, element, ctx) {
9058
9341
  });
9059
9342
  }
9060
9343
  function createFieldLabel(element) {
9344
+ var _a, _b;
9061
9345
  const title = document.createElement("label");
9062
9346
  title.className = "text-sm font-medium text-gray-900";
9063
- title.textContent = element.label || element.key;
9347
+ title.textContent = (_b = (_a = element.label) != null ? _a : element.key) != null ? _b : "";
9064
9348
  if (element.required) {
9065
9349
  const req = document.createElement("span");
9066
9350
  req.className = "text-red-500 ml-1";
@@ -9188,6 +9472,29 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
9188
9472
  }
9189
9473
  }
9190
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
+ }
9191
9498
  const initiallyDisabled = shouldDisableElement(element, ctx);
9192
9499
  const wrapper = document.createElement("div");
9193
9500
  wrapper.className = "mb-6 fb-field-wrapper";
@@ -9398,7 +9705,9 @@ function createInstanceState(config) {
9398
9705
  translations: mergedTranslations
9399
9706
  },
9400
9707
  debounceTimer: null,
9401
- prefill: {}
9708
+ prefill: {},
9709
+ syntheticElementIds: /* @__PURE__ */ new WeakMap(),
9710
+ syntheticElementIdCounter: 0
9402
9711
  };
9403
9712
  }
9404
9713
  function generateInstanceId() {
@@ -9676,6 +9985,11 @@ var componentRegistry = {
9676
9985
  // Legacy type: `type: "hidden"` — reads/writes DOM <input type="hidden"> element
9677
9986
  validate: validateHiddenElement,
9678
9987
  update: updateHiddenField
9988
+ },
9989
+ markdown: {
9990
+ // Display-only element — no value, no errors, skip from form data
9991
+ validate: validateMarkdown,
9992
+ update: updateMarkdown
9679
9993
  }
9680
9994
  };
9681
9995
  function getComponentOperations(elementType) {
@@ -10104,7 +10418,7 @@ var FormBuilderInstance = class {
10104
10418
  }
10105
10419
  schema.elements.forEach((element) => {
10106
10420
  var _a, _b;
10107
- if (element.hidden || element.type === "hidden") {
10421
+ if (element.type !== "markdown" && (element.hidden || element.type === "hidden")) {
10108
10422
  const val = (_b = (_a = prefill == null ? void 0 : prefill[element.key]) != null ? _a : element.default) != null ? _b : null;
10109
10423
  fieldsWrapper.appendChild(createHiddenInput(element.key, val));
10110
10424
  return;
@@ -10139,7 +10453,8 @@ var FormBuilderInstance = class {
10139
10453
  const errors = [];
10140
10454
  const data = {};
10141
10455
  const validateElement2 = (element, ctx, customScopeRoot = null) => {
10142
- const key = element.key;
10456
+ var _a;
10457
+ const key = (_a = element.key) != null ? _a : "";
10143
10458
  const scopeRoot = customScopeRoot || this.state.formRoot;
10144
10459
  const componentContext = {
10145
10460
  scopeRoot,
@@ -10157,7 +10472,8 @@ var FormBuilderInstance = class {
10157
10472
  errors.push(...componentResult.errors);
10158
10473
  return {
10159
10474
  value: componentResult.value,
10160
- spread: !!componentResult.spread
10475
+ spread: !!componentResult.spread,
10476
+ skip: !!componentResult.skip
10161
10477
  };
10162
10478
  }
10163
10479
  console.warn(`Unknown field type "${element.type}" for key "${key}"`);
@@ -10179,6 +10495,9 @@ var FormBuilderInstance = class {
10179
10495
  );
10180
10496
  }
10181
10497
  }
10498
+ if (element.type === "markdown") {
10499
+ return;
10500
+ }
10182
10501
  if (element.hidden || element.type === "hidden") {
10183
10502
  const hiddenInput = this.state.formRoot.querySelector(
10184
10503
  `input[type="hidden"][data-hidden-field="true"][name="${element.key}"]`
@@ -10191,9 +10510,10 @@ var FormBuilderInstance = class {
10191
10510
  }
10192
10511
  } else {
10193
10512
  const result = validateElement2(element, { path: "" });
10513
+ if (result.skip) return;
10194
10514
  if (result.spread && result.value !== null && typeof result.value === "object") {
10195
10515
  Object.assign(data, result.value);
10196
- } else {
10516
+ } else if (element.key) {
10197
10517
  data[element.key] = result.value;
10198
10518
  }
10199
10519
  }
@@ -10269,6 +10589,7 @@ var FormBuilderInstance = class {
10269
10589
  buildHiddenFieldsData(elements) {
10270
10590
  const data = {};
10271
10591
  for (const element of elements) {
10592
+ if (element.type === "markdown" || !element.key) continue;
10272
10593
  const key = element.key;
10273
10594
  if (element.hidden && element.default !== void 0) {
10274
10595
  data[key] = element.default;
@@ -10390,6 +10711,72 @@ var FormBuilderInstance = class {
10390
10711
  );
10391
10712
  }
10392
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
+ }
10393
10780
  /**
10394
10781
  * Re-evaluate all conditional fields (enableIf) based on current form data
10395
10782
  * This is called automatically when form data changes (via onChange events)
@@ -10399,98 +10786,32 @@ var FormBuilderInstance = class {
10399
10786
  const formData = this.validateForm(true).data;
10400
10787
  const checkElements = (elements, currentPath) => {
10401
10788
  elements.forEach((element) => {
10402
- var _a, _b, _c;
10403
- 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 : "";
10404
10791
  if (element.enableIf) {
10405
- let fieldWrapper = null;
10406
- if (currentPath) {
10407
- const pathMatch = currentPath.match(/^(.+)\[(\d+)\]$/);
10408
- if (pathMatch) {
10409
- const containerKey = pathMatch[1];
10410
- const containerIndex = pathMatch[2];
10411
- const containerElement = this.state.formRoot.querySelector(
10412
- `[data-container-item="${containerKey}[${containerIndex}]"]`
10413
- );
10414
- if (containerElement) {
10415
- fieldWrapper = containerElement.querySelector(
10416
- `[data-field-key="${element.key}"]`
10417
- );
10418
- }
10419
- } else {
10420
- const containerElement = this.state.formRoot.querySelector(
10421
- `[data-container="${currentPath}"]`
10422
- );
10423
- if (containerElement) {
10424
- fieldWrapper = containerElement.querySelector(
10425
- `[data-field-key="${element.key}"]`
10426
- );
10427
- }
10428
- }
10429
- } else {
10430
- fieldWrapper = this.state.formRoot.querySelector(
10431
- `[data-field-key="${element.key}"]`
10432
- );
10433
- }
10792
+ const fieldWrapper = this.findFieldWrapper(element, currentPath);
10434
10793
  if (fieldWrapper) {
10435
- const wrapper = fieldWrapper;
10436
- try {
10437
- let containerData = void 0;
10438
- const scope = (_a = element.enableIf.scope) != null ? _a : "relative";
10439
- if (scope === "relative" && currentPath) {
10440
- containerData = getValueByPath(formData, currentPath);
10441
- }
10442
- const shouldEnable = evaluateEnableCondition(
10443
- element.enableIf,
10444
- formData,
10445
- // Use complete formData for absolute scope
10446
- containerData
10447
- // Use container-specific data for relative scope
10448
- );
10449
- const isCurrentlyDisabled = wrapper.getAttribute("data-conditionally-disabled") === "true";
10450
- if (shouldEnable && isCurrentlyDisabled) {
10451
- const containerPrefill = currentPath ? getValueByPath(formData, currentPath) : formData;
10452
- const prefillContext = containerPrefill && typeof containerPrefill === "object" ? containerPrefill : {};
10453
- const newWrapper = renderElement2(element, {
10454
- path: currentPath,
10455
- // Use container path (empty string for root-level)
10456
- prefill: prefillContext,
10457
- formData,
10458
- // Pass complete formData for enableIf evaluation
10459
- state: this.state,
10460
- instance: this
10461
- });
10462
- (_b = wrapper.parentNode) == null ? void 0 : _b.replaceChild(newWrapper, wrapper);
10463
- } else if (!shouldEnable && !isCurrentlyDisabled) {
10464
- const disabledWrapper = document.createElement("div");
10465
- disabledWrapper.className = "fb-field-wrapper-disabled";
10466
- disabledWrapper.style.display = "none";
10467
- disabledWrapper.setAttribute("data-field-key", element.key);
10468
- disabledWrapper.setAttribute(
10469
- "data-conditionally-disabled",
10470
- "true"
10471
- );
10472
- (_c = wrapper.parentNode) == null ? void 0 : _c.replaceChild(disabledWrapper, wrapper);
10473
- }
10474
- } catch (error) {
10475
- console.error(
10476
- `Error re-evaluating enableIf for field "${element.key}" at path "${fullPath}":`,
10477
- error
10478
- );
10479
- }
10794
+ this.applyEnableIfVisibility(
10795
+ element,
10796
+ fieldWrapper,
10797
+ currentPath,
10798
+ fullPath,
10799
+ formData
10800
+ );
10480
10801
  }
10481
10802
  }
10482
10803
  if ((element.type === "container" || element.type === "group") && "elements" in element && element.elements) {
10483
- const containerData = formData == null ? void 0 : formData[element.key];
10804
+ const containerData = element.key ? formData == null ? void 0 : formData[element.key] : void 0;
10484
10805
  if (Array.isArray(containerData)) {
10485
10806
  const containerItems = this.state.formRoot.querySelectorAll(
10486
10807
  `[data-container-item]`
10487
10808
  );
10488
- const directItems = Array.from(containerItems).filter((el) => {
10809
+ const directItems = fullPath ? Array.from(containerItems).filter((el) => {
10489
10810
  const attr = el.getAttribute("data-container-item") || "";
10490
10811
  if (!attr.startsWith(`${fullPath}[`)) return false;
10491
10812
  const suffix = attr.slice(fullPath.length);
10492
10813
  return /^\[\d+\]$/.test(suffix);
10493
- });
10814
+ }) : [];
10494
10815
  directItems.forEach((el) => {
10495
10816
  const attr = el.getAttribute("data-container-item") || "";
10496
10817
  checkElements(element.elements, attr);