import * as shared from './shared.js'; import { LitElement, html, css, type TemplateResult } from 'lit'; import { customElement } from 'lit/decorators.js'; import { property } from 'lit/decorators/property.js'; import * as csInterfaces from '@consent.software/interfaces'; import * as csWebclient from '@consent.software/webclient'; import { delayFor } from '@push.rocks/smartdelay'; declare global { interface HTMLElementTagNameMap { 'consentsoftware-cookieconsent': ConsentsoftwareCookieconsent; } } @customElement('consentsoftware-cookieconsent') export class ConsentsoftwareCookieconsent extends LitElement { public static demo = () => html``; public csWebclientInstance = new csWebclient.CsWebclient(); public csWebclientRan = false; @property({ type: String, reflect: true }) public theme: 'light' | 'dark' = 'light'; /** * We bind `heightPixels` to a CSS variable (--cookieconsent-height). * Then we can do margin-bottom animations in CSS. */ public static styles = css` :host { font-family: ${shared.fontStack}; user-select: none; /* Default theme variables */ --text-color: #333; --background-color: #eeeeee; --accent-color: #333333; --button-bg: #ffffff; --button-hover-bg: #f2f2f2; --icon-color: #4496f5; --link-color: #333; --padding-sides: 16px; } :host([theme='dark']) { --text-color: #fff; --background-color: #111; --accent-color: #333333; --button-bg: #252525; --button-hover-bg: #222222; --icon-color: #4496f5; --link-color: #fff; } :host([theme='light']) { --text-color: #333; --background-color: #eeeeee; --accent-color: #333333; --button-bg: #ffffff; --button-hover-bg: #f2f2f2; --icon-color: #4496f5; --link-color: #333; } .pageOverlay { position: fixed; top: 0px; bottom: 0px; right: 0px; left: 0px; display: grid; align-items: center; justify-content: center; z-index: 1000; /* standard z-index for fixed elements */ background: rgba(255, 255, 255, 0); backdrop-filter: blur(0px); transition: all 0.2s; } .modalBox { display: block; color: var(--text-color); background: var(--background-color); box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.6); position: realtive; border: 1px dotted rgba(255, 255, 255, 0.1); border-top: 1px solid var(--accent-color); border-radius: 16px; max-width: 1100px; min-width: calc(100vw / 3); box-sizing: border-box; overflow: hidden; will-change: transform; /* ensure efficient rendering */ transition: all 0.3s; transform: scale(0.95); opacity: 0; } /* * Toggle display based on [show] attribute * (so if show=false, the banner doesn't show at all). */ :host([show='false']) { display: none; } :host([show='true']) { display: block; } .content { margin: auto; } .text-container { padding-left: var(--padding-sides); padding-right: var(--padding-sides); display: grid; grid-template-columns: auto; } .text-container a { color: var(--link-color); text-decoration: none; } .button-container { padding: var(--padding-sides); display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; } .info-container { color: var(--text-color); text-align: center; line-height: 3em; background: rgba(0, 0, 0, 0.1); border-top: 1px dotted rgba(255, 255, 255, 0.1); font-size: 0.8em; color: rgba(255, 255, 255, 0.5); } .info-container a { text-decoration: underline; color: var(--link-color); transition: color 0.2s; } .info-container a:hover { color: #ffffff; } .consent-button { border-radius: 3px; background: var(--button-bg); box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2); padding: 10px; line-height: 30px; text-align: center; cursor: pointer; transition: all 0.2s; } .consent-button:hover { background: var(--button-hover-bg); } `; constructor() { super(); this.setAttribute('show', 'false'); } public render(): TemplateResult { return html`
`; } /** * Called when the element is inserted into the DOM. */ public async connectedCallback() { super.connectedCallback(); this.updateTheme(); const cookieLevel = await this.csWebclientInstance.getCookieLevels(); if (!cookieLevel) { this.setAttribute('show', 'true'); requestAnimationFrame(async () => { await this.updated(); const pageOverlay: HTMLDivElement = this.shadowRoot?.querySelector('.pageOverlay'); if (pageOverlay) { pageOverlay.style.background = 'rgba(255,255,255, 0.1)'; pageOverlay.style.backdropFilter = 'blur(1px)'; } const modalBox: HTMLDivElement = this.shadowRoot?.querySelector('.modalBox'); if (modalBox) { modalBox.style.transform = `scale(1)`; modalBox.style.opacity = '1'; } }); } else { this.setAttribute('show', 'false'); } } /** * Called after the first render of the component. * We measure the actual height of the banner and update the CSS variable. */ public async firstUpdated() {} /** * Called whenever the element is updated or re-rendered. */ public async updated() { console.log(`The height of the cookie banner is ${this.shadowRoot?.host?.clientHeight}px`); const acceptedCookieLevels = await this.csWebclientInstance.getCookieLevels(); if (!this.csWebclientRan && acceptedCookieLevels) { this.csWebclientRan = true; await this.csWebclientInstance.getAndRunConsentTuples(); } } /** * Sets the user’s chosen cookie level(s) and hides the banner. */ private async handleConsentButtonClick( event: MouseEvent, levelsArg: csInterfaces.TCookieLevel[] ) { console.log(`Set level to ${levelsArg}`); const pageOverlay: HTMLDivElement = this.shadowRoot?.querySelector('.pageOverlay'); if (pageOverlay) { pageOverlay.style.background = 'rgba(255,255,255, 0)'; pageOverlay.style.backdropFilter = 'blur(0px)'; } const modalBox: HTMLDivElement = this.shadowRoot?.querySelector('.modalBox'); if (modalBox) { modalBox.style.transform = `scale(0.95)`; modalBox.style.opacity = '0'; } await this.csWebclientInstance.setCookieLevels(levelsArg); await delayFor(300); this.setAttribute('show', 'false'); // After user selection, re-check for any required scripts to run this.updated(); } /** * Dynamically switches the theme between light/dark, * respecting `prefers-color-scheme` by default. */ private updateTheme() { const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; this.theme = prefersDark ? 'dark' : 'light'; window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { this.theme = e.matches ? 'dark' : 'light'; }); } }