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 (
|
return (
|
||||||
<div
|
<div
|
||||||
id={props.id}
|
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}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -25,39 +25,14 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
|
||||||
appLayout.mainPanelReduced && "px-4"
|
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>
|
||||||
<div className="grid place-items-center">
|
<div className="grid place-items-center">
|
||||||
<Link href="/" passHref>
|
<Link href="/" passHref>
|
||||||
<div
|
<div
|
||||||
onClick={() => appLayout.setMainPanelOpen(false)}
|
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
|
<SVG
|
||||||
src={"/icons/accords.svg"}
|
src={"/icons/accords.svg"}
|
||||||
|
@ -66,12 +41,25 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
{appLayout.mainPanelReduced && isDesktop ? (
|
||||||
|
""
|
||||||
|
) : (
|
||||||
<h2 className="text-3xl">Accord’s Library</h2>
|
<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
|
<Button
|
||||||
onClick={() => appLayout.setDarkMode(!appLayout.darkMode)}
|
onClick={() => {
|
||||||
className="right-0 top-[-1.3em] !py-0.5 !px-2.5"
|
appLayout.setDarkMode(!appLayout.darkMode);
|
||||||
|
appLayout.setSelectedThemeMode(true);
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
appLayout.mainPanelReduced && isDesktop ? "" : "!py-0.5 !px-2.5"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<span className="material-icons !text-sm">
|
<span className="material-icons !text-sm">
|
||||||
{appLayout.darkMode ? "dark_mode" : "light_mode"}
|
{appLayout.darkMode ? "dark_mode" : "light_mode"}
|
||||||
|
@ -81,7 +69,11 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
|
||||||
{router.locale && (
|
{router.locale && (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => appLayout.setLanguagePanelOpen(true)}
|
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()}
|
{router.locale.toUpperCase()}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -89,7 +81,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
<HorizontalLine />
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/library"
|
url="/library"
|
||||||
|
|
|
@ -1,17 +1,25 @@
|
||||||
|
import useDarkMode from "hooks/useDarkMode";
|
||||||
import useStateWithLocalStorage from "hooks/useStateWithLocalStorage";
|
import useStateWithLocalStorage from "hooks/useStateWithLocalStorage";
|
||||||
import React, { ReactNode, useContext } from "react";
|
import React, { ReactNode, useContext, useEffect } from "react";
|
||||||
|
|
||||||
export interface AppLayoutState {
|
export interface AppLayoutState {
|
||||||
subPanelOpen: boolean;
|
subPanelOpen: boolean | undefined;
|
||||||
languagePanelOpen: boolean;
|
languagePanelOpen: boolean | undefined;
|
||||||
mainPanelReduced: boolean;
|
mainPanelReduced: boolean | undefined;
|
||||||
mainPanelOpen: boolean;
|
mainPanelOpen: boolean | undefined;
|
||||||
darkMode: boolean;
|
darkMode: boolean | undefined;
|
||||||
setSubPanelOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
setSubPanelOpen: React.Dispatch<React.SetStateAction<boolean | undefined>>;
|
||||||
setLanguagePanelOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
setLanguagePanelOpen: React.Dispatch<
|
||||||
setMainPanelReduced: React.Dispatch<React.SetStateAction<boolean>>;
|
React.SetStateAction<boolean | undefined>
|
||||||
setMainPanelOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
>;
|
||||||
setDarkMode: React.Dispatch<React.SetStateAction<boolean>>;
|
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 = {
|
const initialState: AppLayoutState = {
|
||||||
|
@ -25,6 +33,7 @@ const initialState: AppLayoutState = {
|
||||||
setMainPanelReduced: () => {},
|
setMainPanelReduced: () => {},
|
||||||
setMainPanelOpen: () => {},
|
setMainPanelOpen: () => {},
|
||||||
setDarkMode: () => {},
|
setDarkMode: () => {},
|
||||||
|
setSelectedThemeMode: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const AppContext = React.createContext<AppLayoutState>(initialState);
|
const AppContext = React.createContext<AppLayoutState>(initialState);
|
||||||
|
@ -40,29 +49,23 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AppContextProvider = (props: Props) => {
|
export const AppContextProvider = (props: Props) => {
|
||||||
const [subPanelOpen, setSubPanelOpen] = useStateWithLocalStorage<boolean>(
|
const [subPanelOpen, setSubPanelOpen] = useStateWithLocalStorage<
|
||||||
"subPanelOpen",
|
boolean | undefined
|
||||||
initialState.subPanelOpen
|
>("subPanelOpen", initialState.subPanelOpen);
|
||||||
);
|
|
||||||
|
|
||||||
const [languagePanelOpen, setLanguagePanelOpen] =
|
const [languagePanelOpen, setLanguagePanelOpen] = useStateWithLocalStorage<
|
||||||
useStateWithLocalStorage<boolean>(
|
boolean | undefined
|
||||||
"languagePanelOpen",
|
>("languagePanelOpen", initialState.languagePanelOpen);
|
||||||
initialState.languagePanelOpen
|
|
||||||
);
|
|
||||||
|
|
||||||
const [mainPanelReduced, setMainPanelReduced] =
|
const [mainPanelReduced, setMainPanelReduced] = useStateWithLocalStorage<
|
||||||
useStateWithLocalStorage<boolean>(
|
boolean | undefined
|
||||||
"mainPanelReduced",
|
>("mainPanelReduced", initialState.mainPanelReduced);
|
||||||
initialState.mainPanelReduced
|
|
||||||
);
|
|
||||||
|
|
||||||
const [mainPanelOpen, setMainPanelOpen] = useStateWithLocalStorage<boolean>(
|
const [mainPanelOpen, setMainPanelOpen] = useStateWithLocalStorage<
|
||||||
"mainPanelOpen",
|
boolean | undefined
|
||||||
initialState.mainPanelOpen
|
>("mainPanelOpen", initialState.mainPanelOpen);
|
||||||
);
|
|
||||||
|
|
||||||
const [darkMode, setDarkMode] = useStateWithLocalStorage<boolean>(
|
const [darkMode, setDarkMode, setSelectedThemeMode] = useDarkMode(
|
||||||
"darkMode",
|
"darkMode",
|
||||||
initialState.darkMode
|
initialState.darkMode
|
||||||
);
|
);
|
||||||
|
@ -80,6 +83,7 @@ export const AppContextProvider = (props: Props) => {
|
||||||
setMainPanelReduced,
|
setMainPanelReduced,
|
||||||
setMainPanelOpen,
|
setMainPanelOpen,
|
||||||
setDarkMode,
|
setDarkMode,
|
||||||
|
setSelectedThemeMode,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.children}
|
{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];
|
||||||
|
}
|
|
@ -52,3 +52,7 @@ export function useMediaCoarse() {
|
||||||
export function useMediaFine() {
|
export function useMediaFine() {
|
||||||
return useMediaQuery("(pointer: fine)");
|
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>(
|
export default function useStateWithLocalStorage<T>(
|
||||||
key: string,
|
key: string,
|
||||||
initialValue: T
|
initialValue: T
|
||||||
): [T, React.Dispatch<React.SetStateAction<T>>] {
|
): [T | undefined, React.Dispatch<React.SetStateAction<T | undefined>>] {
|
||||||
const [value, setValue] = useState<T>(initialValue);
|
const [value, setValue] = useState<T | undefined>(undefined);
|
||||||
|
const [, setFromLocaleStorage] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
const item = localStorage.getItem(key);
|
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) {
|
} catch (error) {
|
||||||
console.warn(`Error reading localStorage key “${key}”:`, error);
|
console.warn(`Error reading localStorage key “${key}”:`, error);
|
||||||
|
setValue(initialValue);
|
||||||
}
|
}
|
||||||
}, [setValue, key]);
|
}, [initialValue, key]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(key, JSON.stringify(value));
|
localStorage.setItem(key, JSON.stringify(value));
|
||||||
|
|
|
@ -374,7 +374,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
|
||||||
<div
|
<div
|
||||||
id={content.attributes.slug}
|
id={content.attributes.slug}
|
||||||
key={content.id}
|
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]">
|
<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}`}>
|
<a href={`#${content.attributes.slug}`}>
|
||||||
|
|
Loading…
Reference in New Issue