73 lines
1.8 KiB
Vue
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>
|