import React, { type DetailedHTMLProps, type HTMLAttributes } from "react"; import { twMerge } from "tailwind-merge"; import { Moon, Sun } from "lucide-react"; import type { TWUIButtonSizeType } from "../layout/Button"; const SizeMap: Record< TWUIButtonSizeType, { icon: number; className: string } > = { smaller: { icon: 16, className: "w-4 h-4" }, small: { icon: 20, className: "w-5 h-5" }, normal: { icon: 24, className: "w-6 h-6" }, large: { icon: 28, className: "w-7 h-7" }, larger: { icon: 32, className: "w-8 h-8" }, }; type Props = DetailedHTMLProps< HTMLAttributes, HTMLDivElement > & { active?: boolean; setActive?: React.Dispatch>; iconWrapperProps?: DetailedHTMLProps< HTMLAttributes, HTMLDivElement >; defaultScheme?: "light" | "dark"; /** * Icon size. Matches the twui `Button` size scale. * @default "normal" */ size?: TWUIButtonSizeType; }; /** * # Color Scheme Loader * @className_wrapper twui-color-scheme-selector */ export default function ColorSchemeSelector({ active: initialActive, setActive: externalSetActive, iconWrapperProps, defaultScheme, size = "normal", ...props }: Props) { const { icon: iconSize, className: sizeClassName } = SizeMap[size]; const [active, setActive] = React.useState(initialActive); React.useEffect(() => { const isDocumentDark = document.documentElement.classList.contains("dark"); const isDocumentLight = document.documentElement.classList.contains("light"); if (isDocumentDark) { setActive(true); return; } else if (isDocumentLight) { setActive(false); return; } const existingTheme = localStorage.getItem("theme"); if (existingTheme === "dark") { setActive(true); } else if (existingTheme === "light") { setActive(false); } else if (defaultScheme) { setActive(defaultScheme == "dark"); } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) { setActive(true); } else if (typeof active == "undefined") { setActive(false); } }, []); React.useEffect(() => { if (typeof active == "undefined") return; document.documentElement.classList.remove("dark", "light"); document.documentElement.classList.add(active ? "dark" : "light"); }, [active]); /** * Only an explicit toggle is persisted, so visitors who never choose * keep following their OS preference. */ function toggleScheme() { const next = !active; setActive(next); externalSetActive?.(next); localStorage.setItem("theme", next ? "dark" : "light"); } return ( {active == false && } {active == true && } ); }