@unlev/exeq 0.1.4 → 0.1.6

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/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 rect = canvasRef.current.getBoundingClientRect();
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
- setFields((prev) => [...prev, field]);
681
- setSelectedFieldId(field.id);
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) => prev.map((f) => f.id === id ? { ...f, width, height } : f));
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
- return prev.map((f) => f.id === id ? { ...f, ...updates } : f);
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
- setFields((prev) => [...prev, field]);
763
- setSelectedFieldId(field.id);
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: "Complete & Download"
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: [
@@ -1290,14 +1359,14 @@ function SignerView({
1290
1359
  setLoading(false);
1291
1360
  }
1292
1361
  }, []);
1293
- const editableFields = fields.filter((f) => f.assignee === signer).sort((a, b) => {
1362
+ const editableFields = fields.filter((f) => f.assignee === signer && f.type !== "blackout" && f.type !== "whiteout").sort((a, b) => {
1294
1363
  if (a.page !== b.page) return a.page - b.page;
1295
1364
  const bandThreshold = 2;
1296
1365
  if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
1297
1366
  return a.x - b.x;
1298
1367
  });
1299
1368
  const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
1300
- const isFieldEditable = selectedField ? selectedField.assignee === signer : false;
1369
+ const isFieldEditable = selectedField ? selectedField.assignee === signer && selectedField.type !== "blackout" && selectedField.type !== "whiteout" : false;
1301
1370
  const handleFieldUpdate = useCallback4((id, value) => {
1302
1371
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, value } : f));
1303
1372
  }, []);
@@ -1322,13 +1391,15 @@ function SignerView({
1322
1391
  try {
1323
1392
  const pdfBytes = await generateFilledPdf(pdfSource, fields);
1324
1393
  const blob = new Blob([pdfBytes.slice().buffer], { type: "application/pdf" });
1325
- downloadPdf(pdfBytes, "signed-document.pdf");
1326
1394
  if (callbackUrl) {
1327
1395
  await postPdfToCallback(pdfBytes, callbackUrl, "signed-document.pdf");
1328
1396
  }
1329
1397
  if (onComplete) {
1330
1398
  onComplete(blob);
1331
1399
  }
1400
+ if (!callbackUrl && !onComplete) {
1401
+ downloadPdf(pdfBytes, "signed-document.pdf");
1402
+ }
1332
1403
  window.parent?.postMessage({ type: "signing-complete" }, "*");
1333
1404
  } catch (err) {
1334
1405
  console.error("Failed to generate PDF:", err);
@@ -1337,6 +1408,9 @@ function SignerView({
1337
1408
  }
1338
1409
  }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete]);
1339
1410
  const renderFieldContent = useCallback4((field) => {
1411
+ if (field.type === "blackout" || field.type === "whiteout") {
1412
+ return null;
1413
+ }
1340
1414
  const editable = field.assignee === signer;
1341
1415
  if (!editable) {
1342
1416
  if (field.type === "signature" || field.type === "initials") {
@@ -1377,11 +1451,12 @@ function SignerView({
1377
1451
  value: field.value,
1378
1452
  placeholder: field.placeholder,
1379
1453
  onChange: (e) => handleFieldUpdate(field.id, e.target.value),
1454
+ onFocus: () => setSelectedFieldId(field.id),
1380
1455
  onClick: (e) => e.stopPropagation(),
1381
1456
  style: { fontSize: `${field.fontSize * 0.6}px` }
1382
1457
  }
1383
1458
  );
1384
- }, [signer, handleFieldUpdate]);
1459
+ }, [signer, handleFieldUpdate, setSelectedFieldId]);
1385
1460
  useEffect3(() => {
1386
1461
  const sigFields = fields.filter((f) => f.assignee === signer && f.type === "signature" && f.value);
1387
1462
  if (sigFields.length > 0) {
@@ -1457,7 +1532,8 @@ function SignerView({
1457
1532
  currentFieldId: selectedFieldId,
1458
1533
  onNavigate: handleNavigate,
1459
1534
  allRequiredFilled,
1460
- onSubmit: handleSubmit
1535
+ onSubmit: handleSubmit,
1536
+ submitLabel
1461
1537
  }
1462
1538
  ),
1463
1539
  submitting && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Generating PDF..." })