@unlev/exeq 0.2.0 → 0.2.2
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/dist/index.d.mts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +146 -63
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +146 -63
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
|
|
3
3
|
declare function generateId(): string;
|
|
4
|
-
type FieldType = 'text' | 'signature' | 'signed-date' | 'checkbox' | 'initials' | 'blackout' | 'whiteout';
|
|
4
|
+
type FieldType = 'text' | 'dropdown' | 'signature' | 'signed-date' | 'checkbox' | 'initials' | 'blackout' | 'whiteout';
|
|
5
5
|
type TextSubtype = 'freeform' | 'number' | 'date' | 'email' | 'phone';
|
|
6
6
|
interface FormField {
|
|
7
7
|
id: string;
|
|
@@ -112,17 +112,19 @@ declare function renderPdfPages(source: string | ArrayBuffer, scale?: number): P
|
|
|
112
112
|
interface PdfViewerProps {
|
|
113
113
|
pages: RenderedPage[];
|
|
114
114
|
fields: FormField[];
|
|
115
|
-
|
|
116
|
-
onSelectField: (id: string | null) => void;
|
|
115
|
+
selectedFieldIds: Set<string>;
|
|
116
|
+
onSelectField: (id: string | null, e?: React.MouseEvent) => void;
|
|
117
117
|
onFieldMove?: (id: string, page: number, x: number, y: number) => void;
|
|
118
118
|
onFieldResize?: (id: string, width: number, height: number) => void;
|
|
119
119
|
onPageClick?: (page: number, x: number, y: number) => void;
|
|
120
120
|
onDropField?: (page: number, x: number, y: number, fieldType: FieldType) => void;
|
|
121
|
+
/** Move multiple fields as a group. Called with dx/dy in percentage units. */
|
|
122
|
+
onGroupMove?: (ids: string[], dx: number, dy: number) => void;
|
|
121
123
|
mode: 'designer' | 'signer';
|
|
122
124
|
currentSigner?: string;
|
|
123
125
|
renderFieldContent?: (field: FormField) => React.ReactNode;
|
|
124
126
|
}
|
|
125
|
-
declare function PdfViewer({ pages, fields,
|
|
127
|
+
declare function PdfViewer({ pages, fields, selectedFieldIds, onSelectField, onFieldMove, onFieldResize, onPageClick, onDropField, onGroupMove, mode, currentSigner, renderFieldContent, }: PdfViewerProps): react_jsx_runtime.JSX.Element;
|
|
126
128
|
|
|
127
129
|
interface SignatureCanvasProps {
|
|
128
130
|
width?: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
|
|
3
3
|
declare function generateId(): string;
|
|
4
|
-
type FieldType = 'text' | 'signature' | 'signed-date' | 'checkbox' | 'initials' | 'blackout' | 'whiteout';
|
|
4
|
+
type FieldType = 'text' | 'dropdown' | 'signature' | 'signed-date' | 'checkbox' | 'initials' | 'blackout' | 'whiteout';
|
|
5
5
|
type TextSubtype = 'freeform' | 'number' | 'date' | 'email' | 'phone';
|
|
6
6
|
interface FormField {
|
|
7
7
|
id: string;
|
|
@@ -112,17 +112,19 @@ declare function renderPdfPages(source: string | ArrayBuffer, scale?: number): P
|
|
|
112
112
|
interface PdfViewerProps {
|
|
113
113
|
pages: RenderedPage[];
|
|
114
114
|
fields: FormField[];
|
|
115
|
-
|
|
116
|
-
onSelectField: (id: string | null) => void;
|
|
115
|
+
selectedFieldIds: Set<string>;
|
|
116
|
+
onSelectField: (id: string | null, e?: React.MouseEvent) => void;
|
|
117
117
|
onFieldMove?: (id: string, page: number, x: number, y: number) => void;
|
|
118
118
|
onFieldResize?: (id: string, width: number, height: number) => void;
|
|
119
119
|
onPageClick?: (page: number, x: number, y: number) => void;
|
|
120
120
|
onDropField?: (page: number, x: number, y: number, fieldType: FieldType) => void;
|
|
121
|
+
/** Move multiple fields as a group. Called with dx/dy in percentage units. */
|
|
122
|
+
onGroupMove?: (ids: string[], dx: number, dy: number) => void;
|
|
121
123
|
mode: 'designer' | 'signer';
|
|
122
124
|
currentSigner?: string;
|
|
123
125
|
renderFieldContent?: (field: FormField) => React.ReactNode;
|
|
124
126
|
}
|
|
125
|
-
declare function PdfViewer({ pages, fields,
|
|
127
|
+
declare function PdfViewer({ pages, fields, selectedFieldIds, onSelectField, onFieldMove, onFieldResize, onPageClick, onDropField, onGroupMove, mode, currentSigner, renderFieldContent, }: PdfViewerProps): react_jsx_runtime.JSX.Element;
|
|
126
128
|
|
|
127
129
|
interface SignatureCanvasProps {
|
|
128
130
|
width?: number;
|
package/dist/index.js
CHANGED
|
@@ -95,6 +95,12 @@ var FIELD_DEFAULTS = {
|
|
|
95
95
|
placeholder: "Enter text",
|
|
96
96
|
textSubtype: "freeform"
|
|
97
97
|
},
|
|
98
|
+
dropdown: {
|
|
99
|
+
width: 20,
|
|
100
|
+
height: 3,
|
|
101
|
+
fontSize: 12,
|
|
102
|
+
placeholder: "Select..."
|
|
103
|
+
},
|
|
98
104
|
signature: {
|
|
99
105
|
width: 20,
|
|
100
106
|
height: 6,
|
|
@@ -134,6 +140,7 @@ var FIELD_DEFAULTS = {
|
|
|
134
140
|
};
|
|
135
141
|
var TYPE_LABELS = {
|
|
136
142
|
text: "Text Field",
|
|
143
|
+
dropdown: "Dropdown",
|
|
137
144
|
signature: "Signature",
|
|
138
145
|
"signed-date": "Signed Date",
|
|
139
146
|
checkbox: "Checkbox",
|
|
@@ -205,12 +212,13 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
205
212
|
function PdfViewer({
|
|
206
213
|
pages,
|
|
207
214
|
fields,
|
|
208
|
-
|
|
215
|
+
selectedFieldIds,
|
|
209
216
|
onSelectField,
|
|
210
217
|
onFieldMove,
|
|
211
218
|
onFieldResize,
|
|
212
219
|
onPageClick,
|
|
213
220
|
onDropField,
|
|
221
|
+
onGroupMove,
|
|
214
222
|
mode,
|
|
215
223
|
currentSigner,
|
|
216
224
|
renderFieldContent
|
|
@@ -268,10 +276,13 @@ function PdfViewer({
|
|
|
268
276
|
FieldOverlayItem,
|
|
269
277
|
{
|
|
270
278
|
field,
|
|
271
|
-
isSelected: field.id
|
|
272
|
-
|
|
279
|
+
isSelected: selectedFieldIds.has(field.id),
|
|
280
|
+
isMultiSelected: selectedFieldIds.size > 1 && selectedFieldIds.has(field.id),
|
|
281
|
+
onSelect: (e) => onSelectField(field.id, e),
|
|
273
282
|
onMove: onFieldMove,
|
|
274
283
|
onResize: onFieldResize,
|
|
284
|
+
onGroupMove: selectedFieldIds.size > 1 && selectedFieldIds.has(field.id) ? onGroupMove : void 0,
|
|
285
|
+
selectedIds: selectedFieldIds,
|
|
275
286
|
mode,
|
|
276
287
|
currentSigner,
|
|
277
288
|
renderContent: renderFieldContent
|
|
@@ -287,9 +298,12 @@ function PdfViewer({
|
|
|
287
298
|
function FieldOverlayItem({
|
|
288
299
|
field,
|
|
289
300
|
isSelected,
|
|
301
|
+
isMultiSelected,
|
|
290
302
|
onSelect,
|
|
291
303
|
onMove,
|
|
292
304
|
onResize,
|
|
305
|
+
onGroupMove,
|
|
306
|
+
selectedIds,
|
|
293
307
|
mode,
|
|
294
308
|
currentSigner,
|
|
295
309
|
renderContent
|
|
@@ -306,7 +320,7 @@ function FieldOverlayItem({
|
|
|
306
320
|
if (mode !== "designer" || !onMove) return;
|
|
307
321
|
e.preventDefault();
|
|
308
322
|
e.stopPropagation();
|
|
309
|
-
onSelect();
|
|
323
|
+
onSelect(e);
|
|
310
324
|
const pageEl = overlayRef.current?.closest(".pdf-page");
|
|
311
325
|
if (!pageEl) return;
|
|
312
326
|
dragStartRef.current = {
|
|
@@ -315,14 +329,20 @@ function FieldOverlayItem({
|
|
|
315
329
|
fieldX: field.x,
|
|
316
330
|
fieldY: field.y
|
|
317
331
|
};
|
|
318
|
-
const handleMouseMove = (
|
|
332
|
+
const handleMouseMove = (ev) => {
|
|
319
333
|
if (!dragStartRef.current) return;
|
|
320
334
|
const rect = pageEl.getBoundingClientRect();
|
|
321
|
-
const dx = (
|
|
322
|
-
const dy = (
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
335
|
+
const dx = (ev.clientX - dragStartRef.current.startX) / rect.width * 100;
|
|
336
|
+
const dy = (ev.clientY - dragStartRef.current.startY) / rect.height * 100;
|
|
337
|
+
if (isMultiSelected && onGroupMove) {
|
|
338
|
+
onGroupMove(Array.from(selectedIds), dx, dy);
|
|
339
|
+
dragStartRef.current.startX = ev.clientX;
|
|
340
|
+
dragStartRef.current.startY = ev.clientY;
|
|
341
|
+
} else {
|
|
342
|
+
const newX = Math.max(0, Math.min(100 - field.width, dragStartRef.current.fieldX + dx));
|
|
343
|
+
const newY = Math.max(0, Math.min(100 - field.height, dragStartRef.current.fieldY + dy));
|
|
344
|
+
onMove(field.id, field.page, newX, newY);
|
|
345
|
+
}
|
|
326
346
|
};
|
|
327
347
|
const handleMouseUp = () => {
|
|
328
348
|
dragStartRef.current = null;
|
|
@@ -331,7 +351,7 @@ function FieldOverlayItem({
|
|
|
331
351
|
};
|
|
332
352
|
window.addEventListener("mousemove", handleMouseMove);
|
|
333
353
|
window.addEventListener("mouseup", handleMouseUp);
|
|
334
|
-
}, [field, mode, onMove, onSelect]);
|
|
354
|
+
}, [field, mode, onMove, onSelect, isMultiSelected, onGroupMove, selectedIds]);
|
|
335
355
|
const handleResizeMouseDown = (0, import_react.useCallback)((e) => {
|
|
336
356
|
if (mode !== "designer" || !onResize) return;
|
|
337
357
|
e.preventDefault();
|
|
@@ -379,7 +399,7 @@ function FieldOverlayItem({
|
|
|
379
399
|
},
|
|
380
400
|
onClick: (e) => {
|
|
381
401
|
e.stopPropagation();
|
|
382
|
-
onSelect();
|
|
402
|
+
onSelect(e);
|
|
383
403
|
},
|
|
384
404
|
onMouseDown: handleMouseDown,
|
|
385
405
|
children: [
|
|
@@ -407,8 +427,8 @@ var INK_COLORS = [
|
|
|
407
427
|
function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
408
428
|
const color = getSignerColor(field.assignee);
|
|
409
429
|
const isRedactField = field.type === "blackout" || field.type === "whiteout";
|
|
410
|
-
const isTextField = field.type === "text" || field.type === "signed-date";
|
|
411
|
-
const showInkColor = field.type === "text" || field.type === "signature" || field.type === "initials" || field.type === "signed-date";
|
|
430
|
+
const isTextField = field.type === "text" || field.type === "signed-date" || field.type === "dropdown";
|
|
431
|
+
const showInkColor = field.type === "text" || field.type === "dropdown" || field.type === "signature" || field.type === "initials" || field.type === "signed-date";
|
|
412
432
|
const [newOption, setNewOption] = (0, import_react2.useState)("");
|
|
413
433
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "field-property-panel", children: [
|
|
414
434
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-header", children: [
|
|
@@ -435,6 +455,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
435
455
|
onChange: (e) => onUpdate(field.id, { type: e.target.value }),
|
|
436
456
|
children: [
|
|
437
457
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "text", children: "Text" }),
|
|
458
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "dropdown", children: "Dropdown" }),
|
|
438
459
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "signature", children: "Signature" }),
|
|
439
460
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "signed-date", children: "Signed Date" }),
|
|
440
461
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "checkbox", children: "Checkbox" }),
|
|
@@ -586,8 +607,8 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
586
607
|
c.value
|
|
587
608
|
)) })
|
|
588
609
|
] }),
|
|
589
|
-
field.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
|
|
590
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Predefined Options" }),
|
|
610
|
+
(field.type === "text" || field.type === "dropdown") && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
|
|
611
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: field.type === "dropdown" ? "Options" : "Predefined Options" }),
|
|
591
612
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-options-list", children: [
|
|
592
613
|
(field.options || []).map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-option-item", children: [
|
|
593
614
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: opt }),
|
|
@@ -633,7 +654,8 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
|
|
|
633
654
|
)
|
|
634
655
|
] })
|
|
635
656
|
] }),
|
|
636
|
-
(field.options?.length ?? 0)
|
|
657
|
+
field.type === "dropdown" && (field.options?.length ?? 0) === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "panel-hint", style: { color: "#c44" }, children: "Add at least one option" }),
|
|
658
|
+
field.type === "text" && (field.options?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "panel-hint", children: "Signer will choose from these options via dropdown" })
|
|
637
659
|
] })
|
|
638
660
|
] });
|
|
639
661
|
}
|
|
@@ -782,6 +804,7 @@ function isValidApiKey(key) {
|
|
|
782
804
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
783
805
|
var FIELD_TYPE_META = [
|
|
784
806
|
{ type: "text", label: "Text", icon: "T" },
|
|
807
|
+
{ type: "dropdown", label: "Dropdown", icon: "\u25BE" },
|
|
785
808
|
{ type: "signature", label: "Signature", icon: "\u270D" },
|
|
786
809
|
{ type: "signed-date", label: "Date", icon: "\u{1F4C5}" },
|
|
787
810
|
{ type: "checkbox", label: "Checkbox", icon: "\u2611" },
|
|
@@ -809,7 +832,7 @@ function DesignerView({
|
|
|
809
832
|
}
|
|
810
833
|
const [pages, setPages] = (0, import_react4.useState)([]);
|
|
811
834
|
const [fields, setFields] = (0, import_react4.useState)(initialTemplate?.fields ?? []);
|
|
812
|
-
const [
|
|
835
|
+
const [selectedFieldIds, setSelectedFieldIds] = (0, import_react4.useState)(/* @__PURE__ */ new Set());
|
|
813
836
|
const [signerRoles, setSignerRoles] = (0, import_react4.useState)(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
|
|
814
837
|
const [activeRole, setActiveRole] = (0, import_react4.useState)("Sender");
|
|
815
838
|
const [activeFieldType, setActiveFieldType] = (0, import_react4.useState)("text");
|
|
@@ -820,7 +843,7 @@ function DesignerView({
|
|
|
820
843
|
const [newRoleName, setNewRoleName] = (0, import_react4.useState)("");
|
|
821
844
|
const [draggingFieldType, setDraggingFieldType] = (0, import_react4.useState)(null);
|
|
822
845
|
const [panelWidth, setPanelWidth] = (0, import_react4.useState)(380);
|
|
823
|
-
const [
|
|
846
|
+
const [clipboardFields, setClipboardFields] = (0, import_react4.useState)([]);
|
|
824
847
|
const dragGhostRef = (0, import_react4.useRef)(null);
|
|
825
848
|
const resizingRef = (0, import_react4.useRef)(false);
|
|
826
849
|
const lastStylesRef = (0, import_react4.useRef)({});
|
|
@@ -874,7 +897,7 @@ function DesignerView({
|
|
|
874
897
|
const sticky = lastStylesRef.current[activeFieldType];
|
|
875
898
|
const styledField = sticky ? { ...field, ...sticky } : field;
|
|
876
899
|
setFields((prev) => [...prev, styledField]);
|
|
877
|
-
|
|
900
|
+
setSelectedFieldIds(/* @__PURE__ */ new Set([styledField.id]));
|
|
878
901
|
setRightTab("properties");
|
|
879
902
|
}, [activeFieldType, activeRole, fields]);
|
|
880
903
|
const handleFieldMove = (0, import_react4.useCallback)((id, page, x, y) => {
|
|
@@ -912,8 +935,54 @@ function DesignerView({
|
|
|
912
935
|
}, []);
|
|
913
936
|
const handleFieldDelete = (0, import_react4.useCallback)((id) => {
|
|
914
937
|
setFields((prev) => prev.filter((f) => f.id !== id));
|
|
915
|
-
|
|
916
|
-
|
|
938
|
+
setSelectedFieldIds((prev) => {
|
|
939
|
+
const next = new Set(prev);
|
|
940
|
+
next.delete(id);
|
|
941
|
+
return next;
|
|
942
|
+
});
|
|
943
|
+
}, []);
|
|
944
|
+
const handleSelectField = (0, import_react4.useCallback)((id, e) => {
|
|
945
|
+
if (!id) {
|
|
946
|
+
setSelectedFieldIds(/* @__PURE__ */ new Set());
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
if (e && (e.metaKey || e.ctrlKey)) {
|
|
950
|
+
setSelectedFieldIds((prev) => {
|
|
951
|
+
const next = new Set(prev);
|
|
952
|
+
if (next.has(id)) next.delete(id);
|
|
953
|
+
else next.add(id);
|
|
954
|
+
return next;
|
|
955
|
+
});
|
|
956
|
+
} else if (e && e.shiftKey && selectedFieldIds.size > 0) {
|
|
957
|
+
const lastId = Array.from(selectedFieldIds).pop();
|
|
958
|
+
const sorted = sortedFields.map((f) => f.id);
|
|
959
|
+
const a = sorted.indexOf(lastId);
|
|
960
|
+
const b = sorted.indexOf(id);
|
|
961
|
+
if (a >= 0 && b >= 0) {
|
|
962
|
+
const start = Math.min(a, b);
|
|
963
|
+
const end = Math.max(a, b);
|
|
964
|
+
const range = sorted.slice(start, end + 1);
|
|
965
|
+
setSelectedFieldIds((prev) => {
|
|
966
|
+
const next = new Set(prev);
|
|
967
|
+
range.forEach((fid) => next.add(fid));
|
|
968
|
+
return next;
|
|
969
|
+
});
|
|
970
|
+
} else {
|
|
971
|
+
setSelectedFieldIds(/* @__PURE__ */ new Set([id]));
|
|
972
|
+
}
|
|
973
|
+
} else {
|
|
974
|
+
setSelectedFieldIds(/* @__PURE__ */ new Set([id]));
|
|
975
|
+
}
|
|
976
|
+
setRightTab("properties");
|
|
977
|
+
}, [selectedFieldIds]);
|
|
978
|
+
const handleGroupMove = (0, import_react4.useCallback)((ids, dx, dy) => {
|
|
979
|
+
setFields((prev) => prev.map((f) => {
|
|
980
|
+
if (!ids.includes(f.id)) return f;
|
|
981
|
+
const newX = Math.max(0, Math.min(100 - f.width, f.x + dx));
|
|
982
|
+
const newY = Math.max(0, Math.min(100 - f.height, f.y + dy));
|
|
983
|
+
return { ...f, x: newX, y: newY };
|
|
984
|
+
}));
|
|
985
|
+
}, []);
|
|
917
986
|
const handleAddRole = (0, import_react4.useCallback)(() => {
|
|
918
987
|
const name = newRoleName.trim();
|
|
919
988
|
if (name && !signerRoles.includes(name)) {
|
|
@@ -976,56 +1045,58 @@ function DesignerView({
|
|
|
976
1045
|
const sticky = lastStylesRef.current[fieldType];
|
|
977
1046
|
const styledField = sticky ? { ...field, ...sticky } : field;
|
|
978
1047
|
setFields((prev) => [...prev, styledField]);
|
|
979
|
-
|
|
1048
|
+
setSelectedFieldIds(/* @__PURE__ */ new Set([styledField.id]));
|
|
980
1049
|
setRightTab("properties");
|
|
981
1050
|
}, [activeRole, fields]);
|
|
982
1051
|
(0, import_react4.useEffect)(() => {
|
|
983
1052
|
const handleKeyDown = (e) => {
|
|
984
1053
|
if (e.key !== "Delete" && e.key !== "Backspace") return;
|
|
985
|
-
if (
|
|
1054
|
+
if (selectedFieldIds.size === 0) return;
|
|
986
1055
|
const tag = (document.activeElement?.tagName || "").toLowerCase();
|
|
987
1056
|
if (tag === "input" || tag === "textarea" || tag === "select") return;
|
|
988
1057
|
if (document.activeElement?.isContentEditable) return;
|
|
989
1058
|
e.preventDefault();
|
|
990
|
-
|
|
991
|
-
|
|
1059
|
+
const count = selectedFieldIds.size;
|
|
1060
|
+
if (window.confirm(`Delete ${count > 1 ? `${count} fields` : "this field"}?`)) {
|
|
1061
|
+
setFields((prev) => prev.filter((f) => !selectedFieldIds.has(f.id)));
|
|
1062
|
+
setSelectedFieldIds(/* @__PURE__ */ new Set());
|
|
992
1063
|
}
|
|
993
1064
|
};
|
|
994
1065
|
window.addEventListener("keydown", handleKeyDown);
|
|
995
1066
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
996
|
-
}, [
|
|
1067
|
+
}, [selectedFieldIds]);
|
|
997
1068
|
(0, import_react4.useEffect)(() => {
|
|
998
1069
|
const handleKeyDown = (e) => {
|
|
999
1070
|
const tag = (document.activeElement?.tagName || "").toLowerCase();
|
|
1000
1071
|
if (tag === "input" || tag === "textarea" || tag === "select") return;
|
|
1001
1072
|
if (document.activeElement?.isContentEditable) return;
|
|
1002
1073
|
const isMod = e.metaKey || e.ctrlKey;
|
|
1003
|
-
if (isMod && e.key === "c" &&
|
|
1004
|
-
const
|
|
1005
|
-
if (
|
|
1074
|
+
if (isMod && e.key === "c" && selectedFieldIds.size > 0) {
|
|
1075
|
+
const selected = fields.filter((f) => selectedFieldIds.has(f.id));
|
|
1076
|
+
if (selected.length > 0) {
|
|
1006
1077
|
e.preventDefault();
|
|
1007
|
-
|
|
1078
|
+
setClipboardFields(selected);
|
|
1008
1079
|
}
|
|
1009
1080
|
}
|
|
1010
|
-
if (isMod && e.key === "v" &&
|
|
1081
|
+
if (isMod && e.key === "v" && clipboardFields.length > 0) {
|
|
1011
1082
|
e.preventDefault();
|
|
1012
|
-
|
|
1013
|
-
const
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
value: ""
|
|
1020
|
-
};
|
|
1021
|
-
setFields((prev) => [...prev,
|
|
1022
|
-
|
|
1083
|
+
let existingLabels = fields.map((f) => f.label);
|
|
1084
|
+
const newIds = /* @__PURE__ */ new Set();
|
|
1085
|
+
const newFields = clipboardFields.map((cf) => {
|
|
1086
|
+
const label = uniqueLabel(cf.label, existingLabels);
|
|
1087
|
+
existingLabels.push(label);
|
|
1088
|
+
const id = generateId();
|
|
1089
|
+
newIds.add(id);
|
|
1090
|
+
return { ...cf, id, label, x: cf.x + 2, y: cf.y + 2, value: "" };
|
|
1091
|
+
});
|
|
1092
|
+
setFields((prev) => [...prev, ...newFields]);
|
|
1093
|
+
setSelectedFieldIds(newIds);
|
|
1023
1094
|
setRightTab("properties");
|
|
1024
1095
|
}
|
|
1025
1096
|
};
|
|
1026
1097
|
window.addEventListener("keydown", handleKeyDown);
|
|
1027
1098
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1028
|
-
}, [
|
|
1099
|
+
}, [selectedFieldIds, fields, clipboardFields]);
|
|
1029
1100
|
const handleResizeStart = (0, import_react4.useCallback)((e) => {
|
|
1030
1101
|
e.preventDefault();
|
|
1031
1102
|
resizingRef.current = true;
|
|
@@ -1050,7 +1121,7 @@ function DesignerView({
|
|
|
1050
1121
|
if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
|
|
1051
1122
|
return a.x - b.x;
|
|
1052
1123
|
});
|
|
1053
|
-
const selectedField = fields.find((f) => f.id ===
|
|
1124
|
+
const selectedField = selectedFieldIds.size === 1 ? fields.find((f) => f.id === Array.from(selectedFieldIds)[0]) || null : null;
|
|
1054
1125
|
const renderFieldContent = (0, import_react4.useCallback)((field) => {
|
|
1055
1126
|
if (field.type === "blackout" || field.type === "whiteout") {
|
|
1056
1127
|
return null;
|
|
@@ -1061,15 +1132,22 @@ function DesignerView({
|
|
|
1061
1132
|
}
|
|
1062
1133
|
}
|
|
1063
1134
|
const inkColor = field.inkColor || "#000000";
|
|
1135
|
+
const cssFontFamily = field.fontFamily === "Courier" ? '"Courier New", Courier, monospace' : field.fontFamily === "TimesRoman" ? '"Times New Roman", Times, serif' : field.fontFamily === "Helvetica" ? "Helvetica, Arial, sans-serif" : void 0;
|
|
1064
1136
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1065
1137
|
"div",
|
|
1066
1138
|
{
|
|
1067
1139
|
className: "field-overlay-placeholder",
|
|
1068
|
-
style: {
|
|
1140
|
+
style: {
|
|
1141
|
+
color: field.value ? inkColor : void 0,
|
|
1142
|
+
fontSize: `${field.fontSize * 0.6}px`,
|
|
1143
|
+
fontFamily: cssFontFamily,
|
|
1144
|
+
letterSpacing: field.letterSpacing ? `${field.letterSpacing * 0.6}px` : void 0,
|
|
1145
|
+
lineHeight: field.lineHeight ? `${field.lineHeight}` : void 0
|
|
1146
|
+
},
|
|
1069
1147
|
children: field.value || field.placeholder
|
|
1070
1148
|
}
|
|
1071
1149
|
);
|
|
1072
|
-
}, []);
|
|
1150
|
+
}, [fields]);
|
|
1073
1151
|
const headerButtons = pages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1074
1152
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "header-btn header-btn-outline", children: [
|
|
1075
1153
|
"Change PDF",
|
|
@@ -1163,13 +1241,11 @@ function DesignerView({
|
|
|
1163
1241
|
{
|
|
1164
1242
|
pages,
|
|
1165
1243
|
fields,
|
|
1166
|
-
|
|
1167
|
-
onSelectField:
|
|
1168
|
-
setSelectedFieldId(id);
|
|
1169
|
-
if (id) setRightTab("properties");
|
|
1170
|
-
},
|
|
1244
|
+
selectedFieldIds,
|
|
1245
|
+
onSelectField: handleSelectField,
|
|
1171
1246
|
onFieldMove: handleFieldMove,
|
|
1172
1247
|
onFieldResize: handleFieldResize,
|
|
1248
|
+
onGroupMove: handleGroupMove,
|
|
1173
1249
|
onPageClick: handlePageClick,
|
|
1174
1250
|
onDropField: handleDropOnPage,
|
|
1175
1251
|
mode: "designer",
|
|
@@ -1206,7 +1282,13 @@ function DesignerView({
|
|
|
1206
1282
|
)
|
|
1207
1283
|
] }),
|
|
1208
1284
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "panel-tab-content", children: [
|
|
1209
|
-
rightTab === "properties" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children:
|
|
1285
|
+
rightTab === "properties" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: selectedFieldIds.size > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "panel-empty", children: [
|
|
1286
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("strong", { children: [
|
|
1287
|
+
selectedFieldIds.size,
|
|
1288
|
+
" fields selected"
|
|
1289
|
+
] }),
|
|
1290
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: "Drag to move as a group. Press Delete to remove all. Cmd/Ctrl+C to copy." })
|
|
1291
|
+
] }) : selectedField ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1210
1292
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1211
1293
|
FieldPropertyPanel,
|
|
1212
1294
|
{
|
|
@@ -1253,9 +1335,9 @@ function DesignerView({
|
|
|
1253
1335
|
rightTab === "fields" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: fields.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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__ */ (0, import_jsx_runtime4.jsx)("div", { className: "field-list", children: sortedFields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1254
1336
|
"div",
|
|
1255
1337
|
{
|
|
1256
|
-
className: `field-list-item ${f.id
|
|
1338
|
+
className: `field-list-item ${selectedFieldIds.has(f.id) ? "active" : ""}`,
|
|
1257
1339
|
onClick: () => {
|
|
1258
|
-
|
|
1340
|
+
setSelectedFieldIds(/* @__PURE__ */ new Set([f.id]));
|
|
1259
1341
|
setRightTab("properties");
|
|
1260
1342
|
},
|
|
1261
1343
|
children: [
|
|
@@ -1850,9 +1932,10 @@ function SignerView({
|
|
|
1850
1932
|
const fontStyle = {
|
|
1851
1933
|
fontSize: `${field.fontSize * 0.6}px`,
|
|
1852
1934
|
letterSpacing: field.letterSpacing ? `${field.letterSpacing * 0.6}px` : void 0,
|
|
1853
|
-
|
|
1935
|
+
lineHeight: field.lineHeight ? `${field.lineHeight}` : void 0,
|
|
1936
|
+
fontFamily: field.fontFamily === "Courier" ? '"Courier New", Courier, monospace' : field.fontFamily === "TimesRoman" ? '"Times New Roman", Times, serif' : field.fontFamily === "Helvetica" ? "Helvetica, Arial, sans-serif" : void 0
|
|
1854
1937
|
};
|
|
1855
|
-
if (field.options && field.options.length > 0) {
|
|
1938
|
+
if (field.type === "dropdown" || field.options && field.options.length > 0) {
|
|
1856
1939
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1857
1940
|
"select",
|
|
1858
1941
|
{
|
|
@@ -1864,7 +1947,7 @@ function SignerView({
|
|
|
1864
1947
|
style: fontStyle,
|
|
1865
1948
|
children: [
|
|
1866
1949
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: field.placeholder || "Select..." }),
|
|
1867
|
-
field.options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: opt, children: opt }, opt))
|
|
1950
|
+
(field.options || []).map((opt) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: opt, children: opt }, opt))
|
|
1868
1951
|
]
|
|
1869
1952
|
}
|
|
1870
1953
|
);
|
|
@@ -1911,8 +1994,8 @@ function SignerView({
|
|
|
1911
1994
|
{
|
|
1912
1995
|
pages,
|
|
1913
1996
|
fields,
|
|
1914
|
-
selectedFieldId,
|
|
1915
|
-
onSelectField: setSelectedFieldId,
|
|
1997
|
+
selectedFieldIds: new Set(selectedFieldId ? [selectedFieldId] : []),
|
|
1998
|
+
onSelectField: (id) => setSelectedFieldId(id),
|
|
1916
1999
|
mode: "signer",
|
|
1917
2000
|
currentSigner: signer,
|
|
1918
2001
|
renderFieldContent
|
|
@@ -1944,8 +2027,8 @@ function SignerView({
|
|
|
1944
2027
|
initialValue: selectedField.value
|
|
1945
2028
|
}
|
|
1946
2029
|
),
|
|
1947
|
-
selectedField.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1948
|
-
selectedField.options && selectedField.options.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2030
|
+
(selectedField.type === "text" || selectedField.type === "dropdown") && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
2031
|
+
selectedField.type === "dropdown" || selectedField.options && selectedField.options.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1949
2032
|
"select",
|
|
1950
2033
|
{
|
|
1951
2034
|
value: selectedField.value,
|
|
@@ -1953,7 +2036,7 @@ function SignerView({
|
|
|
1953
2036
|
className: "signer-text-input",
|
|
1954
2037
|
children: [
|
|
1955
2038
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: selectedField.placeholder || "Select..." }),
|
|
1956
|
-
selectedField.options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: opt, children: opt }, opt))
|
|
2039
|
+
(selectedField.options || []).map((opt) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: opt, children: opt }, opt))
|
|
1957
2040
|
]
|
|
1958
2041
|
}
|
|
1959
2042
|
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|