@josephomills/esign 0.8.0 → 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;
@@ -341,6 +362,8 @@ function FieldDesigner({
341
362
  const [failed, setFailed] = useState(false);
342
363
  const [selectedId, setSelectedId] = useState(value[0]?.id ?? null);
343
364
  const [drawMode, setDrawMode] = useState(false);
365
+ const [pendingType, setPendingType] = useState("signature");
366
+ const [configId, setConfigId] = useState(null);
344
367
  const [drag, setDrag] = useState(null);
345
368
  const [live, setLive] = useState(null);
346
369
  const stageRef = useRef(null);
@@ -433,15 +456,21 @@ function FieldDesigner({
433
456
  function onPointerUp() {
434
457
  if (drag && live && live.w >= MIN_W && live.h >= MIN_H) {
435
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";
436
462
  const f = {
437
463
  id: newId(),
438
- 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 } : {},
439
467
  page,
440
468
  ...live
441
469
  };
442
470
  onChange([...value, f]);
443
471
  setSelectedId(f.id);
444
472
  setDrawMode(false);
473
+ if (isDate) setConfigId(f.id);
445
474
  } else {
446
475
  onChange(value.map((x) => x.id === drag.id ? { ...x, ...live } : x));
447
476
  }
@@ -452,10 +481,15 @@ function FieldDesigner({
452
481
  function removeField(id) {
453
482
  onChange(value.filter((f) => f.id !== id));
454
483
  if (selectedId === id) setSelectedId(null);
484
+ if (configId === id) setConfigId(null);
455
485
  }
456
486
  function rename(id, label) {
457
487
  onChange(value.map((f) => f.id === id ? { ...f, label } : f));
458
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;
459
493
  const handle = (id, geom, c) => {
460
494
  const cx = c[1] === "w" ? geom.x : geom.x + geom.w;
461
495
  const cy = c[0] === "n" ? geom.y : geom.y + geom.h;
@@ -501,25 +535,35 @@ function FieldDesigner({
501
535
  flexWrap: "wrap"
502
536
  },
503
537
  children: [
504
- /* @__PURE__ */ jsx(
505
- "button",
506
- {
507
- type: "button",
508
- onClick: () => setDrawMode((d) => !d),
509
- style: {
510
- fontSize: 13,
511
- fontWeight: 600,
512
- padding: "5px 12px",
513
- borderRadius: 6,
514
- border: `1px solid ${primaryColor}`,
515
- background: drawMode ? primaryColor : "#fff",
516
- color: drawMode ? "#fff" : primaryColor,
517
- 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"
518
562
  },
519
- children: drawMode ? "Now drag on the page\u2026" : "+ Add field"
520
- }
521
- ),
522
- /* @__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." }),
523
567
  numPages > 1 ? /* @__PURE__ */ jsxs("label", { style: { marginLeft: "auto", fontSize: 13, color: "#374151" }, children: [
524
568
  "Page",
525
569
  " ",
@@ -590,8 +634,9 @@ function FieldDesigner({
590
634
  padding: "0 4px"
591
635
  },
592
636
  children: [
593
- "\u270D ",
594
- f.label || "Signature"
637
+ f.type === "date" ? "\u{1F4C5}" : "\u270D",
638
+ " ",
639
+ f.label || (f.type === "date" ? "Date" : "Signature")
595
640
  ]
596
641
  }
597
642
  )
@@ -630,7 +675,39 @@ function FieldDesigner({
630
675
  },
631
676
  children: "\xD7"
632
677
  }
633
- )
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
634
711
  ] }) : null
635
712
  ] }, f.id);
636
713
  }),
@@ -652,11 +729,99 @@ function FieldDesigner({
652
729
  ) : null
653
730
  ]
654
731
  }
655
- )
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
656
821
  ] }),
657
822
  hideFieldList ? null : /* @__PURE__ */ jsxs("div", { style: { flex: "1 1 200px", minWidth: 180, maxWidth: 300 }, children: [
658
823
  /* @__PURE__ */ jsxs("p", { style: { fontSize: 13, fontWeight: 600, color: "#374151", margin: "0 0 8px" }, children: [
659
- "Signature fields (",
824
+ "Fields (",
660
825
  value.length,
661
826
  ")"
662
827
  ] }),
@@ -678,6 +843,7 @@ function FieldDesigner({
678
843
  cursor: "pointer"
679
844
  },
680
845
  children: [
846
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 13 }, children: f.type === "date" ? "\u{1F4C5}" : "\u270D" }),
681
847
  /* @__PURE__ */ jsx(
682
848
  "input",
683
849
  {
@@ -699,6 +865,27 @@ function FieldDesigner({
699
865
  "p",
700
866
  f.page
701
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,
702
889
  /* @__PURE__ */ jsx(
703
890
  "button",
704
891
  {
@@ -731,9 +918,12 @@ function SigningExperience(props) {
731
918
  const primary = props.branding?.primaryColor ?? "#4f46e5";
732
919
  const [signaturePng, setSignaturePng] = useState(null);
733
920
  const [consent, setConsent] = useState({ signerName: "", consent: false });
921
+ const [dateValues, setDateValues] = useState({});
734
922
  const [submitting, setSubmitting] = useState(false);
735
923
  const [error, setError] = useState(null);
736
- 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;
737
927
  async function submit() {
738
928
  if (!canSubmit) return;
739
929
  setSubmitting(true);
@@ -745,7 +935,8 @@ function SigningExperience(props) {
745
935
  body: JSON.stringify({
746
936
  signaturePng,
747
937
  signerName: consent.signerName.trim(),
748
- consent: true
938
+ consent: true,
939
+ dateValues
749
940
  })
750
941
  });
751
942
  if (!res.ok) {
@@ -790,6 +981,31 @@ function SigningExperience(props) {
790
981
  /* @__PURE__ */ jsx("label", { style: { fontSize: 13, fontWeight: 600, color: "#374151" }, children: "Your signature" }),
791
982
  /* @__PURE__ */ jsx("div", { style: { marginTop: 6 }, children: /* @__PURE__ */ jsx(SignaturePad, { primaryColor: primary, onChange: setSignaturePng }) })
792
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,
793
1009
  /* @__PURE__ */ jsx(ConsentBlock, { value: consent, onChange: setConsent, primaryColor: primary }),
794
1010
  error ? /* @__PURE__ */ jsx(
795
1011
  "div",