Added media query awareness.

This commit is contained in:
Jay
2025-07-20 15:26:32 -04:00
parent b57af5f518
commit c872e33d93
7 changed files with 123 additions and 18 deletions
+13 -1
View File
@@ -3,6 +3,7 @@ import styles from "./App.module.css";
import ColorPicker from "./components/ColorPicker/ColorPicker"; import ColorPicker from "./components/ColorPicker/ColorPicker";
import ColorValues from "./components/ColorValues/ColorValues"; import ColorValues from "./components/ColorValues/ColorValues";
import { LeftMenu, RightMenu } from "./components/SideMenu"; import { LeftMenu, RightMenu } from "./components/SideMenu";
import { useMediaQuery } from "./providers/hooks";
import clsx from "clsx"; import clsx from "clsx";
// Menu Button Components // Menu Button Components
@@ -133,22 +134,29 @@ function MobileContent({
}: MenuStateProps) { }: MenuStateProps) {
const toggleRightMenu = () => setIsRightMenuOpen(!isRightMenuOpen); const toggleRightMenu = () => setIsRightMenuOpen(!isRightMenuOpen);
const toggleLeftMenu = () => setIsLeftMenuOpen(!isLeftMenuOpen); const toggleLeftMenu = () => setIsLeftMenuOpen(!isLeftMenuOpen);
const { isMobilePortrait, isMobileLandscape } = useMediaQuery();
return ( return (
<main className={styles.mobileContent}> <main className={styles.mobileContent}>
{isMobilePortrait && (
<MobileTopNav <MobileTopNav
onLeftMenuClick={toggleLeftMenu} onLeftMenuClick={toggleLeftMenu}
onRightMenuClick={toggleRightMenu} onRightMenuClick={toggleRightMenu}
isLeftMenuOpen={isLeftMenuOpen} isLeftMenuOpen={isLeftMenuOpen}
isRightMenuOpen={isRightMenuOpen} isRightMenuOpen={isRightMenuOpen}
/> />
)}
{isMobileLandscape && (
<MobileLeftNav onClick={toggleLeftMenu} isOpen={isLeftMenuOpen} /> <MobileLeftNav onClick={toggleLeftMenu} isOpen={isLeftMenuOpen} />
)}
<MobileFirstZone /> <MobileFirstZone />
<MobileSecondZone /> <MobileSecondZone />
{isMobileLandscape && (
<MobileRightNav onClick={toggleRightMenu} isOpen={isRightMenuOpen} /> <MobileRightNav onClick={toggleRightMenu} isOpen={isRightMenuOpen} />
)}
<LeftMenu <LeftMenu
isOpen={isLeftMenuOpen} isOpen={isLeftMenuOpen}
@@ -220,16 +228,20 @@ function DesktopContent() {
function App() { function App() {
const [isRightMenuOpen, setIsRightMenuOpen] = useState(false); const [isRightMenuOpen, setIsRightMenuOpen] = useState(false);
const [isLeftMenuOpen, setIsLeftMenuOpen] = useState(false); const [isLeftMenuOpen, setIsLeftMenuOpen] = useState(false);
const { isDesktop } = useMediaQuery();
return ( return (
<div className={styles.appWrapper} role="application"> <div className={styles.appWrapper} role="application">
{!isDesktop && (
<MobileContent <MobileContent
isLeftMenuOpen={isLeftMenuOpen} isLeftMenuOpen={isLeftMenuOpen}
setIsLeftMenuOpen={setIsLeftMenuOpen} setIsLeftMenuOpen={setIsLeftMenuOpen}
isRightMenuOpen={isRightMenuOpen} isRightMenuOpen={isRightMenuOpen}
setIsRightMenuOpen={setIsRightMenuOpen} setIsRightMenuOpen={setIsRightMenuOpen}
/> />
<DesktopContent /> )}
{isDesktop && <DesktopContent />}
</div> </div>
); );
} }
+3
View File
@@ -2,9 +2,12 @@ import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import "./index.css"; import "./index.css";
import App from "./App.tsx"; import App from "./App.tsx";
import { MediaQueryProvider } from "./providers";
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(
<StrictMode> <StrictMode>
<MediaQueryProvider>
<App /> <App />
</MediaQueryProvider>
</StrictMode>, </StrictMode>,
); );
+77
View File
@@ -0,0 +1,77 @@
import { createContext, useState, useEffect } from "react";
import type { ReactNode } from "react";
enum ViewportMode {
DESKTOP = "desktop",
MOBILE_LANDSCAPE = "mobile-landscape",
MOBILE_PORTRAIT = "mobile-portrait",
}
interface MediaQueryContextType {
viewportMode: ViewportMode;
isDesktop: boolean;
isMobileLandscape: boolean;
isMobilePortrait: boolean;
}
const MediaQueryContext = createContext<MediaQueryContextType | undefined>(
undefined,
);
function MediaQueryProvider({ children }: { children: ReactNode }) {
const [viewportMode, setViewportMode] = useState<ViewportMode>(
ViewportMode.DESKTOP,
);
useEffect(() => {
const desktopQuery = window.matchMedia(
"(min-width: 992px), (min-width: 568px) and (max-width: 991px) and (orientation: portrait)",
);
const mobileLandscapeQuery = window.matchMedia(
"(min-width: 568px) and (max-width: 991px) and (orientation: landscape)",
);
const mobilePortraitQuery = window.matchMedia("(max-width: 567px)");
const updateViewportMode = () => {
if (desktopQuery.matches) {
setViewportMode(ViewportMode.DESKTOP);
} else if (mobileLandscapeQuery.matches) {
setViewportMode(ViewportMode.MOBILE_LANDSCAPE);
} else if (mobilePortraitQuery.matches) {
setViewportMode(ViewportMode.MOBILE_PORTRAIT);
}
};
updateViewportMode();
desktopQuery.addEventListener("change", updateViewportMode);
mobileLandscapeQuery.addEventListener("change", updateViewportMode);
mobilePortraitQuery.addEventListener("change", updateViewportMode);
return () => {
desktopQuery.removeEventListener("change", updateViewportMode);
mobileLandscapeQuery.removeEventListener("change", updateViewportMode);
mobilePortraitQuery.removeEventListener("change", updateViewportMode);
};
}, []);
const isDesktop = viewportMode === ViewportMode.DESKTOP;
const isMobileLandscape = viewportMode === ViewportMode.MOBILE_LANDSCAPE;
const isMobilePortrait = viewportMode === ViewportMode.MOBILE_PORTRAIT;
return (
<MediaQueryContext.Provider
value={{
viewportMode,
isDesktop,
isMobileLandscape,
isMobilePortrait,
}}
>
{children}
</MediaQueryContext.Provider>
);
}
export default MediaQueryProvider;
export { MediaQueryContext };
+12
View File
@@ -0,0 +1,12 @@
import { useContext } from "react";
import { MediaQueryContext } from "./MediaQueryProvider";
function useMediaQuery() {
const context = useContext(MediaQueryContext);
if (context === undefined) {
throw new Error("useMediaQuery must be used within a MediaQueryProvider");
}
return context;
}
export { useMediaQuery };
+3
View File
@@ -0,0 +1,3 @@
import MediaQueryProvider from "./MediaQueryProvider";
export { MediaQueryProvider };
-1
View File
@@ -19,7 +19,6 @@
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true "noUncheckedSideEffectImports": true
}, },
-1
View File
@@ -17,7 +17,6 @@
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true "noUncheckedSideEffectImports": true
}, },