UNPKG

@wordpress/components

Version:
172 lines (171 loc) 5.95 kB
// packages/components/src/validated-form-controls/control-with-error.tsx import { __ } from "@wordpress/i18n"; import { cloneElement, forwardRef, useEffect, useId, useState } from "@wordpress/element"; import { ValidityIndicator } from "./validity-indicator.mjs"; import { Fragment as _Fragment, jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime"; function appendRequiredIndicator(label, required, markWhenOptional) { if (required && !markWhenOptional) { return /* @__PURE__ */ _jsxs(_Fragment, { children: [label, " ", `(${__("Required")})`] }); } if (!required && markWhenOptional) { return /* @__PURE__ */ _jsxs(_Fragment, { children: [label, " ", `(${__("Optional")})`] }); } return label; } var VALIDITY_VISIBLE_ATTRIBUTE = "data-validity-visible"; var className = "components-validated-control"; function UnforwardedControlWithError({ required, markWhenOptional, customValidity, getValidityTarget, children }, forwardedRef) { const [errorMessage, setErrorMessage] = useState(); const [statusMessage, setStatusMessage] = useState(); const [showMessage, setShowMessage] = useState(false); const [isTouched, setIsTouched] = useState(false); useEffect(() => { const validityTarget = getValidityTarget(); const handler = () => { setShowMessage(true); validityTarget?.setAttribute(VALIDITY_VISIBLE_ATTRIBUTE, ""); }; validityTarget?.addEventListener("invalid", handler); return () => validityTarget?.removeEventListener("invalid", handler); }, [getValidityTarget]); useEffect(() => { const validityTarget = getValidityTarget(); const suppressNativePopover = (event) => { event.preventDefault(); const target = event.target; const firstErrorInForm = Array.from(target.form?.elements ?? []).find((el) => !el.validity.valid); if (!target.form || firstErrorInForm === target) { target.focus(); } }; const radioSibilings = validityTarget?.type === "radio" && validityTarget?.name ? Array.from(validityTarget?.closest(`.${className}`)?.querySelectorAll(`input[type="radio"][name="${validityTarget?.name}"]`) ?? []).filter((sibling) => sibling !== validityTarget) : []; validityTarget?.addEventListener("invalid", suppressNativePopover); radioSibilings.forEach((sibling) => sibling.addEventListener("invalid", suppressNativePopover)); return () => { validityTarget?.removeEventListener("invalid", suppressNativePopover); radioSibilings.forEach((sibling) => sibling.removeEventListener("invalid", suppressNativePopover)); }; }, [getValidityTarget]); useEffect(() => { const validityTarget = getValidityTarget(); if (!customValidity?.type) { validityTarget?.setCustomValidity(""); setErrorMessage(validityTarget?.validationMessage); setStatusMessage(void 0); return; } switch (customValidity.type) { case "validating": { validityTarget?.setCustomValidity(""); setErrorMessage(void 0); setStatusMessage({ type: "validating", message: customValidity.message }); break; } case "valid": { validityTarget?.setCustomValidity(""); setErrorMessage(validityTarget?.validationMessage); setStatusMessage({ type: "valid", message: customValidity.message }); break; } case "invalid": { validityTarget?.setCustomValidity(customValidity.message ?? ""); setErrorMessage(validityTarget?.validationMessage); setStatusMessage(void 0); break; } } }, [customValidity, getValidityTarget]); useEffect(() => { if (!isTouched || showMessage) { return; } if (customValidity?.type === "validating") { const timer = setTimeout(() => { setShowMessage(true); }, 1e3); return () => clearTimeout(timer); } setShowMessage(true); }, [isTouched, customValidity?.type, showMessage]); const onBlur = (event) => { if (isTouched) { return; } if (!event.relatedTarget || !event.currentTarget.contains(event.relatedTarget)) { setIsTouched(true); getValidityTarget()?.setAttribute(VALIDITY_VISIBLE_ATTRIBUTE, ""); } }; const messageId = useId(); const message = (() => { if (errorMessage) { return /* @__PURE__ */ _jsx(ValidityIndicator, { id: messageId, type: "invalid", message: errorMessage }); } if (statusMessage?.type) { return /* @__PURE__ */ _jsx(ValidityIndicator, { id: messageId, type: statusMessage.type, message: statusMessage.message }); } return null; })(); const visibleMessage = showMessage ? message : null; useEffect(() => { const target = getValidityTarget(); if (!target) { return; } function setDescribedBy(el, shouldAdd) { const ids = (el.getAttribute("aria-describedby") ?? "").split(" ").filter((id) => id && id !== messageId); if (shouldAdd) { ids.push(messageId); } if (ids.length) { el.setAttribute("aria-describedby", ids.join(" ")); } else { el.removeAttribute("aria-describedby"); } } setDescribedBy(target, !!visibleMessage); return () => setDescribedBy(target, false); }, [visibleMessage, messageId, getValidityTarget]); return /* @__PURE__ */ _jsxs("div", { className, ref: forwardedRef, onBlur, children: [cloneElement(children, { label: appendRequiredIndicator(children.props.label, required, markWhenOptional), required }), /* @__PURE__ */ _jsx("div", { "aria-live": "polite", children: visibleMessage })] }); } var ControlWithError = forwardRef(UnforwardedControlWithError); ControlWithError.displayName = "ControlWithError"; export { ControlWithError }; //# sourceMappingURL=control-with-error.mjs.map