react-waitlist
Version:
A customizable waitlist form component for React applications
1,322 lines (1,304 loc) • 136 kB
JavaScript
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var react = require('react');
var resend = require('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 = react.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 (jsxRuntime.jsx(AriaContext.Provider, { value: contextValue, children: children }));
};
/**
* Hook to use ARIA context
*/
var useAria = function () {
var context = react.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 = react.useState(false), prefersReducedMotion = _a[0], setPrefersReducedMotion = _a[1];
react.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 = react.useState(false), loading = _a[0], setLoading = _a[1];
var _b = react.useState(null), error = _b[0], setError = _b[1];
// Initialize Resend client if API key is provided
var resendClient = apiKey ? new resend.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 returned n