30 Commits

Author SHA1 Message Date
8a26378689 v1.6.1 2025-11-30 21:22:51 +00:00
1e74560aca fix(build): Update TypeScript config and build tooling; use accessor for Lit properties; bump deps and adjust package metadata 2025-11-30 21:22:51 +00:00
2184d8be18 1.6.0 2025-01-22 07:34:07 +01:00
46602851aa feat(elements): Enhance theme handling and CSS organization for consent component 2025-01-22 07:34:06 +01:00
fc2ba54725 1.5.4 2025-01-21 12:39:35 +01:00
a9ae67b43b fix(consentsoftware-components): Add interactive consent software components for managing cookie levels. 2025-01-21 12:39:35 +01:00
599b529744 1.5.3 2025-01-20 20:59:36 +01:00
d15f8ecf39 fix(consentsoftware-cookieconsent): Fix visual inconsistencies in the consent software modal overlay and shadow. 2025-01-20 20:59:36 +01:00
ed51ea2049 1.5.2 2025-01-20 18:06:26 +01:00
7ab409ca80 fix(core): No changes detected 2025-01-20 18:06:26 +01:00
e4b611c86d 1.5.1 2025-01-20 18:04:47 +01:00
5bb5e8e458 fix(consentsoftware-cookieconsent): Adjusted shake animation duration and box-shadow for modalBox in consent cookie component. 2025-01-20 18:04:47 +01:00
e4efdcd9a6 1.5.0 2025-01-20 17:59:50 +01:00
6b31a80f07 feat(consentsoftware-cookieconsent): Enhance consent modal with shake animation on overlay click 2025-01-20 17:59:50 +01:00
7829e6a052 1.4.5 2025-01-17 19:21:20 +01:00
8b18e33c6e fix(elements): Fix issues in consentsoftware-cookieconsent component 2025-01-17 19:21:20 +01:00
9407295866 1.4.4 2025-01-17 17:45:33 +01:00
472fa6b684 fix(core): Update LitElement properties to use accessors 2025-01-17 17:45:33 +01:00
bdf9aa7e54 1.4.3 2025-01-17 06:45:06 +01:00
72c318a793 fix(consentsoftware-cookieconsent): Fix transition property in consent button styles and add HTMLElementTagNameMap declaration for global interface. 2025-01-17 06:45:06 +01:00
71ee96307e 1.4.2 2025-01-16 03:06:46 +01:00
e0ed973b3a fix(cookieconsent): Fix user-select property in consentsoftware-cookieconsent component for consistency in interaction. 2025-01-16 03:06:45 +01:00
1ab6e09c95 1.4.1 2025-01-16 02:59:10 +01:00
26fd75b8ae fix(consentsoftware-toggle): Fix issue in drag event handling logic for the toggle component. 2025-01-16 02:59:10 +01:00
48a5b96b2a 1.4.0 2025-01-16 01:23:13 +01:00
66a883d90e feat(toggle component): Enhanced consent toggle component with drag functionality 2025-01-16 01:23:13 +01:00
6e6f5273a2 1.3.5 2025-01-16 00:28:47 +01:00
1e091ec600 fix(elements): Improved styling consistency across several components 2025-01-16 00:28:47 +01:00
fb15f62189 1.3.4 2025-01-14 03:29:05 +01:00
f5d9f05bf2 fix(dependencies): Corrected build script and updated @consent.software/webclient dependency version 2025-01-14 03:29:04 +01:00
12 changed files with 1922 additions and 2374 deletions

View File

@@ -1,5 +1,103 @@
# Changelog # Changelog
## 2025-11-30 - 1.6.1 - fix(build)
Update TypeScript config and build tooling; use accessor for Lit properties; bump deps and adjust package metadata
- ts_web/tsconfig.json: target -> ES2022, module -> ES2022, moduleResolution -> bundler, lib updated to ES2022/DOM/DOM.Iterable
- tsconfig.json (root): target -> ES2022, module -> NodeNext, moduleResolution -> NodeNext
- Component properties migrated to use the TypeScript 'accessor' modifier (consentsoftware-cookieconsent, consentsoftware-mainselection, consentsoftware-toggle)
- Bumped runtime and dev dependencies: lit -> ^3.3.1; upgrades to @design.estate/dees-wcctools, @git.zone/tsbuild, @git.zone/tsbundle, @git.zone/tswatch
- Package metadata: author updated to Task Venture Capital GmbH and test script changed to use pnpm
## 2025-01-22 - 1.6.0 - feat(elements)
Enhance theme handling and CSS organization for consent component
- Introduced theme-specific CSS variables for better theming support in consentsoftware-cookieconsent.
- Reorganized styles in consentsoftware-cookieconsent for cleaner look and consistency.
- Added media queries for responsive design in consentsoftware-tabs.
## 2025-01-21 - 1.5.4 - fix(consentsoftware-components)
Add interactive consent software components for managing cookie levels.
- Implemented 'consentsoftware-cookieconsent' component for cookie management
- Introduced 'consentsoftware-toggle' for user interaction improvements
- Added theme support for light/dark modes in consent components
- Enhanced consent interface with responsive design and accessibility features
## 2025-01-20 - 1.5.3 - fix(consentsoftware-cookieconsent)
Fix visual inconsistencies in the consent software modal overlay and shadow.
- Updated the shadow color in the modal box to use a consistent color.
- Restored backdrop blur effect in the page overlay when visible.
## 2025-01-20 - 1.5.2 - fix(core)
No changes detected
## 2025-01-20 - 1.5.1 - fix(consentsoftware-cookieconsent)
Adjusted shake animation duration and box-shadow for modalBox in consent cookie component.
- Changed the box-shadow to be softer with reduced spread for more subtle visual effect.
- Increased the duration of shake animation from 300ms to 2000ms for enhanced user feedback upon overlay interaction.
## 2025-01-20 - 1.5.0 - feat(consentsoftware-cookieconsent)
Enhance consent modal with shake animation on overlay click
- Added shake animation to the modal and overlay when clicked outside the modal.
- Updated dependency @consent.software/interfaces to version ^1.0.14.
## 2025-01-17 - 1.4.5 - fix(elements)
Fix issues in consentsoftware-cookieconsent component
- Removed unused attribute handling for 'gotIt'.
- Fixed background and backdrop-filter transitions to ensure proper UI behavior.
- Consolidated consent button click handling into handleConsentButtonClick.
- Improved modal visibility toggling and acceptance flow.
## 2025-01-17 - 1.4.4 - fix(core)
Update LitElement properties to use accessors
- Changes made in ts_web/elements/consentsoftware-toggle.ts
- Updated properties 'required' and 'selected' to use accessors
## 2025-01-17 - 1.4.3 - fix(consentsoftware-cookieconsent)
Fix transition property in consent button styles and add HTMLElementTagNameMap declaration for global interface.
- Updated the transition property for consent buttons to transition all properties instead of just the background.
- Added global interface HTMLElementTagNameMap for custom element type support.
## 2025-01-16 - 1.4.2 - fix(cookieconsent)
Fix user-select property in consentsoftware-cookieconsent component for consistency in interaction.
## 2025-01-16 - 1.4.1 - fix(consentsoftware-toggle)
Fix issue in drag event handling logic for the toggle component.
- Corrected dragging functionality to accurately track dragging state.
- Included logic to delay the reset of the dragging state after toggling.
## 2025-01-16 - 1.4.0 - feat(toggle component)
Enhanced consent toggle component with drag functionality
- Implemented drag functionality for the toggle knob.
- Added smooth transitions for the toggle knob movement.
- Handled drag state management to differentiate between actual click and drag.
- Improved user interaction by allowing click anywhere in the toggle area.
## 2025-01-16 - 1.3.5 - fix(elements)
Improved styling consistency across several components
- Increased box-shadow intensity for better visual separation
- Changed border-bottom styles to use dotted lines for consistency
- Adjusted line-height for better vertical spacing
- Fixed border-right gradient on itemBox for better styling
## 2025-01-14 - 1.3.4 - fix(dependencies)
Corrected build script and updated @consent.software/webclient dependency version
- Corrected the build script entry in package.json.
- Updated @consent.software/webclient dependency to version ^1.1.0.
## 2025-01-14 - 1.3.3 - fix(package.json) ## 2025-01-14 - 1.3.3 - fix(package.json)
Fixed incorrect main and typings fields, replacing with exports field Fixed incorrect main and typings fields, replacing with exports field

View File

@@ -1,6 +1,6 @@
{ {
"name": "@consent.software/catalog", "name": "@consent.software/catalog",
"version": "1.3.3", "version": "1.6.1",
"private": false, "private": false,
"description": "A library of web components designed to integrate robust consent management capabilities into web applications, ensuring compliance with privacy regulations.", "description": "A library of web components designed to integrate robust consent management capabilities into web applications, ensuring compliance with privacy regulations.",
"exports": { "exports": {
@@ -8,23 +8,23 @@
}, },
"type": "module", "type": "module",
"scripts": { "scripts": {
"test": "npm run build", "test": "pnpm run build",
"build": "tsbuild element --allowimplicitany && tsbundle element --production", "build": "tsbuild tsfolders --allowimplicitany && tsbundle element --production",
"watch": "tswatch element" "watch": "tswatch element"
}, },
"author": "Lossless GmbH", "author": "Task Venture Capital GmbH",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@consent.software/interfaces": "^1.0.11", "@consent.software/interfaces": "^1.0.14",
"@consent.software/webclient": "^1.0.14", "@consent.software/webclient": "^1.1.0",
"@push.rocks/smartdelay": "^3.0.5", "@push.rocks/smartdelay": "^3.0.5",
"lit": "^3.2.1" "lit": "^3.3.1"
}, },
"devDependencies": { "devDependencies": {
"@design.estate/dees-wcctools": "^1.0.90", "@design.estate/dees-wcctools": "^1.2.1",
"@git.zone/tsbuild": "^2.2.0", "@git.zone/tsbuild": "^3.1.2",
"@git.zone/tsbundle": "^2.1.0", "@git.zone/tsbundle": "^2.6.2",
"@git.zone/tswatch": "^2.0.37", "@git.zone/tswatch": "^2.2.1",
"@push.rocks/projectinfo": "^5.0.2" "@push.rocks/projectinfo": "^5.0.2"
}, },
"files": [ "files": [

3407
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

251
readme.md
View File

@@ -1,138 +1,207 @@
# @consent.software_private/catalog # @consent.software/catalog
Webcomponents for consent.software widget. A sleek, modern library of web components for seamless consent management integration. Built with Lit and designed for GDPR/CCPA compliance out of the box.
## Install ## Issue Reporting and Security
To install this module, you need to have [Node.js](https://nodejs.org/) and [npm](https://www.npmjs.com/) installed on your machine. You can then run the following command in your terminal to add the module to your project: For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
```shell ## Features
npm install @consent.software_private/catalog
🍪 **Cookie Consent Modal** - Beautiful, responsive consent dialog with light/dark theme support
🎛️ **Interactive Toggles** - Draggable toggle switches with smooth animations
📱 **Mobile-First Design** - Fully responsive across all device sizes
🎨 **Theme-Aware** - Automatically detects system light/dark mode preference
🔒 **Privacy-First** - Integrates with consent.software backend for compliant consent tracking
**Zero Config** - Drop-in components that just work
## Installation
```bash
npm install @consent.software/catalog
# or
pnpm add @consent.software/catalog
``` ```
Ensure you have ESM support enabled in your TypeScript configuration, as this package uses ES module syntax. ## Quick Start
## Usage Import the components and add the consent banner to your page:
The `@consent.software_private/catalog` module offers a set of web components to integrate consent management capabilities into your web application. These components leverage modern browser APIs and employ a system for cookie management that aligns with regulatory requirements.
### Initial Setup
First, import the necessary modules and set up your project to use the web components provided:
```typescript ```typescript
import '@consent.software_private/catalog'; import '@consent.software/catalog';
// That's it! Add the component to your HTML
``` ```
Ensure this import statement is included in your main JavaScript/TypeScript file where you want to utilize the consent management web components. It is vital to transpile with TypeScript compiler settings that support ES modules. ```html
<consentsoftware-cookieconsent></consentsoftware-cookieconsent>
### Building a Consent Modal
A primary feature of this module is the `consentsoftware-cookieconsent` component. This component can be embedded into your application to allow users to select their preferred cookie levels.
#### Setting Up the Environment
Before creating instances of the components, make sure your project environment is ready. Setup the DOM tools and any associated dependencies:
```typescript
import { domtools } from '@design.estate/dees-domtools';
domtools.elementBasic.setup();
``` ```
#### Using the Consentsoftware-Cookieconsent Component The consent banner automatically:
- Shows on first visit when no consent preferences are stored
- Hides after user makes a selection
- Respects system dark/light mode
- Stores preferences via the consent.software backend
Here's how you can include the `consentsoftware-cookieconsent` component in your application: ## Components
```typescript ### `<consentsoftware-cookieconsent>`
import { html, render } from 'lit-html';
const template = html` The main consent modal component. Provides a full-screen overlay with cookie preference options.
<consentsoftware-cookieconsent></consentsoftware-cookieconsent>
`;
render(template, document.body); ```html
<consentsoftware-cookieconsent></consentsoftware-cookieconsent>
``` ```
This will add the consent banner to the bottom of the page, providing users options to set their preferences. **Features:**
- Three-button interface: Deny, Accept Selection, Accept All
- Four cookie categories: Required, Preferences, Statistics, Marketing
- Animated modal with backdrop blur
- Shake feedback when clicking outside the modal
- Automatic theme detection (light/dark)
#### Customizing the Component ### `<consentsoftware-toggle>`
The `consentsoftware-cookieconsent` component allows you to customize its design via CSS properties. Leverage light and dark themes through the CSS manager used in styling attributes: A draggable toggle switch component with touch support.
```typescript ```html
const styles = ` <consentsoftware-toggle>Label Text</consentsoftware-toggle>
:host { <consentsoftware-toggle required>Required</consentsoftware-toggle>
--cookieconsent-height: 70px;
--cookieconsent-background: #222;
--cookieconsent-text-color: #fff;
}
`;
``` ```
Apply these custom styles to modify the appearance to match your site's design language. **Properties:**
- `required` (boolean) - When true, toggle is locked in the "on" position
- `selected` (boolean) - Current toggle state
#### Handling User Interactions **Events:**
- `toggle` - Fired when state changes, includes `{ selected: boolean }` in detail
The component is interactive, responding to user actions to set cookie levels. Heres how the interaction flow works: ### `<consentsoftware-tabs>`
1. **User Interaction**: Users click one of the consent buttons (e.g., "Functional Cookies"). Tab navigation component with animated indicator.
2. **Handling Consent Levels**: The component's `setLevel` method is invoked, updating the user's cookie preferences, e.g.:
```html
<consentsoftware-tabs></consentsoftware-tabs>
```
Displays three tabs: Consent, Details, and Cookie Policy.
### `<consentsoftware-header>`
Simple header component for the consent modal.
```html
<consentsoftware-header></consentsoftware-header>
```
### `<consentsoftware-mainselection>`
Grid layout containing toggle switches for each cookie category.
```html
<consentsoftware-mainselection></consentsoftware-mainselection>
```
## Backend Integration
This catalog integrates with the `@consent.software/webclient` package for backend communication:
```typescript ```typescript
private async setLevel(event: MouseEvent, levelsArg: Array<TCookieLevel>) { import { CsWebclient } from '@consent.software/webclient';
await this.csWebclientInstance.setCookieLevels(levelsArg);
// Adjust styling and visibility const client = new CsWebclient();
this.setAttribute('gotIt', 'true');
await delayFor(300); // Check current consent levels
this.setAttribute('show', 'false'); const levels = await client.getCookieLevels();
// Update other business logic based on consent levels if (!levels) {
// Show consent dialog
}
// Set consent levels programmatically
await client.setCookieLevels(['functional', 'analytics']);
// Run consent-dependent scripts
await client.getAndRunConsentTuples();
```
## Styling & Theming
The components use CSS custom properties for easy theming:
```css
consentsoftware-cookieconsent {
/* Text and colors */
--text-color: #333;
--background-color: #eeeeee;
--accent-color: #333333;
/* Buttons */
--button-bg: #ffffff;
--button-hover-bg: #f2f2f2;
/* Links */
--link-color: #333;
--icon-color: #4496f5;
/* Modal */
--modal-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.2);
--info-bg: rgba(0, 0, 0, 0.1);
--info-text: rgba(0, 0, 0, 0.5);
} }
``` ```
The system remembers user choices using a backend managed by the `consent.software` infrastructure. The component automatically switches to dark theme variables when `theme="dark"` is set or when the system prefers dark mode.
### Integrating with Your Backend ## Cookie Levels
For full functionality, the component interacts with a backend to manage user consent states. Using `@consent.software/webclient`, you can query the user's current consent levels, perform updates, and verify whether consent has been previously granted: The consent system uses three user-selectable levels plus a required baseline:
```typescript | Level | Description |
const webclientInstance = new CsWebclient(); |-------|-------------|
const cookieLevels = await webclientInstance.getCookieLevels(); | `functional` | Required cookies for site operation (always enabled) |
| `preferences` | User preference cookies |
| `analytics` | Statistics and analytics tracking |
| `marketing` | Marketing and advertising cookies |
if (!cookieLevels) { ## Browser Support
// Show consent dialog if no consent levels are set
document.querySelector('consentsoftware-cookieconsent').show = true; - Chrome (last 2 versions)
} else { - Firefox (last 2 versions)
// Apply the consent settings to your analytics, advertisements, etc. - Safari (last 2 versions)
applyConsentSettings(cookieLevels); - Edge (last 2 versions)
}
Built with modern ES modules and requires browsers with native Custom Elements v1 support.
## Development
```bash
# Install dependencies
pnpm install
# Build the project
pnpm run build
# Watch mode for development
pnpm run watch
``` ```
### Advanced Considerations ## License and Legal Information
#### Responsive Design This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
The component is responsive, optimized for various screen sizes using a grid layout. Ensure your main HTML elements are responsive to accommodate the consent bar. **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
#### Accessibility and Compliance ### Trademarks
Ensure your application properly adheres to legal and accessibility standards by aligning with the consent management system's features. This often involves using correct semantics, polyfills for custom elements, and thorough testing on different devices and browsers. This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
#### Managing State Across Sessions Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
Store user's consent preferences beyond the session using either localStorage, cookies, or server-side sessions. Adjust the components connectedCallback and updated lifecycle methods to fetch and apply these stored settings. ### Company Information
Proper handling ensures a seamless user experience, helping companies comply with international privacy laws like GDPR. Task Venture Capital GmbH
Registered at District Court Bremen HRB 35230 HB, Germany
### Integration and Testing For any legal inquiries or further information, please contact us via email at hello@task.vc.
Test extensively in your development environment, ensure correct rendering, and simulate various user interactions to gain confidence in production-level functionality. Automated testing might include: By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
- Unit tests for lifecycle behavior using testing libraries such as Mocha or Jest.
- Integration trials ensuring the component plays well with surrounding UI elements.
### Conclusion
With these guidelines, developers can integrate, customize, and extend the consent management capabilities within their applications using `@consent.software_private/catalog`. Adapt the styling, maximize UX, and ensure regulatory compliance for a robust implementation. Mastery of the component enables seamless user consent handling and enhances your app's governance credentials.
undefined

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@consent.software/catalog', name: '@consent.software/catalog',
version: '1.3.3', version: '1.6.1',
description: 'A library of web components designed to integrate robust consent management capabilities into web applications, ensuring compliance with privacy regulations.' description: 'A library of web components designed to integrate robust consent management capabilities into web applications, ensuring compliance with privacy regulations.'
} }

View File

@@ -8,6 +8,12 @@ import * as csInterfaces from '@consent.software/interfaces';
import * as csWebclient from '@consent.software/webclient'; import * as csWebclient from '@consent.software/webclient';
import { delayFor } from '@push.rocks/smartdelay'; import { delayFor } from '@push.rocks/smartdelay';
declare global {
interface HTMLElementTagNameMap {
'consentsoftware-cookieconsent': ConsentsoftwareCookieconsent;
}
}
@customElement('consentsoftware-cookieconsent') @customElement('consentsoftware-cookieconsent')
export class ConsentsoftwareCookieconsent extends LitElement { export class ConsentsoftwareCookieconsent extends LitElement {
public static demo = () => html`<consentsoftware-cookieconsent></consentsoftware-cookieconsent>`; public static demo = () => html`<consentsoftware-cookieconsent></consentsoftware-cookieconsent>`;
@@ -15,17 +21,20 @@ export class ConsentsoftwareCookieconsent extends LitElement {
public csWebclientInstance = new csWebclient.CsWebclient(); public csWebclientInstance = new csWebclient.CsWebclient();
public csWebclientRan = false; public csWebclientRan = false;
// Reflects the current theme ('light' or 'dark')
@property({ type: String, reflect: true }) @property({ type: String, reflect: true })
public theme: 'light' | 'dark' = 'light'; public accessor theme: 'light' | 'dark' = 'light';
/** /**
* We bind `heightPixels` to a CSS variable (--cookieconsent-height). * Define component styles with CSS variables that adjust based on theme.
* Then we can do margin-bottom animations in CSS. * The default variables serve as baseline for the light theme.
* Theme-specific overrides modify these for dark mode.
*/ */
public static styles = css` public static styles = css`
:host { :host {
font-family: ${shared.fontStack}; font-family: ${shared.fontStack};
/* Default theme variables */ user-select: none;
/* Default variables for Light Theme */
--text-color: #333; --text-color: #333;
--background-color: #eeeeee; --background-color: #eeeeee;
--accent-color: #333333; --accent-color: #333333;
@@ -34,8 +43,14 @@ export class ConsentsoftwareCookieconsent extends LitElement {
--icon-color: #4496f5; --icon-color: #4496f5;
--link-color: #333; --link-color: #333;
--padding-sides: 16px; --padding-sides: 16px;
/* Additional variables for modal and info container styling */
--info-bg: rgba(0, 0, 0, 0.1);
--info-text: rgba(255, 255, 255, 0.5);
--modal-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.2);
} }
/* Dark Theme Overrides:
When theme attribute is 'dark', override variables accordingly. */
:host([theme='dark']) { :host([theme='dark']) {
--text-color: #fff; --text-color: #fff;
--background-color: #111; --background-color: #111;
@@ -44,8 +59,14 @@ export class ConsentsoftwareCookieconsent extends LitElement {
--button-hover-bg: #222222; --button-hover-bg: #222222;
--icon-color: #4496f5; --icon-color: #4496f5;
--link-color: #fff; --link-color: #fff;
--info-bg: rgba(0, 0, 0, 0.1);
--info-text: rgba(255, 255, 255, 0.5);
--modal-box-shadow: 0px 0px 8px rgba(255, 255, 255, 0.6);
} }
/* Light Theme Overrides:
Explicit light theme settings, currently matching defaults.
Can be customized independently if desired. */
:host([theme='light']) { :host([theme='light']) {
--text-color: #333; --text-color: #333;
--background-color: #eeeeee; --background-color: #eeeeee;
@@ -54,49 +75,79 @@ export class ConsentsoftwareCookieconsent extends LitElement {
--button-hover-bg: #f2f2f2; --button-hover-bg: #f2f2f2;
--icon-color: #4496f5; --icon-color: #4496f5;
--link-color: #333; --link-color: #333;
--info-bg: rgba(0, 0, 0, 0.1);
--info-text: rgba(0, 0, 0, 0.5);
--modal-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.2);
} }
/* Overlay covering the page behind the modal */
.pageOverlay { .pageOverlay {
position: fixed; position: fixed;
top: 0px; top: 0;
bottom: 0px; bottom: 0;
right: 0px; right: 0;
left: 0px; left: 0;
display: grid; display: grid;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 1000; /* standard z-index for fixed elements */ z-index: 1000; /* Ensures the overlay is on top of other elements */
background: rgba(0, 0, 0, 0); background: rgba(255, 255, 255, 0);
backdrop-filter: blur(0px); backdrop-filter: blur(0px);
transition: all 0.2s; transition: all 0.2s;
} }
/* Shake animation for overlay when clicked */
.pageOverlay.shake {
background: rgba(0, 0, 0, 0.5) !important;
}
/* Modal box styling using theme variables for colors and shadows */
.modalBox { .modalBox {
display: block; display: block;
color: var(--text-color); color: var(--text-color);
background: var(--background-color); background: var(--background-color);
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.4); box-shadow: var(--modal-box-shadow);
position: realtive; position: relative;
border: 1px dotted rgba(255, 255, 255, 0.1);
border-top: 1px solid var(--accent-color); border-top: 1px solid var(--accent-color);
border-radius: 16px; border-radius: 16px;
max-width: 1100px; max-width: 1100px;
min-width: calc(100vw / 3); min-width: calc(100vw / 3);
box-sizing: border-box; box-sizing: border-box;
overflow: hidden; overflow: hidden;
/* will-change: transform;
* We start with margin-bottom as negative to hide the banner.
* The animation occurs when we toggle the gotIt/show attributes.
*/
will-change: transform; /* ensure efficient rendering */
transition: all 0.3s; transition: all 0.3s;
transform: scale(0.95); transform: scale(0.95);
opacity: 0; opacity: 0;
} }
/* /* Media query for mobile devices: stack buttons vertically */
* Toggle display based on [show] attribute @media (max-width: 600px) {
* (so if show=false, the banner doesn't show at all). .modalBox {
*/ height: 100vh;
box-shadow: none;
border-radius: 0px;
}
}
/* Shake animation for modal box */
.modalBox.shake {
animation: shake 150ms 2 linear;
}
@keyframes shake {
0% {
transform: translate(3px, 0);
}
50% {
transform: translate(-3px, 0);
}
100% {
transform: translate(0, 0);
}
}
/* Toggle display based on [show] attribute */
:host([show='false']) { :host([show='false']) {
display: none; display: none;
} }
@@ -104,18 +155,6 @@ export class ConsentsoftwareCookieconsent extends LitElement {
display: block; display: block;
} }
/*
* Animate margin-bottom when [gotIt] toggles.
* If gotIt=true, push the banner down off-screen.
* If gotIt=false, pull the banner into view.
*/
:host([gotIt='true']) {
background: blue;
}
:host([gotIt='false']) {
background: red;
}
.content { .content {
margin: auto; margin: auto;
} }
@@ -139,14 +178,37 @@ export class ConsentsoftwareCookieconsent extends LitElement {
gap: 16px; gap: 16px;
} }
.info-container { /* Media query for mobile devices: stack buttons vertically */
color: var(--text-color); @media (max-width: 600px) {
.button-container {
grid-template-columns: 1fr;
}
}
/* Consent button styling using theme variables */
.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; text-align: center;
line-height: 2em; cursor: pointer;
background: rgba(0, 0, 0, 0.1); transition: all 0.2s;
border-top: 1px solid rgba(255, 255, 255, 0.1); }
.consent-button:hover {
background: var(--button-hover-bg);
}
/* Use theme variables for info container background and text */
.info-container {
text-align: center;
line-height: 3em;
background: var(--info-bg);
border-top: 1px dotted rgba(255, 255, 255, 0.1);
font-size: 0.8em; font-size: 0.8em;
color: rgba(255, 255, 255, 0.5); color: var(--info-text);
} }
.info-container a { .info-container a {
@@ -157,32 +219,17 @@ export class ConsentsoftwareCookieconsent extends LitElement {
.info-container a:hover { .info-container a:hover {
color: #ffffff; 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: background 0.2s;
}
.consent-button:hover {
background: var(--button-hover-bg);
}
`; `;
constructor() { constructor() {
super(); super();
this.setAttribute('gotIt', 'true'); // Initially hide the consent banner until needed
this.setAttribute('show', 'false'); this.setAttribute('show', 'false');
} }
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<div class="pageOverlay"> <div class="pageOverlay" @click=${this.pageOverlayClick}>
<div class="modalBox"> <div class="modalBox">
<div class="content"> <div class="content">
<consentsoftware-header></consentsoftware-header> <consentsoftware-header></consentsoftware-header>
@@ -194,52 +241,26 @@ export class ConsentsoftwareCookieconsent extends LitElement {
and choose which cookie level you are willing to accept. and choose which cookie level you are willing to accept.
</div> </div>
</div> </div>
<!-- <div class="button-container">
<div
class="consent-button"
@click=${(event: MouseEvent) => this.setLevel(event, ['functional'])}
>
Functional cookies
</div>
<div
class="consent-button"
@click=${(event: MouseEvent) => this.setLevel(event, ['functional', 'analytics'])}
>
Analytics cookies
</div>
<div
class="consent-button"
@click=${(event: MouseEvent) =>
this.setLevel(event, ['functional', 'analytics', 'marketing'])}
>
Marketing cookies
</div>
<div
class="consent-button"
@click=${(event: MouseEvent) =>
this.setLevel(event, ['functional', 'analytics', 'marketing', 'all'])}
>
All cookies
</div>
</div> -->
<consentsoftware-mainselection></consentsoftware-mainselection> <consentsoftware-mainselection></consentsoftware-mainselection>
<div class="button-container"> <div class="button-container">
<div <div
class="consent-button" class="consent-button"
@click=${(event: MouseEvent) => this.setLevel(event, ['functional'])} @click=${(event: MouseEvent) =>
this.handleConsentButtonClick(event, ['functional'])}
> >
Deny Deny
</div> </div>
<div <div
class="consent-button" class="consent-button"
@click=${(event: MouseEvent) => this.setLevel(event, ['functional', 'analytics'])} @click=${(event: MouseEvent) =>
this.handleConsentButtonClick(event, ['functional', 'analytics'])}
> >
Accept selection Accept selection
</div> </div>
<div <div
class="consent-button" class="consent-button"
@click=${(event: MouseEvent) => @click=${(event: MouseEvent) =>
this.setLevel(event, ['functional', 'analytics', 'marketing'])} this.handleConsentButtonClick(event, ['functional', 'analytics', 'marketing'])}
> >
Accept all cookies Accept all cookies
</div> </div>
@@ -255,43 +276,44 @@ export class ConsentsoftwareCookieconsent extends LitElement {
} }
/** /**
* Called when the element is inserted into the DOM. * Lifecycle method called when the element is connected to the DOM.
* It sets up the theme and displays the consent banner if no cookie levels are set.
*/ */
public async connectedCallback() { public async connectedCallback() {
super.connectedCallback(); super.connectedCallback();
this.updateTheme(); this.updateTheme(); // Initialize theme based on system preference
const cookieLevel = await this.csWebclientInstance.getCookieLevels(); const cookieLevel = await this.csWebclientInstance.getCookieLevels();
if (!cookieLevel) { if (!cookieLevel) {
// Show consent banner if cookie levels haven't been set yet
this.setAttribute('show', 'true'); this.setAttribute('show', 'true');
this.setAttribute('gotIt', 'false');
requestAnimationFrame(async () => { requestAnimationFrame(async () => {
await this.updated(); await this.updated();
const pageOverlay: HTMLDivElement = this.shadowRoot?.querySelector('.pageOverlay'); const pageOverlay: HTMLDivElement = this.shadowRoot?.querySelector('.pageOverlay');
if (pageOverlay) { if (pageOverlay) {
pageOverlay.style.background = 'rgba(0, 0, 0, 0.5)'; // Apply dark overlay styling when modal appears
pageOverlay.style.backdropFilter = 'blur(2px)'; pageOverlay.style.background = 'rgba(0,0,0, 0.5)';
pageOverlay.style.backdropFilter = 'blur(20px)';
} }
const modalBox: HTMLDivElement = this.shadowRoot?.querySelector('.modalBox'); const modalBox: HTMLDivElement = this.shadowRoot?.querySelector('.modalBox');
if (modalBox) { if (modalBox) {
// Animate modal box appearance
modalBox.style.transform = `scale(1)`; modalBox.style.transform = `scale(1)`;
modalBox.style.opacity = '1'; modalBox.style.opacity = '1';
} }
}); });
} else { } else {
// Hide banner if cookie levels are already set
this.setAttribute('show', 'false'); this.setAttribute('show', 'false');
this.setAttribute('gotIt', 'true');
} }
} }
/** public async firstUpdated() {
* Called after the first render of the component. // Placeholder for any logic needed after first render
* 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. * Called after updates. Logs banner height and runs consent scripts if necessary.
*/ */
public async updated() { public async updated() {
console.log(`The height of the cookie banner is ${this.shadowRoot?.host?.clientHeight}px`); console.log(`The height of the cookie banner is ${this.shadowRoot?.host?.clientHeight}px`);
@@ -303,25 +325,61 @@ export class ConsentsoftwareCookieconsent extends LitElement {
} }
/** /**
* Sets the users chosen cookie level(s) and hides the banner. * Handles consent button clicks, sets cookie levels, and hides the banner.
* Uses theme variables for styling transitions.
*/ */
private async setLevel(event: MouseEvent, levelsArg: csInterfaces.TCookieLevel[]) { private async handleConsentButtonClick(
event: MouseEvent,
levelsArg: csInterfaces.TCookieLevel[]
) {
console.log(`Set level to ${levelsArg}`); console.log(`Set level to ${levelsArg}`);
const pageOverlay: HTMLDivElement = this.shadowRoot?.querySelector('.pageOverlay');
if (pageOverlay) {
// Fade out overlay effect using inline styles for transition
pageOverlay.style.background = 'rgba(255,255,255, 0)';
pageOverlay.style.backdropFilter = 'blur(0px)';
}
const modalBox: HTMLDivElement = this.shadowRoot?.querySelector('.modalBox');
if (modalBox) {
// Scale down and fade out modal box before hiding
modalBox.style.transform = `scale(0.95)`;
modalBox.style.opacity = '0';
}
// Save user consent preferences
await this.csWebclientInstance.setCookieLevels(levelsArg); await this.csWebclientInstance.setCookieLevels(levelsArg);
this.setAttribute('gotIt', 'true');
await delayFor(300); await delayFor(300);
this.setAttribute('show', 'false'); this.setAttribute('show', 'false'); // Hide the consent banner
// After user selection, re-check for any required scripts to run this.updated(); // Trigger any post-consent actions
this.updated();
} }
/** /**
* Dynamically switches the theme between light/dark, * Handles clicks on the page overlay. If clicked outside the modal,
* respecting `prefers-color-scheme` by default. * triggers a shake animation as feedback.
*/
private async pageOverlayClick(e: MouseEvent) {
if (e.target === e.currentTarget) {
const pageOverlay: HTMLDivElement = this.shadowRoot?.querySelector('.pageOverlay');
const modalBox: HTMLDivElement = this.shadowRoot?.querySelector('.modalBox');
if (pageOverlay && modalBox) {
pageOverlay.classList.add('shake');
modalBox.classList.add('shake');
await delayFor(2000);
pageOverlay.classList.remove('shake');
modalBox.classList.remove('shake');
}
}
}
/**
* Dynamically switches the theme between light and dark.
* Listens for system theme changes to update the component's theme.
*/ */
private updateTheme() { private updateTheme() {
// Check the initial system preference for dark mode
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
this.theme = prefersDark ? 'dark' : 'light'; this.theme = prefersDark ? 'dark' : 'light';
// Listen for changes in the system color scheme preference
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
this.theme = e.matches ? 'dark' : 'light'; this.theme = e.matches ? 'dark' : 'light';
}); });

View File

@@ -11,10 +11,10 @@ export class ConsentsoftwareHeader extends LitElement {
public static styles = css` public static styles = css`
:host { :host {
display: block; display: block;
line-height: 2em; line-height: 3em;
text-align: center; text-align: center;
font-family: ${shared.fontStack}; font-family: ${shared.fontStack};
border-bottom: 1px solid rgba(255, 255, 255, 0.1); border-bottom: 1px dotted rgba(255, 255, 255, 0.1);
} }
.heading { .heading {

View File

@@ -7,17 +7,15 @@ export class ConsentsoftwareMainSelection extends LitElement {
public static demo = () => html`<consentsoftware-mainselection></consentsoftware-mainselection>`; public static demo = () => html`<consentsoftware-mainselection></consentsoftware-mainselection>`;
@property({ type: Boolean }) @property({ type: Boolean })
public required = false; public accessor required = false;
@property({ type: Boolean }) @property({ type: Boolean })
public selected = false; public accessor selected = false;
public static styles = css` public static styles = css`
:host { :host {
display: block; display: block;
position: relative; position: relative;
border-top: 1px solid rgba(255, 255, 255, 0.1);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
} }
.maincontainer { .maincontainer {
@@ -28,7 +26,8 @@ export class ConsentsoftwareMainSelection extends LitElement {
.itemBox { .itemBox {
padding: 16px; padding: 16px;
text-align: center; text-align: center;
border-right: 1px solid rgba(255, 255, 255, 0.1); border-right: 1px solid;
border-image: radial-gradient(circle, rgba(255, 255, 255, 0.7) 0%, rgba(255, 255, 255, 0) 100%) 1;
} }
.itemBox:last-child { .itemBox:last-child {
border-right: none; border-right: none;

View File

@@ -24,6 +24,13 @@ export class ConsentsoftwareTabs extends LitElement {
cursor: pointer; cursor: pointer;
} }
/* Media query for mobile devices: stack buttons vertically */
@media (max-width: 600px) {
.tabs .tab {
font-size: 0.8em;
}
}
.selector { .selector {
position: absolute; position: absolute;
width: calc(100% / 3); width: calc(100% / 3);
@@ -44,7 +51,7 @@ export class ConsentsoftwareTabs extends LitElement {
<div class="tabs"> <div class="tabs">
<div class="tab" @click=${this.handleClick}>Consent</div> <div class="tab" @click=${this.handleClick}>Consent</div>
<div class="tab" @click=${this.handleClick}>Details</div> <div class="tab" @click=${this.handleClick}>Details</div>
<div class="tab" @click=${this.handleClick}>About Cookies</div> <div class="tab" @click=${this.handleClick}>Cookie Policy</div>
</div> </div>
<div class="selector"></div> <div class="selector"></div>
`; `;

View File

@@ -1,18 +1,31 @@
import { LitElement, html, css, type TemplateResult } from 'lit'; import { LitElement, html, css, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js'; import { customElement } from 'lit/decorators.js';
import { property } from 'lit/decorators/property.js'; import { property } from 'lit/decorators/property.js';
import { delayFor } from '@push.rocks/smartdelay'; import { delayFor } from '@push.rocks/smartdelay';
@customElement('consentsoftware-toggle') @customElement('consentsoftware-toggle')
export class ConsentsoftwareToggle extends LitElement { export class ConsentsoftwareToggle extends LitElement {
public static demo = () => html`<consentsoftware-toggle></consentsoftware-toggle>`;
@property({ type: Boolean }) @property({ type: Boolean })
public required = false; public accessor required = false;
@property({ type: Boolean, reflect: true }) @property({ type: Boolean, reflect: true })
public selected = false; public accessor selected = false;
/**
* We always track the knobs left offset in `currentX`.
* - 0 => fully left
* - 30 => fully right
*/
private currentX = 0;
/**
* Drag state
*/
private isDragging = false;
private hasDragged = false;
private startX = 0; // pointerdown offset
private readonly knobWidth = 30;
private readonly trackWidth = 60;
public static styles = css` public static styles = css`
:host { :host {
@@ -24,17 +37,21 @@ export class ConsentsoftwareToggle extends LitElement {
margin-bottom: 16px; margin-bottom: 16px;
} }
.toggle {
user-select: none; /* helps avoid text selection on drag */
}
.toggleKnobArea { .toggleKnobArea {
cursor: pointer; position: relative;
margin: auto; margin: auto;
height: 30px; height: 30px;
width: 60px; width: 60px;
border-radius: 20px; border-radius: 20px;
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
position: relative;
overflow: hidden;
transition: all 0.2s;
border: 1px solid rgba(255, 255, 255, 0); border: 1px solid rgba(255, 255, 255, 0);
overflow: hidden;
transition: background 0.2s ease;
cursor: pointer;
} }
:host([selected]) .toggleKnobArea { :host([selected]) .toggleKnobArea {
@@ -42,19 +59,27 @@ export class ConsentsoftwareToggle extends LitElement {
} }
.toggleKnobMover { .toggleKnobMover {
transition: all 0.2s; position: relative;
height: 100%;
width: 100%;
} }
.toggleKnobInner { .toggleKnobInner {
height: 30px;
width: 30px;
border-radius: 15px;
background: rgba(255, 255, 255, 0.5);
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
transition: all 0.2s; width: 30px;
height: 30px;
border-radius: 15px;
background: rgba(255, 255, 255, 0.5);
transition: left 0.2s ease, background 0.2s ease;
transform: scale(0.7); transform: scale(0.7);
/* Prevent scroll gestures on mobile */
touch-action: none;
}
.toggleKnobInner.dragging {
transition: background 0.2s ease;
} }
:host([selected]) .toggleKnobInner { :host([selected]) .toggleKnobInner {
@@ -78,50 +103,153 @@ export class ConsentsoftwareToggle extends LitElement {
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<div class="label">${this.getText()}</div> <div class="label">${this.getText()}</div>
<!-- A user can click anywhere in this toggle area. -->
<div class="toggle" @click=${this.handleClick}> <div class="toggle" @click=${this.handleClick}>
<div class="toggleKnobArea"> <div class="toggleKnobArea">
<div class="toggleKnobMover"> <div class="toggleKnobMover">
<div class="toggleKnobInner"></div> <!-- The knob itself, with pointer events for dragging. -->
<div
class="toggleKnobInner"
style="left: ${this.currentX}px;"
@pointerdown=${this.onPointerDown}
@pointermove=${this.onPointerMove}
@pointerup=${this.onPointerUp}
@pointercancel=${this.onPointerUp}
></div>
</div> </div>
</div> </div>
</div> </div>
`; `;
} }
public async firstUpdated(_changedProperties: Map<string | number | symbol, unknown>) { /**
super.firstUpdated(_changedProperties); * If required = true on first render, auto-select and set the knob to the right.
*/
public async firstUpdated() {
if (this.required) { if (this.required) {
// If "required" => always selected
this.selected = true; this.selected = true;
this.syncSelection(); this.currentX = this.knobWidth; // 30
this.requestUpdate();
} else {
// If not required, set knob to 0 or 30 depending on `selected`
this.currentX = this.selected ? this.knobWidth : 0;
this.requestUpdate();
} }
} }
public async handleClick(mouseEvent) { /**
if (this.required) { * CLICK HANDLER
const moverElement: HTMLDivElement = this.shadowRoot.querySelector('.toggleKnobMover'); */
moverElement.style.transform = 'translateX(20px)'; public async handleClick(event: MouseEvent) {
await delayFor(250); // If the user truly dragged the knob, skip the normal click toggle.
moverElement.style.transform = 'translateX(30px)'; if (this.isDragging || this.hasDragged) {
event.stopPropagation();
event.preventDefault();
return; return;
} }
if (this.required) {
// small bounce from 30 -> 20 -> 30
this.currentX = this.knobWidth; // ensure at 30
this.requestUpdate();
await new Promise((r) => setTimeout(r, 10));
this.currentX = 20; // small bounce left
this.requestUpdate();
await delayFor(200);
this.currentX = this.knobWidth; // back to 30
this.requestUpdate();
return;
}
// Normal toggle if no drag & not required
event.stopPropagation();
event.preventDefault();
this.selected = !this.selected; this.selected = !this.selected;
mouseEvent.stopPropagation(); this.currentX = this.selected ? this.knobWidth : 0; // snap knob left(0) or right(30)
mouseEvent.preventDefault(); this.requestUpdate();
this.syncSelection();
this.dispatchEvent(new CustomEvent('toggle', { detail: { selected: this.selected } })); this.dispatchEvent(new CustomEvent('toggle', { detail: { selected: this.selected } }));
delayFor(0).then(() => {
this.hasDragged = false;
});
} }
public async syncSelection() { /**
console.log(`Selected ${this.selected}`); * DRAG HANDLERS (pointer events)
const moverElement: HTMLDivElement = this.shadowRoot.querySelector('.toggleKnobMover'); */
if (this.selected) { private onPointerDown(event: PointerEvent) {
moverElement.style.transform = 'translateX(30px)'; if (this.required) {
} else { // If "required", disallow manual dragging
moverElement.style.transform = 'translateX(0px)'; return;
}
} }
public getText() { // Start dragging
this.isDragging = true;
// The difference between the pointers X and the knobs current position
this.startX = event.clientX - this.currentX;
// capture pointer so we keep receiving pointermove/pointerup
(event.target as HTMLElement).setPointerCapture(event.pointerId);
}
private onPointerMove(event: PointerEvent) {
if (!this.isDragging) return;
const newX = event.clientX - this.startX;
this.hasDragged = true;
const toggleKnobInner: HTMLDivElement = this.shadowRoot.querySelector('.toggleKnobInner');
toggleKnobInner.classList.add('dragging');
// Clamp
this.currentX = Math.max(0, Math.min(newX, this.trackWidth - this.knobWidth));
this.requestUpdate();
}
private onPointerUp(event: PointerEvent) {
if (!this.isDragging) return;
(event.target as HTMLElement).releasePointerCapture(event.pointerId);
this.isDragging = false;
// If we didnt truly drag, pointerup does nothing; click handler handles toggling.
if (!this.hasDragged) {
return;
}
const toggleKnobInner: HTMLDivElement = this.shadowRoot.querySelector('.toggleKnobInner');
toggleKnobInner.classList.remove('dragging');
// Real drag => decide final side
const midpoint = (this.trackWidth - this.knobWidth) / 2; // 15
this.selected = this.currentX > midpoint;
this.currentX = this.selected ? this.knobWidth : 0; // snap to edge
this.requestUpdate();
// Dispatch toggle event
this.dispatchEvent(new CustomEvent('toggle', { detail: { selected: this.selected } }));
delayFor(0).then(() => {
this.hasDragged = false;
});
}
/**
* If external code sets `selected = true/false`, we also sync the knob position
*/
protected updated(changedProperties: Map<PropertyKey, unknown>): void {
if (
changedProperties.has('selected') &&
!this.isDragging &&
!this.hasDragged &&
!this.required
) {
this.currentX = this.selected ? this.knobWidth : 0;
this.requestUpdate();
}
super.updated(changedProperties);
}
public getText(): string | null {
return this.textContent; return this.textContent;
} }
} }

View File

@@ -1,9 +1,9 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es2017", "target": "ES2022",
"module": "es2015", "module": "ES2022",
"moduleResolution": "node", "moduleResolution": "bundler",
"lib": ["es2017", "dom"], "lib": ["ES2022", "DOM", "DOM.Iterable"],
"declaration": true, "declaration": true,
"inlineSources": true, "inlineSources": true,
"inlineSourceMap": true, "inlineSourceMap": true,

View File

@@ -1,7 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2020", "target": "ES2022",
"module": "ES2020", "module": "NodeNext",
"moduleResolution": "node12" "moduleResolution": "NodeNext"
} }
} }