@unlev/exeq 0.1.7 → 0.1.10

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
@@ -235,9 +235,10 @@ function FieldOverlayItem({
235
235
  const overlayRef = useRef(null);
236
236
  const dragStartRef = useRef(null);
237
237
  const resizeStartRef = useRef(null);
238
- const color = getSignerColor(field.assignee);
239
238
  const isRedact = field.type === "blackout" || field.type === "whiteout";
240
239
  const isEditable = mode === "designer" || mode === "signer" && field.assignee === currentSigner;
240
+ const isInactiveSigner = mode === "signer" && !isRedact && field.assignee !== currentSigner;
241
+ const color = isInactiveSigner ? "#cccccc" : getSignerColor(field.assignee);
241
242
  const isPreFilled = !isEditable && !!field.value;
242
243
  const handleMouseDown = useCallback((e) => {
243
244
  if (mode !== "designer" || !onMove) return;
@@ -302,7 +303,7 @@ function FieldOverlayItem({
302
303
  "div",
303
304
  {
304
305
  ref: overlayRef,
305
- className: `field-overlay ${isSelected ? "selected" : ""} ${isEditable ? "editable" : "readonly"} ${isPreFilled ? "prefilled" : ""} ${isRedact ? "redact" : ""}`,
306
+ className: `field-overlay ${isSelected ? "selected" : ""} ${isEditable ? "editable" : "readonly"} ${isPreFilled ? "prefilled" : ""} ${isRedact ? "redact" : ""} ${isInactiveSigner ? "inactive-signer" : ""}`,
306
307
  style: {
307
308
  left: `${field.x}%`,
308
309
  top: `${field.y}%`,
@@ -310,9 +311,9 @@ function FieldOverlayItem({
310
311
  height: `${field.height}%`,
311
312
  borderColor: isRedact ? field.type === "blackout" ? "#666" : "#bbb" : color,
312
313
  borderStyle: isRedact ? "dashed" : "solid",
313
- backgroundColor: isRedact ? field.type === "blackout" ? "#000000" : "#ffffff" : isSelected ? `${color}22` : `${color}11`,
314
- cursor: mode === "designer" ? "move" : "default",
315
- pointerEvents: mode === "signer" && isRedact ? "none" : void 0
314
+ backgroundColor: isRedact ? field.type === "blackout" ? "#000000" : "#ffffff" : isInactiveSigner ? "#f5f5f5" : isSelected ? `${color}22` : `${color}11`,
315
+ cursor: mode === "designer" ? "move" : isInactiveSigner ? "default" : "default",
316
+ pointerEvents: mode === "signer" && (isRedact || isInactiveSigner) ? "none" : void 0
316
317
  },
317
318
  onClick: (e) => {
318
319
  e.stopPropagation();
@@ -999,6 +1000,10 @@ function DesignerView({
999
1000
  pages.length > 0 && /* @__PURE__ */ jsxs4(Fragment, { children: [
1000
1001
  /* @__PURE__ */ jsx4("div", { className: "panel-resize-handle", onMouseDown: handleResizeStart }),
1001
1002
  /* @__PURE__ */ jsxs4("div", { className: "designer-panel", style: { width: panelWidth }, children: [
1003
+ /* @__PURE__ */ jsxs4("div", { className: "signer-role-indicator", children: [
1004
+ /* @__PURE__ */ jsx4("span", { className: "signer-role-indicator-label", children: "Editing as" }),
1005
+ /* @__PURE__ */ jsx4("strong", { children: activeRole })
1006
+ ] }),
1002
1007
  /* @__PURE__ */ jsxs4("div", { className: "panel-tabs", children: [
1003
1008
  /* @__PURE__ */ jsx4(
1004
1009
  "button",
@@ -1216,8 +1221,8 @@ function FieldNavigator({
1216
1221
  currentFieldId,
1217
1222
  onNavigate,
1218
1223
  allRequiredFilled,
1219
- onSubmit,
1220
- submitLabel = "Complete"
1224
+ onComplete,
1225
+ completeLabel = "Complete"
1221
1226
  }) {
1222
1227
  const currentIndex = fields.findIndex((f) => f.id === currentFieldId);
1223
1228
  const hasPrev = currentIndex > 0;
@@ -1268,10 +1273,10 @@ function FieldNavigator({
1268
1273
  /* @__PURE__ */ jsx5(
1269
1274
  "button",
1270
1275
  {
1271
- onClick: onSubmit,
1276
+ onClick: onComplete,
1272
1277
  disabled: !allRequiredFilled,
1273
- className: "submit-btn",
1274
- children: submitLabel
1278
+ className: "complete-btn",
1279
+ children: completeLabel
1275
1280
  }
1276
1281
  )
1277
1282
  ] });
@@ -1287,7 +1292,8 @@ function SignerView({
1287
1292
  callbackUrl: initialCallbackUrl,
1288
1293
  onComplete,
1289
1294
  initialValues,
1290
- submitLabel
1295
+ submitLabel,
1296
+ signerOrder: signerOrderProp
1291
1297
  } = {}) {
1292
1298
  if (!isValidApiKey(apiKey)) {
1293
1299
  return /* @__PURE__ */ jsx6("div", { className: "signer-layout", children: /* @__PURE__ */ jsxs6("div", { className: "empty-state", children: [
@@ -1302,22 +1308,28 @@ function SignerView({
1302
1308
  const [pages, setPages] = useState3([]);
1303
1309
  const [fields, setFields] = useState3([]);
1304
1310
  const [selectedFieldId, setSelectedFieldId] = useState3(null);
1305
- const [signer, setSigner] = useState3(initialSigner || "Signer 1");
1311
+ const [signerRoles, setSignerRoles] = useState3([]);
1312
+ const [currentSignerIndex, setCurrentSignerIndex] = useState3(() => {
1313
+ if (initialSigner && signerOrderProp) {
1314
+ const idx = signerOrderProp.indexOf(initialSigner);
1315
+ return idx >= 0 ? idx : 0;
1316
+ }
1317
+ return 0;
1318
+ });
1306
1319
  const initializedRef = useRef4(false);
1307
1320
  const [loading, setLoading] = useState3(false);
1308
1321
  const [submitting, setSubmitting] = useState3(false);
1309
1322
  const [pdfSource, setPdfSource] = useState3(null);
1310
1323
  const [callbackUrl, setCallbackUrl] = useState3(initialCallbackUrl || "");
1311
1324
  const containerRef = useRef4(null);
1312
- useEffect3(() => {
1313
- if (initialSigner) {
1314
- setSigner(initialSigner);
1315
- setSelectedFieldId(null);
1316
- }
1317
- }, [initialSigner]);
1325
+ const signerOrder = signerOrderProp || (signerRoles.length > 0 ? [...signerRoles.filter((r) => r !== "Sender"), ...signerRoles.filter((r) => r === "Sender")] : [initialSigner || "Signer 1"]);
1326
+ const signer = signerOrder[currentSignerIndex] || signerOrder[0] || "Signer 1";
1327
+ const isLastSigner = currentSignerIndex >= signerOrder.length - 1;
1328
+ const isMultiSigner = signerOrder.length > 1;
1318
1329
  useEffect3(() => {
1319
1330
  if (initialTemplate) {
1320
1331
  let templateFields = initialTemplate.fields;
1332
+ if (initialTemplate.signerRoles) setSignerRoles(initialTemplate.signerRoles);
1321
1333
  if (initialValues) {
1322
1334
  templateFields = templateFields.map((f) => {
1323
1335
  const byLabel = Object.entries(initialValues).find(
@@ -1343,13 +1355,12 @@ function SignerView({
1343
1355
  const params = new URLSearchParams(window.location.search);
1344
1356
  const pdfUrl = params.get("pdf");
1345
1357
  const fieldsUrl = params.get("fields");
1346
- const signerParam = params.get("signer");
1347
1358
  const cbUrl = params.get("callbackUrl");
1348
- if (signerParam) setSigner(signerParam);
1349
1359
  if (cbUrl) setCallbackUrl(cbUrl);
1350
1360
  if (pdfUrl) loadPdf(pdfUrl);
1351
1361
  if (fieldsUrl) {
1352
1362
  fetch(fieldsUrl).then((r) => r.json()).then((template) => {
1363
+ if (template.signerRoles) setSignerRoles(template.signerRoles);
1353
1364
  if (initializedRef.current) {
1354
1365
  setFields((prev) => {
1355
1366
  const valueMap = new Map(prev.filter((f) => f.value).map((f) => [f.id, f.value]));
@@ -1375,7 +1386,10 @@ function SignerView({
1375
1386
  }
1376
1387
  }
1377
1388
  if (e.data.pdfUrl) loadPdf(e.data.pdfUrl);
1378
- if (e.data.signer) setSigner(e.data.signer);
1389
+ if (e.data.signer) {
1390
+ const idx = signerOrder.indexOf(e.data.signer);
1391
+ if (idx >= 0) setCurrentSignerIndex(idx);
1392
+ }
1379
1393
  if (e.data.callbackUrl) setCallbackUrl(e.data.callbackUrl);
1380
1394
  }
1381
1395
  };
@@ -1420,8 +1434,18 @@ function SignerView({
1420
1434
  if (f.type === "checkbox") return true;
1421
1435
  return !!f.value;
1422
1436
  });
1423
- const handleSubmit = useCallback4(async () => {
1424
- if (!pdfSource || !allRequiredFilled) return;
1437
+ const handleAdvanceOrSubmit = useCallback4(async () => {
1438
+ if (!allRequiredFilled) return;
1439
+ if (!isLastSigner) {
1440
+ setCurrentSignerIndex((prev) => prev + 1);
1441
+ setSelectedFieldId(null);
1442
+ const pdfArea = containerRef.current?.querySelector(".signer-pdf-area");
1443
+ if (pdfArea) {
1444
+ pdfArea.scrollTo({ top: 0, behavior: "smooth" });
1445
+ }
1446
+ return;
1447
+ }
1448
+ if (!pdfSource) return;
1425
1449
  setSubmitting(true);
1426
1450
  try {
1427
1451
  const pdfBytes = await generateFilledPdf(pdfSource, fields);
@@ -1441,7 +1465,7 @@ function SignerView({
1441
1465
  } finally {
1442
1466
  setSubmitting(false);
1443
1467
  }
1444
- }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete]);
1468
+ }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete, isLastSigner]);
1445
1469
  const renderFieldContent = useCallback4((field) => {
1446
1470
  if (field.type === "blackout" || field.type === "whiteout") {
1447
1471
  return null;
@@ -1520,63 +1544,79 @@ function SignerView({
1520
1544
  }
1521
1545
  ) }),
1522
1546
  /* @__PURE__ */ jsxs6("div", { className: "signer-panel", children: [
1523
- selectedField && isFieldEditable && /* @__PURE__ */ jsxs6("div", { className: "signer-field-input", children: [
1524
- /* @__PURE__ */ jsx6("h3", { children: selectedField.label }),
1525
- selectedField.required && /* @__PURE__ */ jsx6("span", { className: "required-badge", children: "Required" }),
1526
- (selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */ jsx6(
1527
- SignatureCanvas,
1528
- {
1529
- width: 280,
1530
- height: selectedField.type === "initials" ? 80 : 120,
1531
- onSign: (dataUrl) => handleFieldUpdate(selectedField.id, dataUrl),
1532
- initialValue: selectedField.value
1533
- }
1534
- ),
1535
- selectedField.type === "text" && /* @__PURE__ */ jsx6(
1536
- "input",
1537
- {
1538
- type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
1539
- value: selectedField.value,
1540
- onChange: (e) => handleFieldUpdate(selectedField.id, e.target.value),
1541
- placeholder: selectedField.placeholder,
1542
- className: "signer-text-input"
1543
- }
1544
- ),
1545
- selectedField.type === "checkbox" && /* @__PURE__ */ jsxs6("label", { className: "signer-checkbox-label", children: [
1546
- /* @__PURE__ */ jsx6(
1547
+ /* @__PURE__ */ jsxs6("div", { className: "signer-panel-header", children: [
1548
+ /* @__PURE__ */ jsxs6("div", { className: "signer-role-indicator", children: [
1549
+ /* @__PURE__ */ jsx6("span", { className: "signer-role-indicator-label", children: "Signing as" }),
1550
+ /* @__PURE__ */ jsx6("strong", { children: signer })
1551
+ ] }),
1552
+ isMultiSigner && /* @__PURE__ */ jsxs6("div", { className: "signer-step-badge", children: [
1553
+ "Step ",
1554
+ currentSignerIndex + 1,
1555
+ " of ",
1556
+ signerOrder.length
1557
+ ] })
1558
+ ] }),
1559
+ /* @__PURE__ */ jsxs6("div", { className: "signer-panel-body", children: [
1560
+ selectedField && isFieldEditable && /* @__PURE__ */ jsxs6("div", { className: "signer-field-input", children: [
1561
+ /* @__PURE__ */ jsx6("h3", { children: selectedField.label }),
1562
+ selectedField.required && /* @__PURE__ */ jsx6("span", { className: "required-badge", children: "Required" }),
1563
+ (selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */ jsx6(
1564
+ SignatureCanvas,
1565
+ {
1566
+ width: 280,
1567
+ height: selectedField.type === "initials" ? 80 : 120,
1568
+ onSign: (dataUrl) => handleFieldUpdate(selectedField.id, dataUrl),
1569
+ initialValue: selectedField.value
1570
+ }
1571
+ ),
1572
+ selectedField.type === "text" && /* @__PURE__ */ jsx6(
1547
1573
  "input",
1548
1574
  {
1549
- type: "checkbox",
1550
- checked: selectedField.value === "true",
1551
- onChange: (e) => handleFieldUpdate(selectedField.id, e.target.checked ? "true" : "")
1575
+ type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
1576
+ value: selectedField.value,
1577
+ onChange: (e) => handleFieldUpdate(selectedField.id, e.target.value),
1578
+ placeholder: selectedField.placeholder,
1579
+ className: "signer-text-input"
1552
1580
  }
1553
1581
  ),
1554
- selectedField.placeholder || "Check this box"
1582
+ selectedField.type === "checkbox" && /* @__PURE__ */ jsxs6("label", { className: "signer-checkbox-label", children: [
1583
+ /* @__PURE__ */ jsx6(
1584
+ "input",
1585
+ {
1586
+ type: "checkbox",
1587
+ checked: selectedField.value === "true",
1588
+ onChange: (e) => handleFieldUpdate(selectedField.id, e.target.checked ? "true" : "")
1589
+ }
1590
+ ),
1591
+ selectedField.placeholder || "Check this box"
1592
+ ] }),
1593
+ selectedField.type === "signed-date" && /* @__PURE__ */ jsx6("div", { className: "signer-date-display", children: selectedField.value || "Will auto-fill when you sign" })
1555
1594
  ] }),
1556
- selectedField.type === "signed-date" && /* @__PURE__ */ jsx6("div", { className: "signer-date-display", children: selectedField.value || "Will auto-fill when you sign" })
1557
- ] }),
1558
- selectedField && !isFieldEditable && /* @__PURE__ */ jsxs6("div", { className: "signer-field-readonly", children: [
1559
- /* @__PURE__ */ jsx6("h3", { children: selectedField.label }),
1560
- /* @__PURE__ */ jsx6("p", { children: "This field belongs to another signer." })
1595
+ selectedField && !isFieldEditable && /* @__PURE__ */ jsxs6("div", { className: "signer-field-readonly", children: [
1596
+ /* @__PURE__ */ jsx6("h3", { children: selectedField.label }),
1597
+ /* @__PURE__ */ jsx6("p", { children: "This field belongs to another signer." })
1598
+ ] }),
1599
+ !selectedField && editableFields.length > 0 && /* @__PURE__ */ jsx6("div", { className: "panel-empty", children: "Select a field to fill it in, or use the navigation below." })
1561
1600
  ] }),
1562
- !selectedField && editableFields.length > 0 && /* @__PURE__ */ jsx6("div", { className: "panel-empty", children: "Select a field to fill it in, or use the navigation below." }),
1563
- /* @__PURE__ */ jsx6(
1564
- FieldNavigator,
1565
- {
1566
- fields: editableFields,
1567
- currentFieldId: selectedFieldId,
1568
- onNavigate: handleNavigate,
1569
- allRequiredFilled,
1570
- onSubmit: handleSubmit,
1571
- submitLabel
1572
- }
1573
- ),
1574
- submitting && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Generating PDF..." })
1601
+ /* @__PURE__ */ jsxs6("div", { className: "signer-panel-footer", children: [
1602
+ /* @__PURE__ */ jsx6(
1603
+ FieldNavigator,
1604
+ {
1605
+ fields: editableFields,
1606
+ currentFieldId: selectedFieldId,
1607
+ onNavigate: handleNavigate,
1608
+ allRequiredFilled,
1609
+ onComplete: handleAdvanceOrSubmit,
1610
+ completeLabel: isLastSigner ? submitLabel || "Complete" : `Next: ${signerOrder[currentSignerIndex + 1]}`
1611
+ }
1612
+ ),
1613
+ submitting && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Generating PDF..." }),
1614
+ /* @__PURE__ */ jsxs6("div", { className: "powered-by", children: [
1615
+ "Powered by ",
1616
+ /* @__PURE__ */ jsx6("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1617
+ ] })
1618
+ ] })
1575
1619
  ] })
1576
- ] }),
1577
- /* @__PURE__ */ jsxs6("div", { className: "powered-by", children: [
1578
- "Powered by ",
1579
- /* @__PURE__ */ jsx6("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1580
1620
  ] })
1581
1621
  ] });
1582
1622
  }