@unlev/exeq 0.1.5 → 0.1.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 +1 -0
- package/dist/index.css +4 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +129 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +129 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -95,7 +95,7 @@ function createField(type, assignee, page, x, y, existingFields) {
|
|
|
95
95
|
type,
|
|
96
96
|
label: uniqueLabel(baseLabel, existingLabels),
|
|
97
97
|
placeholder: defaults.placeholder || "",
|
|
98
|
-
required: true,
|
|
98
|
+
required: type === "checkbox" || type === "blackout" || type === "whiteout" ? false : true,
|
|
99
99
|
assignee,
|
|
100
100
|
page,
|
|
101
101
|
x,
|
|
@@ -311,7 +311,8 @@ function FieldOverlayItem({
|
|
|
311
311
|
borderColor: isRedact ? field.type === "blackout" ? "#666" : "#bbb" : color,
|
|
312
312
|
borderStyle: isRedact ? "dashed" : "solid",
|
|
313
313
|
backgroundColor: isRedact ? field.type === "blackout" ? "#000000" : "#ffffff" : isSelected ? `${color}22` : `${color}11`,
|
|
314
|
-
cursor: mode === "designer" ? "move" : "default"
|
|
314
|
+
cursor: mode === "designer" ? "move" : "default",
|
|
315
|
+
pointerEvents: mode === "signer" && isRedact ? "none" : void 0
|
|
315
316
|
},
|
|
316
317
|
onClick: (e) => {
|
|
317
318
|
e.stopPropagation();
|
|
@@ -416,7 +417,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
416
417
|
}
|
|
417
418
|
)
|
|
418
419
|
] }),
|
|
419
|
-
!isRedactField && /* @__PURE__ */ jsx2("div", { className: "panel-field", children: /* @__PURE__ */ jsxs2("label", { className: "panel-checkbox-label", children: [
|
|
420
|
+
!isRedactField && field.type !== "checkbox" && /* @__PURE__ */ jsx2("div", { className: "panel-field", children: /* @__PURE__ */ jsxs2("label", { className: "panel-checkbox-label", children: [
|
|
420
421
|
/* @__PURE__ */ jsx2(
|
|
421
422
|
"input",
|
|
422
423
|
{
|
|
@@ -516,10 +517,20 @@ function SignatureCanvas({
|
|
|
516
517
|
}
|
|
517
518
|
}, [paths, currentPath, width, height]);
|
|
518
519
|
const getPoint = useCallback2((e) => {
|
|
519
|
-
const
|
|
520
|
+
const canvas = canvasRef.current;
|
|
521
|
+
const rect = canvas.getBoundingClientRect();
|
|
522
|
+
const style = getComputedStyle(canvas);
|
|
523
|
+
const borderL = parseFloat(style.borderLeftWidth) || 0;
|
|
524
|
+
const borderT = parseFloat(style.borderTopWidth) || 0;
|
|
525
|
+
const borderR = parseFloat(style.borderRightWidth) || 0;
|
|
526
|
+
const borderB = parseFloat(style.borderBottomWidth) || 0;
|
|
527
|
+
const contentW = rect.width - borderL - borderR;
|
|
528
|
+
const contentH = rect.height - borderT - borderB;
|
|
529
|
+
const scaleX = canvas.width / contentW;
|
|
530
|
+
const scaleY = canvas.height / contentH;
|
|
520
531
|
return [
|
|
521
|
-
e.clientX - rect.left,
|
|
522
|
-
e.clientY - rect.top,
|
|
532
|
+
(e.clientX - rect.left - borderL) * scaleX,
|
|
533
|
+
(e.clientY - rect.top - borderT) * scaleY,
|
|
523
534
|
e.pressure
|
|
524
535
|
];
|
|
525
536
|
}, []);
|
|
@@ -628,8 +639,10 @@ function DesignerView({
|
|
|
628
639
|
const [newRoleName, setNewRoleName] = useState2("");
|
|
629
640
|
const [draggingFieldType, setDraggingFieldType] = useState2(null);
|
|
630
641
|
const [panelWidth, setPanelWidth] = useState2(380);
|
|
642
|
+
const [clipboardField, setClipboardField] = useState2(null);
|
|
631
643
|
const dragGhostRef = useRef3(null);
|
|
632
644
|
const resizingRef = useRef3(false);
|
|
645
|
+
const lastStylesRef = useRef3({});
|
|
633
646
|
useEffect2(() => {
|
|
634
647
|
const pdfUrl = initialPdfUrl || initialTemplate?.pdfUrl;
|
|
635
648
|
if (pdfUrl) {
|
|
@@ -677,15 +690,24 @@ function DesignerView({
|
|
|
677
690
|
}, [loadPdf]);
|
|
678
691
|
const handlePageClick = useCallback3((page, x, y) => {
|
|
679
692
|
const field = createField(activeFieldType, activeRole, page, x, y, fields);
|
|
680
|
-
|
|
681
|
-
|
|
693
|
+
const sticky = lastStylesRef.current[activeFieldType];
|
|
694
|
+
const styledField = sticky ? { ...field, ...sticky } : field;
|
|
695
|
+
setFields((prev) => [...prev, styledField]);
|
|
696
|
+
setSelectedFieldId(styledField.id);
|
|
682
697
|
setRightTab("properties");
|
|
683
698
|
}, [activeFieldType, activeRole, fields]);
|
|
684
699
|
const handleFieldMove = useCallback3((id, page, x, y) => {
|
|
685
700
|
setFields((prev) => prev.map((f) => f.id === id ? { ...f, page, x, y } : f));
|
|
686
701
|
}, []);
|
|
687
702
|
const handleFieldResize = useCallback3((id, width, height) => {
|
|
688
|
-
setFields((prev) =>
|
|
703
|
+
setFields((prev) => {
|
|
704
|
+
const field = prev.find((f) => f.id === id);
|
|
705
|
+
if (field) {
|
|
706
|
+
const existing = lastStylesRef.current[field.type] || {};
|
|
707
|
+
lastStylesRef.current[field.type] = { ...existing, width, height, fontSize: field.fontSize };
|
|
708
|
+
}
|
|
709
|
+
return prev.map((f) => f.id === id ? { ...f, width, height } : f);
|
|
710
|
+
});
|
|
689
711
|
}, []);
|
|
690
712
|
const handleFieldUpdate = useCallback3((id, updates) => {
|
|
691
713
|
setFields((prev) => {
|
|
@@ -693,7 +715,18 @@ function DesignerView({
|
|
|
693
715
|
const otherLabels = prev.filter((f) => f.id !== id).map((f) => f.label);
|
|
694
716
|
updates.label = uniqueLabel(updates.label, otherLabels);
|
|
695
717
|
}
|
|
696
|
-
|
|
718
|
+
const updated = prev.map((f) => f.id === id ? { ...f, ...updates } : f);
|
|
719
|
+
const field = prev.find((f) => f.id === id);
|
|
720
|
+
if (field && (updates.fontSize !== void 0 || updates.width !== void 0 || updates.height !== void 0 || updates.inkColor !== void 0)) {
|
|
721
|
+
const merged = { ...field, ...updates };
|
|
722
|
+
lastStylesRef.current[field.type] = {
|
|
723
|
+
width: merged.width,
|
|
724
|
+
height: merged.height,
|
|
725
|
+
fontSize: merged.fontSize,
|
|
726
|
+
inkColor: merged.inkColor
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
return updated;
|
|
697
730
|
});
|
|
698
731
|
}, []);
|
|
699
732
|
const handleFieldDelete = useCallback3((id) => {
|
|
@@ -759,8 +792,10 @@ function DesignerView({
|
|
|
759
792
|
}, []);
|
|
760
793
|
const handleDropOnPage = useCallback3((page, x, y, fieldType) => {
|
|
761
794
|
const field = createField(fieldType, activeRole, page, x, y, fields);
|
|
762
|
-
|
|
763
|
-
|
|
795
|
+
const sticky = lastStylesRef.current[fieldType];
|
|
796
|
+
const styledField = sticky ? { ...field, ...sticky } : field;
|
|
797
|
+
setFields((prev) => [...prev, styledField]);
|
|
798
|
+
setSelectedFieldId(styledField.id);
|
|
764
799
|
setRightTab("properties");
|
|
765
800
|
}, [activeRole, fields]);
|
|
766
801
|
useEffect2(() => {
|
|
@@ -778,6 +813,38 @@ function DesignerView({
|
|
|
778
813
|
window.addEventListener("keydown", handleKeyDown);
|
|
779
814
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
780
815
|
}, [selectedFieldId, handleFieldDelete]);
|
|
816
|
+
useEffect2(() => {
|
|
817
|
+
const handleKeyDown = (e) => {
|
|
818
|
+
const tag = (document.activeElement?.tagName || "").toLowerCase();
|
|
819
|
+
if (tag === "input" || tag === "textarea" || tag === "select") return;
|
|
820
|
+
if (document.activeElement?.isContentEditable) return;
|
|
821
|
+
const isMod = e.metaKey || e.ctrlKey;
|
|
822
|
+
if (isMod && e.key === "c" && selectedFieldId) {
|
|
823
|
+
const field = fields.find((f) => f.id === selectedFieldId);
|
|
824
|
+
if (field) {
|
|
825
|
+
e.preventDefault();
|
|
826
|
+
setClipboardField(field);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
if (isMod && e.key === "v" && clipboardField) {
|
|
830
|
+
e.preventDefault();
|
|
831
|
+
const existingLabels = fields.map((f) => f.label);
|
|
832
|
+
const newField = {
|
|
833
|
+
...clipboardField,
|
|
834
|
+
id: generateId(),
|
|
835
|
+
label: uniqueLabel(clipboardField.label, existingLabels),
|
|
836
|
+
x: clipboardField.x + 2,
|
|
837
|
+
y: clipboardField.y + 2,
|
|
838
|
+
value: ""
|
|
839
|
+
};
|
|
840
|
+
setFields((prev) => [...prev, newField]);
|
|
841
|
+
setSelectedFieldId(newField.id);
|
|
842
|
+
setRightTab("properties");
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
846
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
847
|
+
}, [selectedFieldId, fields, clipboardField]);
|
|
781
848
|
const handleResizeStart = useCallback3((e) => {
|
|
782
849
|
e.preventDefault();
|
|
783
850
|
resizingRef.current = true;
|
|
@@ -1149,7 +1216,8 @@ function FieldNavigator({
|
|
|
1149
1216
|
currentFieldId,
|
|
1150
1217
|
onNavigate,
|
|
1151
1218
|
allRequiredFilled,
|
|
1152
|
-
onSubmit
|
|
1219
|
+
onSubmit,
|
|
1220
|
+
submitLabel = "Complete"
|
|
1153
1221
|
}) {
|
|
1154
1222
|
const currentIndex = fields.findIndex((f) => f.id === currentFieldId);
|
|
1155
1223
|
const hasPrev = currentIndex > 0;
|
|
@@ -1203,7 +1271,7 @@ function FieldNavigator({
|
|
|
1203
1271
|
onClick: onSubmit,
|
|
1204
1272
|
disabled: !allRequiredFilled,
|
|
1205
1273
|
className: "submit-btn",
|
|
1206
|
-
children:
|
|
1274
|
+
children: submitLabel
|
|
1207
1275
|
}
|
|
1208
1276
|
)
|
|
1209
1277
|
] });
|
|
@@ -1218,7 +1286,8 @@ function SignerView({
|
|
|
1218
1286
|
initialSigner,
|
|
1219
1287
|
callbackUrl: initialCallbackUrl,
|
|
1220
1288
|
onComplete,
|
|
1221
|
-
initialValues
|
|
1289
|
+
initialValues,
|
|
1290
|
+
submitLabel
|
|
1222
1291
|
} = {}) {
|
|
1223
1292
|
if (!isValidApiKey(apiKey)) {
|
|
1224
1293
|
return /* @__PURE__ */ jsx6("div", { className: "signer-layout", children: /* @__PURE__ */ jsxs6("div", { className: "empty-state", children: [
|
|
@@ -1234,11 +1303,18 @@ function SignerView({
|
|
|
1234
1303
|
const [fields, setFields] = useState3([]);
|
|
1235
1304
|
const [selectedFieldId, setSelectedFieldId] = useState3(null);
|
|
1236
1305
|
const [signer, setSigner] = useState3(initialSigner || "Signer 1");
|
|
1306
|
+
const initializedRef = useRef4(false);
|
|
1237
1307
|
const [loading, setLoading] = useState3(false);
|
|
1238
1308
|
const [submitting, setSubmitting] = useState3(false);
|
|
1239
1309
|
const [pdfSource, setPdfSource] = useState3(null);
|
|
1240
1310
|
const [callbackUrl, setCallbackUrl] = useState3(initialCallbackUrl || "");
|
|
1241
1311
|
const containerRef = useRef4(null);
|
|
1312
|
+
useEffect3(() => {
|
|
1313
|
+
if (initialSigner) {
|
|
1314
|
+
setSigner(initialSigner);
|
|
1315
|
+
setSelectedFieldId(null);
|
|
1316
|
+
}
|
|
1317
|
+
}, [initialSigner]);
|
|
1242
1318
|
useEffect3(() => {
|
|
1243
1319
|
if (initialTemplate) {
|
|
1244
1320
|
let templateFields = initialTemplate.fields;
|
|
@@ -1252,7 +1328,15 @@ function SignerView({
|
|
|
1252
1328
|
return value !== void 0 ? { ...f, value } : f;
|
|
1253
1329
|
});
|
|
1254
1330
|
}
|
|
1255
|
-
|
|
1331
|
+
if (initializedRef.current) {
|
|
1332
|
+
setFields((prev) => {
|
|
1333
|
+
const valueMap = new Map(prev.filter((f) => f.value).map((f) => [f.id, f.value]));
|
|
1334
|
+
return templateFields.map((f) => valueMap.has(f.id) ? { ...f, value: valueMap.get(f.id) } : f);
|
|
1335
|
+
});
|
|
1336
|
+
} else {
|
|
1337
|
+
setFields(templateFields);
|
|
1338
|
+
initializedRef.current = true;
|
|
1339
|
+
}
|
|
1256
1340
|
if (initialPdfUrl) loadPdf(initialPdfUrl);
|
|
1257
1341
|
return;
|
|
1258
1342
|
}
|
|
@@ -1265,11 +1349,31 @@ function SignerView({
|
|
|
1265
1349
|
if (cbUrl) setCallbackUrl(cbUrl);
|
|
1266
1350
|
if (pdfUrl) loadPdf(pdfUrl);
|
|
1267
1351
|
if (fieldsUrl) {
|
|
1268
|
-
fetch(fieldsUrl).then((r) => r.json()).then((template) =>
|
|
1352
|
+
fetch(fieldsUrl).then((r) => r.json()).then((template) => {
|
|
1353
|
+
if (initializedRef.current) {
|
|
1354
|
+
setFields((prev) => {
|
|
1355
|
+
const valueMap = new Map(prev.filter((f) => f.value).map((f) => [f.id, f.value]));
|
|
1356
|
+
return template.fields.map((f) => valueMap.has(f.id) ? { ...f, value: valueMap.get(f.id) } : f);
|
|
1357
|
+
});
|
|
1358
|
+
} else {
|
|
1359
|
+
setFields(template.fields);
|
|
1360
|
+
initializedRef.current = true;
|
|
1361
|
+
}
|
|
1362
|
+
}).catch((err) => console.error("Failed to load fields:", err));
|
|
1269
1363
|
}
|
|
1270
1364
|
const handleMessage = (e) => {
|
|
1271
1365
|
if (e.data?.type === "load-signing") {
|
|
1272
|
-
if (e.data.fields)
|
|
1366
|
+
if (e.data.fields) {
|
|
1367
|
+
if (initializedRef.current) {
|
|
1368
|
+
setFields((prev) => {
|
|
1369
|
+
const valueMap = new Map(prev.filter((f) => f.value).map((f) => [f.id, f.value]));
|
|
1370
|
+
return e.data.fields.map((f) => valueMap.has(f.id) ? { ...f, value: valueMap.get(f.id) } : f);
|
|
1371
|
+
});
|
|
1372
|
+
} else {
|
|
1373
|
+
setFields(e.data.fields);
|
|
1374
|
+
initializedRef.current = true;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1273
1377
|
if (e.data.pdfUrl) loadPdf(e.data.pdfUrl);
|
|
1274
1378
|
if (e.data.signer) setSigner(e.data.signer);
|
|
1275
1379
|
if (e.data.callbackUrl) setCallbackUrl(e.data.callbackUrl);
|
|
@@ -1290,14 +1394,14 @@ function SignerView({
|
|
|
1290
1394
|
setLoading(false);
|
|
1291
1395
|
}
|
|
1292
1396
|
}, []);
|
|
1293
|
-
const editableFields = fields.filter((f) => f.assignee === signer).sort((a, b) => {
|
|
1397
|
+
const editableFields = fields.filter((f) => f.assignee === signer && f.type !== "blackout" && f.type !== "whiteout").sort((a, b) => {
|
|
1294
1398
|
if (a.page !== b.page) return a.page - b.page;
|
|
1295
1399
|
const bandThreshold = 2;
|
|
1296
1400
|
if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
|
|
1297
1401
|
return a.x - b.x;
|
|
1298
1402
|
});
|
|
1299
1403
|
const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
|
|
1300
|
-
const isFieldEditable = selectedField ? selectedField.assignee === signer : false;
|
|
1404
|
+
const isFieldEditable = selectedField ? selectedField.assignee === signer && selectedField.type !== "blackout" && selectedField.type !== "whiteout" : false;
|
|
1301
1405
|
const handleFieldUpdate = useCallback4((id, value) => {
|
|
1302
1406
|
setFields((prev) => prev.map((f) => f.id === id ? { ...f, value } : f));
|
|
1303
1407
|
}, []);
|
|
@@ -1339,6 +1443,9 @@ function SignerView({
|
|
|
1339
1443
|
}
|
|
1340
1444
|
}, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete]);
|
|
1341
1445
|
const renderFieldContent = useCallback4((field) => {
|
|
1446
|
+
if (field.type === "blackout" || field.type === "whiteout") {
|
|
1447
|
+
return null;
|
|
1448
|
+
}
|
|
1342
1449
|
const editable = field.assignee === signer;
|
|
1343
1450
|
if (!editable) {
|
|
1344
1451
|
if (field.type === "signature" || field.type === "initials") {
|
|
@@ -1460,7 +1567,8 @@ function SignerView({
|
|
|
1460
1567
|
currentFieldId: selectedFieldId,
|
|
1461
1568
|
onNavigate: handleNavigate,
|
|
1462
1569
|
allRequiredFilled,
|
|
1463
|
-
onSubmit: handleSubmit
|
|
1570
|
+
onSubmit: handleSubmit,
|
|
1571
|
+
submitLabel
|
|
1464
1572
|
}
|
|
1465
1573
|
),
|
|
1466
1574
|
submitting && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Generating PDF..." })
|