formanitor 0.0.4 → 0.0.6

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.cjs CHANGED
@@ -90,6 +90,7 @@ function validateField(value, field) {
90
90
  function validateForm(values, fields) {
91
91
  const errors = {};
92
92
  for (const [key, field] of Object.entries(fields)) {
93
+ if (field.type === "static_text") continue;
93
94
  const error = validateField(values[key], field);
94
95
  if (error) {
95
96
  errors[key] = error;
@@ -237,6 +238,7 @@ var FormStore = class {
237
238
  initializeDefaults() {
238
239
  const values = {};
239
240
  this.schema.fields.forEach((field) => {
241
+ if (field.type === "static_text") return;
240
242
  if (field.defaultValue !== void 0) {
241
243
  values[field.id] = field.defaultValue;
242
244
  } else {
@@ -344,13 +346,20 @@ var FormStore = class {
344
346
  serializeValuesForSubmission(values) {
345
347
  const result = { ...values };
346
348
  this.schema.fields.forEach((field) => {
349
+ if (field.type === "static_text") {
350
+ delete result[field.id];
351
+ return;
352
+ }
347
353
  if (field.type === "image_upload" || field.type === "signature") {
348
354
  const raw = values[field.id];
349
355
  if (raw !== void 0 && raw !== null && raw !== "") {
350
- result[field.id] = {
351
- _type: "s3_key",
352
- value: raw
353
- };
356
+ const key = typeof raw === "string" ? raw : raw.s3_key || raw.value || null;
357
+ if (key && typeof key === "string") {
358
+ result[field.id] = {
359
+ _type: "s3_key",
360
+ value: key
361
+ };
362
+ }
354
363
  }
355
364
  }
356
365
  });
@@ -595,15 +604,17 @@ var Label = React11__namespace.forwardRef(({ className, ...props }, ref) => /* @
595
604
  ));
596
605
  Label.displayName = LabelPrimitive__namespace.Root.displayName;
597
606
  var Textarea = React11__namespace.forwardRef(
598
- ({ className, ...props }, ref) => {
607
+ ({ className, height, style, ...props }, ref) => {
599
608
  return /* @__PURE__ */ jsxRuntime.jsx(
600
609
  "textarea",
601
610
  {
602
611
  className: cn(
603
- "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
612
+ "flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
613
+ height == null && "min-h-[80px]",
604
614
  className
605
615
  ),
606
616
  ref,
617
+ style: height != null ? { ...style, height: `${height}px` } : style,
607
618
  ...props
608
619
  }
609
620
  );
@@ -616,6 +627,8 @@ var TextWidget = ({ fieldId }) => {
616
627
  const showError = !!error && (touched || state.submitAttempted);
617
628
  if (!fieldDef) return null;
618
629
  const Component = fieldDef.type === "textarea" || fieldDef.type === "richtext" ? Textarea : Input;
630
+ const isTextarea = fieldDef.type === "textarea" || fieldDef.type === "richtext";
631
+ const height = isTextarea && "height" in fieldDef ? fieldDef.height : void 0;
619
632
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
620
633
  /* @__PURE__ */ jsxRuntime.jsxs(Label, { htmlFor: fieldId, children: [
621
634
  fieldDef.label,
@@ -631,7 +644,8 @@ var TextWidget = ({ fieldId }) => {
631
644
  onBlur: setTouched,
632
645
  disabled,
633
646
  type: fieldDef.type === "number" ? "number" : "text",
634
- className: showError ? "border-red-500" : ""
647
+ className: showError ? "border-red-500" : "",
648
+ ...isTextarea && { height }
635
649
  }
636
650
  ),
637
651
  showError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500", children: error })
@@ -1528,8 +1542,15 @@ var smoothPoint = (prev, curr, smoothing = 0.75) => {
1528
1542
  y: prev.y * smoothing + curr.y * (1 - smoothing)
1529
1543
  };
1530
1544
  };
1545
+ function extractDisplayUrl(value) {
1546
+ if (!value || typeof value !== "object") return null;
1547
+ if (typeof value.presigned_url === "string" && value.presigned_url) {
1548
+ return value.presigned_url;
1549
+ }
1550
+ return null;
1551
+ }
1531
1552
  var SignatureUploadWidget = ({ fieldId }) => {
1532
- const { fieldDef, setValue, setTouched, error, disabled, touched } = useField(fieldId);
1553
+ const { fieldDef, setValue, setTouched, error, disabled, touched, value } = useField(fieldId);
1533
1554
  const store = useFormStore();
1534
1555
  const { state } = useForm();
1535
1556
  const canvasRef = React11.useRef(null);
@@ -1538,13 +1559,14 @@ var SignatureUploadWidget = ({ fieldId }) => {
1538
1559
  const lastTimeRef = React11.useRef(null);
1539
1560
  const lastWidthRef = React11.useRef(2);
1540
1561
  const [isDrawing, setIsDrawing] = React11.useState(false);
1541
- const [hasSignature, setHasSignature] = React11.useState(false);
1562
+ const [hasDrawing, setHasDrawing] = React11.useState(false);
1542
1563
  const [isUploading, setIsUploading] = React11.useState(false);
1564
+ const [isConfirmed, setIsConfirmed] = React11.useState(false);
1543
1565
  const showError = !!error && (touched || state.submitAttempted);
1544
- if (!fieldDef) return null;
1545
- const uploadHandler = store.getUploadHandler();
1546
- if (!uploadHandler) return null;
1566
+ const displayUrl = React11.useMemo(() => extractDisplayUrl(value), [value]);
1567
+ const showPreview = displayUrl !== null;
1547
1568
  React11.useEffect(() => {
1569
+ if (showPreview) return;
1548
1570
  const canvas = canvasRef.current;
1549
1571
  if (!canvas) return;
1550
1572
  const ctx = canvas.getContext("2d");
@@ -1562,7 +1584,9 @@ var SignatureUploadWidget = ({ fieldId }) => {
1562
1584
  ctx.strokeStyle = "#000";
1563
1585
  ctx.fillStyle = "#fff";
1564
1586
  ctx.fillRect(0, 0, width, height);
1565
- }, []);
1587
+ }, [showPreview]);
1588
+ const uploadHandler = store.getUploadHandler();
1589
+ if (!fieldDef || !uploadHandler) return null;
1566
1590
  const getPoint = (event) => {
1567
1591
  const canvas = canvasRef.current;
1568
1592
  const rect = canvas.getBoundingClientRect();
@@ -1575,6 +1599,7 @@ var SignatureUploadWidget = ({ fieldId }) => {
1575
1599
  const startDrawing = (event) => {
1576
1600
  if (disabled) return;
1577
1601
  setIsDrawing(true);
1602
+ setIsConfirmed(false);
1578
1603
  setTouched();
1579
1604
  const ctx = canvasRef.current?.getContext("2d");
1580
1605
  if (!ctx) return;
@@ -1616,23 +1641,31 @@ var SignatureUploadWidget = ({ fieldId }) => {
1616
1641
  lastPointRef.current = smoothed;
1617
1642
  lastTimeRef.current = now;
1618
1643
  lastWidthRef.current = width;
1619
- setHasSignature(true);
1644
+ setHasDrawing(true);
1620
1645
  };
1621
1646
  const stopDrawing = () => {
1622
1647
  setIsDrawing(false);
1623
1648
  lastPointRef.current = null;
1624
1649
  lastTimeRef.current = null;
1625
1650
  };
1626
- const clearCanvas = () => {
1651
+ const clearSignature = () => {
1652
+ if (showPreview) {
1653
+ setValue(null);
1654
+ setTouched();
1655
+ return;
1656
+ }
1627
1657
  const canvas = canvasRef.current;
1628
1658
  const ctx = canvas?.getContext("2d");
1629
- if (!canvas || !ctx) return;
1630
- ctx.fillStyle = "#fff";
1631
- ctx.fillRect(0, 0, 400, 200);
1659
+ if (canvas && ctx) {
1660
+ ctx.fillStyle = "#fff";
1661
+ ctx.fillRect(0, 0, 400, 200);
1662
+ }
1632
1663
  pathRef.current = "";
1633
1664
  lastPointRef.current = null;
1634
- setHasSignature(false);
1665
+ setHasDrawing(false);
1666
+ setIsConfirmed(false);
1635
1667
  setValue(null);
1668
+ setTouched();
1636
1669
  };
1637
1670
  const generateSVG = () => `
1638
1671
  <svg width="400" height="200" xmlns="http://www.w3.org/2000/svg">
@@ -1645,7 +1678,7 @@ var SignatureUploadWidget = ({ fieldId }) => {
1645
1678
  fill="none"/>
1646
1679
  </svg>`.trim();
1647
1680
  const confirmSignature = async () => {
1648
- if (!hasSignature) return;
1681
+ if (!hasDrawing) return;
1649
1682
  setIsUploading(true);
1650
1683
  setTouched();
1651
1684
  try {
@@ -1654,55 +1687,95 @@ var SignatureUploadWidget = ({ fieldId }) => {
1654
1687
  const filename = `signature_${Date.now()}.svg`;
1655
1688
  const key = await uploadHandler(blob, filename);
1656
1689
  setValue(key);
1690
+ setIsConfirmed(true);
1691
+ setHasDrawing(false);
1692
+ } catch (err) {
1693
+ console.error("[SignatureUpload] Upload failed:", err);
1657
1694
  } finally {
1658
1695
  setIsUploading(false);
1659
1696
  }
1660
1697
  };
1698
+ const canClear = showPreview ? !disabled : hasDrawing || isConfirmed;
1661
1699
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1662
1700
  /* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
1663
1701
  fieldDef.label,
1664
1702
  fieldDef.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500", children: "*" })
1665
1703
  ] }),
1666
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1667
- /* @__PURE__ */ jsxRuntime.jsx(
1668
- "canvas",
1669
- {
1670
- ref: canvasRef,
1671
- className: "border border-gray-200 rounded cursor-crosshair touch-none",
1672
- onMouseDown: startDrawing,
1673
- onMouseMove: draw,
1674
- onMouseUp: stopDrawing,
1675
- onMouseLeave: stopDrawing,
1676
- onTouchStart: startDrawing,
1677
- onTouchMove: draw,
1678
- onTouchEnd: stopDrawing
1679
- }
1680
- ),
1681
- /* @__PURE__ */ jsxRuntime.jsx(
1682
- Button,
1683
- {
1684
- type: "button",
1685
- size: "icon",
1686
- variant: "ghost",
1687
- className: "absolute top-2 left-2 bg-white/80",
1688
- onClick: clearCanvas,
1689
- disabled: !hasSignature,
1690
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-4 w-4" })
1691
- }
1692
- )
1693
- ] }),
1694
- /* @__PURE__ */ jsxRuntime.jsxs(
1695
- Button,
1696
- {
1697
- type: "button",
1698
- onClick: confirmSignature,
1699
- disabled: !hasSignature || isUploading,
1700
- className: "text-xs",
1701
- children: [
1702
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4 mr-1" }),
1703
- isUploading ? "Saving\u2026" : "Confirm"
1704
- ]
1705
- }
1704
+ showPreview ? (
1705
+ /* ── Preview mode: existing signature from backend ── */
1706
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1707
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border border-gray-200 rounded bg-white flex items-center justify-start p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
1708
+ "img",
1709
+ {
1710
+ src: displayUrl,
1711
+ alt: "Signature",
1712
+ className: "object-contain max-w-full",
1713
+ style: { width: 400, height: 200 }
1714
+ }
1715
+ ) }),
1716
+ /* @__PURE__ */ jsxRuntime.jsx(
1717
+ Button,
1718
+ {
1719
+ type: "button",
1720
+ size: "icon",
1721
+ variant: "ghost",
1722
+ className: "absolute top-2 left-2 bg-white/80 hover:bg-red-50",
1723
+ onClick: clearSignature,
1724
+ disabled: !canClear,
1725
+ title: "Delete signature and draw a new one",
1726
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-4 w-4" })
1727
+ }
1728
+ )
1729
+ ] })
1730
+ ) : (
1731
+ /* ── Canvas mode: drawing / confirmed ── */
1732
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1733
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1734
+ /* @__PURE__ */ jsxRuntime.jsx(
1735
+ "canvas",
1736
+ {
1737
+ ref: canvasRef,
1738
+ className: `border rounded touch-none ${isConfirmed ? "border-green-300 cursor-default" : "border-gray-200 cursor-crosshair"}`,
1739
+ onMouseDown: startDrawing,
1740
+ onMouseMove: draw,
1741
+ onMouseUp: stopDrawing,
1742
+ onMouseLeave: stopDrawing,
1743
+ onTouchStart: startDrawing,
1744
+ onTouchMove: draw,
1745
+ onTouchEnd: stopDrawing
1746
+ }
1747
+ ),
1748
+ /* @__PURE__ */ jsxRuntime.jsx(
1749
+ Button,
1750
+ {
1751
+ type: "button",
1752
+ size: "icon",
1753
+ variant: "ghost",
1754
+ className: "absolute top-2 left-2 bg-white/80",
1755
+ onClick: clearSignature,
1756
+ disabled: !canClear,
1757
+ title: isConfirmed ? "Clear saved signature" : "Clear canvas",
1758
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-4 w-4" })
1759
+ }
1760
+ )
1761
+ ] }),
1762
+ isConfirmed ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm text-green-600", children: [
1763
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" }),
1764
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Signature saved" })
1765
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(
1766
+ Button,
1767
+ {
1768
+ type: "button",
1769
+ onClick: confirmSignature,
1770
+ disabled: !hasDrawing || isUploading,
1771
+ className: "text-xs",
1772
+ children: [
1773
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4 mr-1" }),
1774
+ isUploading ? "Saving\u2026" : "Confirm"
1775
+ ]
1776
+ }
1777
+ )
1778
+ ] })
1706
1779
  ),
1707
1780
  showError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500", children: error })
1708
1781
  ] });
@@ -2150,6 +2223,16 @@ var FieldRenderer = ({ fieldId }) => {
2150
2223
  return /* @__PURE__ */ jsxRuntime.jsx(SignatureUploadWidget, { fieldId });
2151
2224
  case "editable_table":
2152
2225
  return /* @__PURE__ */ jsxRuntime.jsx(EditableTableWidget, { fieldId });
2226
+ case "static_text": {
2227
+ const def = fieldDef;
2228
+ const size = def.size ?? "regular";
2229
+ const labelClass = size === "large" ? "text-base" : "text-sm";
2230
+ const descClass = size === "large" ? "text-sm" : "text-xs";
2231
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${labelClass} text-muted-foreground`, children: [
2232
+ def.label,
2233
+ def.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: `mt-1 ${descClass} opacity-90`, children: def.description })
2234
+ ] });
2235
+ }
2153
2236
  default:
2154
2237
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-red-500", children: [
2155
2238
  "Unknown field type: ",
@@ -2250,7 +2333,7 @@ var ReadOnlyText = ({ fieldDef, value }) => {
2250
2333
  className: "prose prose-sm max-w-none text-gray-900 border border-gray-200 rounded-md p-3 bg-gray-50",
2251
2334
  dangerouslySetInnerHTML: { __html: displayValue }
2252
2335
  }
2253
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-900 border border-gray-200 rounded-md p-3 bg-gray-50 min-h-[2.5rem] flex items-center", children: displayValue || /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400 italic", children: "No value" }) })
2336
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-900 border border-gray-200 rounded-md p-3 bg-gray-50 min-h-[2.5rem] whitespace-pre-wrap", children: displayValue || /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400 italic", children: "No value" }) })
2254
2337
  ] });
2255
2338
  };
2256
2339
  var ReadOnlySelect = ({ fieldDef, value }) => {
@@ -2563,6 +2646,16 @@ var ReadOnlyFieldRenderer = ({ fieldDef, value }) => {
2563
2646
  return /* @__PURE__ */ jsxRuntime.jsx(ReadOnlySignature, { fieldDef, value });
2564
2647
  case "editable_table":
2565
2648
  return /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyTable, { fieldDef, value });
2649
+ case "static_text": {
2650
+ const def = fieldDef;
2651
+ const size = def.size ?? "regular";
2652
+ const labelClass = size === "large" ? "text-base" : "text-sm";
2653
+ const descClass = size === "large" ? "text-sm" : "text-xs";
2654
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${labelClass} text-muted-foreground`, children: [
2655
+ def.label,
2656
+ def.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: `mt-1 ${descClass} opacity-90`, children: def.description })
2657
+ ] });
2658
+ }
2566
2659
  default:
2567
2660
  return /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyText, { fieldDef, value });
2568
2661
  }