Files
2026-08-03 12:24:28 +02:00

64 lines
2.2 KiB
JavaScript

'use client';
import { jsx as _jsx } from "react/jsx-runtime";
import { useEffect, useRef } from 'react';
const SCRIPT_SRC = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit';
/** Loads the Turnstile script once, shared across all widget instances. */ function ensureScript() {
if (typeof document === 'undefined') {
return;
}
if (document.querySelector(`script[src="${SCRIPT_SRC}"]`)) {
return;
}
const script = document.createElement('script');
script.src = SCRIPT_SRC;
script.async = true;
script.defer = true;
document.head.appendChild(script);
}
/**
* Cloudflare Turnstile widget — reusable across any form. Renders the challenge
* and reports the token via onToken. The token must then be verified
* server-side (see verifyTurnstile) before a submission is trusted.
*
* siteKey is a prop rather than an env var so all Turnstile config lives in one
* place (SiteIntegrations), consistent with the plugin's panel-managed model.
* The script is injected directly (no next/script dependency) so the widget
* stays framework-agnostic.
*/ export function Turnstile({ onToken, siteKey, theme = 'auto' }) {
const ref = useRef(null);
const widgetId = useRef(null);
useEffect(()=>{
if (!siteKey) {
return;
}
ensureScript();
function render() {
if (!ref.current || !window.turnstile || widgetId.current) {
return;
}
widgetId.current = window.turnstile.render(ref.current, {
callback: (token)=>onToken(token),
'error-callback': ()=>onToken(null),
'expired-callback': ()=>onToken(null),
sitekey: siteKey,
theme
});
}
render();
// Script may load after mount — retry briefly until ready.
const interval = setInterval(render, 300);
return ()=>clearInterval(interval);
}, [
siteKey,
theme,
onToken
]);
if (!siteKey) {
return null;
}
return /*#__PURE__*/ _jsx("div", {
ref: ref
});
}
//# sourceMappingURL=Turnstile.js.map