/** * Cookie Banner — Embed Code Generation (CSS, HTML, JS) * * Generates the embeddable cookie banner code from configuration. */ import { CookieBannerConfig, CookieBannerStyling, CookieBannerEmbedCode, LocalizedText, SupportedLanguage, } from '../types' // ============================================================================= // MAIN EXPORT // ============================================================================= export function generateEmbedCode( config: CookieBannerConfig, privacyPolicyUrl: string = '/datenschutz' ): CookieBannerEmbedCode { const css = generateCSS(config.styling) const html = generateHTML(config, privacyPolicyUrl) const js = generateJS(config) const scriptTag = `` return { html, css, js, scriptTag } } // ============================================================================= // CSS GENERATION // ============================================================================= function generateCSS(styling: CookieBannerStyling): string { const positionStyles: Record = { BOTTOM: 'bottom: 0; left: 0; right: 0;', TOP: 'top: 0; left: 0; right: 0;', CENTER: 'top: 50%; left: 50%; transform: translate(-50%, -50%);', } const isDark = styling.theme === 'DARK' const bgColor = isDark ? '#1e293b' : styling.backgroundColor || '#ffffff' const textColor = isDark ? '#f1f5f9' : styling.textColor || '#1e293b' const borderColor = isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)' return ` /* Cookie Banner Styles */ .cookie-banner-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.4); z-index: 9998; opacity: 0; visibility: hidden; transition: all 0.3s ease; } .cookie-banner-overlay.active { opacity: 1; visibility: visible; } .cookie-banner { position: fixed; ${positionStyles[styling.position]} z-index: 9999; background: ${bgColor}; color: ${textColor}; border-radius: ${styling.borderRadius || 12}px; box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1); padding: 24px; max-width: ${styling.maxWidth}px; margin: ${styling.position === 'CENTER' ? '0' : '16px'}; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; transform: translateY(100%); opacity: 0; transition: all 0.3s ease; } .cookie-banner.active { transform: translateY(0); opacity: 1; } .cookie-banner-title { font-size: 18px; font-weight: 600; margin-bottom: 12px; } .cookie-banner-description { font-size: 14px; line-height: 1.5; margin-bottom: 16px; opacity: 0.8; } .cookie-banner-buttons { display: flex; gap: 12px; flex-wrap: wrap; } .cookie-banner-btn { flex: 1; min-width: 120px; padding: 12px 20px; border-radius: ${(styling.borderRadius || 12) / 2}px; font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.2s ease; border: none; } .cookie-banner-btn-primary { background: ${styling.primaryColor}; color: white; } .cookie-banner-btn-primary:hover { filter: brightness(1.1); } .cookie-banner-btn-secondary { background: ${styling.secondaryColor || borderColor}; color: ${textColor}; } .cookie-banner-btn-secondary:hover { filter: brightness(0.95); } .cookie-banner-link { display: block; margin-top: 16px; font-size: 12px; color: ${styling.primaryColor}; text-decoration: none; } .cookie-banner-link:hover { text-decoration: underline; } /* Category Details */ .cookie-banner-details { margin-top: 16px; border-top: 1px solid ${borderColor}; padding-top: 16px; display: none; } .cookie-banner-details.active { display: block; } .cookie-banner-category { display: flex; justify-content: space-between; align-items: center; padding: 12px 0; border-bottom: 1px solid ${borderColor}; } .cookie-banner-category:last-child { border-bottom: none; } .cookie-banner-category-info { flex: 1; } .cookie-banner-category-name { font-weight: 500; font-size: 14px; } .cookie-banner-category-desc { font-size: 12px; opacity: 0.7; margin-top: 4px; } .cookie-banner-toggle { position: relative; width: 48px; height: 28px; background: ${borderColor}; border-radius: 14px; cursor: pointer; transition: all 0.2s ease; } .cookie-banner-toggle.active { background: ${styling.primaryColor}; } .cookie-banner-toggle.disabled { opacity: 0.5; cursor: not-allowed; } .cookie-banner-toggle::after { content: ''; position: absolute; top: 4px; left: 4px; width: 20px; height: 20px; background: white; border-radius: 50%; transition: all 0.2s ease; } .cookie-banner-toggle.active::after { left: 24px; } @media (max-width: 640px) { .cookie-banner { margin: 0; border-radius: ${styling.position === 'CENTER' ? (styling.borderRadius || 12) : 0}px; max-width: 100%; } .cookie-banner-buttons { flex-direction: column; } .cookie-banner-btn { width: 100%; } } `.trim() } // ============================================================================= // HTML GENERATION // ============================================================================= function generateHTML(config: CookieBannerConfig, privacyPolicyUrl: string): string { const categoriesHTML = config.categories .map((cat) => { const isRequired = cat.isRequired return ` ` }) .join('') return ` `.trim() } // ============================================================================= // JS GENERATION // ============================================================================= function generateJS(config: CookieBannerConfig): string { const categoryIds = config.categories.map((c) => c.id) const requiredCategories = config.categories.filter((c) => c.isRequired).map((c) => c.id) return ` (function() { 'use strict'; const COOKIE_NAME = 'cookie_consent'; const COOKIE_EXPIRY_DAYS = 365; const CATEGORIES = ${JSON.stringify(categoryIds)}; const REQUIRED_CATEGORIES = ${JSON.stringify(requiredCategories)}; function getConsent() { const cookie = document.cookie.split('; ').find(row => row.startsWith(COOKIE_NAME + '=')); if (!cookie) return null; try { return JSON.parse(decodeURIComponent(cookie.split('=')[1])); } catch { return null; } } function saveConsent(consent) { const date = new Date(); date.setTime(date.getTime() + (COOKIE_EXPIRY_DAYS * 24 * 60 * 60 * 1000)); document.cookie = COOKIE_NAME + '=' + encodeURIComponent(JSON.stringify(consent)) + ';expires=' + date.toUTCString() + ';path=/;SameSite=Lax'; window.dispatchEvent(new CustomEvent('cookieConsentUpdated', { detail: consent })); } function hasConsent(category) { const consent = getConsent(); if (!consent) return REQUIRED_CATEGORIES.includes(category); return consent[category] === true; } function initBanner() { const banner = document.getElementById('cookieBanner'); const overlay = document.getElementById('cookieBannerOverlay'); const details = document.getElementById('cookieBannerDetails'); if (!banner) return; const consent = getConsent(); if (consent) return; setTimeout(() => { banner.classList.add('active'); overlay.classList.add('active'); }, 500); document.getElementById('cookieBannerAccept')?.addEventListener('click', () => { const consent = {}; CATEGORIES.forEach(cat => consent[cat] = true); saveConsent(consent); closeBanner(); }); document.getElementById('cookieBannerReject')?.addEventListener('click', () => { const consent = {}; CATEGORIES.forEach(cat => consent[cat] = REQUIRED_CATEGORIES.includes(cat)); saveConsent(consent); closeBanner(); }); document.getElementById('cookieBannerCustomize')?.addEventListener('click', () => { details.classList.toggle('active'); }); document.getElementById('cookieBannerSave')?.addEventListener('click', () => { const consent = {}; CATEGORIES.forEach(cat => { const toggle = document.querySelector('.cookie-banner-toggle[data-category="' + cat + '"]'); consent[cat] = toggle?.classList.contains('active') || REQUIRED_CATEGORIES.includes(cat); }); saveConsent(consent); closeBanner(); }); document.querySelectorAll('.cookie-banner-toggle').forEach(toggle => { if (toggle.dataset.required === 'true') return; toggle.addEventListener('click', () => { toggle.classList.toggle('active'); }); }); overlay?.addEventListener('click', () => { // Don't close - user must make a choice }); } function closeBanner() { const banner = document.getElementById('cookieBanner'); const overlay = document.getElementById('cookieBannerOverlay'); banner?.classList.remove('active'); overlay?.classList.remove('active'); } window.CookieConsent = { getConsent, saveConsent, hasConsent, show: () => { document.getElementById('cookieBanner')?.classList.add('active'); document.getElementById('cookieBannerOverlay')?.classList.add('active'); }, hide: closeBanner }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initBanner); } else { initBanner(); } })(); `.trim() }