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 +154 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.mjs +154 -61
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
|
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 [
|
|
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
|
-
|
|
1545
|
-
const
|
|
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
|
-
|
|
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
|
|
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 (
|
|
1630
|
-
|
|
1631
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
1667
|
-
/*
|
|
1668
|
-
|
|
1669
|
-
{
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
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]
|
|
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
|
}
|