@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.
- package/README.md +51 -0
- package/dist/browser/formbuilder.min.js +196 -119
- package/dist/browser/formbuilder.v0.2.30.min.js +1033 -0
- package/dist/cjs/index.cjs +406 -100
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +400 -99
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +196 -119
- package/dist/types/components/markdown/index.d.ts +15 -0
- package/dist/types/components/markdown/render.d.ts +6 -0
- package/dist/types/components/markdown/snarkdown.d.ts +2 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/instance/FormBuilderInstance.d.ts +10 -0
- package/dist/types/types/component-operations.d.ts +5 -0
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/schema.d.ts +14 -1
- package/dist/types/types/state.d.ts +5 -1
- package/dist/types/utils/helpers.d.ts +14 -0
- package/package.json +1 -1
- package/dist/browser/formbuilder.v0.2.29.min.js +0 -956
package/dist/cjs/index.cjs
CHANGED
|
@@ -159,12 +159,14 @@ function validateSchema(schema) {
|
|
|
159
159
|
allOutputKeys.add(textKey);
|
|
160
160
|
allOutputKeys.add(filesKey);
|
|
161
161
|
} else {
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
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, """).replace(/</g, "<").replace(/>/g, ">");
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
10451
|
-
|
|
10452
|
-
|
|
10453
|
-
|
|
10454
|
-
|
|
10455
|
-
|
|
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);
|