@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/README.md +51 -0
- package/dist/index.css +54 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +37 -2
- package/dist/index.d.ts +37 -2
- package/dist/index.js +399 -91
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +355 -52
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/pdf-builder/DesignerView.tsx
|
|
2
|
-
import { useState as
|
|
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
|
|
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
|
-
|
|
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] =
|
|
488
|
-
const [currentPath, setCurrentPath] =
|
|
489
|
-
const [isEmpty, setIsEmpty] =
|
|
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] =
|
|
631
|
-
const [fields, setFields] =
|
|
632
|
-
const [selectedFieldId, setSelectedFieldId] =
|
|
633
|
-
const [signerRoles, setSignerRoles] =
|
|
634
|
-
const [activeRole, setActiveRole] =
|
|
635
|
-
const [activeFieldType, setActiveFieldType] =
|
|
636
|
-
const [loading, setLoading] =
|
|
637
|
-
const [pdfSource, setPdfSource] =
|
|
638
|
-
const [rightTab, setRightTab] =
|
|
639
|
-
const [isAddingRole, setIsAddingRole] =
|
|
640
|
-
const [newRoleName, setNewRoleName] =
|
|
641
|
-
const [draggingFieldType, setDraggingFieldType] =
|
|
642
|
-
const [panelWidth, setPanelWidth] =
|
|
643
|
-
const [clipboardField, setClipboardField] =
|
|
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
|
|
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
|
|
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
|
-
|
|
1190
|
-
if (textWidth <= w - padding) break;
|
|
1328
|
+
if (textWidthAtSize(field.value, fontSize) <= w - padding) break;
|
|
1191
1329
|
fontSize -= 0.5;
|
|
1192
1330
|
}
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
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
|
|
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] =
|
|
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] =
|
|
1338
|
-
const [fields, setFields] =
|
|
1339
|
-
const [selectedFieldId, setSelectedFieldId] =
|
|
1340
|
-
const [signerRoles, setSignerRoles] =
|
|
1341
|
-
const [currentSignerIndex, setCurrentSignerIndex] =
|
|
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] =
|
|
1350
|
-
const [submitting, setSubmitting] =
|
|
1351
|
-
const [pdfSource, setPdfSource] =
|
|
1352
|
-
const [callbackUrl, setCallbackUrl] =
|
|
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
|
|
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:
|
|
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__ */
|
|
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
|
|
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] =
|
|
1683
|
-
const [newRoleName, setNewRoleName] =
|
|
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
|