tailwind-ui-library/components/elements/ColorSchemeSelector.tsx
2026-03-04 17:35:14 +01:00

102 lines
3.0 KiB
TypeScript

import React, { DetailedHTMLProps, HTMLAttributes } from "react";
import { twMerge } from "tailwind-merge";
import { Moon, Sun } from "lucide-react";
type Props = DetailedHTMLProps<
HTMLAttributes<HTMLDivElement>,
HTMLDivElement
> & {
active?: boolean;
setActive?: React.Dispatch<React.SetStateAction<boolean>>;
iconWrapperProps?: DetailedHTMLProps<
HTMLAttributes<HTMLDivElement>,
HTMLDivElement
>;
defaultScheme?: "light" | "dark";
};
/**
* # Color Scheme Loader
* @className_wrapper twui-color-scheme-selector
*/
export default function ColorSchemeSelector({
active: initialActive,
setActive: externalSetActive,
iconWrapperProps,
defaultScheme,
...props
}: Props) {
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" ? false : true);
} 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;
if (active) {
document.documentElement.className = "dark";
localStorage.setItem("theme", "dark");
} else {
document.documentElement.className = "light";
localStorage.setItem("theme", "light");
}
}, [active]);
return (
<div
{...props}
className={twMerge(
"flex flex-row items-center",
"twui-color-scheme-selector",
props.className
)}
>
<button
title="Color Scheme Selector Button"
onClick={() => setActive(!active)}
className={twMerge(
"cursor-pointer hover:opacity-70 flex items-center justify-center"
)}
>
<div
{...iconWrapperProps}
className={twMerge(
"w-6 h-6 flex items-center justify-center",
iconWrapperProps?.className
)}
>
{active == false && <Sun />}
{active == true && <Moon />}
</div>
</button>
</div>
);
}