@dmitryvim/form-builder 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/formbuilder.min.js +114 -101
- package/dist/browser/{formbuilder.v0.5.1.min.js → formbuilder.v0.5.2.min.js} +114 -101
- package/dist/cjs/index.cjs +95 -136
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +94 -133
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +114 -101
- package/dist/types/utils/helpers.d.ts +8 -0
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -307,6 +307,15 @@ function getElementLookupKey(element, state) {
|
|
|
307
307
|
function pathJoin(base, key) {
|
|
308
308
|
return base ? `${base}.${key}` : key;
|
|
309
309
|
}
|
|
310
|
+
function findDirectContainerRows(scopeRoot, containerPath) {
|
|
311
|
+
if (!containerPath) return [];
|
|
312
|
+
const all = scopeRoot.querySelectorAll("[data-container-item]");
|
|
313
|
+
return Array.from(all).filter((el) => {
|
|
314
|
+
const attr = el.getAttribute("data-container-item") || "";
|
|
315
|
+
if (!attr.startsWith(`${containerPath}[`)) return false;
|
|
316
|
+
return /^\[\d+\]$/.test(attr.slice(containerPath.length));
|
|
317
|
+
});
|
|
318
|
+
}
|
|
310
319
|
function clear(node) {
|
|
311
320
|
while (node.firstChild) node.removeChild(node.firstChild);
|
|
312
321
|
}
|
|
@@ -457,6 +466,19 @@ function ensureThemingHooks(doc) {
|
|
|
457
466
|
color: var(--fb-error-color);
|
|
458
467
|
background-color: var(--fb-background-hover-color);
|
|
459
468
|
}
|
|
469
|
+
.fb-item-remove:disabled {
|
|
470
|
+
opacity: 0.35;
|
|
471
|
+
cursor: not-allowed;
|
|
472
|
+
}
|
|
473
|
+
/* Cards carrying a row-level remove button reserve a right-hand lane for it
|
|
474
|
+
(24px button + inset + gap) so it never sits on top of the first field's
|
|
475
|
+
control. A class rather than an inline style, so host CSS can override the
|
|
476
|
+
gutter without !important; the compound selector keeps it ahead of the
|
|
477
|
+
card's own p-2 utility regardless of stylesheet order. Its own token, not
|
|
478
|
+
--fb-slide-card-padding, which is slides-only and always resolves. */
|
|
479
|
+
.containerItem.fb-row-removable {
|
|
480
|
+
padding-right: var(--fb-row-remove-gutter, 40px);
|
|
481
|
+
}
|
|
460
482
|
/* Prefill-suggestion pills rendered by createPrefillHints. Outline pill at rest,
|
|
461
483
|
soft-fill on hover, solid-fill when selected. All colors flow from the active
|
|
462
484
|
theme \u2014 consumers don't need to ship their own CSS. */
|
|
@@ -4934,12 +4956,21 @@ function buildClearAllRow(state, ridCount, onClearAll) {
|
|
|
4934
4956
|
return row;
|
|
4935
4957
|
}
|
|
4936
4958
|
var gridResizeObservers = /* @__PURE__ */ new WeakMap();
|
|
4959
|
+
var gridMeasureFrames = /* @__PURE__ */ new WeakMap();
|
|
4960
|
+
function cancelPendingMeasure(container) {
|
|
4961
|
+
const frame = gridMeasureFrames.get(container);
|
|
4962
|
+
if (frame !== void 0) {
|
|
4963
|
+
cancelAnimationFrame(frame);
|
|
4964
|
+
gridMeasureFrames.delete(container);
|
|
4965
|
+
}
|
|
4966
|
+
}
|
|
4937
4967
|
function disposePlaceholdersForUpload(container) {
|
|
4938
4968
|
const observer = gridResizeObservers.get(container);
|
|
4939
4969
|
if (observer) {
|
|
4940
4970
|
observer.disconnect();
|
|
4941
4971
|
gridResizeObservers.delete(container);
|
|
4942
4972
|
}
|
|
4973
|
+
cancelPendingMeasure(container);
|
|
4943
4974
|
container.querySelectorAll(".fb-multi-placeholder").forEach((p) => p.remove());
|
|
4944
4975
|
}
|
|
4945
4976
|
function renderResourcePills(opts) {
|
|
@@ -4965,6 +4996,7 @@ function renderResourcePills(opts) {
|
|
|
4965
4996
|
previousObserver.disconnect();
|
|
4966
4997
|
gridResizeObservers.delete(container);
|
|
4967
4998
|
}
|
|
4999
|
+
cancelPendingMeasure(container);
|
|
4968
5000
|
while (container.firstChild) container.removeChild(container.firstChild);
|
|
4969
5001
|
const ridList = rids != null ? rids : [];
|
|
4970
5002
|
const effectiveMax = maxCount != null ? maxCount : Infinity;
|
|
@@ -5066,7 +5098,7 @@ function renderResourcePills(opts) {
|
|
|
5066
5098
|
if (effectiveMax === Infinity || effectiveMax > occupied) {
|
|
5067
5099
|
grid.appendChild(buildPlaceholderTile());
|
|
5068
5100
|
}
|
|
5069
|
-
requestAnimationFrame(adjustPlaceholders);
|
|
5101
|
+
gridMeasureFrames.set(container, requestAnimationFrame(adjustPlaceholders));
|
|
5070
5102
|
if (typeof ResizeObserver !== "undefined") {
|
|
5071
5103
|
const ro = new ResizeObserver(() => adjustPlaceholders());
|
|
5072
5104
|
ro.observe(grid);
|
|
@@ -6865,17 +6897,13 @@ function mountRemoveButton(item, onRemove, state) {
|
|
|
6865
6897
|
`;
|
|
6866
6898
|
rem.innerHTML = BIN_ICON_SVG;
|
|
6867
6899
|
rem.onclick = onRemove;
|
|
6868
|
-
const labelRow = item.querySelector("[data-fb-label-row]");
|
|
6869
|
-
if (labelRow) {
|
|
6870
|
-
rem.style.marginLeft = "auto";
|
|
6871
|
-
labelRow.appendChild(rem);
|
|
6872
|
-
return;
|
|
6873
|
-
}
|
|
6874
6900
|
rem.style.position = "absolute";
|
|
6875
|
-
rem.style.top = "8px";
|
|
6876
|
-
rem.style.right = "8px";
|
|
6901
|
+
rem.style.top = "var(--fb-row-remove-inset, 8px)";
|
|
6902
|
+
rem.style.right = "var(--fb-row-remove-inset, 8px)";
|
|
6903
|
+
rem.style.zIndex = "1";
|
|
6877
6904
|
item.style.position = "relative";
|
|
6878
|
-
item.
|
|
6905
|
+
item.classList.add("fb-row-removable");
|
|
6906
|
+
item.insertBefore(rem, item.firstChild);
|
|
6879
6907
|
}
|
|
6880
6908
|
function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
6881
6909
|
var _a, _b, _c, _d;
|
|
@@ -6901,24 +6929,29 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6901
6929
|
const max = (_b = element.maxCount) != null ? _b : Infinity;
|
|
6902
6930
|
const pre = Array.isArray((_c = ctx.prefill) == null ? void 0 : _c[element.key]) ? ctx.prefill[element.key] : null;
|
|
6903
6931
|
const childDefaults = extractChildDefaults(element.elements);
|
|
6904
|
-
|
|
6905
|
-
const
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6932
|
+
let nextRowIndex = 0;
|
|
6933
|
+
const takeRowIndex = () => nextRowIndex++;
|
|
6934
|
+
const directRows = () => Array.from(itemsWrap.children).filter(
|
|
6935
|
+
(el) => el.classList.contains("containerItem")
|
|
6936
|
+
);
|
|
6937
|
+
const countItems = () => directRows().length;
|
|
6938
|
+
const handleRemoveItem = (item) => {
|
|
6939
|
+
if (countItems() <= min) return;
|
|
6940
|
+
item.remove();
|
|
6941
|
+
updateAddButton();
|
|
6942
|
+
};
|
|
6943
|
+
const createContainerItem = (idx, rowPrefill, formData) => {
|
|
6909
6944
|
const subCtx = {
|
|
6910
|
-
state
|
|
6945
|
+
state,
|
|
6911
6946
|
instance: ctx.instance,
|
|
6912
6947
|
path: pathJoin(ctx.path, `${element.key}[${idx}]`),
|
|
6913
|
-
prefill: childDefaults,
|
|
6914
|
-
|
|
6915
|
-
formData: currentFormData,
|
|
6916
|
-
// Current root data from DOM for enableIf
|
|
6948
|
+
prefill: mergeWithDefaults(rowPrefill != null ? rowPrefill : {}, childDefaults),
|
|
6949
|
+
formData,
|
|
6917
6950
|
inheritedReadonly: childInheritedReadonly
|
|
6918
6951
|
};
|
|
6919
6952
|
const item = document.createElement("div");
|
|
6920
6953
|
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
6921
|
-
item.setAttribute("data-container-item",
|
|
6954
|
+
item.setAttribute("data-container-item", subCtx.path);
|
|
6922
6955
|
if (isSlides) {
|
|
6923
6956
|
item.setAttribute("data-fb-slide-card", "");
|
|
6924
6957
|
}
|
|
@@ -6927,13 +6960,11 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6927
6960
|
isSlides ? void 0 : element.columns
|
|
6928
6961
|
);
|
|
6929
6962
|
element.elements.forEach((child) => {
|
|
6930
|
-
var _a2;
|
|
6963
|
+
var _a2, _b2;
|
|
6931
6964
|
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
6965
|
+
const hiddenValue = (_b2 = (_a2 = rowPrefill == null ? void 0 : rowPrefill[child.key]) != null ? _a2 : "default" in child ? child.default : null) != null ? _b2 : null;
|
|
6932
6966
|
childWrapper.appendChild(
|
|
6933
|
-
createHiddenInput(
|
|
6934
|
-
pathJoin(subCtx.path, child.key),
|
|
6935
|
-
(_a2 = "default" in child ? child.default : null) != null ? _a2 : null
|
|
6936
|
-
)
|
|
6967
|
+
createHiddenInput(pathJoin(subCtx.path, child.key), hiddenValue)
|
|
6937
6968
|
);
|
|
6938
6969
|
} else {
|
|
6939
6970
|
childWrapper.appendChild(renderElement(child, subCtx));
|
|
@@ -6943,6 +6974,17 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6943
6974
|
if (!containerIsReadonly) {
|
|
6944
6975
|
mountRemoveButton(item, () => handleRemoveItem(item), state);
|
|
6945
6976
|
}
|
|
6977
|
+
return item;
|
|
6978
|
+
};
|
|
6979
|
+
const handleAddItem = () => {
|
|
6980
|
+
if (countItems() >= max) return;
|
|
6981
|
+
const item = createContainerItem(
|
|
6982
|
+
takeRowIndex(),
|
|
6983
|
+
null,
|
|
6984
|
+
// Current root data read back from the DOM, so enableIf sees what the
|
|
6985
|
+
// user has actually typed rather than the original prefill.
|
|
6986
|
+
state.formRoot ? extractRootFormData(state.formRoot) : {}
|
|
6987
|
+
);
|
|
6946
6988
|
if (slideAddTile && slideAddTile.parentElement === itemsWrap) {
|
|
6947
6989
|
itemsWrap.insertBefore(item, slideAddTile);
|
|
6948
6990
|
} else {
|
|
@@ -6955,112 +6997,44 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6955
6997
|
let pillAddUpdate = null;
|
|
6956
6998
|
const syncSlideTileSize = () => {
|
|
6957
6999
|
if (!slideAddTile) return;
|
|
6958
|
-
const firstSlide =
|
|
6959
|
-
":scope > .containerItem"
|
|
6960
|
-
);
|
|
7000
|
+
const firstSlide = directRows()[0];
|
|
6961
7001
|
if (firstSlide && firstSlide.offsetHeight > 0) {
|
|
6962
7002
|
slideAddTile.style.minHeight = `${firstSlide.offsetHeight}px`;
|
|
6963
7003
|
}
|
|
6964
7004
|
};
|
|
7005
|
+
const syncRemoveButtons = () => {
|
|
7006
|
+
const atFloor = countItems() <= min;
|
|
7007
|
+
directRows().forEach((row) => {
|
|
7008
|
+
const btn = Array.from(row.children).find(
|
|
7009
|
+
(el) => el.classList.contains("fb-item-remove")
|
|
7010
|
+
);
|
|
7011
|
+
if (btn) btn.disabled = atFloor;
|
|
7012
|
+
});
|
|
7013
|
+
};
|
|
6965
7014
|
const updateAddButton = () => {
|
|
6966
7015
|
const currentCount = countItems();
|
|
6967
7016
|
if (slideAddUpdate) slideAddUpdate(currentCount, max);
|
|
6968
7017
|
if (pillAddUpdate) pillAddUpdate(currentCount, max);
|
|
6969
7018
|
if (slideAddTile) syncSlideTileSize();
|
|
7019
|
+
syncRemoveButtons();
|
|
6970
7020
|
};
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
updateAddButton();
|
|
6974
|
-
};
|
|
6975
|
-
if (pre && Array.isArray(pre)) {
|
|
6976
|
-
pre.forEach((prefillObj, idx) => {
|
|
7021
|
+
if (pre) {
|
|
7022
|
+
pre.forEach((prefillObj) => {
|
|
6977
7023
|
var _a2;
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
// Merged prefill with defaults for enableIf
|
|
6985
|
-
formData: (_a2 = ctx.formData) != null ? _a2 : ctx.prefill,
|
|
6986
|
-
// Complete root data for enableIf
|
|
6987
|
-
inheritedReadonly: childInheritedReadonly
|
|
6988
|
-
};
|
|
6989
|
-
const item = document.createElement("div");
|
|
6990
|
-
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
6991
|
-
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
6992
|
-
if (isSlides) {
|
|
6993
|
-
item.setAttribute("data-fb-slide-card", "");
|
|
6994
|
-
}
|
|
6995
|
-
const childWrapper = document.createElement("div");
|
|
6996
|
-
childWrapper.className = getChildWrapperClass(
|
|
6997
|
-
isSlides ? void 0 : element.columns
|
|
7024
|
+
itemsWrap.appendChild(
|
|
7025
|
+
createContainerItem(
|
|
7026
|
+
takeRowIndex(),
|
|
7027
|
+
prefillObj != null ? prefillObj : {},
|
|
7028
|
+
(_a2 = ctx.formData) != null ? _a2 : ctx.prefill
|
|
7029
|
+
)
|
|
6998
7030
|
);
|
|
6999
|
-
element.elements.forEach((child) => {
|
|
7000
|
-
var _a3, _b2;
|
|
7001
|
-
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
7002
|
-
const prefillVal = (_b2 = (_a3 = prefillObj == null ? void 0 : prefillObj[child.key]) != null ? _a3 : "default" in child ? child.default : null) != null ? _b2 : null;
|
|
7003
|
-
childWrapper.appendChild(
|
|
7004
|
-
createHiddenInput(pathJoin(subCtx.path, child.key), prefillVal)
|
|
7005
|
-
);
|
|
7006
|
-
} else {
|
|
7007
|
-
childWrapper.appendChild(renderElement(child, subCtx));
|
|
7008
|
-
}
|
|
7009
|
-
});
|
|
7010
|
-
item.appendChild(childWrapper);
|
|
7011
|
-
if (!containerIsReadonly) {
|
|
7012
|
-
mountRemoveButton(item, () => handleRemoveItem(item), ctx.state);
|
|
7013
|
-
}
|
|
7014
|
-
itemsWrap.appendChild(item);
|
|
7015
7031
|
});
|
|
7016
7032
|
}
|
|
7017
7033
|
if (!containerIsReadonly) {
|
|
7018
7034
|
while (countItems() < min) {
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
state: ctx.state,
|
|
7022
|
-
instance: ctx.instance,
|
|
7023
|
-
path: pathJoin(ctx.path, `${element.key}[${idx}]`),
|
|
7024
|
-
prefill: childDefaults,
|
|
7025
|
-
// Defaults for enableIf evaluation
|
|
7026
|
-
formData: (_d = ctx.formData) != null ? _d : ctx.prefill,
|
|
7027
|
-
// Complete root data for enableIf
|
|
7028
|
-
inheritedReadonly: childInheritedReadonly
|
|
7029
|
-
};
|
|
7030
|
-
const item = document.createElement("div");
|
|
7031
|
-
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
7032
|
-
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
7033
|
-
if (isSlides) {
|
|
7034
|
-
item.setAttribute("data-fb-slide-card", "");
|
|
7035
|
-
}
|
|
7036
|
-
const childWrapper = document.createElement("div");
|
|
7037
|
-
childWrapper.className = getChildWrapperClass(
|
|
7038
|
-
isSlides ? void 0 : element.columns
|
|
7039
|
-
);
|
|
7040
|
-
element.elements.forEach((child) => {
|
|
7041
|
-
var _a2;
|
|
7042
|
-
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
7043
|
-
childWrapper.appendChild(
|
|
7044
|
-
createHiddenInput(
|
|
7045
|
-
pathJoin(subCtx.path, child.key),
|
|
7046
|
-
(_a2 = "default" in child ? child.default : null) != null ? _a2 : null
|
|
7047
|
-
)
|
|
7048
|
-
);
|
|
7049
|
-
} else {
|
|
7050
|
-
childWrapper.appendChild(renderElement(child, subCtx));
|
|
7051
|
-
}
|
|
7052
|
-
});
|
|
7053
|
-
item.appendChild(childWrapper);
|
|
7054
|
-
mountRemoveButton(
|
|
7055
|
-
item,
|
|
7056
|
-
() => {
|
|
7057
|
-
if (countItems() > min) {
|
|
7058
|
-
handleRemoveItem(item);
|
|
7059
|
-
}
|
|
7060
|
-
},
|
|
7061
|
-
ctx.state
|
|
7035
|
+
itemsWrap.appendChild(
|
|
7036
|
+
createContainerItem(takeRowIndex(), null, (_d = ctx.formData) != null ? _d : ctx.prefill)
|
|
7062
7037
|
);
|
|
7063
|
-
itemsWrap.appendChild(item);
|
|
7064
7038
|
}
|
|
7065
7039
|
}
|
|
7066
7040
|
containerWrap.appendChild(itemsWrap);
|
|
@@ -7128,15 +7102,7 @@ function validateContainerElement(element, key, context) {
|
|
|
7128
7102
|
};
|
|
7129
7103
|
if ("multiple" in element && element.multiple) {
|
|
7130
7104
|
const items = [];
|
|
7131
|
-
const
|
|
7132
|
-
"[data-container-item]"
|
|
7133
|
-
);
|
|
7134
|
-
const containerWrappers = Array.from(allContainerWrappers).filter((el) => {
|
|
7135
|
-
const attr = el.getAttribute("data-container-item") || "";
|
|
7136
|
-
if (!attr.startsWith(`${key}[`)) return false;
|
|
7137
|
-
const suffix = attr.slice(key.length);
|
|
7138
|
-
return /^\[\d+\]$/.test(suffix);
|
|
7139
|
-
});
|
|
7105
|
+
const containerWrappers = findDirectContainerRows(scopeRoot, key);
|
|
7140
7106
|
containerWrappers.forEach((itemContainer) => {
|
|
7141
7107
|
const itemData = {};
|
|
7142
7108
|
const containerAttr = itemContainer.getAttribute("data-container-item") || "";
|
|
@@ -7262,9 +7228,7 @@ function updateContainerField(element, fieldPath, value, context) {
|
|
|
7262
7228
|
});
|
|
7263
7229
|
}
|
|
7264
7230
|
});
|
|
7265
|
-
const existingContainers = scopeRoot
|
|
7266
|
-
`[data-container-item^="${fieldPath}["]`
|
|
7267
|
-
);
|
|
7231
|
+
const existingContainers = findDirectContainerRows(scopeRoot, fieldPath);
|
|
7268
7232
|
if (value.length !== existingContainers.length) {
|
|
7269
7233
|
console.warn(
|
|
7270
7234
|
`updateContainerField: Multiple container field "${fieldPath}" item count mismatch. Consider re-rendering for add/remove.`
|
|
@@ -12106,17 +12070,12 @@ var FormBuilderInstance = class {
|
|
|
12106
12070
|
}
|
|
12107
12071
|
}
|
|
12108
12072
|
if ((element.type === "container" || element.type === "group") && "elements" in element && element.elements) {
|
|
12109
|
-
const containerData = element.key ? formData
|
|
12073
|
+
const containerData = element.key ? getValueByPath(formData, fullPath) : void 0;
|
|
12110
12074
|
if (Array.isArray(containerData)) {
|
|
12111
|
-
const
|
|
12112
|
-
|
|
12075
|
+
const directItems = findDirectContainerRows(
|
|
12076
|
+
this.state.formRoot,
|
|
12077
|
+
fullPath
|
|
12113
12078
|
);
|
|
12114
|
-
const directItems = fullPath ? Array.from(containerItems).filter((el) => {
|
|
12115
|
-
const attr = el.getAttribute("data-container-item") || "";
|
|
12116
|
-
if (!attr.startsWith(`${fullPath}[`)) return false;
|
|
12117
|
-
const suffix = attr.slice(fullPath.length);
|
|
12118
|
-
return /^\[\d+\]$/.test(suffix);
|
|
12119
|
-
}) : [];
|
|
12120
12079
|
directItems.forEach((el) => {
|
|
12121
12080
|
const attr = el.getAttribute("data-container-item") || "";
|
|
12122
12081
|
checkElements(element.elements, attr);
|