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} | ||||
|  | ||||
							
								
								
									
										27
									
								
								src/hooks/useDarkMode.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/hooks/useDarkMode.ts
									
									
									
									
									
										Normal 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]; | ||||
| } | ||||
| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrMint
						DrMint