2020-03-29 16:05:39 +02:00
|
|
|
import { h } from "preact";
|
|
|
|
import { useEffect, useState, useMemo } from "preact/hooks";
|
|
|
|
|
|
|
|
export enum ThemeModes {
|
|
|
|
AUTO,
|
|
|
|
LIGHT,
|
2020-04-10 20:08:21 +02:00
|
|
|
DARK,
|
2020-03-29 16:05:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2020-04-10 20:08:21 +02:00
|
|
|
colors.onSuccess,
|
2020-03-29 16:05:39 +02:00
|
|
|
]);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div class={isDark ? "dark-theme" : "light-theme"} style={style}>
|
|
|
|
{children}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|