feat: Add CookieModal component with Toggle functionality

This commit is contained in:
Gauthier Daniels 2025-04-18 14:19:06 +02:00
parent df859c0c06
commit 993089e8a0
3 changed files with 144 additions and 0 deletions

View File

@ -0,0 +1,44 @@
import clsx from "clsx";
type ToggleProps = {
id: string;
label: string;
onChange?: (checked: boolean) => void;
checked: boolean;
};
export function Toggle(props: ToggleProps) {
return (
<div className="flex items-center justify-center">
<input
type="checkbox"
id={props.id}
className="sr-only"
onChange={(e) => props.onChange?.(e.target.checked)}
checked={props.checked}
aria-checked={props.checked}
role="switch"
aria-label={props.label}
/>
<label htmlFor={props.id} className="flex cursor-pointer items-center justify-between rounded-full">
<span className="relative flex h-6 w-10 items-center">
<span
className={clsx(
"h-4 w-4 rounded-full bg-white shadow-md transition-transform duration-200 ease-in-out z-10",
props.checked ? "translate-x-full" : "translate-x-0",
)}
/>
<span
className={clsx(
"absolute top-1/2 left-1/2 h-full w-full -translate-x-1/2 -translate-y-1/2 rounded-full transition duration-200 ease-in-out z-0",
props.checked ? "bg-violet-500" : "bg-slate-300",
)}
/>
</span>
<span className="ml-2 text-sm text-slate-700 dark:text-slate-300">{props.label}</span>
</label>
</div>
);
}

View File

@ -7,6 +7,8 @@ const variantStyles = {
"bg-violet-300 font-semibold text-slate-900 hover:bg-violet-200 focus:outline-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-violet-300/50 active:bg-violet-500",
secondary:
"bg-slate-800 font-medium text-white hover:bg-slate-700 focus:outline-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white/50 active:text-slate-400",
ghost:
"bg-transparent font-medium text-slate-900 dark:text-slate-400 hover:bg-slate-100 focus:outline-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-300/50 active:bg-slate-200",
};
const sizeStyles = {

View File

@ -2,11 +2,14 @@ import { MobileNavigation } from "@syntax/MobileNavigation";
import { usePageContext } from "vike-react/usePageContext";
import { ThemeProvider } from "@/providers/ThemeProvider";
import { ThemeSelector } from "@syntax/ThemeSelector";
import { Button } from "@/components/syntax/Button";
import { Toggle } from "@/components/common/Toggle";
import { clientOnly } from "vike-react/clientOnly";
import React, { useEffect, useState } from "react";
import { ToastContainer } from "react-toastify";
import { Navigation } from "@syntax/Navigation";
import { Link } from "@/components/common/Link";
import { reload } from "vike/client/router";
import { Hero } from "@syntax/Hero";
import { Logo } from "@syntax/Logo";
import clsx from "clsx";
@ -74,12 +77,107 @@ function Header() {
);
}
function CookieModal() {
const { cookies } = usePageContext();
const [consentCookies, setConsentCookies] = useState(cookies.consent);
const [isSelectionOpen, setIsSelectionOpen] = useState(false);
const [isOpen, setIsOpen] = useState(() => {
return Object.keys(cookies.consent).every((value) => value);
});
if (isSelectionOpen) {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/50">
<div className="flex flex-col gap-2 bg-slate-50 dark:bg-slate-800 rounded-md shadow-xl w-full max-w-sm p-4">
<p className="font-display dark:text-slate-300 font-bold text-lg">Personnalisation des cookies 🍪</p>
<div className="flex flex-col gap-2 w-full items-start">
<Toggle
id="cookies-analytics"
label="Cookies d'analyse (Umami et Google Analytics)"
checked={cookies.consent.analytics}
onChange={(checked) => {
setConsentCookies((prev) => ({ ...prev, analytics: checked }));
reload();
}}
/>
<Toggle
id="cookies-customization"
label="Cookie de personnalisation (thème)"
checked={cookies.consent.customization}
onChange={(checked) => {
setConsentCookies((prev) => ({ ...prev, customization: checked }));
reload();
}}
/>
</div>
</div>
</div>
);
}
return (
<div className="flex flex-col fixed bottom-4 left-4 bg-slate-50 dark:bg-slate-800 z-50 rounded-md shadow-xl w-full max-w-sm overflow-hidden">
<div className="flex flex-col gap-2 p-4">
<p className="font-display dark:text-slate-300">
<span className="text-sm">Coucou c'est nous...</span>
<br />
<span className="font-bold text-lg">les cookies ! 🍪</span>
</p>
<p className="text-slate-700 dark:text-slate-300">
On ne t'embête pas longtemps, on te laisse même le choix <em>(si ça c'est pas la classe 😎)</em>.
</p>
<p className="text-slate-700 dark:text-slate-300">
Si tu veux en savoir plus, tu peux consulter la page{" "}
<Link href="/politique-de-confidentialite" className="font-bold">
Politique de confidentialité
</Link>
.
</p>
</div>
<div className="grid items-center grid-cols-3 justify-between bg-slate-100 dark:bg-slate-700">
<button
className="cursor-pointer px-2 py-1 text-slate-600 dark:text-slate-300"
onClick={async () => {
// TODO
}}
>
Non merci
</button>
<button
className="cursor-pointer px-2 py-1 text-slate-600 dark:text-slate-300"
onClick={() => setIsSelectionOpen(true)}
>
Je choisis
</button>
<button
className="cursor-pointer px-2 py-1 font-bold text-violet-600 dark:text-violet-300"
onClick={async () => {
// TODO
}}
>
Oui, j'ai faim !
</button>
</div>
</div>
);
}
export default function DefaultLayout({ children }: { children: React.ReactNode }) {
const { urlPathname, cookies } = usePageContext();
const isHomePage = urlPathname === "/";
return (
<ThemeProvider defaultTheme={cookies.theme}>
<CookieModal />
<div className="flex w-full flex-col font-sans">
<Header />