@unlev/exeq 0.1.12 → 0.2.1
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 +38 -3
- package/dist/index.d.ts +38 -3
- package/dist/index.js +421 -95
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +377 -56
- 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,
|
|
@@ -33,6 +38,12 @@ var FIELD_DEFAULTS = {
|
|
|
33
38
|
placeholder: "Enter text",
|
|
34
39
|
textSubtype: "freeform"
|
|
35
40
|
},
|
|
41
|
+
dropdown: {
|
|
42
|
+
width: 20,
|
|
43
|
+
height: 3,
|
|
44
|
+
fontSize: 12,
|
|
45
|
+
placeholder: "Select..."
|
|
46
|
+
},
|
|
36
47
|
signature: {
|
|
37
48
|
width: 20,
|
|
38
49
|
height: 6,
|
|
@@ -72,6 +83,7 @@ var FIELD_DEFAULTS = {
|
|
|
72
83
|
};
|
|
73
84
|
var TYPE_LABELS = {
|
|
74
85
|
text: "Text Field",
|
|
86
|
+
dropdown: "Dropdown",
|
|
75
87
|
signature: "Signature",
|
|
76
88
|
"signed-date": "Signed Date",
|
|
77
89
|
checkbox: "Checkbox",
|
|
@@ -336,6 +348,7 @@ function FieldOverlayItem({
|
|
|
336
348
|
}
|
|
337
349
|
|
|
338
350
|
// src/components/pdf-builder/FieldPropertyPanel.tsx
|
|
351
|
+
import { useState } from "react";
|
|
339
352
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
340
353
|
var INK_COLORS = [
|
|
341
354
|
{ value: "#000000", label: "Black" },
|
|
@@ -344,8 +357,9 @@ var INK_COLORS = [
|
|
|
344
357
|
function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
345
358
|
const color = getSignerColor(field.assignee);
|
|
346
359
|
const isRedactField = field.type === "blackout" || field.type === "whiteout";
|
|
347
|
-
const
|
|
348
|
-
const showInkColor = field.type === "text" || field.type === "signature" || field.type === "initials" || field.type === "signed-date";
|
|
360
|
+
const isTextField = field.type === "text" || field.type === "signed-date" || field.type === "dropdown";
|
|
361
|
+
const showInkColor = field.type === "text" || field.type === "dropdown" || field.type === "signature" || field.type === "initials" || field.type === "signed-date";
|
|
362
|
+
const [newOption, setNewOption] = useState("");
|
|
349
363
|
return /* @__PURE__ */ jsxs2("div", { className: "field-property-panel", children: [
|
|
350
364
|
/* @__PURE__ */ jsxs2("div", { className: "panel-header", children: [
|
|
351
365
|
/* @__PURE__ */ jsx2("h3", { style: { color }, children: field.label }),
|
|
@@ -371,6 +385,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
371
385
|
onChange: (e) => onUpdate(field.id, { type: e.target.value }),
|
|
372
386
|
children: [
|
|
373
387
|
/* @__PURE__ */ jsx2("option", { value: "text", children: "Text" }),
|
|
388
|
+
/* @__PURE__ */ jsx2("option", { value: "dropdown", children: "Dropdown" }),
|
|
374
389
|
/* @__PURE__ */ jsx2("option", { value: "signature", children: "Signature" }),
|
|
375
390
|
/* @__PURE__ */ jsx2("option", { value: "signed-date", children: "Signed Date" }),
|
|
376
391
|
/* @__PURE__ */ jsx2("option", { value: "checkbox", children: "Checkbox" }),
|
|
@@ -418,6 +433,19 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
418
433
|
}
|
|
419
434
|
)
|
|
420
435
|
] }),
|
|
436
|
+
isTextField && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
437
|
+
/* @__PURE__ */ jsx2("label", { children: "Formula" }),
|
|
438
|
+
/* @__PURE__ */ jsx2(
|
|
439
|
+
"input",
|
|
440
|
+
{
|
|
441
|
+
type: "text",
|
|
442
|
+
value: field.formula || "",
|
|
443
|
+
onChange: (e) => onUpdate(field.id, { formula: e.target.value || void 0 }),
|
|
444
|
+
placeholder: "e.g. {{Date Field | month}}"
|
|
445
|
+
}
|
|
446
|
+
),
|
|
447
|
+
field.formula && /* @__PURE__ */ jsx2("span", { className: "panel-hint", children: "This field auto-computes from other fields. Signer cannot edit it." })
|
|
448
|
+
] }),
|
|
421
449
|
!isRedactField && field.type !== "checkbox" && /* @__PURE__ */ jsx2("div", { className: "panel-field", children: /* @__PURE__ */ jsxs2("label", { className: "panel-checkbox-label", children: [
|
|
422
450
|
/* @__PURE__ */ jsx2(
|
|
423
451
|
"input",
|
|
@@ -429,7 +457,18 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
429
457
|
),
|
|
430
458
|
"Required"
|
|
431
459
|
] }) }),
|
|
432
|
-
|
|
460
|
+
isTextField && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
461
|
+
/* @__PURE__ */ jsx2("label", { children: "Font" }),
|
|
462
|
+
/* @__PURE__ */ jsx2(
|
|
463
|
+
"select",
|
|
464
|
+
{
|
|
465
|
+
value: field.fontFamily || "Helvetica",
|
|
466
|
+
onChange: (e) => onUpdate(field.id, { fontFamily: e.target.value }),
|
|
467
|
+
children: FONT_FAMILIES.map((f) => /* @__PURE__ */ jsx2("option", { value: f.value, children: f.label }, f.value))
|
|
468
|
+
}
|
|
469
|
+
)
|
|
470
|
+
] }),
|
|
471
|
+
isTextField && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
433
472
|
/* @__PURE__ */ jsx2("label", { children: "Font Size (pt)" }),
|
|
434
473
|
/* @__PURE__ */ jsx2(
|
|
435
474
|
"input",
|
|
@@ -442,6 +481,49 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
442
481
|
}
|
|
443
482
|
)
|
|
444
483
|
] }),
|
|
484
|
+
isTextField && /* @__PURE__ */ jsxs2("div", { className: "panel-field-row", children: [
|
|
485
|
+
/* @__PURE__ */ jsxs2("div", { className: "panel-field panel-field-half", children: [
|
|
486
|
+
/* @__PURE__ */ jsx2("label", { children: "Letter Spacing" }),
|
|
487
|
+
/* @__PURE__ */ jsx2(
|
|
488
|
+
"input",
|
|
489
|
+
{
|
|
490
|
+
type: "number",
|
|
491
|
+
min: "0",
|
|
492
|
+
max: "20",
|
|
493
|
+
step: "0.5",
|
|
494
|
+
value: field.letterSpacing || 0,
|
|
495
|
+
onChange: (e) => onUpdate(field.id, { letterSpacing: Number(e.target.value) })
|
|
496
|
+
}
|
|
497
|
+
)
|
|
498
|
+
] }),
|
|
499
|
+
/* @__PURE__ */ jsxs2("div", { className: "panel-field panel-field-half", children: [
|
|
500
|
+
/* @__PURE__ */ jsx2("label", { children: "Line Height" }),
|
|
501
|
+
/* @__PURE__ */ jsx2(
|
|
502
|
+
"input",
|
|
503
|
+
{
|
|
504
|
+
type: "number",
|
|
505
|
+
min: "0.8",
|
|
506
|
+
max: "3",
|
|
507
|
+
step: "0.1",
|
|
508
|
+
value: field.lineHeight || 1.2,
|
|
509
|
+
onChange: (e) => onUpdate(field.id, { lineHeight: Number(e.target.value) })
|
|
510
|
+
}
|
|
511
|
+
)
|
|
512
|
+
] })
|
|
513
|
+
] }),
|
|
514
|
+
field.type === "text" && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
515
|
+
/* @__PURE__ */ jsx2("label", { children: "Max Characters (0 = unlimited)" }),
|
|
516
|
+
/* @__PURE__ */ jsx2(
|
|
517
|
+
"input",
|
|
518
|
+
{
|
|
519
|
+
type: "number",
|
|
520
|
+
min: "0",
|
|
521
|
+
max: "9999",
|
|
522
|
+
value: field.maxLength || 0,
|
|
523
|
+
onChange: (e) => onUpdate(field.id, { maxLength: Number(e.target.value) })
|
|
524
|
+
}
|
|
525
|
+
)
|
|
526
|
+
] }),
|
|
445
527
|
showInkColor && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
446
528
|
/* @__PURE__ */ jsx2("label", { children: "Ink Color" }),
|
|
447
529
|
/* @__PURE__ */ jsx2("div", { className: "ink-color-picker", children: INK_COLORS.map((c) => /* @__PURE__ */ jsx2(
|
|
@@ -454,12 +536,62 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
454
536
|
},
|
|
455
537
|
c.value
|
|
456
538
|
)) })
|
|
539
|
+
] }),
|
|
540
|
+
(field.type === "text" || field.type === "dropdown") && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
541
|
+
/* @__PURE__ */ jsx2("label", { children: field.type === "dropdown" ? "Options" : "Predefined Options" }),
|
|
542
|
+
/* @__PURE__ */ jsxs2("div", { className: "panel-options-list", children: [
|
|
543
|
+
(field.options || []).map((opt, i) => /* @__PURE__ */ jsxs2("div", { className: "panel-option-item", children: [
|
|
544
|
+
/* @__PURE__ */ jsx2("span", { children: opt }),
|
|
545
|
+
/* @__PURE__ */ jsx2(
|
|
546
|
+
"button",
|
|
547
|
+
{
|
|
548
|
+
className: "panel-option-remove",
|
|
549
|
+
onClick: () => {
|
|
550
|
+
const updated = (field.options || []).filter((_, j) => j !== i);
|
|
551
|
+
onUpdate(field.id, { options: updated.length > 0 ? updated : void 0 });
|
|
552
|
+
},
|
|
553
|
+
children: "\xD7"
|
|
554
|
+
}
|
|
555
|
+
)
|
|
556
|
+
] }, i)),
|
|
557
|
+
/* @__PURE__ */ jsxs2("div", { className: "panel-option-add", children: [
|
|
558
|
+
/* @__PURE__ */ jsx2(
|
|
559
|
+
"input",
|
|
560
|
+
{
|
|
561
|
+
type: "text",
|
|
562
|
+
value: newOption,
|
|
563
|
+
onChange: (e) => setNewOption(e.target.value),
|
|
564
|
+
onKeyDown: (e) => {
|
|
565
|
+
if (e.key === "Enter" && newOption.trim()) {
|
|
566
|
+
onUpdate(field.id, { options: [...field.options || [], newOption.trim()] });
|
|
567
|
+
setNewOption("");
|
|
568
|
+
}
|
|
569
|
+
},
|
|
570
|
+
placeholder: "Add option..."
|
|
571
|
+
}
|
|
572
|
+
),
|
|
573
|
+
/* @__PURE__ */ jsx2(
|
|
574
|
+
"button",
|
|
575
|
+
{
|
|
576
|
+
onClick: () => {
|
|
577
|
+
if (newOption.trim()) {
|
|
578
|
+
onUpdate(field.id, { options: [...field.options || [], newOption.trim()] });
|
|
579
|
+
setNewOption("");
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
children: "+"
|
|
583
|
+
}
|
|
584
|
+
)
|
|
585
|
+
] })
|
|
586
|
+
] }),
|
|
587
|
+
field.type === "dropdown" && (field.options?.length ?? 0) === 0 && /* @__PURE__ */ jsx2("span", { className: "panel-hint", style: { color: "#c44" }, children: "Add at least one option" }),
|
|
588
|
+
field.type === "text" && (field.options?.length ?? 0) > 0 && /* @__PURE__ */ jsx2("span", { className: "panel-hint", children: "Signer will choose from these options via dropdown" })
|
|
457
589
|
] })
|
|
458
590
|
] });
|
|
459
591
|
}
|
|
460
592
|
|
|
461
593
|
// src/components/pdf-builder/SignatureCanvas.tsx
|
|
462
|
-
import { useRef as useRef2, useState, useCallback as useCallback2, useEffect } from "react";
|
|
594
|
+
import { useRef as useRef2, useState as useState2, useCallback as useCallback2, useEffect } from "react";
|
|
463
595
|
import { getStroke } from "perfect-freehand";
|
|
464
596
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
465
597
|
function getSvgPathFromStroke(stroke) {
|
|
@@ -484,9 +616,9 @@ function SignatureCanvas({
|
|
|
484
616
|
inkColor = "#000000"
|
|
485
617
|
}) {
|
|
486
618
|
const canvasRef = useRef2(null);
|
|
487
|
-
const [paths, setPaths] =
|
|
488
|
-
const [currentPath, setCurrentPath] =
|
|
489
|
-
const [isEmpty, setIsEmpty] =
|
|
619
|
+
const [paths, setPaths] = useState2([]);
|
|
620
|
+
const [currentPath, setCurrentPath] = useState2(null);
|
|
621
|
+
const [isEmpty, setIsEmpty] = useState2(!initialValue);
|
|
490
622
|
useEffect(() => {
|
|
491
623
|
if (initialValue && canvasRef.current) {
|
|
492
624
|
const ctx = canvasRef.current.getContext("2d");
|
|
@@ -602,6 +734,7 @@ function isValidApiKey(key) {
|
|
|
602
734
|
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
603
735
|
var FIELD_TYPE_META = [
|
|
604
736
|
{ type: "text", label: "Text", icon: "T" },
|
|
737
|
+
{ type: "dropdown", label: "Dropdown", icon: "\u25BE" },
|
|
605
738
|
{ type: "signature", label: "Signature", icon: "\u270D" },
|
|
606
739
|
{ type: "signed-date", label: "Date", icon: "\u{1F4C5}" },
|
|
607
740
|
{ type: "checkbox", label: "Checkbox", icon: "\u2611" },
|
|
@@ -627,20 +760,20 @@ function DesignerView({
|
|
|
627
760
|
] })
|
|
628
761
|
] }) });
|
|
629
762
|
}
|
|
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] =
|
|
763
|
+
const [pages, setPages] = useState3([]);
|
|
764
|
+
const [fields, setFields] = useState3(initialTemplate?.fields ?? []);
|
|
765
|
+
const [selectedFieldId, setSelectedFieldId] = useState3(null);
|
|
766
|
+
const [signerRoles, setSignerRoles] = useState3(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
|
|
767
|
+
const [activeRole, setActiveRole] = useState3("Sender");
|
|
768
|
+
const [activeFieldType, setActiveFieldType] = useState3("text");
|
|
769
|
+
const [loading, setLoading] = useState3(false);
|
|
770
|
+
const [pdfSource, setPdfSource] = useState3(null);
|
|
771
|
+
const [rightTab, setRightTab] = useState3("properties");
|
|
772
|
+
const [isAddingRole, setIsAddingRole] = useState3(false);
|
|
773
|
+
const [newRoleName, setNewRoleName] = useState3("");
|
|
774
|
+
const [draggingFieldType, setDraggingFieldType] = useState3(null);
|
|
775
|
+
const [panelWidth, setPanelWidth] = useState3(380);
|
|
776
|
+
const [clipboardField, setClipboardField] = useState3(null);
|
|
644
777
|
const dragGhostRef = useRef3(null);
|
|
645
778
|
const resizingRef = useRef3(false);
|
|
646
779
|
const lastStylesRef = useRef3({});
|
|
@@ -881,15 +1014,22 @@ function DesignerView({
|
|
|
881
1014
|
}
|
|
882
1015
|
}
|
|
883
1016
|
const inkColor = field.inkColor || "#000000";
|
|
1017
|
+
const cssFontFamily = field.fontFamily === "Courier" ? '"Courier New", Courier, monospace' : field.fontFamily === "TimesRoman" ? '"Times New Roman", Times, serif' : field.fontFamily === "Helvetica" ? "Helvetica, Arial, sans-serif" : void 0;
|
|
884
1018
|
return /* @__PURE__ */ jsx4(
|
|
885
1019
|
"div",
|
|
886
1020
|
{
|
|
887
1021
|
className: "field-overlay-placeholder",
|
|
888
|
-
style: {
|
|
1022
|
+
style: {
|
|
1023
|
+
color: field.value ? inkColor : void 0,
|
|
1024
|
+
fontSize: `${field.fontSize * 0.6}px`,
|
|
1025
|
+
fontFamily: cssFontFamily,
|
|
1026
|
+
letterSpacing: field.letterSpacing ? `${field.letterSpacing * 0.6}px` : void 0,
|
|
1027
|
+
lineHeight: field.lineHeight ? `${field.lineHeight}` : void 0
|
|
1028
|
+
},
|
|
889
1029
|
children: field.value || field.placeholder
|
|
890
1030
|
}
|
|
891
1031
|
);
|
|
892
|
-
}, []);
|
|
1032
|
+
}, [fields]);
|
|
893
1033
|
const headerButtons = pages.length > 0 ? /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
894
1034
|
/* @__PURE__ */ jsxs4("label", { className: "header-btn header-btn-outline", children: [
|
|
895
1035
|
"Change PDF",
|
|
@@ -1108,10 +1248,15 @@ function DesignerView({
|
|
|
1108
1248
|
}
|
|
1109
1249
|
|
|
1110
1250
|
// src/components/pdf-builder/SignerView.tsx
|
|
1111
|
-
import { useState as
|
|
1251
|
+
import { useState as useState5, useCallback as useCallback4, useEffect as useEffect3, useRef as useRef4 } from "react";
|
|
1112
1252
|
|
|
1113
1253
|
// src/utils/pdfFiller.ts
|
|
1114
1254
|
import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
|
|
1255
|
+
var FONT_MAP = {
|
|
1256
|
+
"Helvetica": StandardFonts.Helvetica,
|
|
1257
|
+
"Courier": StandardFonts.Courier,
|
|
1258
|
+
"TimesRoman": StandardFonts.TimesRoman
|
|
1259
|
+
};
|
|
1115
1260
|
async function generateFilledPdf(pdfSource, fields) {
|
|
1116
1261
|
let pdfBytes;
|
|
1117
1262
|
if (typeof pdfSource === "string") {
|
|
@@ -1121,7 +1266,12 @@ async function generateFilledPdf(pdfSource, fields) {
|
|
|
1121
1266
|
pdfBytes = pdfSource;
|
|
1122
1267
|
}
|
|
1123
1268
|
const pdfDoc = await PDFDocument.load(pdfBytes);
|
|
1124
|
-
const
|
|
1269
|
+
const fontCache = /* @__PURE__ */ new Map();
|
|
1270
|
+
const usedFontKeys = new Set(fields.map((f) => f.fontFamily || "Helvetica"));
|
|
1271
|
+
for (const key of usedFontKeys) {
|
|
1272
|
+
const stdFont = FONT_MAP[key] || StandardFonts.Helvetica;
|
|
1273
|
+
fontCache.set(key, await pdfDoc.embedFont(stdFont));
|
|
1274
|
+
}
|
|
1125
1275
|
const pages = pdfDoc.getPages();
|
|
1126
1276
|
function hexToRgb(hex) {
|
|
1127
1277
|
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
@@ -1182,21 +1332,35 @@ async function generateFilledPdf(pdfSource, fields) {
|
|
|
1182
1332
|
});
|
|
1183
1333
|
}
|
|
1184
1334
|
} else {
|
|
1335
|
+
const font = fontCache.get(field.fontFamily || "Helvetica");
|
|
1336
|
+
const spacing = field.letterSpacing || 0;
|
|
1337
|
+
const textWidthAtSize = (text, size) => {
|
|
1338
|
+
const baseWidth = font.widthOfTextAtSize(text, size);
|
|
1339
|
+
return baseWidth + spacing * (text.length - 1);
|
|
1340
|
+
};
|
|
1185
1341
|
const maxFontSize = Math.min(field.fontSize, h * 0.7);
|
|
1186
1342
|
let fontSize = maxFontSize;
|
|
1187
1343
|
const padding = 4;
|
|
1188
1344
|
while (fontSize > 4) {
|
|
1189
|
-
|
|
1190
|
-
if (textWidth <= w - padding) break;
|
|
1345
|
+
if (textWidthAtSize(field.value, fontSize) <= w - padding) break;
|
|
1191
1346
|
fontSize -= 0.5;
|
|
1192
1347
|
}
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1348
|
+
if (spacing > 0) {
|
|
1349
|
+
let cx = x + 2;
|
|
1350
|
+
const cy = y + h * 0.3;
|
|
1351
|
+
for (const char of field.value) {
|
|
1352
|
+
page.drawText(char, { x: cx, y: cy, size: fontSize, font, color: inkColor });
|
|
1353
|
+
cx += font.widthOfTextAtSize(char, fontSize) + spacing;
|
|
1354
|
+
}
|
|
1355
|
+
} else {
|
|
1356
|
+
page.drawText(field.value, {
|
|
1357
|
+
x: x + 2,
|
|
1358
|
+
y: y + h * 0.3,
|
|
1359
|
+
size: fontSize,
|
|
1360
|
+
font,
|
|
1361
|
+
color: inkColor
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1200
1364
|
}
|
|
1201
1365
|
}
|
|
1202
1366
|
return pdfDoc.save();
|
|
@@ -1221,7 +1385,7 @@ async function postPdfToCallback(bytes, callbackUrl, filename) {
|
|
|
1221
1385
|
}
|
|
1222
1386
|
|
|
1223
1387
|
// src/components/pdf-builder/FieldNavigator.tsx
|
|
1224
|
-
import { useState as
|
|
1388
|
+
import { useState as useState4 } from "react";
|
|
1225
1389
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1226
1390
|
function isFieldFilled(f) {
|
|
1227
1391
|
if (!f.required) return true;
|
|
@@ -1236,7 +1400,7 @@ function FieldNavigator({
|
|
|
1236
1400
|
onComplete,
|
|
1237
1401
|
completeLabel = "Complete"
|
|
1238
1402
|
}) {
|
|
1239
|
-
const [showIncomplete, setShowIncomplete] =
|
|
1403
|
+
const [showIncomplete, setShowIncomplete] = useState4(false);
|
|
1240
1404
|
const currentIndex = fields.findIndex((f) => f.id === currentFieldId);
|
|
1241
1405
|
const hasPrev = currentIndex > 0;
|
|
1242
1406
|
const hasNext = currentIndex < fields.length - 1;
|
|
@@ -1311,6 +1475,95 @@ function FieldNavigator({
|
|
|
1311
1475
|
] });
|
|
1312
1476
|
}
|
|
1313
1477
|
|
|
1478
|
+
// src/utils/formulaResolver.ts
|
|
1479
|
+
var BUILTIN_TRANSFORMS = {
|
|
1480
|
+
// Date transforms (expects a parseable date string)
|
|
1481
|
+
month: (v) => {
|
|
1482
|
+
const d = new Date(v);
|
|
1483
|
+
return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1);
|
|
1484
|
+
},
|
|
1485
|
+
month2: (v) => {
|
|
1486
|
+
const d = new Date(v);
|
|
1487
|
+
return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1).padStart(2, "0");
|
|
1488
|
+
},
|
|
1489
|
+
monthname: (v) => {
|
|
1490
|
+
const d = new Date(v);
|
|
1491
|
+
return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "long" });
|
|
1492
|
+
},
|
|
1493
|
+
monthshort: (v) => {
|
|
1494
|
+
const d = new Date(v);
|
|
1495
|
+
return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "short" });
|
|
1496
|
+
},
|
|
1497
|
+
day: (v) => {
|
|
1498
|
+
const d = new Date(v);
|
|
1499
|
+
return isNaN(d.getTime()) ? "" : String(d.getDate());
|
|
1500
|
+
},
|
|
1501
|
+
day2: (v) => {
|
|
1502
|
+
const d = new Date(v);
|
|
1503
|
+
return isNaN(d.getTime()) ? "" : String(d.getDate()).padStart(2, "0");
|
|
1504
|
+
},
|
|
1505
|
+
year: (v) => {
|
|
1506
|
+
const d = new Date(v);
|
|
1507
|
+
return isNaN(d.getTime()) ? "" : String(d.getFullYear());
|
|
1508
|
+
},
|
|
1509
|
+
year2: (v) => {
|
|
1510
|
+
const d = new Date(v);
|
|
1511
|
+
return isNaN(d.getTime()) ? "" : String(d.getFullYear()).slice(-2);
|
|
1512
|
+
},
|
|
1513
|
+
// String transforms
|
|
1514
|
+
upper: (v) => v.toUpperCase(),
|
|
1515
|
+
lower: (v) => v.toLowerCase(),
|
|
1516
|
+
trim: (v) => v.trim(),
|
|
1517
|
+
first: (v) => v.split(/\s+/)[0] || "",
|
|
1518
|
+
last: (v) => {
|
|
1519
|
+
const parts = v.split(/\s+/);
|
|
1520
|
+
return parts[parts.length - 1] || "";
|
|
1521
|
+
},
|
|
1522
|
+
initials: (v) => v.split(/\s+/).map((w) => w[0] || "").join("").toUpperCase(),
|
|
1523
|
+
// Numeric / substring
|
|
1524
|
+
last4: (v) => v.slice(-4),
|
|
1525
|
+
last2: (v) => v.slice(-2),
|
|
1526
|
+
first4: (v) => v.slice(0, 4),
|
|
1527
|
+
first2: (v) => v.slice(0, 2),
|
|
1528
|
+
digits: (v) => v.replace(/\D/g, ""),
|
|
1529
|
+
number: (v) => {
|
|
1530
|
+
const n = parseFloat(v);
|
|
1531
|
+
return isNaN(n) ? "" : String(n);
|
|
1532
|
+
},
|
|
1533
|
+
currency: (v) => {
|
|
1534
|
+
const n = parseFloat(v.replace(/[^0-9.-]/g, ""));
|
|
1535
|
+
return isNaN(n) ? "" : `$${n.toFixed(2)}`;
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1538
|
+
var FORMULA_RE = /\{\{(.+?)\}\}/g;
|
|
1539
|
+
var PIPE_RE = /^(.+?)\s*\|\s*(.+)$/;
|
|
1540
|
+
function resolveFormula(formula, fields, customTransforms) {
|
|
1541
|
+
const transforms = { ...BUILTIN_TRANSFORMS, ...customTransforms };
|
|
1542
|
+
return formula.replace(FORMULA_RE, (_match, expr) => {
|
|
1543
|
+
const pipeMatch = expr.match(PIPE_RE);
|
|
1544
|
+
const sourceLabel = (pipeMatch ? pipeMatch[1] : expr).trim();
|
|
1545
|
+
const transformName = pipeMatch ? pipeMatch[2].trim() : null;
|
|
1546
|
+
const sourceField = fields.find((f) => f.label.toLowerCase() === sourceLabel.toLowerCase()) || fields.find((f) => f.id === sourceLabel);
|
|
1547
|
+
if (!sourceField) return "";
|
|
1548
|
+
const rawValue = sourceField.value || "";
|
|
1549
|
+
if (!transformName) return rawValue;
|
|
1550
|
+
const fn = transforms[transformName];
|
|
1551
|
+
if (!fn) return rawValue;
|
|
1552
|
+
try {
|
|
1553
|
+
return fn(rawValue);
|
|
1554
|
+
} catch {
|
|
1555
|
+
return rawValue;
|
|
1556
|
+
}
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
function resolveAllFormulas(fields, customTransforms) {
|
|
1560
|
+
return fields.map((f) => {
|
|
1561
|
+
if (!f.formula) return f;
|
|
1562
|
+
const computed = resolveFormula(f.formula, fields, customTransforms);
|
|
1563
|
+
return computed !== f.value ? { ...f, value: computed } : f;
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1314
1567
|
// src/components/pdf-builder/SignerView.tsx
|
|
1315
1568
|
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1316
1569
|
function SignerView({
|
|
@@ -1322,7 +1575,8 @@ function SignerView({
|
|
|
1322
1575
|
onComplete,
|
|
1323
1576
|
initialValues,
|
|
1324
1577
|
submitLabel,
|
|
1325
|
-
signerOrder: signerOrderProp
|
|
1578
|
+
signerOrder: signerOrderProp,
|
|
1579
|
+
transforms
|
|
1326
1580
|
} = {}) {
|
|
1327
1581
|
if (!isValidApiKey(apiKey)) {
|
|
1328
1582
|
return /* @__PURE__ */ jsx6("div", { className: "signer-layout", children: /* @__PURE__ */ jsxs6("div", { className: "empty-state", children: [
|
|
@@ -1334,11 +1588,11 @@ function SignerView({
|
|
|
1334
1588
|
] })
|
|
1335
1589
|
] }) });
|
|
1336
1590
|
}
|
|
1337
|
-
const [pages, setPages] =
|
|
1338
|
-
const [fields, setFields] =
|
|
1339
|
-
const [selectedFieldId, setSelectedFieldId] =
|
|
1340
|
-
const [signerRoles, setSignerRoles] =
|
|
1341
|
-
const [currentSignerIndex, setCurrentSignerIndex] =
|
|
1591
|
+
const [pages, setPages] = useState5([]);
|
|
1592
|
+
const [fields, setFields] = useState5([]);
|
|
1593
|
+
const [selectedFieldId, setSelectedFieldId] = useState5(null);
|
|
1594
|
+
const [signerRoles, setSignerRoles] = useState5([]);
|
|
1595
|
+
const [currentSignerIndex, setCurrentSignerIndex] = useState5(() => {
|
|
1342
1596
|
if (initialSigner && signerOrderProp) {
|
|
1343
1597
|
const idx = signerOrderProp.indexOf(initialSigner);
|
|
1344
1598
|
return idx >= 0 ? idx : 0;
|
|
@@ -1346,15 +1600,30 @@ function SignerView({
|
|
|
1346
1600
|
return 0;
|
|
1347
1601
|
});
|
|
1348
1602
|
const initializedRef = useRef4(false);
|
|
1349
|
-
const [loading, setLoading] =
|
|
1350
|
-
const [submitting, setSubmitting] =
|
|
1351
|
-
const [pdfSource, setPdfSource] =
|
|
1352
|
-
const [callbackUrl, setCallbackUrl] =
|
|
1603
|
+
const [loading, setLoading] = useState5(false);
|
|
1604
|
+
const [submitting, setSubmitting] = useState5(false);
|
|
1605
|
+
const [pdfSource, setPdfSource] = useState5(null);
|
|
1606
|
+
const [callbackUrl, setCallbackUrl] = useState5(initialCallbackUrl || "");
|
|
1353
1607
|
const containerRef = useRef4(null);
|
|
1354
1608
|
const signerOrder = signerOrderProp || (signerRoles.length > 0 ? [...signerRoles.filter((r) => r !== "Sender"), ...signerRoles.filter((r) => r === "Sender")] : [initialSigner || "Signer 1"]);
|
|
1355
1609
|
const signer = signerOrder[currentSignerIndex] || signerOrder[0] || "Signer 1";
|
|
1356
1610
|
const isLastSigner = currentSignerIndex >= signerOrder.length - 1;
|
|
1357
1611
|
const isMultiSigner = signerOrder.length > 1;
|
|
1612
|
+
useEffect3(() => {
|
|
1613
|
+
if (fields.length === 0 || !isMultiSigner) return;
|
|
1614
|
+
const signerFields = fields.filter(
|
|
1615
|
+
(f) => f.assignee === signer && f.type !== "blackout" && f.type !== "whiteout"
|
|
1616
|
+
);
|
|
1617
|
+
const allFilled = signerFields.length > 0 && signerFields.every((f) => {
|
|
1618
|
+
if (!f.required) return true;
|
|
1619
|
+
if (f.type === "checkbox") return true;
|
|
1620
|
+
return !!f.value;
|
|
1621
|
+
});
|
|
1622
|
+
if (allFilled && !isLastSigner) {
|
|
1623
|
+
setCurrentSignerIndex((prev) => prev + 1);
|
|
1624
|
+
setSelectedFieldId(null);
|
|
1625
|
+
}
|
|
1626
|
+
}, [currentSignerIndex, fields, signer, isLastSigner, isMultiSigner]);
|
|
1358
1627
|
useEffect3(() => {
|
|
1359
1628
|
if (initialTemplate) {
|
|
1360
1629
|
let templateFields = initialTemplate.fields;
|
|
@@ -1437,14 +1706,14 @@ function SignerView({
|
|
|
1437
1706
|
setLoading(false);
|
|
1438
1707
|
}
|
|
1439
1708
|
}, []);
|
|
1440
|
-
const editableFields = fields.filter((f) => f.assignee === signer && f.type !== "blackout" && f.type !== "whiteout").sort((a, b) => {
|
|
1709
|
+
const editableFields = fields.filter((f) => f.assignee === signer && f.type !== "blackout" && f.type !== "whiteout" && !f.formula).sort((a, b) => {
|
|
1441
1710
|
if (a.page !== b.page) return a.page - b.page;
|
|
1442
1711
|
const bandThreshold = 2;
|
|
1443
1712
|
if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
|
|
1444
1713
|
return a.x - b.x;
|
|
1445
1714
|
});
|
|
1446
1715
|
const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
|
|
1447
|
-
const isFieldEditable = selectedField ? selectedField.assignee === signer && selectedField.type !== "blackout" && selectedField.type !== "whiteout" : false;
|
|
1716
|
+
const isFieldEditable = selectedField ? selectedField.assignee === signer && selectedField.type !== "blackout" && selectedField.type !== "whiteout" && !selectedField.formula : false;
|
|
1448
1717
|
const handleFieldUpdate = useCallback4((id, value) => {
|
|
1449
1718
|
setFields((prev) => prev.map((f) => f.id === id ? { ...f, value } : f));
|
|
1450
1719
|
}, []);
|
|
@@ -1480,7 +1749,8 @@ function SignerView({
|
|
|
1480
1749
|
if (!pdfSource) return;
|
|
1481
1750
|
setSubmitting(true);
|
|
1482
1751
|
try {
|
|
1483
|
-
const
|
|
1752
|
+
const finalFields = resolveAllFormulas(fields, transforms);
|
|
1753
|
+
const pdfBytes = await generateFilledPdf(pdfSource, finalFields);
|
|
1484
1754
|
const blob = new Blob([pdfBytes.slice().buffer], { type: "application/pdf" });
|
|
1485
1755
|
if (callbackUrl) {
|
|
1486
1756
|
await postPdfToCallback(pdfBytes, callbackUrl, "signed-document.pdf");
|
|
@@ -1502,6 +1772,9 @@ function SignerView({
|
|
|
1502
1772
|
if (field.type === "blackout" || field.type === "whiteout") {
|
|
1503
1773
|
return null;
|
|
1504
1774
|
}
|
|
1775
|
+
if (field.formula) {
|
|
1776
|
+
return /* @__PURE__ */ jsx6("div", { className: "field-overlay-value formula", children: field.value || "..." });
|
|
1777
|
+
}
|
|
1505
1778
|
const editable = field.assignee === signer;
|
|
1506
1779
|
if (!editable) {
|
|
1507
1780
|
if (field.type === "signature" || field.type === "initials") {
|
|
@@ -1534,6 +1807,29 @@ function SignerView({
|
|
|
1534
1807
|
if (field.type === "signed-date") {
|
|
1535
1808
|
return /* @__PURE__ */ jsx6("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
|
|
1536
1809
|
}
|
|
1810
|
+
const fontStyle = {
|
|
1811
|
+
fontSize: `${field.fontSize * 0.6}px`,
|
|
1812
|
+
letterSpacing: field.letterSpacing ? `${field.letterSpacing * 0.6}px` : void 0,
|
|
1813
|
+
lineHeight: field.lineHeight ? `${field.lineHeight}` : void 0,
|
|
1814
|
+
fontFamily: field.fontFamily === "Courier" ? '"Courier New", Courier, monospace' : field.fontFamily === "TimesRoman" ? '"Times New Roman", Times, serif' : field.fontFamily === "Helvetica" ? "Helvetica, Arial, sans-serif" : void 0
|
|
1815
|
+
};
|
|
1816
|
+
if (field.type === "dropdown" || field.options && field.options.length > 0) {
|
|
1817
|
+
return /* @__PURE__ */ jsxs6(
|
|
1818
|
+
"select",
|
|
1819
|
+
{
|
|
1820
|
+
className: "field-inline-input",
|
|
1821
|
+
value: field.value,
|
|
1822
|
+
onChange: (e) => handleFieldUpdate(field.id, e.target.value),
|
|
1823
|
+
onFocus: () => setSelectedFieldId(field.id),
|
|
1824
|
+
onClick: (e) => e.stopPropagation(),
|
|
1825
|
+
style: fontStyle,
|
|
1826
|
+
children: [
|
|
1827
|
+
/* @__PURE__ */ jsx6("option", { value: "", children: field.placeholder || "Select..." }),
|
|
1828
|
+
(field.options || []).map((opt) => /* @__PURE__ */ jsx6("option", { value: opt, children: opt }, opt))
|
|
1829
|
+
]
|
|
1830
|
+
}
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1537
1833
|
return /* @__PURE__ */ jsx6(
|
|
1538
1834
|
"input",
|
|
1539
1835
|
{
|
|
@@ -1541,10 +1837,11 @@ function SignerView({
|
|
|
1541
1837
|
className: "field-inline-input",
|
|
1542
1838
|
value: field.value,
|
|
1543
1839
|
placeholder: field.placeholder,
|
|
1840
|
+
maxLength: field.maxLength || void 0,
|
|
1544
1841
|
onChange: (e) => handleFieldUpdate(field.id, e.target.value),
|
|
1545
1842
|
onFocus: () => setSelectedFieldId(field.id),
|
|
1546
1843
|
onClick: (e) => e.stopPropagation(),
|
|
1547
|
-
style:
|
|
1844
|
+
style: fontStyle
|
|
1548
1845
|
}
|
|
1549
1846
|
);
|
|
1550
1847
|
}, [signer, handleFieldUpdate, setSelectedFieldId]);
|
|
@@ -1560,6 +1857,13 @@ function SignerView({
|
|
|
1560
1857
|
}));
|
|
1561
1858
|
}
|
|
1562
1859
|
}, [fields.filter((f) => f.type === "signature" && f.value).length]);
|
|
1860
|
+
useEffect3(() => {
|
|
1861
|
+
const hasFormulas = fields.some((f) => f.formula);
|
|
1862
|
+
if (!hasFormulas) return;
|
|
1863
|
+
const resolved = resolveAllFormulas(fields, transforms);
|
|
1864
|
+
const changed = resolved.some((f, i) => f.value !== fields[i].value);
|
|
1865
|
+
if (changed) setFields(resolved);
|
|
1866
|
+
}, [fields, transforms]);
|
|
1563
1867
|
return /* @__PURE__ */ jsxs6("div", { className: "signer-layout", ref: containerRef, children: [
|
|
1564
1868
|
loading && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Loading document..." }),
|
|
1565
1869
|
/* @__PURE__ */ jsxs6("div", { className: "signer-content", children: [
|
|
@@ -1601,14 +1905,26 @@ function SignerView({
|
|
|
1601
1905
|
initialValue: selectedField.value
|
|
1602
1906
|
}
|
|
1603
1907
|
),
|
|
1604
|
-
selectedField.type === "text" && /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1605
|
-
/* @__PURE__ */
|
|
1908
|
+
(selectedField.type === "text" || selectedField.type === "dropdown") && /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1909
|
+
selectedField.type === "dropdown" || selectedField.options && selectedField.options.length > 0 ? /* @__PURE__ */ jsxs6(
|
|
1910
|
+
"select",
|
|
1911
|
+
{
|
|
1912
|
+
value: selectedField.value,
|
|
1913
|
+
onChange: (e) => handleFieldUpdate(selectedField.id, e.target.value),
|
|
1914
|
+
className: "signer-text-input",
|
|
1915
|
+
children: [
|
|
1916
|
+
/* @__PURE__ */ jsx6("option", { value: "", children: selectedField.placeholder || "Select..." }),
|
|
1917
|
+
(selectedField.options || []).map((opt) => /* @__PURE__ */ jsx6("option", { value: opt, children: opt }, opt))
|
|
1918
|
+
]
|
|
1919
|
+
}
|
|
1920
|
+
) : /* @__PURE__ */ jsx6(
|
|
1606
1921
|
"input",
|
|
1607
1922
|
{
|
|
1608
1923
|
type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
|
|
1609
1924
|
value: selectedField.value,
|
|
1610
1925
|
onChange: (e) => handleFieldUpdate(selectedField.id, e.target.value),
|
|
1611
1926
|
placeholder: selectedField.placeholder,
|
|
1927
|
+
maxLength: selectedField.maxLength || void 0,
|
|
1612
1928
|
className: "signer-text-input"
|
|
1613
1929
|
}
|
|
1614
1930
|
),
|
|
@@ -1670,7 +1986,7 @@ function SignerView({
|
|
|
1670
1986
|
}
|
|
1671
1987
|
|
|
1672
1988
|
// src/components/pdf-builder/SignerRoleSelector.tsx
|
|
1673
|
-
import { useState as
|
|
1989
|
+
import { useState as useState6 } from "react";
|
|
1674
1990
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1675
1991
|
function SignerRoleSelector({
|
|
1676
1992
|
roles,
|
|
@@ -1679,8 +1995,8 @@ function SignerRoleSelector({
|
|
|
1679
1995
|
onAddRole,
|
|
1680
1996
|
onRemoveRole
|
|
1681
1997
|
}) {
|
|
1682
|
-
const [isAdding, setIsAdding] =
|
|
1683
|
-
const [newRoleName, setNewRoleName] =
|
|
1998
|
+
const [isAdding, setIsAdding] = useState6(false);
|
|
1999
|
+
const [newRoleName, setNewRoleName] = useState6("");
|
|
1684
2000
|
const handleAdd = () => {
|
|
1685
2001
|
if (newRoleName.trim()) {
|
|
1686
2002
|
onAddRole(newRoleName.trim());
|
|
@@ -1744,9 +2060,11 @@ function SignerRoleSelector({
|
|
|
1744
2060
|
] });
|
|
1745
2061
|
}
|
|
1746
2062
|
export {
|
|
2063
|
+
BUILTIN_TRANSFORMS,
|
|
1747
2064
|
DEFAULT_SIGNER_ROLES,
|
|
1748
2065
|
DesignerView,
|
|
1749
2066
|
FIELD_DEFAULTS,
|
|
2067
|
+
FONT_FAMILIES,
|
|
1750
2068
|
FieldNavigator,
|
|
1751
2069
|
FieldPropertyPanel,
|
|
1752
2070
|
PdfViewer,
|
|
@@ -1757,9 +2075,12 @@ export {
|
|
|
1757
2075
|
createField,
|
|
1758
2076
|
downloadPdf,
|
|
1759
2077
|
generateFilledPdf,
|
|
2078
|
+
generateId,
|
|
1760
2079
|
getSignerColor,
|
|
1761
2080
|
postPdfToCallback,
|
|
1762
2081
|
renderPdfPages,
|
|
2082
|
+
resolveAllFormulas,
|
|
2083
|
+
resolveFormula,
|
|
1763
2084
|
uniqueLabel
|
|
1764
2085
|
};
|
|
1765
2086
|
//# sourceMappingURL=index.mjs.map
|