|
9 | 9 |
|
10 | 10 | <x-dynamic-component :component="$fieldWrapperView" :field="$turnstile"> |
11 | 11 |
|
12 | | - <div x-data="{ |
13 | | - state: $wire.entangle('{{ $statePath }}').defer |
| 12 | + <div wire:ignore |
| 13 | + x-load-js="['https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=onTurnstileLoad']" |
| 14 | + x-data="{ |
| 15 | + state: $wire.entangle('{{ $statePath }}').defer, |
| 16 | + widgetId: null, |
14 | 17 | }" |
15 | | - wire:ignore |
16 | | - x-load-js="['https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback']" |
17 | 18 | x-init="(() => { |
18 | | - let options= { |
| 19 | + let options = { |
| 20 | + sitekey: '{{config('turnstile.turnstile_site_key')}}', |
| 21 | + theme: '{{ $theme }}', |
| 22 | + size: '{{ $size }}', |
| 23 | + language: '{{ $language }}', |
19 | 24 | callback: function (token) { |
20 | 25 | $wire.set('{{ $statePath }}', token) |
21 | 26 | }, |
22 | | -
|
23 | | - errorCallback: function () { |
| 27 | + 'error-callback': function () { |
24 | 28 | $wire.set('{{ $statePath }}', null) |
25 | | - }, |
| 29 | + } |
26 | 30 | } |
27 | 31 |
|
28 | | - window.onloadTurnstileCallback = () => { |
29 | | - turnstile.render($refs.turnstile, options) |
| 32 | + // Render widget when Turnstile API is ready |
| 33 | + const renderWidget = () => { |
| 34 | + if (!window.turnstile || !$refs.turnstile || widgetId !== null) { |
| 35 | + return; |
| 36 | + } |
| 37 | +
|
| 38 | + widgetId = turnstile.render($refs.turnstile, options); |
30 | 39 | } |
31 | 40 |
|
32 | | - resetCaptcha = () => { |
33 | | - turnstile.reset($refs.turnstile) |
| 41 | + // Called when Turnstile API loads |
| 42 | + window.onTurnstileLoad = () => { |
| 43 | + renderWidget(); |
34 | 44 | } |
35 | 45 |
|
36 | | - $wire.on('reset-captcha', () => resetCaptcha()) |
| 46 | + // If API already loaded (on re-render), render immediately |
| 47 | + if (window.turnstile) { |
| 48 | + renderWidget(); |
| 49 | + } |
37 | 50 |
|
38 | | - const observer = new IntersectionObserver((entries) => { |
39 | | - entries.forEach(entry => { |
40 | | - if (entry.isIntersecting && |
41 | | - window.turnstile && |
42 | | - !$refs.turnstile.querySelector('.cf-turnstile')) { |
43 | | - turnstile.render($refs.turnstile, options); |
44 | | - } |
45 | | - }); |
46 | | - }, { threshold: 0.1 }) |
| 51 | + $wire.on('reset-captcha', () => { |
| 52 | + if (widgetId !== null && window.turnstile) { |
| 53 | + turnstile.reset(widgetId); |
| 54 | + } |
| 55 | + }) |
47 | 56 |
|
48 | | - if ($refs.turnstile) { |
49 | | - observer.observe($refs.turnstile); |
| 57 | + // Cleanup when component is destroyed |
| 58 | + return () => { |
| 59 | + if (widgetId !== null && window.turnstile) { |
| 60 | + turnstile.remove(widgetId); |
| 61 | + widgetId = null; |
| 62 | + } |
50 | 63 | } |
51 | 64 | })()" |
52 | 65 | > |
53 | | - <div data-sitekey="{{config('turnstile.turnstile_site_key')}}" |
54 | | - data-theme="{{ $theme }}" |
55 | | - data-language="{{ $language }}" |
56 | | - data-size="{{ $size }}" |
57 | | - x-ref="turnstile" |
58 | | - > |
59 | | - </div> |
| 66 | + <div x-ref="turnstile"></div> |
60 | 67 | </div> |
61 | 68 | </x-dynamic-component> |
0 commit comments