@josephomills/esign 0.7.1 → 0.9.0

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/ui/index.js CHANGED
@@ -313,7 +313,7 @@ function PdfViewer({ url, placements, workerSrc, primaryColor = "#4f46e5" }) {
313
313
  opacity: 0.85,
314
314
  whiteSpace: "nowrap"
315
315
  },
316
- children: "\u270D Sign here"
316
+ children: pl.type === "date" ? "\u{1F4C5} Select a date" : "\u270D Sign here"
317
317
  }
318
318
  )
319
319
  },
@@ -324,6 +324,27 @@ function PdfViewer({ url, placements, workerSrc, primaryColor = "#4f46e5" }) {
324
324
  )
325
325
  ] });
326
326
  }
327
+
328
+ // src/shared/fields.ts
329
+ var DATE_FORMATS = [
330
+ "DD/MM/YYYY",
331
+ "MM/DD/YYYY",
332
+ "YYYY-MM-DD",
333
+ "D MMM YYYY",
334
+ "Do MMM YYYY",
335
+ "Do MMMM YYYY",
336
+ "MMMM D, YYYY"
337
+ ];
338
+ var DEFAULT_DATE_FORMAT = "Do MMMM YYYY";
339
+ var DATE_FORMAT_LABELS = {
340
+ "DD/MM/YYYY": "02/10/2026",
341
+ "MM/DD/YYYY": "10/02/2026",
342
+ "YYYY-MM-DD": "2026-10-02",
343
+ "D MMM YYYY": "2 Oct 2026",
344
+ "Do MMM YYYY": "2nd Oct 2026",
345
+ "Do MMMM YYYY": "2nd October 2026",
346
+ "MMMM D, YYYY": "October 2, 2026"
347
+ };
327
348
  var clamp = (v, min, max) => Math.min(Math.max(v, min), max);
328
349
  var MIN_W = 0.04;
329
350
  var MIN_H = 0.02;
@@ -333,13 +354,16 @@ function FieldDesigner({
333
354
  value,
334
355
  onChange,
335
356
  workerSrc,
336
- primaryColor = "#4f46e5"
357
+ primaryColor = "#4f46e5",
358
+ hideFieldList = false
337
359
  }) {
338
360
  const [numPages, setNumPages] = useState(0);
339
361
  const [page, setPage] = useState(value[0]?.page ?? 1);
340
362
  const [failed, setFailed] = useState(false);
341
363
  const [selectedId, setSelectedId] = useState(value[0]?.id ?? null);
342
364
  const [drawMode, setDrawMode] = useState(false);
365
+ const [pendingType, setPendingType] = useState("signature");
366
+ const [configId, setConfigId] = useState(null);
343
367
  const [drag, setDrag] = useState(null);
344
368
  const [live, setLive] = useState(null);
345
369
  const stageRef = useRef(null);
@@ -432,15 +456,21 @@ function FieldDesigner({
432
456
  function onPointerUp() {
433
457
  if (drag && live && live.w >= MIN_W && live.h >= MIN_H) {
434
458
  if (drag.mode === "new") {
459
+ const isDate = pendingType === "date";
460
+ const n = value.filter((v) => v.type === pendingType).length + 1;
461
+ const base = isDate ? "Date" : "Signature";
435
462
  const f = {
436
463
  id: newId(),
437
- label: value.length === 0 ? "Signature" : `Signature ${value.length + 1}`,
464
+ label: n === 1 ? base : `${base} ${n}`,
465
+ type: pendingType,
466
+ ...isDate ? { dateFormat: DEFAULT_DATE_FORMAT, minDate: null } : {},
438
467
  page,
439
468
  ...live
440
469
  };
441
470
  onChange([...value, f]);
442
471
  setSelectedId(f.id);
443
472
  setDrawMode(false);
473
+ if (isDate) setConfigId(f.id);
444
474
  } else {
445
475
  onChange(value.map((x) => x.id === drag.id ? { ...x, ...live } : x));
446
476
  }
@@ -451,10 +481,15 @@ function FieldDesigner({
451
481
  function removeField(id) {
452
482
  onChange(value.filter((f) => f.id !== id));
453
483
  if (selectedId === id) setSelectedId(null);
484
+ if (configId === id) setConfigId(null);
454
485
  }
455
486
  function rename(id, label) {
456
487
  onChange(value.map((f) => f.id === id ? { ...f, label } : f));
457
488
  }
489
+ function setConfig(id, patch) {
490
+ onChange(value.map((f) => f.id === id ? { ...f, ...patch } : f));
491
+ }
492
+ const configField = value.find((f) => f.id === configId && f.type === "date") ?? null;
458
493
  const handle = (id, geom, c) => {
459
494
  const cx = c[1] === "w" ? geom.x : geom.x + geom.w;
460
495
  const cy = c[0] === "n" ? geom.y : geom.y + geom.h;
@@ -500,25 +535,35 @@ function FieldDesigner({
500
535
  flexWrap: "wrap"
501
536
  },
502
537
  children: [
503
- /* @__PURE__ */ jsx(
504
- "button",
505
- {
506
- type: "button",
507
- onClick: () => setDrawMode((d) => !d),
508
- style: {
509
- fontSize: 13,
510
- fontWeight: 600,
511
- padding: "5px 12px",
512
- borderRadius: 6,
513
- border: `1px solid ${primaryColor}`,
514
- background: drawMode ? primaryColor : "#fff",
515
- color: drawMode ? "#fff" : primaryColor,
516
- cursor: "pointer"
538
+ ["signature", "date"].map((t) => {
539
+ const active = drawMode && pendingType === t;
540
+ return /* @__PURE__ */ jsx(
541
+ "button",
542
+ {
543
+ type: "button",
544
+ onClick: () => {
545
+ if (active) setDrawMode(false);
546
+ else {
547
+ setPendingType(t);
548
+ setDrawMode(true);
549
+ }
550
+ },
551
+ style: {
552
+ fontSize: 13,
553
+ fontWeight: 600,
554
+ padding: "5px 12px",
555
+ borderRadius: 6,
556
+ border: `1px solid ${primaryColor}`,
557
+ background: active ? primaryColor : "#fff",
558
+ color: active ? "#fff" : primaryColor,
559
+ cursor: "pointer"
560
+ },
561
+ children: t === "date" ? "+ Date" : "+ Signature"
517
562
  },
518
- children: drawMode ? "Now drag on the page\u2026" : "+ Add field"
519
- }
520
- ),
521
- /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#6b7280" }, children: "or drag on the page (desktop)." }),
563
+ t
564
+ );
565
+ }),
566
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#6b7280" }, children: drawMode ? `Drag a ${pendingType} box on the page\u2026` : "or drag on the page." }),
522
567
  numPages > 1 ? /* @__PURE__ */ jsxs("label", { style: { marginLeft: "auto", fontSize: 13, color: "#374151" }, children: [
523
568
  "Page",
524
569
  " ",
@@ -589,8 +634,9 @@ function FieldDesigner({
589
634
  padding: "0 4px"
590
635
  },
591
636
  children: [
592
- "\u270D ",
593
- f.label || "Signature"
637
+ f.type === "date" ? "\u{1F4C5}" : "\u270D",
638
+ " ",
639
+ f.label || (f.type === "date" ? "Date" : "Signature")
594
640
  ]
595
641
  }
596
642
  )
@@ -629,7 +675,39 @@ function FieldDesigner({
629
675
  },
630
676
  children: "\xD7"
631
677
  }
632
- )
678
+ ),
679
+ f.type === "date" ? /* @__PURE__ */ jsx(
680
+ "button",
681
+ {
682
+ type: "button",
683
+ "aria-label": `Configure ${f.label}`,
684
+ onPointerDown: (e) => e.stopPropagation(),
685
+ onClick: (e) => {
686
+ e.stopPropagation();
687
+ setConfigId(f.id);
688
+ },
689
+ style: {
690
+ position: "absolute",
691
+ left: `${geom.x * 100}%`,
692
+ top: `${geom.y * 100}%`,
693
+ transform: "translate(-50%, -50%)",
694
+ marginTop: -18,
695
+ width: 22,
696
+ height: 22,
697
+ borderRadius: "50%",
698
+ background: "#fff",
699
+ color: primaryColor,
700
+ border: `2px solid ${primaryColor}`,
701
+ boxShadow: "0 1px 3px rgba(0,0,0,0.3)",
702
+ fontSize: 12,
703
+ lineHeight: "14px",
704
+ cursor: "pointer",
705
+ padding: 0,
706
+ touchAction: "none"
707
+ },
708
+ children: "\u2699"
709
+ }
710
+ ) : null
633
711
  ] }) : null
634
712
  ] }, f.id);
635
713
  }),
@@ -651,11 +729,99 @@ function FieldDesigner({
651
729
  ) : null
652
730
  ]
653
731
  }
654
- )
732
+ ),
733
+ configField ? /* @__PURE__ */ jsxs(
734
+ "div",
735
+ {
736
+ style: {
737
+ marginTop: 10,
738
+ border: `1px solid ${primaryColor}66`,
739
+ borderRadius: 8,
740
+ padding: 12,
741
+ background: "#fff",
742
+ fontSize: 13,
743
+ color: "#374151"
744
+ },
745
+ children: [
746
+ /* @__PURE__ */ jsxs(
747
+ "div",
748
+ {
749
+ style: {
750
+ display: "flex",
751
+ alignItems: "center",
752
+ justifyContent: "space-between",
753
+ marginBottom: 8
754
+ },
755
+ children: [
756
+ /* @__PURE__ */ jsxs("strong", { children: [
757
+ "\u{1F4C5} ",
758
+ configField.label || "Date"
759
+ ] }),
760
+ /* @__PURE__ */ jsx(
761
+ "button",
762
+ {
763
+ type: "button",
764
+ onClick: () => setConfigId(null),
765
+ style: {
766
+ fontSize: 12,
767
+ padding: "3px 10px",
768
+ borderRadius: 6,
769
+ border: `1px solid ${primaryColor}`,
770
+ background: primaryColor,
771
+ color: "#fff",
772
+ cursor: "pointer"
773
+ },
774
+ children: "Done"
775
+ }
776
+ )
777
+ ]
778
+ }
779
+ ),
780
+ /* @__PURE__ */ jsxs("label", { style: { display: "block", marginBottom: 8 }, children: [
781
+ "Display format",
782
+ /* @__PURE__ */ jsx(
783
+ "select",
784
+ {
785
+ value: configField.dateFormat ?? DEFAULT_DATE_FORMAT,
786
+ onChange: (e) => setConfig(configField.id, { dateFormat: e.target.value }),
787
+ style: {
788
+ display: "block",
789
+ marginTop: 4,
790
+ width: "100%",
791
+ padding: "4px 6px",
792
+ borderRadius: 6,
793
+ border: "1px solid #d1d5db"
794
+ },
795
+ children: DATE_FORMATS.map((fmt) => /* @__PURE__ */ jsx("option", { value: fmt, children: DATE_FORMAT_LABELS[fmt] }, fmt))
796
+ }
797
+ )
798
+ ] }),
799
+ /* @__PURE__ */ jsxs("label", { style: { display: "block" }, children: [
800
+ "Earliest date",
801
+ /* @__PURE__ */ jsx(
802
+ "input",
803
+ {
804
+ type: "date",
805
+ value: configField.minDate ?? "",
806
+ onChange: (e) => setConfig(configField.id, { minDate: e.target.value || null }),
807
+ style: {
808
+ display: "block",
809
+ marginTop: 4,
810
+ padding: "4px 6px",
811
+ borderRadius: 6,
812
+ border: "1px solid #d1d5db"
813
+ }
814
+ }
815
+ ),
816
+ /* @__PURE__ */ jsx("span", { style: { color: "#9ca3af", fontSize: 11 }, children: "Leave blank to use the document's creation date." })
817
+ ] })
818
+ ]
819
+ }
820
+ ) : null
655
821
  ] }),
656
- /* @__PURE__ */ jsxs("div", { style: { flex: "1 1 200px", minWidth: 180, maxWidth: 300 }, children: [
822
+ hideFieldList ? null : /* @__PURE__ */ jsxs("div", { style: { flex: "1 1 200px", minWidth: 180, maxWidth: 300 }, children: [
657
823
  /* @__PURE__ */ jsxs("p", { style: { fontSize: 13, fontWeight: 600, color: "#374151", margin: "0 0 8px" }, children: [
658
- "Signature fields (",
824
+ "Fields (",
659
825
  value.length,
660
826
  ")"
661
827
  ] }),
@@ -677,6 +843,7 @@ function FieldDesigner({
677
843
  cursor: "pointer"
678
844
  },
679
845
  children: [
846
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 13 }, children: f.type === "date" ? "\u{1F4C5}" : "\u270D" }),
680
847
  /* @__PURE__ */ jsx(
681
848
  "input",
682
849
  {
@@ -698,6 +865,27 @@ function FieldDesigner({
698
865
  "p",
699
866
  f.page
700
867
  ] }),
868
+ f.type === "date" ? /* @__PURE__ */ jsx(
869
+ "button",
870
+ {
871
+ type: "button",
872
+ "aria-label": `Configure ${f.label}`,
873
+ onClick: (e) => {
874
+ e.stopPropagation();
875
+ setConfigId(f.id);
876
+ },
877
+ style: {
878
+ border: "none",
879
+ background: "transparent",
880
+ color: "#6b7280",
881
+ cursor: "pointer",
882
+ fontSize: 13,
883
+ lineHeight: 1,
884
+ padding: 0
885
+ },
886
+ children: "\u2699"
887
+ }
888
+ ) : null,
701
889
  /* @__PURE__ */ jsx(
702
890
  "button",
703
891
  {
@@ -730,9 +918,12 @@ function SigningExperience(props) {
730
918
  const primary = props.branding?.primaryColor ?? "#4f46e5";
731
919
  const [signaturePng, setSignaturePng] = useState(null);
732
920
  const [consent, setConsent] = useState({ signerName: "", consent: false });
921
+ const [dateValues, setDateValues] = useState({});
733
922
  const [submitting, setSubmitting] = useState(false);
734
923
  const [error, setError] = useState(null);
735
- const canSubmit = !!signaturePng && consent.consent && consent.signerName.trim().length > 0 && !submitting;
924
+ const dateFields = (props.placements ?? []).filter((p) => p.type === "date" && p.id);
925
+ const allDatesFilled = dateFields.every((f) => dateValues[f.id]);
926
+ const canSubmit = !!signaturePng && consent.consent && consent.signerName.trim().length > 0 && allDatesFilled && !submitting;
736
927
  async function submit() {
737
928
  if (!canSubmit) return;
738
929
  setSubmitting(true);
@@ -744,7 +935,8 @@ function SigningExperience(props) {
744
935
  body: JSON.stringify({
745
936
  signaturePng,
746
937
  signerName: consent.signerName.trim(),
747
- consent: true
938
+ consent: true,
939
+ dateValues
748
940
  })
749
941
  });
750
942
  if (!res.ok) {
@@ -789,6 +981,31 @@ function SigningExperience(props) {
789
981
  /* @__PURE__ */ jsx("label", { style: { fontSize: 13, fontWeight: 600, color: "#374151" }, children: "Your signature" }),
790
982
  /* @__PURE__ */ jsx("div", { style: { marginTop: 6 }, children: /* @__PURE__ */ jsx(SignaturePad, { primaryColor: primary, onChange: setSignaturePng }) })
791
983
  ] }),
984
+ dateFields.length > 0 ? /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: dateFields.map((f) => /* @__PURE__ */ jsxs("div", { children: [
985
+ /* @__PURE__ */ jsxs("label", { style: { fontSize: 13, fontWeight: 600, color: "#374151" }, children: [
986
+ "\u{1F4C5} ",
987
+ f.label || "Date"
988
+ ] }),
989
+ /* @__PURE__ */ jsx(
990
+ "input",
991
+ {
992
+ type: "date",
993
+ value: dateValues[f.id] ?? "",
994
+ min: f.minDate ?? void 0,
995
+ onChange: (e) => setDateValues((prev) => ({ ...prev, [f.id]: e.target.value })),
996
+ style: {
997
+ display: "block",
998
+ marginTop: 6,
999
+ padding: "10px 12px",
1000
+ borderRadius: 8,
1001
+ border: "1px solid #d1d5db",
1002
+ fontSize: 15,
1003
+ width: "100%",
1004
+ maxWidth: 280
1005
+ }
1006
+ }
1007
+ )
1008
+ ] }, f.id)) }) : null,
792
1009
  /* @__PURE__ */ jsx(ConsentBlock, { value: consent, onChange: setConsent, primaryColor: primary }),
793
1010
  error ? /* @__PURE__ */ jsx(
794
1011
  "div",