diff --git a/src/components/Form.astro b/src/components/Form.astro
index 3658438..b0d6f94 100644
--- a/src/components/Form.astro
+++ b/src/components/Form.astro
@@ -15,6 +15,7 @@ interface Props extends HTMLAttributes<"form"> {
resetOnSubmit?: boolean;
validateOn?: string;
showSuccesInput?: boolean;
+ formError?: string;
}
const {
@@ -30,6 +31,7 @@ const {
resetOnSubmit = false,
validateOn = "input,blur,change",
showSuccesInput = false,
+ formError = "",
...rest
}: Props = Astro.props;
@@ -57,6 +59,17 @@ const formId = `f-${Math.random().toString(36).slice(2)}`;
data-validateon={validateOn}
novalidate
>
+
+ {formError ?? ""}
+
+
{
title && (
@@ -98,6 +111,7 @@ const formId = `f-${Math.random().toString(36).slice(2)}`;
];
const form = document.getElementById(formId);
+
if (!form || form.dataset.__afInit) return;
form.dataset.__afInit = "1";
@@ -332,6 +346,12 @@ const formId = `f-${Math.random().toString(36).slice(2)}`;
setError(root, issue.message);
if (!firstRoot) firstRoot = root;
}
+
+ const globalIssue = (zodError.issues ?? []).find(
+ (i) => !i.path || i.path.length === 0,
+ );
+ setFormError(globalIssue?.message || "");
+
if (firstRoot) fieldControls(firstRoot).ctrls[0]?.focus();
}
@@ -374,6 +394,7 @@ const formId = `f-${Math.random().toString(36).slice(2)}`;
showErrors(form, result.error);
return;
}
+ setFormError("");
form.querySelectorAll("[data-field]").forEach(clearState);
if (!onSubmitName) {
form.submit();
@@ -415,13 +436,40 @@ const formId = `f-${Math.random().toString(36).slice(2)}`;
form.addEventListener("wr:set-errors", (e) => {
const map = e.detail || {};
- Object.entries(map).forEach(([name, message]) => {
+ if ("_form" in map) setFormError(map._form);
+
+ for (const [key, val] of Object.entries(map)) {
+ if (key === "_form") continue;
const root = form.querySelector(
- `[data-field="${CSS.escape(name)}"]`,
+ `[data-field="${CSS.escape(key)}"]`,
);
- if (root) setError(root, message || "Invalid value");
- });
+ if (root) {
+ setError(root, val);
+ }
+ }
});
+
+ const formErrEl = form.querySelector("[data-form-error]");
+
+ function setFormError(msg) {
+ if (!formErrEl) return;
+ if (msg && String(msg).trim()) {
+ formErrEl.textContent = String(msg);
+ formErrEl.hidden = false;
+ } else {
+ formErrEl.textContent = "";
+ formErrEl.hidden = true;
+ }
+ }
+
+ form.addEventListener("input", () => setFormError(""));
+
+ form.addEventListener("wr:set-form-error", (e) => {
+ const msg = e.detail;
+ setFormError(msg);
+ });
+
+ form.addEventListener("wr:clear-form-error", () => setFormError(""));
};
if (document.readyState === "loading") {