@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/README.md +51 -0
- package/dist/index.css +91 -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 +438 -100
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +394 -61
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -30,9 +30,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/lib/index.ts
|
|
31
31
|
var lib_exports = {};
|
|
32
32
|
__export(lib_exports, {
|
|
33
|
+
BUILTIN_TRANSFORMS: () => BUILTIN_TRANSFORMS,
|
|
33
34
|
DEFAULT_SIGNER_ROLES: () => DEFAULT_SIGNER_ROLES,
|
|
34
35
|
DesignerView: () => DesignerView,
|
|
35
36
|
FIELD_DEFAULTS: () => FIELD_DEFAULTS,
|
|
37
|
+
FONT_FAMILIES: () => FONT_FAMILIES,
|
|
36
38
|
FieldNavigator: () => FieldNavigator,
|
|
37
39
|
FieldPropertyPanel: () => FieldPropertyPanel,
|
|
38
40
|
PdfViewer: () => PdfViewer,
|
|
@@ -43,15 +45,18 @@ __export(lib_exports, {
|
|
|
43
45
|
createField: () => createField,
|
|
44
46
|
downloadPdf: () => downloadPdf,
|
|
45
47
|
generateFilledPdf: () => generateFilledPdf,
|
|
48
|
+
generateId: () => generateId,
|
|
46
49
|
getSignerColor: () => getSignerColor,
|
|
47
50
|
postPdfToCallback: () => postPdfToCallback,
|
|
48
51
|
renderPdfPages: () => renderPdfPages,
|
|
52
|
+
resolveAllFormulas: () => resolveAllFormulas,
|
|
53
|
+
resolveFormula: () => resolveFormula,
|
|
49
54
|
uniqueLabel: () => uniqueLabel
|
|
50
55
|
});
|
|
51
56
|
module.exports = __toCommonJS(lib_exports);
|
|
52
57
|
|
|
53
58
|
// src/components/pdf-builder/DesignerView.tsx
|
|
54
|
-
var
|
|
59
|
+
var import_react4 = require("react");
|
|
55
60
|
var import_react_dom = require("react-dom");
|
|
56
61
|
|
|
57
62
|
// src/types/pdf-builder.ts
|
|
@@ -77,6 +82,11 @@ var SIGNER_ROLE_COLORS = {
|
|
|
77
82
|
function getSignerColor(role) {
|
|
78
83
|
return SIGNER_ROLE_COLORS[role] || "#888888";
|
|
79
84
|
}
|
|
85
|
+
var FONT_FAMILIES = [
|
|
86
|
+
{ value: "Helvetica", label: "Helvetica" },
|
|
87
|
+
{ value: "Courier", label: "Courier New" },
|
|
88
|
+
{ value: "TimesRoman", label: "Times New Roman" }
|
|
89
|
+
];
|
|
80
90
|
var FIELD_DEFAULTS = {
|
|
81
91
|
text: {
|
|
82
92
|
width: 20,
|
|
@@ -388,6 +398,7 @@ function FieldOverlayItem({
|
|
|
388
398
|
}
|
|
389
399
|
|
|
390
400
|
// src/components/pdf-builder/FieldPropertyPanel.tsx
|
|
401
|
+
var import_react2 = require("react");
|
|
391
402
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
392
403
|
var INK_COLORS = [
|
|
393
404
|
{ value: "#000000", label: "Black" },
|
|
@@ -396,8 +407,9 @@ var INK_COLORS = [
|
|
|
396
407
|
function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
397
408
|
const color = getSignerColor(field.assignee);
|
|
398
409
|
const isRedactField = field.type === "blackout" || field.type === "whiteout";
|
|
399
|
-
const
|
|
410
|
+
const isTextField = field.type === "text" || field.type === "signed-date";
|
|
400
411
|
const showInkColor = field.type === "text" || field.type === "signature" || field.type === "initials" || field.type === "signed-date";
|
|
412
|
+
const [newOption, setNewOption] = (0, import_react2.useState)("");
|
|
401
413
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "field-property-panel", children: [
|
|
402
414
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-header", children: [
|
|
403
415
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: { color }, children: field.label }),
|
|
@@ -470,6 +482,19 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
470
482
|
}
|
|
471
483
|
)
|
|
472
484
|
] }),
|
|
485
|
+
isTextField && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
|
|
486
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Formula" }),
|
|
487
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
488
|
+
"input",
|
|
489
|
+
{
|
|
490
|
+
type: "text",
|
|
491
|
+
value: field.formula || "",
|
|
492
|
+
onChange: (e) => onUpdate(field.id, { formula: e.target.value || void 0 }),
|
|
493
|
+
placeholder: "e.g. {{Date Field | month}}"
|
|
494
|
+
}
|
|
495
|
+
),
|
|
496
|
+
field.formula && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "panel-hint", children: "This field auto-computes from other fields. Signer cannot edit it." })
|
|
497
|
+
] }),
|
|
473
498
|
!isRedactField && field.type !== "checkbox" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "panel-field", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "panel-checkbox-label", children: [
|
|
474
499
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
475
500
|
"input",
|
|
@@ -481,7 +506,18 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
481
506
|
),
|
|
482
507
|
"Required"
|
|
483
508
|
] }) }),
|
|
484
|
-
|
|
509
|
+
isTextField && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
|
|
510
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Font" }),
|
|
511
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
512
|
+
"select",
|
|
513
|
+
{
|
|
514
|
+
value: field.fontFamily || "Helvetica",
|
|
515
|
+
onChange: (e) => onUpdate(field.id, { fontFamily: e.target.value }),
|
|
516
|
+
children: FONT_FAMILIES.map((f) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: f.value, children: f.label }, f.value))
|
|
517
|
+
}
|
|
518
|
+
)
|
|
519
|
+
] }),
|
|
520
|
+
isTextField && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
|
|
485
521
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Font Size (pt)" }),
|
|
486
522
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
487
523
|
"input",
|
|
@@ -494,6 +530,49 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
494
530
|
}
|
|
495
531
|
)
|
|
496
532
|
] }),
|
|
533
|
+
isTextField && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field-row", children: [
|
|
534
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field panel-field-half", children: [
|
|
535
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Letter Spacing" }),
|
|
536
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
537
|
+
"input",
|
|
538
|
+
{
|
|
539
|
+
type: "number",
|
|
540
|
+
min: "0",
|
|
541
|
+
max: "20",
|
|
542
|
+
step: "0.5",
|
|
543
|
+
value: field.letterSpacing || 0,
|
|
544
|
+
onChange: (e) => onUpdate(field.id, { letterSpacing: Number(e.target.value) })
|
|
545
|
+
}
|
|
546
|
+
)
|
|
547
|
+
] }),
|
|
548
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field panel-field-half", children: [
|
|
549
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Line Height" }),
|
|
550
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
551
|
+
"input",
|
|
552
|
+
{
|
|
553
|
+
type: "number",
|
|
554
|
+
min: "0.8",
|
|
555
|
+
max: "3",
|
|
556
|
+
step: "0.1",
|
|
557
|
+
value: field.lineHeight || 1.2,
|
|
558
|
+
onChange: (e) => onUpdate(field.id, { lineHeight: Number(e.target.value) })
|
|
559
|
+
}
|
|
560
|
+
)
|
|
561
|
+
] })
|
|
562
|
+
] }),
|
|
563
|
+
field.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
|
|
564
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Max Characters (0 = unlimited)" }),
|
|
565
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
566
|
+
"input",
|
|
567
|
+
{
|
|
568
|
+
type: "number",
|
|
569
|
+
min: "0",
|
|
570
|
+
max: "9999",
|
|
571
|
+
value: field.maxLength || 0,
|
|
572
|
+
onChange: (e) => onUpdate(field.id, { maxLength: Number(e.target.value) })
|
|
573
|
+
}
|
|
574
|
+
)
|
|
575
|
+
] }),
|
|
497
576
|
showInkColor && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
|
|
498
577
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Ink Color" }),
|
|
499
578
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ink-color-picker", children: INK_COLORS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
@@ -506,12 +585,61 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
506
585
|
},
|
|
507
586
|
c.value
|
|
508
587
|
)) })
|
|
588
|
+
] }),
|
|
589
|
+
field.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
|
|
590
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Predefined Options" }),
|
|
591
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-options-list", children: [
|
|
592
|
+
(field.options || []).map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-option-item", children: [
|
|
593
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: opt }),
|
|
594
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
595
|
+
"button",
|
|
596
|
+
{
|
|
597
|
+
className: "panel-option-remove",
|
|
598
|
+
onClick: () => {
|
|
599
|
+
const updated = (field.options || []).filter((_, j) => j !== i);
|
|
600
|
+
onUpdate(field.id, { options: updated.length > 0 ? updated : void 0 });
|
|
601
|
+
},
|
|
602
|
+
children: "\xD7"
|
|
603
|
+
}
|
|
604
|
+
)
|
|
605
|
+
] }, i)),
|
|
606
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-option-add", children: [
|
|
607
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
608
|
+
"input",
|
|
609
|
+
{
|
|
610
|
+
type: "text",
|
|
611
|
+
value: newOption,
|
|
612
|
+
onChange: (e) => setNewOption(e.target.value),
|
|
613
|
+
onKeyDown: (e) => {
|
|
614
|
+
if (e.key === "Enter" && newOption.trim()) {
|
|
615
|
+
onUpdate(field.id, { options: [...field.options || [], newOption.trim()] });
|
|
616
|
+
setNewOption("");
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
placeholder: "Add option..."
|
|
620
|
+
}
|
|
621
|
+
),
|
|
622
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
623
|
+
"button",
|
|
624
|
+
{
|
|
625
|
+
onClick: () => {
|
|
626
|
+
if (newOption.trim()) {
|
|
627
|
+
onUpdate(field.id, { options: [...field.options || [], newOption.trim()] });
|
|
628
|
+
setNewOption("");
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
children: "+"
|
|
632
|
+
}
|
|
633
|
+
)
|
|
634
|
+
] })
|
|
635
|
+
] }),
|
|
636
|
+
(field.options?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "panel-hint", children: "Signer will choose from these options via dropdown" })
|
|
509
637
|
] })
|
|
510
638
|
] });
|
|
511
639
|
}
|
|
512
640
|
|
|
513
641
|
// src/components/pdf-builder/SignatureCanvas.tsx
|
|
514
|
-
var
|
|
642
|
+
var import_react3 = require("react");
|
|
515
643
|
var import_perfect_freehand = require("perfect-freehand");
|
|
516
644
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
517
645
|
function getSvgPathFromStroke(stroke) {
|
|
@@ -535,11 +663,11 @@ function SignatureCanvas({
|
|
|
535
663
|
className,
|
|
536
664
|
inkColor = "#000000"
|
|
537
665
|
}) {
|
|
538
|
-
const canvasRef = (0,
|
|
539
|
-
const [paths, setPaths] = (0,
|
|
540
|
-
const [currentPath, setCurrentPath] = (0,
|
|
541
|
-
const [isEmpty, setIsEmpty] = (0,
|
|
542
|
-
(0,
|
|
666
|
+
const canvasRef = (0, import_react3.useRef)(null);
|
|
667
|
+
const [paths, setPaths] = (0, import_react3.useState)([]);
|
|
668
|
+
const [currentPath, setCurrentPath] = (0, import_react3.useState)(null);
|
|
669
|
+
const [isEmpty, setIsEmpty] = (0, import_react3.useState)(!initialValue);
|
|
670
|
+
(0, import_react3.useEffect)(() => {
|
|
543
671
|
if (initialValue && canvasRef.current) {
|
|
544
672
|
const ctx = canvasRef.current.getContext("2d");
|
|
545
673
|
const img = new Image();
|
|
@@ -550,7 +678,7 @@ function SignatureCanvas({
|
|
|
550
678
|
img.src = initialValue;
|
|
551
679
|
}
|
|
552
680
|
}, [initialValue, width, height]);
|
|
553
|
-
(0,
|
|
681
|
+
(0, import_react3.useEffect)(() => {
|
|
554
682
|
const canvas = canvasRef.current;
|
|
555
683
|
if (!canvas) return;
|
|
556
684
|
const ctx = canvas.getContext("2d");
|
|
@@ -569,7 +697,7 @@ function SignatureCanvas({
|
|
|
569
697
|
ctx.fill(path2d);
|
|
570
698
|
}
|
|
571
699
|
}, [paths, currentPath, width, height]);
|
|
572
|
-
const getPoint = (0,
|
|
700
|
+
const getPoint = (0, import_react3.useCallback)((e) => {
|
|
573
701
|
const canvas = canvasRef.current;
|
|
574
702
|
const rect = canvas.getBoundingClientRect();
|
|
575
703
|
const style = getComputedStyle(canvas);
|
|
@@ -587,17 +715,17 @@ function SignatureCanvas({
|
|
|
587
715
|
e.pressure
|
|
588
716
|
];
|
|
589
717
|
}, []);
|
|
590
|
-
const handlePointerDown = (0,
|
|
718
|
+
const handlePointerDown = (0, import_react3.useCallback)((e) => {
|
|
591
719
|
e.preventDefault();
|
|
592
720
|
e.target.setPointerCapture(e.pointerId);
|
|
593
721
|
setCurrentPath([getPoint(e)]);
|
|
594
722
|
}, [getPoint]);
|
|
595
|
-
const handlePointerMove = (0,
|
|
723
|
+
const handlePointerMove = (0, import_react3.useCallback)((e) => {
|
|
596
724
|
if (!currentPath) return;
|
|
597
725
|
e.preventDefault();
|
|
598
726
|
setCurrentPath([...currentPath, getPoint(e)]);
|
|
599
727
|
}, [currentPath, getPoint]);
|
|
600
|
-
const handlePointerUp = (0,
|
|
728
|
+
const handlePointerUp = (0, import_react3.useCallback)(() => {
|
|
601
729
|
if (!currentPath) return;
|
|
602
730
|
setPaths((prev) => [...prev, currentPath]);
|
|
603
731
|
setCurrentPath(null);
|
|
@@ -608,7 +736,7 @@ function SignatureCanvas({
|
|
|
608
736
|
}
|
|
609
737
|
});
|
|
610
738
|
}, [currentPath, onSign]);
|
|
611
|
-
const handleClear = (0,
|
|
739
|
+
const handleClear = (0, import_react3.useCallback)(() => {
|
|
612
740
|
setPaths([]);
|
|
613
741
|
setCurrentPath(null);
|
|
614
742
|
setIsEmpty(true);
|
|
@@ -679,24 +807,24 @@ function DesignerView({
|
|
|
679
807
|
] })
|
|
680
808
|
] }) });
|
|
681
809
|
}
|
|
682
|
-
const [pages, setPages] = (0,
|
|
683
|
-
const [fields, setFields] = (0,
|
|
684
|
-
const [selectedFieldId, setSelectedFieldId] = (0,
|
|
685
|
-
const [signerRoles, setSignerRoles] = (0,
|
|
686
|
-
const [activeRole, setActiveRole] = (0,
|
|
687
|
-
const [activeFieldType, setActiveFieldType] = (0,
|
|
688
|
-
const [loading, setLoading] = (0,
|
|
689
|
-
const [pdfSource, setPdfSource] = (0,
|
|
690
|
-
const [rightTab, setRightTab] = (0,
|
|
691
|
-
const [isAddingRole, setIsAddingRole] = (0,
|
|
692
|
-
const [newRoleName, setNewRoleName] = (0,
|
|
693
|
-
const [draggingFieldType, setDraggingFieldType] = (0,
|
|
694
|
-
const [panelWidth, setPanelWidth] = (0,
|
|
695
|
-
const [clipboardField, setClipboardField] = (0,
|
|
696
|
-
const dragGhostRef = (0,
|
|
697
|
-
const resizingRef = (0,
|
|
698
|
-
const lastStylesRef = (0,
|
|
699
|
-
(0,
|
|
810
|
+
const [pages, setPages] = (0, import_react4.useState)([]);
|
|
811
|
+
const [fields, setFields] = (0, import_react4.useState)(initialTemplate?.fields ?? []);
|
|
812
|
+
const [selectedFieldId, setSelectedFieldId] = (0, import_react4.useState)(null);
|
|
813
|
+
const [signerRoles, setSignerRoles] = (0, import_react4.useState)(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
|
|
814
|
+
const [activeRole, setActiveRole] = (0, import_react4.useState)("Sender");
|
|
815
|
+
const [activeFieldType, setActiveFieldType] = (0, import_react4.useState)("text");
|
|
816
|
+
const [loading, setLoading] = (0, import_react4.useState)(false);
|
|
817
|
+
const [pdfSource, setPdfSource] = (0, import_react4.useState)(null);
|
|
818
|
+
const [rightTab, setRightTab] = (0, import_react4.useState)("properties");
|
|
819
|
+
const [isAddingRole, setIsAddingRole] = (0, import_react4.useState)(false);
|
|
820
|
+
const [newRoleName, setNewRoleName] = (0, import_react4.useState)("");
|
|
821
|
+
const [draggingFieldType, setDraggingFieldType] = (0, import_react4.useState)(null);
|
|
822
|
+
const [panelWidth, setPanelWidth] = (0, import_react4.useState)(380);
|
|
823
|
+
const [clipboardField, setClipboardField] = (0, import_react4.useState)(null);
|
|
824
|
+
const dragGhostRef = (0, import_react4.useRef)(null);
|
|
825
|
+
const resizingRef = (0, import_react4.useRef)(false);
|
|
826
|
+
const lastStylesRef = (0, import_react4.useRef)({});
|
|
827
|
+
(0, import_react4.useEffect)(() => {
|
|
700
828
|
const pdfUrl = initialPdfUrl || initialTemplate?.pdfUrl;
|
|
701
829
|
if (pdfUrl) {
|
|
702
830
|
loadPdf(pdfUrl);
|
|
@@ -720,7 +848,7 @@ function DesignerView({
|
|
|
720
848
|
window.addEventListener("message", handleMessage);
|
|
721
849
|
return () => window.removeEventListener("message", handleMessage);
|
|
722
850
|
}, [initialPdfUrl, initialTemplate]);
|
|
723
|
-
const loadPdf = (0,
|
|
851
|
+
const loadPdf = (0, import_react4.useCallback)(async (source) => {
|
|
724
852
|
setLoading(true);
|
|
725
853
|
try {
|
|
726
854
|
const rendered = await renderPdfPages(source);
|
|
@@ -732,7 +860,7 @@ function DesignerView({
|
|
|
732
860
|
setLoading(false);
|
|
733
861
|
}
|
|
734
862
|
}, []);
|
|
735
|
-
const handleFileUpload = (0,
|
|
863
|
+
const handleFileUpload = (0, import_react4.useCallback)((e) => {
|
|
736
864
|
const file = e.target.files?.[0];
|
|
737
865
|
if (!file) return;
|
|
738
866
|
const reader = new FileReader();
|
|
@@ -741,7 +869,7 @@ function DesignerView({
|
|
|
741
869
|
};
|
|
742
870
|
reader.readAsArrayBuffer(file);
|
|
743
871
|
}, [loadPdf]);
|
|
744
|
-
const handlePageClick = (0,
|
|
872
|
+
const handlePageClick = (0, import_react4.useCallback)((page, x, y) => {
|
|
745
873
|
const field = createField(activeFieldType, activeRole, page, x, y, fields);
|
|
746
874
|
const sticky = lastStylesRef.current[activeFieldType];
|
|
747
875
|
const styledField = sticky ? { ...field, ...sticky } : field;
|
|
@@ -749,10 +877,10 @@ function DesignerView({
|
|
|
749
877
|
setSelectedFieldId(styledField.id);
|
|
750
878
|
setRightTab("properties");
|
|
751
879
|
}, [activeFieldType, activeRole, fields]);
|
|
752
|
-
const handleFieldMove = (0,
|
|
880
|
+
const handleFieldMove = (0, import_react4.useCallback)((id, page, x, y) => {
|
|
753
881
|
setFields((prev) => prev.map((f) => f.id === id ? { ...f, page, x, y } : f));
|
|
754
882
|
}, []);
|
|
755
|
-
const handleFieldResize = (0,
|
|
883
|
+
const handleFieldResize = (0, import_react4.useCallback)((id, width, height) => {
|
|
756
884
|
setFields((prev) => {
|
|
757
885
|
const field = prev.find((f) => f.id === id);
|
|
758
886
|
if (field) {
|
|
@@ -762,7 +890,7 @@ function DesignerView({
|
|
|
762
890
|
return prev.map((f) => f.id === id ? { ...f, width, height } : f);
|
|
763
891
|
});
|
|
764
892
|
}, []);
|
|
765
|
-
const handleFieldUpdate = (0,
|
|
893
|
+
const handleFieldUpdate = (0, import_react4.useCallback)((id, updates) => {
|
|
766
894
|
setFields((prev) => {
|
|
767
895
|
if (updates.label !== void 0) {
|
|
768
896
|
const otherLabels = prev.filter((f) => f.id !== id).map((f) => f.label);
|
|
@@ -782,11 +910,11 @@ function DesignerView({
|
|
|
782
910
|
return updated;
|
|
783
911
|
});
|
|
784
912
|
}, []);
|
|
785
|
-
const handleFieldDelete = (0,
|
|
913
|
+
const handleFieldDelete = (0, import_react4.useCallback)((id) => {
|
|
786
914
|
setFields((prev) => prev.filter((f) => f.id !== id));
|
|
787
915
|
if (selectedFieldId === id) setSelectedFieldId(null);
|
|
788
916
|
}, [selectedFieldId]);
|
|
789
|
-
const handleAddRole = (0,
|
|
917
|
+
const handleAddRole = (0, import_react4.useCallback)(() => {
|
|
790
918
|
const name = newRoleName.trim();
|
|
791
919
|
if (name && !signerRoles.includes(name)) {
|
|
792
920
|
setSignerRoles((prev) => [...prev, name]);
|
|
@@ -794,12 +922,12 @@ function DesignerView({
|
|
|
794
922
|
setNewRoleName("");
|
|
795
923
|
setIsAddingRole(false);
|
|
796
924
|
}, [newRoleName, signerRoles]);
|
|
797
|
-
const handleRemoveRole = (0,
|
|
925
|
+
const handleRemoveRole = (0, import_react4.useCallback)((role) => {
|
|
798
926
|
setSignerRoles((prev) => prev.filter((r) => r !== role));
|
|
799
927
|
setFields((prev) => prev.map((f) => f.assignee === role ? { ...f, assignee: signerRoles[0] } : f));
|
|
800
928
|
if (activeRole === role) setActiveRole(signerRoles[0]);
|
|
801
929
|
}, [signerRoles, activeRole]);
|
|
802
|
-
const handleExport = (0,
|
|
930
|
+
const handleExport = (0, import_react4.useCallback)(() => {
|
|
803
931
|
const template = {
|
|
804
932
|
fields,
|
|
805
933
|
signerRoles,
|
|
@@ -819,7 +947,7 @@ function DesignerView({
|
|
|
819
947
|
}
|
|
820
948
|
window.parent?.postMessage({ type: "template-saved", template }, "*");
|
|
821
949
|
}, [fields, signerRoles, pdfSource, onSave]);
|
|
822
|
-
const handlePaletteDragStart = (0,
|
|
950
|
+
const handlePaletteDragStart = (0, import_react4.useCallback)((e, type) => {
|
|
823
951
|
setDraggingFieldType(type);
|
|
824
952
|
e.dataTransfer.setData("application/exeq-field-type", type);
|
|
825
953
|
e.dataTransfer.effectAllowed = "copy";
|
|
@@ -836,14 +964,14 @@ function DesignerView({
|
|
|
836
964
|
e.dataTransfer.setDragImage(ghost, 40, 16);
|
|
837
965
|
dragGhostRef.current = ghost;
|
|
838
966
|
}, [activeRole]);
|
|
839
|
-
const handlePaletteDragEnd = (0,
|
|
967
|
+
const handlePaletteDragEnd = (0, import_react4.useCallback)(() => {
|
|
840
968
|
setDraggingFieldType(null);
|
|
841
969
|
if (dragGhostRef.current) {
|
|
842
970
|
document.body.removeChild(dragGhostRef.current);
|
|
843
971
|
dragGhostRef.current = null;
|
|
844
972
|
}
|
|
845
973
|
}, []);
|
|
846
|
-
const handleDropOnPage = (0,
|
|
974
|
+
const handleDropOnPage = (0, import_react4.useCallback)((page, x, y, fieldType) => {
|
|
847
975
|
const field = createField(fieldType, activeRole, page, x, y, fields);
|
|
848
976
|
const sticky = lastStylesRef.current[fieldType];
|
|
849
977
|
const styledField = sticky ? { ...field, ...sticky } : field;
|
|
@@ -851,7 +979,7 @@ function DesignerView({
|
|
|
851
979
|
setSelectedFieldId(styledField.id);
|
|
852
980
|
setRightTab("properties");
|
|
853
981
|
}, [activeRole, fields]);
|
|
854
|
-
(0,
|
|
982
|
+
(0, import_react4.useEffect)(() => {
|
|
855
983
|
const handleKeyDown = (e) => {
|
|
856
984
|
if (e.key !== "Delete" && e.key !== "Backspace") return;
|
|
857
985
|
if (!selectedFieldId) return;
|
|
@@ -866,7 +994,7 @@ function DesignerView({
|
|
|
866
994
|
window.addEventListener("keydown", handleKeyDown);
|
|
867
995
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
868
996
|
}, [selectedFieldId, handleFieldDelete]);
|
|
869
|
-
(0,
|
|
997
|
+
(0, import_react4.useEffect)(() => {
|
|
870
998
|
const handleKeyDown = (e) => {
|
|
871
999
|
const tag = (document.activeElement?.tagName || "").toLowerCase();
|
|
872
1000
|
if (tag === "input" || tag === "textarea" || tag === "select") return;
|
|
@@ -898,7 +1026,7 @@ function DesignerView({
|
|
|
898
1026
|
window.addEventListener("keydown", handleKeyDown);
|
|
899
1027
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
900
1028
|
}, [selectedFieldId, fields, clipboardField]);
|
|
901
|
-
const handleResizeStart = (0,
|
|
1029
|
+
const handleResizeStart = (0, import_react4.useCallback)((e) => {
|
|
902
1030
|
e.preventDefault();
|
|
903
1031
|
resizingRef.current = true;
|
|
904
1032
|
const startX = e.clientX;
|
|
@@ -923,7 +1051,7 @@ function DesignerView({
|
|
|
923
1051
|
return a.x - b.x;
|
|
924
1052
|
});
|
|
925
1053
|
const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
|
|
926
|
-
const renderFieldContent = (0,
|
|
1054
|
+
const renderFieldContent = (0, import_react4.useCallback)((field) => {
|
|
927
1055
|
if (field.type === "blackout" || field.type === "whiteout") {
|
|
928
1056
|
return null;
|
|
929
1057
|
}
|
|
@@ -1160,10 +1288,15 @@ function DesignerView({
|
|
|
1160
1288
|
}
|
|
1161
1289
|
|
|
1162
1290
|
// src/components/pdf-builder/SignerView.tsx
|
|
1163
|
-
var
|
|
1291
|
+
var import_react6 = require("react");
|
|
1164
1292
|
|
|
1165
1293
|
// src/utils/pdfFiller.ts
|
|
1166
1294
|
var import_pdf_lib = require("pdf-lib");
|
|
1295
|
+
var FONT_MAP = {
|
|
1296
|
+
"Helvetica": import_pdf_lib.StandardFonts.Helvetica,
|
|
1297
|
+
"Courier": import_pdf_lib.StandardFonts.Courier,
|
|
1298
|
+
"TimesRoman": import_pdf_lib.StandardFonts.TimesRoman
|
|
1299
|
+
};
|
|
1167
1300
|
async function generateFilledPdf(pdfSource, fields) {
|
|
1168
1301
|
let pdfBytes;
|
|
1169
1302
|
if (typeof pdfSource === "string") {
|
|
@@ -1173,7 +1306,12 @@ async function generateFilledPdf(pdfSource, fields) {
|
|
|
1173
1306
|
pdfBytes = pdfSource;
|
|
1174
1307
|
}
|
|
1175
1308
|
const pdfDoc = await import_pdf_lib.PDFDocument.load(pdfBytes);
|
|
1176
|
-
const
|
|
1309
|
+
const fontCache = /* @__PURE__ */ new Map();
|
|
1310
|
+
const usedFontKeys = new Set(fields.map((f) => f.fontFamily || "Helvetica"));
|
|
1311
|
+
for (const key of usedFontKeys) {
|
|
1312
|
+
const stdFont = FONT_MAP[key] || import_pdf_lib.StandardFonts.Helvetica;
|
|
1313
|
+
fontCache.set(key, await pdfDoc.embedFont(stdFont));
|
|
1314
|
+
}
|
|
1177
1315
|
const pages = pdfDoc.getPages();
|
|
1178
1316
|
function hexToRgb(hex) {
|
|
1179
1317
|
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
@@ -1234,15 +1372,35 @@ async function generateFilledPdf(pdfSource, fields) {
|
|
|
1234
1372
|
});
|
|
1235
1373
|
}
|
|
1236
1374
|
} else {
|
|
1237
|
-
const
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1375
|
+
const font = fontCache.get(field.fontFamily || "Helvetica");
|
|
1376
|
+
const spacing = field.letterSpacing || 0;
|
|
1377
|
+
const textWidthAtSize = (text, size) => {
|
|
1378
|
+
const baseWidth = font.widthOfTextAtSize(text, size);
|
|
1379
|
+
return baseWidth + spacing * (text.length - 1);
|
|
1380
|
+
};
|
|
1381
|
+
const maxFontSize = Math.min(field.fontSize, h * 0.7);
|
|
1382
|
+
let fontSize = maxFontSize;
|
|
1383
|
+
const padding = 4;
|
|
1384
|
+
while (fontSize > 4) {
|
|
1385
|
+
if (textWidthAtSize(field.value, fontSize) <= w - padding) break;
|
|
1386
|
+
fontSize -= 0.5;
|
|
1387
|
+
}
|
|
1388
|
+
if (spacing > 0) {
|
|
1389
|
+
let cx = x + 2;
|
|
1390
|
+
const cy = y + h * 0.3;
|
|
1391
|
+
for (const char of field.value) {
|
|
1392
|
+
page.drawText(char, { x: cx, y: cy, size: fontSize, font, color: inkColor });
|
|
1393
|
+
cx += font.widthOfTextAtSize(char, fontSize) + spacing;
|
|
1394
|
+
}
|
|
1395
|
+
} else {
|
|
1396
|
+
page.drawText(field.value, {
|
|
1397
|
+
x: x + 2,
|
|
1398
|
+
y: y + h * 0.3,
|
|
1399
|
+
size: fontSize,
|
|
1400
|
+
font,
|
|
1401
|
+
color: inkColor
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1246
1404
|
}
|
|
1247
1405
|
}
|
|
1248
1406
|
return pdfDoc.save();
|
|
@@ -1267,7 +1425,13 @@ async function postPdfToCallback(bytes, callbackUrl, filename) {
|
|
|
1267
1425
|
}
|
|
1268
1426
|
|
|
1269
1427
|
// src/components/pdf-builder/FieldNavigator.tsx
|
|
1428
|
+
var import_react5 = require("react");
|
|
1270
1429
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1430
|
+
function isFieldFilled(f) {
|
|
1431
|
+
if (!f.required) return true;
|
|
1432
|
+
if (f.type === "checkbox") return true;
|
|
1433
|
+
return !!f.value;
|
|
1434
|
+
}
|
|
1271
1435
|
function FieldNavigator({
|
|
1272
1436
|
fields,
|
|
1273
1437
|
currentFieldId,
|
|
@@ -1276,6 +1440,7 @@ function FieldNavigator({
|
|
|
1276
1440
|
onComplete,
|
|
1277
1441
|
completeLabel = "Complete"
|
|
1278
1442
|
}) {
|
|
1443
|
+
const [showIncomplete, setShowIncomplete] = (0, import_react5.useState)(false);
|
|
1279
1444
|
const currentIndex = fields.findIndex((f) => f.id === currentFieldId);
|
|
1280
1445
|
const hasPrev = currentIndex > 0;
|
|
1281
1446
|
const hasNext = currentIndex < fields.length - 1;
|
|
@@ -1289,19 +1454,36 @@ function FieldNavigator({
|
|
|
1289
1454
|
onNavigate(fields[currentIndex + 1].id);
|
|
1290
1455
|
}
|
|
1291
1456
|
};
|
|
1292
|
-
const filledCount = fields.filter(
|
|
1293
|
-
|
|
1294
|
-
if (f.type === "checkbox") return true;
|
|
1295
|
-
return !!f.value;
|
|
1296
|
-
}).length;
|
|
1457
|
+
const filledCount = fields.filter(isFieldFilled).length;
|
|
1458
|
+
const incompleteFields = fields.filter((f) => !isFieldFilled(f));
|
|
1297
1459
|
const showCompleteAsNext = !hasNext && allRequiredFilled;
|
|
1298
1460
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "field-navigator", children: [
|
|
1299
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1461
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1462
|
+
"div",
|
|
1463
|
+
{
|
|
1464
|
+
className: `field-navigator-progress ${incompleteFields.length > 0 ? "clickable" : ""}`,
|
|
1465
|
+
onClick: () => incompleteFields.length > 0 && setShowIncomplete(!showIncomplete),
|
|
1466
|
+
children: [
|
|
1467
|
+
filledCount,
|
|
1468
|
+
" of ",
|
|
1469
|
+
fields.length,
|
|
1470
|
+
" fields completed",
|
|
1471
|
+
incompleteFields.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "field-navigator-toggle", children: showIncomplete ? "\u25B2" : "\u25BC" })
|
|
1472
|
+
]
|
|
1473
|
+
}
|
|
1474
|
+
),
|
|
1475
|
+
showIncomplete && incompleteFields.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "incomplete-fields-list", children: incompleteFields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1476
|
+
"button",
|
|
1477
|
+
{
|
|
1478
|
+
className: "incomplete-field-item",
|
|
1479
|
+
onClick: () => {
|
|
1480
|
+
onNavigate(f.id);
|
|
1481
|
+
setShowIncomplete(false);
|
|
1482
|
+
},
|
|
1483
|
+
children: f.label
|
|
1484
|
+
},
|
|
1485
|
+
f.id
|
|
1486
|
+
)) }),
|
|
1305
1487
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "field-navigator-controls", children: [
|
|
1306
1488
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1307
1489
|
"button",
|
|
@@ -1333,6 +1515,95 @@ function FieldNavigator({
|
|
|
1333
1515
|
] });
|
|
1334
1516
|
}
|
|
1335
1517
|
|
|
1518
|
+
// src/utils/formulaResolver.ts
|
|
1519
|
+
var BUILTIN_TRANSFORMS = {
|
|
1520
|
+
// Date transforms (expects a parseable date string)
|
|
1521
|
+
month: (v) => {
|
|
1522
|
+
const d = new Date(v);
|
|
1523
|
+
return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1);
|
|
1524
|
+
},
|
|
1525
|
+
month2: (v) => {
|
|
1526
|
+
const d = new Date(v);
|
|
1527
|
+
return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1).padStart(2, "0");
|
|
1528
|
+
},
|
|
1529
|
+
monthname: (v) => {
|
|
1530
|
+
const d = new Date(v);
|
|
1531
|
+
return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "long" });
|
|
1532
|
+
},
|
|
1533
|
+
monthshort: (v) => {
|
|
1534
|
+
const d = new Date(v);
|
|
1535
|
+
return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "short" });
|
|
1536
|
+
},
|
|
1537
|
+
day: (v) => {
|
|
1538
|
+
const d = new Date(v);
|
|
1539
|
+
return isNaN(d.getTime()) ? "" : String(d.getDate());
|
|
1540
|
+
},
|
|
1541
|
+
day2: (v) => {
|
|
1542
|
+
const d = new Date(v);
|
|
1543
|
+
return isNaN(d.getTime()) ? "" : String(d.getDate()).padStart(2, "0");
|
|
1544
|
+
},
|
|
1545
|
+
year: (v) => {
|
|
1546
|
+
const d = new Date(v);
|
|
1547
|
+
return isNaN(d.getTime()) ? "" : String(d.getFullYear());
|
|
1548
|
+
},
|
|
1549
|
+
year2: (v) => {
|
|
1550
|
+
const d = new Date(v);
|
|
1551
|
+
return isNaN(d.getTime()) ? "" : String(d.getFullYear()).slice(-2);
|
|
1552
|
+
},
|
|
1553
|
+
// String transforms
|
|
1554
|
+
upper: (v) => v.toUpperCase(),
|
|
1555
|
+
lower: (v) => v.toLowerCase(),
|
|
1556
|
+
trim: (v) => v.trim(),
|
|
1557
|
+
first: (v) => v.split(/\s+/)[0] || "",
|
|
1558
|
+
last: (v) => {
|
|
1559
|
+
const parts = v.split(/\s+/);
|
|
1560
|
+
return parts[parts.length - 1] || "";
|
|
1561
|
+
},
|
|
1562
|
+
initials: (v) => v.split(/\s+/).map((w) => w[0] || "").join("").toUpperCase(),
|
|
1563
|
+
// Numeric / substring
|
|
1564
|
+
last4: (v) => v.slice(-4),
|
|
1565
|
+
last2: (v) => v.slice(-2),
|
|
1566
|
+
first4: (v) => v.slice(0, 4),
|
|
1567
|
+
first2: (v) => v.slice(0, 2),
|
|
1568
|
+
digits: (v) => v.replace(/\D/g, ""),
|
|
1569
|
+
number: (v) => {
|
|
1570
|
+
const n = parseFloat(v);
|
|
1571
|
+
return isNaN(n) ? "" : String(n);
|
|
1572
|
+
},
|
|
1573
|
+
currency: (v) => {
|
|
1574
|
+
const n = parseFloat(v.replace(/[^0-9.-]/g, ""));
|
|
1575
|
+
return isNaN(n) ? "" : `$${n.toFixed(2)}`;
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
var FORMULA_RE = /\{\{(.+?)\}\}/g;
|
|
1579
|
+
var PIPE_RE = /^(.+?)\s*\|\s*(.+)$/;
|
|
1580
|
+
function resolveFormula(formula, fields, customTransforms) {
|
|
1581
|
+
const transforms = { ...BUILTIN_TRANSFORMS, ...customTransforms };
|
|
1582
|
+
return formula.replace(FORMULA_RE, (_match, expr) => {
|
|
1583
|
+
const pipeMatch = expr.match(PIPE_RE);
|
|
1584
|
+
const sourceLabel = (pipeMatch ? pipeMatch[1] : expr).trim();
|
|
1585
|
+
const transformName = pipeMatch ? pipeMatch[2].trim() : null;
|
|
1586
|
+
const sourceField = fields.find((f) => f.label.toLowerCase() === sourceLabel.toLowerCase()) || fields.find((f) => f.id === sourceLabel);
|
|
1587
|
+
if (!sourceField) return "";
|
|
1588
|
+
const rawValue = sourceField.value || "";
|
|
1589
|
+
if (!transformName) return rawValue;
|
|
1590
|
+
const fn = transforms[transformName];
|
|
1591
|
+
if (!fn) return rawValue;
|
|
1592
|
+
try {
|
|
1593
|
+
return fn(rawValue);
|
|
1594
|
+
} catch {
|
|
1595
|
+
return rawValue;
|
|
1596
|
+
}
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
function resolveAllFormulas(fields, customTransforms) {
|
|
1600
|
+
return fields.map((f) => {
|
|
1601
|
+
if (!f.formula) return f;
|
|
1602
|
+
const computed = resolveFormula(f.formula, fields, customTransforms);
|
|
1603
|
+
return computed !== f.value ? { ...f, value: computed } : f;
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1336
1607
|
// src/components/pdf-builder/SignerView.tsx
|
|
1337
1608
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1338
1609
|
function SignerView({
|
|
@@ -1344,7 +1615,8 @@ function SignerView({
|
|
|
1344
1615
|
onComplete,
|
|
1345
1616
|
initialValues,
|
|
1346
1617
|
submitLabel,
|
|
1347
|
-
signerOrder: signerOrderProp
|
|
1618
|
+
signerOrder: signerOrderProp,
|
|
1619
|
+
transforms
|
|
1348
1620
|
} = {}) {
|
|
1349
1621
|
if (!isValidApiKey(apiKey)) {
|
|
1350
1622
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "signer-layout", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "empty-state", children: [
|
|
@@ -1356,28 +1628,43 @@ function SignerView({
|
|
|
1356
1628
|
] })
|
|
1357
1629
|
] }) });
|
|
1358
1630
|
}
|
|
1359
|
-
const [pages, setPages] = (0,
|
|
1360
|
-
const [fields, setFields] = (0,
|
|
1361
|
-
const [selectedFieldId, setSelectedFieldId] = (0,
|
|
1362
|
-
const [signerRoles, setSignerRoles] = (0,
|
|
1363
|
-
const [currentSignerIndex, setCurrentSignerIndex] = (0,
|
|
1631
|
+
const [pages, setPages] = (0, import_react6.useState)([]);
|
|
1632
|
+
const [fields, setFields] = (0, import_react6.useState)([]);
|
|
1633
|
+
const [selectedFieldId, setSelectedFieldId] = (0, import_react6.useState)(null);
|
|
1634
|
+
const [signerRoles, setSignerRoles] = (0, import_react6.useState)([]);
|
|
1635
|
+
const [currentSignerIndex, setCurrentSignerIndex] = (0, import_react6.useState)(() => {
|
|
1364
1636
|
if (initialSigner && signerOrderProp) {
|
|
1365
1637
|
const idx = signerOrderProp.indexOf(initialSigner);
|
|
1366
1638
|
return idx >= 0 ? idx : 0;
|
|
1367
1639
|
}
|
|
1368
1640
|
return 0;
|
|
1369
1641
|
});
|
|
1370
|
-
const initializedRef = (0,
|
|
1371
|
-
const [loading, setLoading] = (0,
|
|
1372
|
-
const [submitting, setSubmitting] = (0,
|
|
1373
|
-
const [pdfSource, setPdfSource] = (0,
|
|
1374
|
-
const [callbackUrl, setCallbackUrl] = (0,
|
|
1375
|
-
const containerRef = (0,
|
|
1642
|
+
const initializedRef = (0, import_react6.useRef)(false);
|
|
1643
|
+
const [loading, setLoading] = (0, import_react6.useState)(false);
|
|
1644
|
+
const [submitting, setSubmitting] = (0, import_react6.useState)(false);
|
|
1645
|
+
const [pdfSource, setPdfSource] = (0, import_react6.useState)(null);
|
|
1646
|
+
const [callbackUrl, setCallbackUrl] = (0, import_react6.useState)(initialCallbackUrl || "");
|
|
1647
|
+
const containerRef = (0, import_react6.useRef)(null);
|
|
1376
1648
|
const signerOrder = signerOrderProp || (signerRoles.length > 0 ? [...signerRoles.filter((r) => r !== "Sender"), ...signerRoles.filter((r) => r === "Sender")] : [initialSigner || "Signer 1"]);
|
|
1377
1649
|
const signer = signerOrder[currentSignerIndex] || signerOrder[0] || "Signer 1";
|
|
1378
1650
|
const isLastSigner = currentSignerIndex >= signerOrder.length - 1;
|
|
1379
1651
|
const isMultiSigner = signerOrder.length > 1;
|
|
1380
|
-
(0,
|
|
1652
|
+
(0, import_react6.useEffect)(() => {
|
|
1653
|
+
if (fields.length === 0 || !isMultiSigner) return;
|
|
1654
|
+
const signerFields = fields.filter(
|
|
1655
|
+
(f) => f.assignee === signer && f.type !== "blackout" && f.type !== "whiteout"
|
|
1656
|
+
);
|
|
1657
|
+
const allFilled = signerFields.length > 0 && signerFields.every((f) => {
|
|
1658
|
+
if (!f.required) return true;
|
|
1659
|
+
if (f.type === "checkbox") return true;
|
|
1660
|
+
return !!f.value;
|
|
1661
|
+
});
|
|
1662
|
+
if (allFilled && !isLastSigner) {
|
|
1663
|
+
setCurrentSignerIndex((prev) => prev + 1);
|
|
1664
|
+
setSelectedFieldId(null);
|
|
1665
|
+
}
|
|
1666
|
+
}, [currentSignerIndex, fields, signer, isLastSigner, isMultiSigner]);
|
|
1667
|
+
(0, import_react6.useEffect)(() => {
|
|
1381
1668
|
if (initialTemplate) {
|
|
1382
1669
|
let templateFields = initialTemplate.fields;
|
|
1383
1670
|
if (initialTemplate.signerRoles) setSignerRoles(initialTemplate.signerRoles);
|
|
@@ -1447,7 +1734,7 @@ function SignerView({
|
|
|
1447
1734
|
window.addEventListener("message", handleMessage);
|
|
1448
1735
|
return () => window.removeEventListener("message", handleMessage);
|
|
1449
1736
|
}, [initialTemplate, initialPdfUrl]);
|
|
1450
|
-
const loadPdf = (0,
|
|
1737
|
+
const loadPdf = (0, import_react6.useCallback)(async (source) => {
|
|
1451
1738
|
setLoading(true);
|
|
1452
1739
|
try {
|
|
1453
1740
|
const rendered = await renderPdfPages(source);
|
|
@@ -1459,21 +1746,21 @@ function SignerView({
|
|
|
1459
1746
|
setLoading(false);
|
|
1460
1747
|
}
|
|
1461
1748
|
}, []);
|
|
1462
|
-
const editableFields = fields.filter((f) => f.assignee === signer && f.type !== "blackout" && f.type !== "whiteout").sort((a, b) => {
|
|
1749
|
+
const editableFields = fields.filter((f) => f.assignee === signer && f.type !== "blackout" && f.type !== "whiteout" && !f.formula).sort((a, b) => {
|
|
1463
1750
|
if (a.page !== b.page) return a.page - b.page;
|
|
1464
1751
|
const bandThreshold = 2;
|
|
1465
1752
|
if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
|
|
1466
1753
|
return a.x - b.x;
|
|
1467
1754
|
});
|
|
1468
1755
|
const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
|
|
1469
|
-
const isFieldEditable = selectedField ? selectedField.assignee === signer && selectedField.type !== "blackout" && selectedField.type !== "whiteout" : false;
|
|
1470
|
-
const handleFieldUpdate = (0,
|
|
1756
|
+
const isFieldEditable = selectedField ? selectedField.assignee === signer && selectedField.type !== "blackout" && selectedField.type !== "whiteout" && !selectedField.formula : false;
|
|
1757
|
+
const handleFieldUpdate = (0, import_react6.useCallback)((id, value) => {
|
|
1471
1758
|
setFields((prev) => prev.map((f) => f.id === id ? { ...f, value } : f));
|
|
1472
1759
|
}, []);
|
|
1473
|
-
const handleFieldPropertyUpdate = (0,
|
|
1760
|
+
const handleFieldPropertyUpdate = (0, import_react6.useCallback)((id, updates) => {
|
|
1474
1761
|
setFields((prev) => prev.map((f) => f.id === id ? { ...f, ...updates } : f));
|
|
1475
1762
|
}, []);
|
|
1476
|
-
const handleNavigate = (0,
|
|
1763
|
+
const handleNavigate = (0, import_react6.useCallback)((fieldId) => {
|
|
1477
1764
|
setSelectedFieldId(fieldId);
|
|
1478
1765
|
const field = fields.find((f) => f.id === fieldId);
|
|
1479
1766
|
if (field && containerRef.current) {
|
|
@@ -1488,7 +1775,7 @@ function SignerView({
|
|
|
1488
1775
|
if (f.type === "checkbox") return true;
|
|
1489
1776
|
return !!f.value;
|
|
1490
1777
|
});
|
|
1491
|
-
const handleAdvanceOrSubmit = (0,
|
|
1778
|
+
const handleAdvanceOrSubmit = (0, import_react6.useCallback)(async () => {
|
|
1492
1779
|
if (!allRequiredFilled) return;
|
|
1493
1780
|
if (!isLastSigner) {
|
|
1494
1781
|
setCurrentSignerIndex((prev) => prev + 1);
|
|
@@ -1502,7 +1789,8 @@ function SignerView({
|
|
|
1502
1789
|
if (!pdfSource) return;
|
|
1503
1790
|
setSubmitting(true);
|
|
1504
1791
|
try {
|
|
1505
|
-
const
|
|
1792
|
+
const finalFields = resolveAllFormulas(fields, transforms);
|
|
1793
|
+
const pdfBytes = await generateFilledPdf(pdfSource, finalFields);
|
|
1506
1794
|
const blob = new Blob([pdfBytes.slice().buffer], { type: "application/pdf" });
|
|
1507
1795
|
if (callbackUrl) {
|
|
1508
1796
|
await postPdfToCallback(pdfBytes, callbackUrl, "signed-document.pdf");
|
|
@@ -1520,10 +1808,13 @@ function SignerView({
|
|
|
1520
1808
|
setSubmitting(false);
|
|
1521
1809
|
}
|
|
1522
1810
|
}, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete, isLastSigner]);
|
|
1523
|
-
const renderFieldContent = (0,
|
|
1811
|
+
const renderFieldContent = (0, import_react6.useCallback)((field) => {
|
|
1524
1812
|
if (field.type === "blackout" || field.type === "whiteout") {
|
|
1525
1813
|
return null;
|
|
1526
1814
|
}
|
|
1815
|
+
if (field.formula) {
|
|
1816
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-overlay-value formula", children: field.value || "..." });
|
|
1817
|
+
}
|
|
1527
1818
|
const editable = field.assignee === signer;
|
|
1528
1819
|
if (!editable) {
|
|
1529
1820
|
if (field.type === "signature" || field.type === "initials") {
|
|
@@ -1556,6 +1847,28 @@ function SignerView({
|
|
|
1556
1847
|
if (field.type === "signed-date") {
|
|
1557
1848
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
|
|
1558
1849
|
}
|
|
1850
|
+
const fontStyle = {
|
|
1851
|
+
fontSize: `${field.fontSize * 0.6}px`,
|
|
1852
|
+
letterSpacing: field.letterSpacing ? `${field.letterSpacing * 0.6}px` : void 0,
|
|
1853
|
+
fontFamily: field.fontFamily === "Courier" ? "monospace" : field.fontFamily === "TimesRoman" ? "serif" : void 0
|
|
1854
|
+
};
|
|
1855
|
+
if (field.options && field.options.length > 0) {
|
|
1856
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1857
|
+
"select",
|
|
1858
|
+
{
|
|
1859
|
+
className: "field-inline-input",
|
|
1860
|
+
value: field.value,
|
|
1861
|
+
onChange: (e) => handleFieldUpdate(field.id, e.target.value),
|
|
1862
|
+
onFocus: () => setSelectedFieldId(field.id),
|
|
1863
|
+
onClick: (e) => e.stopPropagation(),
|
|
1864
|
+
style: fontStyle,
|
|
1865
|
+
children: [
|
|
1866
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: field.placeholder || "Select..." }),
|
|
1867
|
+
field.options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: opt, children: opt }, opt))
|
|
1868
|
+
]
|
|
1869
|
+
}
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1559
1872
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1560
1873
|
"input",
|
|
1561
1874
|
{
|
|
@@ -1563,14 +1876,15 @@ function SignerView({
|
|
|
1563
1876
|
className: "field-inline-input",
|
|
1564
1877
|
value: field.value,
|
|
1565
1878
|
placeholder: field.placeholder,
|
|
1879
|
+
maxLength: field.maxLength || void 0,
|
|
1566
1880
|
onChange: (e) => handleFieldUpdate(field.id, e.target.value),
|
|
1567
1881
|
onFocus: () => setSelectedFieldId(field.id),
|
|
1568
1882
|
onClick: (e) => e.stopPropagation(),
|
|
1569
|
-
style:
|
|
1883
|
+
style: fontStyle
|
|
1570
1884
|
}
|
|
1571
1885
|
);
|
|
1572
1886
|
}, [signer, handleFieldUpdate, setSelectedFieldId]);
|
|
1573
|
-
(0,
|
|
1887
|
+
(0, import_react6.useEffect)(() => {
|
|
1574
1888
|
const sigFields = fields.filter((f) => f.assignee === signer && f.type === "signature" && f.value);
|
|
1575
1889
|
if (sigFields.length > 0) {
|
|
1576
1890
|
const dateStr = (/* @__PURE__ */ new Date()).toLocaleDateString();
|
|
@@ -1582,6 +1896,13 @@ function SignerView({
|
|
|
1582
1896
|
}));
|
|
1583
1897
|
}
|
|
1584
1898
|
}, [fields.filter((f) => f.type === "signature" && f.value).length]);
|
|
1899
|
+
(0, import_react6.useEffect)(() => {
|
|
1900
|
+
const hasFormulas = fields.some((f) => f.formula);
|
|
1901
|
+
if (!hasFormulas) return;
|
|
1902
|
+
const resolved = resolveAllFormulas(fields, transforms);
|
|
1903
|
+
const changed = resolved.some((f, i) => f.value !== fields[i].value);
|
|
1904
|
+
if (changed) setFields(resolved);
|
|
1905
|
+
}, [fields, transforms]);
|
|
1585
1906
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "signer-layout", ref: containerRef, children: [
|
|
1586
1907
|
loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "loading-indicator", children: "Loading document..." }),
|
|
1587
1908
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "signer-content", children: [
|
|
@@ -1624,13 +1945,25 @@ function SignerView({
|
|
|
1624
1945
|
}
|
|
1625
1946
|
),
|
|
1626
1947
|
selectedField.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1627
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
1948
|
+
selectedField.options && selectedField.options.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1949
|
+
"select",
|
|
1950
|
+
{
|
|
1951
|
+
value: selectedField.value,
|
|
1952
|
+
onChange: (e) => handleFieldUpdate(selectedField.id, e.target.value),
|
|
1953
|
+
className: "signer-text-input",
|
|
1954
|
+
children: [
|
|
1955
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: selectedField.placeholder || "Select..." }),
|
|
1956
|
+
selectedField.options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: opt, children: opt }, opt))
|
|
1957
|
+
]
|
|
1958
|
+
}
|
|
1959
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1628
1960
|
"input",
|
|
1629
1961
|
{
|
|
1630
1962
|
type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
|
|
1631
1963
|
value: selectedField.value,
|
|
1632
1964
|
onChange: (e) => handleFieldUpdate(selectedField.id, e.target.value),
|
|
1633
1965
|
placeholder: selectedField.placeholder,
|
|
1966
|
+
maxLength: selectedField.maxLength || void 0,
|
|
1634
1967
|
className: "signer-text-input"
|
|
1635
1968
|
}
|
|
1636
1969
|
),
|
|
@@ -1692,7 +2025,7 @@ function SignerView({
|
|
|
1692
2025
|
}
|
|
1693
2026
|
|
|
1694
2027
|
// src/components/pdf-builder/SignerRoleSelector.tsx
|
|
1695
|
-
var
|
|
2028
|
+
var import_react7 = require("react");
|
|
1696
2029
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1697
2030
|
function SignerRoleSelector({
|
|
1698
2031
|
roles,
|
|
@@ -1701,8 +2034,8 @@ function SignerRoleSelector({
|
|
|
1701
2034
|
onAddRole,
|
|
1702
2035
|
onRemoveRole
|
|
1703
2036
|
}) {
|
|
1704
|
-
const [isAdding, setIsAdding] = (0,
|
|
1705
|
-
const [newRoleName, setNewRoleName] = (0,
|
|
2037
|
+
const [isAdding, setIsAdding] = (0, import_react7.useState)(false);
|
|
2038
|
+
const [newRoleName, setNewRoleName] = (0, import_react7.useState)("");
|
|
1706
2039
|
const handleAdd = () => {
|
|
1707
2040
|
if (newRoleName.trim()) {
|
|
1708
2041
|
onAddRole(newRoleName.trim());
|
|
@@ -1767,9 +2100,11 @@ function SignerRoleSelector({
|
|
|
1767
2100
|
}
|
|
1768
2101
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1769
2102
|
0 && (module.exports = {
|
|
2103
|
+
BUILTIN_TRANSFORMS,
|
|
1770
2104
|
DEFAULT_SIGNER_ROLES,
|
|
1771
2105
|
DesignerView,
|
|
1772
2106
|
FIELD_DEFAULTS,
|
|
2107
|
+
FONT_FAMILIES,
|
|
1773
2108
|
FieldNavigator,
|
|
1774
2109
|
FieldPropertyPanel,
|
|
1775
2110
|
PdfViewer,
|
|
@@ -1780,9 +2115,12 @@ function SignerRoleSelector({
|
|
|
1780
2115
|
createField,
|
|
1781
2116
|
downloadPdf,
|
|
1782
2117
|
generateFilledPdf,
|
|
2118
|
+
generateId,
|
|
1783
2119
|
getSignerColor,
|
|
1784
2120
|
postPdfToCallback,
|
|
1785
2121
|
renderPdfPages,
|
|
2122
|
+
resolveAllFormulas,
|
|
2123
|
+
resolveFormula,
|
|
1786
2124
|
uniqueLabel
|
|
1787
2125
|
});
|
|
1788
2126
|
//# sourceMappingURL=index.js.map
|