Files
ppeasy_vg12/resources/js/components/ReCaptcha.vue
T
2026-10-09 08:21:06 +02:00

73 lines
1.8 KiB
Vue

<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref } from 'vue';
const props = defineProps<{ sitekey: string }>();
const emit = defineEmits<{ verified: [token: string] }>();
const container = ref<HTMLDivElement | null>(null);
const widgetId = ref<number | null>(null);
const SCRIPT_ID = 'recaptcha-api';
const CALLBACK = '__vanguardRecaptchaLoaded';
interface Grecaptcha {
render(el: HTMLElement, options: Record<string, unknown>): number;
reset(id?: number): void;
}
function grecaptcha(): Grecaptcha | undefined {
return (window as unknown as { grecaptcha?: Grecaptcha }).grecaptcha;
}
function renderWidget() {
const api = grecaptcha();
if (!api || !container.value || widgetId.value !== null) {
return;
}
widgetId.value = api.render(container.value, {
sitekey: props.sitekey,
callback: (token: string) => emit('verified', token),
'expired-callback': () => emit('verified', ''),
});
}
onMounted(() => {
if (grecaptcha()) {
renderWidget();
return;
}
(window as unknown as Record<string, unknown>)[CALLBACK] = renderWidget;
if (!document.getElementById(SCRIPT_ID)) {
const script = document.createElement('script');
script.id = SCRIPT_ID;
script.src = `https://www.google.com/recaptcha/api.js?onload=${CALLBACK}&render=explicit`;
script.async = true;
script.defer = true;
document.head.appendChild(script);
}
});
onBeforeUnmount(() => {
if (widgetId.value !== null) {
grecaptcha()?.reset(widgetId.value);
}
});
defineExpose({
reset: () => {
if (widgetId.value !== null) {
grecaptcha()?.reset(widgetId.value);
}
},
});
</script>
<template>
<div ref="container" />
</template>