@unlev/exeq 0.1.12 → 0.2.0

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
@@ -1,5 +1,5 @@
1
1
  // src/components/pdf-builder/DesignerView.tsx
2
- import { useState as useState2, useCallback as useCallback3, useEffect as useEffect2, useRef as useRef3 } from "react";
2
+ import { useState as useState3, useCallback as useCallback3, useEffect as useEffect2, useRef as useRef3 } from "react";
3
3
  import { createPortal } from "react-dom";
4
4
 
5
5
  // src/types/pdf-builder.ts
@@ -25,6 +25,11 @@ var SIGNER_ROLE_COLORS = {
25
25
  function getSignerColor(role) {
26
26
  return SIGNER_ROLE_COLORS[role] || "#888888";
27
27
  }
28
+ var FONT_FAMILIES = [
29
+ { value: "Helvetica", label: "Helvetica" },
30
+ { value: "Courier", label: "Courier New" },
31
+ { value: "TimesRoman", label: "Times New Roman" }
32
+ ];
28
33
  var FIELD_DEFAULTS = {
29
34
  text: {
30
35
  width: 20,
@@ -336,6 +341,7 @@ function FieldOverlayItem({
336
341
  }
337
342
 
338
343
  // src/components/pdf-builder/FieldPropertyPanel.tsx
344
+ import { useState } from "react";
339
345
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
340
346
  var INK_COLORS = [
341
347
  { value: "#000000", label: "Black" },
@@ -344,8 +350,9 @@ var INK_COLORS = [
344
350
  function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
345
351
  const color = getSignerColor(field.assignee);
346
352
  const isRedactField = field.type === "blackout" || field.type === "whiteout";
347
- const showFontSize = field.type === "text" || field.type === "signed-date";
353
+ const isTextField = field.type === "text" || field.type === "signed-date";
348
354
  const showInkColor = field.type === "text" || field.type === "signature" || field.type === "initials" || field.type === "signed-date";
355
+ const [newOption, setNewOption] = useState("");
349
356
  return /* @__PURE__ */ jsxs2("div", { className: "field-property-panel", children: [
350
357
  /* @__PURE__ */ jsxs2("div", { className: "panel-header", children: [
351
358
  /* @__PURE__ */ jsx2("h3", { style: { color }, children: field.label }),
@@ -418,6 +425,19 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
418
425
  }
419
426
  )
420
427
  ] }),
428
+ isTextField && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
429
+ /* @__PURE__ */ jsx2("label", { children: "Formula" }),
430
+ /* @__PURE__ */ jsx2(
431
+ "input",
432
+ {
433
+ type: "text",
434
+ value: field.formula || "",
435
+ onChange: (e) => onUpdate(field.id, { formula: e.target.value || void 0 }),
436
+ placeholder: "e.g. {{Date Field | month}}"
437
+ }
438
+ ),
439
+ field.formula && /* @__PURE__ */ jsx2("span", { className: "panel-hint", children: "This field auto-computes from other fields. Signer cannot edit it." })
440
+ ] }),
421
441
  !isRedactField && field.type !== "checkbox" && /* @__PURE__ */ jsx2("div", { className: "panel-field", children: /* @__PURE__ */ jsxs2("label", { className: "panel-checkbox-label", children: [
422
442
  /* @__PURE__ */ jsx2(
423
443
  "input",
@@ -429,7 +449,18 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
429
449
  ),
430
450
  "Required"
431
451
  ] }) }),
432
- showFontSize && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
452
+ isTextField && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
453
+ /* @__PURE__ */ jsx2("label", { children: "Font" }),
454
+ /* @__PURE__ */ jsx2(
455
+ "select",
456
+ {
457
+ value: field.fontFamily || "Helvetica",
458
+ onChange: (e) => onUpdate(field.id, { fontFamily: e.target.value }),
459
+ children: FONT_FAMILIES.map((f) => /* @__PURE__ */ jsx2("option", { value: f.value, children: f.label }, f.value))
460
+ }
461
+ )
462
+ ] }),
463
+ isTextField && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
433
464
  /* @__PURE__ */ jsx2("label", { children: "Font Size (pt)" }),
434
465
  /* @__PURE__ */ jsx2(
435
466
  "input",
@@ -442,6 +473,49 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
442
473
  }
443
474
  )
444
475
  ] }),
476
+ isTextField && /* @__PURE__ */ jsxs2("div", { className: "panel-field-row", children: [
477
+ /* @__PURE__ */ jsxs2("div", { className: "panel-field panel-field-half", children: [
478
+ /* @__PURE__ */ jsx2("label", { children: "Letter Spacing" }),
479
+ /* @__PURE__ */ jsx2(
480
+ "input",
481
+ {
482
+ type: "number",
483
+ min: "0",
484
+ max: "20",
485
+ step: "0.5",
486
+ value: field.letterSpacing || 0,
487
+ onChange: (e) => onUpdate(field.id, { letterSpacing: Number(e.target.value) })
488
+ }
489
+ )
490
+ ] }),
491
+ /* @__PURE__ */ jsxs2("div", { className: "panel-field panel-field-half", children: [
492
+ /* @__PURE__ */ jsx2("label", { children: "Line Height" }),
493
+ /* @__PURE__ */ jsx2(
494
+ "input",
495
+ {
496
+ type: "number",
497
+ min: "0.8",
498
+ max: "3",
499
+ step: "0.1",
500
+ value: field.lineHeight || 1.2,
501
+ onChange: (e) => onUpdate(field.id, { lineHeight: Number(e.target.value) })
502
+ }
503
+ )
504
+ ] })
505
+ ] }),
506
+ field.type === "text" && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
507
+ /* @__PURE__ */ jsx2("label", { children: "Max Characters (0 = unlimited)" }),
508
+ /* @__PURE__ */ jsx2(
509
+ "input",
510
+ {
511
+ type: "number",
512
+ min: "0",
513
+ max: "9999",
514
+ value: field.maxLength || 0,
515
+ onChange: (e) => onUpdate(field.id, { maxLength: Number(e.target.value) })
516
+ }
517
+ )
518
+ ] }),
445
519
  showInkColor && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
446
520
  /* @__PURE__ */ jsx2("label", { children: "Ink Color" }),
447
521
  /* @__PURE__ */ jsx2("div", { className: "ink-color-picker", children: INK_COLORS.map((c) => /* @__PURE__ */ jsx2(
@@ -454,12 +528,61 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
454
528
  },
455
529
  c.value
456
530
  )) })
531
+ ] }),
532
+ field.type === "text" && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
533
+ /* @__PURE__ */ jsx2("label", { children: "Predefined Options" }),
534
+ /* @__PURE__ */ jsxs2("div", { className: "panel-options-list", children: [
535
+ (field.options || []).map((opt, i) => /* @__PURE__ */ jsxs2("div", { className: "panel-option-item", children: [
536
+ /* @__PURE__ */ jsx2("span", { children: opt }),
537
+ /* @__PURE__ */ jsx2(
538
+ "button",
539
+ {
540
+ className: "panel-option-remove",
541
+ onClick: () => {
542
+ const updated = (field.options || []).filter((_, j) => j !== i);
543
+ onUpdate(field.id, { options: updated.length > 0 ? updated : void 0 });
544
+ },
545
+ children: "\xD7"
546
+ }
547
+ )
548
+ ] }, i)),
549
+ /* @__PURE__ */ jsxs2("div", { className: "panel-option-add", children: [
550
+ /* @__PURE__ */ jsx2(
551
+ "input",
552
+ {
553
+ type: "text",
554
+ value: newOption,
555
+ onChange: (e) => setNewOption(e.target.value),
556
+ onKeyDown: (e) => {
557
+ if (e.key === "Enter" && newOption.trim()) {
558
+ onUpdate(field.id, { options: [...field.options || [], newOption.trim()] });
559
+ setNewOption("");
560
+ }
561
+ },
562
+ placeholder: "Add option..."
563
+ }
564
+ ),
565
+ /* @__PURE__ */ jsx2(
566
+ "button",
567
+ {
568
+ onClick: () => {
569
+ if (newOption.trim()) {
570
+ onUpdate(field.id, { options: [...field.options || [], newOption.trim()] });
571
+ setNewOption("");
572
+ }
573
+ },
574
+ children: "+"
575
+ }
576
+ )
577
+ ] })
578
+ ] }),
579
+ (field.options?.length ?? 0) > 0 && /* @__PURE__ */ jsx2("span", { className: "panel-hint", children: "Signer will choose from these options via dropdown" })
457
580
  ] })
458
581
  ] });
459
582
  }
460
583
 
461
584
  // src/components/pdf-builder/SignatureCanvas.tsx
462
- import { useRef as useRef2, useState, useCallback as useCallback2, useEffect } from "react";
585
+ import { useRef as useRef2, useState as useState2, useCallback as useCallback2, useEffect } from "react";
463
586
  import { getStroke } from "perfect-freehand";
464
587
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
465
588
  function getSvgPathFromStroke(stroke) {
@@ -484,9 +607,9 @@ function SignatureCanvas({
484
607
  inkColor = "#000000"
485
608
  }) {
486
609
  const canvasRef = useRef2(null);
487
- const [paths, setPaths] = useState([]);
488
- const [currentPath, setCurrentPath] = useState(null);
489
- const [isEmpty, setIsEmpty] = useState(!initialValue);
610
+ const [paths, setPaths] = useState2([]);
611
+ const [currentPath, setCurrentPath] = useState2(null);
612
+ const [isEmpty, setIsEmpty] = useState2(!initialValue);
490
613
  useEffect(() => {
491
614
  if (initialValue && canvasRef.current) {
492
615
  const ctx = canvasRef.current.getContext("2d");
@@ -627,20 +750,20 @@ function DesignerView({
627
750
  ] })
628
751
  ] }) });
629
752
  }
630
- const [pages, setPages] = useState2([]);
631
- const [fields, setFields] = useState2(initialTemplate?.fields ?? []);
632
- const [selectedFieldId, setSelectedFieldId] = useState2(null);
633
- const [signerRoles, setSignerRoles] = useState2(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
634
- const [activeRole, setActiveRole] = useState2("Sender");
635
- const [activeFieldType, setActiveFieldType] = useState2("text");
636
- const [loading, setLoading] = useState2(false);
637
- const [pdfSource, setPdfSource] = useState2(null);
638
- const [rightTab, setRightTab] = useState2("properties");
639
- const [isAddingRole, setIsAddingRole] = useState2(false);
640
- const [newRoleName, setNewRoleName] = useState2("");
641
- const [draggingFieldType, setDraggingFieldType] = useState2(null);
642
- const [panelWidth, setPanelWidth] = useState2(380);
643
- const [clipboardField, setClipboardField] = useState2(null);
753
+ const [pages, setPages] = useState3([]);
754
+ const [fields, setFields] = useState3(initialTemplate?.fields ?? []);
755
+ const [selectedFieldId, setSelectedFieldId] = useState3(null);
756
+ const [signerRoles, setSignerRoles] = useState3(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
757
+ const [activeRole, setActiveRole] = useState3("Sender");
758
+ const [activeFieldType, setActiveFieldType] = useState3("text");
759
+ const [loading, setLoading] = useState3(false);
760
+ const [pdfSource, setPdfSource] = useState3(null);
761
+ const [rightTab, setRightTab] = useState3("properties");
762
+ const [isAddingRole, setIsAddingRole] = useState3(false);
763
+ const [newRoleName, setNewRoleName] = useState3("");
764
+ const [draggingFieldType, setDraggingFieldType] = useState3(null);
765
+ const [panelWidth, setPanelWidth] = useState3(380);
766
+ const [clipboardField, setClipboardField] = useState3(null);
644
767
  const dragGhostRef = useRef3(null);
645
768
  const resizingRef = useRef3(false);
646
769
  const lastStylesRef = useRef3({});
@@ -1108,10 +1231,15 @@ function DesignerView({
1108
1231
  }
1109
1232
 
1110
1233
  // src/components/pdf-builder/SignerView.tsx
1111
- import { useState as useState4, useCallback as useCallback4, useEffect as useEffect3, useRef as useRef4 } from "react";
1234
+ import { useState as useState5, useCallback as useCallback4, useEffect as useEffect3, useRef as useRef4 } from "react";
1112
1235
 
1113
1236
  // src/utils/pdfFiller.ts
1114
1237
  import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
1238
+ var FONT_MAP = {
1239
+ "Helvetica": StandardFonts.Helvetica,
1240
+ "Courier": StandardFonts.Courier,
1241
+ "TimesRoman": StandardFonts.TimesRoman
1242
+ };
1115
1243
  async function generateFilledPdf(pdfSource, fields) {
1116
1244
  let pdfBytes;
1117
1245
  if (typeof pdfSource === "string") {
@@ -1121,7 +1249,12 @@ async function generateFilledPdf(pdfSource, fields) {
1121
1249
  pdfBytes = pdfSource;
1122
1250
  }
1123
1251
  const pdfDoc = await PDFDocument.load(pdfBytes);
1124
- const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
1252
+ const fontCache = /* @__PURE__ */ new Map();
1253
+ const usedFontKeys = new Set(fields.map((f) => f.fontFamily || "Helvetica"));
1254
+ for (const key of usedFontKeys) {
1255
+ const stdFont = FONT_MAP[key] || StandardFonts.Helvetica;
1256
+ fontCache.set(key, await pdfDoc.embedFont(stdFont));
1257
+ }
1125
1258
  const pages = pdfDoc.getPages();
1126
1259
  function hexToRgb(hex) {
1127
1260
  const r = parseInt(hex.slice(1, 3), 16) / 255;
@@ -1182,21 +1315,35 @@ async function generateFilledPdf(pdfSource, fields) {
1182
1315
  });
1183
1316
  }
1184
1317
  } else {
1318
+ const font = fontCache.get(field.fontFamily || "Helvetica");
1319
+ const spacing = field.letterSpacing || 0;
1320
+ const textWidthAtSize = (text, size) => {
1321
+ const baseWidth = font.widthOfTextAtSize(text, size);
1322
+ return baseWidth + spacing * (text.length - 1);
1323
+ };
1185
1324
  const maxFontSize = Math.min(field.fontSize, h * 0.7);
1186
1325
  let fontSize = maxFontSize;
1187
1326
  const padding = 4;
1188
1327
  while (fontSize > 4) {
1189
- const textWidth = font.widthOfTextAtSize(field.value, fontSize);
1190
- if (textWidth <= w - padding) break;
1328
+ if (textWidthAtSize(field.value, fontSize) <= w - padding) break;
1191
1329
  fontSize -= 0.5;
1192
1330
  }
1193
- page.drawText(field.value, {
1194
- x: x + 2,
1195
- y: y + h * 0.3,
1196
- size: fontSize,
1197
- font,
1198
- color: inkColor
1199
- });
1331
+ if (spacing > 0) {
1332
+ let cx = x + 2;
1333
+ const cy = y + h * 0.3;
1334
+ for (const char of field.value) {
1335
+ page.drawText(char, { x: cx, y: cy, size: fontSize, font, color: inkColor });
1336
+ cx += font.widthOfTextAtSize(char, fontSize) + spacing;
1337
+ }
1338
+ } else {
1339
+ page.drawText(field.value, {
1340
+ x: x + 2,
1341
+ y: y + h * 0.3,
1342
+ size: fontSize,
1343
+ font,
1344
+ color: inkColor
1345
+ });
1346
+ }
1200
1347
  }
1201
1348
  }
1202
1349
  return pdfDoc.save();
@@ -1221,7 +1368,7 @@ async function postPdfToCallback(bytes, callbackUrl, filename) {
1221
1368
  }
1222
1369
 
1223
1370
  // src/components/pdf-builder/FieldNavigator.tsx
1224
- import { useState as useState3 } from "react";
1371
+ import { useState as useState4 } from "react";
1225
1372
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1226
1373
  function isFieldFilled(f) {
1227
1374
  if (!f.required) return true;
@@ -1236,7 +1383,7 @@ function FieldNavigator({
1236
1383
  onComplete,
1237
1384
  completeLabel = "Complete"
1238
1385
  }) {
1239
- const [showIncomplete, setShowIncomplete] = useState3(false);
1386
+ const [showIncomplete, setShowIncomplete] = useState4(false);
1240
1387
  const currentIndex = fields.findIndex((f) => f.id === currentFieldId);
1241
1388
  const hasPrev = currentIndex > 0;
1242
1389
  const hasNext = currentIndex < fields.length - 1;
@@ -1311,6 +1458,95 @@ function FieldNavigator({
1311
1458
  ] });
1312
1459
  }
1313
1460
 
1461
+ // src/utils/formulaResolver.ts
1462
+ var BUILTIN_TRANSFORMS = {
1463
+ // Date transforms (expects a parseable date string)
1464
+ month: (v) => {
1465
+ const d = new Date(v);
1466
+ return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1);
1467
+ },
1468
+ month2: (v) => {
1469
+ const d = new Date(v);
1470
+ return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1).padStart(2, "0");
1471
+ },
1472
+ monthname: (v) => {
1473
+ const d = new Date(v);
1474
+ return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "long" });
1475
+ },
1476
+ monthshort: (v) => {
1477
+ const d = new Date(v);
1478
+ return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "short" });
1479
+ },
1480
+ day: (v) => {
1481
+ const d = new Date(v);
1482
+ return isNaN(d.getTime()) ? "" : String(d.getDate());
1483
+ },
1484
+ day2: (v) => {
1485
+ const d = new Date(v);
1486
+ return isNaN(d.getTime()) ? "" : String(d.getDate()).padStart(2, "0");
1487
+ },
1488
+ year: (v) => {
1489
+ const d = new Date(v);
1490
+ return isNaN(d.getTime()) ? "" : String(d.getFullYear());
1491
+ },
1492
+ year2: (v) => {
1493
+ const d = new Date(v);
1494
+ return isNaN(d.getTime()) ? "" : String(d.getFullYear()).slice(-2);
1495
+ },
1496
+ // String transforms
1497
+ upper: (v) => v.toUpperCase(),
1498
+ lower: (v) => v.toLowerCase(),
1499
+ trim: (v) => v.trim(),
1500
+ first: (v) => v.split(/\s+/)[0] || "",
1501
+ last: (v) => {
1502
+ const parts = v.split(/\s+/);
1503
+ return parts[parts.length - 1] || "";
1504
+ },
1505
+ initials: (v) => v.split(/\s+/).map((w) => w[0] || "").join("").toUpperCase(),
1506
+ // Numeric / substring
1507
+ last4: (v) => v.slice(-4),
1508
+ last2: (v) => v.slice(-2),
1509
+ first4: (v) => v.slice(0, 4),
1510
+ first2: (v) => v.slice(0, 2),
1511
+ digits: (v) => v.replace(/\D/g, ""),
1512
+ number: (v) => {
1513
+ const n = parseFloat(v);
1514
+ return isNaN(n) ? "" : String(n);
1515
+ },
1516
+ currency: (v) => {
1517
+ const n = parseFloat(v.replace(/[^0-9.-]/g, ""));
1518
+ return isNaN(n) ? "" : `$${n.toFixed(2)}`;
1519
+ }
1520
+ };
1521
+ var FORMULA_RE = /\{\{(.+?)\}\}/g;
1522
+ var PIPE_RE = /^(.+?)\s*\|\s*(.+)$/;
1523
+ function resolveFormula(formula, fields, customTransforms) {
1524
+ const transforms = { ...BUILTIN_TRANSFORMS, ...customTransforms };
1525
+ return formula.replace(FORMULA_RE, (_match, expr) => {
1526
+ const pipeMatch = expr.match(PIPE_RE);
1527
+ const sourceLabel = (pipeMatch ? pipeMatch[1] : expr).trim();
1528
+ const transformName = pipeMatch ? pipeMatch[2].trim() : null;
1529
+ const sourceField = fields.find((f) => f.label.toLowerCase() === sourceLabel.toLowerCase()) || fields.find((f) => f.id === sourceLabel);
1530
+ if (!sourceField) return "";
1531
+ const rawValue = sourceField.value || "";
1532
+ if (!transformName) return rawValue;
1533
+ const fn = transforms[transformName];
1534
+ if (!fn) return rawValue;
1535
+ try {
1536
+ return fn(rawValue);
1537
+ } catch {
1538
+ return rawValue;
1539
+ }
1540
+ });
1541
+ }
1542
+ function resolveAllFormulas(fields, customTransforms) {
1543
+ return fields.map((f) => {
1544
+ if (!f.formula) return f;
1545
+ const computed = resolveFormula(f.formula, fields, customTransforms);
1546
+ return computed !== f.value ? { ...f, value: computed } : f;
1547
+ });
1548
+ }
1549
+
1314
1550
  // src/components/pdf-builder/SignerView.tsx
1315
1551
  import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1316
1552
  function SignerView({
@@ -1322,7 +1558,8 @@ function SignerView({
1322
1558
  onComplete,
1323
1559
  initialValues,
1324
1560
  submitLabel,
1325
- signerOrder: signerOrderProp
1561
+ signerOrder: signerOrderProp,
1562
+ transforms
1326
1563
  } = {}) {
1327
1564
  if (!isValidApiKey(apiKey)) {
1328
1565
  return /* @__PURE__ */ jsx6("div", { className: "signer-layout", children: /* @__PURE__ */ jsxs6("div", { className: "empty-state", children: [
@@ -1334,11 +1571,11 @@ function SignerView({
1334
1571
  ] })
1335
1572
  ] }) });
1336
1573
  }
1337
- const [pages, setPages] = useState4([]);
1338
- const [fields, setFields] = useState4([]);
1339
- const [selectedFieldId, setSelectedFieldId] = useState4(null);
1340
- const [signerRoles, setSignerRoles] = useState4([]);
1341
- const [currentSignerIndex, setCurrentSignerIndex] = useState4(() => {
1574
+ const [pages, setPages] = useState5([]);
1575
+ const [fields, setFields] = useState5([]);
1576
+ const [selectedFieldId, setSelectedFieldId] = useState5(null);
1577
+ const [signerRoles, setSignerRoles] = useState5([]);
1578
+ const [currentSignerIndex, setCurrentSignerIndex] = useState5(() => {
1342
1579
  if (initialSigner && signerOrderProp) {
1343
1580
  const idx = signerOrderProp.indexOf(initialSigner);
1344
1581
  return idx >= 0 ? idx : 0;
@@ -1346,15 +1583,30 @@ function SignerView({
1346
1583
  return 0;
1347
1584
  });
1348
1585
  const initializedRef = useRef4(false);
1349
- const [loading, setLoading] = useState4(false);
1350
- const [submitting, setSubmitting] = useState4(false);
1351
- const [pdfSource, setPdfSource] = useState4(null);
1352
- const [callbackUrl, setCallbackUrl] = useState4(initialCallbackUrl || "");
1586
+ const [loading, setLoading] = useState5(false);
1587
+ const [submitting, setSubmitting] = useState5(false);
1588
+ const [pdfSource, setPdfSource] = useState5(null);
1589
+ const [callbackUrl, setCallbackUrl] = useState5(initialCallbackUrl || "");
1353
1590
  const containerRef = useRef4(null);
1354
1591
  const signerOrder = signerOrderProp || (signerRoles.length > 0 ? [...signerRoles.filter((r) => r !== "Sender"), ...signerRoles.filter((r) => r === "Sender")] : [initialSigner || "Signer 1"]);
1355
1592
  const signer = signerOrder[currentSignerIndex] || signerOrder[0] || "Signer 1";
1356
1593
  const isLastSigner = currentSignerIndex >= signerOrder.length - 1;
1357
1594
  const isMultiSigner = signerOrder.length > 1;
1595
+ useEffect3(() => {
1596
+ if (fields.length === 0 || !isMultiSigner) return;
1597
+ const signerFields = fields.filter(
1598
+ (f) => f.assignee === signer && f.type !== "blackout" && f.type !== "whiteout"
1599
+ );
1600
+ const allFilled = signerFields.length > 0 && signerFields.every((f) => {
1601
+ if (!f.required) return true;
1602
+ if (f.type === "checkbox") return true;
1603
+ return !!f.value;
1604
+ });
1605
+ if (allFilled && !isLastSigner) {
1606
+ setCurrentSignerIndex((prev) => prev + 1);
1607
+ setSelectedFieldId(null);
1608
+ }
1609
+ }, [currentSignerIndex, fields, signer, isLastSigner, isMultiSigner]);
1358
1610
  useEffect3(() => {
1359
1611
  if (initialTemplate) {
1360
1612
  let templateFields = initialTemplate.fields;
@@ -1437,14 +1689,14 @@ function SignerView({
1437
1689
  setLoading(false);
1438
1690
  }
1439
1691
  }, []);
1440
- const editableFields = fields.filter((f) => f.assignee === signer && f.type !== "blackout" && f.type !== "whiteout").sort((a, b) => {
1692
+ const editableFields = fields.filter((f) => f.assignee === signer && f.type !== "blackout" && f.type !== "whiteout" && !f.formula).sort((a, b) => {
1441
1693
  if (a.page !== b.page) return a.page - b.page;
1442
1694
  const bandThreshold = 2;
1443
1695
  if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
1444
1696
  return a.x - b.x;
1445
1697
  });
1446
1698
  const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
1447
- const isFieldEditable = selectedField ? selectedField.assignee === signer && selectedField.type !== "blackout" && selectedField.type !== "whiteout" : false;
1699
+ const isFieldEditable = selectedField ? selectedField.assignee === signer && selectedField.type !== "blackout" && selectedField.type !== "whiteout" && !selectedField.formula : false;
1448
1700
  const handleFieldUpdate = useCallback4((id, value) => {
1449
1701
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, value } : f));
1450
1702
  }, []);
@@ -1480,7 +1732,8 @@ function SignerView({
1480
1732
  if (!pdfSource) return;
1481
1733
  setSubmitting(true);
1482
1734
  try {
1483
- const pdfBytes = await generateFilledPdf(pdfSource, fields);
1735
+ const finalFields = resolveAllFormulas(fields, transforms);
1736
+ const pdfBytes = await generateFilledPdf(pdfSource, finalFields);
1484
1737
  const blob = new Blob([pdfBytes.slice().buffer], { type: "application/pdf" });
1485
1738
  if (callbackUrl) {
1486
1739
  await postPdfToCallback(pdfBytes, callbackUrl, "signed-document.pdf");
@@ -1502,6 +1755,9 @@ function SignerView({
1502
1755
  if (field.type === "blackout" || field.type === "whiteout") {
1503
1756
  return null;
1504
1757
  }
1758
+ if (field.formula) {
1759
+ return /* @__PURE__ */ jsx6("div", { className: "field-overlay-value formula", children: field.value || "..." });
1760
+ }
1505
1761
  const editable = field.assignee === signer;
1506
1762
  if (!editable) {
1507
1763
  if (field.type === "signature" || field.type === "initials") {
@@ -1534,6 +1790,28 @@ function SignerView({
1534
1790
  if (field.type === "signed-date") {
1535
1791
  return /* @__PURE__ */ jsx6("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
1536
1792
  }
1793
+ const fontStyle = {
1794
+ fontSize: `${field.fontSize * 0.6}px`,
1795
+ letterSpacing: field.letterSpacing ? `${field.letterSpacing * 0.6}px` : void 0,
1796
+ fontFamily: field.fontFamily === "Courier" ? "monospace" : field.fontFamily === "TimesRoman" ? "serif" : void 0
1797
+ };
1798
+ if (field.options && field.options.length > 0) {
1799
+ return /* @__PURE__ */ jsxs6(
1800
+ "select",
1801
+ {
1802
+ className: "field-inline-input",
1803
+ value: field.value,
1804
+ onChange: (e) => handleFieldUpdate(field.id, e.target.value),
1805
+ onFocus: () => setSelectedFieldId(field.id),
1806
+ onClick: (e) => e.stopPropagation(),
1807
+ style: fontStyle,
1808
+ children: [
1809
+ /* @__PURE__ */ jsx6("option", { value: "", children: field.placeholder || "Select..." }),
1810
+ field.options.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt, children: opt }, opt))
1811
+ ]
1812
+ }
1813
+ );
1814
+ }
1537
1815
  return /* @__PURE__ */ jsx6(
1538
1816
  "input",
1539
1817
  {
@@ -1541,10 +1819,11 @@ function SignerView({
1541
1819
  className: "field-inline-input",
1542
1820
  value: field.value,
1543
1821
  placeholder: field.placeholder,
1822
+ maxLength: field.maxLength || void 0,
1544
1823
  onChange: (e) => handleFieldUpdate(field.id, e.target.value),
1545
1824
  onFocus: () => setSelectedFieldId(field.id),
1546
1825
  onClick: (e) => e.stopPropagation(),
1547
- style: { fontSize: `${field.fontSize * 0.6}px` }
1826
+ style: fontStyle
1548
1827
  }
1549
1828
  );
1550
1829
  }, [signer, handleFieldUpdate, setSelectedFieldId]);
@@ -1560,6 +1839,13 @@ function SignerView({
1560
1839
  }));
1561
1840
  }
1562
1841
  }, [fields.filter((f) => f.type === "signature" && f.value).length]);
1842
+ useEffect3(() => {
1843
+ const hasFormulas = fields.some((f) => f.formula);
1844
+ if (!hasFormulas) return;
1845
+ const resolved = resolveAllFormulas(fields, transforms);
1846
+ const changed = resolved.some((f, i) => f.value !== fields[i].value);
1847
+ if (changed) setFields(resolved);
1848
+ }, [fields, transforms]);
1563
1849
  return /* @__PURE__ */ jsxs6("div", { className: "signer-layout", ref: containerRef, children: [
1564
1850
  loading && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Loading document..." }),
1565
1851
  /* @__PURE__ */ jsxs6("div", { className: "signer-content", children: [
@@ -1602,13 +1888,25 @@ function SignerView({
1602
1888
  }
1603
1889
  ),
1604
1890
  selectedField.type === "text" && /* @__PURE__ */ jsxs6(Fragment2, { children: [
1605
- /* @__PURE__ */ jsx6(
1891
+ selectedField.options && selectedField.options.length > 0 ? /* @__PURE__ */ jsxs6(
1892
+ "select",
1893
+ {
1894
+ value: selectedField.value,
1895
+ onChange: (e) => handleFieldUpdate(selectedField.id, e.target.value),
1896
+ className: "signer-text-input",
1897
+ children: [
1898
+ /* @__PURE__ */ jsx6("option", { value: "", children: selectedField.placeholder || "Select..." }),
1899
+ selectedField.options.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt, children: opt }, opt))
1900
+ ]
1901
+ }
1902
+ ) : /* @__PURE__ */ jsx6(
1606
1903
  "input",
1607
1904
  {
1608
1905
  type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
1609
1906
  value: selectedField.value,
1610
1907
  onChange: (e) => handleFieldUpdate(selectedField.id, e.target.value),
1611
1908
  placeholder: selectedField.placeholder,
1909
+ maxLength: selectedField.maxLength || void 0,
1612
1910
  className: "signer-text-input"
1613
1911
  }
1614
1912
  ),
@@ -1670,7 +1968,7 @@ function SignerView({
1670
1968
  }
1671
1969
 
1672
1970
  // src/components/pdf-builder/SignerRoleSelector.tsx
1673
- import { useState as useState5 } from "react";
1971
+ import { useState as useState6 } from "react";
1674
1972
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1675
1973
  function SignerRoleSelector({
1676
1974
  roles,
@@ -1679,8 +1977,8 @@ function SignerRoleSelector({
1679
1977
  onAddRole,
1680
1978
  onRemoveRole
1681
1979
  }) {
1682
- const [isAdding, setIsAdding] = useState5(false);
1683
- const [newRoleName, setNewRoleName] = useState5("");
1980
+ const [isAdding, setIsAdding] = useState6(false);
1981
+ const [newRoleName, setNewRoleName] = useState6("");
1684
1982
  const handleAdd = () => {
1685
1983
  if (newRoleName.trim()) {
1686
1984
  onAddRole(newRoleName.trim());
@@ -1744,9 +2042,11 @@ function SignerRoleSelector({
1744
2042
  ] });
1745
2043
  }
1746
2044
  export {
2045
+ BUILTIN_TRANSFORMS,
1747
2046
  DEFAULT_SIGNER_ROLES,
1748
2047
  DesignerView,
1749
2048
  FIELD_DEFAULTS,
2049
+ FONT_FAMILIES,
1750
2050
  FieldNavigator,
1751
2051
  FieldPropertyPanel,
1752
2052
  PdfViewer,
@@ -1757,9 +2057,12 @@ export {
1757
2057
  createField,
1758
2058
  downloadPdf,
1759
2059
  generateFilledPdf,
2060
+ generateId,
1760
2061
  getSignerColor,
1761
2062
  postPdfToCallback,
1762
2063
  renderPdfPages,
2064
+ resolveAllFormulas,
2065
+ resolveFormula,
1763
2066
  uniqueLabel
1764
2067
  };
1765
2068
  //# sourceMappingURL=index.mjs.map