import { useEffect, useRef, useState } from 'react' interface TurnstileOptions { sitekey: string callback?: (token: string) => void 'error-callback'?: () => void 'expired-callback'?: () => void theme?: 'auto' | 'light' | 'dark' } declare global { interface Window { turnstile?: { render: (el: HTMLElement, opts: TurnstileOptions) => string reset: (id?: string) => void remove: (id: string) => void } } } const SCRIPT_ID = 'cf-turnstile-script' /** * Carga el script de Cloudflare Turnstile (una vez) y renderiza el widget en el * div referenciado. Devuelve el ref para el contenedor y el token resuelto. * Si sitekey es vacĂ­o, no hace nada (captcha desactivado). */ export function useTurnstile(sitekey: string) { const ref = useRef(null) const [token, setToken] = useState('') const rendered = useRef(false) useEffect(() => { if (!sitekey) return let cancelled = false function tryRender() { if (cancelled || rendered.current || !ref.current || !window.turnstile) return rendered.current = true window.turnstile.render(ref.current, { sitekey, theme: 'dark', callback: (t) => setToken(t), 'error-callback': () => setToken(''), 'expired-callback': () => setToken(''), }) } if (window.turnstile) { tryRender() return () => { cancelled = true } } if (!document.getElementById(SCRIPT_ID)) { const s = document.createElement('script') s.id = SCRIPT_ID s.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js' s.async = true s.defer = true document.head.appendChild(s) } const iv = window.setInterval(() => { if (window.turnstile) { window.clearInterval(iv) tryRender() } }, 200) return () => { cancelled = true window.clearInterval(iv) } }, [sitekey]) return { ref, token } }