diff --git a/src/components/InsetBox.tsx b/src/components/InsetBox.tsx index d833c35..4b3d8ce 100644 --- a/src/components/InsetBox.tsx +++ b/src/components/InsetBox.tsx @@ -8,7 +8,7 @@ export default function InsetBox(props: InsetBoxProps): JSX.Element { return (
{props.children}
diff --git a/src/components/Panels/MainPanel.tsx b/src/components/Panels/MainPanel.tsx index ccdd48c..1a5dc66 100644 --- a/src/components/Panels/MainPanel.tsx +++ b/src/components/Panels/MainPanel.tsx @@ -25,12 +25,14 @@ export default function MainPanel(props: MainPanelProps): JSX.Element { appLayout.mainPanelReduced && "px-4" }`} > - {appLayout.mainPanelReduced && isDesktop ? ( -
+
+
appLayout.setMainPanelOpen(false)} - className="w-12 cursor-pointer transition-[filter] colorize-black dark:colorize-dark-black hover:colorize-dark dark:hover:colorize-dark-dark" + className={`${ + appLayout.mainPanelReduced && isDesktop ? "w-12" : "w-1/2" + } cursor-pointer transition-[filter] colorize-black dark:colorize-dark-black hover:colorize-dark dark:hover:colorize-dark-dark`} >
- - {router.locale ? ( -
appLayout.setLanguagePanelOpen(true)}> - -
- ) : ( + + {appLayout.mainPanelReduced && isDesktop ? ( "" - )} -
- ) : ( -
-
- -
appLayout.setMainPanelOpen(false)} - className="w-1/2 cursor-pointer transition-[filter] colorize-black dark:colorize-dark-black hover:colorize-dark dark:hover:colorize-dark-dark" - > - -
- - + ) : (

Accord’s Library

+ )} -
+
+ + + {router.locale && ( - - {router.locale && ( - - )} -
+ )}
- )} +
+ + >; - setLanguagePanelOpen: React.Dispatch>; - setMainPanelReduced: React.Dispatch>; - setMainPanelOpen: React.Dispatch>; - setDarkMode: React.Dispatch>; + subPanelOpen: boolean | undefined; + languagePanelOpen: boolean | undefined; + mainPanelReduced: boolean | undefined; + mainPanelOpen: boolean | undefined; + darkMode: boolean | undefined; + setSubPanelOpen: React.Dispatch>; + setLanguagePanelOpen: React.Dispatch< + React.SetStateAction + >; + setMainPanelReduced: React.Dispatch< + React.SetStateAction + >; + setMainPanelOpen: React.Dispatch>; + setDarkMode: React.Dispatch>; + setSelectedThemeMode: React.Dispatch< + React.SetStateAction + >; } const initialState: AppLayoutState = { @@ -25,6 +33,7 @@ const initialState: AppLayoutState = { setMainPanelReduced: () => {}, setMainPanelOpen: () => {}, setDarkMode: () => {}, + setSelectedThemeMode: () => {}, }; const AppContext = React.createContext(initialState); @@ -40,29 +49,23 @@ type Props = { }; export const AppContextProvider = (props: Props) => { - const [subPanelOpen, setSubPanelOpen] = useStateWithLocalStorage( - "subPanelOpen", - initialState.subPanelOpen - ); + const [subPanelOpen, setSubPanelOpen] = useStateWithLocalStorage< + boolean | undefined + >("subPanelOpen", initialState.subPanelOpen); - const [languagePanelOpen, setLanguagePanelOpen] = - useStateWithLocalStorage( - "languagePanelOpen", - initialState.languagePanelOpen - ); + const [languagePanelOpen, setLanguagePanelOpen] = useStateWithLocalStorage< + boolean | undefined + >("languagePanelOpen", initialState.languagePanelOpen); - const [mainPanelReduced, setMainPanelReduced] = - useStateWithLocalStorage( - "mainPanelReduced", - initialState.mainPanelReduced - ); + const [mainPanelReduced, setMainPanelReduced] = useStateWithLocalStorage< + boolean | undefined + >("mainPanelReduced", initialState.mainPanelReduced); - const [mainPanelOpen, setMainPanelOpen] = useStateWithLocalStorage( - "mainPanelOpen", - initialState.mainPanelOpen - ); + const [mainPanelOpen, setMainPanelOpen] = useStateWithLocalStorage< + boolean | undefined + >("mainPanelOpen", initialState.mainPanelOpen); - const [darkMode, setDarkMode] = useStateWithLocalStorage( + const [darkMode, setDarkMode, setSelectedThemeMode] = useDarkMode( "darkMode", initialState.darkMode ); @@ -80,6 +83,7 @@ export const AppContextProvider = (props: Props) => { setMainPanelReduced, setMainPanelOpen, setDarkMode, + setSelectedThemeMode, }} > {props.children} diff --git a/src/hooks/useDarkMode.ts b/src/hooks/useDarkMode.ts new file mode 100644 index 0000000..94a7c19 --- /dev/null +++ b/src/hooks/useDarkMode.ts @@ -0,0 +1,27 @@ +import { useEffect } from "react"; +import { usePrefersDarkMode } from "./useMediaQuery"; +import useStateWithLocalStorage from "./useStateWithLocalStorage"; + +export default function useDarkMode( + key: string, + initialValue: boolean | undefined +): [ + boolean | undefined, + React.Dispatch>, + React.Dispatch> +] { + const [darkMode, setDarkMode] = useStateWithLocalStorage(key, initialValue); + + const prefersDarkMode = usePrefersDarkMode(); + + const [selectedThemeMode, setSelectedThemeMode] = useStateWithLocalStorage( + "selectedThemeMode", + false + ); + + useEffect(() => { + if (selectedThemeMode === false) setDarkMode(prefersDarkMode); + }, [selectedThemeMode, prefersDarkMode, setDarkMode]); + + return [darkMode, setDarkMode, setSelectedThemeMode]; +} diff --git a/src/hooks/useMediaQuery.ts b/src/hooks/useMediaQuery.ts index da82243..bbd8cad 100644 --- a/src/hooks/useMediaQuery.ts +++ b/src/hooks/useMediaQuery.ts @@ -34,15 +34,15 @@ export default function useMediaQuery(query: string): boolean { } export function useMediaThin() { - return useMediaQuery("(max-width: 25rem)"); + return useMediaQuery("(max-width: 25rem)"); } export function useMediaMobile() { - return useMediaQuery("(max-width: 60rem)"); + return useMediaQuery("(max-width: 60rem)"); } export function useMediaDesktop() { - return useMediaQuery("(min-width: 60rem)"); + return useMediaQuery("(min-width: 60rem)"); } export function useMediaCoarse() { @@ -52,3 +52,7 @@ export function useMediaCoarse() { export function useMediaFine() { return useMediaQuery("(pointer: fine)"); } + +export function usePrefersDarkMode() { + return useMediaQuery("(prefers-color-scheme: dark)"); +} diff --git a/src/hooks/useStateWithLocalStorage.ts b/src/hooks/useStateWithLocalStorage.ts index 5832fce..a20f08f 100644 --- a/src/hooks/useStateWithLocalStorage.ts +++ b/src/hooks/useStateWithLocalStorage.ts @@ -3,17 +3,24 @@ import { useEffect, useState } from "react"; export default function useStateWithLocalStorage( key: string, initialValue: T -): [T, React.Dispatch>] { - const [value, setValue] = useState(initialValue); +): [T | undefined, React.Dispatch>] { + const [value, setValue] = useState(undefined); + const [, setFromLocaleStorage] = useState(false); useEffect(() => { try { const item = localStorage.getItem(key); - if (item) setValue(JSON.parse(item) as T); + if (item !== undefined && item !== null) { + setValue(JSON.parse(item) as T); + } else { + setValue(initialValue); + } + setFromLocaleStorage(true); } catch (error) { console.warn(`Error reading localStorage key “${key}”:`, error); + setValue(initialValue); } - }, [setValue, key]); + }, [initialValue, key]); useEffect(() => { localStorage.setItem(key, JSON.stringify(value)); diff --git a/src/pages/library/[slug].tsx b/src/pages/library/[slug].tsx index 9342096..38847ea 100644 --- a/src/pages/library/[slug].tsx +++ b/src/pages/library/[slug].tsx @@ -374,7 +374,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {