@unlev/exeq 0.1.11 → 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 useState3, 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,15 +1315,35 @@ async function generateFilledPdf(pdfSource, fields) {
1182
1315
  });
1183
1316
  }
1184
1317
  } else {
1185
- const fontSize = Math.min(field.fontSize, h * 0.7);
1186
- page.drawText(field.value, {
1187
- x: x + 2,
1188
- y: y + h * 0.3,
1189
- size: fontSize,
1190
- font,
1191
- color: inkColor,
1192
- maxWidth: w - 4
1193
- });
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
+ };
1324
+ const maxFontSize = Math.min(field.fontSize, h * 0.7);
1325
+ let fontSize = maxFontSize;
1326
+ const padding = 4;
1327
+ while (fontSize > 4) {
1328
+ if (textWidthAtSize(field.value, fontSize) <= w - padding) break;
1329
+ fontSize -= 0.5;
1330
+ }
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
+ }
1194
1347
  }
1195
1348
  }
1196
1349
  return pdfDoc.save();
@@ -1215,7 +1368,13 @@ async function postPdfToCallback(bytes, callbackUrl, filename) {
1215
1368
  }
1216
1369
 
1217
1370
  // src/components/pdf-builder/FieldNavigator.tsx
1371
+ import { useState as useState4 } from "react";
1218
1372
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1373
+ function isFieldFilled(f) {
1374
+ if (!f.required) return true;
1375
+ if (f.type === "checkbox") return true;
1376
+ return !!f.value;
1377
+ }
1219
1378
  function FieldNavigator({
1220
1379
  fields,
1221
1380
  currentFieldId,
@@ -1224,6 +1383,7 @@ function FieldNavigator({
1224
1383
  onComplete,
1225
1384
  completeLabel = "Complete"
1226
1385
  }) {
1386
+ const [showIncomplete, setShowIncomplete] = useState4(false);
1227
1387
  const currentIndex = fields.findIndex((f) => f.id === currentFieldId);
1228
1388
  const hasPrev = currentIndex > 0;
1229
1389
  const hasNext = currentIndex < fields.length - 1;
@@ -1237,19 +1397,36 @@ function FieldNavigator({
1237
1397
  onNavigate(fields[currentIndex + 1].id);
1238
1398
  }
1239
1399
  };
1240
- const filledCount = fields.filter((f) => {
1241
- if (!f.required) return true;
1242
- if (f.type === "checkbox") return true;
1243
- return !!f.value;
1244
- }).length;
1400
+ const filledCount = fields.filter(isFieldFilled).length;
1401
+ const incompleteFields = fields.filter((f) => !isFieldFilled(f));
1245
1402
  const showCompleteAsNext = !hasNext && allRequiredFilled;
1246
1403
  return /* @__PURE__ */ jsxs5("div", { className: "field-navigator", children: [
1247
- /* @__PURE__ */ jsxs5("div", { className: "field-navigator-progress", children: [
1248
- filledCount,
1249
- " of ",
1250
- fields.length,
1251
- " fields completed"
1252
- ] }),
1404
+ /* @__PURE__ */ jsxs5(
1405
+ "div",
1406
+ {
1407
+ className: `field-navigator-progress ${incompleteFields.length > 0 ? "clickable" : ""}`,
1408
+ onClick: () => incompleteFields.length > 0 && setShowIncomplete(!showIncomplete),
1409
+ children: [
1410
+ filledCount,
1411
+ " of ",
1412
+ fields.length,
1413
+ " fields completed",
1414
+ incompleteFields.length > 0 && /* @__PURE__ */ jsx5("span", { className: "field-navigator-toggle", children: showIncomplete ? "\u25B2" : "\u25BC" })
1415
+ ]
1416
+ }
1417
+ ),
1418
+ showIncomplete && incompleteFields.length > 0 && /* @__PURE__ */ jsx5("div", { className: "incomplete-fields-list", children: incompleteFields.map((f) => /* @__PURE__ */ jsx5(
1419
+ "button",
1420
+ {
1421
+ className: "incomplete-field-item",
1422
+ onClick: () => {
1423
+ onNavigate(f.id);
1424
+ setShowIncomplete(false);
1425
+ },
1426
+ children: f.label
1427
+ },
1428
+ f.id
1429
+ )) }),
1253
1430
  /* @__PURE__ */ jsxs5("div", { className: "field-navigator-controls", children: [
1254
1431
  /* @__PURE__ */ jsx5(
1255
1432
  "button",
@@ -1281,6 +1458,95 @@ function FieldNavigator({
1281
1458
  ] });
1282
1459
  }
1283
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
+
1284
1550
  // src/components/pdf-builder/SignerView.tsx
1285
1551
  import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1286
1552
  function SignerView({
@@ -1292,7 +1558,8 @@ function SignerView({
1292
1558
  onComplete,
1293
1559
  initialValues,
1294
1560
  submitLabel,
1295
- signerOrder: signerOrderProp
1561
+ signerOrder: signerOrderProp,
1562
+ transforms
1296
1563
  } = {}) {
1297
1564
  if (!isValidApiKey(apiKey)) {
1298
1565
  return /* @__PURE__ */ jsx6("div", { className: "signer-layout", children: /* @__PURE__ */ jsxs6("div", { className: "empty-state", children: [
@@ -1304,11 +1571,11 @@ function SignerView({
1304
1571
  ] })
1305
1572
  ] }) });
1306
1573
  }
1307
- const [pages, setPages] = useState3([]);
1308
- const [fields, setFields] = useState3([]);
1309
- const [selectedFieldId, setSelectedFieldId] = useState3(null);
1310
- const [signerRoles, setSignerRoles] = useState3([]);
1311
- const [currentSignerIndex, setCurrentSignerIndex] = useState3(() => {
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(() => {
1312
1579
  if (initialSigner && signerOrderProp) {
1313
1580
  const idx = signerOrderProp.indexOf(initialSigner);
1314
1581
  return idx >= 0 ? idx : 0;
@@ -1316,15 +1583,30 @@ function SignerView({
1316
1583
  return 0;
1317
1584
  });
1318
1585
  const initializedRef = useRef4(false);
1319
- const [loading, setLoading] = useState3(false);
1320
- const [submitting, setSubmitting] = useState3(false);
1321
- const [pdfSource, setPdfSource] = useState3(null);
1322
- const [callbackUrl, setCallbackUrl] = useState3(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 || "");
1323
1590
  const containerRef = useRef4(null);
1324
1591
  const signerOrder = signerOrderProp || (signerRoles.length > 0 ? [...signerRoles.filter((r) => r !== "Sender"), ...signerRoles.filter((r) => r === "Sender")] : [initialSigner || "Signer 1"]);
1325
1592
  const signer = signerOrder[currentSignerIndex] || signerOrder[0] || "Signer 1";
1326
1593
  const isLastSigner = currentSignerIndex >= signerOrder.length - 1;
1327
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]);
1328
1610
  useEffect3(() => {
1329
1611
  if (initialTemplate) {
1330
1612
  let templateFields = initialTemplate.fields;
@@ -1407,14 +1689,14 @@ function SignerView({
1407
1689
  setLoading(false);
1408
1690
  }
1409
1691
  }, []);
1410
- 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) => {
1411
1693
  if (a.page !== b.page) return a.page - b.page;
1412
1694
  const bandThreshold = 2;
1413
1695
  if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
1414
1696
  return a.x - b.x;
1415
1697
  });
1416
1698
  const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
1417
- 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;
1418
1700
  const handleFieldUpdate = useCallback4((id, value) => {
1419
1701
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, value } : f));
1420
1702
  }, []);
@@ -1450,7 +1732,8 @@ function SignerView({
1450
1732
  if (!pdfSource) return;
1451
1733
  setSubmitting(true);
1452
1734
  try {
1453
- const pdfBytes = await generateFilledPdf(pdfSource, fields);
1735
+ const finalFields = resolveAllFormulas(fields, transforms);
1736
+ const pdfBytes = await generateFilledPdf(pdfSource, finalFields);
1454
1737
  const blob = new Blob([pdfBytes.slice().buffer], { type: "application/pdf" });
1455
1738
  if (callbackUrl) {
1456
1739
  await postPdfToCallback(pdfBytes, callbackUrl, "signed-document.pdf");
@@ -1472,6 +1755,9 @@ function SignerView({
1472
1755
  if (field.type === "blackout" || field.type === "whiteout") {
1473
1756
  return null;
1474
1757
  }
1758
+ if (field.formula) {
1759
+ return /* @__PURE__ */ jsx6("div", { className: "field-overlay-value formula", children: field.value || "..." });
1760
+ }
1475
1761
  const editable = field.assignee === signer;
1476
1762
  if (!editable) {
1477
1763
  if (field.type === "signature" || field.type === "initials") {
@@ -1504,6 +1790,28 @@ function SignerView({
1504
1790
  if (field.type === "signed-date") {
1505
1791
  return /* @__PURE__ */ jsx6("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
1506
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
+ }
1507
1815
  return /* @__PURE__ */ jsx6(
1508
1816
  "input",
1509
1817
  {
@@ -1511,10 +1819,11 @@ function SignerView({
1511
1819
  className: "field-inline-input",
1512
1820
  value: field.value,
1513
1821
  placeholder: field.placeholder,
1822
+ maxLength: field.maxLength || void 0,
1514
1823
  onChange: (e) => handleFieldUpdate(field.id, e.target.value),
1515
1824
  onFocus: () => setSelectedFieldId(field.id),
1516
1825
  onClick: (e) => e.stopPropagation(),
1517
- style: { fontSize: `${field.fontSize * 0.6}px` }
1826
+ style: fontStyle
1518
1827
  }
1519
1828
  );
1520
1829
  }, [signer, handleFieldUpdate, setSelectedFieldId]);
@@ -1530,6 +1839,13 @@ function SignerView({
1530
1839
  }));
1531
1840
  }
1532
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]);
1533
1849
  return /* @__PURE__ */ jsxs6("div", { className: "signer-layout", ref: containerRef, children: [
1534
1850
  loading && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Loading document..." }),
1535
1851
  /* @__PURE__ */ jsxs6("div", { className: "signer-content", children: [
@@ -1572,13 +1888,25 @@ function SignerView({
1572
1888
  }
1573
1889
  ),
1574
1890
  selectedField.type === "text" && /* @__PURE__ */ jsxs6(Fragment2, { children: [
1575
- /* @__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(
1576
1903
  "input",
1577
1904
  {
1578
1905
  type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
1579
1906
  value: selectedField.value,
1580
1907
  onChange: (e) => handleFieldUpdate(selectedField.id, e.target.value),
1581
1908
  placeholder: selectedField.placeholder,
1909
+ maxLength: selectedField.maxLength || void 0,
1582
1910
  className: "signer-text-input"
1583
1911
  }
1584
1912
  ),
@@ -1640,7 +1968,7 @@ function SignerView({
1640
1968
  }
1641
1969
 
1642
1970
  // src/components/pdf-builder/SignerRoleSelector.tsx
1643
- import { useState as useState4 } from "react";
1971
+ import { useState as useState6 } from "react";
1644
1972
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1645
1973
  function SignerRoleSelector({
1646
1974
  roles,
@@ -1649,8 +1977,8 @@ function SignerRoleSelector({
1649
1977
  onAddRole,
1650
1978
  onRemoveRole
1651
1979
  }) {
1652
- const [isAdding, setIsAdding] = useState4(false);
1653
- const [newRoleName, setNewRoleName] = useState4("");
1980
+ const [isAdding, setIsAdding] = useState6(false);
1981
+ const [newRoleName, setNewRoleName] = useState6("");
1654
1982
  const handleAdd = () => {
1655
1983
  if (newRoleName.trim()) {
1656
1984
  onAddRole(newRoleName.trim());
@@ -1714,9 +2042,11 @@ function SignerRoleSelector({
1714
2042
  ] });
1715
2043
  }
1716
2044
  export {
2045
+ BUILTIN_TRANSFORMS,
1717
2046
  DEFAULT_SIGNER_ROLES,
1718
2047
  DesignerView,
1719
2048
  FIELD_DEFAULTS,
2049
+ FONT_FAMILIES,
1720
2050
  FieldNavigator,
1721
2051
  FieldPropertyPanel,
1722
2052
  PdfViewer,
@@ -1727,9 +2057,12 @@ export {
1727
2057
  createField,
1728
2058
  downloadPdf,
1729
2059
  generateFilledPdf,
2060
+ generateId,
1730
2061
  getSignerColor,
1731
2062
  postPdfToCallback,
1732
2063
  renderPdfPages,
2064
+ resolveAllFormulas,
2065
+ resolveFormula,
1733
2066
  uniqueLabel
1734
2067
  };
1735
2068
  //# sourceMappingURL=index.mjs.map