Refactored side menu to a separate component.

This commit is contained in:
Jay
2025-06-17 10:20:28 -04:00
parent c0343f2378
commit 2a35da629b
7 changed files with 320 additions and 156 deletions
+3 -115
View File
@@ -4,53 +4,7 @@
overflow: hidden;
}
.leftMobileMenu {
height: 100vh;
width: 0;
position: fixed;
z-index: 30;
top: 0;
left: 0;
background: white;
overflow-x: hidden;
transition: 0.5s ease-out;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.2);
}
.leftMenuWrapper {
position: absolute;
right: 0;
}
.leftMobileMenu .topNav {
display: flex;
}
.leftMenuWrapper .closeButton {
margin: 10px 10px 10px auto;
}
.rightMobileMenu {
height: 100vh;
width: 0;
position: fixed;
z-index: 20;
top: 0;
right: 0;
background: white;
overflow-x: hidden;
transition: 0.5s ease-out;
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.2);
}
.rightMenuWrapper {
position: absolute;
left: 0;
}
/* Large - Landscape Tablets / Desktops */
/* Medium - Portrait Tablets */
/* Horizontal layout, vertically scrolling picker and palette content */
/* Large */
@media (min-width: 992px),
(min-width: 568px) and (max-width: 991px) and (orientation: portrait) {
.appContainer {
@@ -87,8 +41,7 @@
}
}
/* Medium - Landscape Phones */
/* Horizontal layout, side menu, vertical tabbed picker */
/* Landscape Phone */
@media (min-width: 568px) and (max-width: 991px) and (orientation: landscape) {
.appContainer {
}
@@ -116,38 +69,6 @@
flex: 1;
}
.leftMobileMenu {
}
.leftMobileMenu.open {
width: 80vh;
}
.leftMenuWrapper {
width: 80vh;
}
.leftMobileMenu .topNav {
}
.leftMenuWrapper .closeButton {
}
.rightMobileMenu {
}
.rightMobileMenu.open {
width: 80vh;
}
.rightMenuWrapper {
width: 80vh;
}
.rightMobileMenu .closeButton {
margin: 10px;
}
.tabbedContainer {
max-height: 100vh;
overflow-y: scroll;
@@ -172,8 +93,7 @@
}
}
/* Small - Portrait Phones*/
/* Vertical layout, side menu, horizontal tabbed picker */
/* Portrait Phone */
@media (max-width: 567px) {
.appContainer {
}
@@ -205,38 +125,6 @@
.mobileBetaZone {
}
.leftMobileMenu {
}
.leftMobileMenu.open {
width: 80%;
}
.leftMenuWrapper {
width: 80vw;
}
.leftMobileMenu .topNav {
}
.leftMenuWrapper .closeButton {
}
.rightMobileMenu {
}
.rightMobileMenu.open {
width: 80%;
}
.rightMenuWrapper {
width: 80vw;
}
.rightMobileMenu .closeButton {
margin: 10px;
}
.tabbedContainer {
display: flex;
overflow-x: scroll;
+22 -31
View File
@@ -2,14 +2,15 @@ import { useState } from "react";
import styles from "./App.module.css";
import ColorPicker from "./components/ColorPicker/ColorPicker";
import ColorValues from "./components/ColorValues/ColorValues";
import { LeftMenu, RightMenu } from "./components/SideMenu";
import clsx from "clsx";
function App() {
const [rightMenuOpen, setRightMenuOpen] = useState(false);
const [leftMenuOpen, setLeftMenuOpen] = useState(false);
const [isRightMenuOpen, setIsRightMenuOpen] = useState(false);
const [isLeftMenuOpen, setIsLeftMenuOpen] = useState(false);
const toggleRightMenu = () => setRightMenuOpen(!rightMenuOpen);
const toggleLeftMenu = () => setLeftMenuOpen(!leftMenuOpen);
const toggleRightMenu = () => setIsRightMenuOpen(!isRightMenuOpen);
const toggleLeftMenu = () => setIsLeftMenuOpen(!isLeftMenuOpen);
const RightMenuButton = () => (
<button className={styles.rightMenuButton} onClick={toggleRightMenu}>
@@ -29,9 +30,11 @@ function App() {
<LeftMenuButton />
<RightMenuButton />
</div>
<div className={styles.mobileLeftNav}>
<LeftMenuButton />
</div>
<div className={styles.mobileAlphaZone}>
<div className={styles.tabbedContainer}>
<div className={clsx(styles.tab, styles.colorPickerContainer)}>
@@ -42,42 +45,28 @@ function App() {
</div>
</div>
</div>
<div className={styles.mobileBetaZone}>
<div className={styles.paletteEditorContainer}></div>
</div>
<div className={styles.mobileRightNav}>
<RightMenuButton />
</div>
</div>
<div
className={clsx(styles.leftMobileMenu, {
[styles.open]: leftMenuOpen,
})}
>
<div className={styles.leftMenuWrapper}>
<div className={styles.topNav}>
<button className={styles.closeButton} onClick={toggleLeftMenu}>
×
</button>
</div>
<div className={styles.paletteLibraryContainer}>User Info</div>
</div>
</div>
<LeftMenu
isOpen={isLeftMenuOpen}
onClose={() => setIsLeftMenuOpen(false)}
>
<div>User Info</div>
</LeftMenu>
<div
className={clsx(styles.rightMobileMenu, {
[styles.open]: rightMenuOpen,
})}
>
<div className={styles.rightMenuWrapper}>
<div className={styles.topNav}>
<button className={styles.closeButton} onClick={toggleRightMenu}>
×
</button>
</div>
<RightMenu
isOpen={isRightMenuOpen}
onClose={() => setIsRightMenuOpen(false)}
>
<div className={styles.paletteLibraryContainer}>Palette Library</div>
</div>
</RightMenu>
</div>
<div className={styles.mainContent}>
@@ -85,10 +74,12 @@ function App() {
<div className={styles.colorPickerContainer}>
<ColorPicker />
</div>
<div className={styles.colorValuesContainer}>
<ColorValues />
</div>
</div>
<div className={styles.betaZone}>
<div className={styles.paletteEditorContainer}></div>
<div className={styles.paletteLibraryContainer}></div>
@@ -0,0 +1,88 @@
.sideMenu {
height: 100vh;
width: 0;
position: fixed;
z-index: 20;
top: 0;
background: white;
overflow-x: hidden;
transition: 0.3s ease-out;
}
.sideMenuUnderlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 20;
background: rgba(0, 0, 0, 0);
transition: background-color 0.3s;
pointer-events: none;
}
.sideMenuUnderlay.open {
background: rgba(0, 0, 0, 0.2);
pointer-events: auto;
}
.left.sideMenu {
left: 0;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.2);
}
.right.sideMenu {
right: 0;
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.2);
}
.menuWrapper {
position: absolute;
}
.left.menuWrapper {
right: 0;
}
.right.menuWrapper {
left: 0;
}
.topNav {
width: 100%;
display: flex;
}
.right.sideMenu .closeButton {
margin: 10px;
}
.left.sideMenu .closeButton {
margin: 10px 10px 10px auto;
}
/* Landscape Phone */
@media (min-width: 568px) and (max-width: 991px) and (orientation: landscape) {
.left.sideMenu.open,
.left.menuWrapper {
width: 80vh;
}
.right.sideMenu.open,
.right.menuWrapper {
width: 80vh;
}
}
/* Portrait Phone */
@media (max-width: 567px) {
.left.sideMenu.open,
.left.menuWrapper {
width: 80vw;
}
.right.sideMenu.open,
.right.menuWrapper {
width: 80vw;
}
}
+73
View File
@@ -0,0 +1,73 @@
import type { ReactNode } from "react";
import clsx from "clsx";
import styles from "./SideMenu.module.css";
interface SideMenuProps {
isOpen: boolean;
onClose: () => void;
children: ReactNode;
}
interface BaseMenuProps extends SideMenuProps {
position: "left" | "right";
}
function BaseMenu({ isOpen, onClose, children, position }: BaseMenuProps) {
const isLeftMenu = position === "left";
const handleUnderlayClick = (e: React.MouseEvent<HTMLDivElement>) => {
if (e.target === e.currentTarget) {
onClose();
}
};
return (
<div
className={clsx(styles.sideMenuUnderlay, { [styles.open]: isOpen })}
onClick={handleUnderlayClick}
>
<div
data-cy={`${position}-menu`}
className={clsx(
styles.sideMenu,
isLeftMenu ? styles.left : styles.right,
{ [styles.open]: isOpen },
)}
onClick={(e) => e.stopPropagation()}
>
<div
className={clsx(
styles.menuWrapper,
isLeftMenu ? styles.left : styles.right,
)}
>
<div className={styles.topNav}>
<button
data-cy="close-menu"
className={styles.closeButton}
onClick={onClose}
>
×
</button>
</div>
{children}
</div>
</div>
</div>
);
}
function newSideMenu(position: "left" | "right") {
return function SideMenu({ isOpen, onClose, children }: SideMenuProps) {
return (
<BaseMenu isOpen={isOpen} onClose={onClose} position={position}>
{children}
</BaseMenu>
);
};
}
const LeftMenu = newSideMenu("left");
const RightMenu = newSideMenu("right");
export { LeftMenu, RightMenu };
+2
View File
@@ -0,0 +1,2 @@
import { LeftMenu, RightMenu } from "./SideMenu";
export { LeftMenu, RightMenu };
+6 -7
View File
@@ -1,11 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
createRoot(document.getElementById('root')!).render(
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>,
)
);