From 70f6dcd4956bb896f57baed2c61f63c5762e3c1a Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Wed, 26 Apr 2023 22:26:04 +0600 Subject: [PATCH] added theme detection --- package.json | 2 + src/i18n/index.ts | 1 - .../hooks/useGridLayoutPersistance.ts | 5 -- .../settings/context/SettingsContext.ts | 7 ++- .../providers/SettingsProvider/index.tsx | 15 +++++- src/modules/theme/constants/theme.ts | 15 ++++++ .../theme/containers/ThemeProvider/index.tsx | 11 +---- src/modules/theme/hooks/useDetectTheme.ts | 27 ++++++++++ src/modules/theme/hooks/useThemeColors.ts | 19 +++++++ tsconfig.json | 1 + yarn.lock | 49 ++++++++++++++++++- 11 files changed, 133 insertions(+), 19 deletions(-) create mode 100644 src/modules/theme/hooks/useDetectTheme.ts create mode 100644 src/modules/theme/hooks/useThemeColors.ts diff --git a/package.json b/package.json index e85faea..feec6fd 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@remirror/react": "^2.0.27", "@remirror/react-editors": "^1.0.27", "classnames": "^2.3.2", + "color": "^4.2.3", "dockview": "^1.7.1", "formik": "^2.2.9", "i18next": "^22.4.15", @@ -42,6 +43,7 @@ }, "devDependencies": { "@types/classnames": "^2.3.1", + "@types/color": "^3.0.3", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "@types/uuid": "^9.0.1", diff --git a/src/i18n/index.ts b/src/i18n/index.ts index c0f2a60..8303035 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -9,7 +9,6 @@ i18n .use(LanguageDetector) .init({ fallbackLng: "en", - debug: true, interpolation: { escapeValue: false, }, diff --git a/src/modules/layout/components/GridLayout/hooks/useGridLayoutPersistance.ts b/src/modules/layout/components/GridLayout/hooks/useGridLayoutPersistance.ts index b4e5f3d..e478f3f 100644 --- a/src/modules/layout/components/GridLayout/hooks/useGridLayoutPersistance.ts +++ b/src/modules/layout/components/GridLayout/hooks/useGridLayoutPersistance.ts @@ -51,13 +51,8 @@ export const useGridLayoutPersistance = () => { persistLayout(); }); - const onPanelChange = api.current.onDidActivePanelChange((event) => { - console.log(event); - }); - return () => { onLayoutChange.dispose(); - onPanelChange.dispose(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [persistLayout, api.current]); diff --git a/src/modules/settings/context/SettingsContext.ts b/src/modules/settings/context/SettingsContext.ts index c7a1555..5719c58 100644 --- a/src/modules/settings/context/SettingsContext.ts +++ b/src/modules/settings/context/SettingsContext.ts @@ -1,10 +1,13 @@ import { createContext, useContext } from "react"; -export type SettingsValue = { - richEditorEnabled: boolean; +export interface ColorSettings { backgroundColor: string; textColor: string; linkColor: string; +} + +export type SettingsValue = ColorSettings & { + richEditorEnabled: boolean; }; export const defaultSettings: SettingsValue = { diff --git a/src/modules/settings/providers/SettingsProvider/index.tsx b/src/modules/settings/providers/SettingsProvider/index.tsx index db4a560..b1a2e43 100644 --- a/src/modules/settings/providers/SettingsProvider/index.tsx +++ b/src/modules/settings/providers/SettingsProvider/index.tsx @@ -7,9 +7,22 @@ import { defaultSettings, } from "../../context/SettingsContext"; import { ModalPage } from "~/modules/modal/components/ModalPage"; +import { useDetectTheme } from "~/modules/theme/hooks/useDetectTheme"; +import { + Theme, + defaultDarkTheme, + defaultLightTheme, +} from "~/modules/theme/constants/theme"; const SettingsProvider: FC = ({ children }) => { - const [settings, setSettings] = useState(defaultSettings); + const theme = useDetectTheme(); + const defaultColors = + theme === Theme.Dark ? defaultDarkTheme : defaultLightTheme; + + const [settings, setSettings] = useState({ + ...defaultSettings, + ...defaultColors, + }); const [settingsModalVisible, setSettingsModalVisible] = useState(false); const show = useCallback(() => setSettingsModalVisible(true), []); diff --git a/src/modules/theme/constants/theme.ts b/src/modules/theme/constants/theme.ts index 9da1238..bff3dc3 100644 --- a/src/modules/theme/constants/theme.ts +++ b/src/modules/theme/constants/theme.ts @@ -1,3 +1,18 @@ +import { ColorSettings } from "~/modules/settings/context/SettingsContext"; + export enum Theme { Dark = "dark", + Light = "light", } + +export const defaultDarkTheme: ColorSettings = { + backgroundColor: "#2e2e2e", + textColor: "#eeeeee", + linkColor: "#25bfe6", +}; + +export const defaultLightTheme: ColorSettings = { + backgroundColor: "#eeeeee", + textColor: "#2e2e2e", + linkColor: "#25bfe6", +}; diff --git a/src/modules/theme/containers/ThemeProvider/index.tsx b/src/modules/theme/containers/ThemeProvider/index.tsx index 0184bd0..8f012eb 100644 --- a/src/modules/theme/containers/ThemeProvider/index.tsx +++ b/src/modules/theme/containers/ThemeProvider/index.tsx @@ -1,6 +1,7 @@ import { FC, PropsWithChildren, useEffect, useState } from "react"; import { ThemeContext, defaultTheme } from "../../context/ThemeContext"; import { useSettings } from "~/modules/settings/context/SettingsContext"; +import { useThemeColors } from "../../hooks/useThemeColors"; type ThemeProviderProps = PropsWithChildren; @@ -18,15 +19,7 @@ const ThemeProvider: FC = ({ children }) => { return () => document.body.classList.remove(`theme-${theme.theme}`); }, [theme.theme]); - useEffect(() => { - document.body.style.setProperty( - "--color-background", - settings.backgroundColor - ); - - document.body.style.setProperty("--color-text", settings.textColor); - document.body.style.setProperty("--color-link", settings.linkColor); - }, [settings]); + useThemeColors(settings); return ( {children} diff --git a/src/modules/theme/hooks/useDetectTheme.ts b/src/modules/theme/hooks/useDetectTheme.ts new file mode 100644 index 0000000..f1e3f38 --- /dev/null +++ b/src/modules/theme/hooks/useDetectTheme.ts @@ -0,0 +1,27 @@ +import { useEffect, useState } from "react"; +import { Theme } from "../constants/theme"; + +const isDark = () => + window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches; + +export const useDetectTheme = () => { + const [theme, setTheme] = useState(isDark() ? Theme.Dark : Theme.Light); + + useEffect(() => { + const listener = (event: MediaQueryListEvent) => { + setTheme(event.matches ? Theme.Dark : Theme.Light); + }; + + window + .matchMedia("(prefers-color-scheme: dark)") + .addEventListener("change", listener); + + return () => + window + .matchMedia("(prefers-color-scheme: dark)") + .removeEventListener("change", listener); + }, []); + + return theme; +}; diff --git a/src/modules/theme/hooks/useThemeColors.ts b/src/modules/theme/hooks/useThemeColors.ts new file mode 100644 index 0000000..de005d3 --- /dev/null +++ b/src/modules/theme/hooks/useThemeColors.ts @@ -0,0 +1,19 @@ +import Color from "color"; +import { useEffect } from "react"; +import { ColorSettings } from "~/modules/settings/context/SettingsContext"; + +export const useThemeColors = (settings: ColorSettings) => { + useEffect(() => { + const background = new Color(settings.backgroundColor); + const isDark = background.isDark(); + + const border = isDark + ? background.mix(Color("white"), 0.1) + : background.mix(Color("black"), 0.1); + + document.body.style.setProperty("--color-background", background.hex()); + document.body.style.setProperty("--color-text", settings.textColor); + document.body.style.setProperty("--color-link", settings.linkColor); + document.body.style.setProperty("--color-border", border.hex()); + }, [settings]); +}; diff --git a/tsconfig.json b/tsconfig.json index f01ec39..b66c74e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", + "esModuleInterop": true, /* Linting */ "strict": true, diff --git a/yarn.lock b/yarn.lock index b9039ea..57eb2bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2016,6 +2016,25 @@ dependencies: "@types/tern" "*" +"@types/color-convert@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-2.0.0.tgz#8f5ee6b9e863dcbee5703f5a517ffb13d3ea4e22" + integrity sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ== + dependencies: + "@types/color-name" "*" + +"@types/color-name@*": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +"@types/color@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/color/-/color-3.0.3.tgz#e6d8d72b7aaef4bb9fe80847c26c7c786191016d" + integrity sha512-X//qzJ3d3Zj82J9sC/C18ZY5f43utPbAJ6PhYt/M7uG6etcF6MRpKdN880KBy43B0BMzSfeT96MzrsNjFI3GbA== + dependencies: + "@types/color-convert" "*" + "@types/debug@^4.0.0": version "4.1.7" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" @@ -2887,16 +2906,32 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@~1.1.4: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-string@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + color2k@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/color2k/-/color2k-2.0.2.tgz#ac2b4aea11c822a6bcb70c768b5a289f4fffcebb" integrity sha512-kJhwH5nAwb34tmyuqq/lgjEKzlFXn1U99NlnB6Ws4qVaERcRUYeYP1cBw6BJ4vxaWStAUEef4WMr7WjOCnBt8w== +color@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" + integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== + dependencies: + color-convert "^2.0.1" + color-string "^1.9.0" + columnify@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" @@ -4468,6 +4503,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -6854,6 +6894,13 @@ signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"