From fbcfce18b30a62cc649a595728ec79b1491abaab Mon Sep 17 00:00:00 2001 From: cophilot Date: Sun, 11 Aug 2024 15:22:10 +0200 Subject: [PATCH] update: Sun 11 Aug 2024 15:22:10 CEST --- src/App.tsx | 5 +- src/api/BoardScorePage/BoardScorePage.tsx | 40 +++------ src/api/utils/StyleUtils.ts | 17 ++-- src/components/GameButton/GameButton.tsx | 2 +- src/components/Logo.tsx | 13 ++- src/index.scss | 1 - src/providers/ThemeProvider.tsx | 87 +++++++++++++++++++ .../CreateCustomView/CreateCustomView.tsx | 2 - src/views/ExpandableTable.tsx | 13 +-- src/views/HomeView.tsx | 19 +++- src/views/PrivacyView.tsx | 4 +- 11 files changed, 151 insertions(+), 52 deletions(-) create mode 100644 src/providers/ThemeProvider.tsx diff --git a/src/App.tsx b/src/App.tsx index 00a03be..0976149 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,13 +1,14 @@ import './App.scss'; import CookieMessage from './components/CookieMessage/CookieMessage'; +import { ThemeProvider } from './providers/ThemeProvider'; import Routes from './Routes'; function App() { return ( - <> + - + ); } diff --git a/src/api/BoardScorePage/BoardScorePage.tsx b/src/api/BoardScorePage/BoardScorePage.tsx index 60c2d04..b341031 100644 --- a/src/api/BoardScorePage/BoardScorePage.tsx +++ b/src/api/BoardScorePage/BoardScorePage.tsx @@ -13,6 +13,7 @@ interface BoardScoreTableProps { children?: any; onCellChange?: (rowIndex: number, playerIndex: number, value: any) => void; logo?: JSX.Element; + isDarkModeEnabled?: boolean; } /** @@ -26,6 +27,7 @@ export default function BoardScorePage({ children, onCellChange, logo, + isDarkModeEnabled = false, }: BoardScoreTableProps): JSX.Element { const navigate = useNavigate(); @@ -49,9 +51,8 @@ export default function BoardScorePage({ }; useEffect(() => { - setInitialAttributes(definition); - //setTimeout(() => setInitialAttributes(definition), 10); - }, [definition]); + setInitialAttributes(definition, isDarkModeEnabled); + }, [definition, isDarkModeEnabled]); const date = new Date().toLocaleDateString(); const showHelpButton = definition.rows.some((row: any) => row.icon); @@ -134,44 +135,27 @@ function print() { }, 500); } -function setInitialAttributes(definition: any) { +function setInitialAttributes(definition: any, darkMode: boolean) { + StyleUtils.setDefaultValues(darkMode); setAttributeIfPresent(definition.title, (title) => { document.title = title + ' - BoardScoreHub'; }); - setAttributeIfPresent( - definition.bgColor, - StyleUtils.setBackGroundColor, - true - ); - setAttributeIfPresent(definition.fontColor, StyleUtils.setFontColor, true); - setAttributeIfPresent( - definition.primaryColor, - StyleUtils.setPrimaryColor, - true - ); + setAttributeIfPresent(definition.bgColor, StyleUtils.setBackGroundColor); + setAttributeIfPresent(definition.fontColor, StyleUtils.setFontColor); + setAttributeIfPresent(definition.primaryColor, StyleUtils.setPrimaryColor); setAttributeIfPresent( definition.secondaryColor, - StyleUtils.setSecondaryColor, - true - ); - setAttributeIfPresent( - definition.fontFamily, - StyleUtils.setFontFamily, - true + StyleUtils.setSecondaryColor ); + setAttributeIfPresent(definition.fontFamily, StyleUtils.setFontFamily); } function setAttributeIfPresent( attribute: unknown, - callback: (arg0?: any) => void, - callWithDefaultAttribute = false + callback: (arg0?: any) => void ) { if (attribute !== undefined && attribute !== null) { callback(attribute); return; } - - if (callWithDefaultAttribute) { - callback(); - } } diff --git a/src/api/utils/StyleUtils.ts b/src/api/utils/StyleUtils.ts index 280e3b3..6392bce 100644 --- a/src/api/utils/StyleUtils.ts +++ b/src/api/utils/StyleUtils.ts @@ -18,11 +18,18 @@ export default class StyleUtils { document.documentElement.style.setProperty('--font-family', fontFamily); } - static setDefaultValues() { - StyleUtils.setBackGroundColor(); - StyleUtils.setFontColor(); - StyleUtils.setPrimaryColor(); - StyleUtils.setSecondaryColor(); + static setDefaultValues(darkMode: boolean = false) { + if (darkMode) { + StyleUtils.setBackGroundColor('#1D2B53'); + StyleUtils.setFontColor('#fff'); + StyleUtils.setPrimaryColor('#FF004D'); + StyleUtils.setSecondaryColor('#FAEF5D'); + } else { + StyleUtils.setBackGroundColor(); + StyleUtils.setFontColor(); + StyleUtils.setPrimaryColor(); + StyleUtils.setSecondaryColor(); + } StyleUtils.setFontFamily(); } } diff --git a/src/components/GameButton/GameButton.tsx b/src/components/GameButton/GameButton.tsx index 9c7b019..f59c7d2 100644 --- a/src/components/GameButton/GameButton.tsx +++ b/src/components/GameButton/GameButton.tsx @@ -24,7 +24,7 @@ function GameButton({ game, asLink = false, link = '' }: GameButtonProps) { className="" href={link} target="_blank" - style={{ color: 'white' }}> + style={{ color: 'var(--bg-color)' }}> {game} diff --git a/src/components/Logo.tsx b/src/components/Logo.tsx index 4ea45ea..37b7584 100644 --- a/src/components/Logo.tsx +++ b/src/components/Logo.tsx @@ -1,10 +1,12 @@ import logoPic from '../assets/logo.png'; import logoPicWhite from '../assets/logo-white.png'; import ColorUtils from '../utils/ColorUtils'; +import { useIsDarkModeEnabled } from '../providers/ThemeProvider'; interface LogoProps { size?: number; bgColor?: string; + detectDarkMode?: boolean; } /** * This is a Logo component @@ -12,7 +14,16 @@ interface LogoProps { * @version 1.0.0 * @created 2024-7-22 */ -function Logo({ size = 200, bgColor = '#fff' }: LogoProps) { +function Logo({ + size = 200, + bgColor = '#fff', + detectDarkMode = false, +}: LogoProps) { + const isDarkModeEnabled = useIsDarkModeEnabled(); + if (detectDarkMode) { + bgColor = isDarkModeEnabled() ? '#000' : '#fff'; + } + let icon = logoPic; if (ColorUtils.isDarkColor(bgColor)) { icon = logoPicWhite; diff --git a/src/index.scss b/src/index.scss index 653450b..a7ca0a5 100644 --- a/src/index.scss +++ b/src/index.scss @@ -86,7 +86,6 @@ p { .selected { background-color: var(--primary-color); - color: var(--font-color); color: var(--bg-color); } diff --git a/src/providers/ThemeProvider.tsx b/src/providers/ThemeProvider.tsx new file mode 100644 index 0000000..2d1583e --- /dev/null +++ b/src/providers/ThemeProvider.tsx @@ -0,0 +1,87 @@ +import React, { ReactNode, useEffect } from 'react'; +import StyleUtils from '../api/utils/StyleUtils'; + +const ThemeContext = React.createContext({ + isDarkModeEnabled: (): boolean => { + return false; + }, + toggleTheme: () => {}, +}); + +/** + * This is the useIsDarkModeEnabled hook + * @returns {function} isDarkModeEnabled + */ +export function useIsDarkModeEnabled() { + const context = React.useContext(ThemeContext); + if (!context) { + throw new Error( + 'useIsDarkModeEnabled must be used within a ThemeProvider' + ); + } + return context.isDarkModeEnabled; +} +/** + * This is the useToggleTheme hook + * @returns {function} toggleTheme + */ +export function useToggleTheme() { + const context = React.useContext(ThemeContext); + if (!context) { + throw new Error('useToggleTheme must be used within a ThemeProvider'); + } + return context.toggleTheme; +} + +interface Props { + children: ReactNode; +} + +/** + * This is the ThemeProvider + * @author cophilot + * @version 1.0.0 + * @created 2024-8-10 + */ +export function ThemeProvider({ children }: Props) { + const localStorageKey = 'bsh-is-dark-mode'; + + const detectInitialTheme = () => { + const storedIsDarkMode = localStorage.getItem(localStorageKey); + if (storedIsDarkMode) { + return storedIsDarkMode === 'true'; + } + return isDarkModePreferred(); + }; + + const [isDarkMode, setIsDarkMode] = React.useState(detectInitialTheme()); + + useEffect(() => { + StyleUtils.setDefaultValues(isDarkMode); + }, []); + + const isDarkModeEnabled = () => { + return isDarkMode; + }; + + const toggleTheme = () => { + const newIsDarkMode = !isDarkMode; + StyleUtils.setDefaultValues(newIsDarkMode); + setIsDarkMode(newIsDarkMode); + localStorage.setItem(localStorageKey, newIsDarkMode.toString()); + }; + + return ( + + {children} + + ); +} + +function isDarkModePreferred() { + return window.matchMedia('(prefers-color-scheme: dark)').matches; +} diff --git a/src/views/CreateCustomView/CreateCustomView.tsx b/src/views/CreateCustomView/CreateCustomView.tsx index 6867708..b868f7e 100644 --- a/src/views/CreateCustomView/CreateCustomView.tsx +++ b/src/views/CreateCustomView/CreateCustomView.tsx @@ -2,7 +2,6 @@ import { useNavigate } from 'react-router-dom'; import By from '../../components/By'; import './CreateCustomView.scss'; import { useEffect } from 'react'; -import StyleUtils from '../../api/utils/StyleUtils'; /** * This is the CreateCustomView @@ -13,7 +12,6 @@ import StyleUtils from '../../api/utils/StyleUtils'; function CreateCustomView() { const navigate = useNavigate(); useEffect(() => { - StyleUtils.setDefaultValues(); document.title = 'BoardScoreHub'; }, []); return ( diff --git a/src/views/ExpandableTable.tsx b/src/views/ExpandableTable.tsx index 55126e9..173aa0c 100644 --- a/src/views/ExpandableTable.tsx +++ b/src/views/ExpandableTable.tsx @@ -3,9 +3,11 @@ import BoardScorePage from '../api/BoardScorePage/BoardScorePage'; import { WinMode } from '../api/types/WinMode'; import By from '../components/By'; import Logo from '../components/Logo'; +import { useIsDarkModeEnabled } from '../providers/ThemeProvider'; export default function ExpandableTable() { - const rowsLSKey = 'expandable table-rows'; + const rowsLSKey = 'expandable-table-rows'; + const isDarkModeEnabled = useIsDarkModeEnabled(); const [rows, setRows] = useState([ { @@ -26,9 +28,9 @@ export default function ExpandableTable() { playerSizes: [1, 2, 3, 4, 5, 6], winMode: WinMode.NONE, rows: rows, - stripColor: '#d8d8d8', + stripColor: isDarkModeEnabled() ? '#15203f' : '#d8d8d8', }; - }, [rows]); + }, [isDarkModeEnabled, rows]); const onCellChange = (row: number) => { if (row < rows.length - 1) { @@ -43,13 +45,14 @@ export default function ExpandableTable() { setRows(newRows); localStorage.setItem(rowsLSKey, JSON.stringify(newRows)); }; - const logo = ; + const logo = ; return ( + logo={logo} + isDarkModeEnabled={isDarkModeEnabled()}> ); diff --git a/src/views/HomeView.tsx b/src/views/HomeView.tsx index 1b3bd54..df47448 100644 --- a/src/views/HomeView.tsx +++ b/src/views/HomeView.tsx @@ -1,5 +1,4 @@ import { useEffect } from 'react'; -import StyleUtils from '../api/utils/StyleUtils'; import { useNavigate } from 'react-router-dom'; import By from '../components/By'; import { getSortedGameNames } from '../allGames'; @@ -8,20 +7,32 @@ import DevMessage from '../components/DevMessage'; import GameButton from '../components/GameButton/GameButton'; import FavoriteGameSection from '../components/FavoriteGameSection/FavoriteGameSection'; import RouteButton from '../components/RouteButton/RouteButton'; +import { + useIsDarkModeEnabled, + useToggleTheme, +} from '../providers/ThemeProvider'; export default function HomeView() { const navigate = useNavigate(); + const toggleTheme = useToggleTheme(); + const isDarkModeEnabled = useIsDarkModeEnabled(); useEffect(() => { - StyleUtils.setDefaultValues(); document.title = 'BoardScoreHub'; }, []); const games = getSortedGameNames(); - return (
- + +

General