added themes

This commit is contained in:
Fedor Katurov 2023-04-29 13:50:41 +06:00
parent 51576b0309
commit 44d903b6e2
17 changed files with 368 additions and 31 deletions

View file

@ -13,5 +13,5 @@
.title {
flex: 1;
font-size: 1.4em;
font-weight: bold;
}

View file

@ -0,0 +1,46 @@
import { ChangeEvent, FC, useCallback } from "react";
import { useTranslation } from "react-i18next";
import { CustomTheme } from "~/modules/theme/types/theme";
import { ColorSettings } from "../../context/SettingsContext";
interface ThemeSelectProps {
themes: CustomTheme[];
value?: CustomTheme;
onChange: (val: ColorSettings) => void;
}
const ThemeSelect: FC<ThemeSelectProps> = ({ themes, value, onChange }) => {
const { t } = useTranslation();
const onChangeTrigger = useCallback(
(event: ChangeEvent<HTMLSelectElement>) => {
const themeTitle = event.target.value;
if (!themeTitle) {
return;
}
const theme = themes.find((it) => it.title === themeTitle);
if (!theme) {
return;
}
onChange(theme.colors);
},
[themes, onChange]
);
return (
<select onChange={onChangeTrigger} value={value?.title}>
{!value && <option selected>{t("Custom theme")}</option>}
{themes.map((theme) => (
<option key={theme.title} value={theme.title}>
{theme.title}
</option>
))}
</select>
);
};
export { ThemeSelect };

View file

@ -1,15 +1,20 @@
import { ChangeEvent, FC, useCallback } from "react";
import { ChangeEvent, FC, useCallback, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { RowGroup } from "~/components/containers/RowGroup";
import { SettingsRow } from "~/components/containers/SettingsRow";
import {
ColorSettings,
SettingsValue,
useSettings,
} from "~/modules/settings/context/SettingsContext";
import { ThemeSelect } from "../../components/ThemeSelect";
import { useBuiltinThemes } from "~/modules/theme/constants/themes";
import { useDefaultTheme } from "~/modules/theme/hooks/useDefaultTheme";
const ColorSettings: FC = () => {
const { update, settings } = useSettings();
const { t } = useTranslation();
const defaultColors = useDefaultTheme();
const setString = useCallback(
(field: keyof SettingsValue) => (event: ChangeEvent<HTMLInputElement>) => {
@ -18,46 +23,88 @@ const ColorSettings: FC = () => {
[update]
);
const themes = useBuiltinThemes();
const currentTheme = useMemo(
() =>
themes.find((it) =>
Object.entries({ ...defaultColors, ...it.colors }).every(
([key, value]) =>
(settings as unknown as Record<string, string>)[key] === value
)
),
[themes, defaultColors, settings]
);
const setThemeColors = useCallback(
(val: ColorSettings) => {
update({ ...defaultColors, ...val });
},
[defaultColors, update]
);
const themeSubtitle = useMemo(() => {
if (!currentTheme) {
return t("User defined theme");
}
if (!currentTheme.url) {
return t("Built-in theme");
}
return `<a href="${currentTheme.url}" target="__blank">${currentTheme.url}</a>`;
}, [currentTheme, t]);
return (
<RowGroup>
<label htmlFor="color">
<label htmlFor="theme">
<SettingsRow title={t("Color theme")} subTitle={themeSubtitle}>
<ThemeSelect
value={currentTheme}
onChange={setThemeColors}
themes={themes}
/>
</SettingsRow>
</label>
<label htmlFor="backgroundColor">
<SettingsRow title={t("Background")}>
<input
type="color"
id="color"
id="backgroundColor"
onChange={setString("backgroundColor")}
value={settings.backgroundColor}
/>
</SettingsRow>
</label>
<label htmlFor="color">
<label htmlFor="textColor">
<SettingsRow title={t("Text")}>
<input
type="color"
id="color"
id="textColor"
onChange={setString("textColor")}
value={settings.textColor}
/>
</SettingsRow>
</label>
<label htmlFor="color">
<label htmlFor="linkColor">
<SettingsRow title={t("Links")}>
<input
type="color"
id="color"
id="linkColor"
onChange={setString("linkColor")}
value={settings.linkColor}
/>
</SettingsRow>
</label>
<label htmlFor="color">
<label htmlFor="codeColor">
<SettingsRow title={t("Inline code")}>
<input
type="color"
id="color"
id="codeColor"
onChange={setString("codeColor")}
value={settings.codeColor}
/>

View file

@ -5,6 +5,11 @@ export interface ColorSettings {
textColor: string;
linkColor: string;
codeColor: string;
h1Color?: string;
h2Color?: string;
h3Color?: string;
h4Color?: string;
h5Color?: string;
}
export type SettingsValue = ColorSettings & {

View file

@ -1,12 +1,7 @@
import { useCallback, useState } from "react";
import { defaultSettings } from "../context/SettingsContext";
import {
Theme,
defaultDarkTheme,
defaultLightTheme,
} from "~/modules/theme/constants/theme";
import { useDetectTheme } from "~/modules/theme/hooks/useDetectTheme";
import { SettingsValue } from "~/modules/settings/context/SettingsContext";
import { useDefaultTheme } from "~/modules/theme/hooks/useDefaultTheme";
import { defaultSettings } from "../context/SettingsContext";
const getLocalStorageSettings = (defaultValue: SettingsValue) => {
try {
@ -20,9 +15,7 @@ const getLocalStorageSettings = (defaultValue: SettingsValue) => {
};
export const usePersistSettings = () => {
const theme = useDetectTheme();
const defaultColors =
theme === Theme.Dark ? defaultDarkTheme : defaultLightTheme;
const defaultColors = useDefaultTheme();
const [settings, setSettings] = useState<SettingsValue>(
getLocalStorageSettings({ ...defaultSettings, ...defaultColors })

View file

@ -10,6 +10,11 @@ export const defaultDarkTheme: ColorSettings = {
textColor: "#eeeeee",
linkColor: "#25bfe6",
codeColor: "#ff3344",
h1Color: "",
h2Color: "",
h3Color: "",
h4Color: "",
h5Color: "",
};
export const defaultLightTheme: ColorSettings = {
@ -17,4 +22,9 @@ export const defaultLightTheme: ColorSettings = {
textColor: "#2e2e2e",
linkColor: "#25bfe6",
codeColor: "#ff3344",
h1Color: "",
h2Color: "",
h3Color: "",
h4Color: "",
h5Color: "",
};

View file

@ -0,0 +1,146 @@
import { useMemo } from "react";
import { CustomTheme } from "../types/theme";
import { defaultDarkTheme, defaultLightTheme } from "./theme";
import { useTranslation } from "react-i18next";
export const useBuiltinThemes = () => {
const { t } = useTranslation();
return useMemo<CustomTheme[]>(
() => [
{
title: t("Dark theme"),
colors: defaultDarkTheme,
},
{
title: t("Light theme"),
colors: defaultLightTheme,
},
{
title: t("Blueberry boar"),
colors: {
backgroundColor: "#201d26",
textColor: "#eeeeee",
linkColor: "#40bfbf",
codeColor: "#d24276",
h1Color: "#f1b8b8",
h2Color: "#e2c79e",
h3Color: "#a6d6c2",
h4Color: "#a4b8cb",
h5Color: "#c0a1da",
},
},
{
title: "Cattpucin Macchiato",
url: "https://github.com/catppuccin/catppuccin",
colors: {
backgroundColor: "#24273a",
textColor: "#cad3f5",
linkColor: "#ee99a0",
codeColor: "#f5a97f",
},
},
{
title: "Cattpucin Mocha",
url: "https://github.com/catppuccin/catppuccin",
colors: {
backgroundColor: "#1e1e2e",
textColor: "#cdd6f4",
linkColor: "#74c7ec",
codeColor: "#f38ba8",
},
},
{
title: "Cattpucin Frappe",
url: "https://github.com/catppuccin/catppuccin",
colors: {
backgroundColor: "#303446",
textColor: "#c6d0f5",
linkColor: "#a6d189",
codeColor: "#ef9f76",
},
},
{
title: "Cattpucin Latte",
url: "https://github.com/catppuccin/catppuccin",
colors: {
backgroundColor: "#eff1f5",
textColor: "#4c4f69",
linkColor: "#1e66f5",
codeColor: "#e64553",
},
},
{
title: "Dracula",
url: "https://draculatheme.com/",
colors: {
backgroundColor: "#282a36",
textColor: "#f8f8f2",
linkColor: "#ff79c6",
codeColor: "#6272a4",
},
},
{
title: "Tokyo Night",
url: "https://github.com/enkia/tokyo-night-vscode-theme",
colors: {
backgroundColor: "#1a1b26",
textColor: "#a9b1d6",
linkColor: "#7aa2f7",
codeColor: "#f7768e",
},
},
{
title: "Tokyo Night Storm",
url: "https://github.com/enkia/tokyo-night-vscode-theme",
colors: {
backgroundColor: "#24283b",
textColor: "#a9b1d6",
linkColor: "#7aa2f7",
codeColor: "#f7768e",
},
},
{
title: "Tokyo Night Light",
url: "https://github.com/enkia/tokyo-night-vscode-theme",
colors: {
backgroundColor: "#d5d6db",
textColor: "#343b58",
linkColor: "#34548a",
codeColor: "#8c4351",
},
},
{
title: "Solarized",
url: "https://ethanschoonover.com/solarized/",
colors: {
backgroundColor: "#073642",
textColor: "#eee8d5",
linkColor: "#8a8a8a",
codeColor: "#b58900",
},
},
{
title: "Horizon Dark",
url: "https://horizontheme.netlify.app/",
colors: {
backgroundColor: "#232530",
textColor: "#FAC29A",
linkColor: "#21BFC2",
codeColor: "#E95379",
},
},
{
title: "Horizon Bright",
url: "https://horizontheme.netlify.app/",
colors: {
backgroundColor: "#FDF0ED",
textColor: "#2E303E",
linkColor: "#1EAEAE",
codeColor: "#E84A72",
},
},
],
[]
);
};

View file

@ -0,0 +1,11 @@
import {
Theme,
defaultDarkTheme,
defaultLightTheme,
} from "~/modules/theme/constants/theme";
import { useDetectTheme } from "~/modules/theme/hooks/useDetectTheme";
export const useDefaultTheme = () => {
const theme = useDetectTheme();
return theme === Theme.Dark ? defaultDarkTheme : defaultLightTheme;
};

View file

@ -13,8 +13,11 @@ export const useThemeColors = (settings: ColorSettings) => {
const code = new Color(settings.codeColor);
console.log(code.hex());
// Backgrounds and text
document.body.style.setProperty("--color-background", background.hex());
document.body.style.setProperty("--color-code", code.hex());
document.body.style.setProperty("--color-code-text", code.hex());
document.body.style.setProperty(
"--color-code-background",
code.fade(isDark ? 0.9 : 0.7).toString()
@ -24,7 +27,16 @@ export const useThemeColors = (settings: ColorSettings) => {
document.body.style.setProperty("--color-border", border.hex());
document.body.style.setProperty(
"--color-pre-background",
isDark ? background.lighten(0.2).hex() : background.darken(0.2).hex()
isDark ? background.lighten(0.2).hex() : background.darken(0.1).hex()
);
// Headings
[...new Array(5)].forEach((_, i) => {
document.body.style.setProperty(
`--color-h${i + 1}`,
(settings as unknown as Record<string, string>)[`h${i + 1}Color`] ||
settings.textColor
);
});
}, [settings]);
};

View file

@ -0,0 +1,7 @@
import { ColorSettings } from "~/modules/settings/context/SettingsContext";
export interface CustomTheme {
title: string;
url?: string;
colors: ColorSettings;
}