Add cookie-banner React example app
Interactive playground with themed banner, headless components, and debug tabs demonstrating programmatic consent access via getConsent() across separate bundles. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
12
examples/cookie-banner-react/index.html
Normal file
12
examples/cookie-banner-react/index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Cookie Banner SDK — React Example</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
22
examples/cookie-banner-react/package.json
Normal file
22
examples/cookie-banner-react/package.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
85
examples/cookie-banner-react/src/App.tsx
Normal file
85
examples/cookie-banner-react/src/App.tsx
Normal file
@@ -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<TabId>("config");
|
||||||
|
const [themedEvents, setThemedEvents] = useState<EventEntry[]>([]);
|
||||||
|
const [headlessEvents, setHeadlessEvents] = useState<EventEntry[]>([]);
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div style={{ fontFamily: "system-ui, sans-serif", maxWidth: 900, margin: "0 auto", padding: 24 }}>
|
||||||
|
<h1 style={{ marginBottom: 4 }}>
|
||||||
|
@probo/cookie-banner
|
||||||
|
</h1>
|
||||||
|
<p style={{ color: "#666", marginTop: 0, marginBottom: 24 }}>
|
||||||
|
SDK example — React
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<nav
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
gap: 0,
|
||||||
|
borderBottom: "2px solid #ddd",
|
||||||
|
marginBottom: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tabs.map((tab) => (
|
||||||
|
<button
|
||||||
|
key={tab.id}
|
||||||
|
onClick={() => setActiveTab(tab.id)}
|
||||||
|
style={{
|
||||||
|
padding: "8px 16px",
|
||||||
|
border: "none",
|
||||||
|
borderBottom:
|
||||||
|
activeTab === tab.id ? "2px solid #333" : "2px solid transparent",
|
||||||
|
background: "none",
|
||||||
|
fontWeight: activeTab === tab.id ? "bold" : "normal",
|
||||||
|
cursor: "pointer",
|
||||||
|
marginBottom: -2,
|
||||||
|
fontSize: 14,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{activeTab === "config" && <ConfigTab />}
|
||||||
|
{activeTab === "themed" && <ThemedBannerTab events={themedEvents} pushEvent={pushThemedEvent} />}
|
||||||
|
{activeTab === "headless" && <HeadlessTab events={headlessEvents} pushEvent={pushHeadlessEvent} />}
|
||||||
|
{activeTab === "debug" && <DebugTab />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
35
examples/cookie-banner-react/src/custom-elements.d.ts
vendored
Normal file
35
examples/cookie-banner-react/src/custom-elements.d.ts
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import "react";
|
||||||
|
|
||||||
|
type CE<T = object> = React.DetailedHTMLProps<
|
||||||
|
React.HTMLAttributes<HTMLElement> & 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;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
examples/cookie-banner-react/src/hooks/useConfig.ts
Normal file
47
examples/cookie-banner-react/src/hooks/useConfig.ts
Normal file
@@ -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];
|
||||||
|
}
|
||||||
7
examples/cookie-banner-react/src/main.tsx
Normal file
7
examples/cookie-banner-react/src/main.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { StrictMode } from "react";
|
||||||
|
import { createRoot } from "react-dom/client";
|
||||||
|
import { App } from "./App";
|
||||||
|
|
||||||
|
createRoot(document.getElementById("root")!).render(
|
||||||
|
<App />
|
||||||
|
);
|
||||||
96
examples/cookie-banner-react/src/tabs/ConfigTab.tsx
Normal file
96
examples/cookie-banner-react/src/tabs/ConfigTab.tsx
Normal file
@@ -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 (
|
||||||
|
<div>
|
||||||
|
<h2>Configuration</h2>
|
||||||
|
<p style={{ color: "#666", marginBottom: 16 }}>
|
||||||
|
Set the banner ID and base URL for the cookie banner API. These values
|
||||||
|
are persisted to localStorage and used by every other tab.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<label style={{ display: "block", marginBottom: 4, fontWeight: "bold" }}>
|
||||||
|
Banner ID
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={bannerId}
|
||||||
|
onChange={(e) => 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",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: 16 }}>
|
||||||
|
<label style={{ display: "block", marginBottom: 4, fontWeight: "bold" }}>
|
||||||
|
Base URL
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={baseUrl}
|
||||||
|
onChange={(e) => 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",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={save}
|
||||||
|
disabled={!dirty || !bannerId || !baseUrl}
|
||||||
|
style={{
|
||||||
|
padding: "8px 16px",
|
||||||
|
fontWeight: "bold",
|
||||||
|
cursor: dirty && bannerId && baseUrl ? "pointer" : "not-allowed",
|
||||||
|
opacity: dirty && bannerId && baseUrl ? 1 : 0.5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{config.bannerId && config.baseUrl && (
|
||||||
|
<div style={{ marginTop: 24 }}>
|
||||||
|
<h3>Current saved config</h3>
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
background: "#f5f5f5",
|
||||||
|
padding: 12,
|
||||||
|
border: "1px solid #ddd",
|
||||||
|
overflow: "auto",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{JSON.stringify(config, null, 2)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
195
examples/cookie-banner-react/src/tabs/DebugTab.tsx
Normal file
195
examples/cookie-banner-react/src/tabs/DebugTab.tsx
Normal file
@@ -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<ConsentSnapshot>(readSnapshot);
|
||||||
|
const [visitorId, setVisitorId] = useState<string | null>(() =>
|
||||||
|
readVisitorId(config.bannerId),
|
||||||
|
);
|
||||||
|
const [cookie, setCookie] = useState<string | null>(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 (
|
||||||
|
<div>
|
||||||
|
<h2>Debug</h2>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: "1px solid #ccc",
|
||||||
|
padding: 12,
|
||||||
|
background: "#fafafa",
|
||||||
|
marginBottom: 16,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h3 style={{ marginTop: 0 }}>getConsent() State</h3>
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
background: "#f0f0f0",
|
||||||
|
padding: 8,
|
||||||
|
border: "1px solid #ddd",
|
||||||
|
overflow: "auto",
|
||||||
|
margin: "0 0 12px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{JSON.stringify(
|
||||||
|
{ ready: snapshot.ready, hasResponse: snapshot.hasResponse },
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
)}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
{Object.keys(snapshot.data).length === 0 ? (
|
||||||
|
<p style={{ color: "#999", margin: 0 }}>
|
||||||
|
No consent data yet. Interact with the banner to generate consent.
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<table
|
||||||
|
style={{
|
||||||
|
borderCollapse: "collapse",
|
||||||
|
fontFamily: "monospace",
|
||||||
|
fontSize: 14,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style={{
|
||||||
|
textAlign: "left",
|
||||||
|
padding: "4px 16px 4px 0",
|
||||||
|
borderBottom: "1px solid #ccc",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Category
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
style={{
|
||||||
|
textAlign: "left",
|
||||||
|
padding: "4px 0",
|
||||||
|
borderBottom: "1px solid #ccc",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
has()
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{Object.entries(snapshot.data).map(([cat, granted]) => (
|
||||||
|
<tr key={cat}>
|
||||||
|
<td style={{ padding: "4px 16px 4px 0" }}>{cat}</td>
|
||||||
|
<td
|
||||||
|
style={{
|
||||||
|
padding: "4px 0",
|
||||||
|
color: granted ? "green" : "red",
|
||||||
|
fontWeight: "bold",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{String(granted)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: "1px solid #ccc",
|
||||||
|
padding: 12,
|
||||||
|
background: "#fafafa",
|
||||||
|
marginBottom: 16,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h3 style={{ marginTop: 0 }}>Storage</h3>
|
||||||
|
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<strong>Visitor ID</strong>{" "}
|
||||||
|
<span style={{ fontFamily: "monospace", fontSize: 13, color: "#666" }}>
|
||||||
|
(localStorage: probo_consent:{config.bannerId || "?"}:vid)
|
||||||
|
</span>
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
background: "#f0f0f0",
|
||||||
|
padding: 8,
|
||||||
|
border: "1px solid #ddd",
|
||||||
|
overflow: "auto",
|
||||||
|
margin: "4px 0 0 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{visitorId ?? "(not set)"}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<strong>probo_consent cookie</strong>
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
background: "#f0f0f0",
|
||||||
|
padding: 8,
|
||||||
|
border: "1px solid #ddd",
|
||||||
|
overflow: "auto",
|
||||||
|
margin: "4px 0 0 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{cookie
|
||||||
|
? JSON.stringify(JSON.parse(cookie), null, 2)
|
||||||
|
: "(not set)"}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
140
examples/cookie-banner-react/src/tabs/HeadlessTab.tsx
Normal file
140
examples/cookie-banner-react/src/tabs/HeadlessTab.tsx
Normal file
@@ -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<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!registered) {
|
||||||
|
registerHeadlessComponents();
|
||||||
|
registered = true;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const container = containerRef.current;
|
||||||
|
if (!container || !config.bannerId || !config.baseUrl) return;
|
||||||
|
|
||||||
|
container.innerHTML = "";
|
||||||
|
|
||||||
|
container.innerHTML = `
|
||||||
|
<style>probo-banner, probo-preference-panel { display: block !important; }</style>
|
||||||
|
<probo-cookie-banner-root banner-id="${config.bannerId}" base-url="${config.baseUrl}">
|
||||||
|
<probo-banner>
|
||||||
|
<div style="border:2px solid #333;padding:12px;margin-bottom:8px;">
|
||||||
|
<strong>[probo-banner]</strong>
|
||||||
|
<div style="margin-top:8px;">
|
||||||
|
<probo-accept-button><button>Accept All</button></probo-accept-button>
|
||||||
|
<probo-reject-button><button style="margin-left:8px;">Reject All</button></probo-reject-button>
|
||||||
|
<probo-customize-button><button style="margin-left:8px;">Customize</button></probo-customize-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</probo-banner>
|
||||||
|
|
||||||
|
<probo-preference-panel>
|
||||||
|
<div style="border:2px dashed #666;padding:12px;margin-bottom:8px;">
|
||||||
|
<strong>[probo-preference-panel]</strong>
|
||||||
|
<probo-category-list>
|
||||||
|
<template>
|
||||||
|
<div style="border:1px solid #aaa;padding:8px;margin:4px 0;">
|
||||||
|
<span data-slot="name" style="font-weight:bold;"></span>:
|
||||||
|
<span data-slot="description"></span>
|
||||||
|
<probo-category-toggle>
|
||||||
|
<label style="margin-left:8px;"><input type="checkbox" /> toggle</label>
|
||||||
|
</probo-category-toggle>
|
||||||
|
<probo-cookie-list hidden>
|
||||||
|
<template>
|
||||||
|
<div style="padding:4px 0 4px 16px;font-size:13px;">
|
||||||
|
<span data-slot="name" style="font-weight:bold;"></span>
|
||||||
|
— <span data-slot="description"></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</probo-cookie-list>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</probo-category-list>
|
||||||
|
<div style="margin-top:8px;">
|
||||||
|
<probo-accept-button><button>Accept All</button></probo-accept-button>
|
||||||
|
<probo-reject-button><button style="margin-left:8px;">Reject All</button></probo-reject-button>
|
||||||
|
<probo-save-button><button style="margin-left:8px;">Save Preferences</button></probo-save-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</probo-preference-panel>
|
||||||
|
</probo-cookie-banner-root>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div>
|
||||||
|
<h2>Headless Components</h2>
|
||||||
|
<p style={{ color: "tomato" }}>
|
||||||
|
Set banner ID and base URL in the Config tab first.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Headless Components</h2>
|
||||||
|
<p style={{ color: "#666", marginBottom: 16 }}>
|
||||||
|
Uses <code>registerHeadlessComponents()</code> and renders raw headless
|
||||||
|
elements with no themed styling. Borders show element boundaries.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div ref={containerRef} />
|
||||||
|
|
||||||
|
<h3>Events ({events.length})</h3>
|
||||||
|
{events.length === 0 ? (
|
||||||
|
<p style={{ color: "#999" }}>No events yet.</p>
|
||||||
|
) : (
|
||||||
|
events.map((ev, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
style={{
|
||||||
|
marginBottom: 8,
|
||||||
|
border: "1px solid #ddd",
|
||||||
|
padding: 8,
|
||||||
|
background: "#f5f5f5",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ fontWeight: "bold", marginBottom: 4 }}>
|
||||||
|
{ev.type}{" "}
|
||||||
|
<span style={{ fontWeight: "normal", color: "#999" }}>
|
||||||
|
{ev.time}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<pre style={{ margin: 0, overflow: "auto" }}>
|
||||||
|
{JSON.stringify(ev.detail, null, 2)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
94
examples/cookie-banner-react/src/tabs/ThemedBannerTab.tsx
Normal file
94
examples/cookie-banner-react/src/tabs/ThemedBannerTab.tsx
Normal file
@@ -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<HTMLElement | null>(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 (
|
||||||
|
<div>
|
||||||
|
<h2>Themed Banner</h2>
|
||||||
|
<p style={{ color: "tomato" }}>
|
||||||
|
Set banner ID and base URL in the Config tab first.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Themed Banner</h2>
|
||||||
|
<p style={{ color: "#666", marginBottom: 16 }}>
|
||||||
|
Uses <code>registerCookieBanner()</code> and renders{" "}
|
||||||
|
<code><probo-cookie-banner></code>. The banner appears in the
|
||||||
|
bottom-right corner.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<probo-cookie-banner
|
||||||
|
ref={attachListeners}
|
||||||
|
banner-id={config.bannerId}
|
||||||
|
base-url={config.baseUrl}
|
||||||
|
position="bottom-right"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h3>Events ({events.length})</h3>
|
||||||
|
{events.length === 0 ? (
|
||||||
|
<p style={{ color: "#999" }}>No events yet.</p>
|
||||||
|
) : (
|
||||||
|
events.map((ev, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
style={{
|
||||||
|
marginBottom: 8,
|
||||||
|
border: "1px solid #ddd",
|
||||||
|
padding: 8,
|
||||||
|
background: "#f5f5f5",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ fontWeight: "bold", marginBottom: 4 }}>
|
||||||
|
{ev.type}{" "}
|
||||||
|
<span style={{ fontWeight: "normal", color: "#999" }}>
|
||||||
|
{ev.time}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<pre style={{ margin: 0, overflow: "auto" }}>
|
||||||
|
{JSON.stringify(ev.detail, null, 2)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
15
examples/cookie-banner-react/tsconfig.json
Normal file
15
examples/cookie-banner-react/tsconfig.json
Normal file
@@ -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"]
|
||||||
|
}
|
||||||
9
examples/cookie-banner-react/vite.config.ts
Normal file
9
examples/cookie-banner-react/vite.config.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
server: {
|
||||||
|
port: 5180,
|
||||||
|
},
|
||||||
|
});
|
||||||
635
package-lock.json
generated
635
package-lock.json
generated
@@ -8,7 +8,8 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"packages/*"
|
"packages/*",
|
||||||
|
"examples/*"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react-dom": "^19.2.1"
|
"react-dom": "^19.2.1"
|
||||||
@@ -109,6 +110,634 @@
|
|||||||
"vite": "^7.3.2"
|
"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": {
|
"node_modules/@adobe/css-tools": {
|
||||||
"version": "4.4.4",
|
"version": "4.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz",
|
||||||
@@ -3264,6 +3893,10 @@
|
|||||||
"resolved": "packages/eslint-relay-plugin-types",
|
"resolved": "packages/eslint-relay-plugin-types",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@probo/example-cookie-banner-react": {
|
||||||
|
"resolved": "examples/cookie-banner-react",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/@probo/helpers": {
|
"node_modules/@probo/helpers": {
|
||||||
"resolved": "packages/helpers",
|
"resolved": "packages/helpers",
|
||||||
"link": true
|
"link": true
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"packages/*"
|
"packages/*",
|
||||||
|
"examples/*"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
|
|||||||
Reference in New Issue
Block a user