UNPKG

react-waitlist

Version:

A customizable waitlist form component for React applications

1,320 lines (1,303 loc) 135 kB
import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { createContext, useContext, useState, useEffect, useCallback, useRef } from 'react'; import { Resend } from 'resend'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; /** * Generate a random honeypot field name */ var generateHoneypotFieldName = function () { var prefix = 'hp_'; var randomString = Math.random().toString(36).substring(2, 8); return "".concat(prefix).concat(randomString); }; /** * Get CSS styles for honeypot field */ var getHoneypotStyles = function () { return { position: 'absolute', left: '-9999px', top: '-9999px', opacity: 0, height: 0, width: 0, zIndex: -1, overflow: 'hidden', pointerEvents: 'none', }; }; /** * Check if reCAPTCHA is enabled in the security config */ var isReCaptchaEnabled = function (security) { return Boolean((security === null || security === void 0 ? void 0 : security.enableReCaptcha) && (security === null || security === void 0 ? void 0 : security.reCaptchaSiteKey)); }; /** * Dynamically load fonts from Google Fonts */ /** * Font families */ var fontFamilies = { inter: 'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif', roboto: '"Roboto", "Helvetica", "Arial", sans-serif', }; /** * Default theme configuration */ var defaultTheme = { colors: { primary: '#3182CE', // Blue secondary: '#805AD5', // Purple background: '#FFFFFF', // White text: '#1A202C', // Dark gray error: '#E53E3E', // Red success: '#38A169', // Green gray: { 50: '#F7FAFC', 100: '#EDF2F7', 200: '#E2E8F0', 300: '#CBD5E0', 400: '#A0AEC0', 500: '#718096', 600: '#4A5568', 700: '#2D3748', 800: '#1A202C', 900: '#171923', }, }, typography: { fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', fontSizes: { xs: '0.75rem', // 12px sm: '0.875rem', // 14px md: '1rem', // 16px lg: '1.125rem', // 18px xl: '1.25rem', // 20px }, fontWeights: { regular: 400, medium: 500, bold: 700, }, }, spacing: { xs: '0.25rem', // 4px sm: '0.5rem', // 8px md: '1rem', // 16px lg: '1.5rem', // 24px xl: '2rem', // 32px }, borders: { radius: { sm: '0.125rem', // 2px md: '0.25rem', // 4px lg: '0.5rem', // 8px full: '9999px', }, }, // Component-specific styling components: { container: { padding: '1.5rem', borderRadius: '0.5rem', backgroundColor: '#FFFFFF', boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)', }, title: { fontSize: '1.5rem', fontWeight: 700, color: '#1A202C', marginBottom: '0.75rem', }, description: { fontSize: '1rem', color: '#4A5568', marginBottom: '1.5rem', }, form: { display: 'flex', flexDirection: 'column', gap: '1rem', }, fieldContainer: { marginBottom: '1rem', }, label: { display: 'block', fontSize: '0.875rem', fontWeight: 500, color: '#4A5568', marginBottom: '0.5rem', }, input: { width: '100%', padding: '0.75rem 1rem', fontSize: '1rem', lineHeight: 1.5, color: '#1A202C', backgroundColor: '#FFFFFF', border: '1px solid #E2E8F0', borderRadius: '0.375rem', transition: 'border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out', }, inputError: { borderColor: '#E53E3E', }, checkboxContainer: { display: 'flex', alignItems: 'center', gap: '0.5rem', }, checkbox: { width: '1rem', height: '1rem', }, checkboxLabel: { fontSize: '0.875rem', color: '#4A5568', }, button: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0.75rem 1.5rem', fontSize: '1rem', fontWeight: 500, lineHeight: 1.5, color: '#FFFFFF', backgroundColor: '#3182CE', border: '1px solid transparent', borderRadius: '0.375rem', cursor: 'pointer', transition: 'background-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out', }, buttonLoading: { opacity: 0.7, cursor: 'not-allowed', }, errorMessage: { fontSize: '0.875rem', color: '#E53E3E', marginTop: '0.5rem', }, formError: { padding: '0.75rem 1rem', marginBottom: '1rem', fontSize: '0.875rem', color: '#E53E3E', backgroundColor: 'rgba(229, 62, 62, 0.1)', borderRadius: '0.375rem', border: '1px solid rgba(229, 62, 62, 0.2)', }, required: { color: '#E53E3E', marginLeft: '0.25rem', }, successContainer: { textAlign: 'center', padding: '2rem 1rem', }, successTitle: { fontSize: '1.5rem', fontWeight: 700, color: '#38A169', marginBottom: '0.75rem', }, successDescription: { fontSize: '1rem', color: '#4A5568', }, }, // Animation configuration animation: { enabled: true, duration: '0.3s', easing: 'ease-in-out', effects: { hover: true, focus: true, loading: true, success: true, }, }, }; /** * Tailwind CSS theme */ var tailwindDefaultTheme = { colors: { primary: '#2563EB', // Bright blue secondary: '#8B5CF6', // Violet background: '#F8FAFC', // Very light blue-gray text: '#0F172A', // Very dark blue-gray error: '#DC2626', // Bright red success: '#16A34A', // Bright green gray: { 50: '#F8FAFC', 100: '#F1F5F9', 200: '#E2E8F0', 300: '#CBD5E1', 400: '#94A3B8', 500: '#64748B', 600: '#475569', 700: '#334155', 800: '#1E293B', 900: '#0F172A', }, }, typography: { fontFamily: fontFamilies.inter, fontSizes: { xs: '0.75rem', // 12px sm: '0.875rem', // 14px md: '1rem', // 16px lg: '1.125rem', // 18px xl: '1.25rem', // 20px }, fontWeights: { regular: 400, medium: 500, bold: 700, }, }, spacing: { xs: '0.25rem', // 4px sm: '0.5rem', // 8px md: '1rem', // 16px lg: '1.5rem', // 24px xl: '2rem', // 32px }, borders: { radius: { sm: '0.125rem', // 2px md: '0.375rem', // 6px lg: '0.5rem', // 8px full: '9999px', }, }, // Component-specific styling for Tailwind components: { container: { padding: '2rem', borderRadius: '0.75rem', backgroundColor: '#F8FAFC', boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)', }, title: { fontSize: '1.875rem', fontWeight: 800, color: '#0F172A', marginBottom: '1rem', }, description: { fontSize: '1.125rem', color: '#334155', marginBottom: '2rem', }, form: { display: 'flex', flexDirection: 'column', gap: '1.5rem', }, fieldContainer: { marginBottom: '1.5rem', }, label: { display: 'block', fontSize: '0.875rem', fontWeight: 600, color: '#475569', marginBottom: '0.5rem', textTransform: 'uppercase', letterSpacing: '0.05em', }, input: { width: '100%', padding: '0.75rem 1rem', fontSize: '1rem', lineHeight: 1.5, color: '#0F172A', backgroundColor: '#FFFFFF', border: '1px solid #CBD5E1', borderRadius: '0.5rem', transition: 'border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out', boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.05)', }, inputError: { borderColor: '#DC2626', boxShadow: '0 0 0 1px #DC2626', }, checkboxContainer: { display: 'flex', alignItems: 'center', gap: '0.75rem', }, checkbox: { width: '1.25rem', height: '1.25rem', borderRadius: '0.25rem', border: '2px solid #CBD5E1', }, checkboxLabel: { fontSize: '0.875rem', color: '#334155', }, button: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0.875rem 2rem', fontSize: '1rem', fontWeight: 600, lineHeight: 1.5, color: '#FFFFFF', backgroundColor: '#2563EB', border: 'none', borderRadius: '0.5rem', cursor: 'pointer', transition: 'all 0.2s ease', boxShadow: '0 4px 6px -1px rgba(37, 99, 235, 0.2), 0 2px 4px -1px rgba(37, 99, 235, 0.1)', }, buttonLoading: { opacity: 0.7, cursor: 'not-allowed', }, errorMessage: { fontSize: '0.875rem', color: '#DC2626', marginTop: '0.5rem', fontWeight: 500, }, formError: { padding: '1rem', marginBottom: '1.5rem', fontSize: '0.875rem', color: '#DC2626', backgroundColor: 'rgba(220, 38, 38, 0.1)', borderRadius: '0.5rem', border: '1px solid rgba(220, 38, 38, 0.2)', fontWeight: 500, }, required: { color: '#DC2626', marginLeft: '0.25rem', }, successContainer: { textAlign: 'center', padding: '3rem 2rem', }, successTitle: { fontSize: '2rem', fontWeight: 800, color: '#16A34A', marginBottom: '1rem', }, successDescription: { fontSize: '1.125rem', color: '#334155', lineHeight: 1.6, }, }, // Animation configuration animation: { enabled: true, duration: '0.2s', easing: 'cubic-bezier(0.4, 0, 0.2, 1)', effects: { hover: true, focus: true, loading: true, success: true, }, }, // Framework configuration framework: { type: 'tailwind', }, }; /** * Material UI theme */ var materialUIDefaultTheme = { colors: { primary: '#9C27B0', // Purple secondary: '#FF4081', // Pink accent background: '#FAFAFA', // Light gray background text: '#212121', // Almost black error: '#D32F2F', // Red 700 success: '#388E3C', // Green 700 gray: { 50: '#FAFAFA', 100: '#F5F5F5', 200: '#EEEEEE', 300: '#E0E0E0', 400: '#BDBDBD', 500: '#9E9E9E', 600: '#757575', 700: '#616161', 800: '#424242', 900: '#212121', }, }, typography: { fontFamily: fontFamilies.roboto, fontSizes: { xs: '0.75rem', // 12px sm: '0.875rem', // 14px md: '1rem', // 16px lg: '1.25rem', // 20px xl: '1.5rem', // 24px }, fontWeights: { regular: 400, medium: 500, bold: 700, }, }, spacing: { xs: '0.25rem', // 4px sm: '0.5rem', // 8px md: '1rem', // 16px lg: '1.5rem', // 24px xl: '2rem', // 32px }, borders: { radius: { sm: '0.125rem', // 2px md: '0.25rem', // 4px lg: '0.5rem', // 8px full: '9999px', }, }, // Component-specific styling for Material UI components: { container: { padding: '1.5rem', borderRadius: '0.25rem', backgroundColor: '#FFFFFF', boxShadow: '0px 3px 5px -1px rgba(0,0,0,0.2), 0px 6px 10px 0px rgba(0,0,0,0.14), 0px 1px 18px 0px rgba(0,0,0,0.12)', }, title: { fontSize: '1.5rem', fontWeight: 500, color: '#212121', marginBottom: '0.75rem', letterSpacing: '0.0075em', }, description: { fontSize: '1rem', color: '#616161', marginBottom: '1.25rem', lineHeight: 1.6, letterSpacing: '0.00938em', }, form: { display: 'flex', flexDirection: 'column', gap: '1rem', }, fieldContainer: { marginBottom: '1rem', position: 'relative', }, label: { display: 'block', fontSize: '0.75rem', fontWeight: 400, color: '#9C27B0', marginBottom: '0.25rem', letterSpacing: '0.00938em', transition: 'color 0.2s ease', }, input: { width: '100%', padding: '0.75rem 0.75rem', fontSize: '1rem', lineHeight: 1.5, color: '#212121', backgroundColor: '#FFFFFF', border: 'none', borderBottom: '1px solid #9E9E9E', borderRadius: '0', transition: 'border-color 0.2s ease, box-shadow 0.2s ease', letterSpacing: '0.00938em', }, inputError: { borderBottom: '2px solid #D32F2F', }, checkboxContainer: { display: 'flex', alignItems: 'center', gap: '0.5rem', }, checkbox: { width: '1.25rem', height: '1.25rem', borderRadius: '0.125rem', }, checkboxLabel: { fontSize: '0.875rem', color: '#616161', letterSpacing: '0.01071em', }, button: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0.625rem 1.5rem', fontSize: '0.875rem', fontWeight: 500, lineHeight: 1.75, letterSpacing: '0.02857em', textTransform: 'uppercase', color: '#FFFFFF', backgroundColor: '#9C27B0', border: 'none', borderRadius: '0.25rem', cursor: 'pointer', transition: 'background-color 0.25s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.25s cubic-bezier(0.4, 0, 0.2, 1)', boxShadow: '0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)', }, buttonLoading: { opacity: 0.7, cursor: 'not-allowed', }, errorMessage: { fontSize: '0.75rem', color: '#D32F2F', marginTop: '0.25rem', letterSpacing: '0.03333em', }, formError: { padding: '0.75rem', marginBottom: '1rem', fontSize: '0.875rem', color: '#D32F2F', backgroundColor: 'rgba(211, 47, 47, 0.08)', borderRadius: '0.25rem', border: 'none', letterSpacing: '0.01071em', }, required: { color: '#D32F2F', marginLeft: '0.25rem', }, successContainer: { textAlign: 'center', padding: '2rem 1.5rem', }, successTitle: { fontSize: '1.5rem', fontWeight: 500, color: '#388E3C', marginBottom: '1rem', letterSpacing: '0.0075em', }, successDescription: { fontSize: '1rem', color: '#616161', lineHeight: 1.6, letterSpacing: '0.00938em', }, }, // Animation configuration animation: { enabled: true, duration: '0.3s', easing: 'cubic-bezier(0.4, 0, 0.2, 1)', effects: { hover: true, focus: true, loading: true, success: true, }, }, // Framework configuration framework: { type: 'material-ui', }, }; /** * Merge user theme with default theme * This function ensures that all required theme properties are present * while giving absolute priority to user-defined properties */ function mergeTheme(userTheme) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r; if (!userTheme) { return defaultTheme; } // Deep merge the themes, prioritizing user properties return __assign({ colors: __assign(__assign(__assign({}, defaultTheme.colors), userTheme.colors), { // Only merge gray if user hasn't provided a complete replacement gray: ((_a = userTheme.colors) === null || _a === void 0 ? void 0 : _a.gray) ? (Object.keys(userTheme.colors.gray).length === 0 ? __assign({}, (_b = defaultTheme.colors) === null || _b === void 0 ? void 0 : _b.gray) : __assign(__assign({}, (_c = defaultTheme.colors) === null || _c === void 0 ? void 0 : _c.gray), userTheme.colors.gray)) : (_d = defaultTheme.colors) === null || _d === void 0 ? void 0 : _d.gray }), typography: __assign(__assign(__assign({}, defaultTheme.typography), userTheme.typography), { // Only merge nested objects if user hasn't provided a complete replacement fontSizes: ((_e = userTheme.typography) === null || _e === void 0 ? void 0 : _e.fontSizes) ? __assign(__assign({}, (_f = defaultTheme.typography) === null || _f === void 0 ? void 0 : _f.fontSizes), userTheme.typography.fontSizes) : (_g = defaultTheme.typography) === null || _g === void 0 ? void 0 : _g.fontSizes, fontWeights: ((_h = userTheme.typography) === null || _h === void 0 ? void 0 : _h.fontWeights) ? __assign(__assign({}, (_j = defaultTheme.typography) === null || _j === void 0 ? void 0 : _j.fontWeights), userTheme.typography.fontWeights) : (_k = defaultTheme.typography) === null || _k === void 0 ? void 0 : _k.fontWeights }), spacing: __assign(__assign({}, defaultTheme.spacing), userTheme.spacing), borders: __assign(__assign(__assign({}, defaultTheme.borders), userTheme.borders), { // Only merge radius if user hasn't provided a complete replacement radius: ((_l = userTheme.borders) === null || _l === void 0 ? void 0 : _l.radius) ? __assign(__assign({}, (_m = defaultTheme.borders) === null || _m === void 0 ? void 0 : _m.radius), userTheme.borders.radius) : (_o = defaultTheme.borders) === null || _o === void 0 ? void 0 : _o.radius }), animation: __assign(__assign(__assign({}, defaultTheme.animation), userTheme.animation), { // Only merge effects if user hasn't provided a complete replacement effects: ((_p = userTheme.animation) === null || _p === void 0 ? void 0 : _p.effects) ? __assign(__assign({}, (_q = defaultTheme.animation) === null || _q === void 0 ? void 0 : _q.effects), userTheme.animation.effects) : (_r = defaultTheme.animation) === null || _r === void 0 ? void 0 : _r.effects }), components: __assign(__assign({}, defaultTheme.components), userTheme.components) }, (Object.keys(userTheme).filter(function (key) { return !['colors', 'typography', 'spacing', 'borders', 'animation', 'components'].includes(key); }) .reduce(function (obj, key) { var _a; return (__assign(__assign({}, obj), (_a = {}, _a[key] = userTheme[key], _a))); }, {}))); } /** * Default animation configuration */ var defaultAnimation = { enabled: true, duration: '0.3s', easing: 'ease-in-out', type: 'fade', effects: { hover: true, focus: true, loading: true, success: true, }, }; /** * Get animation styles based on animation configuration and reduced motion preference */ var getAnimationStyles = function (config, reducedMotion) { if (config === void 0) { config = defaultAnimation; } if (reducedMotion === void 0) { reducedMotion = false; } // If animations are disabled or reduced motion is preferred, return empty styles if (!config.enabled || reducedMotion || config.type === 'none') { return { fadeIn: {}, fadeOut: {}, slideIn: {}, slideOut: {}, scaleIn: {}, scaleOut: {}, pulse: {}, spin: {}, }; } var duration = config.duration || '0.3s'; var easing = config.easing || 'ease-in-out'; return { fadeIn: { animation: "fadeIn ".concat(duration, " ").concat(easing), opacity: 1, }, fadeOut: { animation: "fadeOut ".concat(duration, " ").concat(easing), opacity: 0, }, slideIn: { animation: "slideIn ".concat(duration, " ").concat(easing), transform: 'translateY(0)', }, slideOut: { animation: "slideOut ".concat(duration, " ").concat(easing), transform: 'translateY(20px)', }, scaleIn: { animation: "scaleIn ".concat(duration, " ").concat(easing), transform: 'scale(1)', }, scaleOut: { animation: "scaleOut ".concat(duration, " ").concat(easing), transform: 'scale(0.95)', }, pulse: { animation: "pulse 2s ".concat(easing, " infinite"), }, spin: { animation: "spin 1s linear infinite", }, }; }; /** * Default ARIA labels */ var defaultAriaLabels = { form: 'Waitlist signup form', emailField: 'Your email address', submitButton: 'Join the waitlist', successMessage: 'Successfully joined the waitlist', errorMessage: 'Error joining the waitlist', }; /** * Create the ARIA context */ var AriaContext = createContext({ announceStatus: true, highContrast: false, reducedMotion: 'auto', ariaLabels: defaultAriaLabels, announce: function () { }, }); /** * Provider component for accessibility features */ var AriaProvider = function (_a) { var _b, _c, _d, _e; var config = _a.config, children = _a.children; // Merge user config with defaults var mergedConfig = { announceStatus: (_b = config === null || config === void 0 ? void 0 : config.announceStatus) !== null && _b !== void 0 ? _b : true, highContrast: (_c = config === null || config === void 0 ? void 0 : config.highContrast) !== null && _c !== void 0 ? _c : false, reducedMotion: (_e = (_d = config === null || config === void 0 ? void 0 : config.reducedMotion) !== null && _d !== void 0 ? _d : config === null || config === void 0 ? void 0 : config.reduceMotion) !== null && _e !== void 0 ? _e : 'auto', ariaLabels: __assign(__assign({}, defaultAriaLabels), ((config === null || config === void 0 ? void 0 : config.ariaLabels) || (config === null || config === void 0 ? void 0 : config.labels))), }; /** * Announce a message to screen readers */ var announce = function (message, assertive) { if (assertive === void 0) { assertive = false; } if (!mergedConfig.announceStatus) return; // Create or get the live region element var liveRegion = document.getElementById(assertive ? 'aria-live-assertive' : 'aria-live-polite'); if (!liveRegion) { liveRegion = document.createElement('div'); liveRegion.id = assertive ? 'aria-live-assertive' : 'aria-live-polite'; liveRegion.setAttribute('aria-live', assertive ? 'assertive' : 'polite'); liveRegion.setAttribute('aria-atomic', 'true'); liveRegion.className = 'sr-only'; liveRegion.style.position = 'absolute'; liveRegion.style.width = '1px'; liveRegion.style.height = '1px'; liveRegion.style.padding = '0'; liveRegion.style.margin = '-1px'; liveRegion.style.overflow = 'hidden'; liveRegion.style.clip = 'rect(0, 0, 0, 0)'; liveRegion.style.whiteSpace = 'nowrap'; liveRegion.style.border = '0'; document.body.appendChild(liveRegion); } // Update the live region with the new message liveRegion.textContent = message; // Clear the message after a delay setTimeout(function () { if (liveRegion) { liveRegion.textContent = ''; } }, 5000); }; // Context value var contextValue = __assign(__assign({}, mergedConfig), { announce: announce }); return (jsx(AriaContext.Provider, { value: contextValue, children: children })); }; /** * Hook to use ARIA context */ var useAria = function () { var context = useContext(AriaContext); if (!context) { throw new Error('useAria must be used within an AriaProvider'); } return context; }; /** * Hook to handle reduced motion preferences */ var useReducedMotion = function () { var reducedMotion = useAria().reducedMotion; var _a = useState(false), prefersReducedMotion = _a[0], setPrefersReducedMotion = _a[1]; useEffect(function () { // If explicitly set, use that value if (typeof reducedMotion === 'boolean') { setPrefersReducedMotion(reducedMotion); return; } // Check if window.matchMedia is available (for testing environments) if (typeof window !== 'undefined' && window.matchMedia) { // Otherwise, check user's system preference var mediaQuery_1 = window.matchMedia('(prefers-reduced-motion: reduce)'); setPrefersReducedMotion(mediaQuery_1.matches); // Listen for changes var handleChange_1 = function (event) { setPrefersReducedMotion(event.matches); }; // Add event listener with modern API if available if (typeof mediaQuery_1.addEventListener === 'function') { mediaQuery_1.addEventListener('change', handleChange_1); return function () { return mediaQuery_1.removeEventListener('change', handleChange_1); }; } else { // Fallback for older browsers mediaQuery_1.addListener(handleChange_1); return function () { return mediaQuery_1.removeListener(handleChange_1); }; } } // Default to false in environments without matchMedia return undefined; }, [reducedMotion]); return prefersReducedMotion; }; /** * Hook to get ARIA labels */ var useAriaLabels = function () { var ariaLabels = useAria().ariaLabels; return ariaLabels; }; /** * Hook to announce messages to screen readers */ var useAnnounce = function () { var announce = useAria().announce; return announce; }; /** * Hook for Resend audience integration */ var useResendAudience = function (options) { var apiKey = options.apiKey, audienceId = options.audienceId, proxyEndpoint = options.proxyEndpoint; // State var _a = useState(false), loading = _a[0], setLoading = _a[1]; var _b = useState(null), error = _b[0], setError = _b[1]; // Initialize Resend client if API key is provided var resendClient = apiKey ? new Resend(apiKey) : null; /** * Make a request to the Resend API */ var makeRequest = function (action, data) { return __awaiter(void 0, void 0, void 0, function () { var response, method, errorData, responseData, result, _a, err_1; return __generator(this, function (_b) { switch (_b.label) { case 0: setLoading(true); setError(null); _b.label = 1; case 1: _b.trys.push([1, 21, 22, 23]); response = void 0; if (!proxyEndpoint) return [3 /*break*/, 6]; method = 'POST'; if (action === 'update') { method = 'PATCH'; } else if (action === 'remove') { method = 'DELETE'; } else if (action === 'get' || action === 'list') { method = 'GET'; } return [4 /*yield*/, fetch("".concat(proxyEndpoint), { method: method, headers: { 'Content-Type': 'application/json', }, body: data ? JSON.stringify(__assign({ action: action, audienceId: audienceId }, data)) : undefined, })]; case 2: response = _b.sent(); if (!!response.ok) return [3 /*break*/, 4]; return [4 /*yield*/, response.json()]; case 3: errorData = _b.sent(); throw new Error(errorData.message || 'Failed to make request to Resend API'); case 4: return [4 /*yield*/, response.json()]; case 5: responseData = _b.sent(); return [2 /*return*/, responseData]; case 6: if (!resendClient) return [3 /*break*/, 19]; result = void 0; _a = action; switch (_a) { case 'create': return [3 /*break*/, 7]; case 'update': return [3 /*break*/, 9]; case 'remove': return [3 /*break*/, 11]; case 'get': return [3 /*break*/, 13]; case 'list': return [3 /*break*/, 15]; } return [3 /*break*/, 17]; case 7: return [4 /*yield*/, resendClient.contacts.create(__assign(__assign({}, data), { audience_id: audienceId }))]; case 8: result = _b.sent(); return [3 /*break*/, 18]; case 9: return [4 /*yield*/, resendClient.contacts.update(__assign(__assign({}, data), { audience_id: audienceId }))]; case 10: result = _b.sent(); return [3 /*break*/, 18]; case 11: return [4 /*yield*/, resendClient.contacts.remove(__assign(__assign({}, data), { audience_id: audienceId }))]; case 12: result = _b.sent(); return [3 /*break*/, 18]; case 13: return [4 /*yield*/, resendClient.contacts.get(__assign(__assign({}, data), { audience_id: audienceId }))]; case 14: result = _b.sent(); return [3 /*break*/, 18]; case 15: return [4 /*yield*/, resendClient.contacts.list({ audience_id: audienceId, })]; case 16: result = _b.sent(); return [3 /*break*/, 18]; case 17: throw new Error('Invalid action'); case 18: return [2 /*return*/, result]; case 19: throw new Error('Either apiKey or proxyEndpoint must be provided'); case 20: return [3 /*break*/, 23]; case 21: err_1 = _b.sent(); setError(err_1 instanceof Error ? err_1 : new Error('Unknown error')); throw err_1; case 22: setLoading(false); return [7 /*endfinally*/]; case 23: return [2 /*return*/]; } }); }); }; /** * Add a contact to the audience */ var addContact = function (contact) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, makeRequest('create', contact)]; }); }); }; /** * Update a contact in the audience */ var updateContact = function (id, contact) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, makeRequest('update', __assign({ id: id }, contact))]; }); }); }; /** * Remove a contact from the audience */ var removeContact = function (id) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, makeRequest('remove', { id: id })]; }); }); }; /** * Get a contact from the audience */ var getContact = function (id) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, makeRequest('get', { id: id })]; }); }); }; /** * List contacts in the audience */ var listContacts = function () { return __awaiter(void 0, void 0, void 0, function () { var response; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, makeRequest('list')]; case 1: response = _a.sent(); return [2 /*return*/, response.data]; } }); }); }; return { addContact: addContact, updateContact: updateContact, removeContact: removeContact, getContact: getContact, listContacts: listContacts, loading: loading, error: error, }; }; // Store widget IDs for multiple reCAPTCHA instances var widgetId = null; /** * Check if code is running in a browser environment */ var isBrowser = function () { return typeof window !== 'undefined' && typeof document !== 'undefined'; }; /** * Load the reCAPTCHA script dynamically */ var loadReCaptchaScript = function (siteKey) { // Return a resolved promise in SSR environment if (!isBrowser()) { console.info('reCAPTCHA: Running in SSR environment, skipping script loading'); return Promise.resolve(); } return new Promise(function (resolve, reject) { try { // Check if script is already loaded if (document.querySelector("script[src*=\"recaptcha\"]")) { console.info('reCAPTCHA: Script already loaded'); if (window.grecaptcha && window.grecaptcha.ready) { window.grecaptcha.ready(function () { console.info('reCAPTCHA: API ready'); resolve(); }); } else { console.info('reCAPTCHA: Script loaded but API not ready yet, waiting...'); // Wait for grecaptcha to be available var checkInterval_1 = setInterval(function () { if (window.grecaptcha && window.grecaptcha.ready) { clearInterval(checkInterval_1); console.info('reCAPTCHA: API now ready'); window.grecaptcha.ready(function () { resolve(); }); } }, 100); // Set a timeout to prevent infinite waiting setTimeout(function () { clearInterval(checkInterval_1); if (!window.grecaptcha) { console.error('reCAPTCHA: Timed out waiting for API'); reject(new Error('reCAPTCHA API not available after timeout')); } else { resolve(); } }, 5000); } return; } // Create script element console.info("reCAPTCHA: Loading script for site key ".concat(siteKey.substring(0, 5), "...")); var script = document.createElement('script'); script.src = "https://www.google.com/recaptcha/api.js?render=explicit"; script.async = true; script.defer = true; // Set up callbacks script.onload = function () { console.info('reCAPTCHA: Script loaded successfully'); if (window.grecaptcha) { // Check if we need to wait for ready if (window.grecaptcha.ready) { window.grecaptcha.ready(function () { console.info('reCAPTCHA: API ready after script load'); resolve(); }); } else { console.info('reCAPTCHA: API available after script load'); resolve(); } } else { console.error('reCAPTCHA: Script loaded but grecaptcha not available'); reject(new Error('reCAPTCHA not available after script load')); } }; script.onerror = function (error) { console.error('reCAPTCHA: Failed to load script', error); reject(new Error('Failed to load reCAPTCHA script')); }; // Add script to document document.head.appendChild(script); } catch (error) { console.error('reCAPTCHA: Error during script loading:', error); reject(error); } }); }; /** * Ensure reCAPTCHA is rendered with invisible badge */ var ensureReCaptchaRendered = function (siteKey, action) { return new Promise(function (resolve, reject) { try { // If already rendered, return the widget ID if (widgetId !== null && window.grecaptcha) { console.info('reCAPTCHA: Widget already rendered, using existing widget ID:', widgetId); resolve(widgetId); return; } // Check if grecaptcha is available if (!window.grecaptcha || !window.grecaptcha.render) { console.error('reCAPTCHA: grecaptcha.render not available'); reject(new Error('reCAPTCHA API not properly initialized')); return; } // Create a container for the invisible reCAPTCHA var container = document.getElementById('g-recaptcha-container'); if (!container) { container = document.createElement('div'); container.id = 'g-recaptcha-container'; container.style.display = 'none'; document.body.appendChild(container); } // Define the callback function that will be called when reCAPTCHA is completed var callbackName = "recaptchaCallback_".concat(Date.now()); window[callbackName] = function (token) { console.info("reCAPTCHA: Callback executed with token length: ".concat(token ? token.length : 0)); // Store the token in a global variable to retrieve it later window.recaptchaToken = token; }; // Render the reCAPTCHA widget console.info("reCAPTCHA: Rendering widget with site key ".concat(siteKey.substring(0, 5), "...")); widgetId = window.grecaptcha.render(container, { 'sitekey': siteKey, 'callback': callbackName, 'size': 'invisible', 'badge': 'bottomright', 'action': action }); console.info('reCAPTCHA: Widget rendered with ID:', widgetId); resolve(widgetId); } catch (error) { console.error('reCAPTCHA: Error rendering widget:', error); reject(new Error("Failed to render reCAPTCHA: ".concat(error instanceof Error ? error.message : String(error)))); } }); }; /** * Execute reCAPTCHA and get a token */ var executeReCaptcha = function (siteKey_1) { var args_1 = []; for (var _i = 1; _i < arguments.length; _i++) { args_1[_i - 1] = arguments[_i]; } return __awaiter(void 0, __spreadArray([siteKey_1], args_1, true), void 0, function (siteKey, action) { var widgetId_1, token, error_1; var _a, _b; if (action === void 0) { action = 'submit_waitlist'; } return __generator(this, function (_c) { switch (_c.label) { case 0: // Return a dummy token in SSR environment if (!isBrowser()) { console.info('reCAPTCHA: Running in SSR environment, returning dummy token'); return [2 /*return*/, 'ssr-dummy-token']; } _c.label = 1; case 1: _c.trys.push([1, 6, , 7]); if (!!window.grecaptcha) return [3 /*break*/, 3]; console.info('reCAPTCHA: Loading script before execution'); return [4 /*yield*/, loadReCaptchaScript(siteKey)]; case 2: _c.sent(); _c.label = 3; case 3: return [4 /*yield*/, ensureReCaptchaRendered(siteKey, action)]; case 4: widgetId_1 = _c.sent(); // Clear any previous token window.recaptchaToken = null; // Execute the reCAPTCHA challenge console.info("reCAPTCHA: Executing for action \"".concat(action, "\" with widget ID ").concat(widgetId_1)); window.grecaptcha.execute(widgetId_1); return [4 /*yield*/, new Promise(function (resolve, reject) { var checkToken = function () { var token = window.recaptchaToken; if (token) { resolve(token); } else { // Check again after a short delay setTimeout(checkToken, 100); } }; // Start checking for the token checkToken(); // Set a timeout to prevent infinite waiting setTimeout(function () { if (!window.recaptchaToken) { console.error('reCAPTCHA: Timed out waiting for token'); reject(new Error('reCAPTCHA token not received after timeout')); } }, 10000); // 10 seconds timeout })]; case 5: token = _c.sent(); // Check if token is null or undefined if (!token) { console.error('reCAPTCHA: Received null or undefined token'); // Check if site key is valid (first few characters for security) console.error("reCAPTCHA: Site key used: ".concat(siteKey.substring(0, 5), "...")); // Check if grecaptcha is in expected state console.error('reCAPTCHA: grecaptcha state:', { defined: typeof window.grecaptcha !== 'undefined', hasExecute: typeof ((_a = window.grecaptcha) === null || _a === void 0 ? void 0 : _a.execute) === 'function', hasReady: typeof ((_b = window.grecaptcha) === null || _b === void 0 ? void 0 : _b.ready) === 'function', widgetId: widgetId_1 }); throw new Error('reCAPTCHA retur