ThemePreact/src/Theme.tsx

107 lines
2.7 KiB
TypeScript

import { h } from "preact";
import { useEffect, useState, useMemo } from "preact/hooks";
export enum ThemeModes {
AUTO,
LIGHT,
DARK
}
export interface IThemeProps {
mode: ThemeModes;
children: any;
primary?: string;
onPrimary?: string;
secondary?: string;
onSecondary?: string;
error?: string;
onError?: string;
success?: string;
onSuccess?: string;
}
const modeMediaQuery = window.matchMedia("prefers-color-scheme: dark");
export default function Theme({ mode, children, ...colors }: IThemeProps) {
const [isDark, setIsDark] = useState(
mode === ThemeModes.AUTO
? modeMediaQuery.matches
: mode === ThemeModes.DARK
);
useEffect(() => {
if (mode === ThemeModes.AUTO) {
const listener = (ev: MediaQueryListEvent) => {
setIsDark(ev.matches);
};
modeMediaQuery.addListener(listener);
setIsDark(modeMediaQuery.matches);
return modeMediaQuery.removeListener(listener);
}
}, [mode]);
const style = useMemo(() => {
let style = "";
const getRGB = (color: string) => {
const cv = document.createElement("canvas");
cv.width = 10;
cv.height = 10;
const ctx = cv.getContext("2d");
ctx.rect(0, 0, 1, 1);
ctx.fillStyle = color;
ctx.fill();
const [r, g, b] = ctx.getImageData(0, 0, 10, 10).data;
return [r, g, b];
};
const add = (name: string, value: string) =>
(style += `--${name}: ${value};`);
if (colors.primary) {
add("primary:", colors.primary);
add("primary-rgb", getRGB(colors.primary).join(", "));
}
if (colors.onPrimary) add("on-primary:", colors.onPrimary);
if (colors.secondary) {
add("secondary:", colors.secondary);
add("secondary-rgb", getRGB(colors.secondary).join(", "));
}
if (colors.onSecondary) add("on-secondary:", colors.onSecondary);
if (colors.error) {
add("error:", colors.error);
add("error-rgb", getRGB(colors.error).join(", "));
}
if (colors.onError) add("on-error:", colors.onError);
if (colors.success) {
add("success:", colors.success);
add("success-rgb", getRGB(colors.success).join(", "));
}
if (colors.onSuccess) add("on-success:", colors.onSuccess);
return style;
}, [
colors.primary,
colors.secondary,
colors.error,
colors.success,
colors.onPrimary,
colors.onSecondary,
colors.onError,
colors.onSuccess
]);
return (
<div class={isDark ? "dark-theme" : "light-theme"} style={style}>
{children}
</div>
);
}