@unlev/exeq 0.2.1 → 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.mjs CHANGED
@@ -155,12 +155,13 @@ import { jsx, jsxs } from "react/jsx-runtime";
155
155
  function PdfViewer({
156
156
  pages,
157
157
  fields,
158
- selectedFieldId,
158
+ selectedFieldIds,
159
159
  onSelectField,
160
160
  onFieldMove,
161
161
  onFieldResize,
162
162
  onPageClick,
163
163
  onDropField,
164
+ onGroupMove,
164
165
  mode,
165
166
  currentSigner,
166
167
  renderFieldContent
@@ -218,10 +219,13 @@ function PdfViewer({
218
219
  FieldOverlayItem,
219
220
  {
220
221
  field,
221
- isSelected: field.id === selectedFieldId,
222
- onSelect: () => onSelectField(field.id),
222
+ isSelected: selectedFieldIds.has(field.id),
223
+ isMultiSelected: selectedFieldIds.size > 1 && selectedFieldIds.has(field.id),
224
+ onSelect: (e) => onSelectField(field.id, e),
223
225
  onMove: onFieldMove,
224
226
  onResize: onFieldResize,
227
+ onGroupMove: selectedFieldIds.size > 1 && selectedFieldIds.has(field.id) ? onGroupMove : void 0,
228
+ selectedIds: selectedFieldIds,
225
229
  mode,
226
230
  currentSigner,
227
231
  renderContent: renderFieldContent
@@ -237,9 +241,12 @@ function PdfViewer({
237
241
  function FieldOverlayItem({
238
242
  field,
239
243
  isSelected,
244
+ isMultiSelected,
240
245
  onSelect,
241
246
  onMove,
242
247
  onResize,
248
+ onGroupMove,
249
+ selectedIds,
243
250
  mode,
244
251
  currentSigner,
245
252
  renderContent
@@ -256,7 +263,7 @@ function FieldOverlayItem({
256
263
  if (mode !== "designer" || !onMove) return;
257
264
  e.preventDefault();
258
265
  e.stopPropagation();
259
- onSelect();
266
+ onSelect(e);
260
267
  const pageEl = overlayRef.current?.closest(".pdf-page");
261
268
  if (!pageEl) return;
262
269
  dragStartRef.current = {
@@ -265,14 +272,20 @@ function FieldOverlayItem({
265
272
  fieldX: field.x,
266
273
  fieldY: field.y
267
274
  };
268
- const handleMouseMove = (e2) => {
275
+ const handleMouseMove = (ev) => {
269
276
  if (!dragStartRef.current) return;
270
277
  const rect = pageEl.getBoundingClientRect();
271
- const dx = (e2.clientX - dragStartRef.current.startX) / rect.width * 100;
272
- const dy = (e2.clientY - dragStartRef.current.startY) / rect.height * 100;
273
- const newX = Math.max(0, Math.min(100 - field.width, dragStartRef.current.fieldX + dx));
274
- const newY = Math.max(0, Math.min(100 - field.height, dragStartRef.current.fieldY + dy));
275
- onMove(field.id, field.page, newX, newY);
278
+ const dx = (ev.clientX - dragStartRef.current.startX) / rect.width * 100;
279
+ const dy = (ev.clientY - dragStartRef.current.startY) / rect.height * 100;
280
+ if (isMultiSelected && onGroupMove) {
281
+ onGroupMove(Array.from(selectedIds), dx, dy);
282
+ dragStartRef.current.startX = ev.clientX;
283
+ dragStartRef.current.startY = ev.clientY;
284
+ } else {
285
+ const newX = Math.max(0, Math.min(100 - field.width, dragStartRef.current.fieldX + dx));
286
+ const newY = Math.max(0, Math.min(100 - field.height, dragStartRef.current.fieldY + dy));
287
+ onMove(field.id, field.page, newX, newY);
288
+ }
276
289
  };
277
290
  const handleMouseUp = () => {
278
291
  dragStartRef.current = null;
@@ -281,7 +294,7 @@ function FieldOverlayItem({
281
294
  };
282
295
  window.addEventListener("mousemove", handleMouseMove);
283
296
  window.addEventListener("mouseup", handleMouseUp);
284
- }, [field, mode, onMove, onSelect]);
297
+ }, [field, mode, onMove, onSelect, isMultiSelected, onGroupMove, selectedIds]);
285
298
  const handleResizeMouseDown = useCallback((e) => {
286
299
  if (mode !== "designer" || !onResize) return;
287
300
  e.preventDefault();
@@ -329,7 +342,7 @@ function FieldOverlayItem({
329
342
  },
330
343
  onClick: (e) => {
331
344
  e.stopPropagation();
332
- onSelect();
345
+ onSelect(e);
333
346
  },
334
347
  onMouseDown: handleMouseDown,
335
348
  children: [
@@ -762,7 +775,7 @@ function DesignerView({
762
775
  }
763
776
  const [pages, setPages] = useState3([]);
764
777
  const [fields, setFields] = useState3(initialTemplate?.fields ?? []);
765
- const [selectedFieldId, setSelectedFieldId] = useState3(null);
778
+ const [selectedFieldIds, setSelectedFieldIds] = useState3(/* @__PURE__ */ new Set());
766
779
  const [signerRoles, setSignerRoles] = useState3(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
767
780
  const [activeRole, setActiveRole] = useState3("Sender");
768
781
  const [activeFieldType, setActiveFieldType] = useState3("text");
@@ -773,7 +786,7 @@ function DesignerView({
773
786
  const [newRoleName, setNewRoleName] = useState3("");
774
787
  const [draggingFieldType, setDraggingFieldType] = useState3(null);
775
788
  const [panelWidth, setPanelWidth] = useState3(380);
776
- const [clipboardField, setClipboardField] = useState3(null);
789
+ const [clipboardFields, setClipboardFields] = useState3([]);
777
790
  const dragGhostRef = useRef3(null);
778
791
  const resizingRef = useRef3(false);
779
792
  const lastStylesRef = useRef3({});
@@ -827,7 +840,7 @@ function DesignerView({
827
840
  const sticky = lastStylesRef.current[activeFieldType];
828
841
  const styledField = sticky ? { ...field, ...sticky } : field;
829
842
  setFields((prev) => [...prev, styledField]);
830
- setSelectedFieldId(styledField.id);
843
+ setSelectedFieldIds(/* @__PURE__ */ new Set([styledField.id]));
831
844
  setRightTab("properties");
832
845
  }, [activeFieldType, activeRole, fields]);
833
846
  const handleFieldMove = useCallback3((id, page, x, y) => {
@@ -865,8 +878,54 @@ function DesignerView({
865
878
  }, []);
866
879
  const handleFieldDelete = useCallback3((id) => {
867
880
  setFields((prev) => prev.filter((f) => f.id !== id));
868
- if (selectedFieldId === id) setSelectedFieldId(null);
869
- }, [selectedFieldId]);
881
+ setSelectedFieldIds((prev) => {
882
+ const next = new Set(prev);
883
+ next.delete(id);
884
+ return next;
885
+ });
886
+ }, []);
887
+ const handleSelectField = useCallback3((id, e) => {
888
+ if (!id) {
889
+ setSelectedFieldIds(/* @__PURE__ */ new Set());
890
+ return;
891
+ }
892
+ if (e && (e.metaKey || e.ctrlKey)) {
893
+ setSelectedFieldIds((prev) => {
894
+ const next = new Set(prev);
895
+ if (next.has(id)) next.delete(id);
896
+ else next.add(id);
897
+ return next;
898
+ });
899
+ } else if (e && e.shiftKey && selectedFieldIds.size > 0) {
900
+ const lastId = Array.from(selectedFieldIds).pop();
901
+ const sorted = sortedFields.map((f) => f.id);
902
+ const a = sorted.indexOf(lastId);
903
+ const b = sorted.indexOf(id);
904
+ if (a >= 0 && b >= 0) {
905
+ const start = Math.min(a, b);
906
+ const end = Math.max(a, b);
907
+ const range = sorted.slice(start, end + 1);
908
+ setSelectedFieldIds((prev) => {
909
+ const next = new Set(prev);
910
+ range.forEach((fid) => next.add(fid));
911
+ return next;
912
+ });
913
+ } else {
914
+ setSelectedFieldIds(/* @__PURE__ */ new Set([id]));
915
+ }
916
+ } else {
917
+ setSelectedFieldIds(/* @__PURE__ */ new Set([id]));
918
+ }
919
+ setRightTab("properties");
920
+ }, [selectedFieldIds]);
921
+ const handleGroupMove = useCallback3((ids, dx, dy) => {
922
+ setFields((prev) => prev.map((f) => {
923
+ if (!ids.includes(f.id)) return f;
924
+ const newX = Math.max(0, Math.min(100 - f.width, f.x + dx));
925
+ const newY = Math.max(0, Math.min(100 - f.height, f.y + dy));
926
+ return { ...f, x: newX, y: newY };
927
+ }));
928
+ }, []);
870
929
  const handleAddRole = useCallback3(() => {
871
930
  const name = newRoleName.trim();
872
931
  if (name && !signerRoles.includes(name)) {
@@ -929,56 +988,58 @@ function DesignerView({
929
988
  const sticky = lastStylesRef.current[fieldType];
930
989
  const styledField = sticky ? { ...field, ...sticky } : field;
931
990
  setFields((prev) => [...prev, styledField]);
932
- setSelectedFieldId(styledField.id);
991
+ setSelectedFieldIds(/* @__PURE__ */ new Set([styledField.id]));
933
992
  setRightTab("properties");
934
993
  }, [activeRole, fields]);
935
994
  useEffect2(() => {
936
995
  const handleKeyDown = (e) => {
937
996
  if (e.key !== "Delete" && e.key !== "Backspace") return;
938
- if (!selectedFieldId) return;
997
+ if (selectedFieldIds.size === 0) return;
939
998
  const tag = (document.activeElement?.tagName || "").toLowerCase();
940
999
  if (tag === "input" || tag === "textarea" || tag === "select") return;
941
1000
  if (document.activeElement?.isContentEditable) return;
942
1001
  e.preventDefault();
943
- if (window.confirm("Delete this field?")) {
944
- handleFieldDelete(selectedFieldId);
1002
+ const count = selectedFieldIds.size;
1003
+ if (window.confirm(`Delete ${count > 1 ? `${count} fields` : "this field"}?`)) {
1004
+ setFields((prev) => prev.filter((f) => !selectedFieldIds.has(f.id)));
1005
+ setSelectedFieldIds(/* @__PURE__ */ new Set());
945
1006
  }
946
1007
  };
947
1008
  window.addEventListener("keydown", handleKeyDown);
948
1009
  return () => window.removeEventListener("keydown", handleKeyDown);
949
- }, [selectedFieldId, handleFieldDelete]);
1010
+ }, [selectedFieldIds]);
950
1011
  useEffect2(() => {
951
1012
  const handleKeyDown = (e) => {
952
1013
  const tag = (document.activeElement?.tagName || "").toLowerCase();
953
1014
  if (tag === "input" || tag === "textarea" || tag === "select") return;
954
1015
  if (document.activeElement?.isContentEditable) return;
955
1016
  const isMod = e.metaKey || e.ctrlKey;
956
- if (isMod && e.key === "c" && selectedFieldId) {
957
- const field = fields.find((f) => f.id === selectedFieldId);
958
- if (field) {
1017
+ if (isMod && e.key === "c" && selectedFieldIds.size > 0) {
1018
+ const selected = fields.filter((f) => selectedFieldIds.has(f.id));
1019
+ if (selected.length > 0) {
959
1020
  e.preventDefault();
960
- setClipboardField(field);
1021
+ setClipboardFields(selected);
961
1022
  }
962
1023
  }
963
- if (isMod && e.key === "v" && clipboardField) {
1024
+ if (isMod && e.key === "v" && clipboardFields.length > 0) {
964
1025
  e.preventDefault();
965
- const existingLabels = fields.map((f) => f.label);
966
- const newField = {
967
- ...clipboardField,
968
- id: generateId(),
969
- label: uniqueLabel(clipboardField.label, existingLabels),
970
- x: clipboardField.x + 2,
971
- y: clipboardField.y + 2,
972
- value: ""
973
- };
974
- setFields((prev) => [...prev, newField]);
975
- setSelectedFieldId(newField.id);
1026
+ let existingLabels = fields.map((f) => f.label);
1027
+ const newIds = /* @__PURE__ */ new Set();
1028
+ const newFields = clipboardFields.map((cf) => {
1029
+ const label = uniqueLabel(cf.label, existingLabels);
1030
+ existingLabels.push(label);
1031
+ const id = generateId();
1032
+ newIds.add(id);
1033
+ return { ...cf, id, label, x: cf.x + 2, y: cf.y + 2, value: "" };
1034
+ });
1035
+ setFields((prev) => [...prev, ...newFields]);
1036
+ setSelectedFieldIds(newIds);
976
1037
  setRightTab("properties");
977
1038
  }
978
1039
  };
979
1040
  window.addEventListener("keydown", handleKeyDown);
980
1041
  return () => window.removeEventListener("keydown", handleKeyDown);
981
- }, [selectedFieldId, fields, clipboardField]);
1042
+ }, [selectedFieldIds, fields, clipboardFields]);
982
1043
  const handleResizeStart = useCallback3((e) => {
983
1044
  e.preventDefault();
984
1045
  resizingRef.current = true;
@@ -1003,7 +1064,7 @@ function DesignerView({
1003
1064
  if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
1004
1065
  return a.x - b.x;
1005
1066
  });
1006
- const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
1067
+ const selectedField = selectedFieldIds.size === 1 ? fields.find((f) => f.id === Array.from(selectedFieldIds)[0]) || null : null;
1007
1068
  const renderFieldContent = useCallback3((field) => {
1008
1069
  if (field.type === "blackout" || field.type === "whiteout") {
1009
1070
  return null;
@@ -1123,13 +1184,11 @@ function DesignerView({
1123
1184
  {
1124
1185
  pages,
1125
1186
  fields,
1126
- selectedFieldId,
1127
- onSelectField: (id) => {
1128
- setSelectedFieldId(id);
1129
- if (id) setRightTab("properties");
1130
- },
1187
+ selectedFieldIds,
1188
+ onSelectField: handleSelectField,
1131
1189
  onFieldMove: handleFieldMove,
1132
1190
  onFieldResize: handleFieldResize,
1191
+ onGroupMove: handleGroupMove,
1133
1192
  onPageClick: handlePageClick,
1134
1193
  onDropField: handleDropOnPage,
1135
1194
  mode: "designer",
@@ -1166,7 +1225,13 @@ function DesignerView({
1166
1225
  )
1167
1226
  ] }),
1168
1227
  /* @__PURE__ */ jsxs4("div", { className: "panel-tab-content", children: [
1169
- rightTab === "properties" && /* @__PURE__ */ jsx4(Fragment, { children: selectedField ? /* @__PURE__ */ jsxs4(Fragment, { children: [
1228
+ rightTab === "properties" && /* @__PURE__ */ jsx4(Fragment, { children: selectedFieldIds.size > 1 ? /* @__PURE__ */ jsxs4("div", { className: "panel-empty", children: [
1229
+ /* @__PURE__ */ jsxs4("strong", { children: [
1230
+ selectedFieldIds.size,
1231
+ " fields selected"
1232
+ ] }),
1233
+ /* @__PURE__ */ jsx4("p", { children: "Drag to move as a group. Press Delete to remove all. Cmd/Ctrl+C to copy." })
1234
+ ] }) : selectedField ? /* @__PURE__ */ jsxs4(Fragment, { children: [
1170
1235
  /* @__PURE__ */ jsx4(
1171
1236
  FieldPropertyPanel,
1172
1237
  {
@@ -1213,9 +1278,9 @@ function DesignerView({
1213
1278
  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(
1214
1279
  "div",
1215
1280
  {
1216
- className: `field-list-item ${f.id === selectedFieldId ? "active" : ""}`,
1281
+ className: `field-list-item ${selectedFieldIds.has(f.id) ? "active" : ""}`,
1217
1282
  onClick: () => {
1218
- setSelectedFieldId(f.id);
1283
+ setSelectedFieldIds(/* @__PURE__ */ new Set([f.id]));
1219
1284
  setRightTab("properties");
1220
1285
  },
1221
1286
  children: [
@@ -1872,8 +1937,8 @@ function SignerView({
1872
1937
  {
1873
1938
  pages,
1874
1939
  fields,
1875
- selectedFieldId,
1876
- onSelectField: setSelectedFieldId,
1940
+ selectedFieldIds: new Set(selectedFieldId ? [selectedFieldId] : []),
1941
+ onSelectField: (id) => setSelectedFieldId(id),
1877
1942
  mode: "signer",
1878
1943
  currentSigner: signer,
1879
1944
  renderFieldContent