@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/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: [
@@ -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
- setFields(templateFields);
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) => setFields(template.fields)).catch((err) => console.error("Failed to load fields:", err));
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) setFields(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..." })