@dmitryvim/form-builder 0.2.6 → 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/dist/browser/formbuilder.min.js +118 -61
- package/dist/browser/formbuilder.v0.2.7.min.js +241 -0
- package/dist/cjs/index.cjs +722 -185
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +715 -182
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +118 -61
- package/dist/types/components/colour.d.ts +18 -0
- package/dist/types/components/index.d.ts +2 -1
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/schema.d.ts +8 -1
- package/package.json +1 -1
- package/dist/browser/formbuilder.v0.2.6.min.js +0 -184
package/dist/cjs/index.cjs
CHANGED
|
@@ -1243,6 +1243,175 @@ function t(key, state) {
|
|
|
1243
1243
|
}
|
|
1244
1244
|
|
|
1245
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
|
+
}
|
|
1246
1415
|
async function renderFilePreview(container, resourceId, state, options = {}) {
|
|
1247
1416
|
const { fileName = "", isReadonly = false, deps = null } = options;
|
|
1248
1417
|
if (!isReadonly && deps && (!deps.picker || !deps.fileUploadHandler || !deps.dragHandler)) {
|
|
@@ -1254,144 +1423,19 @@ async function renderFilePreview(container, resourceId, state, options = {}) {
|
|
|
1254
1423
|
if (isReadonly) {
|
|
1255
1424
|
container.classList.add("cursor-pointer");
|
|
1256
1425
|
}
|
|
1257
|
-
const img = document.createElement("img");
|
|
1258
|
-
img.className = "w-full h-full object-contain";
|
|
1259
|
-
img.alt = fileName || "Preview";
|
|
1260
1426
|
const meta = state.resourceIndex.get(resourceId);
|
|
1261
1427
|
if (meta && meta.file && meta.file instanceof File) {
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
const videoUrl = URL.createObjectURL(meta.file);
|
|
1272
|
-
container.onclick = null;
|
|
1273
|
-
const newContainer = container.cloneNode(false);
|
|
1274
|
-
if (container.parentNode) {
|
|
1275
|
-
container.parentNode.replaceChild(newContainer, container);
|
|
1276
|
-
}
|
|
1277
|
-
container = newContainer;
|
|
1278
|
-
container.innerHTML = `
|
|
1279
|
-
<div class="relative group h-full">
|
|
1280
|
-
<video class="w-full h-full object-contain" controls preload="auto" muted>
|
|
1281
|
-
<source src="${videoUrl}" type="${meta.type}">
|
|
1282
|
-
Your browser does not support the video tag.
|
|
1283
|
-
</video>
|
|
1284
|
-
<div class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity z-10 flex gap-1">
|
|
1285
|
-
<button class="bg-red-600 bg-opacity-75 hover:bg-opacity-90 text-white p-1 rounded text-xs delete-file-btn">
|
|
1286
|
-
${t("removeElement", state)}
|
|
1287
|
-
</button>
|
|
1288
|
-
<button class="bg-gray-800 bg-opacity-75 hover:bg-opacity-90 text-white p-1 rounded text-xs change-file-btn">
|
|
1289
|
-
Change
|
|
1290
|
-
</button>
|
|
1291
|
-
</div>
|
|
1292
|
-
</div>
|
|
1293
|
-
`;
|
|
1294
|
-
const changeBtn = container.querySelector(
|
|
1295
|
-
".change-file-btn"
|
|
1296
|
-
);
|
|
1297
|
-
if (changeBtn) {
|
|
1298
|
-
changeBtn.onclick = (e) => {
|
|
1299
|
-
e.stopPropagation();
|
|
1300
|
-
if (deps == null ? void 0 : deps.picker) {
|
|
1301
|
-
deps.picker.click();
|
|
1302
|
-
}
|
|
1303
|
-
};
|
|
1304
|
-
}
|
|
1305
|
-
const deleteBtn = container.querySelector(
|
|
1306
|
-
".delete-file-btn"
|
|
1307
|
-
);
|
|
1308
|
-
if (deleteBtn) {
|
|
1309
|
-
deleteBtn.onclick = (e) => {
|
|
1310
|
-
var _a;
|
|
1311
|
-
e.stopPropagation();
|
|
1312
|
-
state.resourceIndex.delete(resourceId);
|
|
1313
|
-
const hiddenInput = (_a = container.parentElement) == null ? void 0 : _a.querySelector(
|
|
1314
|
-
'input[type="hidden"]'
|
|
1315
|
-
);
|
|
1316
|
-
if (hiddenInput) {
|
|
1317
|
-
hiddenInput.value = "";
|
|
1318
|
-
}
|
|
1319
|
-
if (deps == null ? void 0 : deps.fileUploadHandler) {
|
|
1320
|
-
container.onclick = deps.fileUploadHandler;
|
|
1321
|
-
}
|
|
1322
|
-
if (deps == null ? void 0 : deps.dragHandler) {
|
|
1323
|
-
setupDragAndDrop(container, deps.dragHandler);
|
|
1324
|
-
}
|
|
1325
|
-
container.innerHTML = `
|
|
1326
|
-
<div class="flex flex-col items-center justify-center h-full text-gray-400">
|
|
1327
|
-
<svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
|
|
1328
|
-
<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"/>
|
|
1329
|
-
</svg>
|
|
1330
|
-
<div class="text-sm text-center">${t("clickDragText", state)}</div>
|
|
1331
|
-
</div>
|
|
1332
|
-
`;
|
|
1333
|
-
};
|
|
1334
|
-
}
|
|
1335
|
-
} else {
|
|
1336
|
-
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>`;
|
|
1337
|
-
}
|
|
1338
|
-
if (!isReadonly && !(meta && meta.type && meta.type.startsWith("video/"))) {
|
|
1339
|
-
addDeleteButton(container, state, () => {
|
|
1340
|
-
var _a;
|
|
1341
|
-
state.resourceIndex.delete(resourceId);
|
|
1342
|
-
const hiddenInput = (_a = container.parentElement) == null ? void 0 : _a.querySelector(
|
|
1343
|
-
'input[type="hidden"]'
|
|
1344
|
-
);
|
|
1345
|
-
if (hiddenInput) {
|
|
1346
|
-
hiddenInput.value = "";
|
|
1347
|
-
}
|
|
1348
|
-
container.innerHTML = `
|
|
1349
|
-
<div class="flex flex-col items-center justify-center h-full text-gray-400">
|
|
1350
|
-
<svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
|
|
1351
|
-
<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"/>
|
|
1352
|
-
</svg>
|
|
1353
|
-
<div class="text-sm text-center">${t("clickDragText", state)}</div>
|
|
1354
|
-
</div>
|
|
1355
|
-
`;
|
|
1356
|
-
});
|
|
1357
|
-
}
|
|
1358
|
-
} else if (state.config.getThumbnail) {
|
|
1359
|
-
try {
|
|
1360
|
-
const thumbnailUrl = await state.config.getThumbnail(resourceId);
|
|
1361
|
-
if (thumbnailUrl) {
|
|
1362
|
-
clear(container);
|
|
1363
|
-
if (meta && meta.type && meta.type.startsWith("video/")) {
|
|
1364
|
-
const video = document.createElement("video");
|
|
1365
|
-
video.className = "w-full h-full object-contain";
|
|
1366
|
-
video.controls = true;
|
|
1367
|
-
video.preload = "metadata";
|
|
1368
|
-
video.muted = true;
|
|
1369
|
-
const source = document.createElement("source");
|
|
1370
|
-
source.src = thumbnailUrl;
|
|
1371
|
-
source.type = meta.type;
|
|
1372
|
-
video.appendChild(source);
|
|
1373
|
-
video.appendChild(document.createTextNode("Your browser does not support the video tag."));
|
|
1374
|
-
container.appendChild(video);
|
|
1375
|
-
} else {
|
|
1376
|
-
img.src = thumbnailUrl;
|
|
1377
|
-
container.appendChild(img);
|
|
1378
|
-
}
|
|
1379
|
-
} else {
|
|
1380
|
-
setEmptyFileContainer(container, state);
|
|
1381
|
-
}
|
|
1382
|
-
} catch (error) {
|
|
1383
|
-
console.error("Failed to get thumbnail:", error);
|
|
1384
|
-
container.innerHTML = `
|
|
1385
|
-
<div class="flex flex-col items-center justify-center h-full text-gray-400">
|
|
1386
|
-
<svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
|
|
1387
|
-
<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"/>
|
|
1388
|
-
</svg>
|
|
1389
|
-
<div class="text-sm text-center">${fileName || "Preview unavailable"}</div>
|
|
1390
|
-
</div>
|
|
1391
|
-
`;
|
|
1392
|
-
}
|
|
1428
|
+
await renderLocalFilePreview(
|
|
1429
|
+
container,
|
|
1430
|
+
meta,
|
|
1431
|
+
fileName,
|
|
1432
|
+
resourceId,
|
|
1433
|
+
isReadonly,
|
|
1434
|
+
state,
|
|
1435
|
+
deps
|
|
1436
|
+
);
|
|
1393
1437
|
} else {
|
|
1394
|
-
|
|
1438
|
+
await renderUploadedFilePreview(container, resourceId, fileName, meta, state);
|
|
1395
1439
|
}
|
|
1396
1440
|
}
|
|
1397
1441
|
async function renderFilePreviewReadonly(resourceId, state, fileName) {
|
|
@@ -2306,6 +2350,464 @@ function updateFileField(element, fieldPath, value, context) {
|
|
|
2306
2350
|
}
|
|
2307
2351
|
}
|
|
2308
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
|
+
|
|
2309
2811
|
// src/components/container.ts
|
|
2310
2812
|
var renderElementFunc = null;
|
|
2311
2813
|
function setRenderElement(fn) {
|
|
@@ -2749,35 +3251,34 @@ if (typeof document !== "undefined") {
|
|
|
2749
3251
|
}
|
|
2750
3252
|
});
|
|
2751
3253
|
}
|
|
2752
|
-
function
|
|
3254
|
+
function checkDisplayCondition(element, ctx) {
|
|
2753
3255
|
var _a;
|
|
2754
|
-
if (element.displayIf) {
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
console.error(
|
|
2771
|
-
`Error evaluating displayIf for field "${element.key}":`,
|
|
2772
|
-
error
|
|
2773
|
-
);
|
|
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;
|
|
2774
3272
|
}
|
|
3273
|
+
} catch (error) {
|
|
3274
|
+
console.error(
|
|
3275
|
+
`Error evaluating displayIf for field "${element.key}":`,
|
|
3276
|
+
error
|
|
3277
|
+
);
|
|
2775
3278
|
}
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
const label = document.createElement("div");
|
|
2780
|
-
label.className = "flex items-center mb-2";
|
|
3279
|
+
return null;
|
|
3280
|
+
}
|
|
3281
|
+
function createFieldLabel(element) {
|
|
2781
3282
|
const title = document.createElement("label");
|
|
2782
3283
|
title.className = "text-sm font-medium text-gray-900";
|
|
2783
3284
|
title.textContent = element.label || element.key;
|
|
@@ -2787,59 +3288,71 @@ function renderElement2(element, ctx) {
|
|
|
2787
3288
|
req.textContent = "*";
|
|
2788
3289
|
title.appendChild(req);
|
|
2789
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);
|
|
2790
3316
|
label.appendChild(title);
|
|
2791
3317
|
if (element.description || element.hint) {
|
|
2792
|
-
const infoBtn =
|
|
2793
|
-
infoBtn.type = "button";
|
|
2794
|
-
infoBtn.className = "ml-2 text-gray-400 hover:text-gray-600";
|
|
2795
|
-
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>';
|
|
2796
|
-
const tooltipId = `tooltip-${element.key}-${Math.random().toString(36).substr(2, 9)}`;
|
|
2797
|
-
const tooltip = document.createElement("div");
|
|
2798
|
-
tooltip.id = tooltipId;
|
|
2799
|
-
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";
|
|
2800
|
-
tooltip.style.position = "fixed";
|
|
2801
|
-
tooltip.textContent = element.description || element.hint || "Field information";
|
|
2802
|
-
document.body.appendChild(tooltip);
|
|
2803
|
-
infoBtn.onclick = (e) => {
|
|
2804
|
-
e.preventDefault();
|
|
2805
|
-
e.stopPropagation();
|
|
2806
|
-
showTooltip(tooltipId, infoBtn);
|
|
2807
|
-
};
|
|
3318
|
+
const infoBtn = createInfoButton(element);
|
|
2808
3319
|
label.appendChild(infoBtn);
|
|
2809
3320
|
}
|
|
2810
|
-
|
|
2811
|
-
|
|
3321
|
+
return label;
|
|
3322
|
+
}
|
|
3323
|
+
function dispatchToRenderer(element, ctx, wrapper, pathKey) {
|
|
3324
|
+
const isMultiple = "multiple" in element && element.multiple;
|
|
2812
3325
|
switch (element.type) {
|
|
2813
3326
|
case "text":
|
|
2814
|
-
if (
|
|
3327
|
+
if (isMultiple) {
|
|
2815
3328
|
renderMultipleTextElement(element, ctx, wrapper, pathKey);
|
|
2816
3329
|
} else {
|
|
2817
3330
|
renderTextElement(element, ctx, wrapper, pathKey);
|
|
2818
3331
|
}
|
|
2819
3332
|
break;
|
|
2820
3333
|
case "textarea":
|
|
2821
|
-
if (
|
|
3334
|
+
if (isMultiple) {
|
|
2822
3335
|
renderMultipleTextareaElement(element, ctx, wrapper, pathKey);
|
|
2823
3336
|
} else {
|
|
2824
3337
|
renderTextareaElement(element, ctx, wrapper, pathKey);
|
|
2825
3338
|
}
|
|
2826
3339
|
break;
|
|
2827
3340
|
case "number":
|
|
2828
|
-
if (
|
|
3341
|
+
if (isMultiple) {
|
|
2829
3342
|
renderMultipleNumberElement(element, ctx, wrapper, pathKey);
|
|
2830
3343
|
} else {
|
|
2831
3344
|
renderNumberElement(element, ctx, wrapper, pathKey);
|
|
2832
3345
|
}
|
|
2833
3346
|
break;
|
|
2834
3347
|
case "select":
|
|
2835
|
-
if (
|
|
3348
|
+
if (isMultiple) {
|
|
2836
3349
|
renderMultipleSelectElement(element, ctx, wrapper, pathKey);
|
|
2837
3350
|
} else {
|
|
2838
3351
|
renderSelectElement(element, ctx, wrapper, pathKey);
|
|
2839
3352
|
}
|
|
2840
3353
|
break;
|
|
2841
3354
|
case "file":
|
|
2842
|
-
if (
|
|
3355
|
+
if (isMultiple) {
|
|
2843
3356
|
renderMultipleFileElement(element, ctx, wrapper, pathKey);
|
|
2844
3357
|
} else {
|
|
2845
3358
|
renderFileElement(element, ctx, wrapper, pathKey);
|
|
@@ -2848,11 +3361,18 @@ function renderElement2(element, ctx) {
|
|
|
2848
3361
|
case "files":
|
|
2849
3362
|
renderFilesElement(element, ctx, wrapper, pathKey);
|
|
2850
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;
|
|
2851
3371
|
case "group":
|
|
2852
3372
|
renderGroupElement(element, ctx, wrapper, pathKey);
|
|
2853
3373
|
break;
|
|
2854
3374
|
case "container":
|
|
2855
|
-
if (
|
|
3375
|
+
if (isMultiple) {
|
|
2856
3376
|
renderMultipleContainerElement(element, ctx, wrapper);
|
|
2857
3377
|
} else {
|
|
2858
3378
|
renderSingleContainerElement(element, ctx, wrapper, pathKey);
|
|
@@ -2865,6 +3385,19 @@ function renderElement2(element, ctx) {
|
|
|
2865
3385
|
wrapper.appendChild(unsupported);
|
|
2866
3386
|
}
|
|
2867
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);
|
|
2868
3401
|
return wrapper;
|
|
2869
3402
|
}
|
|
2870
3403
|
setRenderElement(renderElement2);
|
|
@@ -3148,6 +3681,10 @@ var componentRegistry = {
|
|
|
3148
3681
|
validate: validateFileElement,
|
|
3149
3682
|
update: updateFileField
|
|
3150
3683
|
},
|
|
3684
|
+
colour: {
|
|
3685
|
+
validate: validateColourElement,
|
|
3686
|
+
update: updateColourField
|
|
3687
|
+
},
|
|
3151
3688
|
container: {
|
|
3152
3689
|
validate: validateContainerElement,
|
|
3153
3690
|
update: updateContainerField
|