UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

383 lines (380 loc) 13.4 kB
'use client'; import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { forwardRef, useState, useMemo, useRef, useEffect } from 'react'; import { cn } from '../../lib/utilsComprehensive.js'; import styles from './GlobalCookieConsent.module.css.js'; import { useReducedMotion } from '../../hooks/useReducedMotion.js'; import { useAnimationContext } from '../../contexts/AnimationContext.js'; import { Box } from '../layout/Box.js'; import { GlassButton } from '../button/GlassButton.js'; import '../button/GlassFab.js'; import '../button/GlassMagneticButton.js'; import { Typography } from '../data-display/Typography.js'; import { GlassModal } from '../modal/GlassModal.js'; import { GlassCheckbox } from '../input/GlassCheckbox.js'; import { useGalileoStateSpring } from '../../hooks/useGalileoStateSpring.js'; import { SpringPresets } from '../../animations/physics/springPhysics.js'; // Cookie management utilities const setCookie = (name, value, days) => { const date = new Date(); date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); const expires = `expires=${date.toUTCString()}`; document.cookie = `${name}=${value};${expires};path=/`; }; const getCookie = name => { const nameEQ = `${name}=`; const ca = document.cookie.split(";"); for (let i = 0; i < ca.length; i++) { let c = ca[i]; while (c.charAt(0) === " ") c = c.substring(1, c.length); if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); } return null; }; const POSITION_CLASS_MAP = { bottom: styles.positionCenterBottom, top: styles.positionCenterTop, "bottom-left": styles.positionBottomLeft, "bottom-right": styles.positionBottomRight, "top-left": styles.positionTopLeft, "top-right": styles.positionTopRight }; /** * Global Cookie Consent component for comprehensive cookie consent management */ const GlobalCookieConsent = /*#__PURE__*/forwardRef(({ title = "Manage Cookie Preferences", message = "We use cookies to enhance your browsing experience, analyze site traffic, and personalize content.", position = "bottom", acceptButtonText = "Accept All", declineButtonText = "Decline All", settingsButtonText = "Save Preferences", onAccept, onDecline, onSave, onCategoryChange, enableSettings = true, glassIntensity = 0.8, privacyPolicyUrl, privacyPolicyText = "Privacy Policy", className, animate = true, delay = 700, timeout = 0, onTimeout, dismissible = true, cookieExpiration = 365, style, cookieCategories = [], customContent, initiallyExpanded = false, useModalForDetails = false, defaultSelectedCategories = [], ...rest }, ref) => { const [visible, setVisible] = useState(false); const [expanded, setExpanded] = useState(initiallyExpanded); const [showDetailsModal, setShowDetailsModal] = useState(false); const [selectedCategories, setSelectedCategories] = useState([]); const prefersReducedMotion = useReducedMotion(); const { defaultSpring } = useAnimationContext(); const shouldAnimate = animate && !prefersReducedMotion; // Memoize initial categories to prevent infinite loops const initialCategories = useMemo(() => { const categories = [...defaultSelectedCategories]; // Always include required categories cookieCategories.filter(category => category.required).forEach(category => { if (!categories.includes(category.id)) { categories.push(category.id); } }); return categories; }, [cookieCategories, defaultSelectedCategories]); // Track if categories have been initialized to prevent loops const categoriesInitialized = useRef(false); // Set initial selected categories only once useEffect(() => { if (!categoriesInitialized.current) { setSelectedCategories(initialCategories); categoriesInitialized.current = true; } }, [initialCategories]); // Check if consent was previously given useEffect(() => { const consentValue = getCookie("cookie-consent"); if (!consentValue) { const timer = setTimeout(() => { setVisible(true); }, delay); return () => clearTimeout(timer); } }, [delay]); // Handle timeout useEffect(() => { if (visible && timeout > 0) { const timer = setTimeout(() => { setVisible(false); if (onTimeout) { onTimeout(); } }, timeout); return () => clearTimeout(timer); } }, [visible, timeout, onTimeout]); const handleToggleCategory = (categoryId, required = false) => { if (required) return; // Can't toggle required categories setSelectedCategories(prevSelected => { const newSelected = prevSelected.includes(categoryId) ? prevSelected.filter(id => id !== categoryId) : [...prevSelected, categoryId]; if (onCategoryChange) { onCategoryChange(newSelected); } return newSelected; }); }; const handleAcceptAll = () => { const allCategoryIds = cookieCategories.map(category => category.id); setCookie("cookie-consent", "accepted", cookieExpiration); setCookie("cookie-categories", JSON.stringify(allCategoryIds), cookieExpiration); setVisible(false); if (onAccept) { onAccept(); } }; const handleDeclineAll = () => { // Only include required categories when declining all const requiredCategoryIds = cookieCategories.filter(category => category.required).map(category => category.id); setCookie("cookie-consent", "declined", cookieExpiration); setCookie("cookie-categories", JSON.stringify(requiredCategoryIds), cookieExpiration); setVisible(false); if (onDecline) { onDecline(); } }; const handleSavePreferences = () => { setCookie("cookie-consent", "customized", cookieExpiration); setCookie("cookie-categories", JSON.stringify(selectedCategories), cookieExpiration); setVisible(false); if (onSave) { onSave(selectedCategories); } }; const handleShowDetails = () => { if (useModalForDetails) { setShowDetailsModal(true); } else { setExpanded(true); } }; // Calculate final spring config const finalSpringConfig = useMemo(() => { const baseConfig = SpringPresets.default; let contextConfig = {}; if (typeof defaultSpring === "string" && defaultSpring in SpringPresets) { contextConfig = SpringPresets[defaultSpring]; } else if (typeof defaultSpring === "object") { contextConfig = defaultSpring ?? {}; } return { ...baseConfig, ...contextConfig }; }, [defaultSpring]); const positionClass = useMemo(() => { const key = position ?? "bottom"; return POSITION_CLASS_MAP[key] ?? styles.positionCenterBottom; }, [position]); const containerStyleVars = useMemo(() => { const depth = Math.max(0.5, Math.min(2, glassIntensity)); const shadowDepth = (32 * depth).toFixed(2); return { "--cookie-blur-scale": depth, "--cookie-box-shadow": `0 12px ${shadowDepth}px rgba(15, 23, 42, 0.18)` }; }, [glassIntensity]); const isTop = position?.startsWith("top"); const exitY = isTop ? -30 : 30; // Use 30px like original CSS // Spring for Opacity const { value: animatedOpacity } = useGalileoStateSpring(visible ? 1 : 0, { ...finalSpringConfig, immediate: !shouldAnimate }); // Spring for TranslateY const { value: animatedTranslateY } = useGalileoStateSpring(visible ? 0 : exitY, { ...finalSpringConfig, immediate: !shouldAnimate }); // Calculate transform const isCentered = position === "top" || position === "bottom"; const animatedStyle = { opacity: animatedOpacity, transform: `translateY(${animatedTranslateY}px)${isCentered ? " translateX(-50%)" : ""}` }; if (!visible) { return jsx("div", { ref: ref, className: cn(styles.container, positionClass, className), style: { ...containerStyleVars, display: "none", ...style }, "aria-hidden": true, ...rest }); } // Create the categories section const renderCategories = () => jsx("div", { className: styles.categoryContainer, children: cookieCategories.map(category => jsxs("div", { className: styles.categoryItem, children: [jsxs("div", { className: styles.categoryHeader, children: [jsx(GlassCheckbox, { checked: selectedCategories.includes(category.id), onCheckedChange: () => handleToggleCategory(category.id, category.required), disabled: category.required }), jsxs(Typography, { variant: "span", className: 'font-semibold', children: [category.name, " ", category.required && jsx("em", { children: "(Required)" })] })] }), jsx("div", { className: styles.categoryDescription, children: jsx(Typography, { variant: "p", children: category.description }) }), category.cookies && category.cookies.length > 0 && jsx(Fragment, { children: jsx("button", { type: "button", className: styles.detailsToggle, "aria-expanded": expanded, "aria-controls": "cookie-details", onClick: e => { // Logic to show cookie details could be expanded here }, children: "Show cookie details" }) })] }, category.id)) }); return jsxs(Fragment, { children: [jsx("div", { ref: ref, className: cn(styles.container, positionClass, className), style: { ...containerStyleVars, ...animatedStyle, ...style }, "aria-hidden": !visible, ...rest, children: jsxs(Box, { children: [jsx(Typography, { variant: "h6", className: 'mb-2 font-semibold', children: title }), jsxs(Typography, { variant: "p", children: [message, privacyPolicyUrl && jsxs(Fragment, { children: [" ", jsx("a", { href: privacyPolicyUrl, target: "_blank", rel: "noopener noreferrer", className: "glass-focus glass-touch-target glass-contrast-guard", children: privacyPolicyText })] })] }), customContent && jsx(Box, { className: "glass-mt-4", children: customContent }), !expanded && cookieCategories.length > 0 && jsx(GlassButton, { variant: "ghost", onClick: handleShowDetails, size: "sm", className: "glass-focus glass-touch-target", children: "Customize settings" }), (expanded || initiallyExpanded) && cookieCategories.length > 0 && jsx("div", { id: "cookie-details", children: renderCategories() }), jsxs("div", { className: styles.buttonContainer, children: [dismissible && jsx(GlassButton, { variant: "outline", onClick: handleDeclineAll, size: "sm", className: "glass-focus glass-touch-target", children: declineButtonText }), expanded && enableSettings && jsx(GlassButton, { variant: "outline", onClick: handleSavePreferences, size: "sm", className: "glass-focus glass-touch-target", children: settingsButtonText }), jsx(GlassButton, { variant: "primary", onClick: handleAcceptAll, size: "sm", className: "glass-focus glass-touch-target", children: acceptButtonText })] })] }) }), useModalForDetails && jsx(GlassModal, { open: showDetailsModal, onClose: () => setShowDetailsModal(false), children: jsxs("div", { className: 'dialog-container', children: [jsxs("div", { className: 'dialog-header', children: [jsx(Typography, { variant: "h6", children: "Cookie Settings" }), jsx(GlassButton, { variant: "ghost", onClick: e => setShowDetailsModal(false), className: "glass-focus glass-touch-target", children: "\u00D7" })] }), jsx("div", { className: 'dialog-content', children: renderCategories() }), jsxs("div", { className: 'dialog-actions', children: [jsx(GlassButton, { variant: "outline", onClick: e => setShowDetailsModal(false), className: "glass-focus glass-touch-target", children: "Cancel" }), jsx(GlassButton, { variant: "primary", onClick: e => { handleSavePreferences(); setShowDetailsModal(false); }, className: "glass-focus glass-touch-target", children: "Save Preferences" })] })] }) })] }); }); GlobalCookieConsent.displayName = "GlobalCookieConsent"; // Glass version of the GlobalCookieConsent const GlassGlobalCookieConsent = /*#__PURE__*/forwardRef((props, ref) => jsx(GlobalCookieConsent, { ref: ref, glassIntensity: 0.9, ...props })); GlassGlobalCookieConsent.displayName = "GlassGlobalCookieConsent"; export { GlassGlobalCookieConsent, GlobalCookieConsent }; //# sourceMappingURL=GlobalCookieConsent.js.map