formanitor 0.0.9 → 0.0.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.cjs +101 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.mjs +101 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -223,7 +223,8 @@ var FormStore = class {
|
|
|
223
223
|
workflowState: initialWorkflowState,
|
|
224
224
|
isValid: true,
|
|
225
225
|
isSubmitting: false,
|
|
226
|
-
submitAttempted: false
|
|
226
|
+
submitAttempted: false,
|
|
227
|
+
pendingUploads: 0
|
|
227
228
|
};
|
|
228
229
|
this.initializeDefaults();
|
|
229
230
|
this.evaluate();
|
|
@@ -485,9 +486,52 @@ var FormStore = class {
|
|
|
485
486
|
this.config.onEvent(event, finalPayload);
|
|
486
487
|
}
|
|
487
488
|
}
|
|
489
|
+
/**
|
|
490
|
+
* Update mutable config properties (callbacks, role) without recreating
|
|
491
|
+
* the store. This allows the host app to pass fresh closures (e.g. an
|
|
492
|
+
* upload handler whose auth token was initially undefined) after the
|
|
493
|
+
* initial render.
|
|
494
|
+
*/
|
|
495
|
+
updateConfig(next) {
|
|
496
|
+
let needsReeval = false;
|
|
497
|
+
if (next.onUpload !== void 0) this.config.onUpload = next.onUpload;
|
|
498
|
+
if (next.onEvent !== void 0) this.config.onEvent = next.onEvent;
|
|
499
|
+
if (next.role !== void 0 && next.role !== this.config.role) {
|
|
500
|
+
this.config.role = next.role;
|
|
501
|
+
needsReeval = true;
|
|
502
|
+
}
|
|
503
|
+
if (needsReeval) {
|
|
504
|
+
this.evaluate();
|
|
505
|
+
this.notify();
|
|
506
|
+
}
|
|
507
|
+
}
|
|
488
508
|
getUploadHandler() {
|
|
489
509
|
return this.config.onUpload;
|
|
490
510
|
}
|
|
511
|
+
preSubmitHandlers = /* @__PURE__ */ new Map();
|
|
512
|
+
registerPreSubmitHandler(fieldId, fn) {
|
|
513
|
+
this.preSubmitHandlers.set(fieldId, fn);
|
|
514
|
+
}
|
|
515
|
+
unregisterPreSubmitHandler(fieldId) {
|
|
516
|
+
this.preSubmitHandlers.delete(fieldId);
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Runs all registered pre-submit handlers in parallel.
|
|
520
|
+
* Call this before getSerializedValuesForSubmission() to ensure any
|
|
521
|
+
* unconfirmed signatures (or similar) are uploaded first.
|
|
522
|
+
*/
|
|
523
|
+
async flushPendingUploads() {
|
|
524
|
+
const handlers = Array.from(this.preSubmitHandlers.values());
|
|
525
|
+
await Promise.all(handlers.map((fn) => fn()));
|
|
526
|
+
}
|
|
527
|
+
incrementPendingUploads() {
|
|
528
|
+
this.state.pendingUploads = (this.state.pendingUploads ?? 0) + 1;
|
|
529
|
+
this.notify();
|
|
530
|
+
}
|
|
531
|
+
decrementPendingUploads() {
|
|
532
|
+
this.state.pendingUploads = Math.max(0, (this.state.pendingUploads ?? 0) - 1);
|
|
533
|
+
this.notify();
|
|
534
|
+
}
|
|
491
535
|
// Mark that a submit attempt has been made so UI can decide when to show errors
|
|
492
536
|
markSubmitAttempted() {
|
|
493
537
|
if (!this.state.submitAttempted) {
|
|
@@ -519,6 +563,11 @@ var FormProvider = ({ schema, config, children }) => {
|
|
|
519
563
|
if (!storeRef.current) {
|
|
520
564
|
storeRef.current = new FormStore(schema, config);
|
|
521
565
|
}
|
|
566
|
+
React11.useEffect(() => {
|
|
567
|
+
if (storeRef.current && config) {
|
|
568
|
+
storeRef.current.updateConfig(config);
|
|
569
|
+
}
|
|
570
|
+
});
|
|
522
571
|
React11.useEffect(() => {
|
|
523
572
|
storeRef.current?.load();
|
|
524
573
|
}, []);
|
|
@@ -549,7 +598,8 @@ function useForm() {
|
|
|
549
598
|
availableTransitions: store.getAvailableTransitions(),
|
|
550
599
|
hasPersistence: store.hasPersistence(),
|
|
551
600
|
getSerializedValues: store.getSerializedValuesForSubmission.bind(store),
|
|
552
|
-
markSubmitAttempted: store.markSubmitAttempted.bind(store)
|
|
601
|
+
markSubmitAttempted: store.markSubmitAttempted.bind(store),
|
|
602
|
+
flushPendingUploads: store.flushPendingUploads.bind(store)
|
|
553
603
|
};
|
|
554
604
|
}
|
|
555
605
|
function useField(fieldId) {
|
|
@@ -1457,6 +1507,7 @@ var ImageUploadWidget = ({ fieldId }) => {
|
|
|
1457
1507
|
}
|
|
1458
1508
|
setIsUploading(true);
|
|
1459
1509
|
setTouched();
|
|
1510
|
+
store.incrementPendingUploads();
|
|
1460
1511
|
try {
|
|
1461
1512
|
const previewUrl = URL.createObjectURL(file);
|
|
1462
1513
|
setPreview(previewUrl);
|
|
@@ -1469,6 +1520,7 @@ var ImageUploadWidget = ({ fieldId }) => {
|
|
|
1469
1520
|
setPreview(null);
|
|
1470
1521
|
} finally {
|
|
1471
1522
|
setIsUploading(false);
|
|
1523
|
+
store.decrementPendingUploads();
|
|
1472
1524
|
}
|
|
1473
1525
|
};
|
|
1474
1526
|
const handleRemove = () => {
|
|
@@ -1568,6 +1620,22 @@ var SignatureUploadWidget = ({ fieldId }) => {
|
|
|
1568
1620
|
const [hasDrawing, setHasDrawing] = React11.useState(false);
|
|
1569
1621
|
const [isUploading, setIsUploading] = React11.useState(false);
|
|
1570
1622
|
const [isConfirmed, setIsConfirmed] = React11.useState(false);
|
|
1623
|
+
const [uploadError, setUploadError] = React11.useState(null);
|
|
1624
|
+
const confirmSignatureRef = React11.useRef(null);
|
|
1625
|
+
React11.useEffect(() => {
|
|
1626
|
+
if (hasDrawing && !isConfirmed) {
|
|
1627
|
+
store.registerPreSubmitHandler(fieldId, async () => {
|
|
1628
|
+
if (confirmSignatureRef.current) {
|
|
1629
|
+
await confirmSignatureRef.current();
|
|
1630
|
+
}
|
|
1631
|
+
});
|
|
1632
|
+
} else {
|
|
1633
|
+
store.unregisterPreSubmitHandler(fieldId);
|
|
1634
|
+
}
|
|
1635
|
+
return () => {
|
|
1636
|
+
store.unregisterPreSubmitHandler(fieldId);
|
|
1637
|
+
};
|
|
1638
|
+
}, [hasDrawing, isConfirmed, fieldId, store]);
|
|
1571
1639
|
const showError = !!error && (touched || state.submitAttempted);
|
|
1572
1640
|
const displayUrl = React11.useMemo(() => extractDisplayUrl(value), [value]);
|
|
1573
1641
|
const showPreview = displayUrl !== null;
|
|
@@ -1606,6 +1674,7 @@ var SignatureUploadWidget = ({ fieldId }) => {
|
|
|
1606
1674
|
if (disabled) return;
|
|
1607
1675
|
setIsDrawing(true);
|
|
1608
1676
|
setIsConfirmed(false);
|
|
1677
|
+
setUploadError(null);
|
|
1609
1678
|
setTouched();
|
|
1610
1679
|
const ctx = canvasRef.current?.getContext("2d");
|
|
1611
1680
|
if (!ctx) return;
|
|
@@ -1686,7 +1755,9 @@ var SignatureUploadWidget = ({ fieldId }) => {
|
|
|
1686
1755
|
const confirmSignature = async () => {
|
|
1687
1756
|
if (!hasDrawing) return;
|
|
1688
1757
|
setIsUploading(true);
|
|
1758
|
+
setUploadError(null);
|
|
1689
1759
|
setTouched();
|
|
1760
|
+
store.incrementPendingUploads();
|
|
1690
1761
|
try {
|
|
1691
1762
|
const svg = generateSVG();
|
|
1692
1763
|
const blob = new Blob([svg], { type: "image/svg+xml" });
|
|
@@ -1697,10 +1768,15 @@ var SignatureUploadWidget = ({ fieldId }) => {
|
|
|
1697
1768
|
setHasDrawing(false);
|
|
1698
1769
|
} catch (err) {
|
|
1699
1770
|
console.error("[SignatureUpload] Upload failed:", err);
|
|
1771
|
+
setUploadError(
|
|
1772
|
+
err instanceof Error ? err.message : "Signature upload failed. Please try again."
|
|
1773
|
+
);
|
|
1700
1774
|
} finally {
|
|
1701
1775
|
setIsUploading(false);
|
|
1776
|
+
store.decrementPendingUploads();
|
|
1702
1777
|
}
|
|
1703
1778
|
};
|
|
1779
|
+
confirmSignatureRef.current = confirmSignature;
|
|
1704
1780
|
const canClear = showPreview ? !disabled : hasDrawing || isConfirmed;
|
|
1705
1781
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1706
1782
|
/* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
|
|
@@ -1768,19 +1844,22 @@ var SignatureUploadWidget = ({ fieldId }) => {
|
|
|
1768
1844
|
isConfirmed ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm text-green-600", children: [
|
|
1769
1845
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" }),
|
|
1770
1846
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Signature saved" })
|
|
1771
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1847
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1848
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1849
|
+
Button,
|
|
1850
|
+
{
|
|
1851
|
+
type: "button",
|
|
1852
|
+
onClick: confirmSignature,
|
|
1853
|
+
disabled: !hasDrawing || isUploading,
|
|
1854
|
+
className: "text-xs",
|
|
1855
|
+
children: [
|
|
1856
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4 mr-1" }),
|
|
1857
|
+
isUploading ? "Saving\u2026" : "Confirm"
|
|
1858
|
+
]
|
|
1859
|
+
}
|
|
1860
|
+
),
|
|
1861
|
+
uploadError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500 mt-1", children: uploadError })
|
|
1862
|
+
] })
|
|
1784
1863
|
] })
|
|
1785
1864
|
),
|
|
1786
1865
|
showError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500", children: error })
|
|
@@ -2766,6 +2845,7 @@ var FormControls = ({
|
|
|
2766
2845
|
onSubmit
|
|
2767
2846
|
}) => {
|
|
2768
2847
|
const { state, availableTransitions, submit, transition, hasPersistence, markSubmitAttempted } = useForm();
|
|
2848
|
+
const hasUploadsInFlight = (state.pendingUploads ?? 0) > 0;
|
|
2769
2849
|
const draftAvailable = onSaveDraft || hasPersistence && submit;
|
|
2770
2850
|
const submitAvailable = onSubmit || availableTransitions.length > 0 && transition;
|
|
2771
2851
|
if (!draftAvailable && !submitAvailable) {
|
|
@@ -2786,7 +2866,8 @@ var FormControls = ({
|
|
|
2786
2866
|
onClick: onSaveDraft || submit,
|
|
2787
2867
|
variant: "outline",
|
|
2788
2868
|
size: "sm",
|
|
2789
|
-
|
|
2869
|
+
disabled: hasUploadsInFlight,
|
|
2870
|
+
children: hasUploadsInFlight ? "Upload in progress\u2026" : "Save Draft"
|
|
2790
2871
|
}
|
|
2791
2872
|
),
|
|
2792
2873
|
submitAvailable && /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: availableTransitions.length > 0 ? availableTransitions.map((t) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2800,9 +2881,9 @@ var FormControls = ({
|
|
|
2800
2881
|
transition(t.to);
|
|
2801
2882
|
}
|
|
2802
2883
|
},
|
|
2803
|
-
disabled: !state.isValid,
|
|
2884
|
+
disabled: !state.isValid || hasUploadsInFlight,
|
|
2804
2885
|
size: "sm",
|
|
2805
|
-
children: availableTransitions.length > 1 ? `Submit to ${t.to}` : "Submit"
|
|
2886
|
+
children: hasUploadsInFlight ? "Upload in progress\u2026" : availableTransitions.length > 1 ? `Submit to ${t.to}` : "Submit"
|
|
2806
2887
|
},
|
|
2807
2888
|
t.to
|
|
2808
2889
|
)) : onSubmit ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2812,9 +2893,9 @@ var FormControls = ({
|
|
|
2812
2893
|
markSubmitAttempted();
|
|
2813
2894
|
onSubmit(state.workflowState || "submit");
|
|
2814
2895
|
},
|
|
2815
|
-
disabled: !state.isValid,
|
|
2896
|
+
disabled: !state.isValid || hasUploadsInFlight,
|
|
2816
2897
|
size: "sm",
|
|
2817
|
-
children: "Submit"
|
|
2898
|
+
children: hasUploadsInFlight ? "Upload in progress\u2026" : "Submit"
|
|
2818
2899
|
}
|
|
2819
2900
|
) : null })
|
|
2820
2901
|
] })
|