Autoselect the system theme by default

This commit is contained in:
DrMint 2022-02-23 13:13:05 +01:00
parent 60c32fc86d
commit 8842707ae4
7 changed files with 121 additions and 86 deletions

View File

@ -8,7 +8,7 @@ export default function InsetBox(props: InsetBoxProps): JSX.Element {
return (
<div
id={props.id}
className={`w-full shadow-inner-sm shadow-shade bg-mid dark:bg-dark-mid rounded-xl p-8 ${props.className}`}
className={`w-full shadow-inner-sm shadow-shade dark:shadow-dark-shade bg-mid dark:bg-dark-mid rounded-xl p-8 ${props.className}`}
>
{props.children}
</div>

View File

@ -25,39 +25,14 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
appLayout.mainPanelReduced && "px-4"
}`}
>
{appLayout.mainPanelReduced && isDesktop ? (
<div className="grid place-items-center gap-4">
<Link href="/" passHref>
<div
onClick={() => appLayout.setMainPanelOpen(false)}
className="w-12 cursor-pointer transition-[filter] colorize-black dark:colorize-dark-black hover:colorize-dark dark:hover:colorize-dark-dark"
>
<SVG
src={"/icons/accords.svg"}
alt={"Logo of Accord's Library"}
/>
</div>
</Link>
<Button onClick={() => appLayout.setDarkMode(!appLayout.darkMode)}>
<span className="material-icons !text-sm">
{appLayout.darkMode ? "light_mode" : "dark_mode"}
</span>
</Button>
{router.locale ? (
<div onClick={() => appLayout.setLanguagePanelOpen(true)}>
<Button className="text-xs">{router.locale.toUpperCase()}</Button>
</div>
) : (
""
)}
</div>
) : (
<div>
<div className="grid place-items-center">
<Link href="/" passHref>
<div
onClick={() => 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"
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`}
>
<SVG
src={"/icons/accords.svg"}
@ -66,12 +41,25 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
</div>
</Link>
{appLayout.mainPanelReduced && isDesktop ? (
""
) : (
<h2 className="text-3xl">Accord&rsquo;s Library</h2>
)}
<div className="flex flex-row flex-wrap gap-2">
<div
className={`flex ${
appLayout.mainPanelReduced && isDesktop ? "flex-col" : "flex-row"
} flex-wrap gap-2`}
>
<Button
onClick={() => appLayout.setDarkMode(!appLayout.darkMode)}
className="right-0 top-[-1.3em] !py-0.5 !px-2.5"
onClick={() => {
appLayout.setDarkMode(!appLayout.darkMode);
appLayout.setSelectedThemeMode(true);
}}
className={
appLayout.mainPanelReduced && isDesktop ? "" : "!py-0.5 !px-2.5"
}
>
<span className="material-icons !text-sm">
{appLayout.darkMode ? "dark_mode" : "light_mode"}
@ -81,7 +69,11 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
{router.locale && (
<Button
onClick={() => appLayout.setLanguagePanelOpen(true)}
className="right-0 top-[-1.3em] text-sm !py-0.5 !px-2.5"
className={
appLayout.mainPanelReduced && isDesktop
? ""
: "!py-0.5 !px-2.5 !text-sm"
}
>
{router.locale.toUpperCase()}
</Button>
@ -89,7 +81,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
</div>
</div>
</div>
)}
<HorizontalLine />
<NavOption
url="/library"

View File

@ -1,17 +1,25 @@
import useDarkMode from "hooks/useDarkMode";
import useStateWithLocalStorage from "hooks/useStateWithLocalStorage";
import React, { ReactNode, useContext } from "react";
import React, { ReactNode, useContext, useEffect } from "react";
export interface AppLayoutState {
subPanelOpen: boolean;
languagePanelOpen: boolean;
mainPanelReduced: boolean;
mainPanelOpen: boolean;
darkMode: boolean;
setSubPanelOpen: React.Dispatch<React.SetStateAction<boolean>>;
setLanguagePanelOpen: React.Dispatch<React.SetStateAction<boolean>>;
setMainPanelReduced: React.Dispatch<React.SetStateAction<boolean>>;
setMainPanelOpen: React.Dispatch<React.SetStateAction<boolean>>;
setDarkMode: React.Dispatch<React.SetStateAction<boolean>>;
subPanelOpen: boolean | undefined;
languagePanelOpen: boolean | undefined;
mainPanelReduced: boolean | undefined;
mainPanelOpen: boolean | undefined;
darkMode: boolean | undefined;
setSubPanelOpen: React.Dispatch<React.SetStateAction<boolean | undefined>>;
setLanguagePanelOpen: React.Dispatch<
React.SetStateAction<boolean | undefined>
>;
setMainPanelReduced: React.Dispatch<
React.SetStateAction<boolean | undefined>
>;
setMainPanelOpen: React.Dispatch<React.SetStateAction<boolean | undefined>>;
setDarkMode: React.Dispatch<React.SetStateAction<boolean | undefined>>;
setSelectedThemeMode: React.Dispatch<
React.SetStateAction<boolean | undefined>
>;
}
const initialState: AppLayoutState = {
@ -25,6 +33,7 @@ const initialState: AppLayoutState = {
setMainPanelReduced: () => {},
setMainPanelOpen: () => {},
setDarkMode: () => {},
setSelectedThemeMode: () => {},
};
const AppContext = React.createContext<AppLayoutState>(initialState);
@ -40,29 +49,23 @@ type Props = {
};
export const AppContextProvider = (props: Props) => {
const [subPanelOpen, setSubPanelOpen] = useStateWithLocalStorage<boolean>(
"subPanelOpen",
initialState.subPanelOpen
);
const [subPanelOpen, setSubPanelOpen] = useStateWithLocalStorage<
boolean | undefined
>("subPanelOpen", initialState.subPanelOpen);
const [languagePanelOpen, setLanguagePanelOpen] =
useStateWithLocalStorage<boolean>(
"languagePanelOpen",
initialState.languagePanelOpen
);
const [languagePanelOpen, setLanguagePanelOpen] = useStateWithLocalStorage<
boolean | undefined
>("languagePanelOpen", initialState.languagePanelOpen);
const [mainPanelReduced, setMainPanelReduced] =
useStateWithLocalStorage<boolean>(
"mainPanelReduced",
initialState.mainPanelReduced
);
const [mainPanelReduced, setMainPanelReduced] = useStateWithLocalStorage<
boolean | undefined
>("mainPanelReduced", initialState.mainPanelReduced);
const [mainPanelOpen, setMainPanelOpen] = useStateWithLocalStorage<boolean>(
"mainPanelOpen",
initialState.mainPanelOpen
);
const [mainPanelOpen, setMainPanelOpen] = useStateWithLocalStorage<
boolean | undefined
>("mainPanelOpen", initialState.mainPanelOpen);
const [darkMode, setDarkMode] = useStateWithLocalStorage<boolean>(
const [darkMode, setDarkMode, setSelectedThemeMode] = useDarkMode(
"darkMode",
initialState.darkMode
);
@ -80,6 +83,7 @@ export const AppContextProvider = (props: Props) => {
setMainPanelReduced,
setMainPanelOpen,
setDarkMode,
setSelectedThemeMode,
}}
>
{props.children}

27
src/hooks/useDarkMode.ts Normal file
View File

@ -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.SetStateAction<boolean | undefined>>,
React.Dispatch<React.SetStateAction<boolean | undefined>>
] {
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];
}

View File

@ -52,3 +52,7 @@ export function useMediaCoarse() {
export function useMediaFine() {
return useMediaQuery("(pointer: fine)");
}
export function usePrefersDarkMode() {
return useMediaQuery("(prefers-color-scheme: dark)");
}

View File

@ -3,17 +3,24 @@ import { useEffect, useState } from "react";
export default function useStateWithLocalStorage<T>(
key: string,
initialValue: T
): [T, React.Dispatch<React.SetStateAction<T>>] {
const [value, setValue] = useState<T>(initialValue);
): [T | undefined, React.Dispatch<React.SetStateAction<T | undefined>>] {
const [value, setValue] = useState<T | undefined>(undefined);
const [, setFromLocaleStorage] = useState<boolean>(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));

View File

@ -374,7 +374,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
<div
id={content.attributes.slug}
key={content.id}
className="grid gap-2 px-4 rounded-lg target:bg-mid dark:bg-dark-mid target:shadow-inner-sm target:shadow-shade target:h-auto target:py-3 target:my-2 target:[--displaySubContentMenu:grid] [--displaySubContentMenu:none]"
className="grid gap-2 px-4 rounded-lg target:bg-mid dark:target:bg-dark-mid target:shadow-inner-sm target:shadow-shade dark:target:shadow-dark-shade target:h-auto target:py-3 target:my-2 target:[--displaySubContentMenu:grid] [--displaySubContentMenu:none]"
>
<div className="grid gap-4 place-items-center grid-cols-[auto_auto_1fr_auto_12ch] thin:grid-cols-[auto_auto_1fr_auto]">
<a href={`#${content.attributes.slug}`}>