Autoselect the system theme by default
This commit is contained in:
parent
60c32fc86d
commit
8842707ae4
|
@ -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>
|
||||
|
|
|
@ -25,12 +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">
|
||||
<div>
|
||||
<div className="grid place-items-center">
|
||||
<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"
|
||||
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"}
|
||||
|
@ -38,58 +40,49 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
|
|||
/>
|
||||
</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>
|
||||
) : (
|
||||
|
||||
{appLayout.mainPanelReduced && isDesktop ? (
|
||||
""
|
||||
)}
|
||||
</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"
|
||||
>
|
||||
<SVG
|
||||
src={"/icons/accords.svg"}
|
||||
alt={"Logo of Accord's Library"}
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
) : (
|
||||
<h2 className="text-3xl">Accord’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);
|
||||
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"}
|
||||
</span>
|
||||
</Button>
|
||||
|
||||
{router.locale && (
|
||||
<Button
|
||||
onClick={() => appLayout.setDarkMode(!appLayout.darkMode)}
|
||||
className="right-0 top-[-1.3em] !py-0.5 !px-2.5"
|
||||
onClick={() => appLayout.setLanguagePanelOpen(true)}
|
||||
className={
|
||||
appLayout.mainPanelReduced && isDesktop
|
||||
? ""
|
||||
: "!py-0.5 !px-2.5 !text-sm"
|
||||
}
|
||||
>
|
||||
<span className="material-icons !text-sm">
|
||||
{appLayout.darkMode ? "dark_mode" : "light_mode"}
|
||||
</span>
|
||||
{router.locale.toUpperCase()}
|
||||
</Button>
|
||||
|
||||
{router.locale && (
|
||||
<Button
|
||||
onClick={() => appLayout.setLanguagePanelOpen(true)}
|
||||
className="right-0 top-[-1.3em] text-sm !py-0.5 !px-2.5"
|
||||
>
|
||||
{router.locale.toUpperCase()}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<HorizontalLine />
|
||||
|
||||
<NavOption
|
||||
url="/library"
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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];
|
||||
}
|
|
@ -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)");
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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}`}>
|
||||
|
|
Loading…
Reference in New Issue