@dmitryvim/form-builder 0.2.5 → 0.2.7
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 +121 -2
- package/dist/browser/formbuilder.min.js +117 -60
- package/dist/browser/formbuilder.v0.2.7.min.js +241 -0
- package/dist/cjs/index.cjs +896 -172
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +883 -167
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +117 -60
- package/dist/types/components/colour.d.ts +18 -0
- package/dist/types/components/index.d.ts +2 -1
- package/dist/types/instance/FormBuilderInstance.d.ts +5 -0
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/schema.d.ts +19 -2
- package/dist/types/utils/display-conditions.d.ts +17 -0
- package/package.json +1 -1
- package/dist/browser/formbuilder.v0.2.5.min.js +0 -184
package/dist/cjs/index.cjs
CHANGED
|
@@ -62,9 +62,6 @@ function validateSchema(schema) {
|
|
|
62
62
|
errors.push("Schema must be an object");
|
|
63
63
|
return errors;
|
|
64
64
|
}
|
|
65
|
-
if (!schema.version) {
|
|
66
|
-
errors.push("Schema missing version");
|
|
67
|
-
}
|
|
68
65
|
if (!Array.isArray(schema.elements)) {
|
|
69
66
|
errors.push("Schema missing elements array");
|
|
70
67
|
return errors;
|
|
@@ -78,6 +75,20 @@ function validateSchema(schema) {
|
|
|
78
75
|
if (!element.key) {
|
|
79
76
|
errors.push(`${elementPath}: missing key`);
|
|
80
77
|
}
|
|
78
|
+
if (element.displayIf) {
|
|
79
|
+
const displayIf = element.displayIf;
|
|
80
|
+
if (!displayIf.key || typeof displayIf.key !== "string") {
|
|
81
|
+
errors.push(
|
|
82
|
+
`${elementPath}: displayIf must have a 'key' property of type string`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
const hasOperator = "equals" in displayIf;
|
|
86
|
+
if (!hasOperator) {
|
|
87
|
+
errors.push(
|
|
88
|
+
`${elementPath}: displayIf must have at least one operator (equals, etc.)`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
81
92
|
if (element.type === "group" && "elements" in element && element.elements) {
|
|
82
93
|
validateElements(element.elements, `${elementPath}.elements`);
|
|
83
94
|
}
|
|
@@ -115,6 +126,66 @@ function clear(node) {
|
|
|
115
126
|
while (node.firstChild) node.removeChild(node.firstChild);
|
|
116
127
|
}
|
|
117
128
|
|
|
129
|
+
// src/utils/display-conditions.ts
|
|
130
|
+
function getValueByPath(data, path) {
|
|
131
|
+
if (!data || typeof data !== "object") {
|
|
132
|
+
return void 0;
|
|
133
|
+
}
|
|
134
|
+
const segments = path.match(/[^.[\]]+|\[\d+\]/g);
|
|
135
|
+
if (!segments || segments.length === 0) {
|
|
136
|
+
return void 0;
|
|
137
|
+
}
|
|
138
|
+
let current = data;
|
|
139
|
+
for (const segment of segments) {
|
|
140
|
+
if (current === void 0 || current === null) {
|
|
141
|
+
return void 0;
|
|
142
|
+
}
|
|
143
|
+
if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
144
|
+
const index = parseInt(segment.slice(1, -1), 10);
|
|
145
|
+
if (!Array.isArray(current) || isNaN(index)) {
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
current = current[index];
|
|
149
|
+
} else {
|
|
150
|
+
current = current[segment];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return current;
|
|
154
|
+
}
|
|
155
|
+
function evaluateDisplayCondition(condition, formData) {
|
|
156
|
+
if (!condition || !condition.key) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
"Invalid displayIf condition: must have a 'key' property"
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
const actualValue = getValueByPath(formData, condition.key);
|
|
162
|
+
if ("equals" in condition) {
|
|
163
|
+
return deepEqual(actualValue, condition.equals);
|
|
164
|
+
}
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Invalid displayIf condition: no recognized operator (equals, etc.)`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
function deepEqual(a, b) {
|
|
170
|
+
if (a === b) return true;
|
|
171
|
+
if (a == null || b == null) return a === b;
|
|
172
|
+
if (typeof a !== typeof b) return false;
|
|
173
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
174
|
+
try {
|
|
175
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
176
|
+
} catch (e) {
|
|
177
|
+
if (e instanceof TypeError && (e.message.includes("circular") || e.message.includes("cyclic"))) {
|
|
178
|
+
console.warn(
|
|
179
|
+
"deepEqual: Circular reference detected in displayIf comparison, using reference equality"
|
|
180
|
+
);
|
|
181
|
+
return a === b;
|
|
182
|
+
}
|
|
183
|
+
throw e;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return a === b;
|
|
187
|
+
}
|
|
188
|
+
|
|
118
189
|
// src/components/text.ts
|
|
119
190
|
function renderTextElement(element, ctx, wrapper, pathKey) {
|
|
120
191
|
const state = ctx.state;
|
|
@@ -1172,6 +1243,175 @@ function t(key, state) {
|
|
|
1172
1243
|
}
|
|
1173
1244
|
|
|
1174
1245
|
// src/components/file.ts
|
|
1246
|
+
function renderLocalImagePreview(container, file, fileName) {
|
|
1247
|
+
const img = document.createElement("img");
|
|
1248
|
+
img.className = "w-full h-full object-contain";
|
|
1249
|
+
img.alt = fileName || "Preview";
|
|
1250
|
+
const reader = new FileReader();
|
|
1251
|
+
reader.onload = (e) => {
|
|
1252
|
+
var _a;
|
|
1253
|
+
img.src = ((_a = e.target) == null ? void 0 : _a.result) || "";
|
|
1254
|
+
};
|
|
1255
|
+
reader.readAsDataURL(file);
|
|
1256
|
+
container.appendChild(img);
|
|
1257
|
+
}
|
|
1258
|
+
function renderLocalVideoPreview(container, file, videoType, resourceId, state, deps) {
|
|
1259
|
+
const videoUrl = URL.createObjectURL(file);
|
|
1260
|
+
container.onclick = null;
|
|
1261
|
+
const newContainer = container.cloneNode(false);
|
|
1262
|
+
if (container.parentNode) {
|
|
1263
|
+
container.parentNode.replaceChild(newContainer, container);
|
|
1264
|
+
}
|
|
1265
|
+
newContainer.innerHTML = `
|
|
1266
|
+
<div class="relative group h-full">
|
|
1267
|
+
<video class="w-full h-full object-contain" controls preload="auto" muted>
|
|
1268
|
+
<source src="${videoUrl}" type="${videoType}">
|
|
1269
|
+
Your browser does not support the video tag.
|
|
1270
|
+
</video>
|
|
1271
|
+
<div class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity z-10 flex gap-1">
|
|
1272
|
+
<button class="bg-red-600 bg-opacity-75 hover:bg-opacity-90 text-white p-1 rounded text-xs delete-file-btn">
|
|
1273
|
+
${t("removeElement", state)}
|
|
1274
|
+
</button>
|
|
1275
|
+
<button class="bg-gray-800 bg-opacity-75 hover:bg-opacity-90 text-white p-1 rounded text-xs change-file-btn">
|
|
1276
|
+
Change
|
|
1277
|
+
</button>
|
|
1278
|
+
</div>
|
|
1279
|
+
</div>
|
|
1280
|
+
`;
|
|
1281
|
+
attachVideoButtonHandlers(newContainer, resourceId, state, deps);
|
|
1282
|
+
return newContainer;
|
|
1283
|
+
}
|
|
1284
|
+
function attachVideoButtonHandlers(container, resourceId, state, deps) {
|
|
1285
|
+
const changeBtn = container.querySelector(".change-file-btn");
|
|
1286
|
+
if (changeBtn) {
|
|
1287
|
+
changeBtn.onclick = (e) => {
|
|
1288
|
+
e.stopPropagation();
|
|
1289
|
+
if (deps == null ? void 0 : deps.picker) {
|
|
1290
|
+
deps.picker.click();
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
const deleteBtn = container.querySelector(".delete-file-btn");
|
|
1295
|
+
if (deleteBtn) {
|
|
1296
|
+
deleteBtn.onclick = (e) => {
|
|
1297
|
+
e.stopPropagation();
|
|
1298
|
+
handleVideoDelete(container, resourceId, state, deps);
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
function handleVideoDelete(container, resourceId, state, deps) {
|
|
1303
|
+
var _a;
|
|
1304
|
+
state.resourceIndex.delete(resourceId);
|
|
1305
|
+
const hiddenInput = (_a = container.parentElement) == null ? void 0 : _a.querySelector(
|
|
1306
|
+
'input[type="hidden"]'
|
|
1307
|
+
);
|
|
1308
|
+
if (hiddenInput) {
|
|
1309
|
+
hiddenInput.value = "";
|
|
1310
|
+
}
|
|
1311
|
+
if (deps == null ? void 0 : deps.fileUploadHandler) {
|
|
1312
|
+
container.onclick = deps.fileUploadHandler;
|
|
1313
|
+
}
|
|
1314
|
+
if (deps == null ? void 0 : deps.dragHandler) {
|
|
1315
|
+
setupDragAndDrop(container, deps.dragHandler);
|
|
1316
|
+
}
|
|
1317
|
+
container.innerHTML = `
|
|
1318
|
+
<div class="flex flex-col items-center justify-center h-full text-gray-400">
|
|
1319
|
+
<svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
|
|
1320
|
+
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
|
1321
|
+
</svg>
|
|
1322
|
+
<div class="text-sm text-center">${t("clickDragText", state)}</div>
|
|
1323
|
+
</div>
|
|
1324
|
+
`;
|
|
1325
|
+
}
|
|
1326
|
+
function renderUploadedVideoPreview(container, thumbnailUrl, videoType) {
|
|
1327
|
+
const video = document.createElement("video");
|
|
1328
|
+
video.className = "w-full h-full object-contain";
|
|
1329
|
+
video.controls = true;
|
|
1330
|
+
video.preload = "metadata";
|
|
1331
|
+
video.muted = true;
|
|
1332
|
+
const source = document.createElement("source");
|
|
1333
|
+
source.src = thumbnailUrl;
|
|
1334
|
+
source.type = videoType;
|
|
1335
|
+
video.appendChild(source);
|
|
1336
|
+
video.appendChild(document.createTextNode("Your browser does not support the video tag."));
|
|
1337
|
+
container.appendChild(video);
|
|
1338
|
+
}
|
|
1339
|
+
function renderDeleteButton(container, resourceId, state) {
|
|
1340
|
+
addDeleteButton(container, state, () => {
|
|
1341
|
+
var _a;
|
|
1342
|
+
state.resourceIndex.delete(resourceId);
|
|
1343
|
+
const hiddenInput = (_a = container.parentElement) == null ? void 0 : _a.querySelector(
|
|
1344
|
+
'input[type="hidden"]'
|
|
1345
|
+
);
|
|
1346
|
+
if (hiddenInput) {
|
|
1347
|
+
hiddenInput.value = "";
|
|
1348
|
+
}
|
|
1349
|
+
container.innerHTML = `
|
|
1350
|
+
<div class="flex flex-col items-center justify-center h-full text-gray-400">
|
|
1351
|
+
<svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
|
|
1352
|
+
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
|
1353
|
+
</svg>
|
|
1354
|
+
<div class="text-sm text-center">${t("clickDragText", state)}</div>
|
|
1355
|
+
</div>
|
|
1356
|
+
`;
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
async function renderLocalFilePreview(container, meta, fileName, resourceId, isReadonly, state, deps) {
|
|
1360
|
+
if (!meta.file || !(meta.file instanceof File)) {
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
if (meta.type && meta.type.startsWith("image/")) {
|
|
1364
|
+
renderLocalImagePreview(container, meta.file, fileName);
|
|
1365
|
+
} else if (meta.type && meta.type.startsWith("video/")) {
|
|
1366
|
+
const newContainer = renderLocalVideoPreview(
|
|
1367
|
+
container,
|
|
1368
|
+
meta.file,
|
|
1369
|
+
meta.type,
|
|
1370
|
+
resourceId,
|
|
1371
|
+
state,
|
|
1372
|
+
deps
|
|
1373
|
+
);
|
|
1374
|
+
container = newContainer;
|
|
1375
|
+
} else {
|
|
1376
|
+
container.innerHTML = `<div class="flex flex-col items-center justify-center h-full text-gray-400"><div class="text-2xl mb-2">\u{1F4C1}</div><div class="text-sm">${fileName}</div></div>`;
|
|
1377
|
+
}
|
|
1378
|
+
if (!isReadonly && !(meta.type && meta.type.startsWith("video/"))) {
|
|
1379
|
+
renderDeleteButton(container, resourceId, state);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
async function renderUploadedFilePreview(container, resourceId, fileName, meta, state) {
|
|
1383
|
+
if (!state.config.getThumbnail) {
|
|
1384
|
+
setEmptyFileContainer(container, state);
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
try {
|
|
1388
|
+
const thumbnailUrl = await state.config.getThumbnail(resourceId);
|
|
1389
|
+
if (thumbnailUrl) {
|
|
1390
|
+
clear(container);
|
|
1391
|
+
if (meta && meta.type && meta.type.startsWith("video/")) {
|
|
1392
|
+
renderUploadedVideoPreview(container, thumbnailUrl, meta.type);
|
|
1393
|
+
} else {
|
|
1394
|
+
const img = document.createElement("img");
|
|
1395
|
+
img.className = "w-full h-full object-contain";
|
|
1396
|
+
img.alt = fileName || "Preview";
|
|
1397
|
+
img.src = thumbnailUrl;
|
|
1398
|
+
container.appendChild(img);
|
|
1399
|
+
}
|
|
1400
|
+
} else {
|
|
1401
|
+
setEmptyFileContainer(container, state);
|
|
1402
|
+
}
|
|
1403
|
+
} catch (error) {
|
|
1404
|
+
console.error("Failed to get thumbnail:", error);
|
|
1405
|
+
container.innerHTML = `
|
|
1406
|
+
<div class="flex flex-col items-center justify-center h-full text-gray-400">
|
|
1407
|
+
<svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
|
|
1408
|
+
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
|
1409
|
+
</svg>
|
|
1410
|
+
<div class="text-sm text-center">${fileName || "Preview unavailable"}</div>
|
|
1411
|
+
</div>
|
|
1412
|
+
`;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1175
1415
|
async function renderFilePreview(container, resourceId, state, options = {}) {
|
|
1176
1416
|
const { fileName = "", isReadonly = false, deps = null } = options;
|
|
1177
1417
|
if (!isReadonly && deps && (!deps.picker || !deps.fileUploadHandler || !deps.dragHandler)) {
|
|
@@ -1183,144 +1423,19 @@ async function renderFilePreview(container, resourceId, state, options = {}) {
|
|
|
1183
1423
|
if (isReadonly) {
|
|
1184
1424
|
container.classList.add("cursor-pointer");
|
|
1185
1425
|
}
|
|
1186
|
-
const img = document.createElement("img");
|
|
1187
|
-
img.className = "w-full h-full object-contain";
|
|
1188
|
-
img.alt = fileName || "Preview";
|
|
1189
1426
|
const meta = state.resourceIndex.get(resourceId);
|
|
1190
1427
|
if (meta && meta.file && meta.file instanceof File) {
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
const videoUrl = URL.createObjectURL(meta.file);
|
|
1201
|
-
container.onclick = null;
|
|
1202
|
-
const newContainer = container.cloneNode(false);
|
|
1203
|
-
if (container.parentNode) {
|
|
1204
|
-
container.parentNode.replaceChild(newContainer, container);
|
|
1205
|
-
}
|
|
1206
|
-
container = newContainer;
|
|
1207
|
-
container.innerHTML = `
|
|
1208
|
-
<div class="relative group h-full">
|
|
1209
|
-
<video class="w-full h-full object-contain" controls preload="auto" muted>
|
|
1210
|
-
<source src="${videoUrl}" type="${meta.type}">
|
|
1211
|
-
Your browser does not support the video tag.
|
|
1212
|
-
</video>
|
|
1213
|
-
<div class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity z-10 flex gap-1">
|
|
1214
|
-
<button class="bg-red-600 bg-opacity-75 hover:bg-opacity-90 text-white p-1 rounded text-xs delete-file-btn">
|
|
1215
|
-
${t("removeElement", state)}
|
|
1216
|
-
</button>
|
|
1217
|
-
<button class="bg-gray-800 bg-opacity-75 hover:bg-opacity-90 text-white p-1 rounded text-xs change-file-btn">
|
|
1218
|
-
Change
|
|
1219
|
-
</button>
|
|
1220
|
-
</div>
|
|
1221
|
-
</div>
|
|
1222
|
-
`;
|
|
1223
|
-
const changeBtn = container.querySelector(
|
|
1224
|
-
".change-file-btn"
|
|
1225
|
-
);
|
|
1226
|
-
if (changeBtn) {
|
|
1227
|
-
changeBtn.onclick = (e) => {
|
|
1228
|
-
e.stopPropagation();
|
|
1229
|
-
if (deps == null ? void 0 : deps.picker) {
|
|
1230
|
-
deps.picker.click();
|
|
1231
|
-
}
|
|
1232
|
-
};
|
|
1233
|
-
}
|
|
1234
|
-
const deleteBtn = container.querySelector(
|
|
1235
|
-
".delete-file-btn"
|
|
1236
|
-
);
|
|
1237
|
-
if (deleteBtn) {
|
|
1238
|
-
deleteBtn.onclick = (e) => {
|
|
1239
|
-
var _a;
|
|
1240
|
-
e.stopPropagation();
|
|
1241
|
-
state.resourceIndex.delete(resourceId);
|
|
1242
|
-
const hiddenInput = (_a = container.parentElement) == null ? void 0 : _a.querySelector(
|
|
1243
|
-
'input[type="hidden"]'
|
|
1244
|
-
);
|
|
1245
|
-
if (hiddenInput) {
|
|
1246
|
-
hiddenInput.value = "";
|
|
1247
|
-
}
|
|
1248
|
-
if (deps == null ? void 0 : deps.fileUploadHandler) {
|
|
1249
|
-
container.onclick = deps.fileUploadHandler;
|
|
1250
|
-
}
|
|
1251
|
-
if (deps == null ? void 0 : deps.dragHandler) {
|
|
1252
|
-
setupDragAndDrop(container, deps.dragHandler);
|
|
1253
|
-
}
|
|
1254
|
-
container.innerHTML = `
|
|
1255
|
-
<div class="flex flex-col items-center justify-center h-full text-gray-400">
|
|
1256
|
-
<svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
|
|
1257
|
-
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
|
1258
|
-
</svg>
|
|
1259
|
-
<div class="text-sm text-center">${t("clickDragText", state)}</div>
|
|
1260
|
-
</div>
|
|
1261
|
-
`;
|
|
1262
|
-
};
|
|
1263
|
-
}
|
|
1264
|
-
} else {
|
|
1265
|
-
container.innerHTML = `<div class="flex flex-col items-center justify-center h-full text-gray-400"><div class="text-2xl mb-2">\u{1F4C1}</div><div class="text-sm">${fileName}</div></div>`;
|
|
1266
|
-
}
|
|
1267
|
-
if (!isReadonly && !(meta && meta.type && meta.type.startsWith("video/"))) {
|
|
1268
|
-
addDeleteButton(container, state, () => {
|
|
1269
|
-
var _a;
|
|
1270
|
-
state.resourceIndex.delete(resourceId);
|
|
1271
|
-
const hiddenInput = (_a = container.parentElement) == null ? void 0 : _a.querySelector(
|
|
1272
|
-
'input[type="hidden"]'
|
|
1273
|
-
);
|
|
1274
|
-
if (hiddenInput) {
|
|
1275
|
-
hiddenInput.value = "";
|
|
1276
|
-
}
|
|
1277
|
-
container.innerHTML = `
|
|
1278
|
-
<div class="flex flex-col items-center justify-center h-full text-gray-400">
|
|
1279
|
-
<svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
|
|
1280
|
-
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
|
1281
|
-
</svg>
|
|
1282
|
-
<div class="text-sm text-center">${t("clickDragText", state)}</div>
|
|
1283
|
-
</div>
|
|
1284
|
-
`;
|
|
1285
|
-
});
|
|
1286
|
-
}
|
|
1287
|
-
} else if (state.config.getThumbnail) {
|
|
1288
|
-
try {
|
|
1289
|
-
const thumbnailUrl = await state.config.getThumbnail(resourceId);
|
|
1290
|
-
if (thumbnailUrl) {
|
|
1291
|
-
clear(container);
|
|
1292
|
-
if (meta && meta.type && meta.type.startsWith("video/")) {
|
|
1293
|
-
const video = document.createElement("video");
|
|
1294
|
-
video.className = "w-full h-full object-contain";
|
|
1295
|
-
video.controls = true;
|
|
1296
|
-
video.preload = "metadata";
|
|
1297
|
-
video.muted = true;
|
|
1298
|
-
const source = document.createElement("source");
|
|
1299
|
-
source.src = thumbnailUrl;
|
|
1300
|
-
source.type = meta.type;
|
|
1301
|
-
video.appendChild(source);
|
|
1302
|
-
video.appendChild(document.createTextNode("Your browser does not support the video tag."));
|
|
1303
|
-
container.appendChild(video);
|
|
1304
|
-
} else {
|
|
1305
|
-
img.src = thumbnailUrl;
|
|
1306
|
-
container.appendChild(img);
|
|
1307
|
-
}
|
|
1308
|
-
} else {
|
|
1309
|
-
setEmptyFileContainer(container, state);
|
|
1310
|
-
}
|
|
1311
|
-
} catch (error) {
|
|
1312
|
-
console.error("Failed to get thumbnail:", error);
|
|
1313
|
-
container.innerHTML = `
|
|
1314
|
-
<div class="flex flex-col items-center justify-center h-full text-gray-400">
|
|
1315
|
-
<svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
|
|
1316
|
-
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
|
1317
|
-
</svg>
|
|
1318
|
-
<div class="text-sm text-center">${fileName || "Preview unavailable"}</div>
|
|
1319
|
-
</div>
|
|
1320
|
-
`;
|
|
1321
|
-
}
|
|
1428
|
+
await renderLocalFilePreview(
|
|
1429
|
+
container,
|
|
1430
|
+
meta,
|
|
1431
|
+
fileName,
|
|
1432
|
+
resourceId,
|
|
1433
|
+
isReadonly,
|
|
1434
|
+
state,
|
|
1435
|
+
deps
|
|
1436
|
+
);
|
|
1322
1437
|
} else {
|
|
1323
|
-
|
|
1438
|
+
await renderUploadedFilePreview(container, resourceId, fileName, meta, state);
|
|
1324
1439
|
}
|
|
1325
1440
|
}
|
|
1326
1441
|
async function renderFilePreviewReadonly(resourceId, state, fileName) {
|
|
@@ -2235,6 +2350,464 @@ function updateFileField(element, fieldPath, value, context) {
|
|
|
2235
2350
|
}
|
|
2236
2351
|
}
|
|
2237
2352
|
|
|
2353
|
+
// src/components/colour.ts
|
|
2354
|
+
function normalizeColourValue(value) {
|
|
2355
|
+
if (!value) return "#000000";
|
|
2356
|
+
return value.toUpperCase();
|
|
2357
|
+
}
|
|
2358
|
+
function isValidHexColour(value) {
|
|
2359
|
+
return /^#[0-9A-F]{6}$/i.test(value) || /^#[0-9A-F]{3}$/i.test(value);
|
|
2360
|
+
}
|
|
2361
|
+
function expandHexColour(value) {
|
|
2362
|
+
if (/^#[0-9A-F]{3}$/i.test(value)) {
|
|
2363
|
+
const r = value[1];
|
|
2364
|
+
const g = value[2];
|
|
2365
|
+
const b = value[3];
|
|
2366
|
+
return `#${r}${r}${g}${g}${b}${b}`.toUpperCase();
|
|
2367
|
+
}
|
|
2368
|
+
return value.toUpperCase();
|
|
2369
|
+
}
|
|
2370
|
+
function createReadonlyColourUI(value) {
|
|
2371
|
+
const container = document.createElement("div");
|
|
2372
|
+
container.className = "flex items-center gap-2";
|
|
2373
|
+
const normalizedValue = normalizeColourValue(value);
|
|
2374
|
+
const swatch = document.createElement("div");
|
|
2375
|
+
swatch.style.cssText = `
|
|
2376
|
+
width: 32px;
|
|
2377
|
+
height: 32px;
|
|
2378
|
+
border-radius: var(--fb-border-radius);
|
|
2379
|
+
border: var(--fb-border-width) solid var(--fb-border-color);
|
|
2380
|
+
background-color: ${normalizedValue};
|
|
2381
|
+
`;
|
|
2382
|
+
const hexText = document.createElement("span");
|
|
2383
|
+
hexText.style.cssText = `
|
|
2384
|
+
font-size: var(--fb-font-size);
|
|
2385
|
+
color: var(--fb-text-color);
|
|
2386
|
+
font-family: var(--fb-font-family-mono, monospace);
|
|
2387
|
+
`;
|
|
2388
|
+
hexText.textContent = normalizedValue;
|
|
2389
|
+
container.appendChild(swatch);
|
|
2390
|
+
container.appendChild(hexText);
|
|
2391
|
+
return container;
|
|
2392
|
+
}
|
|
2393
|
+
function createEditColourUI(value, pathKey, ctx) {
|
|
2394
|
+
const normalizedValue = normalizeColourValue(value);
|
|
2395
|
+
const pickerWrapper = document.createElement("div");
|
|
2396
|
+
pickerWrapper.className = "colour-picker-wrapper";
|
|
2397
|
+
pickerWrapper.style.cssText = `
|
|
2398
|
+
display: flex;
|
|
2399
|
+
align-items: center;
|
|
2400
|
+
gap: 8px;
|
|
2401
|
+
`;
|
|
2402
|
+
const swatch = document.createElement("div");
|
|
2403
|
+
swatch.className = "colour-swatch";
|
|
2404
|
+
swatch.style.cssText = `
|
|
2405
|
+
width: 40px;
|
|
2406
|
+
height: 40px;
|
|
2407
|
+
border-radius: var(--fb-border-radius);
|
|
2408
|
+
border: var(--fb-border-width) solid var(--fb-border-color);
|
|
2409
|
+
background-color: ${normalizedValue};
|
|
2410
|
+
cursor: pointer;
|
|
2411
|
+
transition: border-color var(--fb-transition-duration) ease-in-out;
|
|
2412
|
+
flex-shrink: 0;
|
|
2413
|
+
`;
|
|
2414
|
+
const hexInput = document.createElement("input");
|
|
2415
|
+
hexInput.type = "text";
|
|
2416
|
+
hexInput.className = "colour-hex-input";
|
|
2417
|
+
hexInput.name = pathKey;
|
|
2418
|
+
hexInput.value = normalizedValue;
|
|
2419
|
+
hexInput.placeholder = "#000000";
|
|
2420
|
+
hexInput.style.cssText = `
|
|
2421
|
+
width: 100px;
|
|
2422
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
2423
|
+
border: var(--fb-border-width) solid var(--fb-border-color);
|
|
2424
|
+
border-radius: var(--fb-border-radius);
|
|
2425
|
+
background-color: var(--fb-background-color);
|
|
2426
|
+
color: var(--fb-text-color);
|
|
2427
|
+
font-size: var(--fb-font-size);
|
|
2428
|
+
font-family: var(--fb-font-family-mono, monospace);
|
|
2429
|
+
transition: all var(--fb-transition-duration) ease-in-out;
|
|
2430
|
+
`;
|
|
2431
|
+
const colourInput = document.createElement("input");
|
|
2432
|
+
colourInput.type = "color";
|
|
2433
|
+
colourInput.className = "colour-picker-hidden";
|
|
2434
|
+
colourInput.value = normalizedValue.toLowerCase();
|
|
2435
|
+
colourInput.style.cssText = `
|
|
2436
|
+
position: absolute;
|
|
2437
|
+
opacity: 0;
|
|
2438
|
+
pointer-events: none;
|
|
2439
|
+
`;
|
|
2440
|
+
hexInput.addEventListener("input", () => {
|
|
2441
|
+
const inputValue = hexInput.value.trim();
|
|
2442
|
+
if (isValidHexColour(inputValue)) {
|
|
2443
|
+
const expanded = expandHexColour(inputValue);
|
|
2444
|
+
swatch.style.backgroundColor = expanded;
|
|
2445
|
+
colourInput.value = expanded.toLowerCase();
|
|
2446
|
+
hexInput.classList.remove("invalid");
|
|
2447
|
+
if (ctx.instance) {
|
|
2448
|
+
ctx.instance.triggerOnChange(pathKey, expanded);
|
|
2449
|
+
}
|
|
2450
|
+
} else {
|
|
2451
|
+
hexInput.classList.add("invalid");
|
|
2452
|
+
}
|
|
2453
|
+
});
|
|
2454
|
+
hexInput.addEventListener("blur", () => {
|
|
2455
|
+
const inputValue = hexInput.value.trim();
|
|
2456
|
+
if (isValidHexColour(inputValue)) {
|
|
2457
|
+
const expanded = expandHexColour(inputValue);
|
|
2458
|
+
hexInput.value = expanded;
|
|
2459
|
+
swatch.style.backgroundColor = expanded;
|
|
2460
|
+
colourInput.value = expanded.toLowerCase();
|
|
2461
|
+
hexInput.classList.remove("invalid");
|
|
2462
|
+
}
|
|
2463
|
+
});
|
|
2464
|
+
colourInput.addEventListener("change", () => {
|
|
2465
|
+
const normalized = normalizeColourValue(colourInput.value);
|
|
2466
|
+
hexInput.value = normalized;
|
|
2467
|
+
swatch.style.backgroundColor = normalized;
|
|
2468
|
+
if (ctx.instance) {
|
|
2469
|
+
ctx.instance.triggerOnChange(pathKey, normalized);
|
|
2470
|
+
}
|
|
2471
|
+
});
|
|
2472
|
+
swatch.addEventListener("click", () => {
|
|
2473
|
+
colourInput.click();
|
|
2474
|
+
});
|
|
2475
|
+
swatch.addEventListener("mouseenter", () => {
|
|
2476
|
+
swatch.style.borderColor = "var(--fb-border-hover-color)";
|
|
2477
|
+
});
|
|
2478
|
+
swatch.addEventListener("mouseleave", () => {
|
|
2479
|
+
swatch.style.borderColor = "var(--fb-border-color)";
|
|
2480
|
+
});
|
|
2481
|
+
hexInput.addEventListener("focus", () => {
|
|
2482
|
+
hexInput.style.borderColor = "var(--fb-border-focus-color)";
|
|
2483
|
+
hexInput.style.outline = `var(--fb-focus-ring-width) solid var(--fb-focus-ring-color)`;
|
|
2484
|
+
hexInput.style.outlineOffset = "0";
|
|
2485
|
+
});
|
|
2486
|
+
hexInput.addEventListener("blur", () => {
|
|
2487
|
+
hexInput.style.borderColor = "var(--fb-border-color)";
|
|
2488
|
+
hexInput.style.outline = "none";
|
|
2489
|
+
});
|
|
2490
|
+
hexInput.addEventListener("mouseenter", () => {
|
|
2491
|
+
if (document.activeElement !== hexInput) {
|
|
2492
|
+
hexInput.style.borderColor = "var(--fb-border-hover-color)";
|
|
2493
|
+
}
|
|
2494
|
+
});
|
|
2495
|
+
hexInput.addEventListener("mouseleave", () => {
|
|
2496
|
+
if (document.activeElement !== hexInput) {
|
|
2497
|
+
hexInput.style.borderColor = "var(--fb-border-color)";
|
|
2498
|
+
}
|
|
2499
|
+
});
|
|
2500
|
+
pickerWrapper.appendChild(swatch);
|
|
2501
|
+
pickerWrapper.appendChild(hexInput);
|
|
2502
|
+
pickerWrapper.appendChild(colourInput);
|
|
2503
|
+
return pickerWrapper;
|
|
2504
|
+
}
|
|
2505
|
+
function renderColourElement(element, ctx, wrapper, pathKey) {
|
|
2506
|
+
const state = ctx.state;
|
|
2507
|
+
const initialValue = ctx.prefill[element.key] || element.default || "#000000";
|
|
2508
|
+
if (state.config.readonly) {
|
|
2509
|
+
const readonlyUI = createReadonlyColourUI(initialValue);
|
|
2510
|
+
wrapper.appendChild(readonlyUI);
|
|
2511
|
+
} else {
|
|
2512
|
+
const editUI = createEditColourUI(initialValue, pathKey, ctx);
|
|
2513
|
+
wrapper.appendChild(editUI);
|
|
2514
|
+
}
|
|
2515
|
+
const colourHint = document.createElement("p");
|
|
2516
|
+
colourHint.className = "mt-1";
|
|
2517
|
+
colourHint.style.cssText = `
|
|
2518
|
+
font-size: var(--fb-font-size-small);
|
|
2519
|
+
color: var(--fb-text-secondary-color);
|
|
2520
|
+
`;
|
|
2521
|
+
colourHint.textContent = makeFieldHint(element);
|
|
2522
|
+
wrapper.appendChild(colourHint);
|
|
2523
|
+
}
|
|
2524
|
+
function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
|
|
2525
|
+
var _a, _b;
|
|
2526
|
+
const state = ctx.state;
|
|
2527
|
+
const prefillValues = ctx.prefill[element.key] || [];
|
|
2528
|
+
const values = Array.isArray(prefillValues) ? [...prefillValues] : [];
|
|
2529
|
+
const minCount = (_a = element.minCount) != null ? _a : 1;
|
|
2530
|
+
const maxCount = (_b = element.maxCount) != null ? _b : Infinity;
|
|
2531
|
+
while (values.length < minCount) {
|
|
2532
|
+
values.push(element.default || "#000000");
|
|
2533
|
+
}
|
|
2534
|
+
const container = document.createElement("div");
|
|
2535
|
+
container.className = "space-y-2";
|
|
2536
|
+
wrapper.appendChild(container);
|
|
2537
|
+
function updateIndices() {
|
|
2538
|
+
const items = container.querySelectorAll(".multiple-colour-item");
|
|
2539
|
+
items.forEach((item, index) => {
|
|
2540
|
+
const input = item.querySelector("input");
|
|
2541
|
+
if (input) {
|
|
2542
|
+
input.name = `${pathKey}[${index}]`;
|
|
2543
|
+
}
|
|
2544
|
+
});
|
|
2545
|
+
}
|
|
2546
|
+
function addColourItem(value = "#000000", index = -1) {
|
|
2547
|
+
const itemWrapper = document.createElement("div");
|
|
2548
|
+
itemWrapper.className = "multiple-colour-item flex items-center gap-2";
|
|
2549
|
+
if (state.config.readonly) {
|
|
2550
|
+
const readonlyUI = createReadonlyColourUI(value);
|
|
2551
|
+
while (readonlyUI.firstChild) {
|
|
2552
|
+
itemWrapper.appendChild(readonlyUI.firstChild);
|
|
2553
|
+
}
|
|
2554
|
+
} else {
|
|
2555
|
+
const tempPathKey = `${pathKey}[${container.children.length}]`;
|
|
2556
|
+
const editUI = createEditColourUI(value, tempPathKey, ctx);
|
|
2557
|
+
editUI.style.flex = "1";
|
|
2558
|
+
itemWrapper.appendChild(editUI);
|
|
2559
|
+
}
|
|
2560
|
+
if (index === -1) {
|
|
2561
|
+
container.appendChild(itemWrapper);
|
|
2562
|
+
} else {
|
|
2563
|
+
container.insertBefore(itemWrapper, container.children[index]);
|
|
2564
|
+
}
|
|
2565
|
+
updateIndices();
|
|
2566
|
+
return itemWrapper;
|
|
2567
|
+
}
|
|
2568
|
+
function updateRemoveButtons() {
|
|
2569
|
+
if (state.config.readonly) return;
|
|
2570
|
+
const items = container.querySelectorAll(".multiple-colour-item");
|
|
2571
|
+
const currentCount = items.length;
|
|
2572
|
+
items.forEach((item) => {
|
|
2573
|
+
let removeBtn = item.querySelector(
|
|
2574
|
+
".remove-item-btn"
|
|
2575
|
+
);
|
|
2576
|
+
if (!removeBtn) {
|
|
2577
|
+
removeBtn = document.createElement("button");
|
|
2578
|
+
removeBtn.type = "button";
|
|
2579
|
+
removeBtn.className = "remove-item-btn px-2 py-1 rounded";
|
|
2580
|
+
removeBtn.style.cssText = `
|
|
2581
|
+
color: var(--fb-error-color);
|
|
2582
|
+
background-color: transparent;
|
|
2583
|
+
transition: background-color var(--fb-transition-duration);
|
|
2584
|
+
`;
|
|
2585
|
+
removeBtn.innerHTML = "\u2715";
|
|
2586
|
+
removeBtn.addEventListener("mouseenter", () => {
|
|
2587
|
+
removeBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
2588
|
+
});
|
|
2589
|
+
removeBtn.addEventListener("mouseleave", () => {
|
|
2590
|
+
removeBtn.style.backgroundColor = "transparent";
|
|
2591
|
+
});
|
|
2592
|
+
removeBtn.onclick = () => {
|
|
2593
|
+
const currentIndex = Array.from(container.children).indexOf(
|
|
2594
|
+
item
|
|
2595
|
+
);
|
|
2596
|
+
if (container.children.length > minCount) {
|
|
2597
|
+
values.splice(currentIndex, 1);
|
|
2598
|
+
item.remove();
|
|
2599
|
+
updateIndices();
|
|
2600
|
+
updateAddButton();
|
|
2601
|
+
updateRemoveButtons();
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
item.appendChild(removeBtn);
|
|
2605
|
+
}
|
|
2606
|
+
const disabled = currentCount <= minCount;
|
|
2607
|
+
removeBtn.disabled = disabled;
|
|
2608
|
+
removeBtn.style.opacity = disabled ? "0.5" : "1";
|
|
2609
|
+
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
function updateAddButton() {
|
|
2613
|
+
const existingAddBtn = wrapper.querySelector(".add-colour-btn");
|
|
2614
|
+
if (existingAddBtn) existingAddBtn.remove();
|
|
2615
|
+
if (!state.config.readonly && values.length < maxCount) {
|
|
2616
|
+
const addBtn = document.createElement("button");
|
|
2617
|
+
addBtn.type = "button";
|
|
2618
|
+
addBtn.className = "add-colour-btn mt-2 px-3 py-1 rounded";
|
|
2619
|
+
addBtn.style.cssText = `
|
|
2620
|
+
color: var(--fb-primary-color);
|
|
2621
|
+
border: var(--fb-border-width) solid var(--fb-primary-color);
|
|
2622
|
+
background-color: transparent;
|
|
2623
|
+
font-size: var(--fb-font-size);
|
|
2624
|
+
transition: all var(--fb-transition-duration);
|
|
2625
|
+
`;
|
|
2626
|
+
addBtn.textContent = `+ Add ${element.label || "Colour"}`;
|
|
2627
|
+
addBtn.addEventListener("mouseenter", () => {
|
|
2628
|
+
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
2629
|
+
});
|
|
2630
|
+
addBtn.addEventListener("mouseleave", () => {
|
|
2631
|
+
addBtn.style.backgroundColor = "transparent";
|
|
2632
|
+
});
|
|
2633
|
+
addBtn.onclick = () => {
|
|
2634
|
+
const defaultColour = element.default || "#000000";
|
|
2635
|
+
values.push(defaultColour);
|
|
2636
|
+
addColourItem(defaultColour);
|
|
2637
|
+
updateAddButton();
|
|
2638
|
+
updateRemoveButtons();
|
|
2639
|
+
};
|
|
2640
|
+
wrapper.appendChild(addBtn);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
values.forEach((value) => addColourItem(value));
|
|
2644
|
+
updateAddButton();
|
|
2645
|
+
updateRemoveButtons();
|
|
2646
|
+
const hint = document.createElement("p");
|
|
2647
|
+
hint.className = "mt-1";
|
|
2648
|
+
hint.style.cssText = `
|
|
2649
|
+
font-size: var(--fb-font-size-small);
|
|
2650
|
+
color: var(--fb-text-secondary-color);
|
|
2651
|
+
`;
|
|
2652
|
+
hint.textContent = makeFieldHint(element);
|
|
2653
|
+
wrapper.appendChild(hint);
|
|
2654
|
+
}
|
|
2655
|
+
function validateColourElement(element, key, context) {
|
|
2656
|
+
var _a, _b, _c;
|
|
2657
|
+
const errors = [];
|
|
2658
|
+
const { scopeRoot, skipValidation } = context;
|
|
2659
|
+
const markValidity = (input, errorMessage) => {
|
|
2660
|
+
var _a2, _b2;
|
|
2661
|
+
if (!input) return;
|
|
2662
|
+
const errorId = `error-${input.getAttribute("name") || Math.random().toString(36).substring(7)}`;
|
|
2663
|
+
let errorElement = document.getElementById(errorId);
|
|
2664
|
+
if (errorMessage) {
|
|
2665
|
+
input.classList.add("invalid");
|
|
2666
|
+
input.title = errorMessage;
|
|
2667
|
+
if (!errorElement) {
|
|
2668
|
+
errorElement = document.createElement("div");
|
|
2669
|
+
errorElement.id = errorId;
|
|
2670
|
+
errorElement.className = "error-message";
|
|
2671
|
+
errorElement.style.cssText = `
|
|
2672
|
+
color: var(--fb-error-color);
|
|
2673
|
+
font-size: var(--fb-font-size-small);
|
|
2674
|
+
margin-top: 0.25rem;
|
|
2675
|
+
`;
|
|
2676
|
+
if (input.nextSibling) {
|
|
2677
|
+
(_a2 = input.parentNode) == null ? void 0 : _a2.insertBefore(errorElement, input.nextSibling);
|
|
2678
|
+
} else {
|
|
2679
|
+
(_b2 = input.parentNode) == null ? void 0 : _b2.appendChild(errorElement);
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
errorElement.textContent = errorMessage;
|
|
2683
|
+
errorElement.style.display = "block";
|
|
2684
|
+
} else {
|
|
2685
|
+
input.classList.remove("invalid");
|
|
2686
|
+
input.title = "";
|
|
2687
|
+
if (errorElement) {
|
|
2688
|
+
errorElement.remove();
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
};
|
|
2692
|
+
const validateColourValue = (input, val, fieldKey) => {
|
|
2693
|
+
if (!val) {
|
|
2694
|
+
if (!skipValidation && element.required) {
|
|
2695
|
+
errors.push(`${fieldKey}: required`);
|
|
2696
|
+
markValidity(input, "required");
|
|
2697
|
+
return "";
|
|
2698
|
+
}
|
|
2699
|
+
markValidity(input, null);
|
|
2700
|
+
return "";
|
|
2701
|
+
}
|
|
2702
|
+
const normalized = normalizeColourValue(val);
|
|
2703
|
+
if (!skipValidation && !isValidHexColour(normalized)) {
|
|
2704
|
+
errors.push(`${fieldKey}: invalid hex colour format`);
|
|
2705
|
+
markValidity(input, "invalid hex colour format");
|
|
2706
|
+
return val;
|
|
2707
|
+
}
|
|
2708
|
+
markValidity(input, null);
|
|
2709
|
+
return normalized;
|
|
2710
|
+
};
|
|
2711
|
+
if (element.multiple) {
|
|
2712
|
+
const hexInputs = scopeRoot.querySelectorAll(
|
|
2713
|
+
`.colour-hex-input`
|
|
2714
|
+
);
|
|
2715
|
+
const values = [];
|
|
2716
|
+
hexInputs.forEach((input, index) => {
|
|
2717
|
+
var _a2;
|
|
2718
|
+
const val = (_a2 = input == null ? void 0 : input.value) != null ? _a2 : "";
|
|
2719
|
+
const validated = validateColourValue(input, val, `${key}[${index}]`);
|
|
2720
|
+
values.push(validated);
|
|
2721
|
+
});
|
|
2722
|
+
if (!skipValidation) {
|
|
2723
|
+
const minCount = (_a = element.minCount) != null ? _a : 1;
|
|
2724
|
+
const maxCount = (_b = element.maxCount) != null ? _b : Infinity;
|
|
2725
|
+
const filteredValues = values.filter((v) => v !== "");
|
|
2726
|
+
if (element.required && filteredValues.length === 0) {
|
|
2727
|
+
errors.push(`${key}: required`);
|
|
2728
|
+
}
|
|
2729
|
+
if (filteredValues.length < minCount) {
|
|
2730
|
+
errors.push(`${key}: minimum ${minCount} items required`);
|
|
2731
|
+
}
|
|
2732
|
+
if (filteredValues.length > maxCount) {
|
|
2733
|
+
errors.push(`${key}: maximum ${maxCount} items allowed`);
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
return { value: values, errors };
|
|
2737
|
+
} else {
|
|
2738
|
+
const hexInput = scopeRoot.querySelector(
|
|
2739
|
+
`[name="${key}"].colour-hex-input`
|
|
2740
|
+
);
|
|
2741
|
+
const val = (_c = hexInput == null ? void 0 : hexInput.value) != null ? _c : "";
|
|
2742
|
+
if (!skipValidation && element.required && val === "") {
|
|
2743
|
+
errors.push(`${key}: required`);
|
|
2744
|
+
markValidity(hexInput, "required");
|
|
2745
|
+
return { value: "", errors };
|
|
2746
|
+
}
|
|
2747
|
+
const validated = validateColourValue(hexInput, val, key);
|
|
2748
|
+
return { value: validated, errors };
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
function updateColourField(element, fieldPath, value, context) {
|
|
2752
|
+
const { scopeRoot } = context;
|
|
2753
|
+
if (element.multiple) {
|
|
2754
|
+
if (!Array.isArray(value)) {
|
|
2755
|
+
console.warn(
|
|
2756
|
+
`updateColourField: Expected array for multiple field "${fieldPath}", got ${typeof value}`
|
|
2757
|
+
);
|
|
2758
|
+
return;
|
|
2759
|
+
}
|
|
2760
|
+
const hexInputs = scopeRoot.querySelectorAll(
|
|
2761
|
+
`.colour-hex-input`
|
|
2762
|
+
);
|
|
2763
|
+
hexInputs.forEach((hexInput, index) => {
|
|
2764
|
+
if (index < value.length) {
|
|
2765
|
+
const normalized = normalizeColourValue(value[index]);
|
|
2766
|
+
hexInput.value = normalized;
|
|
2767
|
+
hexInput.classList.remove("invalid");
|
|
2768
|
+
hexInput.title = "";
|
|
2769
|
+
const wrapper = hexInput.closest(".colour-picker-wrapper");
|
|
2770
|
+
if (wrapper) {
|
|
2771
|
+
const swatch = wrapper.querySelector(".colour-swatch");
|
|
2772
|
+
const colourInput = wrapper.querySelector(".colour-picker-hidden");
|
|
2773
|
+
if (swatch) {
|
|
2774
|
+
swatch.style.backgroundColor = normalized;
|
|
2775
|
+
}
|
|
2776
|
+
if (colourInput) {
|
|
2777
|
+
colourInput.value = normalized.toLowerCase();
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
});
|
|
2782
|
+
if (value.length !== hexInputs.length) {
|
|
2783
|
+
console.warn(
|
|
2784
|
+
`updateColourField: Multiple field "${fieldPath}" has ${hexInputs.length} inputs but received ${value.length} values. Consider re-rendering for add/remove.`
|
|
2785
|
+
);
|
|
2786
|
+
}
|
|
2787
|
+
} else {
|
|
2788
|
+
const hexInput = scopeRoot.querySelector(
|
|
2789
|
+
`[name="${fieldPath}"].colour-hex-input`
|
|
2790
|
+
);
|
|
2791
|
+
if (hexInput) {
|
|
2792
|
+
const normalized = normalizeColourValue(value);
|
|
2793
|
+
hexInput.value = normalized;
|
|
2794
|
+
hexInput.classList.remove("invalid");
|
|
2795
|
+
hexInput.title = "";
|
|
2796
|
+
const wrapper = hexInput.closest(".colour-picker-wrapper");
|
|
2797
|
+
if (wrapper) {
|
|
2798
|
+
const swatch = wrapper.querySelector(".colour-swatch");
|
|
2799
|
+
const colourInput = wrapper.querySelector(".colour-picker-hidden");
|
|
2800
|
+
if (swatch) {
|
|
2801
|
+
swatch.style.backgroundColor = normalized;
|
|
2802
|
+
}
|
|
2803
|
+
if (colourInput) {
|
|
2804
|
+
colourInput.value = normalized.toLowerCase();
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2810
|
+
|
|
2238
2811
|
// src/components/container.ts
|
|
2239
2812
|
var renderElementFunc = null;
|
|
2240
2813
|
function setRenderElement(fn) {
|
|
@@ -2249,7 +2822,7 @@ function renderElement(element, ctx) {
|
|
|
2249
2822
|
return renderElementFunc(element, ctx);
|
|
2250
2823
|
}
|
|
2251
2824
|
function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
2252
|
-
var _a;
|
|
2825
|
+
var _a, _b;
|
|
2253
2826
|
const containerWrap = document.createElement("div");
|
|
2254
2827
|
containerWrap.className = "border border-gray-200 rounded-lg p-4 bg-gray-50";
|
|
2255
2828
|
containerWrap.setAttribute("data-container", pathKey);
|
|
@@ -2264,6 +2837,9 @@ function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
|
2264
2837
|
const subCtx = {
|
|
2265
2838
|
path: pathJoin(ctx.path, element.key),
|
|
2266
2839
|
prefill: ((_a = ctx.prefill) == null ? void 0 : _a[element.key]) || {},
|
|
2840
|
+
// Sliced data for value population
|
|
2841
|
+
formData: (_b = ctx.formData) != null ? _b : ctx.prefill,
|
|
2842
|
+
// Complete root data for displayIf evaluation
|
|
2267
2843
|
state: ctx.state
|
|
2268
2844
|
};
|
|
2269
2845
|
element.elements.forEach((child) => {
|
|
@@ -2276,7 +2852,7 @@ function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
|
2276
2852
|
wrapper.appendChild(containerWrap);
|
|
2277
2853
|
}
|
|
2278
2854
|
function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
2279
|
-
var _a, _b, _c;
|
|
2855
|
+
var _a, _b, _c, _d;
|
|
2280
2856
|
const state = ctx.state;
|
|
2281
2857
|
const containerWrap = document.createElement("div");
|
|
2282
2858
|
containerWrap.className = "border border-gray-200 rounded-lg p-4 bg-gray-50";
|
|
@@ -2303,12 +2879,15 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
2303
2879
|
add.className = "px-3 py-1.5 bg-blue-600 text-white text-sm rounded-lg hover:bg-blue-700 transition-colors";
|
|
2304
2880
|
add.textContent = t("addElement", state);
|
|
2305
2881
|
add.onclick = () => {
|
|
2882
|
+
var _a2;
|
|
2306
2883
|
if (countItems() < max) {
|
|
2307
2884
|
const idx = countItems();
|
|
2308
2885
|
const subCtx = {
|
|
2309
2886
|
state: ctx.state,
|
|
2310
2887
|
path: pathJoin(ctx.path, `${element.key}[${idx}]`),
|
|
2311
|
-
prefill: {}
|
|
2888
|
+
prefill: {},
|
|
2889
|
+
formData: (_a2 = ctx.formData) != null ? _a2 : ctx.prefill
|
|
2890
|
+
// Complete root data for displayIf
|
|
2312
2891
|
};
|
|
2313
2892
|
const item = document.createElement("div");
|
|
2314
2893
|
item.className = "containerItem border border-gray-300 rounded-lg p-4 bg-white";
|
|
@@ -2350,10 +2929,13 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
2350
2929
|
}
|
|
2351
2930
|
if (pre && Array.isArray(pre)) {
|
|
2352
2931
|
pre.forEach((prefillObj, idx) => {
|
|
2932
|
+
var _a2;
|
|
2353
2933
|
const subCtx = {
|
|
2354
2934
|
state: ctx.state,
|
|
2355
2935
|
path: pathJoin(ctx.path, `${element.key}[${idx}]`),
|
|
2356
|
-
prefill: prefillObj || {}
|
|
2936
|
+
prefill: prefillObj || {},
|
|
2937
|
+
formData: (_a2 = ctx.formData) != null ? _a2 : ctx.prefill
|
|
2938
|
+
// Complete root data for displayIf
|
|
2357
2939
|
};
|
|
2358
2940
|
const item = document.createElement("div");
|
|
2359
2941
|
item.className = "containerItem border border-gray-300 rounded-lg p-4 bg-white";
|
|
@@ -2384,7 +2966,9 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
2384
2966
|
const subCtx = {
|
|
2385
2967
|
state: ctx.state,
|
|
2386
2968
|
path: pathJoin(ctx.path, `${element.key}[${idx}]`),
|
|
2387
|
-
prefill: {}
|
|
2969
|
+
prefill: {},
|
|
2970
|
+
formData: (_d = ctx.formData) != null ? _d : ctx.prefill
|
|
2971
|
+
// Complete root data for displayIf
|
|
2388
2972
|
};
|
|
2389
2973
|
const item = document.createElement("div");
|
|
2390
2974
|
item.className = "containerItem border border-gray-300 rounded-lg p-4 bg-white";
|
|
@@ -2667,11 +3251,34 @@ if (typeof document !== "undefined") {
|
|
|
2667
3251
|
}
|
|
2668
3252
|
});
|
|
2669
3253
|
}
|
|
2670
|
-
function
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
3254
|
+
function checkDisplayCondition(element, ctx) {
|
|
3255
|
+
var _a;
|
|
3256
|
+
if (!element.displayIf) {
|
|
3257
|
+
return null;
|
|
3258
|
+
}
|
|
3259
|
+
try {
|
|
3260
|
+
const dataForCondition = (_a = ctx.formData) != null ? _a : ctx.prefill;
|
|
3261
|
+
const shouldDisplay = evaluateDisplayCondition(
|
|
3262
|
+
element.displayIf,
|
|
3263
|
+
dataForCondition
|
|
3264
|
+
);
|
|
3265
|
+
if (!shouldDisplay) {
|
|
3266
|
+
const hiddenWrapper = document.createElement("div");
|
|
3267
|
+
hiddenWrapper.className = "fb-field-wrapper-hidden";
|
|
3268
|
+
hiddenWrapper.style.display = "none";
|
|
3269
|
+
hiddenWrapper.setAttribute("data-field-key", element.key);
|
|
3270
|
+
hiddenWrapper.setAttribute("data-conditionally-hidden", "true");
|
|
3271
|
+
return hiddenWrapper;
|
|
3272
|
+
}
|
|
3273
|
+
} catch (error) {
|
|
3274
|
+
console.error(
|
|
3275
|
+
`Error evaluating displayIf for field "${element.key}":`,
|
|
3276
|
+
error
|
|
3277
|
+
);
|
|
3278
|
+
}
|
|
3279
|
+
return null;
|
|
3280
|
+
}
|
|
3281
|
+
function createFieldLabel(element) {
|
|
2675
3282
|
const title = document.createElement("label");
|
|
2676
3283
|
title.className = "text-sm font-medium text-gray-900";
|
|
2677
3284
|
title.textContent = element.label || element.key;
|
|
@@ -2681,59 +3288,71 @@ function renderElement2(element, ctx) {
|
|
|
2681
3288
|
req.textContent = "*";
|
|
2682
3289
|
title.appendChild(req);
|
|
2683
3290
|
}
|
|
3291
|
+
return title;
|
|
3292
|
+
}
|
|
3293
|
+
function createInfoButton(element) {
|
|
3294
|
+
const infoBtn = document.createElement("button");
|
|
3295
|
+
infoBtn.type = "button";
|
|
3296
|
+
infoBtn.className = "ml-2 text-gray-400 hover:text-gray-600";
|
|
3297
|
+
infoBtn.innerHTML = '<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>';
|
|
3298
|
+
const tooltipId = `tooltip-${element.key}-${Math.random().toString(36).substr(2, 9)}`;
|
|
3299
|
+
const tooltip = document.createElement("div");
|
|
3300
|
+
tooltip.id = tooltipId;
|
|
3301
|
+
tooltip.className = "hidden absolute z-50 bg-gray-200 text-gray-900 text-sm rounded-lg p-3 max-w-sm border border-gray-300 shadow-lg";
|
|
3302
|
+
tooltip.style.position = "fixed";
|
|
3303
|
+
tooltip.textContent = element.description || element.hint || "Field information";
|
|
3304
|
+
document.body.appendChild(tooltip);
|
|
3305
|
+
infoBtn.onclick = (e) => {
|
|
3306
|
+
e.preventDefault();
|
|
3307
|
+
e.stopPropagation();
|
|
3308
|
+
showTooltip(tooltipId, infoBtn);
|
|
3309
|
+
};
|
|
3310
|
+
return infoBtn;
|
|
3311
|
+
}
|
|
3312
|
+
function createLabelContainer(element) {
|
|
3313
|
+
const label = document.createElement("div");
|
|
3314
|
+
label.className = "flex items-center mb-2";
|
|
3315
|
+
const title = createFieldLabel(element);
|
|
2684
3316
|
label.appendChild(title);
|
|
2685
3317
|
if (element.description || element.hint) {
|
|
2686
|
-
const infoBtn =
|
|
2687
|
-
infoBtn.type = "button";
|
|
2688
|
-
infoBtn.className = "ml-2 text-gray-400 hover:text-gray-600";
|
|
2689
|
-
infoBtn.innerHTML = '<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>';
|
|
2690
|
-
const tooltipId = `tooltip-${element.key}-${Math.random().toString(36).substr(2, 9)}`;
|
|
2691
|
-
const tooltip = document.createElement("div");
|
|
2692
|
-
tooltip.id = tooltipId;
|
|
2693
|
-
tooltip.className = "hidden absolute z-50 bg-gray-200 text-gray-900 text-sm rounded-lg p-3 max-w-sm border border-gray-300 shadow-lg";
|
|
2694
|
-
tooltip.style.position = "fixed";
|
|
2695
|
-
tooltip.textContent = element.description || element.hint || "Field information";
|
|
2696
|
-
document.body.appendChild(tooltip);
|
|
2697
|
-
infoBtn.onclick = (e) => {
|
|
2698
|
-
e.preventDefault();
|
|
2699
|
-
e.stopPropagation();
|
|
2700
|
-
showTooltip(tooltipId, infoBtn);
|
|
2701
|
-
};
|
|
3318
|
+
const infoBtn = createInfoButton(element);
|
|
2702
3319
|
label.appendChild(infoBtn);
|
|
2703
3320
|
}
|
|
2704
|
-
|
|
2705
|
-
|
|
3321
|
+
return label;
|
|
3322
|
+
}
|
|
3323
|
+
function dispatchToRenderer(element, ctx, wrapper, pathKey) {
|
|
3324
|
+
const isMultiple = "multiple" in element && element.multiple;
|
|
2706
3325
|
switch (element.type) {
|
|
2707
3326
|
case "text":
|
|
2708
|
-
if (
|
|
3327
|
+
if (isMultiple) {
|
|
2709
3328
|
renderMultipleTextElement(element, ctx, wrapper, pathKey);
|
|
2710
3329
|
} else {
|
|
2711
3330
|
renderTextElement(element, ctx, wrapper, pathKey);
|
|
2712
3331
|
}
|
|
2713
3332
|
break;
|
|
2714
3333
|
case "textarea":
|
|
2715
|
-
if (
|
|
3334
|
+
if (isMultiple) {
|
|
2716
3335
|
renderMultipleTextareaElement(element, ctx, wrapper, pathKey);
|
|
2717
3336
|
} else {
|
|
2718
3337
|
renderTextareaElement(element, ctx, wrapper, pathKey);
|
|
2719
3338
|
}
|
|
2720
3339
|
break;
|
|
2721
3340
|
case "number":
|
|
2722
|
-
if (
|
|
3341
|
+
if (isMultiple) {
|
|
2723
3342
|
renderMultipleNumberElement(element, ctx, wrapper, pathKey);
|
|
2724
3343
|
} else {
|
|
2725
3344
|
renderNumberElement(element, ctx, wrapper, pathKey);
|
|
2726
3345
|
}
|
|
2727
3346
|
break;
|
|
2728
3347
|
case "select":
|
|
2729
|
-
if (
|
|
3348
|
+
if (isMultiple) {
|
|
2730
3349
|
renderMultipleSelectElement(element, ctx, wrapper, pathKey);
|
|
2731
3350
|
} else {
|
|
2732
3351
|
renderSelectElement(element, ctx, wrapper, pathKey);
|
|
2733
3352
|
}
|
|
2734
3353
|
break;
|
|
2735
3354
|
case "file":
|
|
2736
|
-
if (
|
|
3355
|
+
if (isMultiple) {
|
|
2737
3356
|
renderMultipleFileElement(element, ctx, wrapper, pathKey);
|
|
2738
3357
|
} else {
|
|
2739
3358
|
renderFileElement(element, ctx, wrapper, pathKey);
|
|
@@ -2742,11 +3361,18 @@ function renderElement2(element, ctx) {
|
|
|
2742
3361
|
case "files":
|
|
2743
3362
|
renderFilesElement(element, ctx, wrapper, pathKey);
|
|
2744
3363
|
break;
|
|
3364
|
+
case "colour":
|
|
3365
|
+
if (isMultiple) {
|
|
3366
|
+
renderMultipleColourElement(element, ctx, wrapper, pathKey);
|
|
3367
|
+
} else {
|
|
3368
|
+
renderColourElement(element, ctx, wrapper, pathKey);
|
|
3369
|
+
}
|
|
3370
|
+
break;
|
|
2745
3371
|
case "group":
|
|
2746
3372
|
renderGroupElement(element, ctx, wrapper, pathKey);
|
|
2747
3373
|
break;
|
|
2748
3374
|
case "container":
|
|
2749
|
-
if (
|
|
3375
|
+
if (isMultiple) {
|
|
2750
3376
|
renderMultipleContainerElement(element, ctx, wrapper);
|
|
2751
3377
|
} else {
|
|
2752
3378
|
renderSingleContainerElement(element, ctx, wrapper, pathKey);
|
|
@@ -2759,6 +3385,19 @@ function renderElement2(element, ctx) {
|
|
|
2759
3385
|
wrapper.appendChild(unsupported);
|
|
2760
3386
|
}
|
|
2761
3387
|
}
|
|
3388
|
+
}
|
|
3389
|
+
function renderElement2(element, ctx) {
|
|
3390
|
+
const hiddenElement = checkDisplayCondition(element, ctx);
|
|
3391
|
+
if (hiddenElement) {
|
|
3392
|
+
return hiddenElement;
|
|
3393
|
+
}
|
|
3394
|
+
const wrapper = document.createElement("div");
|
|
3395
|
+
wrapper.className = "mb-6 fb-field-wrapper";
|
|
3396
|
+
wrapper.setAttribute("data-field-key", element.key);
|
|
3397
|
+
const label = createLabelContainer(element);
|
|
3398
|
+
wrapper.appendChild(label);
|
|
3399
|
+
const pathKey = pathJoin(ctx.path, element.key);
|
|
3400
|
+
dispatchToRenderer(element, ctx, wrapper, pathKey);
|
|
2762
3401
|
return wrapper;
|
|
2763
3402
|
}
|
|
2764
3403
|
setRenderElement(renderElement2);
|
|
@@ -3042,6 +3681,10 @@ var componentRegistry = {
|
|
|
3042
3681
|
validate: validateFileElement,
|
|
3043
3682
|
update: updateFileField
|
|
3044
3683
|
},
|
|
3684
|
+
colour: {
|
|
3685
|
+
validate: validateColourElement,
|
|
3686
|
+
update: updateColourField
|
|
3687
|
+
},
|
|
3045
3688
|
container: {
|
|
3046
3689
|
validate: validateContainerElement,
|
|
3047
3690
|
update: updateContainerField
|
|
@@ -3162,6 +3805,7 @@ var FormBuilderInstance = class {
|
|
|
3162
3805
|
}
|
|
3163
3806
|
this.state.debounceTimer = setTimeout(() => {
|
|
3164
3807
|
const formData = this.validateForm(true);
|
|
3808
|
+
this.reevaluateConditionalFields();
|
|
3165
3809
|
if (this.state.config.onChange) {
|
|
3166
3810
|
this.state.config.onChange(formData);
|
|
3167
3811
|
}
|
|
@@ -3421,6 +4065,8 @@ var FormBuilderInstance = class {
|
|
|
3421
4065
|
const block = renderElement2(element, {
|
|
3422
4066
|
path: "",
|
|
3423
4067
|
prefill: prefill || {},
|
|
4068
|
+
formData: prefill || {},
|
|
4069
|
+
// Pass complete root data for displayIf evaluation
|
|
3424
4070
|
state: this.state,
|
|
3425
4071
|
instance: this
|
|
3426
4072
|
});
|
|
@@ -3465,6 +4111,19 @@ var FormBuilderInstance = class {
|
|
|
3465
4111
|
};
|
|
3466
4112
|
setValidateElement(validateElement2);
|
|
3467
4113
|
this.state.schema.elements.forEach((element) => {
|
|
4114
|
+
if (element.displayIf) {
|
|
4115
|
+
try {
|
|
4116
|
+
const shouldDisplay = evaluateDisplayCondition(element.displayIf, data);
|
|
4117
|
+
if (!shouldDisplay) {
|
|
4118
|
+
return;
|
|
4119
|
+
}
|
|
4120
|
+
} catch (error) {
|
|
4121
|
+
console.error(
|
|
4122
|
+
`Error evaluating displayIf for field "${element.key}" during validation:`,
|
|
4123
|
+
error
|
|
4124
|
+
);
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
3468
4127
|
if (element.hidden) {
|
|
3469
4128
|
data[element.key] = element.default !== void 0 ? element.default : null;
|
|
3470
4129
|
} else {
|
|
@@ -3621,6 +4280,71 @@ var FormBuilderInstance = class {
|
|
|
3621
4280
|
);
|
|
3622
4281
|
}
|
|
3623
4282
|
}
|
|
4283
|
+
/**
|
|
4284
|
+
* Re-evaluate all conditional fields (displayIf) based on current form data
|
|
4285
|
+
* This is called automatically when form data changes (via onChange events)
|
|
4286
|
+
*/
|
|
4287
|
+
reevaluateConditionalFields() {
|
|
4288
|
+
if (!this.state.schema || !this.state.formRoot) return;
|
|
4289
|
+
const formData = this.validateForm(true).data;
|
|
4290
|
+
const checkElements = (elements, currentPath) => {
|
|
4291
|
+
elements.forEach((element) => {
|
|
4292
|
+
const fullPath = currentPath ? `${currentPath}.${element.key}` : element.key;
|
|
4293
|
+
if (element.displayIf) {
|
|
4294
|
+
const fieldWrappers = this.state.formRoot.querySelectorAll(
|
|
4295
|
+
`[data-field-key="${element.key}"]`
|
|
4296
|
+
);
|
|
4297
|
+
fieldWrappers.forEach((wrapper) => {
|
|
4298
|
+
var _a, _b;
|
|
4299
|
+
try {
|
|
4300
|
+
const shouldDisplay = evaluateDisplayCondition(
|
|
4301
|
+
element.displayIf,
|
|
4302
|
+
formData
|
|
4303
|
+
// Use complete formData for condition evaluation
|
|
4304
|
+
);
|
|
4305
|
+
const isCurrentlyHidden = wrapper.getAttribute("data-conditionally-hidden") === "true";
|
|
4306
|
+
if (shouldDisplay && isCurrentlyHidden) {
|
|
4307
|
+
const newWrapper = renderElement2(element, {
|
|
4308
|
+
path: fullPath,
|
|
4309
|
+
// Use accumulated path
|
|
4310
|
+
prefill: formData,
|
|
4311
|
+
// Use complete formData for root-level elements
|
|
4312
|
+
formData,
|
|
4313
|
+
// Pass complete formData for displayIf evaluation
|
|
4314
|
+
state: this.state,
|
|
4315
|
+
instance: this
|
|
4316
|
+
});
|
|
4317
|
+
(_a = wrapper.parentNode) == null ? void 0 : _a.replaceChild(newWrapper, wrapper);
|
|
4318
|
+
} else if (!shouldDisplay && !isCurrentlyHidden) {
|
|
4319
|
+
const hiddenWrapper = document.createElement("div");
|
|
4320
|
+
hiddenWrapper.className = "fb-field-wrapper-hidden";
|
|
4321
|
+
hiddenWrapper.style.display = "none";
|
|
4322
|
+
hiddenWrapper.setAttribute("data-field-key", element.key);
|
|
4323
|
+
hiddenWrapper.setAttribute("data-conditionally-hidden", "true");
|
|
4324
|
+
(_b = wrapper.parentNode) == null ? void 0 : _b.replaceChild(hiddenWrapper, wrapper);
|
|
4325
|
+
}
|
|
4326
|
+
} catch (error) {
|
|
4327
|
+
console.error(
|
|
4328
|
+
`Error re-evaluating displayIf for field "${element.key}":`,
|
|
4329
|
+
error
|
|
4330
|
+
);
|
|
4331
|
+
}
|
|
4332
|
+
});
|
|
4333
|
+
}
|
|
4334
|
+
if ((element.type === "container" || element.type === "group") && "elements" in element && element.elements) {
|
|
4335
|
+
const containerData = formData == null ? void 0 : formData[element.key];
|
|
4336
|
+
if (Array.isArray(containerData)) {
|
|
4337
|
+
containerData.forEach((_, index) => {
|
|
4338
|
+
checkElements(element.elements, `${fullPath}[${index}]`);
|
|
4339
|
+
});
|
|
4340
|
+
} else {
|
|
4341
|
+
checkElements(element.elements, fullPath);
|
|
4342
|
+
}
|
|
4343
|
+
}
|
|
4344
|
+
});
|
|
4345
|
+
};
|
|
4346
|
+
checkElements(this.state.schema.elements, "");
|
|
4347
|
+
}
|
|
3624
4348
|
/**
|
|
3625
4349
|
* Destroy instance and clean up resources
|
|
3626
4350
|
*/
|