@unlev/exeq 0.1.2 → 0.1.4
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 +74 -88
- package/dist/index.css +289 -5
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +22 -7
- package/dist/index.d.ts +22 -7
- package/dist/index.js +627 -339
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +599 -312
- 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,28 +569,67 @@ 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 = new Set([
|
|
581
|
+
"exeq_live_2024",
|
|
582
|
+
process.env.NEXT_PUBLIC_EXEQ_API_KEY
|
|
583
|
+
].filter(Boolean));
|
|
584
|
+
function isValidApiKey(key) {
|
|
585
|
+
if (!key) return false;
|
|
586
|
+
return VALID_KEYS.has(key);
|
|
587
|
+
}
|
|
588
|
+
|
|
612
589
|
// src/components/pdf-builder/DesignerView.tsx
|
|
613
|
-
import { Fragment, jsx as
|
|
590
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
591
|
+
var FIELD_TYPE_META = [
|
|
592
|
+
{ type: "text", label: "Text", icon: "T" },
|
|
593
|
+
{ type: "signature", label: "Signature", icon: "\u270D" },
|
|
594
|
+
{ type: "signed-date", label: "Date", icon: "\u{1F4C5}" },
|
|
595
|
+
{ type: "checkbox", label: "Checkbox", icon: "\u2611" },
|
|
596
|
+
{ type: "initials", label: "Initials", icon: "IN" },
|
|
597
|
+
{ type: "blackout", label: "Blackout", icon: "\u25A0", group: "redact" },
|
|
598
|
+
{ type: "whiteout", label: "Whiteout", icon: "\u25A1", group: "redact" }
|
|
599
|
+
];
|
|
614
600
|
function DesignerView({
|
|
601
|
+
apiKey,
|
|
615
602
|
initialPdfUrl,
|
|
616
603
|
initialTemplate,
|
|
617
|
-
onSave
|
|
604
|
+
onSave,
|
|
605
|
+
hideHeader,
|
|
606
|
+
headerPortalRef
|
|
618
607
|
} = {}) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
608
|
+
if (!isValidApiKey(apiKey)) {
|
|
609
|
+
return /* @__PURE__ */ jsx4("div", { className: "designer-layout", children: /* @__PURE__ */ jsxs4("div", { className: "empty-state", children: [
|
|
610
|
+
/* @__PURE__ */ jsx4("h2", { children: "Invalid API Key" }),
|
|
611
|
+
/* @__PURE__ */ jsxs4("p", { children: [
|
|
612
|
+
"A valid API key is required to use Exeq. Visit ",
|
|
613
|
+
/* @__PURE__ */ jsx4("a", { href: "https://exeq.org", children: "exeq.org" }),
|
|
614
|
+
" to get one."
|
|
615
|
+
] })
|
|
616
|
+
] }) });
|
|
617
|
+
}
|
|
618
|
+
const [pages, setPages] = useState2([]);
|
|
619
|
+
const [fields, setFields] = useState2(initialTemplate?.fields ?? []);
|
|
620
|
+
const [selectedFieldId, setSelectedFieldId] = useState2(null);
|
|
621
|
+
const [signerRoles, setSignerRoles] = useState2(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
|
|
622
|
+
const [activeRole, setActiveRole] = useState2("Sender");
|
|
623
|
+
const [activeFieldType, setActiveFieldType] = useState2("text");
|
|
624
|
+
const [loading, setLoading] = useState2(false);
|
|
625
|
+
const [pdfSource, setPdfSource] = useState2(null);
|
|
626
|
+
const [rightTab, setRightTab] = useState2("properties");
|
|
627
|
+
const [isAddingRole, setIsAddingRole] = useState2(false);
|
|
628
|
+
const [newRoleName, setNewRoleName] = useState2("");
|
|
629
|
+
const [draggingFieldType, setDraggingFieldType] = useState2(null);
|
|
630
|
+
const [panelWidth, setPanelWidth] = useState2(380);
|
|
631
|
+
const dragGhostRef = useRef3(null);
|
|
632
|
+
const resizingRef = useRef3(false);
|
|
627
633
|
useEffect2(() => {
|
|
628
634
|
const pdfUrl = initialPdfUrl || initialTemplate?.pdfUrl;
|
|
629
635
|
if (pdfUrl) {
|
|
@@ -670,10 +676,11 @@ function DesignerView({
|
|
|
670
676
|
reader.readAsArrayBuffer(file);
|
|
671
677
|
}, [loadPdf]);
|
|
672
678
|
const handlePageClick = useCallback3((page, x, y) => {
|
|
673
|
-
const field = createField(activeFieldType, activeRole, page, x, y);
|
|
679
|
+
const field = createField(activeFieldType, activeRole, page, x, y, fields);
|
|
674
680
|
setFields((prev) => [...prev, field]);
|
|
675
681
|
setSelectedFieldId(field.id);
|
|
676
|
-
|
|
682
|
+
setRightTab("properties");
|
|
683
|
+
}, [activeFieldType, activeRole, fields]);
|
|
677
684
|
const handleFieldMove = useCallback3((id, page, x, y) => {
|
|
678
685
|
setFields((prev) => prev.map((f) => f.id === id ? { ...f, page, x, y } : f));
|
|
679
686
|
}, []);
|
|
@@ -681,17 +688,26 @@ function DesignerView({
|
|
|
681
688
|
setFields((prev) => prev.map((f) => f.id === id ? { ...f, width, height } : f));
|
|
682
689
|
}, []);
|
|
683
690
|
const handleFieldUpdate = useCallback3((id, updates) => {
|
|
684
|
-
setFields((prev) =>
|
|
691
|
+
setFields((prev) => {
|
|
692
|
+
if (updates.label !== void 0) {
|
|
693
|
+
const otherLabels = prev.filter((f) => f.id !== id).map((f) => f.label);
|
|
694
|
+
updates.label = uniqueLabel(updates.label, otherLabels);
|
|
695
|
+
}
|
|
696
|
+
return prev.map((f) => f.id === id ? { ...f, ...updates } : f);
|
|
697
|
+
});
|
|
685
698
|
}, []);
|
|
686
699
|
const handleFieldDelete = useCallback3((id) => {
|
|
687
700
|
setFields((prev) => prev.filter((f) => f.id !== id));
|
|
688
701
|
if (selectedFieldId === id) setSelectedFieldId(null);
|
|
689
702
|
}, [selectedFieldId]);
|
|
690
|
-
const handleAddRole = useCallback3((
|
|
691
|
-
|
|
692
|
-
|
|
703
|
+
const handleAddRole = useCallback3(() => {
|
|
704
|
+
const name = newRoleName.trim();
|
|
705
|
+
if (name && !signerRoles.includes(name)) {
|
|
706
|
+
setSignerRoles((prev) => [...prev, name]);
|
|
693
707
|
}
|
|
694
|
-
|
|
708
|
+
setNewRoleName("");
|
|
709
|
+
setIsAddingRole(false);
|
|
710
|
+
}, [newRoleName, signerRoles]);
|
|
695
711
|
const handleRemoveRole = useCallback3((role) => {
|
|
696
712
|
setSignerRoles((prev) => prev.filter((r) => r !== role));
|
|
697
713
|
setFields((prev) => prev.map((f) => f.assignee === role ? { ...f, assignee: signerRoles[0] } : f));
|
|
@@ -717,160 +733,310 @@ function DesignerView({
|
|
|
717
733
|
}
|
|
718
734
|
window.parent?.postMessage({ type: "template-saved", template }, "*");
|
|
719
735
|
}, [fields, signerRoles, pdfSource, onSave]);
|
|
736
|
+
const handlePaletteDragStart = useCallback3((e, type) => {
|
|
737
|
+
setDraggingFieldType(type);
|
|
738
|
+
e.dataTransfer.setData("application/exeq-field-type", type);
|
|
739
|
+
e.dataTransfer.effectAllowed = "copy";
|
|
740
|
+
const ghost = document.createElement("div");
|
|
741
|
+
ghost.className = "palette-drag-ghost";
|
|
742
|
+
ghost.textContent = FIELD_TYPE_META.find((f) => f.type === type)?.label || type;
|
|
743
|
+
ghost.style.cssText = `
|
|
744
|
+
position: fixed; top: -200px; left: -200px;
|
|
745
|
+
padding: 6px 16px; background: ${getSignerColor(activeRole)};
|
|
746
|
+
color: #fff; border-radius: 4px; font-size: 12px;
|
|
747
|
+
font-family: system-ui; pointer-events: none; white-space: nowrap;
|
|
748
|
+
`;
|
|
749
|
+
document.body.appendChild(ghost);
|
|
750
|
+
e.dataTransfer.setDragImage(ghost, 40, 16);
|
|
751
|
+
dragGhostRef.current = ghost;
|
|
752
|
+
}, [activeRole]);
|
|
753
|
+
const handlePaletteDragEnd = useCallback3(() => {
|
|
754
|
+
setDraggingFieldType(null);
|
|
755
|
+
if (dragGhostRef.current) {
|
|
756
|
+
document.body.removeChild(dragGhostRef.current);
|
|
757
|
+
dragGhostRef.current = null;
|
|
758
|
+
}
|
|
759
|
+
}, []);
|
|
760
|
+
const handleDropOnPage = useCallback3((page, x, y, fieldType) => {
|
|
761
|
+
const field = createField(fieldType, activeRole, page, x, y, fields);
|
|
762
|
+
setFields((prev) => [...prev, field]);
|
|
763
|
+
setSelectedFieldId(field.id);
|
|
764
|
+
setRightTab("properties");
|
|
765
|
+
}, [activeRole, fields]);
|
|
766
|
+
useEffect2(() => {
|
|
767
|
+
const handleKeyDown = (e) => {
|
|
768
|
+
if (e.key !== "Delete" && e.key !== "Backspace") return;
|
|
769
|
+
if (!selectedFieldId) return;
|
|
770
|
+
const tag = (document.activeElement?.tagName || "").toLowerCase();
|
|
771
|
+
if (tag === "input" || tag === "textarea" || tag === "select") return;
|
|
772
|
+
if (document.activeElement?.isContentEditable) return;
|
|
773
|
+
e.preventDefault();
|
|
774
|
+
if (window.confirm("Delete this field?")) {
|
|
775
|
+
handleFieldDelete(selectedFieldId);
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
779
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
780
|
+
}, [selectedFieldId, handleFieldDelete]);
|
|
781
|
+
const handleResizeStart = useCallback3((e) => {
|
|
782
|
+
e.preventDefault();
|
|
783
|
+
resizingRef.current = true;
|
|
784
|
+
const startX = e.clientX;
|
|
785
|
+
const startWidth = panelWidth;
|
|
786
|
+
const handleMouseMove = (e2) => {
|
|
787
|
+
if (!resizingRef.current) return;
|
|
788
|
+
const delta = startX - e2.clientX;
|
|
789
|
+
setPanelWidth(Math.max(280, Math.min(600, startWidth + delta)));
|
|
790
|
+
};
|
|
791
|
+
const handleMouseUp = () => {
|
|
792
|
+
resizingRef.current = false;
|
|
793
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
794
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
795
|
+
};
|
|
796
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
797
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
798
|
+
}, [panelWidth]);
|
|
799
|
+
const sortedFields = [...fields].sort((a, b) => {
|
|
800
|
+
if (a.page !== b.page) return a.page - b.page;
|
|
801
|
+
const bandThreshold = 2;
|
|
802
|
+
if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
|
|
803
|
+
return a.x - b.x;
|
|
804
|
+
});
|
|
720
805
|
const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
|
|
721
806
|
const renderFieldContent = useCallback3((field) => {
|
|
807
|
+
if (field.type === "blackout" || field.type === "whiteout") {
|
|
808
|
+
return null;
|
|
809
|
+
}
|
|
722
810
|
if (field.type === "signature" || field.type === "initials") {
|
|
723
811
|
if (field.value) {
|
|
724
|
-
return /* @__PURE__ */
|
|
812
|
+
return /* @__PURE__ */ jsx4("img", { src: field.value, alt: field.label, className: "field-signature-preview" });
|
|
725
813
|
}
|
|
726
814
|
}
|
|
727
|
-
|
|
815
|
+
const inkColor = field.inkColor || "#000000";
|
|
816
|
+
return /* @__PURE__ */ jsx4(
|
|
817
|
+
"div",
|
|
818
|
+
{
|
|
819
|
+
className: "field-overlay-placeholder",
|
|
820
|
+
style: { color: field.value ? inkColor : void 0, fontSize: `${field.fontSize * 0.6}px` },
|
|
821
|
+
children: field.value || field.placeholder
|
|
822
|
+
}
|
|
823
|
+
);
|
|
728
824
|
}, []);
|
|
729
|
-
|
|
730
|
-
/* @__PURE__ */
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
825
|
+
const headerButtons = pages.length > 0 ? /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
826
|
+
/* @__PURE__ */ jsxs4("label", { className: "header-btn header-btn-outline", children: [
|
|
827
|
+
"Change PDF",
|
|
828
|
+
/* @__PURE__ */ jsx4("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
|
|
829
|
+
] }),
|
|
830
|
+
/* @__PURE__ */ jsx4("button", { onClick: handleExport, className: "header-btn header-btn-primary", children: "Export Template" })
|
|
831
|
+
] }) : null;
|
|
832
|
+
return /* @__PURE__ */ jsxs4("div", { className: "designer-layout", children: [
|
|
833
|
+
headerPortalRef?.current && headerButtons && createPortal(headerButtons, headerPortalRef.current),
|
|
834
|
+
!hideHeader && !headerPortalRef && /* @__PURE__ */ jsx4("div", { className: "designer-header", children: /* @__PURE__ */ jsx4("div", { className: "designer-header-right", children: headerButtons }) }),
|
|
835
|
+
/* @__PURE__ */ jsxs4("div", { className: "designer-body", children: [
|
|
836
|
+
pages.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "designer-palette", children: [
|
|
837
|
+
/* @__PURE__ */ jsx4("div", { className: "palette-heading", children: "New Field Role" }),
|
|
838
|
+
/* @__PURE__ */ jsxs4("div", { className: "palette-role-section", children: [
|
|
839
|
+
/* @__PURE__ */ jsx4(
|
|
840
|
+
"select",
|
|
743
841
|
{
|
|
744
|
-
className:
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
842
|
+
className: "palette-role-select",
|
|
843
|
+
value: activeRole,
|
|
844
|
+
onChange: (e) => setActiveRole(e.target.value),
|
|
845
|
+
style: { borderColor: getSignerColor(activeRole), color: getSignerColor(activeRole) },
|
|
846
|
+
children: signerRoles.map((role) => /* @__PURE__ */ jsx4("option", { value: role, children: role }, role))
|
|
847
|
+
}
|
|
848
|
+
),
|
|
849
|
+
isAddingRole ? /* @__PURE__ */ jsxs4("div", { className: "palette-role-add-inline", children: [
|
|
850
|
+
/* @__PURE__ */ jsx4(
|
|
851
|
+
"input",
|
|
852
|
+
{
|
|
853
|
+
type: "text",
|
|
854
|
+
value: newRoleName,
|
|
855
|
+
onChange: (e) => setNewRoleName(e.target.value),
|
|
856
|
+
onKeyDown: (e) => e.key === "Enter" && handleAddRole(),
|
|
857
|
+
placeholder: "Role name",
|
|
858
|
+
autoFocus: true
|
|
859
|
+
}
|
|
860
|
+
),
|
|
861
|
+
/* @__PURE__ */ jsx4("button", { onClick: handleAddRole, children: "Add" }),
|
|
862
|
+
/* @__PURE__ */ jsx4("button", { onClick: () => {
|
|
863
|
+
setIsAddingRole(false);
|
|
864
|
+
setNewRoleName("");
|
|
865
|
+
}, children: "Cancel" })
|
|
866
|
+
] }) : /* @__PURE__ */ jsx4("button", { className: "palette-role-add-link", onClick: () => setIsAddingRole(true), children: "+ Add Role" })
|
|
867
|
+
] }),
|
|
868
|
+
/* @__PURE__ */ jsx4("div", { className: "palette-divider" }),
|
|
869
|
+
/* @__PURE__ */ jsx4("div", { className: "palette-heading", children: "Fields" }),
|
|
870
|
+
FIELD_TYPE_META.filter((f) => !f.group).map(({ type, label, icon }) => /* @__PURE__ */ jsxs4(
|
|
871
|
+
"div",
|
|
872
|
+
{
|
|
873
|
+
className: `palette-item ${activeFieldType === type ? "active" : ""}`,
|
|
874
|
+
draggable: true,
|
|
875
|
+
onClick: () => setActiveFieldType(type),
|
|
876
|
+
onDragStart: (e) => handlePaletteDragStart(e, type),
|
|
877
|
+
onDragEnd: handlePaletteDragEnd,
|
|
878
|
+
children: [
|
|
879
|
+
/* @__PURE__ */ jsx4("span", { className: "palette-item-icon", children: icon }),
|
|
880
|
+
/* @__PURE__ */ jsx4("span", { className: "palette-item-label", children: label })
|
|
881
|
+
]
|
|
882
|
+
},
|
|
883
|
+
type
|
|
884
|
+
)),
|
|
885
|
+
/* @__PURE__ */ jsx4("div", { className: "palette-divider" }),
|
|
886
|
+
/* @__PURE__ */ jsx4("div", { className: "palette-heading", children: "Redact" }),
|
|
887
|
+
FIELD_TYPE_META.filter((f) => f.group === "redact").map(({ type, label, icon }) => /* @__PURE__ */ jsxs4(
|
|
888
|
+
"div",
|
|
889
|
+
{
|
|
890
|
+
className: `palette-item ${activeFieldType === type ? "active" : ""}`,
|
|
891
|
+
draggable: true,
|
|
892
|
+
onClick: () => setActiveFieldType(type),
|
|
893
|
+
onDragStart: (e) => handlePaletteDragStart(e, type),
|
|
894
|
+
onDragEnd: handlePaletteDragEnd,
|
|
895
|
+
children: [
|
|
896
|
+
/* @__PURE__ */ jsx4("span", { className: "palette-item-icon", children: icon }),
|
|
897
|
+
/* @__PURE__ */ jsx4("span", { className: "palette-item-label", children: label })
|
|
898
|
+
]
|
|
899
|
+
},
|
|
900
|
+
type
|
|
901
|
+
))
|
|
751
902
|
] }),
|
|
752
|
-
/* @__PURE__ */
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
onAddRole: handleAddRole,
|
|
761
|
-
onRemoveRole: handleRemoveRole
|
|
762
|
-
}
|
|
763
|
-
),
|
|
764
|
-
/* @__PURE__ */ jsxs5("div", { className: "designer-content", children: [
|
|
765
|
-
/* @__PURE__ */ jsxs5("div", { className: "designer-pdf-area", children: [
|
|
766
|
-
loading && /* @__PURE__ */ jsx5("div", { className: "loading-indicator", children: "Loading PDF..." }),
|
|
767
|
-
!pages.length && !loading && /* @__PURE__ */ jsxs5("div", { className: "empty-state", children: [
|
|
768
|
-
/* @__PURE__ */ jsx5("h2", { children: "Upload a PDF to get started" }),
|
|
769
|
-
/* @__PURE__ */ jsx5("p", { children: "Upload a template PDF, then click on the page to place form fields." }),
|
|
770
|
-
/* @__PURE__ */ jsxs5("label", { className: "upload-btn upload-btn-large", children: [
|
|
771
|
-
"Choose PDF File",
|
|
772
|
-
/* @__PURE__ */ jsx5("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
|
|
903
|
+
/* @__PURE__ */ jsxs4("div", { className: "designer-pdf-area", children: [
|
|
904
|
+
loading && /* @__PURE__ */ jsx4("div", { className: "loading-indicator", children: "Loading PDF..." }),
|
|
905
|
+
!pages.length && !loading && /* @__PURE__ */ jsxs4("div", { className: "empty-state", children: [
|
|
906
|
+
/* @__PURE__ */ jsx4("h2", { children: "Open a PDF to get started" }),
|
|
907
|
+
/* @__PURE__ */ jsx4("p", { children: "Select a PDF from your device to begin. Your file stays on your computer \u2014 nothing is uploaded." }),
|
|
908
|
+
/* @__PURE__ */ jsxs4("label", { className: "upload-btn upload-btn-large", children: [
|
|
909
|
+
"Select PDF",
|
|
910
|
+
/* @__PURE__ */ jsx4("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
|
|
773
911
|
] })
|
|
774
912
|
] }),
|
|
775
|
-
pages.length > 0 && /* @__PURE__ */
|
|
913
|
+
pages.length > 0 && /* @__PURE__ */ jsx4(
|
|
776
914
|
PdfViewer,
|
|
777
915
|
{
|
|
778
916
|
pages,
|
|
779
917
|
fields,
|
|
780
918
|
selectedFieldId,
|
|
781
|
-
onSelectField:
|
|
919
|
+
onSelectField: (id) => {
|
|
920
|
+
setSelectedFieldId(id);
|
|
921
|
+
if (id) setRightTab("properties");
|
|
922
|
+
},
|
|
782
923
|
onFieldMove: handleFieldMove,
|
|
783
924
|
onFieldResize: handleFieldResize,
|
|
784
925
|
onPageClick: handlePageClick,
|
|
926
|
+
onDropField: handleDropOnPage,
|
|
785
927
|
mode: "designer",
|
|
786
928
|
renderFieldContent
|
|
787
929
|
}
|
|
788
930
|
)
|
|
789
931
|
] }),
|
|
790
|
-
/* @__PURE__ */
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
signerRoles,
|
|
797
|
-
onUpdate: handleFieldUpdate,
|
|
798
|
-
onDelete: handleFieldDelete
|
|
799
|
-
}
|
|
800
|
-
),
|
|
801
|
-
selectedField.assignee === activeRole && /* @__PURE__ */ jsxs5("div", { className: "prefill-section", children: [
|
|
802
|
-
/* @__PURE__ */ jsx5("h4", { children: "Pre-fill Value" }),
|
|
803
|
-
selectedField.type === "signature" || selectedField.type === "initials" ? /* @__PURE__ */ jsx5(
|
|
804
|
-
SignatureCanvas,
|
|
932
|
+
pages.length > 0 && /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
933
|
+
/* @__PURE__ */ jsx4("div", { className: "panel-resize-handle", onMouseDown: handleResizeStart }),
|
|
934
|
+
/* @__PURE__ */ jsxs4("div", { className: "designer-panel", style: { width: panelWidth }, children: [
|
|
935
|
+
/* @__PURE__ */ jsxs4("div", { className: "panel-tabs", children: [
|
|
936
|
+
/* @__PURE__ */ jsx4(
|
|
937
|
+
"button",
|
|
805
938
|
{
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
initialValue: selectedField.value
|
|
939
|
+
className: `panel-tab ${rightTab === "properties" ? "active" : ""}`,
|
|
940
|
+
onClick: () => setRightTab("properties"),
|
|
941
|
+
children: "Properties"
|
|
810
942
|
}
|
|
811
|
-
)
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
{
|
|
815
|
-
type: "checkbox",
|
|
816
|
-
checked: selectedField.value === "true",
|
|
817
|
-
onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.checked ? "true" : "" })
|
|
818
|
-
}
|
|
819
|
-
),
|
|
820
|
-
"Checked"
|
|
821
|
-
] }) : /* @__PURE__ */ jsx5(
|
|
822
|
-
"input",
|
|
943
|
+
),
|
|
944
|
+
/* @__PURE__ */ jsxs4(
|
|
945
|
+
"button",
|
|
823
946
|
{
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
947
|
+
className: `panel-tab ${rightTab === "fields" ? "active" : ""}`,
|
|
948
|
+
onClick: () => setRightTab("fields"),
|
|
949
|
+
children: [
|
|
950
|
+
"All Fields",
|
|
951
|
+
fields.length > 0 ? ` (${fields.length})` : ""
|
|
952
|
+
]
|
|
829
953
|
}
|
|
830
954
|
)
|
|
831
|
-
] })
|
|
832
|
-
] }) : /* @__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." }),
|
|
833
|
-
fields.length > 0 && /* @__PURE__ */ jsxs5("div", { className: "field-list", children: [
|
|
834
|
-
/* @__PURE__ */ jsxs5("h4", { children: [
|
|
835
|
-
"All Fields (",
|
|
836
|
-
fields.length,
|
|
837
|
-
")"
|
|
838
955
|
] }),
|
|
839
|
-
|
|
840
|
-
"
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
956
|
+
/* @__PURE__ */ jsxs4("div", { className: "panel-tab-content", children: [
|
|
957
|
+
rightTab === "properties" && /* @__PURE__ */ jsx4(Fragment, { children: selectedField ? /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
958
|
+
/* @__PURE__ */ jsx4(
|
|
959
|
+
FieldPropertyPanel,
|
|
960
|
+
{
|
|
961
|
+
field: selectedField,
|
|
962
|
+
signerRoles,
|
|
963
|
+
onUpdate: handleFieldUpdate,
|
|
964
|
+
onDelete: handleFieldDelete
|
|
965
|
+
}
|
|
966
|
+
),
|
|
967
|
+
selectedField.type !== "blackout" && selectedField.type !== "whiteout" && /* @__PURE__ */ jsxs4("div", { className: "prefill-section", children: [
|
|
968
|
+
/* @__PURE__ */ jsx4("h4", { children: "Pre-fill Value" }),
|
|
969
|
+
selectedField.type === "signature" || selectedField.type === "initials" ? /* @__PURE__ */ jsx4(
|
|
970
|
+
SignatureCanvas,
|
|
847
971
|
{
|
|
848
|
-
|
|
849
|
-
|
|
972
|
+
width: panelWidth - 40,
|
|
973
|
+
height: selectedField.type === "initials" ? 100 : 150,
|
|
974
|
+
onSign: (dataUrl) => handleFieldUpdate(selectedField.id, { value: dataUrl }),
|
|
975
|
+
initialValue: selectedField.value,
|
|
976
|
+
inkColor: selectedField.inkColor
|
|
850
977
|
}
|
|
851
|
-
),
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
978
|
+
) : selectedField.type === "checkbox" ? /* @__PURE__ */ jsxs4("label", { className: "panel-checkbox-label", children: [
|
|
979
|
+
/* @__PURE__ */ jsx4(
|
|
980
|
+
"input",
|
|
981
|
+
{
|
|
982
|
+
type: "checkbox",
|
|
983
|
+
checked: selectedField.value === "true",
|
|
984
|
+
onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.checked ? "true" : "" })
|
|
985
|
+
}
|
|
986
|
+
),
|
|
987
|
+
"Checked"
|
|
988
|
+
] }) : /* @__PURE__ */ jsx4(
|
|
989
|
+
"input",
|
|
990
|
+
{
|
|
991
|
+
type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
|
|
992
|
+
value: selectedField.value,
|
|
993
|
+
onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.value }),
|
|
994
|
+
placeholder: `Pre-fill ${selectedField.label}`,
|
|
995
|
+
className: "prefill-input",
|
|
996
|
+
style: { color: selectedField.inkColor || "#000000" }
|
|
997
|
+
}
|
|
998
|
+
)
|
|
999
|
+
] })
|
|
1000
|
+
] }) : /* @__PURE__ */ jsx4("div", { className: "panel-empty", children: "Click on the PDF to place a field, or select an existing field to edit its properties." }) }),
|
|
1001
|
+
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(
|
|
1002
|
+
"div",
|
|
1003
|
+
{
|
|
1004
|
+
className: `field-list-item ${f.id === selectedFieldId ? "active" : ""}`,
|
|
1005
|
+
onClick: () => {
|
|
1006
|
+
setSelectedFieldId(f.id);
|
|
1007
|
+
setRightTab("properties");
|
|
1008
|
+
},
|
|
1009
|
+
children: [
|
|
1010
|
+
/* @__PURE__ */ jsx4(
|
|
1011
|
+
"span",
|
|
1012
|
+
{
|
|
1013
|
+
className: "field-list-dot",
|
|
1014
|
+
style: { backgroundColor: getSignerColor(f.assignee) }
|
|
1015
|
+
}
|
|
1016
|
+
),
|
|
1017
|
+
/* @__PURE__ */ jsx4("span", { className: "field-list-name", children: f.label }),
|
|
1018
|
+
/* @__PURE__ */ jsxs4("span", { className: "field-list-page", children: [
|
|
1019
|
+
"p",
|
|
1020
|
+
f.page + 1
|
|
1021
|
+
] }),
|
|
1022
|
+
f.required && /* @__PURE__ */ jsx4("span", { className: "field-list-required", children: "*" })
|
|
1023
|
+
]
|
|
1024
|
+
},
|
|
1025
|
+
f.id
|
|
1026
|
+
)) }) })
|
|
1027
|
+
] }),
|
|
1028
|
+
/* @__PURE__ */ jsxs4("div", { className: "powered-by", children: [
|
|
1029
|
+
"Powered by ",
|
|
1030
|
+
/* @__PURE__ */ jsx4("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
|
|
1031
|
+
] })
|
|
862
1032
|
] })
|
|
863
1033
|
] })
|
|
864
|
-
] }),
|
|
865
|
-
/* @__PURE__ */ jsxs5("div", { className: "powered-by", children: [
|
|
866
|
-
"Powered by ",
|
|
867
|
-
/* @__PURE__ */ jsx5("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
|
|
868
1034
|
] })
|
|
869
1035
|
] });
|
|
870
1036
|
}
|
|
871
1037
|
|
|
872
1038
|
// src/components/pdf-builder/SignerView.tsx
|
|
873
|
-
import { useState as
|
|
1039
|
+
import { useState as useState3, useCallback as useCallback4, useEffect as useEffect3, useRef as useRef4 } from "react";
|
|
874
1040
|
|
|
875
1041
|
// src/utils/pdfFiller.ts
|
|
876
1042
|
import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
|
|
@@ -885,8 +1051,13 @@ async function generateFilledPdf(pdfSource, fields) {
|
|
|
885
1051
|
const pdfDoc = await PDFDocument.load(pdfBytes);
|
|
886
1052
|
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
|
887
1053
|
const pages = pdfDoc.getPages();
|
|
1054
|
+
function hexToRgb(hex) {
|
|
1055
|
+
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
1056
|
+
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
|
1057
|
+
const b = parseInt(hex.slice(5, 7), 16) / 255;
|
|
1058
|
+
return rgb(r, g, b);
|
|
1059
|
+
}
|
|
888
1060
|
for (const field of fields) {
|
|
889
|
-
if (!field.value) continue;
|
|
890
1061
|
const page = pages[field.page];
|
|
891
1062
|
if (!page) continue;
|
|
892
1063
|
const pageWidth = page.getWidth();
|
|
@@ -895,6 +1066,18 @@ async function generateFilledPdf(pdfSource, fields) {
|
|
|
895
1066
|
const y = pageHeight - field.y / 100 * pageHeight - field.height / 100 * pageHeight;
|
|
896
1067
|
const w = field.width / 100 * pageWidth;
|
|
897
1068
|
const h = field.height / 100 * pageHeight;
|
|
1069
|
+
if (field.type === "blackout" || field.type === "whiteout") {
|
|
1070
|
+
page.drawRectangle({
|
|
1071
|
+
x,
|
|
1072
|
+
y,
|
|
1073
|
+
width: w,
|
|
1074
|
+
height: h,
|
|
1075
|
+
color: field.type === "blackout" ? rgb(0, 0, 0) : rgb(1, 1, 1)
|
|
1076
|
+
});
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
if (!field.value) continue;
|
|
1080
|
+
const inkColor = field.inkColor ? hexToRgb(field.inkColor) : rgb(0, 0, 0);
|
|
898
1081
|
if (field.type === "checkbox") {
|
|
899
1082
|
if (field.value === "true") {
|
|
900
1083
|
const inset = Math.min(w, h) * 0.2;
|
|
@@ -903,13 +1086,13 @@ async function generateFilledPdf(pdfSource, fields) {
|
|
|
903
1086
|
start: { x: x + inset, y: y + inset },
|
|
904
1087
|
end: { x: x + w - inset, y: y + h - inset },
|
|
905
1088
|
thickness: lineWidth,
|
|
906
|
-
color:
|
|
1089
|
+
color: inkColor
|
|
907
1090
|
});
|
|
908
1091
|
page.drawLine({
|
|
909
1092
|
start: { x: x + w - inset, y: y + inset },
|
|
910
1093
|
end: { x: x + inset, y: y + h - inset },
|
|
911
1094
|
thickness: lineWidth,
|
|
912
|
-
color:
|
|
1095
|
+
color: inkColor
|
|
913
1096
|
});
|
|
914
1097
|
}
|
|
915
1098
|
} else if (field.type === "signature" || field.type === "initials") {
|
|
@@ -933,7 +1116,7 @@ async function generateFilledPdf(pdfSource, fields) {
|
|
|
933
1116
|
y: y + h * 0.3,
|
|
934
1117
|
size: fontSize,
|
|
935
1118
|
font,
|
|
936
|
-
color:
|
|
1119
|
+
color: inkColor,
|
|
937
1120
|
maxWidth: w - 4
|
|
938
1121
|
});
|
|
939
1122
|
}
|
|
@@ -960,7 +1143,7 @@ async function postPdfToCallback(bytes, callbackUrl, filename) {
|
|
|
960
1143
|
}
|
|
961
1144
|
|
|
962
1145
|
// src/components/pdf-builder/FieldNavigator.tsx
|
|
963
|
-
import { jsx as
|
|
1146
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
964
1147
|
function FieldNavigator({
|
|
965
1148
|
fields,
|
|
966
1149
|
currentFieldId,
|
|
@@ -986,15 +1169,15 @@ function FieldNavigator({
|
|
|
986
1169
|
if (f.type === "checkbox") return true;
|
|
987
1170
|
return !!f.value;
|
|
988
1171
|
}).length;
|
|
989
|
-
return /* @__PURE__ */
|
|
990
|
-
/* @__PURE__ */
|
|
1172
|
+
return /* @__PURE__ */ jsxs5("div", { className: "field-navigator", children: [
|
|
1173
|
+
/* @__PURE__ */ jsxs5("div", { className: "field-navigator-progress", children: [
|
|
991
1174
|
filledCount,
|
|
992
1175
|
" of ",
|
|
993
1176
|
fields.length,
|
|
994
1177
|
" fields completed"
|
|
995
1178
|
] }),
|
|
996
|
-
/* @__PURE__ */
|
|
997
|
-
/* @__PURE__ */
|
|
1179
|
+
/* @__PURE__ */ jsxs5("div", { className: "field-navigator-controls", children: [
|
|
1180
|
+
/* @__PURE__ */ jsx5(
|
|
998
1181
|
"button",
|
|
999
1182
|
{
|
|
1000
1183
|
onClick: handlePrev,
|
|
@@ -1003,8 +1186,8 @@ function FieldNavigator({
|
|
|
1003
1186
|
children: "Prev"
|
|
1004
1187
|
}
|
|
1005
1188
|
),
|
|
1006
|
-
/* @__PURE__ */
|
|
1007
|
-
/* @__PURE__ */
|
|
1189
|
+
/* @__PURE__ */ jsx5("span", { className: "field-navigator-position", children: currentIndex >= 0 ? `${currentIndex + 1} / ${fields.length}` : "-" }),
|
|
1190
|
+
/* @__PURE__ */ jsx5(
|
|
1008
1191
|
"button",
|
|
1009
1192
|
{
|
|
1010
1193
|
onClick: handleNext,
|
|
@@ -1014,7 +1197,7 @@ function FieldNavigator({
|
|
|
1014
1197
|
}
|
|
1015
1198
|
)
|
|
1016
1199
|
] }),
|
|
1017
|
-
/* @__PURE__ */
|
|
1200
|
+
/* @__PURE__ */ jsx5(
|
|
1018
1201
|
"button",
|
|
1019
1202
|
{
|
|
1020
1203
|
onClick: onSubmit,
|
|
@@ -1027,26 +1210,49 @@ function FieldNavigator({
|
|
|
1027
1210
|
}
|
|
1028
1211
|
|
|
1029
1212
|
// src/components/pdf-builder/SignerView.tsx
|
|
1030
|
-
import { jsx as
|
|
1213
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1031
1214
|
function SignerView({
|
|
1215
|
+
apiKey,
|
|
1032
1216
|
initialPdfUrl,
|
|
1033
1217
|
initialTemplate,
|
|
1034
1218
|
initialSigner,
|
|
1035
1219
|
callbackUrl: initialCallbackUrl,
|
|
1036
|
-
onComplete
|
|
1220
|
+
onComplete,
|
|
1221
|
+
initialValues
|
|
1037
1222
|
} = {}) {
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1223
|
+
if (!isValidApiKey(apiKey)) {
|
|
1224
|
+
return /* @__PURE__ */ jsx6("div", { className: "signer-layout", children: /* @__PURE__ */ jsxs6("div", { className: "empty-state", children: [
|
|
1225
|
+
/* @__PURE__ */ jsx6("h2", { children: "Invalid API Key" }),
|
|
1226
|
+
/* @__PURE__ */ jsxs6("p", { children: [
|
|
1227
|
+
"A valid API key is required to use Exeq. Visit ",
|
|
1228
|
+
/* @__PURE__ */ jsx6("a", { href: "https://exeq.org", children: "exeq.org" }),
|
|
1229
|
+
" to get one."
|
|
1230
|
+
] })
|
|
1231
|
+
] }) });
|
|
1232
|
+
}
|
|
1233
|
+
const [pages, setPages] = useState3([]);
|
|
1234
|
+
const [fields, setFields] = useState3([]);
|
|
1235
|
+
const [selectedFieldId, setSelectedFieldId] = useState3(null);
|
|
1236
|
+
const [signer, setSigner] = useState3(initialSigner || "Signer 1");
|
|
1237
|
+
const [loading, setLoading] = useState3(false);
|
|
1238
|
+
const [submitting, setSubmitting] = useState3(false);
|
|
1239
|
+
const [pdfSource, setPdfSource] = useState3(null);
|
|
1240
|
+
const [callbackUrl, setCallbackUrl] = useState3(initialCallbackUrl || "");
|
|
1241
|
+
const containerRef = useRef4(null);
|
|
1047
1242
|
useEffect3(() => {
|
|
1048
1243
|
if (initialTemplate) {
|
|
1049
|
-
|
|
1244
|
+
let templateFields = initialTemplate.fields;
|
|
1245
|
+
if (initialValues) {
|
|
1246
|
+
templateFields = templateFields.map((f) => {
|
|
1247
|
+
const byLabel = Object.entries(initialValues).find(
|
|
1248
|
+
([key]) => key.toLowerCase() === f.label.toLowerCase()
|
|
1249
|
+
);
|
|
1250
|
+
const byId = initialValues[f.id];
|
|
1251
|
+
const value = byLabel?.[1] ?? byId;
|
|
1252
|
+
return value !== void 0 ? { ...f, value } : f;
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
setFields(templateFields);
|
|
1050
1256
|
if (initialPdfUrl) loadPdf(initialPdfUrl);
|
|
1051
1257
|
return;
|
|
1052
1258
|
}
|
|
@@ -1084,7 +1290,12 @@ function SignerView({
|
|
|
1084
1290
|
setLoading(false);
|
|
1085
1291
|
}
|
|
1086
1292
|
}, []);
|
|
1087
|
-
const editableFields = fields.filter((f) => f.assignee === signer)
|
|
1293
|
+
const editableFields = fields.filter((f) => f.assignee === signer).sort((a, b) => {
|
|
1294
|
+
if (a.page !== b.page) return a.page - b.page;
|
|
1295
|
+
const bandThreshold = 2;
|
|
1296
|
+
if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
|
|
1297
|
+
return a.x - b.x;
|
|
1298
|
+
});
|
|
1088
1299
|
const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
|
|
1089
1300
|
const isFieldEditable = selectedField ? selectedField.assignee === signer : false;
|
|
1090
1301
|
const handleFieldUpdate = useCallback4((id, value) => {
|
|
@@ -1129,21 +1340,21 @@ function SignerView({
|
|
|
1129
1340
|
const editable = field.assignee === signer;
|
|
1130
1341
|
if (!editable) {
|
|
1131
1342
|
if (field.type === "signature" || field.type === "initials") {
|
|
1132
|
-
return field.value ? /* @__PURE__ */
|
|
1343
|
+
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 });
|
|
1133
1344
|
}
|
|
1134
1345
|
if (field.type === "checkbox") {
|
|
1135
|
-
return /* @__PURE__ */
|
|
1346
|
+
return /* @__PURE__ */ jsx6("div", { className: "field-checkbox-display readonly", children: field.value === "true" ? "\u2713" : "" });
|
|
1136
1347
|
}
|
|
1137
|
-
return /* @__PURE__ */
|
|
1348
|
+
return /* @__PURE__ */ jsx6("div", { className: "field-overlay-placeholder readonly", children: field.value || field.placeholder });
|
|
1138
1349
|
}
|
|
1139
1350
|
if (field.type === "signature" || field.type === "initials") {
|
|
1140
1351
|
if (field.value) {
|
|
1141
|
-
return /* @__PURE__ */
|
|
1352
|
+
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" }) });
|
|
1142
1353
|
}
|
|
1143
|
-
return /* @__PURE__ */
|
|
1354
|
+
return /* @__PURE__ */ jsx6("div", { className: "field-overlay-placeholder editable", children: field.placeholder });
|
|
1144
1355
|
}
|
|
1145
1356
|
if (field.type === "checkbox") {
|
|
1146
|
-
return /* @__PURE__ */
|
|
1357
|
+
return /* @__PURE__ */ jsx6(
|
|
1147
1358
|
"div",
|
|
1148
1359
|
{
|
|
1149
1360
|
className: "field-checkbox-display editable",
|
|
@@ -1156,9 +1367,9 @@ function SignerView({
|
|
|
1156
1367
|
);
|
|
1157
1368
|
}
|
|
1158
1369
|
if (field.type === "signed-date") {
|
|
1159
|
-
return /* @__PURE__ */
|
|
1370
|
+
return /* @__PURE__ */ jsx6("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
|
|
1160
1371
|
}
|
|
1161
|
-
return /* @__PURE__ */
|
|
1372
|
+
return /* @__PURE__ */ jsx6(
|
|
1162
1373
|
"input",
|
|
1163
1374
|
{
|
|
1164
1375
|
type: field.textSubtype === "email" ? "email" : field.textSubtype === "number" ? "number" : field.textSubtype === "phone" ? "tel" : field.textSubtype === "date" ? "date" : "text",
|
|
@@ -1183,10 +1394,10 @@ function SignerView({
|
|
|
1183
1394
|
}));
|
|
1184
1395
|
}
|
|
1185
1396
|
}, [fields.filter((f) => f.type === "signature" && f.value).length]);
|
|
1186
|
-
return /* @__PURE__ */
|
|
1187
|
-
loading && /* @__PURE__ */
|
|
1188
|
-
/* @__PURE__ */
|
|
1189
|
-
/* @__PURE__ */
|
|
1397
|
+
return /* @__PURE__ */ jsxs6("div", { className: "signer-layout", ref: containerRef, children: [
|
|
1398
|
+
loading && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Loading document..." }),
|
|
1399
|
+
/* @__PURE__ */ jsxs6("div", { className: "signer-content", children: [
|
|
1400
|
+
/* @__PURE__ */ jsx6("div", { className: "signer-pdf-area", children: pages.length > 0 && /* @__PURE__ */ jsx6(
|
|
1190
1401
|
PdfViewer,
|
|
1191
1402
|
{
|
|
1192
1403
|
pages,
|
|
@@ -1198,11 +1409,11 @@ function SignerView({
|
|
|
1198
1409
|
renderFieldContent
|
|
1199
1410
|
}
|
|
1200
1411
|
) }),
|
|
1201
|
-
/* @__PURE__ */
|
|
1202
|
-
selectedField && isFieldEditable && /* @__PURE__ */
|
|
1203
|
-
/* @__PURE__ */
|
|
1204
|
-
selectedField.required && /* @__PURE__ */
|
|
1205
|
-
(selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */
|
|
1412
|
+
/* @__PURE__ */ jsxs6("div", { className: "signer-panel", children: [
|
|
1413
|
+
selectedField && isFieldEditable && /* @__PURE__ */ jsxs6("div", { className: "signer-field-input", children: [
|
|
1414
|
+
/* @__PURE__ */ jsx6("h3", { children: selectedField.label }),
|
|
1415
|
+
selectedField.required && /* @__PURE__ */ jsx6("span", { className: "required-badge", children: "Required" }),
|
|
1416
|
+
(selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */ jsx6(
|
|
1206
1417
|
SignatureCanvas,
|
|
1207
1418
|
{
|
|
1208
1419
|
width: 280,
|
|
@@ -1211,7 +1422,7 @@ function SignerView({
|
|
|
1211
1422
|
initialValue: selectedField.value
|
|
1212
1423
|
}
|
|
1213
1424
|
),
|
|
1214
|
-
selectedField.type === "text" && /* @__PURE__ */
|
|
1425
|
+
selectedField.type === "text" && /* @__PURE__ */ jsx6(
|
|
1215
1426
|
"input",
|
|
1216
1427
|
{
|
|
1217
1428
|
type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
|
|
@@ -1221,8 +1432,8 @@ function SignerView({
|
|
|
1221
1432
|
className: "signer-text-input"
|
|
1222
1433
|
}
|
|
1223
1434
|
),
|
|
1224
|
-
selectedField.type === "checkbox" && /* @__PURE__ */
|
|
1225
|
-
/* @__PURE__ */
|
|
1435
|
+
selectedField.type === "checkbox" && /* @__PURE__ */ jsxs6("label", { className: "signer-checkbox-label", children: [
|
|
1436
|
+
/* @__PURE__ */ jsx6(
|
|
1226
1437
|
"input",
|
|
1227
1438
|
{
|
|
1228
1439
|
type: "checkbox",
|
|
@@ -1232,14 +1443,14 @@ function SignerView({
|
|
|
1232
1443
|
),
|
|
1233
1444
|
selectedField.placeholder || "Check this box"
|
|
1234
1445
|
] }),
|
|
1235
|
-
selectedField.type === "signed-date" && /* @__PURE__ */
|
|
1446
|
+
selectedField.type === "signed-date" && /* @__PURE__ */ jsx6("div", { className: "signer-date-display", children: selectedField.value || "Will auto-fill when you sign" })
|
|
1236
1447
|
] }),
|
|
1237
|
-
selectedField && !isFieldEditable && /* @__PURE__ */
|
|
1238
|
-
/* @__PURE__ */
|
|
1239
|
-
/* @__PURE__ */
|
|
1448
|
+
selectedField && !isFieldEditable && /* @__PURE__ */ jsxs6("div", { className: "signer-field-readonly", children: [
|
|
1449
|
+
/* @__PURE__ */ jsx6("h3", { children: selectedField.label }),
|
|
1450
|
+
/* @__PURE__ */ jsx6("p", { children: "This field belongs to another signer." })
|
|
1240
1451
|
] }),
|
|
1241
|
-
!selectedField && editableFields.length > 0 && /* @__PURE__ */
|
|
1242
|
-
/* @__PURE__ */
|
|
1452
|
+
!selectedField && editableFields.length > 0 && /* @__PURE__ */ jsx6("div", { className: "panel-empty", children: "Select a field to fill it in, or use the navigation below." }),
|
|
1453
|
+
/* @__PURE__ */ jsx6(
|
|
1243
1454
|
FieldNavigator,
|
|
1244
1455
|
{
|
|
1245
1456
|
fields: editableFields,
|
|
@@ -1249,12 +1460,87 @@ function SignerView({
|
|
|
1249
1460
|
onSubmit: handleSubmit
|
|
1250
1461
|
}
|
|
1251
1462
|
),
|
|
1252
|
-
submitting && /* @__PURE__ */
|
|
1463
|
+
submitting && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Generating PDF..." })
|
|
1253
1464
|
] })
|
|
1254
1465
|
] }),
|
|
1255
|
-
/* @__PURE__ */
|
|
1466
|
+
/* @__PURE__ */ jsxs6("div", { className: "powered-by", children: [
|
|
1256
1467
|
"Powered by ",
|
|
1257
|
-
/* @__PURE__ */
|
|
1468
|
+
/* @__PURE__ */ jsx6("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
|
|
1469
|
+
] })
|
|
1470
|
+
] });
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
// src/components/pdf-builder/SignerRoleSelector.tsx
|
|
1474
|
+
import { useState as useState4 } from "react";
|
|
1475
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1476
|
+
function SignerRoleSelector({
|
|
1477
|
+
roles,
|
|
1478
|
+
activeRole,
|
|
1479
|
+
onSetActiveRole,
|
|
1480
|
+
onAddRole,
|
|
1481
|
+
onRemoveRole
|
|
1482
|
+
}) {
|
|
1483
|
+
const [isAdding, setIsAdding] = useState4(false);
|
|
1484
|
+
const [newRoleName, setNewRoleName] = useState4("");
|
|
1485
|
+
const handleAdd = () => {
|
|
1486
|
+
if (newRoleName.trim()) {
|
|
1487
|
+
onAddRole(newRoleName.trim());
|
|
1488
|
+
setNewRoleName("");
|
|
1489
|
+
setIsAdding(false);
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
return /* @__PURE__ */ jsxs7("div", { className: "signer-role-selector", children: [
|
|
1493
|
+
/* @__PURE__ */ jsx7("div", { className: "signer-role-label", children: "Signer Roles" }),
|
|
1494
|
+
/* @__PURE__ */ jsxs7("div", { className: "signer-role-list", children: [
|
|
1495
|
+
roles.map((role) => /* @__PURE__ */ jsxs7(
|
|
1496
|
+
"button",
|
|
1497
|
+
{
|
|
1498
|
+
className: `signer-role-chip ${role === activeRole ? "active" : ""}`,
|
|
1499
|
+
style: {
|
|
1500
|
+
borderColor: getSignerColor(role),
|
|
1501
|
+
backgroundColor: role === activeRole ? getSignerColor(role) : "transparent",
|
|
1502
|
+
color: role === activeRole ? "#fff" : getSignerColor(role)
|
|
1503
|
+
},
|
|
1504
|
+
onClick: () => onSetActiveRole(role),
|
|
1505
|
+
children: [
|
|
1506
|
+
role,
|
|
1507
|
+
roles.length > 1 && role !== "Sender" && /* @__PURE__ */ jsx7(
|
|
1508
|
+
"span",
|
|
1509
|
+
{
|
|
1510
|
+
className: "signer-role-remove",
|
|
1511
|
+
onClick: (e) => {
|
|
1512
|
+
e.stopPropagation();
|
|
1513
|
+
onRemoveRole(role);
|
|
1514
|
+
},
|
|
1515
|
+
children: "\xD7"
|
|
1516
|
+
}
|
|
1517
|
+
)
|
|
1518
|
+
]
|
|
1519
|
+
},
|
|
1520
|
+
role
|
|
1521
|
+
)),
|
|
1522
|
+
isAdding ? /* @__PURE__ */ jsxs7("div", { className: "signer-role-add-input", children: [
|
|
1523
|
+
/* @__PURE__ */ jsx7(
|
|
1524
|
+
"input",
|
|
1525
|
+
{
|
|
1526
|
+
type: "text",
|
|
1527
|
+
value: newRoleName,
|
|
1528
|
+
onChange: (e) => setNewRoleName(e.target.value),
|
|
1529
|
+
onKeyDown: (e) => e.key === "Enter" && handleAdd(),
|
|
1530
|
+
placeholder: "Role name",
|
|
1531
|
+
autoFocus: true
|
|
1532
|
+
}
|
|
1533
|
+
),
|
|
1534
|
+
/* @__PURE__ */ jsx7("button", { onClick: handleAdd, children: "Add" }),
|
|
1535
|
+
/* @__PURE__ */ jsx7("button", { onClick: () => setIsAdding(false), children: "Cancel" })
|
|
1536
|
+
] }) : /* @__PURE__ */ jsx7(
|
|
1537
|
+
"button",
|
|
1538
|
+
{
|
|
1539
|
+
className: "signer-role-add-btn",
|
|
1540
|
+
onClick: () => setIsAdding(true),
|
|
1541
|
+
children: "+ Add Role"
|
|
1542
|
+
}
|
|
1543
|
+
)
|
|
1258
1544
|
] })
|
|
1259
1545
|
] });
|
|
1260
1546
|
}
|
|
@@ -1274,6 +1560,7 @@ export {
|
|
|
1274
1560
|
generateFilledPdf,
|
|
1275
1561
|
getSignerColor,
|
|
1276
1562
|
postPdfToCallback,
|
|
1277
|
-
renderPdfPages
|
|
1563
|
+
renderPdfPages,
|
|
1564
|
+
uniqueLabel
|
|
1278
1565
|
};
|
|
1279
1566
|
//# sourceMappingURL=index.mjs.map
|