Initial commit

This commit is contained in:
2026-08-02 20:55:23 +02:00
parent 411f484b40
commit d558d18d9a
295 changed files with 9 additions and 5880 deletions
-179
View File
@@ -1,179 +0,0 @@
'use client';
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { Cookie } from 'lucide-react';
import { useEffect, useState } from 'react';
import { CONSENT_CATEGORIES } from './categories.js';
import { useConsentContext } from './ConsentContext.js';
/**
* Colours and radius come from CSS custom properties with built-in fallbacks,
* so the banner looks right with no setup, and re-skinning per client means
* declaring a few variables — no imports, no props, no specificity fights:
*
* ```css
* :root {
* --ipal-primary: #16a34a;
* --ipal-radius: 1rem;
* }
* ```
*
* Available: --ipal-surface, --ipal-border, --ipal-text, --ipal-text-strong,
* --ipal-text-muted, --ipal-primary, --ipal-primary-hover, --ipal-primary-text,
* --ipal-hover, --ipal-radius. Each has a `-dark` counterpart used under
* Tailwind's `dark:` variant (e.g. --ipal-surface-dark).
*
* Layout stays in Tailwind: spacing and flow aren't things a brand changes, and
* exposing them as tokens would mean re-inventing CSS one variable at a time.
*/ const surface = 'bg-[var(--ipal-surface,#fff)] dark:bg-[var(--ipal-surface-dark,#171717)]';
const border = 'border-[var(--ipal-border,#e5e5e5)] dark:border-[var(--ipal-border-dark,#262626)]';
const radius = 'rounded-[var(--ipal-radius,0.375rem)]';
const accent = 'text-[var(--ipal-primary,#2563eb)]';
const defaults = {
primaryButton: `${radius} px-3 py-1.5 text-sm font-medium bg-[var(--ipal-primary,#2563eb)] text-[var(--ipal-primary-text,#fff)] hover:bg-[var(--ipal-primary-hover,#1d4ed8)]`,
root: `fixed inset-x-0 bottom-0 z-50 border-t p-4 shadow-lg ${surface} ${border}`,
secondaryButton: `${radius} px-3 py-1.5 text-sm font-medium text-[var(--ipal-text,#404040)] hover:bg-[var(--ipal-hover,#f5f5f5)] dark:text-[var(--ipal-text-dark,#e5e5e5)] dark:hover:bg-[var(--ipal-hover-dark,#262626)]`
};
const bodyText = 'text-[var(--ipal-text,#404040)] dark:text-[var(--ipal-text-dark,#d4d4d4)]';
const strongText = 'text-[var(--ipal-text-strong,#171717)] dark:text-[var(--ipal-text-strong-dark,#f5f5f5)]';
const mutedText = 'text-[var(--ipal-text-muted,#737373)] dark:text-[var(--ipal-text-muted-dark,#a3a3a3)]';
export function CookieBanner({ classNames } = {}) {
const { acceptAll, decided, rejectAll, savePreferences, state, texts } = useConsentContext();
const [showSettings, setShowSettings] = useState(false);
const [choices, setChoices] = useState(state);
useEffect(()=>{
setChoices(state);
}, [
state
]);
if (decided) {
return null;
}
const toggle = (cat)=>{
if (cat === 'necessary') {
return;
}
setChoices((prev)=>({
...prev,
[cat]: !prev[cat]
}));
};
const rootClass = classNames?.root ?? defaults.root;
const primaryClass = classNames?.primaryButton ?? defaults.primaryButton;
const secondaryClass = classNames?.secondaryButton ?? defaults.secondaryButton;
return /*#__PURE__*/ _jsx("div", {
"aria-label": "Cookie consent",
className: rootClass,
"data-ipal": "banner",
role: "dialog",
children: /*#__PURE__*/ _jsx("div", {
className: "mx-auto max-w-4xl",
children: !showSettings ? /*#__PURE__*/ _jsxs("div", {
className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "flex items-start gap-3",
children: [
/*#__PURE__*/ _jsx(Cookie, {
"aria-hidden": "true",
className: `h-6 w-6 shrink-0 ${accent}`
}),
/*#__PURE__*/ _jsxs("p", {
className: `text-justify text-sm ${bodyText}`,
children: [
texts.message,
texts.privacyLink && /*#__PURE__*/ _jsxs(_Fragment, {
children: [
' ',
/*#__PURE__*/ _jsx("a", {
className: `${accent} underline hover:no-underline`,
href: texts.privacyLink.href,
children: texts.privacyLink.label
})
]
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "flex shrink-0 gap-2",
children: [
/*#__PURE__*/ _jsx("button", {
className: secondaryClass,
onClick: ()=>setShowSettings(true),
children: texts.buttons.settings
}),
/*#__PURE__*/ _jsx("button", {
className: secondaryClass,
onClick: rejectAll,
children: texts.buttons.reject
}),
/*#__PURE__*/ _jsx("button", {
className: primaryClass,
onClick: acceptAll,
children: texts.buttons.acceptAll
})
]
})
]
}) : /*#__PURE__*/ _jsxs("div", {
className: "flex flex-col gap-4",
children: [
/*#__PURE__*/ _jsxs("h2", {
className: `flex items-center gap-2 text-base font-semibold ${strongText}`,
children: [
/*#__PURE__*/ _jsx(Cookie, {
"aria-hidden": "true",
className: `h-5 w-5 ${accent}`
}),
texts.settingsTitle
]
}),
/*#__PURE__*/ _jsx("ul", {
className: "flex flex-col gap-3",
children: CONSENT_CATEGORIES.map((cat)=>/*#__PURE__*/ _jsxs("li", {
className: "flex items-start justify-between gap-4",
children: [
/*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: `text-sm font-medium ${strongText}`,
children: texts.categories[cat].title
}),
/*#__PURE__*/ _jsx("p", {
className: `text-xs ${mutedText}`,
children: texts.categories[cat].description
})
]
}),
/*#__PURE__*/ _jsx("input", {
checked: cat === 'necessary' ? true : choices[cat],
className: "mt-1 h-4 w-4 shrink-0 accent-[var(--ipal-primary,#2563eb)]",
disabled: cat === 'necessary',
onChange: ()=>toggle(cat),
type: "checkbox"
})
]
}, cat))
}),
/*#__PURE__*/ _jsxs("div", {
className: "flex justify-end gap-2",
children: [
/*#__PURE__*/ _jsx("button", {
className: secondaryClass,
onClick: ()=>setShowSettings(false),
children: texts.buttons.back
}),
/*#__PURE__*/ _jsx("button", {
className: primaryClass,
onClick: ()=>savePreferences(choices),
children: texts.buttons.save
})
]
})
]
})
})
});
}
//# sourceMappingURL=CookieBanner.js.map