@modernconsent/widget 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {\n consentState,\n servicesList,\n hasAnswered,\n isPanelOpen,\n openPanel,\n type ConsentState,\n type ServiceMetadata,\n} from '@modernconsent/core';\n\nimport { acceptAll, denyAll, setConsent } from '@modernconsent/core';\n\ntype WidgetMode = 'banner' | 'details';\n\n/**\n * Labels for UI chrome only — NOT for banner title/body content.\n * The integrator provides title and body via <slot name=\"title\"> and <slot name=\"body\">.\n */\ntype WidgetLabels = {\n bannerAcceptAll: string;\n bannerDenyAll: string;\n bannerCustomize: string;\n detailsTitle: string;\n detailsSubtitle: string;\n detailsBack: string;\n detailsAcceptAll: string;\n detailsDenyAll: string;\n serviceAccept: string;\n serviceDeny: string;\n statusAllowed: string;\n statusDenied: string;\n statusPending: string;\n statusAlwaysActive: string;\n categoryPending: string;\n close: string;\n purposeAccept: string;\n purposeDeny: string;\n purposeStatusAllowed: string;\n purposeStatusDenied: string;\n purposeStatusPending: string;\n functionalTitle: string;\n functionalDescription: string;\n};\n\nconst LABELS: Record<string, WidgetLabels> = {\n fr: {\n bannerAcceptAll: 'Tout accepter',\n bannerDenyAll: 'Continuer sans accepter',\n bannerCustomize: 'Personnaliser',\n detailsTitle: 'Personnaliser vos préférences',\n detailsSubtitle: 'Vous pouvez activer ou désactiver chaque service individuellement.',\n detailsBack: 'Retour',\n detailsAcceptAll: 'Tout accepter',\n detailsDenyAll: 'Tout refuser',\n serviceAccept: 'Accepter',\n serviceDeny: 'Refuser',\n statusAllowed: 'Autorisé',\n statusDenied: 'Refusé',\n statusPending: 'En attente',\n statusAlwaysActive: 'Obligatoire',\n categoryPending: 'Services en attente',\n close: 'Fermer',\n purposeAccept: 'Accepter',\n purposeDeny: 'Refuser',\n purposeStatusAllowed: 'Acceptée',\n purposeStatusDenied: 'Refusée',\n purposeStatusPending: 'En attente',\n functionalTitle: 'Fonctionnement du site',\n functionalDescription:\n \"Ces cookies et traceurs sont indispensables au fonctionnement du site, pour fournir nos services et nous assurer de leur bon fonctionnement, pour des raisons de sécurité et pour s'assurer du suivi de vos préférences.\",\n },\n en: {\n bannerAcceptAll: 'Accept all',\n bannerDenyAll: 'Continue without accepting',\n bannerCustomize: 'Customize',\n detailsTitle: 'Customize your preferences',\n detailsSubtitle: 'You can enable or disable each service individually.',\n detailsBack: 'Back',\n detailsAcceptAll: 'Accept all',\n detailsDenyAll: 'Deny all',\n serviceAccept: 'Accept',\n serviceDeny: 'Deny',\n statusAllowed: 'Allowed',\n statusDenied: 'Denied',\n statusPending: 'Pending',\n statusAlwaysActive: 'Always active',\n categoryPending: 'Pending services',\n close: 'Close',\n purposeAccept: 'Accept',\n purposeDeny: 'Deny',\n purposeStatusAllowed: 'Accepted',\n purposeStatusDenied: 'Denied',\n purposeStatusPending: 'Pending',\n functionalTitle: 'Site operation',\n functionalDescription:\n 'These cookies and trackers are essential for the site to function, to provide our services, ensure security, and remember your preferences.',\n },\n};\n\n/**\n * Resolve an i18n field: if it's a Record<lang, string>, pick the right language.\n * Falls back to 'fr', then first available value, then the fallback string.\n */\nfunction resolveI18n(\n value: string | Record<string, string> | undefined,\n lang: string,\n fallback: string,\n): string {\n if (!value) return fallback;\n if (typeof value === 'string') return value;\n return value[lang] ?? value['fr'] ?? Object.values(value)[0] ?? fallback;\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\n/*\n * ─── Theming ──────────────────────────────────────────────────────────────────\n *\n * you can set these CSS custom properties on <mc-consent-widget>\n * or any ancestor:\n *\n * --mc-primary Primary button color (default: #111827)\n * --mc-primary-hover Primary button hover (default: derived)\n * --mc-radius Border radius scale (default: 12px)\n */\nconst STYLES = `\n :host {\n --_primary: var(--mc-primary, #111827);\n --_primary-hover: var(--mc-primary-hover, color-mix(in srgb, var(--_primary) 85%, black));\n --_primary-text: var(--mc-primary-text, #fff);\n --_radius: var(--mc-radius, 12px);\n --_font: var(--mc-font, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n\n position: fixed;\n inset: 0;\n z-index: 99999;\n font-family: var(--_font);\n pointer-events: none;\n display: none;\n }\n\n :host([open]) {\n pointer-events: auto;\n display: block;\n }\n\n *, *::before, *::after { box-sizing: border-box; }\n\n .backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n animation: mc-fade-in 0.2s ease;\n }\n\n @keyframes mc-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n @keyframes mc-slide-up {\n from { opacity: 0; transform: translateY(12px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n .modal {\n background: #ffffff;\n border-radius: var(--_radius);\n max-width: 640px;\n width: calc(100% - 32px);\n max-height: 90vh;\n box-shadow: 0 24px 48px -12px rgba(0, 0, 0, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.05);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: mc-slide-up 0.25s ease;\n }\n\n .header {\n padding: 20px 24px 16px;\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n }\n\n .title {\n font-size: 17px;\n font-weight: 700;\n margin: 0;\n color: #111827;\n letter-spacing: -0.01em;\n }\n\n .subtitle {\n font-size: 13px;\n color: #6b7280;\n margin: 6px 0 0;\n line-height: 1.5;\n }\n\n .close-btn {\n flex-shrink: 0;\n border: none;\n background: #f3f4f6;\n cursor: pointer;\n font-size: 16px;\n line-height: 1;\n padding: 6px 8px;\n color: #6b7280;\n border-radius: 8px;\n transition: background 0.15s;\n }\n .close-btn:hover { background: #e5e7eb; }\n\n .body {\n padding: 0 24px 16px;\n overflow: auto;\n font-size: 13px;\n color: #4b5563;\n line-height: 1.6;\n }\n\n .divider {\n height: 1px;\n background: #e5e7eb;\n margin: 0 24px;\n }\n\n .footer {\n padding: 16px 24px;\n border-top: 1px solid #f3f4f6;\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n justify-content: flex-end;\n }\n\n /* ─── Buttons ─────────────────────────────────────────── */\n\n .btn {\n appearance: none;\n border-radius: 999px;\n padding: 9px 18px;\n font-size: 13px;\n font-weight: 500;\n border: 1px solid transparent;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n letter-spacing: 0.01em;\n }\n\n .btn:active { transform: translateY(1px); }\n\n .btn-primary {\n background: var(--_primary);\n color: var(--_primary-text);\n border-color: var(--_primary);\n }\n .btn-primary:hover {\n background: var(--_primary-hover);\n border-color: var(--_primary-hover);\n }\n\n .btn-outline {\n background: #ffffff;\n border-color: #e5e7eb;\n color: #374151;\n }\n .btn-outline:hover { background: #f9fafb; border-color: #d1d5db; }\n\n .btn-allowed-active {\n background: #059669;\n border-color: #059669;\n color: #fff;\n }\n .btn-allowed-active:hover { background: #047857; border-color: #047857; }\n\n .btn-denied-active {\n background: #dc2626;\n border-color: #dc2626;\n color: #fff;\n }\n .btn-denied-active:hover { background: #b91c1c; border-color: #b91c1c; }\n\n .btn-ghost {\n background: transparent;\n color: #6b7280;\n }\n .btn-ghost:hover { background: #f9fafb; }\n\n /* ─── Categories & Services ───────────────────────────── */\n\n .categories {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .category {\n border-radius: calc(var(--_radius) - 2px);\n border: 1px solid #f3f4f6;\n background: #fafafa;\n padding: 14px;\n }\n\n .category-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n }\n\n .category-title {\n font-size: 13px;\n font-weight: 600;\n color: #111827;\n }\n\n .services-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n\n .service {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 8px 0 6px;\n border-top: 1px solid #f3f4f6;\n }\n\n .service-main { flex: 1; }\n\n .service-name {\n font-size: 13px;\n font-weight: 500;\n color: #111827;\n margin-bottom: 2px;\n }\n\n .service-description { font-size: 12px; color: #6b7280; line-height: 1.4; }\n\n .service-status { font-size: 11px; margin-top: 4px; font-weight: 500; }\n .service-status.allowed { color: #059669; }\n .service-status.denied { color: #dc2626; }\n .service-status.pending { color: #d97706; }\n\n .service-actions { display: flex; gap: 4px; flex-shrink: 0; }\n\n .chip {\n font-size: 11px;\n padding: 3px 10px;\n border-radius: 999px;\n border: 1px solid #e5e7eb;\n color: #6b7280;\n white-space: nowrap;\n font-weight: 500;\n }\n\n .chip-active {\n background: #ecfdf5;\n border-color: #a7f3d0;\n color: #059669;\n }\n\n /* ─── Purpose mode ────────────────────────────────────── */\n\n .category-info {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .category-status {\n font-size: 11px;\n font-weight: 500;\n }\n .category-status.allowed { color: #059669; }\n .category-status.denied { color: #dc2626; }\n .category-status.pending { color: #d97706; }\n\n .purpose-description {\n font-size: 12px;\n color: #6b7280;\n margin: 0 0 8px;\n line-height: 1.5;\n }\n\n .purpose-service-info {\n padding: 4px 0;\n border-top: none;\n }\n .purpose-service-info .service-name {\n font-size: 12px;\n color: #4b5563;\n font-weight: 400;\n }\n .purpose-service-info .service-description {\n font-size: 11px;\n color: #9ca3af;\n }\n`;\n\n// Shared across all instances — instantiated once, never GC'd\nlet _sharedStyleSheet: CSSStyleSheet | null = null;\n\nfunction getSharedStyleSheet(): CSSStyleSheet {\n if (!_sharedStyleSheet) {\n _sharedStyleSheet = new CSSStyleSheet();\n _sharedStyleSheet.replaceSync(STYLES);\n }\n return _sharedStyleSheet;\n}\n\nexport class McConsentWidget extends HTMLElement {\n static observedAttributes = ['lang'];\n\n private shadow: ShadowRoot;\n private mode: WidgetMode = 'banner';\n private _unsubscribers: (() => void)[] = [];\n private _renderScheduled = false;\n private _previouslyFocused: Element | null = null;\n private _keydownHandler: ((e: KeyboardEvent) => void) | null = null;\n\n private services: ServiceMetadata[] = [];\n private consent: ConsentState = {};\n private answered = false;\n private panelOpen = false;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n }\n\n private get labels(): WidgetLabels {\n const lang = this.getAttribute('lang') ?? 'fr';\n return LABELS[lang] ?? LABELS['fr'];\n }\n\n private scheduleRender() {\n if (this._renderScheduled) return;\n this._renderScheduled = true;\n queueMicrotask(() => {\n this._renderScheduled = false;\n this.render();\n });\n }\n\n connectedCallback() {\n // Apply styles once — persists across renders since we're using adoptedStyleSheets\n this.shadow.adoptedStyleSheets = [getSharedStyleSheet()];\n\n // subscribe() fires immediately with current value.\n // ORDER MATTERS: consent + answered must sync BEFORE servicesList\n // because servicesList triggers ensurePanelOpenIfNeeded() which\n // reads this.consent and this.answered to decide whether to show.\n this._unsubscribers.push(\n consentState.subscribe(consent => {\n this.consent = consent;\n this.scheduleRender();\n }),\n hasAnswered.subscribe(answered => {\n this.answered = answered;\n this.scheduleRender();\n }),\n servicesList.subscribe(services => {\n this.services = services;\n this.ensurePanelOpenIfNeeded();\n this.scheduleRender();\n }),\n isPanelOpen.subscribe(open => {\n const previous = this.panelOpen;\n this.panelOpen = open;\n if (open && !previous && this.answered) {\n this.mode = 'details';\n }\n this.scheduleRender();\n }),\n );\n }\n\n disconnectedCallback() {\n this._unsubscribers.forEach(unsub => unsub());\n this._unsubscribers = [];\n this.teardownFocusTrap(false);\n }\n\n attributeChangedCallback() {\n this.scheduleRender();\n }\n\n private ensurePanelOpenIfNeeded() {\n if (this.getPendingServices().length > 0 && !this.answered && !this.panelOpen) {\n openPanel();\n }\n }\n\n private getPendingServices(): ServiceMetadata[] {\n return this.services.filter(s => s.requireConsent && this.consent[s.id] === undefined);\n }\n\n private shouldShowPopup(): boolean {\n return this.panelOpen && this.services.length > 0;\n }\n\n // ─── Focus trap ──────────────────────────────────────────────────────────────\n\n private setupFocusTrap() {\n const modal = this.shadow.querySelector<HTMLElement>('.modal');\n if (!modal) return;\n\n const focusable = [\n ...modal.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), [href], [tabindex]:not([tabindex=\"-1\"])',\n ),\n ];\n if (!focusable.length) return;\n\n // Capture the previously focused element so we can restore it on close\n if (!this._previouslyFocused) {\n this._previouslyFocused = document.activeElement;\n }\n\n // Move focus into the modal on the next frame (ensures paint is complete)\n requestAnimationFrame(() => focusable[0]?.focus());\n\n this._keydownHandler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n isPanelOpen.set(false);\n return;\n }\n\n if (e.key !== 'Tab') return;\n\n // shadow.activeElement gives the focused element within the shadow root\n const active = this.shadow.activeElement;\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n\n if (e.shiftKey && active === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && active === last) {\n e.preventDefault();\n first.focus();\n }\n };\n\n document.addEventListener('keydown', this._keydownHandler);\n }\n\n private teardownFocusTrap(restoreFocus = true) {\n if (this._keydownHandler) {\n document.removeEventListener('keydown', this._keydownHandler);\n this._keydownHandler = null;\n }\n\n if (restoreFocus && this._previouslyFocused && 'focus' in this._previouslyFocused) {\n (this._previouslyFocused as HTMLElement).focus();\n this._previouslyFocused = null;\n }\n }\n\n // ─── Rendering ───────────────────────────────────────────────────────────────\n\n private render() {\n const shouldShow = this.shouldShowPopup();\n\n if (!shouldShow) {\n this.teardownFocusTrap(true);\n this.shadow.innerHTML = '';\n this.removeAttribute('open');\n return;\n }\n\n this.setAttribute('open', '');\n\n // Re-rendering while staying open: remove old keydown handler, keep previouslyFocused\n this.teardownFocusTrap(false);\n\n if (this.mode === 'banner') {\n this.renderBanner();\n } else {\n const displayMode =\n (typeof window !== 'undefined' ? window._modernConsentConfig?.displayMode : undefined) ??\n 'vendor';\n if (displayMode === 'purpose') {\n this.renderPurposeDetails();\n } else {\n this.renderDetails();\n }\n }\n\n this.setupFocusTrap();\n }\n\n private onAcceptAll = () => acceptAll();\n private onDenyAll = () => denyAll();\n\n private onCustomize = () => {\n this.mode = 'details';\n this.scheduleRender();\n };\n\n private onBackToBanner = () => {\n this.mode = 'banner';\n this.scheduleRender();\n };\n\n private onSetConsentForService(id: string, allowed: boolean) {\n setConsent(id, allowed);\n if (this.getPendingServices().filter(s => s.id !== id).length === 0) {\n isPanelOpen.set(false);\n }\n }\n\n private onSetConsentForCategory(category: string, allowed: boolean) {\n const servicesInCategory = this.services.filter(\n s => s.category === category && s.requireConsent,\n );\n servicesInCategory.forEach(s => setConsent(s.id, allowed));\n if (this.getPendingServices().length === 0) {\n isPanelOpen.set(false);\n }\n }\n\n private getCategoryStatus(services: ServiceMetadata[]): 'allowed' | 'denied' | 'pending' {\n const consentRequired = services.filter(s => s.requireConsent);\n if (consentRequired.length === 0) return 'allowed';\n const allAllowed = consentRequired.every(s => this.consent[s.id]);\n if (allAllowed) return 'allowed';\n const allDenied = consentRequired.every(s => !this.consent[s.id]);\n if (allDenied) return 'denied';\n return 'pending';\n }\n\n private groupServicesByCategory(): Record<string, ServiceMetadata[]> {\n const groups: Record<string, ServiceMetadata[]> = {};\n this.services.forEach(service => {\n const cat = service.category || 'Autres';\n if (!groups[cat]) groups[cat] = [];\n groups[cat].push(service);\n });\n return groups;\n }\n\n private renderFunctionalBlock(): string {\n const show =\n typeof window !== 'undefined' && window._modernConsentConfig?.functionalPurpose === true;\n if (!show) return '';\n const l = this.labels;\n return `\n <section class=\"category\">\n <div class=\"category-header\">\n <div class=\"category-info\">\n <span class=\"category-title\">${escapeHtml(l.functionalTitle)}</span>\n </div>\n <span class=\"chip chip-active\">${escapeHtml(l.statusAlwaysActive)}</span>\n </div>\n <div class=\"purpose-description\">${escapeHtml(l.functionalDescription)}</div>\n </section>\n `;\n }\n\n private renderBanner() {\n const l = this.labels;\n\n this.shadow.innerHTML = `\n <div class=\"backdrop\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"mc-title\">\n <div class=\"modal\">\n <div class=\"header\">\n <div>\n <h2 class=\"title\" id=\"mc-title\"><slot name=\"title\"></slot></h2>\n </div>\n <button class=\"close-btn\" type=\"button\" aria-label=\"${escapeHtml(l.close)}\" id=\"mc-close-btn\">✕</button>\n </div>\n <div class=\"body\">\n <slot name=\"body\"></slot>\n </div>\n <div class=\"footer\">\n <button class=\"btn btn-ghost\" type=\"button\" id=\"mc-deny-all-btn\">${escapeHtml(l.bannerDenyAll)}</button>\n <button class=\"btn btn-outline\" type=\"button\" id=\"mc-customize-btn\">${escapeHtml(l.bannerCustomize)}</button>\n <button class=\"btn btn-primary\" type=\"button\" id=\"mc-accept-all-btn\">${escapeHtml(l.bannerAcceptAll)}</button>\n </div>\n </div>\n </div>\n `;\n\n this.shadow.getElementById('mc-accept-all-btn')!.addEventListener('click', this.onAcceptAll);\n this.shadow.getElementById('mc-deny-all-btn')!.addEventListener('click', this.onDenyAll);\n this.shadow.getElementById('mc-customize-btn')!.addEventListener('click', this.onCustomize);\n this.shadow\n .getElementById('mc-close-btn')!\n .addEventListener('click', () => isPanelOpen.set(false));\n }\n\n private renderDetails() {\n const l = this.labels;\n const functionalHtml = this.renderFunctionalBlock();\n const groups = this.groupServicesByCategory();\n\n const bodyHtml = Object.keys(groups)\n .sort()\n .map(cat => {\n const services = groups[cat];\n const hasPending = services.some(s => s.requireConsent && this.consent[s.id] === undefined);\n\n const servicesHtml = services\n .map(s => {\n if (!s.requireConsent) {\n return `\n <div class=\"service\">\n <div class=\"service-main\">\n <div class=\"service-name\">${escapeHtml(s.name)}</div>\n <div class=\"service-description\">${escapeHtml(s.description)}</div>\n </div>\n <span class=\"chip chip-active\">${escapeHtml(l.statusAlwaysActive)}</span>\n </div>\n `;\n }\n\n const consent = this.consent[s.id];\n const statusLabel = consent\n ? l.statusAllowed\n : !consent\n ? l.statusDenied\n : l.statusPending;\n const statusClass = consent ? 'allowed' : !consent ? 'denied' : 'pending';\n\n return `\n <div class=\"service\">\n <div class=\"service-main\">\n <div class=\"service-name\">${escapeHtml(s.name)}</div>\n <div class=\"service-description\">${escapeHtml(s.description)}</div>\n <div class=\"service-status ${statusClass}\">${escapeHtml(statusLabel)}</div>\n </div>\n <div class=\"service-actions\">\n <button class=\"btn btn-outline ${statusClass === 'allowed' ? 'btn-allowed-active' : ''}\" type=\"button\" data-action=\"allow\" data-id=\"${escapeHtml(s.id)}\">\n ${escapeHtml(l.serviceAccept)}\n </button>\n <button class=\"btn btn-outline ${statusClass === 'denied' ? 'btn-denied-active' : ''}\" type=\"button\" data-action=\"deny\" data-id=\"${escapeHtml(s.id)}\">\n ${escapeHtml(l.serviceDeny)}\n </button>\n </div>\n </div>\n `;\n })\n .join('');\n\n return `\n <section class=\"category\">\n <div class=\"category-header\">\n <div class=\"category-title\">${escapeHtml(cat)}</div>\n ${hasPending ? `<span class=\"chip\">${escapeHtml(l.categoryPending)}</span>` : ''}\n </div>\n <div class=\"services-list\">${servicesHtml}</div>\n </section>\n `;\n })\n .join('');\n\n this.shadow.innerHTML = `\n <div class=\"backdrop\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"mc-details-title\">\n <div class=\"modal\">\n <div class=\"header\">\n <div>\n <h2 class=\"title\" id=\"mc-details-title\">${escapeHtml(l.detailsTitle)}</h2>\n <p class=\"subtitle\">${escapeHtml(l.detailsSubtitle)}</p>\n </div>\n <button class=\"close-btn\" type=\"button\" aria-label=\"${escapeHtml(l.close)}\" id=\"mc-close-btn\">✕</button>\n </div>\n <div class=\"body\">\n <div class=\"categories\">${functionalHtml}${bodyHtml}</div>\n </div>\n <div class=\"footer\">\n <button class=\"btn btn-ghost\" type=\"button\" id=\"mc-back-btn\">${escapeHtml(l.detailsBack)}</button>\n <button class=\"btn btn-outline\" type=\"button\" id=\"mc-deny-all-btn\">${escapeHtml(l.detailsDenyAll)}</button>\n <button class=\"btn btn-primary\" type=\"button\" id=\"mc-accept-all-btn\">${escapeHtml(l.detailsAcceptAll)}</button>\n </div>\n </div>\n </div>\n `;\n\n this.shadow.getElementById('mc-accept-all-btn')!.addEventListener('click', this.onAcceptAll);\n this.shadow.getElementById('mc-deny-all-btn')!.addEventListener('click', this.onDenyAll);\n this.shadow.getElementById('mc-back-btn')!.addEventListener('click', this.onBackToBanner);\n this.shadow\n .getElementById('mc-close-btn')!\n .addEventListener('click', () => isPanelOpen.set(false));\n\n this.shadow.querySelectorAll<HTMLButtonElement>('[data-action]').forEach(btn => {\n btn.addEventListener('click', () => {\n this.onSetConsentForService(btn.dataset.id!, btn.dataset.action === 'allow');\n });\n });\n }\n\n private renderPurposeDetails() {\n const l = this.labels;\n const lang = this.getAttribute('lang') ?? 'fr';\n const functionalHtml = this.renderFunctionalBlock();\n const groups = this.groupServicesByCategory();\n const configPurposes =\n typeof window !== 'undefined' ? window._modernConsentConfig?.purposes : undefined;\n\n const bodyHtml = Object.keys(groups)\n .sort()\n .map(cat => {\n const services = groups[cat];\n const allNonConsent = services.every(s => !s.requireConsent);\n const status = this.getCategoryStatus(services);\n\n // Resolve purpose label: vendor-level (i18n) > config-level > raw category name\n const vendorWithPurpose = services.find(s => s.purposeLabel);\n const purposeLabel = resolveI18n(\n vendorWithPurpose?.purposeLabel,\n lang,\n configPurposes?.[cat]?.label ?? cat,\n );\n const purposeDescription = resolveI18n(\n vendorWithPurpose?.purposeDescription,\n lang,\n configPurposes?.[cat]?.description ?? '',\n );\n\n const actionsHtml = allNonConsent\n ? `<span class=\"chip chip-active\">${escapeHtml(l.statusAlwaysActive)}</span>`\n : `\n <div class=\"service-actions\">\n <button class=\"btn btn-outline ${status === 'allowed' ? 'btn-allowed-active' : ''}\" type=\"button\" data-category=\"${escapeHtml(cat)}\" data-cat-action=\"allow\">\n ${escapeHtml(l.purposeAccept)}\n </button>\n <button class=\"btn btn-outline ${status === 'denied' ? 'btn-denied-active' : ''}\" type=\"button\" data-category=\"${escapeHtml(cat)}\" data-cat-action=\"deny\">\n ${escapeHtml(l.purposeDeny)}\n </button>\n </div>\n `;\n\n const vendorsHtml = services\n .map(\n s => `\n <div class=\"service purpose-service-info\">\n <div class=\"service-main\">\n <div class=\"service-name\">${escapeHtml(s.name)}</div>\n <div class=\"service-description\">${escapeHtml(s.description)}</div>\n </div>\n </div>\n `,\n )\n .join('');\n\n return `\n <section class=\"category\">\n <div class=\"category-header\">\n <div class=\"category-info\">\n <span class=\"category-title\">${escapeHtml(purposeLabel)}</span>\n </div>\n ${actionsHtml}\n </div>\n ${purposeDescription ? `<div class=\"purpose-description\">${escapeHtml(purposeDescription)}</div>` : ''}\n <div class=\"services-list\">${vendorsHtml}</div>\n </section>\n `;\n })\n .join('');\n\n this.shadow.innerHTML = `\n <div class=\"backdrop\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"mc-details-title\">\n <div class=\"modal\">\n <div class=\"header\">\n <div>\n <h2 class=\"title\" id=\"mc-details-title\">${escapeHtml(l.detailsTitle)}</h2>\n <p class=\"subtitle\">${escapeHtml(l.detailsSubtitle)}</p>\n </div>\n <button class=\"close-btn\" type=\"button\" aria-label=\"${escapeHtml(l.close)}\" id=\"mc-close-btn\">✕</button>\n </div>\n <div class=\"body\">\n <div class=\"categories\">${functionalHtml}${bodyHtml}</div>\n </div>\n <div class=\"footer\">\n <button class=\"btn btn-ghost\" type=\"button\" id=\"mc-back-btn\">${escapeHtml(l.detailsBack)}</button>\n <button class=\"btn btn-outline\" type=\"button\" id=\"mc-deny-all-btn\">${escapeHtml(l.detailsDenyAll)}</button>\n <button class=\"btn btn-primary\" type=\"button\" id=\"mc-accept-all-btn\">${escapeHtml(l.detailsAcceptAll)}</button>\n </div>\n </div>\n </div>\n `;\n\n this.shadow.getElementById('mc-accept-all-btn')!.addEventListener('click', this.onAcceptAll);\n this.shadow.getElementById('mc-deny-all-btn')!.addEventListener('click', this.onDenyAll);\n this.shadow.getElementById('mc-back-btn')!.addEventListener('click', this.onBackToBanner);\n this.shadow\n .getElementById('mc-close-btn')!\n .addEventListener('click', () => isPanelOpen.set(false));\n\n this.shadow.querySelectorAll<HTMLButtonElement>('[data-cat-action]').forEach(btn => {\n btn.addEventListener('click', () => {\n this.onSetConsentForCategory(btn.dataset.category!, btn.dataset.catAction === 'allow');\n });\n });\n }\n}\n\nif (typeof window !== 'undefined' && !customElements.get('mc-consent-widget')) {\n customElements.define('mc-consent-widget', McConsentWidget);\n}\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAEP,SAAS,WAAW,SAAS,kBAAkB;AAkC/C,IAAM,SAAuC;AAAA,EAC3C,IAAI;AAAA,IACF,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,aAAa;AAAA,IACb,eAAe;AAAA,IACf,cAAc;AAAA,IACd,eAAe;AAAA,IACf,oBAAoB;AAAA,IACpB,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,eAAe;AAAA,IACf,aAAa;AAAA,IACb,sBAAsB;AAAA,IACtB,qBAAqB;AAAA,IACrB,sBAAsB;AAAA,IACtB,iBAAiB;AAAA,IACjB,uBACE;AAAA,EACJ;AAAA,EACA,IAAI;AAAA,IACF,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,aAAa;AAAA,IACb,eAAe;AAAA,IACf,cAAc;AAAA,IACd,eAAe;AAAA,IACf,oBAAoB;AAAA,IACpB,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,eAAe;AAAA,IACf,aAAa;AAAA,IACb,sBAAsB;AAAA,IACtB,qBAAqB;AAAA,IACrB,sBAAsB;AAAA,IACtB,iBAAiB;AAAA,IACjB,uBACE;AAAA,EACJ;AACF;AAMA,SAAS,YACP,OACA,MACA,UACQ;AACR,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,SAAO,MAAM,IAAI,KAAK,MAAM,IAAI,KAAK,OAAO,OAAO,KAAK,EAAE,CAAC,KAAK;AAClE;AAEA,SAAS,WAAW,KAAqB;AACvC,SAAO,IACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,OAAO;AAC1B;AAYA,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":[]}
package/dist/mc-widget.js DELETED
@@ -1,413 +0,0 @@
1
- "use strict";var ModernConsent=(()=>{var y=class{value;subscribers=new Set;constructor(e){this.value=e}get(){return this.value}set(e){this.value!==e&&(this.value=e,this.notify())}update(e){this.set(e(this.value))}subscribe(e){return this.subscribers.add(e),e(this.value),()=>this.subscribers.delete(e)}notify(){this.subscribers.forEach(e=>e(this.value))}};function Q(){let t=new Uint8Array(16);crypto.getRandomValues(t),t[6]=t[6]&15|64,t[8]=t[8]&63|128;let e=Array.from(t,n=>n.toString(16).padStart(2,"0")).join("");return[e.slice(0,8),e.slice(8,12),e.slice(12,16),e.slice(16,20),e.slice(20)].join("-")}var A=()=>typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():Q();var X="mc_consent_state";function Z(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var ee=t=>{if(typeof document>"u")return null;let e=document.cookie.match(new RegExp(`(?:^|; )${Z(t)}=([^;]*)`));return e?decodeURIComponent(e[1]):null},te=(t,e,n={})=>{if(typeof document>"u")return;let{days:i=365,path:o="/",domain:s,sameSite:r="Lax",secure:c=!1}=n,l=new Date;l.setDate(l.getDate()+i);let d=`${t}=${encodeURIComponent(e)};expires=${l.toUTCString()};path=${o};SameSite=${r}`;if(s){let u=typeof window<"u"?window.location.hostname:"";u&&!u.endsWith(s.replace(/^\./,""))&&console.warn(`[modern-consent] cookieDomain "${s}" does not match current hostname "${u}". The cookie will be silently rejected by the browser. Remove cookieDomain for local development.`),d+=`;domain=${s}`}(c||r==="None")&&(d+=";Secure"),document.cookie=d},f=new y({}),g=new y(!1),m=new y([]),p=new y(!1),$=()=>p.set(!0),k=[],H=t=>{let e=t?.cookieName||X,n=t?.cookieDomain,i=t?.consentVersion,o={consent:{},answered:!1};if(typeof window<"u"){let s=ee(e);if(s)try{o=JSON.parse(s)}catch(r){console.error("[modern-consent] Error parsing consent cookie",r)}}if(f.set(o.consent||{}),g.set(o.answered||!1),o.answered&&i&&o.version!==i&&(g.set(!1),p.set(!0)),typeof window<"u"){k.forEach(r=>r()),k=[];let s=()=>{let r={consent:f.get(),answered:g.get(),timestamp:Date.now(),version:i,consentId:A()};te(e,JSON.stringify(r),{domain:n,secure:window.location.protocol==="https:"})};k.push(f.subscribe(s)),k.push(g.subscribe(s))}};var C=[];function q(t){return C.push(t),()=>{let e=C.indexOf(t);e!==-1&&C.splice(e,1)}}function L(t){for(let e=C.length-1;e>=0;e--){let n=C[e](t);if(n)return n}}var M=new Map,x=new Map,_=new Map;function T(t){let e=x.get(t);if(!e?.artifacts)return;let n=_.get(t),i=typeof e.artifacts=="function"?e.artifacts(n):e.artifacts,o=window._modernConsentConfig?.cookieDomain;i.forEach(s=>{document.cookie=`${s}=; max-age=0; path=/`,o&&(document.cookie=`${s}=; max-age=0; path=/; domain=${o}`)})}function V(t){let{id:e,category:n,loader:i,config:o={}}=t;M.has(e)||(M.set(e,i),i().then(s=>{let r=s.default||s;x.set(e,r),o!==void 0&&_.set(e,o),r?.setup&&r.setup(o),r?.link&&r.link.length>0&&r.link.forEach(c=>{if(c.condition&&!c.condition({vendorConfig:o,consentConfig:window._modernConsentConfig}))return;let l=L(c.vendor);l?V({id:c.vendor,category:n,config:c.config??{},loader:l}):console.warn(`[modern-consent] Dependency "${c.vendor}" not found for "${e}"`)}),m.update(c=>c.some(l=>l.id===e)?c:[...c,{id:e,name:r.name,description:r.description,category:r.category,purposeLabel:r.purposeLabel,purposeDescription:r.purposeDescription,loaded:!1,requireConsent:r.requireConsent}]),ne(e)}).catch(s=>{console.error(`[modern-consent] Failed to load vendor "${e}":`,s)}))}function ne(t){let e=x.get(t);if(e&&!e.requireConsent){S(t);return}let n=f.get();g.get()&&n[t]===void 0&&p.set(!0),n[t]&&S(t)}async function S(t){if(typeof window>"u")return;let e=x.get(t);if(!e){let s=M.get(t);if(s){let r=await s();e=r.default||r,e&&x.set(t,e)}}if(!e)return;let n=window._modernConsentConfig?.consentOnly===!0,o=m.get().find(s=>s.id===t);!n&&e.init&&o&&!o.loaded&&(e.init(_.get(t)),m.update(s=>s.map(r=>r.id===t?{...r,loaded:!0}:r)),e.event?.forEach(s=>{if(s.name==="onAccept")try{s.callback()}catch{}})),!n&&e.domSelector&&e.render&&setTimeout(()=>{document.querySelectorAll(e.domSelector).forEach(s=>{let r=s;r.dataset.loaded!=="true"&&e?.render&&(r.innerHTML=e.render(r.dataset),r.dataset.loaded="true",r.classList.remove("mc-placeholder-pending"))})},50)}var P=class{listeners=new Map;on(e,n){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(n),()=>this.listeners.get(e)?.delete(n)}off(e,n){this.listeners.get(e)?.delete(n)}emit(e,n){this.listeners.get(e)?.forEach(i=>{try{i(n)}catch{}})}},v=new P;function R(t){if(typeof window>"u")return;let e={event:"consent_update",consent_state:t};window.consentLayer=window.consentLayer||[],window.consentLayer.push(e),window._modernConsentConfig?.pushDataLayer&&(window.dataLayer=window.dataLayer||[],window.dataLayer.push(e))}function B(t){v.emit("consent:saved",{consentId:A(),timestamp:Date.now(),version:typeof window<"u"?window._modernConsentConfig?.consentVersion:void 0,consent:t})}function E(t,e){let n=m.get().find(o=>o.id===t)?.loaded??!1;f.update(o=>({...o,[t]:e})),g.set(!0),v.emit("consent:update",{vendor:t,status:e?"granted":"denied"});let i=f.get();if(R(i),B(i),e){S(t);return}n&&(T(t),!(typeof window<"u"&&window._modernConsentConfig?.consentOnly===!0)&&typeof window<"u"&&window.location.reload())}function I(){let t=m.get(),e={};t.forEach(n=>{e[n.id]=!0,S(n.id)}),f.set(e),g.set(!0),p.set(!1),t.forEach(n=>{v.emit("consent:update",{vendor:n.id,status:"granted"})}),R(e),B(e)}function O(){let t=m.get(),e={},n=!1;t.forEach(o=>{e[o.id]=!1,T(o.id),o.loaded&&(n=!0)}),f.set(e),g.set(!0),p.set(!1),t.forEach(o=>{v.emit("consent:update",{vendor:o.id,status:"denied"})}),R(e),B(e);let i=typeof window<"u"&&window._modernConsentConfig?.consentOnly===!0;n&&!i&&typeof window<"u"&&window.location.reload()}var U=["config","vendor"];function j(t){let{name:e,label:n,description:i="",category:o="other",config:s={},requireConsent:r=!0,setup:c,init:l,artifacts:d}=t,u;if(l??c)u=async()=>({name:n??e,description:i,category:o,requireConsent:r,setup:c,init:l,artifacts:d});else{let b=L(e);if(!b){console.warn(`[modern-consent] Unknown vendor "${e}". Provide an init() function to define a custom vendor.`);return}u=b}V({id:e,category:o,config:s,loader:u})}function K(){if(typeof window>"u")return;let t=window,e=Array.isArray(t.mcLayer)?[...t.mcLayer]:[];t._modernConsentConfig={},q(i=>async()=>{let o=t._modernConsentConfig?.cdnBase;if(!o)throw new Error(`[modern-consent] Vendor "${i}" not found. Configure cdnBase to enable CDN loading, or provide an inline init().`);let r=await import(`${o.replace(/\/$/,"")}/${i}.js`);return r.default??r}),e.forEach(i=>{let[o,s]=i;if(!U.includes(o)){console.warn(`[modern-consent] "${o}" is not a valid command`);return}if(o==="config"){t._modernConsentConfig={...t._modernConsentConfig,...s};return}j(s)}),H(t._modernConsentConfig);let n=(...i)=>{let[o,s]=i;if(!U.includes(o)){console.warn(`[modern-consent] "${o}" is not a valid command`);return}o==="vendor"?j(s):o==="config"&&(t._modernConsentConfig={...t._modernConsentConfig,...s}),Array.isArray(t.mcLayer)||(t.mcLayer=[]),t.mcLayer.push(i)};t.modernConsent=Object.assign(n,{openPanel:()=>p.set(!0),getConsent:()=>f.get(),on:v.on.bind(v)})}typeof window<"u"&&K();var N={fr:{bannerAcceptAll:"Tout accepter",bannerDenyAll:"Continuer sans accepter",bannerCustomize:"Personnaliser",detailsTitle:"Personnaliser vos pr\xE9f\xE9rences",detailsSubtitle:"Vous pouvez activer ou d\xE9sactiver chaque service individuellement.",detailsBack:"Retour",detailsAcceptAll:"Tout accepter",detailsDenyAll:"Tout refuser",serviceAccept:"Accepter",serviceDeny:"Refuser",statusAllowed:"Autoris\xE9",statusDenied:"Refus\xE9",statusPending:"En attente",statusAlwaysActive:"Obligatoire",categoryPending:"Services en attente",close:"Fermer",purposeAccept:"Accepter",purposeDeny:"Refuser",purposeStatusAllowed:"Accept\xE9e",purposeStatusDenied:"Refus\xE9e",purposeStatusPending:"En attente",functionalTitle:"Fonctionnement du site",functionalDescription:"Ces cookies et traceurs sont indispensables au fonctionnement du site, pour fournir nos services et nous assurer de leur bon fonctionnement, pour des raisons de s\xE9curit\xE9 et pour s'assurer du suivi de vos pr\xE9f\xE9rences."},en:{bannerAcceptAll:"Accept all",bannerDenyAll:"Continue without accepting",bannerCustomize:"Customize",detailsTitle:"Customize your preferences",detailsSubtitle:"You can enable or disable each service individually.",detailsBack:"Back",detailsAcceptAll:"Accept all",detailsDenyAll:"Deny all",serviceAccept:"Accept",serviceDeny:"Deny",statusAllowed:"Allowed",statusDenied:"Denied",statusPending:"Pending",statusAlwaysActive:"Always active",categoryPending:"Pending services",close:"Close",purposeAccept:"Accept",purposeDeny:"Deny",purposeStatusAllowed:"Accepted",purposeStatusDenied:"Denied",purposeStatusPending:"Pending",functionalTitle:"Site operation",functionalDescription:"These cookies and trackers are essential for the site to function, to provide our services, ensure security, and remember your preferences."}};function W(t,e,n){return t?typeof t=="string"?t:t[e]??t.fr??Object.values(t)[0]??n:n}function a(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}var oe=`
2
- :host {
3
- --_primary: var(--mc-primary, #111827);
4
- --_primary-hover: var(--mc-primary-hover, color-mix(in srgb, var(--_primary) 85%, black));
5
- --_primary-text: var(--mc-primary-text, #fff);
6
- --_radius: var(--mc-radius, 12px);
7
- --_font: var(--mc-font, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
8
-
9
- position: fixed;
10
- inset: 0;
11
- z-index: 99999;
12
- font-family: var(--_font);
13
- pointer-events: none;
14
- display: none;
15
- }
16
-
17
- :host([open]) {
18
- pointer-events: auto;
19
- display: block;
20
- }
21
-
22
- *, *::before, *::after { box-sizing: border-box; }
23
-
24
- .backdrop {
25
- position: fixed;
26
- inset: 0;
27
- background: rgba(0, 0, 0, 0.4);
28
- display: flex;
29
- align-items: center;
30
- justify-content: center;
31
- animation: mc-fade-in 0.2s ease;
32
- }
33
-
34
- @keyframes mc-fade-in {
35
- from { opacity: 0; }
36
- to { opacity: 1; }
37
- }
38
-
39
- @keyframes mc-slide-up {
40
- from { opacity: 0; transform: translateY(12px); }
41
- to { opacity: 1; transform: translateY(0); }
42
- }
43
-
44
- .modal {
45
- background: #ffffff;
46
- border-radius: var(--_radius);
47
- max-width: 640px;
48
- width: calc(100% - 32px);
49
- max-height: 90vh;
50
- box-shadow: 0 24px 48px -12px rgba(0, 0, 0, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.05);
51
- display: flex;
52
- flex-direction: column;
53
- overflow: hidden;
54
- animation: mc-slide-up 0.25s ease;
55
- }
56
-
57
- .header {
58
- padding: 20px 24px 16px;
59
- display: flex;
60
- align-items: flex-start;
61
- justify-content: space-between;
62
- gap: 12px;
63
- }
64
-
65
- .title {
66
- font-size: 17px;
67
- font-weight: 700;
68
- margin: 0;
69
- color: #111827;
70
- letter-spacing: -0.01em;
71
- }
72
-
73
- .subtitle {
74
- font-size: 13px;
75
- color: #6b7280;
76
- margin: 6px 0 0;
77
- line-height: 1.5;
78
- }
79
-
80
- .close-btn {
81
- flex-shrink: 0;
82
- border: none;
83
- background: #f3f4f6;
84
- cursor: pointer;
85
- font-size: 16px;
86
- line-height: 1;
87
- padding: 6px 8px;
88
- color: #6b7280;
89
- border-radius: 8px;
90
- transition: background 0.15s;
91
- }
92
- .close-btn:hover { background: #e5e7eb; }
93
-
94
- .body {
95
- padding: 0 24px 16px;
96
- overflow: auto;
97
- font-size: 13px;
98
- color: #4b5563;
99
- line-height: 1.6;
100
- }
101
-
102
- .divider {
103
- height: 1px;
104
- background: #e5e7eb;
105
- margin: 0 24px;
106
- }
107
-
108
- .footer {
109
- padding: 16px 24px;
110
- border-top: 1px solid #f3f4f6;
111
- display: flex;
112
- flex-wrap: wrap;
113
- gap: 8px;
114
- justify-content: flex-end;
115
- }
116
-
117
- /* \u2500\u2500\u2500 Buttons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
118
-
119
- .btn {
120
- appearance: none;
121
- border-radius: 999px;
122
- padding: 9px 18px;
123
- font-size: 13px;
124
- font-weight: 500;
125
- border: 1px solid transparent;
126
- cursor: pointer;
127
- transition: all 0.15s ease;
128
- white-space: nowrap;
129
- letter-spacing: 0.01em;
130
- }
131
-
132
- .btn:active { transform: translateY(1px); }
133
-
134
- .btn-primary {
135
- background: var(--_primary);
136
- color: var(--_primary-text);
137
- border-color: var(--_primary);
138
- }
139
- .btn-primary:hover {
140
- background: var(--_primary-hover);
141
- border-color: var(--_primary-hover);
142
- }
143
-
144
- .btn-outline {
145
- background: #ffffff;
146
- border-color: #e5e7eb;
147
- color: #374151;
148
- }
149
- .btn-outline:hover { background: #f9fafb; border-color: #d1d5db; }
150
-
151
- .btn-allowed-active {
152
- background: #059669;
153
- border-color: #059669;
154
- color: #fff;
155
- }
156
- .btn-allowed-active:hover { background: #047857; border-color: #047857; }
157
-
158
- .btn-denied-active {
159
- background: #dc2626;
160
- border-color: #dc2626;
161
- color: #fff;
162
- }
163
- .btn-denied-active:hover { background: #b91c1c; border-color: #b91c1c; }
164
-
165
- .btn-ghost {
166
- background: transparent;
167
- color: #6b7280;
168
- }
169
- .btn-ghost:hover { background: #f9fafb; }
170
-
171
- /* \u2500\u2500\u2500 Categories & Services \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
172
-
173
- .categories {
174
- display: flex;
175
- flex-direction: column;
176
- gap: 12px;
177
- }
178
-
179
- .category {
180
- border-radius: calc(var(--_radius) - 2px);
181
- border: 1px solid #f3f4f6;
182
- background: #fafafa;
183
- padding: 14px;
184
- }
185
-
186
- .category-header {
187
- display: flex;
188
- align-items: center;
189
- justify-content: space-between;
190
- margin-bottom: 8px;
191
- }
192
-
193
- .category-title {
194
- font-size: 13px;
195
- font-weight: 600;
196
- color: #111827;
197
- }
198
-
199
- .services-list {
200
- display: flex;
201
- flex-direction: column;
202
- gap: 6px;
203
- }
204
-
205
- .service {
206
- display: flex;
207
- align-items: center;
208
- justify-content: space-between;
209
- gap: 12px;
210
- padding: 8px 0 6px;
211
- border-top: 1px solid #f3f4f6;
212
- }
213
-
214
- .service-main { flex: 1; }
215
-
216
- .service-name {
217
- font-size: 13px;
218
- font-weight: 500;
219
- color: #111827;
220
- margin-bottom: 2px;
221
- }
222
-
223
- .service-description { font-size: 12px; color: #6b7280; line-height: 1.4; }
224
-
225
- .service-status { font-size: 11px; margin-top: 4px; font-weight: 500; }
226
- .service-status.allowed { color: #059669; }
227
- .service-status.denied { color: #dc2626; }
228
- .service-status.pending { color: #d97706; }
229
-
230
- .service-actions { display: flex; gap: 4px; flex-shrink: 0; }
231
-
232
- .chip {
233
- font-size: 11px;
234
- padding: 3px 10px;
235
- border-radius: 999px;
236
- border: 1px solid #e5e7eb;
237
- color: #6b7280;
238
- white-space: nowrap;
239
- font-weight: 500;
240
- }
241
-
242
- .chip-active {
243
- background: #ecfdf5;
244
- border-color: #a7f3d0;
245
- color: #059669;
246
- }
247
-
248
- /* \u2500\u2500\u2500 Purpose mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
249
-
250
- .category-info {
251
- display: flex;
252
- align-items: center;
253
- gap: 8px;
254
- }
255
-
256
- .category-status {
257
- font-size: 11px;
258
- font-weight: 500;
259
- }
260
- .category-status.allowed { color: #059669; }
261
- .category-status.denied { color: #dc2626; }
262
- .category-status.pending { color: #d97706; }
263
-
264
- .purpose-description {
265
- font-size: 12px;
266
- color: #6b7280;
267
- margin: 0 0 8px;
268
- line-height: 1.5;
269
- }
270
-
271
- .purpose-service-info {
272
- padding: 4px 0;
273
- border-top: none;
274
- }
275
- .purpose-service-info .service-name {
276
- font-size: 12px;
277
- color: #4b5563;
278
- font-weight: 400;
279
- }
280
- .purpose-service-info .service-description {
281
- font-size: 11px;
282
- color: #9ca3af;
283
- }
284
- `,D=null;function se(){return D||(D=new CSSStyleSheet,D.replaceSync(oe)),D}var z=class extends HTMLElement{static observedAttributes=["lang"];shadow;mode="banner";_unsubscribers=[];_renderScheduled=!1;_previouslyFocused=null;_keydownHandler=null;services=[];consent={};answered=!1;panelOpen=!1;constructor(){super(),this.shadow=this.attachShadow({mode:"open"})}get labels(){let e=this.getAttribute("lang")??"fr";return N[e]??N.fr}scheduleRender(){this._renderScheduled||(this._renderScheduled=!0,queueMicrotask(()=>{this._renderScheduled=!1,this.render()}))}connectedCallback(){this.shadow.adoptedStyleSheets=[se()],this._unsubscribers.push(f.subscribe(e=>{this.consent=e,this.scheduleRender()}),g.subscribe(e=>{this.answered=e,this.scheduleRender()}),m.subscribe(e=>{this.services=e,this.ensurePanelOpenIfNeeded(),this.scheduleRender()}),p.subscribe(e=>{let n=this.panelOpen;this.panelOpen=e,e&&!n&&this.answered&&(this.mode="details"),this.scheduleRender()}))}disconnectedCallback(){this._unsubscribers.forEach(e=>e()),this._unsubscribers=[],this.teardownFocusTrap(!1)}attributeChangedCallback(){this.scheduleRender()}ensurePanelOpenIfNeeded(){this.getPendingServices().length>0&&!this.answered&&!this.panelOpen&&$()}getPendingServices(){return this.services.filter(e=>e.requireConsent&&this.consent[e.id]===void 0)}shouldShowPopup(){return this.panelOpen&&this.services.length>0}setupFocusTrap(){let e=this.shadow.querySelector(".modal");if(!e)return;let n=[...e.querySelectorAll('button:not([disabled]), [href], [tabindex]:not([tabindex="-1"])')];n.length&&(this._previouslyFocused||(this._previouslyFocused=document.activeElement),requestAnimationFrame(()=>n[0]?.focus()),this._keydownHandler=i=>{if(i.key==="Escape"){i.preventDefault(),p.set(!1);return}if(i.key!=="Tab")return;let o=this.shadow.activeElement,s=n[0],r=n[n.length-1];i.shiftKey&&o===s?(i.preventDefault(),r.focus()):!i.shiftKey&&o===r&&(i.preventDefault(),s.focus())},document.addEventListener("keydown",this._keydownHandler))}teardownFocusTrap(e=!0){this._keydownHandler&&(document.removeEventListener("keydown",this._keydownHandler),this._keydownHandler=null),e&&this._previouslyFocused&&"focus"in this._previouslyFocused&&(this._previouslyFocused.focus(),this._previouslyFocused=null)}render(){if(!this.shouldShowPopup()){this.teardownFocusTrap(!0),this.shadow.innerHTML="",this.removeAttribute("open");return}this.setAttribute("open",""),this.teardownFocusTrap(!1),this.mode==="banner"?this.renderBanner():((typeof window<"u"?window._modernConsentConfig?.displayMode:void 0)??"vendor")==="purpose"?this.renderPurposeDetails():this.renderDetails(),this.setupFocusTrap()}onAcceptAll=()=>I();onDenyAll=()=>O();onCustomize=()=>{this.mode="details",this.scheduleRender()};onBackToBanner=()=>{this.mode="banner",this.scheduleRender()};onSetConsentForService(e,n){E(e,n),this.getPendingServices().filter(i=>i.id!==e).length===0&&p.set(!1)}onSetConsentForCategory(e,n){this.services.filter(o=>o.category===e&&o.requireConsent).forEach(o=>E(o.id,n)),this.getPendingServices().length===0&&p.set(!1)}getCategoryStatus(e){let n=e.filter(s=>s.requireConsent);return n.length===0||n.every(s=>this.consent[s.id])?"allowed":n.every(s=>!this.consent[s.id])?"denied":"pending"}groupServicesByCategory(){let e={};return this.services.forEach(n=>{let i=n.category||"Autres";e[i]||(e[i]=[]),e[i].push(n)}),e}renderFunctionalBlock(){if(!(typeof window<"u"&&window._modernConsentConfig?.functionalPurpose===!0))return"";let n=this.labels;return`
285
- <section class="category">
286
- <div class="category-header">
287
- <div class="category-info">
288
- <span class="category-title">${a(n.functionalTitle)}</span>
289
- </div>
290
- <span class="chip chip-active">${a(n.statusAlwaysActive)}</span>
291
- </div>
292
- <div class="purpose-description">${a(n.functionalDescription)}</div>
293
- </section>
294
- `}renderBanner(){let e=this.labels;this.shadow.innerHTML=`
295
- <div class="backdrop" role="dialog" aria-modal="true" aria-labelledby="mc-title">
296
- <div class="modal">
297
- <div class="header">
298
- <div>
299
- <h2 class="title" id="mc-title"><slot name="title"></slot></h2>
300
- </div>
301
- <button class="close-btn" type="button" aria-label="${a(e.close)}" id="mc-close-btn">\u2715</button>
302
- </div>
303
- <div class="body">
304
- <slot name="body"></slot>
305
- </div>
306
- <div class="footer">
307
- <button class="btn btn-ghost" type="button" id="mc-deny-all-btn">${a(e.bannerDenyAll)}</button>
308
- <button class="btn btn-outline" type="button" id="mc-customize-btn">${a(e.bannerCustomize)}</button>
309
- <button class="btn btn-primary" type="button" id="mc-accept-all-btn">${a(e.bannerAcceptAll)}</button>
310
- </div>
311
- </div>
312
- </div>
313
- `,this.shadow.getElementById("mc-accept-all-btn").addEventListener("click",this.onAcceptAll),this.shadow.getElementById("mc-deny-all-btn").addEventListener("click",this.onDenyAll),this.shadow.getElementById("mc-customize-btn").addEventListener("click",this.onCustomize),this.shadow.getElementById("mc-close-btn").addEventListener("click",()=>p.set(!1))}renderDetails(){let e=this.labels,n=this.renderFunctionalBlock(),i=this.groupServicesByCategory(),o=Object.keys(i).sort().map(s=>{let r=i[s],c=r.some(d=>d.requireConsent&&this.consent[d.id]===void 0),l=r.map(d=>{if(!d.requireConsent)return`
314
- <div class="service">
315
- <div class="service-main">
316
- <div class="service-name">${a(d.name)}</div>
317
- <div class="service-description">${a(d.description)}</div>
318
- </div>
319
- <span class="chip chip-active">${a(e.statusAlwaysActive)}</span>
320
- </div>
321
- `;let u=this.consent[d.id],b=u?e.statusAllowed:u?e.statusPending:e.statusDenied,w=u?"allowed":u?"pending":"denied";return`
322
- <div class="service">
323
- <div class="service-main">
324
- <div class="service-name">${a(d.name)}</div>
325
- <div class="service-description">${a(d.description)}</div>
326
- <div class="service-status ${w}">${a(b)}</div>
327
- </div>
328
- <div class="service-actions">
329
- <button class="btn btn-outline ${w==="allowed"?"btn-allowed-active":""}" type="button" data-action="allow" data-id="${a(d.id)}">
330
- ${a(e.serviceAccept)}
331
- </button>
332
- <button class="btn btn-outline ${w==="denied"?"btn-denied-active":""}" type="button" data-action="deny" data-id="${a(d.id)}">
333
- ${a(e.serviceDeny)}
334
- </button>
335
- </div>
336
- </div>
337
- `}).join("");return`
338
- <section class="category">
339
- <div class="category-header">
340
- <div class="category-title">${a(s)}</div>
341
- ${c?`<span class="chip">${a(e.categoryPending)}</span>`:""}
342
- </div>
343
- <div class="services-list">${l}</div>
344
- </section>
345
- `}).join("");this.shadow.innerHTML=`
346
- <div class="backdrop" role="dialog" aria-modal="true" aria-labelledby="mc-details-title">
347
- <div class="modal">
348
- <div class="header">
349
- <div>
350
- <h2 class="title" id="mc-details-title">${a(e.detailsTitle)}</h2>
351
- <p class="subtitle">${a(e.detailsSubtitle)}</p>
352
- </div>
353
- <button class="close-btn" type="button" aria-label="${a(e.close)}" id="mc-close-btn">\u2715</button>
354
- </div>
355
- <div class="body">
356
- <div class="categories">${n}${o}</div>
357
- </div>
358
- <div class="footer">
359
- <button class="btn btn-ghost" type="button" id="mc-back-btn">${a(e.detailsBack)}</button>
360
- <button class="btn btn-outline" type="button" id="mc-deny-all-btn">${a(e.detailsDenyAll)}</button>
361
- <button class="btn btn-primary" type="button" id="mc-accept-all-btn">${a(e.detailsAcceptAll)}</button>
362
- </div>
363
- </div>
364
- </div>
365
- `,this.shadow.getElementById("mc-accept-all-btn").addEventListener("click",this.onAcceptAll),this.shadow.getElementById("mc-deny-all-btn").addEventListener("click",this.onDenyAll),this.shadow.getElementById("mc-back-btn").addEventListener("click",this.onBackToBanner),this.shadow.getElementById("mc-close-btn").addEventListener("click",()=>p.set(!1)),this.shadow.querySelectorAll("[data-action]").forEach(s=>{s.addEventListener("click",()=>{this.onSetConsentForService(s.dataset.id,s.dataset.action==="allow")})})}renderPurposeDetails(){let e=this.labels,n=this.getAttribute("lang")??"fr",i=this.renderFunctionalBlock(),o=this.groupServicesByCategory(),s=typeof window<"u"?window._modernConsentConfig?.purposes:void 0,r=Object.keys(o).sort().map(c=>{let l=o[c],d=l.every(h=>!h.requireConsent),u=this.getCategoryStatus(l),b=l.find(h=>h.purposeLabel),w=W(b?.purposeLabel,n,s?.[c]?.label??c),F=W(b?.purposeDescription,n,s?.[c]?.description??""),J=d?`<span class="chip chip-active">${a(e.statusAlwaysActive)}</span>`:`
366
- <div class="service-actions">
367
- <button class="btn btn-outline ${u==="allowed"?"btn-allowed-active":""}" type="button" data-category="${a(c)}" data-cat-action="allow">
368
- ${a(e.purposeAccept)}
369
- </button>
370
- <button class="btn btn-outline ${u==="denied"?"btn-denied-active":""}" type="button" data-category="${a(c)}" data-cat-action="deny">
371
- ${a(e.purposeDeny)}
372
- </button>
373
- </div>
374
- `,G=l.map(h=>`
375
- <div class="service purpose-service-info">
376
- <div class="service-main">
377
- <div class="service-name">${a(h.name)}</div>
378
- <div class="service-description">${a(h.description)}</div>
379
- </div>
380
- </div>
381
- `).join("");return`
382
- <section class="category">
383
- <div class="category-header">
384
- <div class="category-info">
385
- <span class="category-title">${a(w)}</span>
386
- </div>
387
- ${J}
388
- </div>
389
- ${F?`<div class="purpose-description">${a(F)}</div>`:""}
390
- <div class="services-list">${G}</div>
391
- </section>
392
- `}).join("");this.shadow.innerHTML=`
393
- <div class="backdrop" role="dialog" aria-modal="true" aria-labelledby="mc-details-title">
394
- <div class="modal">
395
- <div class="header">
396
- <div>
397
- <h2 class="title" id="mc-details-title">${a(e.detailsTitle)}</h2>
398
- <p class="subtitle">${a(e.detailsSubtitle)}</p>
399
- </div>
400
- <button class="close-btn" type="button" aria-label="${a(e.close)}" id="mc-close-btn">\u2715</button>
401
- </div>
402
- <div class="body">
403
- <div class="categories">${i}${r}</div>
404
- </div>
405
- <div class="footer">
406
- <button class="btn btn-ghost" type="button" id="mc-back-btn">${a(e.detailsBack)}</button>
407
- <button class="btn btn-outline" type="button" id="mc-deny-all-btn">${a(e.detailsDenyAll)}</button>
408
- <button class="btn btn-primary" type="button" id="mc-accept-all-btn">${a(e.detailsAcceptAll)}</button>
409
- </div>
410
- </div>
411
- </div>
412
- `,this.shadow.getElementById("mc-accept-all-btn").addEventListener("click",this.onAcceptAll),this.shadow.getElementById("mc-deny-all-btn").addEventListener("click",this.onDenyAll),this.shadow.getElementById("mc-back-btn").addEventListener("click",this.onBackToBanner),this.shadow.getElementById("mc-close-btn").addEventListener("click",()=>p.set(!1)),this.shadow.querySelectorAll("[data-cat-action]").forEach(c=>{c.addEventListener("click",()=>{this.onSetConsentForCategory(c.dataset.category,c.dataset.catAction==="allow")})})}};typeof window<"u"&&!customElements.get("mc-consent-widget")&&customElements.define("mc-consent-widget",z);function Y(){if(document.querySelector("mc-consent-widget"))return;let t=document.createElement("mc-consent-widget"),e=document.documentElement.lang?.split("-")[0];e&&t.setAttribute("lang",e),document.body.appendChild(t)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",Y):Y();})();
413
- //# sourceMappingURL=mc-widget.js.map