diff --git a/examples/cookie-banner-react/index.html b/examples/cookie-banner-react/index.html new file mode 100644 index 000000000..3f854ed16 --- /dev/null +++ b/examples/cookie-banner-react/index.html @@ -0,0 +1,12 @@ + + + + + + Cookie Banner SDK — React Example + + +
+ + + diff --git a/examples/cookie-banner-react/package.json b/examples/cookie-banner-react/package.json new file mode 100644 index 000000000..17201dd2f --- /dev/null +++ b/examples/cookie-banner-react/package.json @@ -0,0 +1,22 @@ +{ + "name": "@probo/example-cookie-banner-react", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build" + }, + "dependencies": { + "@probo/cookie-banner": "*", + "react": "^19.2.1", + "react-dom": "^19.2.1" + }, + "devDependencies": { + "@types/react": "^19.2.1", + "@types/react-dom": "^19.2.1", + "@vitejs/plugin-react": "^4.5.2", + "typescript": "~5.8.3", + "vite": "^6.3.5" + } +} diff --git a/examples/cookie-banner-react/src/App.tsx b/examples/cookie-banner-react/src/App.tsx new file mode 100644 index 000000000..8abcee41f --- /dev/null +++ b/examples/cookie-banner-react/src/App.tsx @@ -0,0 +1,85 @@ +import { useCallback, useState } from "react"; +import { ConfigTab } from "./tabs/ConfigTab"; +import { ThemedBannerTab } from "./tabs/ThemedBannerTab"; +import { HeadlessTab } from "./tabs/HeadlessTab"; +import { DebugTab } from "./tabs/DebugTab"; + +export interface EventEntry { + time: string; + type: string; + detail: unknown; +} + +const tabs = [ + { id: "config", label: "Config" }, + { id: "themed", label: "Themed Banner" }, + { id: "headless", label: "Headless" }, + { id: "debug", label: "Debug" }, +] as const; + +type TabId = (typeof tabs)[number]["id"]; + +export function App() { + const [activeTab, setActiveTab] = useState("config"); + const [themedEvents, setThemedEvents] = useState([]); + const [headlessEvents, setHeadlessEvents] = useState([]); + + const pushThemedEvent = useCallback((type: string, detail: unknown) => { + setThemedEvents((prev) => [ + { time: new Date().toISOString(), type, detail }, + ...prev, + ]); + }, []); + + const pushHeadlessEvent = useCallback((type: string, detail: unknown) => { + setHeadlessEvents((prev) => [ + { time: new Date().toISOString(), type, detail }, + ...prev, + ]); + }, []); + + return ( +
+

+ @probo/cookie-banner +

+

+ SDK example — React +

+ + + + {activeTab === "config" && } + {activeTab === "themed" && } + {activeTab === "headless" && } + {activeTab === "debug" && } +
+ ); +} diff --git a/examples/cookie-banner-react/src/custom-elements.d.ts b/examples/cookie-banner-react/src/custom-elements.d.ts new file mode 100644 index 000000000..2caee7d51 --- /dev/null +++ b/examples/cookie-banner-react/src/custom-elements.d.ts @@ -0,0 +1,35 @@ +import "react"; + +type CE = React.DetailedHTMLProps< + React.HTMLAttributes & T, + HTMLElement +>; + +declare module "react" { + namespace JSX { + interface IntrinsicElements { + "probo-cookie-banner-root": CE<{ + "banner-id"?: string; + "base-url"?: string; + lang?: string; + }>; + "probo-banner": CE; + "probo-preference-panel": CE; + "probo-category-list": CE; + "probo-category-toggle": CE; + "probo-cookie-list": CE; + "probo-accept-button": CE; + "probo-reject-button": CE; + "probo-customize-button": CE; + "probo-save-button": CE; + "probo-settings-button": CE<{ position?: string }>; + "probo-cookie-banner": CE<{ + "banner-id"?: string; + "base-url"?: string; + position?: string; + "reopen-widget"?: string; + lang?: string; + }>; + } + } +} diff --git a/examples/cookie-banner-react/src/hooks/useConfig.ts b/examples/cookie-banner-react/src/hooks/useConfig.ts new file mode 100644 index 000000000..7dfac8a8a --- /dev/null +++ b/examples/cookie-banner-react/src/hooks/useConfig.ts @@ -0,0 +1,47 @@ +import { useCallback, useSyncExternalStore } from "react"; + +export interface Config { + bannerId: string; + baseUrl: string; +} + +const STORAGE_KEY = "probo-example-config"; + +const defaultConfig: Config = { + bannerId: "", + baseUrl: "", +}; + +function getSnapshot(): Config { + try { + const raw = localStorage.getItem(STORAGE_KEY); + if (raw) return JSON.parse(raw) as Config; + } catch { + // ignore + } + return defaultConfig; +} + +let cached = getSnapshot(); +const listeners = new Set<() => void>(); + +function subscribe(cb: () => void): () => void { + listeners.add(cb); + return () => listeners.delete(cb); +} + +function snapshot(): Config { + return cached; +} + +function persist(next: Config): void { + cached = next; + localStorage.setItem(STORAGE_KEY, JSON.stringify(next)); + for (const cb of listeners) cb(); +} + +export function useConfig(): [Config, (next: Config) => void] { + const config = useSyncExternalStore(subscribe, snapshot); + const setConfig = useCallback((next: Config) => persist(next), []); + return [config, setConfig]; +} diff --git a/examples/cookie-banner-react/src/main.tsx b/examples/cookie-banner-react/src/main.tsx new file mode 100644 index 000000000..cf8afd8d0 --- /dev/null +++ b/examples/cookie-banner-react/src/main.tsx @@ -0,0 +1,7 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { App } from "./App"; + +createRoot(document.getElementById("root")!).render( + +); diff --git a/examples/cookie-banner-react/src/tabs/ConfigTab.tsx b/examples/cookie-banner-react/src/tabs/ConfigTab.tsx new file mode 100644 index 000000000..ee8a4bf40 --- /dev/null +++ b/examples/cookie-banner-react/src/tabs/ConfigTab.tsx @@ -0,0 +1,96 @@ +import { useState } from "react"; +import { useConfig } from "../hooks/useConfig"; + +export function ConfigTab() { + const [config, setConfig] = useConfig(); + const [bannerId, setBannerId] = useState(config.bannerId); + const [baseUrl, setBaseUrl] = useState(config.baseUrl); + + const dirty = + bannerId !== config.bannerId || baseUrl !== config.baseUrl; + + const save = () => { + setConfig({ bannerId, baseUrl }); + }; + + return ( +
+

Configuration

+

+ Set the banner ID and base URL for the cookie banner API. These values + are persisted to localStorage and used by every other tab. +

+ +
+ + setBannerId(e.target.value)} + placeholder="e.g. cm9xkz5ab000208jx1yy99abc" + style={{ + width: "100%", + maxWidth: 500, + padding: "6px 8px", + fontFamily: "monospace", + fontSize: 14, + border: "1px solid #ccc", + boxSizing: "border-box", + }} + /> +
+ +
+ + setBaseUrl(e.target.value)} + placeholder="e.g. https://cookie-banner.getprobo.com/v1/banners/" + style={{ + width: "100%", + maxWidth: 500, + padding: "6px 8px", + fontFamily: "monospace", + fontSize: 14, + border: "1px solid #ccc", + boxSizing: "border-box", + }} + /> +
+ + + + {config.bannerId && config.baseUrl && ( +
+

Current saved config

+
+            {JSON.stringify(config, null, 2)}
+          
+
+ )} +
+ ); +} diff --git a/examples/cookie-banner-react/src/tabs/DebugTab.tsx b/examples/cookie-banner-react/src/tabs/DebugTab.tsx new file mode 100644 index 000000000..6f977a2b9 --- /dev/null +++ b/examples/cookie-banner-react/src/tabs/DebugTab.tsx @@ -0,0 +1,195 @@ +import { useEffect, useState } from "react"; +import { getConsent } from "@probo/cookie-banner/consent"; +import type { ConsentData } from "@probo/cookie-banner/consent"; +import { useConfig } from "../hooks/useConfig"; + +interface ConsentSnapshot { + ready: boolean; + hasResponse: boolean; + data: ConsentData; +} + +function readSnapshot(): ConsentSnapshot { + const mgr = getConsent(); + return { + ready: mgr.ready, + hasResponse: mgr.hasResponse, + data: mgr.getAll(), + }; +} + +function readVisitorId(bannerId: string): string | null { + if (!bannerId) return null; + try { + return localStorage.getItem(`probo_consent:${bannerId}:vid`); + } catch { + return null; + } +} + +function readCookie(): string | null { + try { + const prefix = "probo_consent="; + const entry = document.cookie + .split("; ") + .find((c) => c.startsWith(prefix)); + if (!entry) return null; + return decodeURIComponent(entry.substring(prefix.length)); + } catch { + return null; + } +} + +export function DebugTab() { + const [config] = useConfig(); + const [snapshot, setSnapshot] = useState(readSnapshot); + const [visitorId, setVisitorId] = useState(() => + readVisitorId(config.bannerId), + ); + const [cookie, setCookie] = useState(readCookie); + + useEffect(() => { + const mgr = getConsent(); + return mgr.subscribe(() => { + setSnapshot(readSnapshot()); + setVisitorId(readVisitorId(config.bannerId)); + setCookie(readCookie()); + }); + }, [config.bannerId]); + + useEffect(() => { + setVisitorId(readVisitorId(config.bannerId)); + setCookie(readCookie()); + }, [config.bannerId]); + + return ( +
+

Debug

+ +
+

getConsent() State

+
+          {JSON.stringify(
+            { ready: snapshot.ready, hasResponse: snapshot.hasResponse },
+            null,
+            2,
+          )}
+        
+ + {Object.keys(snapshot.data).length === 0 ? ( +

+ No consent data yet. Interact with the banner to generate consent. +

+ ) : ( + + + + + + + + + {Object.entries(snapshot.data).map(([cat, granted]) => ( + + + + + ))} + +
+ Category + + has() +
{cat} + {String(granted)} +
+ )} +
+ +
+

Storage

+ +
+ Visitor ID{" "} + + (localStorage: probo_consent:{config.bannerId || "?"}:vid) + +
+            {visitorId ?? "(not set)"}
+          
+
+ +
+ probo_consent cookie +
+            {cookie
+              ? JSON.stringify(JSON.parse(cookie), null, 2)
+              : "(not set)"}
+          
+
+
+
+ ); +} diff --git a/examples/cookie-banner-react/src/tabs/HeadlessTab.tsx b/examples/cookie-banner-react/src/tabs/HeadlessTab.tsx new file mode 100644 index 000000000..f04f47725 --- /dev/null +++ b/examples/cookie-banner-react/src/tabs/HeadlessTab.tsx @@ -0,0 +1,140 @@ +import { useEffect, useRef } from "react"; +import { registerHeadlessComponents } from "@probo/cookie-banner/headless"; +import { useConfig } from "../hooks/useConfig"; +import type { EventEntry } from "../App"; + +let registered = false; + +interface HeadlessTabProps { + events: EventEntry[]; + pushEvent: (type: string, detail: unknown) => void; +} + +export function HeadlessTab({ events, pushEvent }: HeadlessTabProps) { + const [config] = useConfig(); + const containerRef = useRef(null); + + useEffect(() => { + if (!registered) { + registerHeadlessComponents(); + registered = true; + } + }, []); + + useEffect(() => { + const container = containerRef.current; + if (!container || !config.bannerId || !config.baseUrl) return; + + container.innerHTML = ""; + + container.innerHTML = ` + + + +
+ [probo-banner] +
+ + + +
+
+
+ + +
+ [probo-preference-panel] + + + +
+ + + +
+
+
+
+ `; + + const root = container.querySelector("probo-cookie-banner-root"); + if (root) { + root.addEventListener("probo-ready", (e: Event) => + pushEvent("probo-ready", (e as CustomEvent).detail), + ); + root.addEventListener("probo-consent", (e: Event) => + pushEvent("probo-consent", (e as CustomEvent).detail), + ); + } + + return () => { + container.innerHTML = ""; + }; + }, [config.bannerId, config.baseUrl, pushEvent]); + + if (!config.bannerId || !config.baseUrl) { + return ( +
+

Headless Components

+

+ Set banner ID and base URL in the Config tab first. +

+
+ ); + } + + return ( +
+

Headless Components

+

+ Uses registerHeadlessComponents() and renders raw headless + elements with no themed styling. Borders show element boundaries. +

+ +
+ +

Events ({events.length})

+ {events.length === 0 ? ( +

No events yet.

+ ) : ( + events.map((ev, i) => ( +
+
+ {ev.type}{" "} + + {ev.time} + +
+
+              {JSON.stringify(ev.detail, null, 2)}
+            
+
+ )) + )} +
+ ); +} diff --git a/examples/cookie-banner-react/src/tabs/ThemedBannerTab.tsx b/examples/cookie-banner-react/src/tabs/ThemedBannerTab.tsx new file mode 100644 index 000000000..e52895381 --- /dev/null +++ b/examples/cookie-banner-react/src/tabs/ThemedBannerTab.tsx @@ -0,0 +1,94 @@ +import { useCallback, useEffect, useRef } from "react"; +import { registerCookieBanner } from "@probo/cookie-banner"; +import { useConfig } from "../hooks/useConfig"; +import type { EventEntry } from "../App"; + +let registered = false; + +interface ThemedBannerTabProps { + events: EventEntry[]; + pushEvent: (type: string, detail: unknown) => void; +} + +export function ThemedBannerTab({ events, pushEvent }: ThemedBannerTabProps) { + const [config] = useConfig(); + const elRef = useRef(null); + + useEffect(() => { + if (!registered) { + registerCookieBanner(); + registered = true; + } + }, []); + + const attachListeners = useCallback( + (el: HTMLElement | null) => { + elRef.current = el; + if (!el) return; + + el.addEventListener("probo-ready", (e: Event) => + pushEvent("probo-ready", (e as CustomEvent).detail), + ); + el.addEventListener("probo-consent", (e: Event) => + pushEvent("probo-consent", (e as CustomEvent).detail), + ); + }, + [pushEvent], + ); + + if (!config.bannerId || !config.baseUrl) { + return ( +
+

Themed Banner

+

+ Set banner ID and base URL in the Config tab first. +

+
+ ); + } + + return ( +
+

Themed Banner

+

+ Uses registerCookieBanner() and renders{" "} + <probo-cookie-banner>. The banner appears in the + bottom-right corner. +

+ + + +

Events ({events.length})

+ {events.length === 0 ? ( +

No events yet.

+ ) : ( + events.map((ev, i) => ( +
+
+ {ev.type}{" "} + + {ev.time} + +
+
+              {JSON.stringify(ev.detail, null, 2)}
+            
+
+ )) + )} +
+ ); +} diff --git a/examples/cookie-banner-react/tsconfig.json b/examples/cookie-banner-react/tsconfig.json new file mode 100644 index 000000000..2df48d01e --- /dev/null +++ b/examples/cookie-banner-react/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "isolatedModules": true, + "noEmit": true + }, + "include": ["src"] +} diff --git a/examples/cookie-banner-react/vite.config.ts b/examples/cookie-banner-react/vite.config.ts new file mode 100644 index 000000000..a2889c22f --- /dev/null +++ b/examples/cookie-banner-react/vite.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig({ + plugins: [react()], + server: { + port: 5180, + }, +}); diff --git a/package-lock.json b/package-lock.json index 7f195299f..c6e210f27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,8 @@ "license": "MIT", "workspaces": [ "apps/*", - "packages/*" + "packages/*", + "examples/*" ], "dependencies": { "react-dom": "^19.2.1" @@ -109,6 +110,634 @@ "vite": "^7.3.2" } }, + "examples/cookie-banner-react": { + "name": "@probo/example-cookie-banner-react", + "version": "0.0.0", + "dependencies": { + "@probo/cookie-banner": "*", + "react": "^19.2.1", + "react-dom": "^19.2.1" + }, + "devDependencies": { + "@types/react": "^19.2.1", + "@types/react-dom": "^19.2.1", + "@vitejs/plugin-react": "^4.5.2", + "typescript": "~5.8.3", + "vite": "^6.3.5" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "examples/cookie-banner-react/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "examples/cookie-banner-react/node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "examples/cookie-banner-react/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "examples/cookie-banner-react/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "examples/cookie-banner-react/node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "examples/cookie-banner-react/node_modules/vite": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "node_modules/@adobe/css-tools": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", @@ -3264,6 +3893,10 @@ "resolved": "packages/eslint-relay-plugin-types", "link": true }, + "node_modules/@probo/example-cookie-banner-react": { + "resolved": "examples/cookie-banner-react", + "link": true + }, "node_modules/@probo/helpers": { "resolved": "packages/helpers", "link": true diff --git a/package.json b/package.json index 0762c0c2d..77aef2171 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ }, "workspaces": [ "apps/*", - "packages/*" + "packages/*", + "examples/*" ], "scripts": { "build": "turbo run build",