@unlev/exeq 0.1.1 → 0.1.3
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 +82 -81
- package/dist/index.css +289 -5
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +31 -8
- package/dist/index.d.ts +31 -8
- package/dist/index.js +651 -351
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +623 -324
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -4
- package/dist/embed.css +0 -1
- package/dist/embed.global.js +0 -419
- package/dist/styles.css +0 -566
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/components/pdf-builder/DesignerView.tsx
|
|
2
|
-
import { useState as
|
|
2
|
+
import { useState as useState2, useCallback as useCallback3, useEffect as useEffect2, useRef as useRef3 } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
3
4
|
|
|
4
5
|
// src/types/pdf-builder.ts
|
|
5
6
|
function generateId() {
|
|
@@ -55,14 +56,44 @@ var FIELD_DEFAULTS = {
|
|
|
55
56
|
height: 5,
|
|
56
57
|
fontSize: 12,
|
|
57
58
|
placeholder: "Initials"
|
|
59
|
+
},
|
|
60
|
+
blackout: {
|
|
61
|
+
width: 20,
|
|
62
|
+
height: 3,
|
|
63
|
+
fontSize: 12,
|
|
64
|
+
placeholder: ""
|
|
65
|
+
},
|
|
66
|
+
whiteout: {
|
|
67
|
+
width: 20,
|
|
68
|
+
height: 3,
|
|
69
|
+
fontSize: 12,
|
|
70
|
+
placeholder: ""
|
|
58
71
|
}
|
|
59
72
|
};
|
|
60
|
-
|
|
73
|
+
var TYPE_LABELS = {
|
|
74
|
+
text: "Text Field",
|
|
75
|
+
signature: "Signature",
|
|
76
|
+
"signed-date": "Signed Date",
|
|
77
|
+
checkbox: "Checkbox",
|
|
78
|
+
initials: "Initials",
|
|
79
|
+
blackout: "Blackout",
|
|
80
|
+
whiteout: "Whiteout"
|
|
81
|
+
};
|
|
82
|
+
function uniqueLabel(desired, existingLabels) {
|
|
83
|
+
const lower = desired.toLowerCase();
|
|
84
|
+
if (!existingLabels.some((l) => l.toLowerCase() === lower)) return desired;
|
|
85
|
+
let i = 2;
|
|
86
|
+
while (existingLabels.some((l) => l.toLowerCase() === `${lower} ${i}`)) i++;
|
|
87
|
+
return `${desired} ${i}`;
|
|
88
|
+
}
|
|
89
|
+
function createField(type, assignee, page, x, y, existingFields) {
|
|
61
90
|
const defaults = FIELD_DEFAULTS[type];
|
|
91
|
+
const baseLabel = TYPE_LABELS[type];
|
|
92
|
+
const existingLabels = existingFields?.map((f) => f.label) ?? [];
|
|
62
93
|
return {
|
|
63
94
|
id: generateId(),
|
|
64
95
|
type,
|
|
65
|
-
label:
|
|
96
|
+
label: uniqueLabel(baseLabel, existingLabels),
|
|
66
97
|
placeholder: defaults.placeholder || "",
|
|
67
98
|
required: true,
|
|
68
99
|
assignee,
|
|
@@ -117,6 +148,7 @@ function PdfViewer({
|
|
|
117
148
|
onFieldMove,
|
|
118
149
|
onFieldResize,
|
|
119
150
|
onPageClick,
|
|
151
|
+
onDropField,
|
|
120
152
|
mode,
|
|
121
153
|
currentSigner,
|
|
122
154
|
renderFieldContent
|
|
@@ -134,6 +166,21 @@ function PdfViewer({
|
|
|
134
166
|
onSelectField(null);
|
|
135
167
|
}
|
|
136
168
|
}, [onPageClick, onSelectField]);
|
|
169
|
+
const handleDragOver = useCallback((e) => {
|
|
170
|
+
if (e.dataTransfer.types.includes("application/exeq-field-type")) {
|
|
171
|
+
e.preventDefault();
|
|
172
|
+
e.dataTransfer.dropEffect = "copy";
|
|
173
|
+
}
|
|
174
|
+
}, []);
|
|
175
|
+
const handleDrop = useCallback((e, pageIndex) => {
|
|
176
|
+
const fieldType = e.dataTransfer.getData("application/exeq-field-type");
|
|
177
|
+
if (!fieldType || !onDropField) return;
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
180
|
+
const x = (e.clientX - rect.left) / rect.width * 100;
|
|
181
|
+
const y = (e.clientY - rect.top) / rect.height * 100;
|
|
182
|
+
onDropField(pageIndex, x, y, fieldType);
|
|
183
|
+
}, [onDropField]);
|
|
137
184
|
return /* @__PURE__ */ jsx("div", { className: "pdf-viewer", ref: containerRef, children: pages.map((page, pageIndex) => {
|
|
138
185
|
const pageFields = fields.filter((f) => f.page === pageIndex);
|
|
139
186
|
return /* @__PURE__ */ jsxs(
|
|
@@ -142,6 +189,8 @@ function PdfViewer({
|
|
|
142
189
|
className: "pdf-page",
|
|
143
190
|
style: { aspectRatio: `${page.width} / ${page.height}` },
|
|
144
191
|
onClick: (e) => handlePageClick(e, pageIndex),
|
|
192
|
+
onDragOver: handleDragOver,
|
|
193
|
+
onDrop: (e) => handleDrop(e, pageIndex),
|
|
145
194
|
"data-page": pageIndex,
|
|
146
195
|
children: [
|
|
147
196
|
/* @__PURE__ */ jsx(
|
|
@@ -187,6 +236,7 @@ function FieldOverlayItem({
|
|
|
187
236
|
const dragStartRef = useRef(null);
|
|
188
237
|
const resizeStartRef = useRef(null);
|
|
189
238
|
const color = getSignerColor(field.assignee);
|
|
239
|
+
const isRedact = field.type === "blackout" || field.type === "whiteout";
|
|
190
240
|
const isEditable = mode === "designer" || mode === "signer" && field.assignee === currentSigner;
|
|
191
241
|
const isPreFilled = !isEditable && !!field.value;
|
|
192
242
|
const handleMouseDown = useCallback((e) => {
|
|
@@ -252,14 +302,15 @@ function FieldOverlayItem({
|
|
|
252
302
|
"div",
|
|
253
303
|
{
|
|
254
304
|
ref: overlayRef,
|
|
255
|
-
className: `field-overlay ${isSelected ? "selected" : ""} ${isEditable ? "editable" : "readonly"} ${isPreFilled ? "prefilled" : ""}`,
|
|
305
|
+
className: `field-overlay ${isSelected ? "selected" : ""} ${isEditable ? "editable" : "readonly"} ${isPreFilled ? "prefilled" : ""} ${isRedact ? "redact" : ""}`,
|
|
256
306
|
style: {
|
|
257
307
|
left: `${field.x}%`,
|
|
258
308
|
top: `${field.y}%`,
|
|
259
309
|
width: `${field.width}%`,
|
|
260
310
|
height: `${field.height}%`,
|
|
261
|
-
borderColor: color,
|
|
262
|
-
|
|
311
|
+
borderColor: isRedact ? field.type === "blackout" ? "#666" : "#bbb" : color,
|
|
312
|
+
borderStyle: isRedact ? "dashed" : "solid",
|
|
313
|
+
backgroundColor: isRedact ? field.type === "blackout" ? "#000000" : "#ffffff" : isSelected ? `${color}22` : `${color}11`,
|
|
263
314
|
cursor: mode === "designer" ? "move" : "default"
|
|
264
315
|
},
|
|
265
316
|
onClick: (e) => {
|
|
@@ -268,7 +319,7 @@ function FieldOverlayItem({
|
|
|
268
319
|
},
|
|
269
320
|
onMouseDown: handleMouseDown,
|
|
270
321
|
children: [
|
|
271
|
-
mode === "designer" && /* @__PURE__ */ jsx("div", { className: "field-overlay-label", style: { backgroundColor: color }, children: field.label }),
|
|
322
|
+
mode === "designer" && !isRedact && /* @__PURE__ */ jsx("div", { className: "field-overlay-label", style: { backgroundColor: color }, children: field.label }),
|
|
272
323
|
renderContent ? renderContent(field) : /* @__PURE__ */ jsx("div", { className: "field-overlay-placeholder", children: field.value || field.placeholder }),
|
|
273
324
|
mode === "designer" && isSelected && /* @__PURE__ */ jsx(
|
|
274
325
|
"div",
|
|
@@ -284,8 +335,15 @@ function FieldOverlayItem({
|
|
|
284
335
|
|
|
285
336
|
// src/components/pdf-builder/FieldPropertyPanel.tsx
|
|
286
337
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
338
|
+
var INK_COLORS = [
|
|
339
|
+
{ value: "#000000", label: "Black" },
|
|
340
|
+
{ value: "#1a56db", label: "Blue" }
|
|
341
|
+
];
|
|
287
342
|
function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
288
343
|
const color = getSignerColor(field.assignee);
|
|
344
|
+
const isRedactField = field.type === "blackout" || field.type === "whiteout";
|
|
345
|
+
const showFontSize = field.type === "text" || field.type === "signed-date";
|
|
346
|
+
const showInkColor = field.type === "text" || field.type === "signature" || field.type === "initials" || field.type === "signed-date";
|
|
289
347
|
return /* @__PURE__ */ jsxs2("div", { className: "field-property-panel", children: [
|
|
290
348
|
/* @__PURE__ */ jsxs2("div", { className: "panel-header", children: [
|
|
291
349
|
/* @__PURE__ */ jsx2("h3", { style: { color }, children: field.label }),
|
|
@@ -302,7 +360,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
302
360
|
}
|
|
303
361
|
)
|
|
304
362
|
] }),
|
|
305
|
-
/* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
363
|
+
!isRedactField && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
306
364
|
/* @__PURE__ */ jsx2("label", { children: "Field Type" }),
|
|
307
365
|
/* @__PURE__ */ jsxs2(
|
|
308
366
|
"select",
|
|
@@ -336,7 +394,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
336
394
|
}
|
|
337
395
|
)
|
|
338
396
|
] }),
|
|
339
|
-
/* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
397
|
+
!isRedactField && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
340
398
|
/* @__PURE__ */ jsx2("label", { children: "Assigned To" }),
|
|
341
399
|
/* @__PURE__ */ jsx2(
|
|
342
400
|
"select",
|
|
@@ -347,7 +405,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
347
405
|
}
|
|
348
406
|
)
|
|
349
407
|
] }),
|
|
350
|
-
/* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
408
|
+
!isRedactField && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
351
409
|
/* @__PURE__ */ jsx2("label", { children: "Placeholder" }),
|
|
352
410
|
/* @__PURE__ */ jsx2(
|
|
353
411
|
"input",
|
|
@@ -358,7 +416,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
358
416
|
}
|
|
359
417
|
)
|
|
360
418
|
] }),
|
|
361
|
-
/* @__PURE__ */ jsx2("div", { className: "panel-field", children: /* @__PURE__ */ jsxs2("label", { className: "panel-checkbox-label", children: [
|
|
419
|
+
!isRedactField && /* @__PURE__ */ jsx2("div", { className: "panel-field", children: /* @__PURE__ */ jsxs2("label", { className: "panel-checkbox-label", children: [
|
|
362
420
|
/* @__PURE__ */ jsx2(
|
|
363
421
|
"input",
|
|
364
422
|
{
|
|
@@ -369,7 +427,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
369
427
|
),
|
|
370
428
|
"Required"
|
|
371
429
|
] }) }),
|
|
372
|
-
|
|
430
|
+
showFontSize && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
373
431
|
/* @__PURE__ */ jsx2("label", { children: "Font Size (pt)" }),
|
|
374
432
|
/* @__PURE__ */ jsx2(
|
|
375
433
|
"input",
|
|
@@ -382,118 +440,26 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
382
440
|
}
|
|
383
441
|
)
|
|
384
442
|
] }),
|
|
385
|
-
/* @__PURE__ */ jsxs2("div", { className: "panel-field
|
|
386
|
-
/* @__PURE__ */
|
|
387
|
-
|
|
388
|
-
/* @__PURE__ */ jsx2(
|
|
389
|
-
"input",
|
|
390
|
-
{
|
|
391
|
-
type: "number",
|
|
392
|
-
min: "1",
|
|
393
|
-
max: "100",
|
|
394
|
-
step: "0.5",
|
|
395
|
-
value: field.width,
|
|
396
|
-
onChange: (e) => onUpdate(field.id, { width: Number(e.target.value) })
|
|
397
|
-
}
|
|
398
|
-
)
|
|
399
|
-
] }),
|
|
400
|
-
/* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
401
|
-
/* @__PURE__ */ jsx2("label", { children: "Height (%)" }),
|
|
402
|
-
/* @__PURE__ */ jsx2(
|
|
403
|
-
"input",
|
|
404
|
-
{
|
|
405
|
-
type: "number",
|
|
406
|
-
min: "1",
|
|
407
|
-
max: "100",
|
|
408
|
-
step: "0.5",
|
|
409
|
-
value: field.height,
|
|
410
|
-
onChange: (e) => onUpdate(field.id, { height: Number(e.target.value) })
|
|
411
|
-
}
|
|
412
|
-
)
|
|
413
|
-
] })
|
|
414
|
-
] })
|
|
415
|
-
] });
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// src/components/pdf-builder/SignerRoleSelector.tsx
|
|
419
|
-
import { useState } from "react";
|
|
420
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
421
|
-
function SignerRoleSelector({
|
|
422
|
-
roles,
|
|
423
|
-
activeRole,
|
|
424
|
-
onSetActiveRole,
|
|
425
|
-
onAddRole,
|
|
426
|
-
onRemoveRole
|
|
427
|
-
}) {
|
|
428
|
-
const [isAdding, setIsAdding] = useState(false);
|
|
429
|
-
const [newRoleName, setNewRoleName] = useState("");
|
|
430
|
-
const handleAdd = () => {
|
|
431
|
-
if (newRoleName.trim()) {
|
|
432
|
-
onAddRole(newRoleName.trim());
|
|
433
|
-
setNewRoleName("");
|
|
434
|
-
setIsAdding(false);
|
|
435
|
-
}
|
|
436
|
-
};
|
|
437
|
-
return /* @__PURE__ */ jsxs3("div", { className: "signer-role-selector", children: [
|
|
438
|
-
/* @__PURE__ */ jsx3("div", { className: "signer-role-label", children: "Signer Roles" }),
|
|
439
|
-
/* @__PURE__ */ jsxs3("div", { className: "signer-role-list", children: [
|
|
440
|
-
roles.map((role) => /* @__PURE__ */ jsxs3(
|
|
443
|
+
showInkColor && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
|
|
444
|
+
/* @__PURE__ */ jsx2("label", { children: "Ink Color" }),
|
|
445
|
+
/* @__PURE__ */ jsx2("div", { className: "ink-color-picker", children: INK_COLORS.map((c) => /* @__PURE__ */ jsx2(
|
|
441
446
|
"button",
|
|
442
447
|
{
|
|
443
|
-
className: `
|
|
444
|
-
style: {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
color: role === activeRole ? "#fff" : getSignerColor(role)
|
|
448
|
-
},
|
|
449
|
-
onClick: () => onSetActiveRole(role),
|
|
450
|
-
children: [
|
|
451
|
-
role,
|
|
452
|
-
roles.length > 1 && role !== "Sender" && /* @__PURE__ */ jsx3(
|
|
453
|
-
"span",
|
|
454
|
-
{
|
|
455
|
-
className: "signer-role-remove",
|
|
456
|
-
onClick: (e) => {
|
|
457
|
-
e.stopPropagation();
|
|
458
|
-
onRemoveRole(role);
|
|
459
|
-
},
|
|
460
|
-
children: "\xD7"
|
|
461
|
-
}
|
|
462
|
-
)
|
|
463
|
-
]
|
|
448
|
+
className: `ink-color-swatch ${(field.inkColor || "#000000") === c.value ? "active" : ""}`,
|
|
449
|
+
style: { backgroundColor: c.value },
|
|
450
|
+
onClick: () => onUpdate(field.id, { inkColor: c.value }),
|
|
451
|
+
title: c.label
|
|
464
452
|
},
|
|
465
|
-
|
|
466
|
-
))
|
|
467
|
-
isAdding ? /* @__PURE__ */ jsxs3("div", { className: "signer-role-add-input", children: [
|
|
468
|
-
/* @__PURE__ */ jsx3(
|
|
469
|
-
"input",
|
|
470
|
-
{
|
|
471
|
-
type: "text",
|
|
472
|
-
value: newRoleName,
|
|
473
|
-
onChange: (e) => setNewRoleName(e.target.value),
|
|
474
|
-
onKeyDown: (e) => e.key === "Enter" && handleAdd(),
|
|
475
|
-
placeholder: "Role name",
|
|
476
|
-
autoFocus: true
|
|
477
|
-
}
|
|
478
|
-
),
|
|
479
|
-
/* @__PURE__ */ jsx3("button", { onClick: handleAdd, children: "Add" }),
|
|
480
|
-
/* @__PURE__ */ jsx3("button", { onClick: () => setIsAdding(false), children: "Cancel" })
|
|
481
|
-
] }) : /* @__PURE__ */ jsx3(
|
|
482
|
-
"button",
|
|
483
|
-
{
|
|
484
|
-
className: "signer-role-add-btn",
|
|
485
|
-
onClick: () => setIsAdding(true),
|
|
486
|
-
children: "+ Add Role"
|
|
487
|
-
}
|
|
488
|
-
)
|
|
453
|
+
c.value
|
|
454
|
+
)) })
|
|
489
455
|
] })
|
|
490
456
|
] });
|
|
491
457
|
}
|
|
492
458
|
|
|
493
459
|
// src/components/pdf-builder/SignatureCanvas.tsx
|
|
494
|
-
import { useRef as useRef2, useState
|
|
460
|
+
import { useRef as useRef2, useState, useCallback as useCallback2, useEffect } from "react";
|
|
495
461
|
import { getStroke } from "perfect-freehand";
|
|
496
|
-
import { jsx as
|
|
462
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
497
463
|
function getSvgPathFromStroke(stroke) {
|
|
498
464
|
if (!stroke.length) return "";
|
|
499
465
|
const d = stroke.reduce(
|
|
@@ -512,12 +478,13 @@ function SignatureCanvas({
|
|
|
512
478
|
height = 150,
|
|
513
479
|
onSign,
|
|
514
480
|
initialValue,
|
|
515
|
-
className
|
|
481
|
+
className,
|
|
482
|
+
inkColor = "#000000"
|
|
516
483
|
}) {
|
|
517
484
|
const canvasRef = useRef2(null);
|
|
518
|
-
const [paths, setPaths] =
|
|
519
|
-
const [currentPath, setCurrentPath] =
|
|
520
|
-
const [isEmpty, setIsEmpty] =
|
|
485
|
+
const [paths, setPaths] = useState([]);
|
|
486
|
+
const [currentPath, setCurrentPath] = useState(null);
|
|
487
|
+
const [isEmpty, setIsEmpty] = useState(!initialValue);
|
|
521
488
|
useEffect(() => {
|
|
522
489
|
if (initialValue && canvasRef.current) {
|
|
523
490
|
const ctx = canvasRef.current.getContext("2d");
|
|
@@ -544,7 +511,7 @@ function SignatureCanvas({
|
|
|
544
511
|
});
|
|
545
512
|
const pathStr = getSvgPathFromStroke(stroke);
|
|
546
513
|
const path2d = new Path2D(pathStr);
|
|
547
|
-
ctx.fillStyle =
|
|
514
|
+
ctx.fillStyle = inkColor;
|
|
548
515
|
ctx.fill(path2d);
|
|
549
516
|
}
|
|
550
517
|
}, [paths, currentPath, width, height]);
|
|
@@ -588,8 +555,8 @@ function SignatureCanvas({
|
|
|
588
555
|
onSign("");
|
|
589
556
|
}
|
|
590
557
|
}, [width, height, onSign]);
|
|
591
|
-
return /* @__PURE__ */
|
|
592
|
-
/* @__PURE__ */
|
|
558
|
+
return /* @__PURE__ */ jsxs3("div", { className: `signature-canvas-wrapper ${className || ""}`, children: [
|
|
559
|
+
/* @__PURE__ */ jsx3(
|
|
593
560
|
"canvas",
|
|
594
561
|
{
|
|
595
562
|
ref: canvasRef,
|
|
@@ -602,29 +569,76 @@ function SignatureCanvas({
|
|
|
602
569
|
style: { touchAction: "none" }
|
|
603
570
|
}
|
|
604
571
|
),
|
|
605
|
-
/* @__PURE__ */
|
|
606
|
-
!isEmpty && /* @__PURE__ */
|
|
607
|
-
isEmpty && /* @__PURE__ */
|
|
572
|
+
/* @__PURE__ */ jsxs3("div", { className: "signature-canvas-actions", children: [
|
|
573
|
+
!isEmpty && /* @__PURE__ */ jsx3("button", { type: "button", onClick: handleClear, className: "signature-clear-btn", children: "Clear" }),
|
|
574
|
+
isEmpty && /* @__PURE__ */ jsx3("span", { className: "signature-hint", children: "Draw your signature above" })
|
|
608
575
|
] })
|
|
609
576
|
] });
|
|
610
577
|
}
|
|
611
578
|
|
|
579
|
+
// src/utils/apiKey.ts
|
|
580
|
+
var VALID_KEYS = /* @__PURE__ */ new Set([
|
|
581
|
+
"exeq_live_2024"
|
|
582
|
+
]);
|
|
583
|
+
function isValidApiKey(key) {
|
|
584
|
+
if (!key) return false;
|
|
585
|
+
return VALID_KEYS.has(key);
|
|
586
|
+
}
|
|
587
|
+
|
|
612
588
|
// src/components/pdf-builder/DesignerView.tsx
|
|
613
|
-
import { Fragment, jsx as
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
589
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
590
|
+
var FIELD_TYPE_META = [
|
|
591
|
+
{ type: "text", label: "Text", icon: "T" },
|
|
592
|
+
{ type: "signature", label: "Signature", icon: "\u270D" },
|
|
593
|
+
{ type: "signed-date", label: "Date", icon: "\u{1F4C5}" },
|
|
594
|
+
{ type: "checkbox", label: "Checkbox", icon: "\u2611" },
|
|
595
|
+
{ type: "initials", label: "Initials", icon: "IN" },
|
|
596
|
+
{ type: "blackout", label: "Blackout", icon: "\u25A0", group: "redact" },
|
|
597
|
+
{ type: "whiteout", label: "Whiteout", icon: "\u25A1", group: "redact" }
|
|
598
|
+
];
|
|
599
|
+
function DesignerView({
|
|
600
|
+
apiKey,
|
|
601
|
+
initialPdfUrl,
|
|
602
|
+
initialTemplate,
|
|
603
|
+
onSave,
|
|
604
|
+
hideHeader,
|
|
605
|
+
headerPortalRef
|
|
606
|
+
} = {}) {
|
|
607
|
+
if (!isValidApiKey(apiKey)) {
|
|
608
|
+
return /* @__PURE__ */ jsx4("div", { className: "designer-layout", children: /* @__PURE__ */ jsxs4("div", { className: "empty-state", children: [
|
|
609
|
+
/* @__PURE__ */ jsx4("h2", { children: "Invalid API Key" }),
|
|
610
|
+
/* @__PURE__ */ jsxs4("p", { children: [
|
|
611
|
+
"A valid API key is required to use Exeq. Visit ",
|
|
612
|
+
/* @__PURE__ */ jsx4("a", { href: "https://exeq.org", children: "exeq.org" }),
|
|
613
|
+
" to get one."
|
|
614
|
+
] })
|
|
615
|
+
] }) });
|
|
616
|
+
}
|
|
617
|
+
const [pages, setPages] = useState2([]);
|
|
618
|
+
const [fields, setFields] = useState2(initialTemplate?.fields ?? []);
|
|
619
|
+
const [selectedFieldId, setSelectedFieldId] = useState2(null);
|
|
620
|
+
const [signerRoles, setSignerRoles] = useState2(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
|
|
621
|
+
const [activeRole, setActiveRole] = useState2("Sender");
|
|
622
|
+
const [activeFieldType, setActiveFieldType] = useState2("text");
|
|
623
|
+
const [loading, setLoading] = useState2(false);
|
|
624
|
+
const [pdfSource, setPdfSource] = useState2(null);
|
|
625
|
+
const [rightTab, setRightTab] = useState2("properties");
|
|
626
|
+
const [isAddingRole, setIsAddingRole] = useState2(false);
|
|
627
|
+
const [newRoleName, setNewRoleName] = useState2("");
|
|
628
|
+
const [draggingFieldType, setDraggingFieldType] = useState2(null);
|
|
629
|
+
const [panelWidth, setPanelWidth] = useState2(380);
|
|
630
|
+
const dragGhostRef = useRef3(null);
|
|
631
|
+
const resizingRef = useRef3(false);
|
|
623
632
|
useEffect2(() => {
|
|
624
|
-
const
|
|
625
|
-
const pdfUrl = params.get("pdf");
|
|
633
|
+
const pdfUrl = initialPdfUrl || initialTemplate?.pdfUrl;
|
|
626
634
|
if (pdfUrl) {
|
|
627
635
|
loadPdf(pdfUrl);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
const params = new URLSearchParams(window.location.search);
|
|
639
|
+
const paramPdf = params.get("pdf");
|
|
640
|
+
if (paramPdf) {
|
|
641
|
+
loadPdf(paramPdf);
|
|
628
642
|
}
|
|
629
643
|
const handleMessage = (e) => {
|
|
630
644
|
if (e.data?.type === "load-pdf" && e.data.url) {
|
|
@@ -638,7 +652,7 @@ function DesignerView() {
|
|
|
638
652
|
};
|
|
639
653
|
window.addEventListener("message", handleMessage);
|
|
640
654
|
return () => window.removeEventListener("message", handleMessage);
|
|
641
|
-
}, []);
|
|
655
|
+
}, [initialPdfUrl, initialTemplate]);
|
|
642
656
|
const loadPdf = useCallback3(async (source) => {
|
|
643
657
|
setLoading(true);
|
|
644
658
|
try {
|
|
@@ -661,10 +675,11 @@ function DesignerView() {
|
|
|
661
675
|
reader.readAsArrayBuffer(file);
|
|
662
676
|
}, [loadPdf]);
|
|
663
677
|
const handlePageClick = useCallback3((page, x, y) => {
|
|
664
|
-
const field = createField(activeFieldType, activeRole, page, x, y);
|
|
678
|
+
const field = createField(activeFieldType, activeRole, page, x, y, fields);
|
|
665
679
|
setFields((prev) => [...prev, field]);
|
|
666
680
|
setSelectedFieldId(field.id);
|
|
667
|
-
|
|
681
|
+
setRightTab("properties");
|
|
682
|
+
}, [activeFieldType, activeRole, fields]);
|
|
668
683
|
const handleFieldMove = useCallback3((id, page, x, y) => {
|
|
669
684
|
setFields((prev) => prev.map((f) => f.id === id ? { ...f, page, x, y } : f));
|
|
670
685
|
}, []);
|
|
@@ -672,17 +687,26 @@ function DesignerView() {
|
|
|
672
687
|
setFields((prev) => prev.map((f) => f.id === id ? { ...f, width, height } : f));
|
|
673
688
|
}, []);
|
|
674
689
|
const handleFieldUpdate = useCallback3((id, updates) => {
|
|
675
|
-
setFields((prev) =>
|
|
690
|
+
setFields((prev) => {
|
|
691
|
+
if (updates.label !== void 0) {
|
|
692
|
+
const otherLabels = prev.filter((f) => f.id !== id).map((f) => f.label);
|
|
693
|
+
updates.label = uniqueLabel(updates.label, otherLabels);
|
|
694
|
+
}
|
|
695
|
+
return prev.map((f) => f.id === id ? { ...f, ...updates } : f);
|
|
696
|
+
});
|
|
676
697
|
}, []);
|
|
677
698
|
const handleFieldDelete = useCallback3((id) => {
|
|
678
699
|
setFields((prev) => prev.filter((f) => f.id !== id));
|
|
679
700
|
if (selectedFieldId === id) setSelectedFieldId(null);
|
|
680
701
|
}, [selectedFieldId]);
|
|
681
|
-
const handleAddRole = useCallback3((
|
|
682
|
-
|
|
683
|
-
|
|
702
|
+
const handleAddRole = useCallback3(() => {
|
|
703
|
+
const name = newRoleName.trim();
|
|
704
|
+
if (name && !signerRoles.includes(name)) {
|
|
705
|
+
setSignerRoles((prev) => [...prev, name]);
|
|
684
706
|
}
|
|
685
|
-
|
|
707
|
+
setNewRoleName("");
|
|
708
|
+
setIsAddingRole(false);
|
|
709
|
+
}, [newRoleName, signerRoles]);
|
|
686
710
|
const handleRemoveRole = useCallback3((role) => {
|
|
687
711
|
setSignerRoles((prev) => prev.filter((r) => r !== role));
|
|
688
712
|
setFields((prev) => prev.map((f) => f.assignee === role ? { ...f, assignee: signerRoles[0] } : f));
|
|
@@ -694,170 +718,324 @@ function DesignerView() {
|
|
|
694
718
|
signerRoles,
|
|
695
719
|
pdfUrl: typeof pdfSource === "string" ? pdfSource : ""
|
|
696
720
|
};
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
721
|
+
if (onSave) {
|
|
722
|
+
onSave(template);
|
|
723
|
+
} else {
|
|
724
|
+
const json = JSON.stringify(template, null, 2);
|
|
725
|
+
const blob = new Blob([json], { type: "application/json" });
|
|
726
|
+
const url = URL.createObjectURL(blob);
|
|
727
|
+
const a = document.createElement("a");
|
|
728
|
+
a.href = url;
|
|
729
|
+
a.download = "template.json";
|
|
730
|
+
a.click();
|
|
731
|
+
URL.revokeObjectURL(url);
|
|
732
|
+
}
|
|
705
733
|
window.parent?.postMessage({ type: "template-saved", template }, "*");
|
|
706
|
-
}, [fields, signerRoles, pdfSource]);
|
|
734
|
+
}, [fields, signerRoles, pdfSource, onSave]);
|
|
735
|
+
const handlePaletteDragStart = useCallback3((e, type) => {
|
|
736
|
+
setDraggingFieldType(type);
|
|
737
|
+
e.dataTransfer.setData("application/exeq-field-type", type);
|
|
738
|
+
e.dataTransfer.effectAllowed = "copy";
|
|
739
|
+
const ghost = document.createElement("div");
|
|
740
|
+
ghost.className = "palette-drag-ghost";
|
|
741
|
+
ghost.textContent = FIELD_TYPE_META.find((f) => f.type === type)?.label || type;
|
|
742
|
+
ghost.style.cssText = `
|
|
743
|
+
position: fixed; top: -200px; left: -200px;
|
|
744
|
+
padding: 6px 16px; background: ${getSignerColor(activeRole)};
|
|
745
|
+
color: #fff; border-radius: 4px; font-size: 12px;
|
|
746
|
+
font-family: system-ui; pointer-events: none; white-space: nowrap;
|
|
747
|
+
`;
|
|
748
|
+
document.body.appendChild(ghost);
|
|
749
|
+
e.dataTransfer.setDragImage(ghost, 40, 16);
|
|
750
|
+
dragGhostRef.current = ghost;
|
|
751
|
+
}, [activeRole]);
|
|
752
|
+
const handlePaletteDragEnd = useCallback3(() => {
|
|
753
|
+
setDraggingFieldType(null);
|
|
754
|
+
if (dragGhostRef.current) {
|
|
755
|
+
document.body.removeChild(dragGhostRef.current);
|
|
756
|
+
dragGhostRef.current = null;
|
|
757
|
+
}
|
|
758
|
+
}, []);
|
|
759
|
+
const handleDropOnPage = useCallback3((page, x, y, fieldType) => {
|
|
760
|
+
const field = createField(fieldType, activeRole, page, x, y, fields);
|
|
761
|
+
setFields((prev) => [...prev, field]);
|
|
762
|
+
setSelectedFieldId(field.id);
|
|
763
|
+
setRightTab("properties");
|
|
764
|
+
}, [activeRole, fields]);
|
|
765
|
+
useEffect2(() => {
|
|
766
|
+
const handleKeyDown = (e) => {
|
|
767
|
+
if (e.key !== "Delete" && e.key !== "Backspace") return;
|
|
768
|
+
if (!selectedFieldId) return;
|
|
769
|
+
const tag = (document.activeElement?.tagName || "").toLowerCase();
|
|
770
|
+
if (tag === "input" || tag === "textarea" || tag === "select") return;
|
|
771
|
+
if (document.activeElement?.isContentEditable) return;
|
|
772
|
+
e.preventDefault();
|
|
773
|
+
if (window.confirm("Delete this field?")) {
|
|
774
|
+
handleFieldDelete(selectedFieldId);
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
778
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
779
|
+
}, [selectedFieldId, handleFieldDelete]);
|
|
780
|
+
const handleResizeStart = useCallback3((e) => {
|
|
781
|
+
e.preventDefault();
|
|
782
|
+
resizingRef.current = true;
|
|
783
|
+
const startX = e.clientX;
|
|
784
|
+
const startWidth = panelWidth;
|
|
785
|
+
const handleMouseMove = (e2) => {
|
|
786
|
+
if (!resizingRef.current) return;
|
|
787
|
+
const delta = startX - e2.clientX;
|
|
788
|
+
setPanelWidth(Math.max(280, Math.min(600, startWidth + delta)));
|
|
789
|
+
};
|
|
790
|
+
const handleMouseUp = () => {
|
|
791
|
+
resizingRef.current = false;
|
|
792
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
793
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
794
|
+
};
|
|
795
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
796
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
797
|
+
}, [panelWidth]);
|
|
798
|
+
const sortedFields = [...fields].sort((a, b) => {
|
|
799
|
+
if (a.page !== b.page) return a.page - b.page;
|
|
800
|
+
const bandThreshold = 2;
|
|
801
|
+
if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
|
|
802
|
+
return a.x - b.x;
|
|
803
|
+
});
|
|
707
804
|
const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
|
|
708
805
|
const renderFieldContent = useCallback3((field) => {
|
|
806
|
+
if (field.type === "blackout" || field.type === "whiteout") {
|
|
807
|
+
return null;
|
|
808
|
+
}
|
|
709
809
|
if (field.type === "signature" || field.type === "initials") {
|
|
710
810
|
if (field.value) {
|
|
711
|
-
return /* @__PURE__ */
|
|
811
|
+
return /* @__PURE__ */ jsx4("img", { src: field.value, alt: field.label, className: "field-signature-preview" });
|
|
712
812
|
}
|
|
713
813
|
}
|
|
714
|
-
|
|
814
|
+
const inkColor = field.inkColor || "#000000";
|
|
815
|
+
return /* @__PURE__ */ jsx4(
|
|
816
|
+
"div",
|
|
817
|
+
{
|
|
818
|
+
className: "field-overlay-placeholder",
|
|
819
|
+
style: { color: field.value ? inkColor : void 0, fontSize: `${field.fontSize * 0.6}px` },
|
|
820
|
+
children: field.value || field.placeholder
|
|
821
|
+
}
|
|
822
|
+
);
|
|
715
823
|
}, []);
|
|
716
|
-
|
|
717
|
-
/* @__PURE__ */
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
824
|
+
const headerButtons = pages.length > 0 ? /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
825
|
+
/* @__PURE__ */ jsxs4("label", { className: "header-btn header-btn-outline", children: [
|
|
826
|
+
"Change PDF",
|
|
827
|
+
/* @__PURE__ */ jsx4("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
|
|
828
|
+
] }),
|
|
829
|
+
/* @__PURE__ */ jsx4("button", { onClick: handleExport, className: "header-btn header-btn-primary", children: "Export Template" })
|
|
830
|
+
] }) : null;
|
|
831
|
+
return /* @__PURE__ */ jsxs4("div", { className: "designer-layout", children: [
|
|
832
|
+
headerPortalRef?.current && headerButtons && createPortal(headerButtons, headerPortalRef.current),
|
|
833
|
+
!hideHeader && !headerPortalRef && /* @__PURE__ */ jsx4("div", { className: "designer-header", children: /* @__PURE__ */ jsx4("div", { className: "designer-header-right", children: headerButtons }) }),
|
|
834
|
+
/* @__PURE__ */ jsxs4("div", { className: "designer-body", children: [
|
|
835
|
+
pages.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "designer-palette", children: [
|
|
836
|
+
/* @__PURE__ */ jsx4("div", { className: "palette-heading", children: "New Field Role" }),
|
|
837
|
+
/* @__PURE__ */ jsxs4("div", { className: "palette-role-section", children: [
|
|
838
|
+
/* @__PURE__ */ jsx4(
|
|
839
|
+
"select",
|
|
730
840
|
{
|
|
731
|
-
className:
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
841
|
+
className: "palette-role-select",
|
|
842
|
+
value: activeRole,
|
|
843
|
+
onChange: (e) => setActiveRole(e.target.value),
|
|
844
|
+
style: { borderColor: getSignerColor(activeRole), color: getSignerColor(activeRole) },
|
|
845
|
+
children: signerRoles.map((role) => /* @__PURE__ */ jsx4("option", { value: role, children: role }, role))
|
|
846
|
+
}
|
|
847
|
+
),
|
|
848
|
+
isAddingRole ? /* @__PURE__ */ jsxs4("div", { className: "palette-role-add-inline", children: [
|
|
849
|
+
/* @__PURE__ */ jsx4(
|
|
850
|
+
"input",
|
|
851
|
+
{
|
|
852
|
+
type: "text",
|
|
853
|
+
value: newRoleName,
|
|
854
|
+
onChange: (e) => setNewRoleName(e.target.value),
|
|
855
|
+
onKeyDown: (e) => e.key === "Enter" && handleAddRole(),
|
|
856
|
+
placeholder: "Role name",
|
|
857
|
+
autoFocus: true
|
|
858
|
+
}
|
|
859
|
+
),
|
|
860
|
+
/* @__PURE__ */ jsx4("button", { onClick: handleAddRole, children: "Add" }),
|
|
861
|
+
/* @__PURE__ */ jsx4("button", { onClick: () => {
|
|
862
|
+
setIsAddingRole(false);
|
|
863
|
+
setNewRoleName("");
|
|
864
|
+
}, children: "Cancel" })
|
|
865
|
+
] }) : /* @__PURE__ */ jsx4("button", { className: "palette-role-add-link", onClick: () => setIsAddingRole(true), children: "+ Add Role" })
|
|
866
|
+
] }),
|
|
867
|
+
/* @__PURE__ */ jsx4("div", { className: "palette-divider" }),
|
|
868
|
+
/* @__PURE__ */ jsx4("div", { className: "palette-heading", children: "Fields" }),
|
|
869
|
+
FIELD_TYPE_META.filter((f) => !f.group).map(({ type, label, icon }) => /* @__PURE__ */ jsxs4(
|
|
870
|
+
"div",
|
|
871
|
+
{
|
|
872
|
+
className: `palette-item ${activeFieldType === type ? "active" : ""}`,
|
|
873
|
+
draggable: true,
|
|
874
|
+
onClick: () => setActiveFieldType(type),
|
|
875
|
+
onDragStart: (e) => handlePaletteDragStart(e, type),
|
|
876
|
+
onDragEnd: handlePaletteDragEnd,
|
|
877
|
+
children: [
|
|
878
|
+
/* @__PURE__ */ jsx4("span", { className: "palette-item-icon", children: icon }),
|
|
879
|
+
/* @__PURE__ */ jsx4("span", { className: "palette-item-label", children: label })
|
|
880
|
+
]
|
|
881
|
+
},
|
|
882
|
+
type
|
|
883
|
+
)),
|
|
884
|
+
/* @__PURE__ */ jsx4("div", { className: "palette-divider" }),
|
|
885
|
+
/* @__PURE__ */ jsx4("div", { className: "palette-heading", children: "Redact" }),
|
|
886
|
+
FIELD_TYPE_META.filter((f) => f.group === "redact").map(({ type, label, icon }) => /* @__PURE__ */ jsxs4(
|
|
887
|
+
"div",
|
|
888
|
+
{
|
|
889
|
+
className: `palette-item ${activeFieldType === type ? "active" : ""}`,
|
|
890
|
+
draggable: true,
|
|
891
|
+
onClick: () => setActiveFieldType(type),
|
|
892
|
+
onDragStart: (e) => handlePaletteDragStart(e, type),
|
|
893
|
+
onDragEnd: handlePaletteDragEnd,
|
|
894
|
+
children: [
|
|
895
|
+
/* @__PURE__ */ jsx4("span", { className: "palette-item-icon", children: icon }),
|
|
896
|
+
/* @__PURE__ */ jsx4("span", { className: "palette-item-label", children: label })
|
|
897
|
+
]
|
|
898
|
+
},
|
|
899
|
+
type
|
|
900
|
+
))
|
|
738
901
|
] }),
|
|
739
|
-
/* @__PURE__ */
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
onAddRole: handleAddRole,
|
|
748
|
-
onRemoveRole: handleRemoveRole
|
|
749
|
-
}
|
|
750
|
-
),
|
|
751
|
-
/* @__PURE__ */ jsxs5("div", { className: "designer-content", children: [
|
|
752
|
-
/* @__PURE__ */ jsxs5("div", { className: "designer-pdf-area", children: [
|
|
753
|
-
loading && /* @__PURE__ */ jsx5("div", { className: "loading-indicator", children: "Loading PDF..." }),
|
|
754
|
-
!pages.length && !loading && /* @__PURE__ */ jsxs5("div", { className: "empty-state", children: [
|
|
755
|
-
/* @__PURE__ */ jsx5("h2", { children: "Upload a PDF to get started" }),
|
|
756
|
-
/* @__PURE__ */ jsx5("p", { children: "Upload a template PDF, then click on the page to place form fields." }),
|
|
757
|
-
/* @__PURE__ */ jsxs5("label", { className: "upload-btn upload-btn-large", children: [
|
|
758
|
-
"Choose PDF File",
|
|
759
|
-
/* @__PURE__ */ jsx5("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
|
|
902
|
+
/* @__PURE__ */ jsxs4("div", { className: "designer-pdf-area", children: [
|
|
903
|
+
loading && /* @__PURE__ */ jsx4("div", { className: "loading-indicator", children: "Loading PDF..." }),
|
|
904
|
+
!pages.length && !loading && /* @__PURE__ */ jsxs4("div", { className: "empty-state", children: [
|
|
905
|
+
/* @__PURE__ */ jsx4("h2", { children: "Open a PDF to get started" }),
|
|
906
|
+
/* @__PURE__ */ jsx4("p", { children: "Select a PDF from your device to begin. Your file stays on your computer \u2014 nothing is uploaded." }),
|
|
907
|
+
/* @__PURE__ */ jsxs4("label", { className: "upload-btn upload-btn-large", children: [
|
|
908
|
+
"Select PDF",
|
|
909
|
+
/* @__PURE__ */ jsx4("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
|
|
760
910
|
] })
|
|
761
911
|
] }),
|
|
762
|
-
pages.length > 0 && /* @__PURE__ */
|
|
912
|
+
pages.length > 0 && /* @__PURE__ */ jsx4(
|
|
763
913
|
PdfViewer,
|
|
764
914
|
{
|
|
765
915
|
pages,
|
|
766
916
|
fields,
|
|
767
917
|
selectedFieldId,
|
|
768
|
-
onSelectField:
|
|
918
|
+
onSelectField: (id) => {
|
|
919
|
+
setSelectedFieldId(id);
|
|
920
|
+
if (id) setRightTab("properties");
|
|
921
|
+
},
|
|
769
922
|
onFieldMove: handleFieldMove,
|
|
770
923
|
onFieldResize: handleFieldResize,
|
|
771
924
|
onPageClick: handlePageClick,
|
|
925
|
+
onDropField: handleDropOnPage,
|
|
772
926
|
mode: "designer",
|
|
773
927
|
renderFieldContent
|
|
774
928
|
}
|
|
775
929
|
)
|
|
776
930
|
] }),
|
|
777
|
-
/* @__PURE__ */
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
signerRoles,
|
|
784
|
-
onUpdate: handleFieldUpdate,
|
|
785
|
-
onDelete: handleFieldDelete
|
|
786
|
-
}
|
|
787
|
-
),
|
|
788
|
-
selectedField.assignee === activeRole && /* @__PURE__ */ jsxs5("div", { className: "prefill-section", children: [
|
|
789
|
-
/* @__PURE__ */ jsx5("h4", { children: "Pre-fill Value" }),
|
|
790
|
-
selectedField.type === "signature" || selectedField.type === "initials" ? /* @__PURE__ */ jsx5(
|
|
791
|
-
SignatureCanvas,
|
|
931
|
+
pages.length > 0 && /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
932
|
+
/* @__PURE__ */ jsx4("div", { className: "panel-resize-handle", onMouseDown: handleResizeStart }),
|
|
933
|
+
/* @__PURE__ */ jsxs4("div", { className: "designer-panel", style: { width: panelWidth }, children: [
|
|
934
|
+
/* @__PURE__ */ jsxs4("div", { className: "panel-tabs", children: [
|
|
935
|
+
/* @__PURE__ */ jsx4(
|
|
936
|
+
"button",
|
|
792
937
|
{
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
initialValue: selectedField.value
|
|
938
|
+
className: `panel-tab ${rightTab === "properties" ? "active" : ""}`,
|
|
939
|
+
onClick: () => setRightTab("properties"),
|
|
940
|
+
children: "Properties"
|
|
797
941
|
}
|
|
798
|
-
)
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
{
|
|
802
|
-
type: "checkbox",
|
|
803
|
-
checked: selectedField.value === "true",
|
|
804
|
-
onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.checked ? "true" : "" })
|
|
805
|
-
}
|
|
806
|
-
),
|
|
807
|
-
"Checked"
|
|
808
|
-
] }) : /* @__PURE__ */ jsx5(
|
|
809
|
-
"input",
|
|
942
|
+
),
|
|
943
|
+
/* @__PURE__ */ jsxs4(
|
|
944
|
+
"button",
|
|
810
945
|
{
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
946
|
+
className: `panel-tab ${rightTab === "fields" ? "active" : ""}`,
|
|
947
|
+
onClick: () => setRightTab("fields"),
|
|
948
|
+
children: [
|
|
949
|
+
"All Fields",
|
|
950
|
+
fields.length > 0 ? ` (${fields.length})` : ""
|
|
951
|
+
]
|
|
816
952
|
}
|
|
817
953
|
)
|
|
818
|
-
] })
|
|
819
|
-
] }) : /* @__PURE__ */ jsx5("div", { className: "panel-empty", children: pages.length > 0 ? "Click on the PDF to place a field, or select an existing field to edit its properties." : "Upload a PDF to begin designing your template." }),
|
|
820
|
-
fields.length > 0 && /* @__PURE__ */ jsxs5("div", { className: "field-list", children: [
|
|
821
|
-
/* @__PURE__ */ jsxs5("h4", { children: [
|
|
822
|
-
"All Fields (",
|
|
823
|
-
fields.length,
|
|
824
|
-
")"
|
|
825
954
|
] }),
|
|
826
|
-
|
|
827
|
-
"
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
955
|
+
/* @__PURE__ */ jsxs4("div", { className: "panel-tab-content", children: [
|
|
956
|
+
rightTab === "properties" && /* @__PURE__ */ jsx4(Fragment, { children: selectedField ? /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
957
|
+
/* @__PURE__ */ jsx4(
|
|
958
|
+
FieldPropertyPanel,
|
|
959
|
+
{
|
|
960
|
+
field: selectedField,
|
|
961
|
+
signerRoles,
|
|
962
|
+
onUpdate: handleFieldUpdate,
|
|
963
|
+
onDelete: handleFieldDelete
|
|
964
|
+
}
|
|
965
|
+
),
|
|
966
|
+
selectedField.type !== "blackout" && selectedField.type !== "whiteout" && /* @__PURE__ */ jsxs4("div", { className: "prefill-section", children: [
|
|
967
|
+
/* @__PURE__ */ jsx4("h4", { children: "Pre-fill Value" }),
|
|
968
|
+
selectedField.type === "signature" || selectedField.type === "initials" ? /* @__PURE__ */ jsx4(
|
|
969
|
+
SignatureCanvas,
|
|
834
970
|
{
|
|
835
|
-
|
|
836
|
-
|
|
971
|
+
width: panelWidth - 40,
|
|
972
|
+
height: selectedField.type === "initials" ? 100 : 150,
|
|
973
|
+
onSign: (dataUrl) => handleFieldUpdate(selectedField.id, { value: dataUrl }),
|
|
974
|
+
initialValue: selectedField.value,
|
|
975
|
+
inkColor: selectedField.inkColor
|
|
837
976
|
}
|
|
838
|
-
),
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
977
|
+
) : selectedField.type === "checkbox" ? /* @__PURE__ */ jsxs4("label", { className: "panel-checkbox-label", children: [
|
|
978
|
+
/* @__PURE__ */ jsx4(
|
|
979
|
+
"input",
|
|
980
|
+
{
|
|
981
|
+
type: "checkbox",
|
|
982
|
+
checked: selectedField.value === "true",
|
|
983
|
+
onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.checked ? "true" : "" })
|
|
984
|
+
}
|
|
985
|
+
),
|
|
986
|
+
"Checked"
|
|
987
|
+
] }) : /* @__PURE__ */ jsx4(
|
|
988
|
+
"input",
|
|
989
|
+
{
|
|
990
|
+
type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
|
|
991
|
+
value: selectedField.value,
|
|
992
|
+
onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.value }),
|
|
993
|
+
placeholder: `Pre-fill ${selectedField.label}`,
|
|
994
|
+
className: "prefill-input",
|
|
995
|
+
style: { color: selectedField.inkColor || "#000000" }
|
|
996
|
+
}
|
|
997
|
+
)
|
|
998
|
+
] })
|
|
999
|
+
] }) : /* @__PURE__ */ jsx4("div", { className: "panel-empty", children: "Click on the PDF to place a field, or select an existing field to edit its properties." }) }),
|
|
1000
|
+
rightTab === "fields" && /* @__PURE__ */ jsx4(Fragment, { children: fields.length === 0 ? /* @__PURE__ */ jsx4("div", { className: "panel-empty", children: "No fields yet. Drag a field from the left palette onto the PDF, or click on the PDF to place one." }) : /* @__PURE__ */ jsx4("div", { className: "field-list", children: sortedFields.map((f) => /* @__PURE__ */ jsxs4(
|
|
1001
|
+
"div",
|
|
1002
|
+
{
|
|
1003
|
+
className: `field-list-item ${f.id === selectedFieldId ? "active" : ""}`,
|
|
1004
|
+
onClick: () => {
|
|
1005
|
+
setSelectedFieldId(f.id);
|
|
1006
|
+
setRightTab("properties");
|
|
1007
|
+
},
|
|
1008
|
+
children: [
|
|
1009
|
+
/* @__PURE__ */ jsx4(
|
|
1010
|
+
"span",
|
|
1011
|
+
{
|
|
1012
|
+
className: "field-list-dot",
|
|
1013
|
+
style: { backgroundColor: getSignerColor(f.assignee) }
|
|
1014
|
+
}
|
|
1015
|
+
),
|
|
1016
|
+
/* @__PURE__ */ jsx4("span", { className: "field-list-name", children: f.label }),
|
|
1017
|
+
/* @__PURE__ */ jsxs4("span", { className: "field-list-page", children: [
|
|
1018
|
+
"p",
|
|
1019
|
+
f.page + 1
|
|
1020
|
+
] }),
|
|
1021
|
+
f.required && /* @__PURE__ */ jsx4("span", { className: "field-list-required", children: "*" })
|
|
1022
|
+
]
|
|
1023
|
+
},
|
|
1024
|
+
f.id
|
|
1025
|
+
)) }) })
|
|
1026
|
+
] }),
|
|
1027
|
+
/* @__PURE__ */ jsxs4("div", { className: "powered-by", children: [
|
|
1028
|
+
"Powered by ",
|
|
1029
|
+
/* @__PURE__ */ jsx4("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
|
|
1030
|
+
] })
|
|
849
1031
|
] })
|
|
850
1032
|
] })
|
|
851
|
-
] }),
|
|
852
|
-
/* @__PURE__ */ jsxs5("div", { className: "powered-by", children: [
|
|
853
|
-
"Powered by ",
|
|
854
|
-
/* @__PURE__ */ jsx5("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
|
|
855
1033
|
] })
|
|
856
1034
|
] });
|
|
857
1035
|
}
|
|
858
1036
|
|
|
859
1037
|
// src/components/pdf-builder/SignerView.tsx
|
|
860
|
-
import { useState as
|
|
1038
|
+
import { useState as useState3, useCallback as useCallback4, useEffect as useEffect3, useRef as useRef4 } from "react";
|
|
861
1039
|
|
|
862
1040
|
// src/utils/pdfFiller.ts
|
|
863
1041
|
import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
|
|
@@ -872,8 +1050,13 @@ async function generateFilledPdf(pdfSource, fields) {
|
|
|
872
1050
|
const pdfDoc = await PDFDocument.load(pdfBytes);
|
|
873
1051
|
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
|
874
1052
|
const pages = pdfDoc.getPages();
|
|
1053
|
+
function hexToRgb(hex) {
|
|
1054
|
+
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
1055
|
+
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
|
1056
|
+
const b = parseInt(hex.slice(5, 7), 16) / 255;
|
|
1057
|
+
return rgb(r, g, b);
|
|
1058
|
+
}
|
|
875
1059
|
for (const field of fields) {
|
|
876
|
-
if (!field.value) continue;
|
|
877
1060
|
const page = pages[field.page];
|
|
878
1061
|
if (!page) continue;
|
|
879
1062
|
const pageWidth = page.getWidth();
|
|
@@ -882,6 +1065,18 @@ async function generateFilledPdf(pdfSource, fields) {
|
|
|
882
1065
|
const y = pageHeight - field.y / 100 * pageHeight - field.height / 100 * pageHeight;
|
|
883
1066
|
const w = field.width / 100 * pageWidth;
|
|
884
1067
|
const h = field.height / 100 * pageHeight;
|
|
1068
|
+
if (field.type === "blackout" || field.type === "whiteout") {
|
|
1069
|
+
page.drawRectangle({
|
|
1070
|
+
x,
|
|
1071
|
+
y,
|
|
1072
|
+
width: w,
|
|
1073
|
+
height: h,
|
|
1074
|
+
color: field.type === "blackout" ? rgb(0, 0, 0) : rgb(1, 1, 1)
|
|
1075
|
+
});
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
if (!field.value) continue;
|
|
1079
|
+
const inkColor = field.inkColor ? hexToRgb(field.inkColor) : rgb(0, 0, 0);
|
|
885
1080
|
if (field.type === "checkbox") {
|
|
886
1081
|
if (field.value === "true") {
|
|
887
1082
|
const inset = Math.min(w, h) * 0.2;
|
|
@@ -890,13 +1085,13 @@ async function generateFilledPdf(pdfSource, fields) {
|
|
|
890
1085
|
start: { x: x + inset, y: y + inset },
|
|
891
1086
|
end: { x: x + w - inset, y: y + h - inset },
|
|
892
1087
|
thickness: lineWidth,
|
|
893
|
-
color:
|
|
1088
|
+
color: inkColor
|
|
894
1089
|
});
|
|
895
1090
|
page.drawLine({
|
|
896
1091
|
start: { x: x + w - inset, y: y + inset },
|
|
897
1092
|
end: { x: x + inset, y: y + h - inset },
|
|
898
1093
|
thickness: lineWidth,
|
|
899
|
-
color:
|
|
1094
|
+
color: inkColor
|
|
900
1095
|
});
|
|
901
1096
|
}
|
|
902
1097
|
} else if (field.type === "signature" || field.type === "initials") {
|
|
@@ -920,7 +1115,7 @@ async function generateFilledPdf(pdfSource, fields) {
|
|
|
920
1115
|
y: y + h * 0.3,
|
|
921
1116
|
size: fontSize,
|
|
922
1117
|
font,
|
|
923
|
-
color:
|
|
1118
|
+
color: inkColor,
|
|
924
1119
|
maxWidth: w - 4
|
|
925
1120
|
});
|
|
926
1121
|
}
|
|
@@ -947,7 +1142,7 @@ async function postPdfToCallback(bytes, callbackUrl, filename) {
|
|
|
947
1142
|
}
|
|
948
1143
|
|
|
949
1144
|
// src/components/pdf-builder/FieldNavigator.tsx
|
|
950
|
-
import { jsx as
|
|
1145
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
951
1146
|
function FieldNavigator({
|
|
952
1147
|
fields,
|
|
953
1148
|
currentFieldId,
|
|
@@ -973,15 +1168,15 @@ function FieldNavigator({
|
|
|
973
1168
|
if (f.type === "checkbox") return true;
|
|
974
1169
|
return !!f.value;
|
|
975
1170
|
}).length;
|
|
976
|
-
return /* @__PURE__ */
|
|
977
|
-
/* @__PURE__ */
|
|
1171
|
+
return /* @__PURE__ */ jsxs5("div", { className: "field-navigator", children: [
|
|
1172
|
+
/* @__PURE__ */ jsxs5("div", { className: "field-navigator-progress", children: [
|
|
978
1173
|
filledCount,
|
|
979
1174
|
" of ",
|
|
980
1175
|
fields.length,
|
|
981
1176
|
" fields completed"
|
|
982
1177
|
] }),
|
|
983
|
-
/* @__PURE__ */
|
|
984
|
-
/* @__PURE__ */
|
|
1178
|
+
/* @__PURE__ */ jsxs5("div", { className: "field-navigator-controls", children: [
|
|
1179
|
+
/* @__PURE__ */ jsx5(
|
|
985
1180
|
"button",
|
|
986
1181
|
{
|
|
987
1182
|
onClick: handlePrev,
|
|
@@ -990,8 +1185,8 @@ function FieldNavigator({
|
|
|
990
1185
|
children: "Prev"
|
|
991
1186
|
}
|
|
992
1187
|
),
|
|
993
|
-
/* @__PURE__ */
|
|
994
|
-
/* @__PURE__ */
|
|
1188
|
+
/* @__PURE__ */ jsx5("span", { className: "field-navigator-position", children: currentIndex >= 0 ? `${currentIndex + 1} / ${fields.length}` : "-" }),
|
|
1189
|
+
/* @__PURE__ */ jsx5(
|
|
995
1190
|
"button",
|
|
996
1191
|
{
|
|
997
1192
|
onClick: handleNext,
|
|
@@ -1001,7 +1196,7 @@ function FieldNavigator({
|
|
|
1001
1196
|
}
|
|
1002
1197
|
)
|
|
1003
1198
|
] }),
|
|
1004
|
-
/* @__PURE__ */
|
|
1199
|
+
/* @__PURE__ */ jsx5(
|
|
1005
1200
|
"button",
|
|
1006
1201
|
{
|
|
1007
1202
|
onClick: onSubmit,
|
|
@@ -1014,26 +1209,49 @@ function FieldNavigator({
|
|
|
1014
1209
|
}
|
|
1015
1210
|
|
|
1016
1211
|
// src/components/pdf-builder/SignerView.tsx
|
|
1017
|
-
import { jsx as
|
|
1212
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1018
1213
|
function SignerView({
|
|
1214
|
+
apiKey,
|
|
1019
1215
|
initialPdfUrl,
|
|
1020
1216
|
initialTemplate,
|
|
1021
1217
|
initialSigner,
|
|
1022
1218
|
callbackUrl: initialCallbackUrl,
|
|
1023
|
-
onComplete
|
|
1219
|
+
onComplete,
|
|
1220
|
+
initialValues
|
|
1024
1221
|
} = {}) {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1222
|
+
if (!isValidApiKey(apiKey)) {
|
|
1223
|
+
return /* @__PURE__ */ jsx6("div", { className: "signer-layout", children: /* @__PURE__ */ jsxs6("div", { className: "empty-state", children: [
|
|
1224
|
+
/* @__PURE__ */ jsx6("h2", { children: "Invalid API Key" }),
|
|
1225
|
+
/* @__PURE__ */ jsxs6("p", { children: [
|
|
1226
|
+
"A valid API key is required to use Exeq. Visit ",
|
|
1227
|
+
/* @__PURE__ */ jsx6("a", { href: "https://exeq.org", children: "exeq.org" }),
|
|
1228
|
+
" to get one."
|
|
1229
|
+
] })
|
|
1230
|
+
] }) });
|
|
1231
|
+
}
|
|
1232
|
+
const [pages, setPages] = useState3([]);
|
|
1233
|
+
const [fields, setFields] = useState3([]);
|
|
1234
|
+
const [selectedFieldId, setSelectedFieldId] = useState3(null);
|
|
1235
|
+
const [signer, setSigner] = useState3(initialSigner || "Signer 1");
|
|
1236
|
+
const [loading, setLoading] = useState3(false);
|
|
1237
|
+
const [submitting, setSubmitting] = useState3(false);
|
|
1238
|
+
const [pdfSource, setPdfSource] = useState3(null);
|
|
1239
|
+
const [callbackUrl, setCallbackUrl] = useState3(initialCallbackUrl || "");
|
|
1240
|
+
const containerRef = useRef4(null);
|
|
1034
1241
|
useEffect3(() => {
|
|
1035
1242
|
if (initialTemplate) {
|
|
1036
|
-
|
|
1243
|
+
let templateFields = initialTemplate.fields;
|
|
1244
|
+
if (initialValues) {
|
|
1245
|
+
templateFields = templateFields.map((f) => {
|
|
1246
|
+
const byLabel = Object.entries(initialValues).find(
|
|
1247
|
+
([key]) => key.toLowerCase() === f.label.toLowerCase()
|
|
1248
|
+
);
|
|
1249
|
+
const byId = initialValues[f.id];
|
|
1250
|
+
const value = byLabel?.[1] ?? byId;
|
|
1251
|
+
return value !== void 0 ? { ...f, value } : f;
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
setFields(templateFields);
|
|
1037
1255
|
if (initialPdfUrl) loadPdf(initialPdfUrl);
|
|
1038
1256
|
return;
|
|
1039
1257
|
}
|
|
@@ -1071,7 +1289,12 @@ function SignerView({
|
|
|
1071
1289
|
setLoading(false);
|
|
1072
1290
|
}
|
|
1073
1291
|
}, []);
|
|
1074
|
-
const editableFields = fields.filter((f) => f.assignee === signer)
|
|
1292
|
+
const editableFields = fields.filter((f) => f.assignee === signer).sort((a, b) => {
|
|
1293
|
+
if (a.page !== b.page) return a.page - b.page;
|
|
1294
|
+
const bandThreshold = 2;
|
|
1295
|
+
if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
|
|
1296
|
+
return a.x - b.x;
|
|
1297
|
+
});
|
|
1075
1298
|
const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
|
|
1076
1299
|
const isFieldEditable = selectedField ? selectedField.assignee === signer : false;
|
|
1077
1300
|
const handleFieldUpdate = useCallback4((id, value) => {
|
|
@@ -1116,21 +1339,21 @@ function SignerView({
|
|
|
1116
1339
|
const editable = field.assignee === signer;
|
|
1117
1340
|
if (!editable) {
|
|
1118
1341
|
if (field.type === "signature" || field.type === "initials") {
|
|
1119
|
-
return field.value ? /* @__PURE__ */
|
|
1342
|
+
return field.value ? /* @__PURE__ */ jsx6("img", { src: field.value, alt: field.label, className: "field-signature-preview" }) : /* @__PURE__ */ jsx6("div", { className: "field-overlay-placeholder readonly", children: field.placeholder });
|
|
1120
1343
|
}
|
|
1121
1344
|
if (field.type === "checkbox") {
|
|
1122
|
-
return /* @__PURE__ */
|
|
1345
|
+
return /* @__PURE__ */ jsx6("div", { className: "field-checkbox-display readonly", children: field.value === "true" ? "\u2713" : "" });
|
|
1123
1346
|
}
|
|
1124
|
-
return /* @__PURE__ */
|
|
1347
|
+
return /* @__PURE__ */ jsx6("div", { className: "field-overlay-placeholder readonly", children: field.value || field.placeholder });
|
|
1125
1348
|
}
|
|
1126
1349
|
if (field.type === "signature" || field.type === "initials") {
|
|
1127
1350
|
if (field.value) {
|
|
1128
|
-
return /* @__PURE__ */
|
|
1351
|
+
return /* @__PURE__ */ jsx6("div", { className: "field-signature-filled", onClick: () => handleFieldUpdate(field.id, ""), children: /* @__PURE__ */ jsx6("img", { src: field.value, alt: field.label, className: "field-signature-preview" }) });
|
|
1129
1352
|
}
|
|
1130
|
-
return /* @__PURE__ */
|
|
1353
|
+
return /* @__PURE__ */ jsx6("div", { className: "field-overlay-placeholder editable", children: field.placeholder });
|
|
1131
1354
|
}
|
|
1132
1355
|
if (field.type === "checkbox") {
|
|
1133
|
-
return /* @__PURE__ */
|
|
1356
|
+
return /* @__PURE__ */ jsx6(
|
|
1134
1357
|
"div",
|
|
1135
1358
|
{
|
|
1136
1359
|
className: "field-checkbox-display editable",
|
|
@@ -1143,9 +1366,9 @@ function SignerView({
|
|
|
1143
1366
|
);
|
|
1144
1367
|
}
|
|
1145
1368
|
if (field.type === "signed-date") {
|
|
1146
|
-
return /* @__PURE__ */
|
|
1369
|
+
return /* @__PURE__ */ jsx6("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
|
|
1147
1370
|
}
|
|
1148
|
-
return /* @__PURE__ */
|
|
1371
|
+
return /* @__PURE__ */ jsx6(
|
|
1149
1372
|
"input",
|
|
1150
1373
|
{
|
|
1151
1374
|
type: field.textSubtype === "email" ? "email" : field.textSubtype === "number" ? "number" : field.textSubtype === "phone" ? "tel" : field.textSubtype === "date" ? "date" : "text",
|
|
@@ -1170,10 +1393,10 @@ function SignerView({
|
|
|
1170
1393
|
}));
|
|
1171
1394
|
}
|
|
1172
1395
|
}, [fields.filter((f) => f.type === "signature" && f.value).length]);
|
|
1173
|
-
return /* @__PURE__ */
|
|
1174
|
-
loading && /* @__PURE__ */
|
|
1175
|
-
/* @__PURE__ */
|
|
1176
|
-
/* @__PURE__ */
|
|
1396
|
+
return /* @__PURE__ */ jsxs6("div", { className: "signer-layout", ref: containerRef, children: [
|
|
1397
|
+
loading && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Loading document..." }),
|
|
1398
|
+
/* @__PURE__ */ jsxs6("div", { className: "signer-content", children: [
|
|
1399
|
+
/* @__PURE__ */ jsx6("div", { className: "signer-pdf-area", children: pages.length > 0 && /* @__PURE__ */ jsx6(
|
|
1177
1400
|
PdfViewer,
|
|
1178
1401
|
{
|
|
1179
1402
|
pages,
|
|
@@ -1185,11 +1408,11 @@ function SignerView({
|
|
|
1185
1408
|
renderFieldContent
|
|
1186
1409
|
}
|
|
1187
1410
|
) }),
|
|
1188
|
-
/* @__PURE__ */
|
|
1189
|
-
selectedField && isFieldEditable && /* @__PURE__ */
|
|
1190
|
-
/* @__PURE__ */
|
|
1191
|
-
selectedField.required && /* @__PURE__ */
|
|
1192
|
-
(selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */
|
|
1411
|
+
/* @__PURE__ */ jsxs6("div", { className: "signer-panel", children: [
|
|
1412
|
+
selectedField && isFieldEditable && /* @__PURE__ */ jsxs6("div", { className: "signer-field-input", children: [
|
|
1413
|
+
/* @__PURE__ */ jsx6("h3", { children: selectedField.label }),
|
|
1414
|
+
selectedField.required && /* @__PURE__ */ jsx6("span", { className: "required-badge", children: "Required" }),
|
|
1415
|
+
(selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */ jsx6(
|
|
1193
1416
|
SignatureCanvas,
|
|
1194
1417
|
{
|
|
1195
1418
|
width: 280,
|
|
@@ -1198,7 +1421,7 @@ function SignerView({
|
|
|
1198
1421
|
initialValue: selectedField.value
|
|
1199
1422
|
}
|
|
1200
1423
|
),
|
|
1201
|
-
selectedField.type === "text" && /* @__PURE__ */
|
|
1424
|
+
selectedField.type === "text" && /* @__PURE__ */ jsx6(
|
|
1202
1425
|
"input",
|
|
1203
1426
|
{
|
|
1204
1427
|
type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
|
|
@@ -1208,8 +1431,8 @@ function SignerView({
|
|
|
1208
1431
|
className: "signer-text-input"
|
|
1209
1432
|
}
|
|
1210
1433
|
),
|
|
1211
|
-
selectedField.type === "checkbox" && /* @__PURE__ */
|
|
1212
|
-
/* @__PURE__ */
|
|
1434
|
+
selectedField.type === "checkbox" && /* @__PURE__ */ jsxs6("label", { className: "signer-checkbox-label", children: [
|
|
1435
|
+
/* @__PURE__ */ jsx6(
|
|
1213
1436
|
"input",
|
|
1214
1437
|
{
|
|
1215
1438
|
type: "checkbox",
|
|
@@ -1219,14 +1442,14 @@ function SignerView({
|
|
|
1219
1442
|
),
|
|
1220
1443
|
selectedField.placeholder || "Check this box"
|
|
1221
1444
|
] }),
|
|
1222
|
-
selectedField.type === "signed-date" && /* @__PURE__ */
|
|
1445
|
+
selectedField.type === "signed-date" && /* @__PURE__ */ jsx6("div", { className: "signer-date-display", children: selectedField.value || "Will auto-fill when you sign" })
|
|
1223
1446
|
] }),
|
|
1224
|
-
selectedField && !isFieldEditable && /* @__PURE__ */
|
|
1225
|
-
/* @__PURE__ */
|
|
1226
|
-
/* @__PURE__ */
|
|
1447
|
+
selectedField && !isFieldEditable && /* @__PURE__ */ jsxs6("div", { className: "signer-field-readonly", children: [
|
|
1448
|
+
/* @__PURE__ */ jsx6("h3", { children: selectedField.label }),
|
|
1449
|
+
/* @__PURE__ */ jsx6("p", { children: "This field belongs to another signer." })
|
|
1227
1450
|
] }),
|
|
1228
|
-
!selectedField && editableFields.length > 0 && /* @__PURE__ */
|
|
1229
|
-
/* @__PURE__ */
|
|
1451
|
+
!selectedField && editableFields.length > 0 && /* @__PURE__ */ jsx6("div", { className: "panel-empty", children: "Select a field to fill it in, or use the navigation below." }),
|
|
1452
|
+
/* @__PURE__ */ jsx6(
|
|
1230
1453
|
FieldNavigator,
|
|
1231
1454
|
{
|
|
1232
1455
|
fields: editableFields,
|
|
@@ -1236,12 +1459,87 @@ function SignerView({
|
|
|
1236
1459
|
onSubmit: handleSubmit
|
|
1237
1460
|
}
|
|
1238
1461
|
),
|
|
1239
|
-
submitting && /* @__PURE__ */
|
|
1462
|
+
submitting && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Generating PDF..." })
|
|
1240
1463
|
] })
|
|
1241
1464
|
] }),
|
|
1242
|
-
/* @__PURE__ */
|
|
1465
|
+
/* @__PURE__ */ jsxs6("div", { className: "powered-by", children: [
|
|
1243
1466
|
"Powered by ",
|
|
1244
|
-
/* @__PURE__ */
|
|
1467
|
+
/* @__PURE__ */ jsx6("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
|
|
1468
|
+
] })
|
|
1469
|
+
] });
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
// src/components/pdf-builder/SignerRoleSelector.tsx
|
|
1473
|
+
import { useState as useState4 } from "react";
|
|
1474
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1475
|
+
function SignerRoleSelector({
|
|
1476
|
+
roles,
|
|
1477
|
+
activeRole,
|
|
1478
|
+
onSetActiveRole,
|
|
1479
|
+
onAddRole,
|
|
1480
|
+
onRemoveRole
|
|
1481
|
+
}) {
|
|
1482
|
+
const [isAdding, setIsAdding] = useState4(false);
|
|
1483
|
+
const [newRoleName, setNewRoleName] = useState4("");
|
|
1484
|
+
const handleAdd = () => {
|
|
1485
|
+
if (newRoleName.trim()) {
|
|
1486
|
+
onAddRole(newRoleName.trim());
|
|
1487
|
+
setNewRoleName("");
|
|
1488
|
+
setIsAdding(false);
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
return /* @__PURE__ */ jsxs7("div", { className: "signer-role-selector", children: [
|
|
1492
|
+
/* @__PURE__ */ jsx7("div", { className: "signer-role-label", children: "Signer Roles" }),
|
|
1493
|
+
/* @__PURE__ */ jsxs7("div", { className: "signer-role-list", children: [
|
|
1494
|
+
roles.map((role) => /* @__PURE__ */ jsxs7(
|
|
1495
|
+
"button",
|
|
1496
|
+
{
|
|
1497
|
+
className: `signer-role-chip ${role === activeRole ? "active" : ""}`,
|
|
1498
|
+
style: {
|
|
1499
|
+
borderColor: getSignerColor(role),
|
|
1500
|
+
backgroundColor: role === activeRole ? getSignerColor(role) : "transparent",
|
|
1501
|
+
color: role === activeRole ? "#fff" : getSignerColor(role)
|
|
1502
|
+
},
|
|
1503
|
+
onClick: () => onSetActiveRole(role),
|
|
1504
|
+
children: [
|
|
1505
|
+
role,
|
|
1506
|
+
roles.length > 1 && role !== "Sender" && /* @__PURE__ */ jsx7(
|
|
1507
|
+
"span",
|
|
1508
|
+
{
|
|
1509
|
+
className: "signer-role-remove",
|
|
1510
|
+
onClick: (e) => {
|
|
1511
|
+
e.stopPropagation();
|
|
1512
|
+
onRemoveRole(role);
|
|
1513
|
+
},
|
|
1514
|
+
children: "\xD7"
|
|
1515
|
+
}
|
|
1516
|
+
)
|
|
1517
|
+
]
|
|
1518
|
+
},
|
|
1519
|
+
role
|
|
1520
|
+
)),
|
|
1521
|
+
isAdding ? /* @__PURE__ */ jsxs7("div", { className: "signer-role-add-input", children: [
|
|
1522
|
+
/* @__PURE__ */ jsx7(
|
|
1523
|
+
"input",
|
|
1524
|
+
{
|
|
1525
|
+
type: "text",
|
|
1526
|
+
value: newRoleName,
|
|
1527
|
+
onChange: (e) => setNewRoleName(e.target.value),
|
|
1528
|
+
onKeyDown: (e) => e.key === "Enter" && handleAdd(),
|
|
1529
|
+
placeholder: "Role name",
|
|
1530
|
+
autoFocus: true
|
|
1531
|
+
}
|
|
1532
|
+
),
|
|
1533
|
+
/* @__PURE__ */ jsx7("button", { onClick: handleAdd, children: "Add" }),
|
|
1534
|
+
/* @__PURE__ */ jsx7("button", { onClick: () => setIsAdding(false), children: "Cancel" })
|
|
1535
|
+
] }) : /* @__PURE__ */ jsx7(
|
|
1536
|
+
"button",
|
|
1537
|
+
{
|
|
1538
|
+
className: "signer-role-add-btn",
|
|
1539
|
+
onClick: () => setIsAdding(true),
|
|
1540
|
+
children: "+ Add Role"
|
|
1541
|
+
}
|
|
1542
|
+
)
|
|
1245
1543
|
] })
|
|
1246
1544
|
] });
|
|
1247
1545
|
}
|
|
@@ -1261,6 +1559,7 @@ export {
|
|
|
1261
1559
|
generateFilledPdf,
|
|
1262
1560
|
getSignerColor,
|
|
1263
1561
|
postPdfToCallback,
|
|
1264
|
-
renderPdfPages
|
|
1562
|
+
renderPdfPages,
|
|
1563
|
+
uniqueLabel
|
|
1265
1564
|
};
|
|
1266
1565
|
//# sourceMappingURL=index.mjs.map
|