From 282e51c2a8bde88f41de38d7c0ff061ca38ec7cb Mon Sep 17 00:00:00 2001 From: oat9002 Date: Fri, 17 Apr 2026 16:06:34 +0700 Subject: [PATCH 01/10] Enhance Layout and Menu components with new animations and state management --- src/components/Layout.tsx | 19 ++- src/components/menu/Menu.tsx | 250 ++++++++++++++++++++++++++--------- 2 files changed, 202 insertions(+), 67 deletions(-) diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index 97b3c928..522faa91 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Outlet, useNavigate, useOutletContext } from "react-router-dom"; +import { Outlet, useLocation, useNavigate, useOutletContext } from "react-router-dom"; import styled from "styled-components"; import pj from "../../package.json"; import { accountsState, categoriesState, totalAccountsBalanceState } from "../common/shareState"; @@ -13,6 +13,7 @@ import Menu from "./menu/Menu"; import { Box, Container, Heading } from "@radix-ui/themes"; import { color } from "../common/constants"; import { useAtom, useAtomValue } from "jotai"; +import { AnimatePresence } from "framer-motion"; const Header = styled.div` padding-top: 12px; @@ -43,6 +44,7 @@ const Layout: React.FC = () => { const [isLoading, setIsLoading] = React.useState(true); const [isSignIn, redirectToSignIn, isSignInLoading] = useSignIn(); const navigate = useNavigate(); + const location = useLocation(); const [backToHomeParam, setBackToHomeParam] = React.useState(null); React.useEffect(() => { @@ -87,9 +89,20 @@ const Layout: React.FC = () => { - + }> - + + + diff --git a/src/components/menu/Menu.tsx b/src/components/menu/Menu.tsx index a287b574..9b51fa74 100644 --- a/src/components/menu/Menu.tsx +++ b/src/components/menu/Menu.tsx @@ -1,12 +1,18 @@ -import { ReactElement } from "react"; -import { Link } from "react-router-dom"; +import React from "react"; +import { + GearIcon, + HomeIcon, + PersonIcon, + ExitIcon, + CardStackIcon, + ReaderIcon, +} from "@radix-ui/react-icons"; +import { Link, useLocation, useNavigate } from "react-router-dom"; import styled from "styled-components"; import Account from "../../service/model/Account"; import BalanceWithCurrency from "../bases/BalanceWithCurrency"; -import Drawer from "../bases/Drawer"; import Switch from "../bases/Switch"; -import { Box, Container, Flex, Grid, Separator, Text } from "@radix-ui/themes"; -import React from "react"; +import { Box, Card, Flex, Grid, Separator, Text } from "@radix-ui/themes"; import { useAtom } from "jotai"; import { isHideBalanceOnMenuState } from "../../common/shareState"; @@ -17,22 +23,106 @@ interface MenuProps { version: string; } +const BottomMenuWrapper = styled.div` + position: fixed; + left: 50%; + bottom: 10px; + transform: translateX(-50%); + width: min(420px, calc(100vw - 24px)); + z-index: 1000; + padding-bottom: env(safe-area-inset-bottom, 0px); + + @media (max-width: 768px) { + width: calc(100vw - 16px); + transform: translateX(-50%); + } +`; + +const BottomMenuBar = styled.div` + border-radius: 18px 18px 0 0; + background: var(--gray-2); + border: none; + box-shadow: none; +`; + +const TabButton = styled.button<{ $active: boolean }>` + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + background: transparent; + border: none; + font: inherit; + color: inherit; + cursor: pointer; + padding: 0; +`; + +const SheetBackdrop = styled.div<{ $open: boolean }>` + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 1); + opacity: ${(props) => (props.$open ? 1 : 0)}; + pointer-events: ${(props) => (props.$open ? "auto" : "none")}; + transition: opacity 0.2s ease; + z-index: 998; +`; + +const Sheet = styled.div<{ $open: boolean }>` + position: fixed; + left: 50%; + bottom: 90px; + transform: translateX(-50%) translateY(${(props) => (props.$open ? "0" : "18px")}); + width: min(420px, calc(100vw - 24px)); + max-height: 65vh; + overflow-y: auto; + opacity: ${(props) => (props.$open ? 1 : 0)}; + pointer-events: ${(props) => (props.$open ? "auto" : "none")}; + transition: opacity 0.2s ease, transform 0.2s ease; + z-index: 999; + border-radius: 18px; + background: var(--gray-1); + border: none; + box-shadow: none; + padding: 16px; + + @media (max-width: 768px) { + left: 0; + bottom: 74px; + width: 100vw; + border-radius: 16px 16px 0 0; + transform: translateY(${(props) => (props.$open ? "0" : "18px")}); + } +`; + const Version = styled.div` - position: absolute; - bottom: 16px; - right: 0; - font-size: 0.5em; - margin-right: 5px; + font-size: 12px; + opacity: 0.7; `; -const SignOut = styled.a` + +const ActionText = styled.span` cursor: pointer; - color: inherit; `; -const hideBalanceSwitchId = "hideBalanceSwitch"; -const preventDrawerCloseIdList = ["rt-SwitchButton", "rt-SwitchThumb", hideBalanceSwitchId]; + +type ActiveTab = "none" | "home" | "account" | "settings"; const Menu: React.FC = (props) => { const [isHideBalance, setIsHideBalance] = useAtom(isHideBalanceOnMenuState); + const [activeTab, setActiveTab] = React.useState("none"); + const navigate = useNavigate(); + const location = useLocation(); + + const isSheetOpen = activeTab === "account" || activeTab === "settings"; + + React.useEffect(() => { + if (location.pathname === "/") { + setActiveTab("home"); + return; + } + + setActiveTab("none"); + }, [location.pathname]); + const onHideBalanceChangeHandler = () => { setIsHideBalance((prevState) => { const nextState = !prevState; @@ -40,49 +130,30 @@ const Menu: React.FC = (props) => { return nextState; }); }; - const getMenuTitle = (title: string) => ( - - - {title.toUpperCase()} - - - ); - const getMenuContent = (title: string, link: string) => ( - - - - - - - {title} - - - - ); - const getMenuContentWithChildren = (element: ReactElement) => ( - - - - - {element} - - ); + + const navigateToHome = () => { + setActiveTab("home"); + navigate("/"); + }; + + const closeSheet = () => { + setActiveTab(location.pathname === "/" ? "home" : "none"); + }; return ( - - - - - - ACCOUNTS - - + <> + + + + ACCOUNTS + + {props.accounts.map((x) => ( {x.name} - + = (props) => { = (props) => { isRtl /> - {getMenuTitle("page")} - {getMenuContent("Home", "/")} - {getMenuTitle("Setting")} - {getMenuContent("Account", "/account/setting")} - {getMenuContent("Category", "/category/setting")} - {getMenuContent("Page", "/page/setting")} - {getMenuTitle("Misc")} - {getMenuContentWithChildren( - Sign out - )} - - v{props.version} - + + + + SETTINGS + + + + + + Account + + + + + + + Category + + + + + + + Page + + + + + + Sign out + + v{props.version} + + + + + + + + + + Home + + + setActiveTab("account")}> + + + Account + + + setActiveTab("settings")}> + + + Settings + + + + + + ); }; From 19440dff931cd8f91f8e8b2b739a99aa4503f539 Mon Sep 17 00:00:00 2001 From: oat9002 Date: Fri, 17 Apr 2026 16:13:02 +0700 Subject: [PATCH 02/10] style: update BottomMenuBar border-radius and adjust SheetBackdrop background opacity --- src/components/menu/Menu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/menu/Menu.tsx b/src/components/menu/Menu.tsx index 9b51fa74..1923307d 100644 --- a/src/components/menu/Menu.tsx +++ b/src/components/menu/Menu.tsx @@ -39,7 +39,7 @@ const BottomMenuWrapper = styled.div` `; const BottomMenuBar = styled.div` - border-radius: 18px 18px 0 0; + border-radius: 18px; background: var(--gray-2); border: none; box-shadow: none; @@ -61,7 +61,7 @@ const TabButton = styled.button<{ $active: boolean }>` const SheetBackdrop = styled.div<{ $open: boolean }>` position: fixed; inset: 0; - background: rgba(0, 0, 0, 1); + background: rgba(0, 0, 0, 0.45); opacity: ${(props) => (props.$open ? 1 : 0)}; pointer-events: ${(props) => (props.$open ? "auto" : "none")}; transition: opacity 0.2s ease; From d23154d785859122f8c6e58a30a3250dd9e938e4 Mon Sep 17 00:00:00 2001 From: oat9002 Date: Sun, 19 Apr 2026 20:44:43 +0700 Subject: [PATCH 03/10] =?UTF-8?q?=E2=9C=A8=20refactor=20menu=20to=20use=20?= =?UTF-8?q?Drawer=20component=20for=20account=20and=20settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/bases/Drawer.tsx | 133 ++++++++++++++++++++++++-------- src/components/menu/Menu.tsx | 63 +++++---------- 2 files changed, 123 insertions(+), 73 deletions(-) diff --git a/src/components/bases/Drawer.tsx b/src/components/bases/Drawer.tsx index 7fbe7413..9738bda6 100644 --- a/src/components/bases/Drawer.tsx +++ b/src/components/bases/Drawer.tsx @@ -1,5 +1,5 @@ import React from "react"; -import styled, { keyframes } from "styled-components"; +import styled, { keyframes, css } from "styled-components"; import { pageSettingState } from "../../common/shareState"; import { DotsVerticalIcon } from "@radix-ui/react-icons"; import { useAtomValue } from "jotai"; @@ -8,11 +8,14 @@ interface StyledProps { $isMenuOnRightSide: boolean; $isShow: boolean; $isDarkTheme: boolean; + $position: "side" | "bottom"; + $backdropOpacity?: number; } interface DrawStyledProps { $isMenuOnRightSide: boolean; $isLightMenu: boolean; + $position: "side" | "bottom"; } const animationDuration = 0.5; @@ -35,6 +38,26 @@ const slideOut = keyframes` const slideOutRtl = keyframes` 100% {right: -100%;} `; +const slideInBottom = keyframes` + 0% { + opacity: 0; + transform: translateX(-50%) translateY(40px); + } + 100% { + opacity: 1; + transform: translateX(-50%) translateY(0); + } +`; +const slideOutBottom = keyframes` + 0% { + opacity: 1; + transform: translateX(-50%) translateY(0); + } + 100% { + opacity: 0; + transform: translateX(-50%) translateY(40px); + } +`; const fadeOut = keyframes` 100% {background-color: rgba(0, 0, 0, 0);} `; @@ -42,23 +65,47 @@ const fadeOut = keyframes` const Panel = styled.div` z-index: ${zIndex}; position: fixed; - top: 0; - left: ${(props) => (props.$isMenuOnRightSide ? "initial" : 0)}; - right: ${(props) => (props.$isMenuOnRightSide ? 0 : "initial")}; - border-radius: ${(props) => (props.$isMenuOnRightSide ? "8px 0 0 8px" : "0 8px 8px 0")}; - height: 100%; - width: 30%; - min-width: 350px; - background-color: ${(props) => (props.$isDarkTheme ? "#363636" : "white")}; - animation: ${(props) => - props.$isShow - ? props.$isMenuOnRightSide - ? slideInRtl - : slideIn - : props.$isMenuOnRightSide - ? slideOutRtl - : slideOut} - ${animationDuration}s forwards; + background-color: ${(props) => + props.$position === "bottom" ? "var(--gray-1)" : props.$isDarkTheme ? "#363636" : "white"}; + + ${(props) => { + if (props.$position === "side") { + return css` + top: 0; + left: ${props.$isMenuOnRightSide ? "initial" : 0}; + right: ${props.$isMenuOnRightSide ? 0 : "initial"}; + border-radius: ${props.$isMenuOnRightSide ? "8px 0 0 8px" : "0 8px 8px 0"}; + height: 100%; + width: 30%; + min-width: 350px; + animation: ${props.$isShow + ? props.$isMenuOnRightSide + ? slideInRtl + : slideIn + : props.$isMenuOnRightSide + ? slideOutRtl + : slideOut} + ${animationDuration}s forwards; + `; + } else { + return css` + bottom: 90px; + left: 50%; + width: min(420px, calc(100vw - 24px)); + max-height: 65vh; + overflow-y: auto; + border-radius: 18px 18px 0 0; + padding: 16px; + animation: ${props.$isShow ? slideInBottom : slideOutBottom} ${animationDuration}s + forwards; + @media (max-width: 768px) { + bottom: 74px; + width: 100vw; + border-radius: 16px 16px 0 0; + } + `; + } + }} `; const Background = styled.div` @@ -69,7 +116,7 @@ const Background = styled.div` right: ${(props) => (props.$isMenuOnRightSide ? 0 : "initial")}; height: 100%; width: 100%; - background-color: rgba(0, 0, 0, 0.8); + background-color: rgba(0, 0, 0, ${(props) => props.$backdropOpacity || 0.8}); animation: ${(props) => (props.$isShow ? fadeIn : fadeOut)} ${animationDuration}s forwards; `; @@ -84,6 +131,7 @@ const Draw = styled.div` border-radius: ${(props) => (props.$isMenuOnRightSide ? "8px 0 0 8px" : "0 8px 8px 0")}; z-index: 10; right: ${(props) => (props.$isMenuOnRightSide ? "0" : "initial")}; + display: ${(props) => (props.$position === "side" ? "block" : "none")}; `; const Icon = styled(DotsVerticalIcon)` margin-top: 38px; @@ -93,16 +141,28 @@ const Icon = styled(DotsVerticalIcon)` interface OwnProps { preventCloseIdOrClassList?: string[]; + position?: "side" | "bottom"; + open?: boolean; + onOpenChange?: (open: boolean) => void; + backdropOpacity?: number; } type DrawerProps = React.PropsWithChildren; const Drawer: React.FC = (props) => { const { isMenuOnRightSide, isLightMenu, isDarkTheme } = useAtomValue(pageSettingState); - const [isShowPanel, setIsShowPanel] = React.useState(false); + const position = props.position || "side"; + const isControlled = props.open !== undefined && props.onOpenChange !== undefined; + const [internalIsShow, setInternalIsShow] = React.useState(false); const [isAnimationUnmount, setIsAnimationUnmount] = React.useState(false); + const isShowPanel = isControlled ? props.open : internalIsShow; + const btnClickHandler = () => { - setIsShowPanel(true); + if (isControlled) { + props.onOpenChange(true); + } else { + setInternalIsShow(true); + } }; const closePanelHandler = (event: React.MouseEvent) => { const target = event.target as HTMLElement; @@ -117,29 +177,40 @@ const Drawer: React.FC = (props) => { setIsAnimationUnmount(true); setTimeout(() => { - setIsShowPanel(false); + if (isControlled) { + props.onOpenChange(false); + } else { + setInternalIsShow(false); + } setIsAnimationUnmount(false); }, animationDuration * 1000); }; return ( <> - - - - {isShowPanel ? ( + {position === "side" && ( + + + + )} + {isShowPanel || isAnimationUnmount ? ( + $isDarkTheme={isDarkTheme} + $position={position} + $backdropOpacity={props.backdropOpacity}> + $isDarkTheme={isDarkTheme} + $position={position} + $backdropOpacity={props.backdropOpacity}> {props.children} diff --git a/src/components/menu/Menu.tsx b/src/components/menu/Menu.tsx index 1923307d..f8c75025 100644 --- a/src/components/menu/Menu.tsx +++ b/src/components/menu/Menu.tsx @@ -11,6 +11,7 @@ import { Link, useLocation, useNavigate } from "react-router-dom"; import styled from "styled-components"; import Account from "../../service/model/Account"; import BalanceWithCurrency from "../bases/BalanceWithCurrency"; +import Drawer from "../bases/Drawer"; import Switch from "../bases/Switch"; import { Box, Card, Flex, Grid, Separator, Text } from "@radix-ui/themes"; import { useAtom } from "jotai"; @@ -58,43 +59,6 @@ const TabButton = styled.button<{ $active: boolean }>` padding: 0; `; -const SheetBackdrop = styled.div<{ $open: boolean }>` - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.45); - opacity: ${(props) => (props.$open ? 1 : 0)}; - pointer-events: ${(props) => (props.$open ? "auto" : "none")}; - transition: opacity 0.2s ease; - z-index: 998; -`; - -const Sheet = styled.div<{ $open: boolean }>` - position: fixed; - left: 50%; - bottom: 90px; - transform: translateX(-50%) translateY(${(props) => (props.$open ? "0" : "18px")}); - width: min(420px, calc(100vw - 24px)); - max-height: 65vh; - overflow-y: auto; - opacity: ${(props) => (props.$open ? 1 : 0)}; - pointer-events: ${(props) => (props.$open ? "auto" : "none")}; - transition: opacity 0.2s ease, transform 0.2s ease; - z-index: 999; - border-radius: 18px; - background: var(--gray-1); - border: none; - box-shadow: none; - padding: 16px; - - @media (max-width: 768px) { - left: 0; - bottom: 74px; - width: 100vw; - border-radius: 16px 16px 0 0; - transform: translateY(${(props) => (props.$open ? "0" : "18px")}); - } -`; - const Version = styled.div` font-size: 12px; opacity: 0.7; @@ -142,8 +106,15 @@ const Menu: React.FC = (props) => { return ( <> - - + { + if (!open) { + closeSheet(); + } + }} + backdropOpacity={0.45}> ACCOUNTS @@ -184,8 +155,16 @@ const Menu: React.FC = (props) => { isRtl /> - - + + { + if (!open) { + closeSheet(); + } + }} + backdropOpacity={0.45}> SETTINGS @@ -217,7 +196,7 @@ const Menu: React.FC = (props) => { v{props.version} - + From 115d7b98278ea2991d73b8147413e569161cfd54 Mon Sep 17 00:00:00 2001 From: oat9002 Date: Sun, 19 Apr 2026 21:03:18 +0700 Subject: [PATCH 04/10] =?UTF-8?q?=E2=9C=A8=20refactor=20Drawer=20component?= =?UTF-8?q?=20to=20enhance=20styling=20and=20add=20new=20props=20for=20cus?= =?UTF-8?q?tomization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/bases/Drawer.tsx | 72 ++++++++++++++++++--------------- src/components/menu/Menu.tsx | 45 +++++++++------------ 2 files changed, 57 insertions(+), 60 deletions(-) diff --git a/src/components/bases/Drawer.tsx b/src/components/bases/Drawer.tsx index 9738bda6..49c4d0a5 100644 --- a/src/components/bases/Drawer.tsx +++ b/src/components/bases/Drawer.tsx @@ -4,12 +4,20 @@ import { pageSettingState } from "../../common/shareState"; import { DotsVerticalIcon } from "@radix-ui/react-icons"; import { useAtomValue } from "jotai"; -interface StyledProps { +interface BackgroundStyledProps { + $isMenuOnRightSide: boolean; + $isShow: boolean; + $backdropOpacity?: number; +} + +interface PanelStyledProps { $isMenuOnRightSide: boolean; $isShow: boolean; $isDarkTheme: boolean; $position: "side" | "bottom"; - $backdropOpacity?: number; + $bottomOffset?: string; + $padding?: string; + $margin?: string; } interface DrawStyledProps { @@ -28,10 +36,6 @@ const slideInRtl = keyframes` 0% {right: -100%} 100% {right: 0;} `; -const fadeIn = keyframes` - 0% {background-color: rgba(0, 0, 0, 0);} - 100% {background-color: rgba(0, 0, 0, 0.8);} -`; const slideOut = keyframes` 100% {left: -100%;} `; @@ -58,11 +62,7 @@ const slideOutBottom = keyframes` transform: translateX(-50%) translateY(40px); } `; -const fadeOut = keyframes` - 100% {background-color: rgba(0, 0, 0, 0);} -`; - -const Panel = styled.div` +const Panel = styled.div` z-index: ${zIndex}; position: fixed; background-color: ${(props) => @@ -88,18 +88,24 @@ const Panel = styled.div` ${animationDuration}s forwards; `; } else { + const bottomDesktop = props.$bottomOffset ?? "90px"; + const bottomMobile = + props.$bottomOffset !== undefined ? props.$bottomOffset : "74px"; + const padding = props.$padding ?? "16px"; + return css` - bottom: 90px; + bottom: ${bottomDesktop}; left: 50%; width: min(420px, calc(100vw - 24px)); max-height: 65vh; overflow-y: auto; border-radius: 18px 18px 0 0; - padding: 16px; + padding: ${padding}; + ${props.$margin !== undefined ? css`margin: ${props.$margin};` : css``} animation: ${props.$isShow ? slideInBottom : slideOutBottom} ${animationDuration}s forwards; @media (max-width: 768px) { - bottom: 74px; + bottom: ${bottomMobile}; width: 100vw; border-radius: 16px 16px 0 0; } @@ -108,7 +114,7 @@ const Panel = styled.div` }} `; -const Background = styled.div` +const Background = styled.div` z-index: ${zIndex - 1}; position: fixed; top: 0; @@ -117,7 +123,9 @@ const Background = styled.div` height: 100%; width: 100%; background-color: rgba(0, 0, 0, ${(props) => props.$backdropOpacity || 0.8}); - animation: ${(props) => (props.$isShow ? fadeIn : fadeOut)} ${animationDuration}s forwards; + opacity: ${(props) => (props.$isShow ? 1 : 0)}; + pointer-events: ${(props) => (props.$isShow ? "auto" : "none")}; + transition: opacity ${animationDuration}s ease-in-out; `; const Draw = styled.div` @@ -145,6 +153,9 @@ interface OwnProps { open?: boolean; onOpenChange?: (open: boolean) => void; backdropOpacity?: number; + bottomOffset?: string; + padding?: string; + margin?: string; } type DrawerProps = React.PropsWithChildren; @@ -154,7 +165,6 @@ const Drawer: React.FC = (props) => { const position = props.position || "side"; const isControlled = props.open !== undefined && props.onOpenChange !== undefined; const [internalIsShow, setInternalIsShow] = React.useState(false); - const [isAnimationUnmount, setIsAnimationUnmount] = React.useState(false); const isShowPanel = isControlled ? props.open : internalIsShow; const btnClickHandler = () => { @@ -175,15 +185,11 @@ const Drawer: React.FC = (props) => { return; } - setIsAnimationUnmount(true); - setTimeout(() => { - if (isControlled) { - props.onOpenChange(false); - } else { - setInternalIsShow(false); - } - setIsAnimationUnmount(false); - }, animationDuration * 1000); + if (isControlled) { + props.onOpenChange(false); + } else { + setInternalIsShow(false); + } }; return ( @@ -197,24 +203,24 @@ const Drawer: React.FC = (props) => { )} - {isShowPanel || isAnimationUnmount ? ( + {isShowPanel && ( + $bottomOffset={props.bottomOffset} + $padding={props.padding} + $margin={props.margin}> {props.children} - ) : null} + )} ); }; diff --git a/src/components/menu/Menu.tsx b/src/components/menu/Menu.tsx index f8c75025..b7944af4 100644 --- a/src/components/menu/Menu.tsx +++ b/src/components/menu/Menu.tsx @@ -13,7 +13,7 @@ import Account from "../../service/model/Account"; import BalanceWithCurrency from "../bases/BalanceWithCurrency"; import Drawer from "../bases/Drawer"; import Switch from "../bases/Switch"; -import { Box, Card, Flex, Grid, Separator, Text } from "@radix-ui/themes"; +import { Box, Flex, Grid, Separator, Text } from "@radix-ui/themes"; import { useAtom } from "jotai"; import { isHideBalanceOnMenuState } from "../../common/shareState"; @@ -46,7 +46,7 @@ const BottomMenuBar = styled.div` box-shadow: none; `; -const TabButton = styled.button<{ $active: boolean }>` +const TabButton = styled.button` display: flex; flex-direction: column; align-items: center; @@ -76,8 +76,6 @@ const Menu: React.FC = (props) => { const navigate = useNavigate(); const location = useLocation(); - const isSheetOpen = activeTab === "account" || activeTab === "settings"; - React.useEffect(() => { if (location.pathname === "/") { setActiveTab("home"); @@ -88,11 +86,7 @@ const Menu: React.FC = (props) => { }, [location.pathname]); const onHideBalanceChangeHandler = () => { - setIsHideBalance((prevState) => { - const nextState = !prevState; - - return nextState; - }); + setIsHideBalance((prev) => !prev); }; const navigateToHome = () => { @@ -104,16 +98,18 @@ const Menu: React.FC = (props) => { setActiveTab(location.pathname === "/" ? "home" : "none"); }; + const handleBottomDrawerOpenChange = (open: boolean) => { + if (!open) { + closeSheet(); + } + }; + return ( <> { - if (!open) { - closeSheet(); - } - }} + onOpenChange={handleBottomDrawerOpenChange} backdropOpacity={0.45}> ACCOUNTS @@ -159,12 +155,11 @@ const Menu: React.FC = (props) => { { - if (!open) { - closeSheet(); - } - }} - backdropOpacity={0.45}> + onOpenChange={handleBottomDrawerOpenChange} + backdropOpacity={0.45} + bottomOffset="0" + padding="16px" + margin="0"> SETTINGS @@ -201,23 +196,19 @@ const Menu: React.FC = (props) => { - + Home - setActiveTab("account")}> + setActiveTab("account")}> Account - setActiveTab("settings")}> + setActiveTab("settings")}> Settings From f3e80f1c809ce24905a43eee595bfb6779c873cc Mon Sep 17 00:00:00 2001 From: oat9002 Date: Sun, 26 Apr 2026 12:11:03 +0700 Subject: [PATCH 05/10] =?UTF-8?q?=F0=9F=A7=B9=20refactor=20PageSetting=20a?= =?UTF-8?q?nd=20Drawer=20components=20to=20remove=20unused=20state=20prope?= =?UTF-8?q?rties=20and=20simplify=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/shareState.ts | 2 - src/components/bases/Drawer.tsx | 83 +++++++----------------- src/components/setting/PageSetting.tsx | 43 +----------- src/service/model/configs/PageSetting.ts | 2 - 4 files changed, 25 insertions(+), 105 deletions(-) diff --git a/src/common/shareState.ts b/src/common/shareState.ts index 71068d35..f05f59dd 100644 --- a/src/common/shareState.ts +++ b/src/common/shareState.ts @@ -24,8 +24,6 @@ export const isSignInState = atom(false); export const currencyState = atomWithStorage("currency", "฿"); export const pageSettingState = atomWithStorage("pageSetting", { - isMenuOnRightSide: false, - isLightMenu: false, isDarkTheme: false, }); diff --git a/src/components/bases/Drawer.tsx b/src/components/bases/Drawer.tsx index 49c4d0a5..efd29ff0 100644 --- a/src/components/bases/Drawer.tsx +++ b/src/components/bases/Drawer.tsx @@ -1,17 +1,16 @@ import React from "react"; import styled, { keyframes, css } from "styled-components"; import { pageSettingState } from "../../common/shareState"; -import { DotsVerticalIcon } from "@radix-ui/react-icons"; import { useAtomValue } from "jotai"; +import { motion } from "framer-motion"; interface BackgroundStyledProps { - $isMenuOnRightSide: boolean; $isShow: boolean; $backdropOpacity?: number; + children?: React.ReactNode; } interface PanelStyledProps { - $isMenuOnRightSide: boolean; $isShow: boolean; $isDarkTheme: boolean; $position: "side" | "bottom"; @@ -21,7 +20,6 @@ interface PanelStyledProps { } interface DrawStyledProps { - $isMenuOnRightSide: boolean; $isLightMenu: boolean; $position: "side" | "bottom"; } @@ -42,7 +40,7 @@ const slideOut = keyframes` const slideOutRtl = keyframes` 100% {right: -100%;} `; -const slideInBottom = keyframes` +const slideUp = keyframes` 0% { opacity: 0; transform: translateX(-50%) translateY(40px); @@ -52,7 +50,7 @@ const slideInBottom = keyframes` transform: translateX(-50%) translateY(0); } `; -const slideOutBottom = keyframes` +const slideDown = keyframes` 0% { opacity: 1; transform: translateX(-50%) translateY(0); @@ -72,25 +70,13 @@ const Panel = styled.div` if (props.$position === "side") { return css` top: 0; - left: ${props.$isMenuOnRightSide ? "initial" : 0}; - right: ${props.$isMenuOnRightSide ? 0 : "initial"}; - border-radius: ${props.$isMenuOnRightSide ? "8px 0 0 8px" : "0 8px 8px 0"}; height: 100%; width: 30%; min-width: 350px; - animation: ${props.$isShow - ? props.$isMenuOnRightSide - ? slideInRtl - : slideIn - : props.$isMenuOnRightSide - ? slideOutRtl - : slideOut} - ${animationDuration}s forwards; `; } else { const bottomDesktop = props.$bottomOffset ?? "90px"; - const bottomMobile = - props.$bottomOffset !== undefined ? props.$bottomOffset : "74px"; + const bottomMobile = props.$bottomOffset !== undefined ? props.$bottomOffset : "74px"; const padding = props.$padding ?? "16px"; return css` @@ -101,8 +87,12 @@ const Panel = styled.div` overflow-y: auto; border-radius: 18px 18px 0 0; padding: ${padding}; - ${props.$margin !== undefined ? css`margin: ${props.$margin};` : css``} - animation: ${props.$isShow ? slideInBottom : slideOutBottom} ${animationDuration}s + ${props.$margin !== undefined + ? css` + margin: ${props.$margin}; + ` + : css``} + animation: ${props.$isShow ? slideUp : slideDown} ${animationDuration}s forwards; @media (max-width: 768px) { bottom: ${bottomMobile}; @@ -113,38 +103,24 @@ const Panel = styled.div` } }} `; - -const Background = styled.div` +const BackgroundMotion = (props: BackgroundStyledProps) => ( + + {props.children} + +); +const Background = styled(BackgroundMotion)` z-index: ${zIndex - 1}; position: fixed; top: 0; - left: ${(props) => (props.$isMenuOnRightSide ? "initial" : 0)}; - right: ${(props) => (props.$isMenuOnRightSide ? 0 : "initial")}; height: 100%; width: 100%; background-color: rgba(0, 0, 0, ${(props) => props.$backdropOpacity || 0.8}); - opacity: ${(props) => (props.$isShow ? 1 : 0)}; pointer-events: ${(props) => (props.$isShow ? "auto" : "none")}; - transition: opacity ${animationDuration}s ease-in-out; -`; - -const Draw = styled.div` - position: fixed; - cursor: pointer; - top: 40%; - width: 24px; - height: 100px; - background-color: ${(props) => (props.$isLightMenu ? "white" : "#363636")}; - color: ${(props) => (props.$isLightMenu ? "black" : "white")}; - border-radius: ${(props) => (props.$isMenuOnRightSide ? "8px 0 0 8px" : "0 8px 8px 0")}; - z-index: 10; - right: ${(props) => (props.$isMenuOnRightSide ? "0" : "initial")}; - display: ${(props) => (props.$position === "side" ? "block" : "none")}; -`; -const Icon = styled(DotsVerticalIcon)` - margin-top: 38px; - width: 24px; - height: 24px; `; interface OwnProps { @@ -161,7 +137,7 @@ interface OwnProps { type DrawerProps = React.PropsWithChildren; const Drawer: React.FC = (props) => { - const { isMenuOnRightSide, isLightMenu, isDarkTheme } = useAtomValue(pageSettingState); + const { isDarkTheme } = useAtomValue(pageSettingState); const position = props.position || "side"; const isControlled = props.open !== undefined && props.onOpenChange !== undefined; const [internalIsShow, setInternalIsShow] = React.useState(false); @@ -194,24 +170,13 @@ const Drawer: React.FC = (props) => { return ( <> - {position === "side" && ( - - - - )} {isShowPanel && ( { const [pageSetting, setPageSetting] = useAtom(pageSettingState); - const onMoveMenuToRightSideChangeHandler = () => { - setPageSetting((prevState) => { - const nextState = { - ...prevState, - isMenuOnRightSide: !prevState.isMenuOnRightSide, - }; - - return nextState; - }); - }; - const onMenuColorChangeHandler = () => { - setPageSetting((prevState) => { - const nextState = { - ...prevState, - isLightMenu: !prevState.isLightMenu, - }; - - return nextState; - }); - }; const onThemeChangeHandler = () => { setPageSetting((prevState) => { const nextState = { @@ -40,28 +20,7 @@ const PageSetting: React.FC = () => { return ( - - Move menu to the right - - - - - Change menu to light color - - - + Change to dark theme Date: Sun, 26 Apr 2026 15:47:12 +0700 Subject: [PATCH 06/10] =?UTF-8?q?=E2=9C=A8=20refactor=20Drawer=20component?= =?UTF-8?q?=20to=20simplify=20structure=20and=20enhance=20styling=20with?= =?UTF-8?q?=20Box=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot --- src/components/bases/Drawer.tsx | 128 ++++++++------------------------ 1 file changed, 29 insertions(+), 99 deletions(-) diff --git a/src/components/bases/Drawer.tsx b/src/components/bases/Drawer.tsx index efd29ff0..5ad3c5f5 100644 --- a/src/components/bases/Drawer.tsx +++ b/src/components/bases/Drawer.tsx @@ -3,116 +3,55 @@ import styled, { keyframes, css } from "styled-components"; import { pageSettingState } from "../../common/shareState"; import { useAtomValue } from "jotai"; import { motion } from "framer-motion"; +import { Box } from "@radix-ui/themes"; interface BackgroundStyledProps { $isShow: boolean; $backdropOpacity?: number; + onClick?: (event: React.MouseEvent) => void; children?: React.ReactNode; } -interface PanelStyledProps { - $isShow: boolean; - $isDarkTheme: boolean; - $position: "side" | "bottom"; - $bottomOffset?: string; - $padding?: string; - $margin?: string; -} - -interface DrawStyledProps { - $isLightMenu: boolean; - $position: "side" | "bottom"; +interface Panel { + children?: React.ReactNode; + isDarkTheme?: boolean; } const animationDuration = 0.5; const zIndex = 1000; -const slideIn = keyframes` - 0% {left: -100%} - 100% {left: 0;} -`; -const slideInRtl = keyframes` - 0% {right: -100%} - 100% {right: 0;} -`; -const slideOut = keyframes` - 100% {left: -100%;} -`; -const slideOutRtl = keyframes` - 100% {right: -100%;} -`; -const slideUp = keyframes` - 0% { - opacity: 0; - transform: translateX(-50%) translateY(40px); - } - 100% { - opacity: 1; - transform: translateX(-50%) translateY(0); - } -`; -const slideDown = keyframes` - 0% { - opacity: 1; - transform: translateX(-50%) translateY(0); - } - 100% { - opacity: 0; - transform: translateX(-50%) translateY(40px); - } -`; -const Panel = styled.div` - z-index: ${zIndex}; - position: fixed; - background-color: ${(props) => - props.$position === "bottom" ? "var(--gray-1)" : props.$isDarkTheme ? "#363636" : "white"}; - ${(props) => { - if (props.$position === "side") { - return css` - top: 0; - height: 100%; - width: 30%; - min-width: 350px; - `; - } else { - const bottomDesktop = props.$bottomOffset ?? "90px"; - const bottomMobile = props.$bottomOffset !== undefined ? props.$bottomOffset : "74px"; - const padding = props.$padding ?? "16px"; +const Panel = (props: Panel) => ( + + + {props.children} + + +); - return css` - bottom: ${bottomDesktop}; - left: 50%; - width: min(420px, calc(100vw - 24px)); - max-height: 65vh; - overflow-y: auto; - border-radius: 18px 18px 0 0; - padding: ${padding}; - ${props.$margin !== undefined - ? css` - margin: ${props.$margin}; - ` - : css``} - animation: ${props.$isShow ? slideUp : slideDown} ${animationDuration}s - forwards; - @media (max-width: 768px) { - bottom: ${bottomMobile}; - width: 100vw; - border-radius: 16px 16px 0 0; - } - `; - } - }} -`; const BackgroundMotion = (props: BackgroundStyledProps) => ( {props.children} ); + const Background = styled(BackgroundMotion)` z-index: ${zIndex - 1}; position: fixed; @@ -138,7 +77,6 @@ type DrawerProps = React.PropsWithChildren; const Drawer: React.FC = (props) => { const { isDarkTheme } = useAtomValue(pageSettingState); - const position = props.position || "side"; const isControlled = props.open !== undefined && props.onOpenChange !== undefined; const [internalIsShow, setInternalIsShow] = React.useState(false); const isShowPanel = isControlled ? props.open : internalIsShow; @@ -173,17 +111,9 @@ const Drawer: React.FC = (props) => { {isShowPanel && ( - - {props.children} - + {props.children} )} From cf6973526a59372e6e3666099f410e3f1b4df2f2 Mon Sep 17 00:00:00 2001 From: oat9002 Date: Sun, 26 Apr 2026 16:06:34 +0700 Subject: [PATCH 07/10] =?UTF-8?q?=E2=9C=A8=20refactor=20Drawer=20component?= =?UTF-8?q?=20to=20improve=20animation=20handling=20and=20structure=20with?= =?UTF-8?q?=20AnimatePresence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/bases/Drawer.tsx | 73 +++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/src/components/bases/Drawer.tsx b/src/components/bases/Drawer.tsx index 5ad3c5f5..4d8c7bfe 100644 --- a/src/components/bases/Drawer.tsx +++ b/src/components/bases/Drawer.tsx @@ -1,9 +1,8 @@ import React from "react"; -import styled, { keyframes, css } from "styled-components"; +import styled from "styled-components"; import { pageSettingState } from "../../common/shareState"; import { useAtomValue } from "jotai"; -import { motion } from "framer-motion"; -import { Box } from "@radix-ui/themes"; +import { motion, AnimatePresence } from "framer-motion"; interface BackgroundStyledProps { $isShow: boolean; @@ -20,33 +19,43 @@ interface Panel { const animationDuration = 0.5; const zIndex = 1000; +const PanelContainer = styled(motion.div)` + box-sizing: border-box; + left: 50%; + width: min(420px, calc(100vw - 24px)); + + @media (max-width: 768px) { + width: calc(100vw - 16px); + } +`; + const Panel = (props: Panel) => ( - - - {props.children} - - + + {props.children} + ); const BackgroundMotion = (props: BackgroundStyledProps) => ( {props.children} @@ -108,14 +117,18 @@ const Drawer: React.FC = (props) => { return ( <> - {isShowPanel && ( - - {props.children} - - )} + + {isShowPanel && ( + <> + + {props.children} + + )} + ); }; From 714fc273d6938a31e6804c0c3c93e326edfa289c Mon Sep 17 00:00:00 2001 From: oat9002 Date: Sun, 26 Apr 2026 16:23:22 +0700 Subject: [PATCH 08/10] =?UTF-8?q?=E2=9C=A8=20refactor=20Drawer=20component?= =?UTF-8?q?=20to=20streamline=20animation=20logic=20and=20improve=20stylin?= =?UTF-8?q?g=20consistency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/bases/Drawer.tsx | 125 +++++++++++++------------------- 1 file changed, 52 insertions(+), 73 deletions(-) diff --git a/src/components/bases/Drawer.tsx b/src/components/bases/Drawer.tsx index 4d8c7bfe..b8ec58da 100644 --- a/src/components/bases/Drawer.tsx +++ b/src/components/bases/Drawer.tsx @@ -1,26 +1,25 @@ import React from "react"; import styled from "styled-components"; -import { pageSettingState } from "../../common/shareState"; -import { useAtomValue } from "jotai"; import { motion, AnimatePresence } from "framer-motion"; interface BackgroundStyledProps { - $isShow: boolean; $backdropOpacity?: number; onClick?: (event: React.MouseEvent) => void; - children?: React.ReactNode; } -interface Panel { - children?: React.ReactNode; - isDarkTheme?: boolean; -} - -const animationDuration = 0.5; +const animationDuration = 0.25; const zIndex = 1000; const PanelContainer = styled(motion.div)` box-sizing: border-box; + z-index: ${zIndex}; + bottom: 80px; + position: fixed; + overflow-y: auto; + max-height: 65vh; + padding: var(--space-4); + background-color: var(--gray-2); + border-radius: 16px; left: 50%; width: min(420px, calc(100vw - 24px)); @@ -29,46 +28,28 @@ const PanelContainer = styled(motion.div)` } `; -const Panel = (props: Panel) => ( - - {props.children} - -); +const panelMotion = { + initial: { y: "100%", x: "-50%", opacity: 0 }, + animate: { y: 0, x: "-50%", opacity: 1 }, + exit: { y: "100%", x: "-50%", opacity: 0 }, +}; -const BackgroundMotion = (props: BackgroundStyledProps) => ( - - {props.children} - -); +const backdropMotion = { + initial: { opacity: 0, backdropFilter: "blur(0px)" }, + animate: { opacity: 1, backdropFilter: "blur(8px)" }, + exit: { opacity: 0, backdropFilter: "blur(0px)" }, +}; -const Background = styled(BackgroundMotion)` +const Background = styled(motion.div)` z-index: ${zIndex - 1}; position: fixed; top: 0; height: 100%; width: 100%; - background-color: rgba(0, 0, 0, ${(props) => props.$backdropOpacity || 0.8}); - pointer-events: ${(props) => (props.$isShow ? "auto" : "none")}; + background-color: rgba(0, 0, 0, ${(props) => props.$backdropOpacity ?? 0.8}); + will-change: opacity, backdrop-filter; + -webkit-backdrop-filter: blur(0px); + pointer-events: auto; `; interface OwnProps { @@ -77,31 +58,22 @@ interface OwnProps { open?: boolean; onOpenChange?: (open: boolean) => void; backdropOpacity?: number; - bottomOffset?: string; - padding?: string; - margin?: string; } type DrawerProps = React.PropsWithChildren; const Drawer: React.FC = (props) => { - const { isDarkTheme } = useAtomValue(pageSettingState); - const isControlled = props.open !== undefined && props.onOpenChange !== undefined; + const { open, onOpenChange, preventCloseIdOrClassList, backdropOpacity, children } = props; + const isControlled = open !== undefined && onOpenChange !== undefined; const [internalIsShow, setInternalIsShow] = React.useState(false); - const isShowPanel = isControlled ? props.open : internalIsShow; + const isShowPanel = isControlled ? open : internalIsShow; - const btnClickHandler = () => { - if (isControlled) { - props.onOpenChange(true); - } else { - setInternalIsShow(true); - } - }; const closePanelHandler = (event: React.MouseEvent) => { const target = event.target as HTMLElement; + const targetClassName = typeof target.className === "string" ? target.className : ""; const shouldPrevent = - props.preventCloseIdOrClassList?.some( - (x) => x === target.id || target.className.includes(x) + preventCloseIdOrClassList?.some( + (x) => x === target.id || targetClassName.includes(x) ) ?? false; if (shouldPrevent) { @@ -109,27 +81,34 @@ const Drawer: React.FC = (props) => { } if (isControlled) { - props.onOpenChange(false); + onOpenChange(false); } else { setInternalIsShow(false); } }; return ( - <> - - {isShowPanel && ( - <> - - {props.children} - - )} - - + + {isShowPanel && ( + <> + + + {children} + + + )} + ); }; From 0cf4ca399a7205bbce36abd2e7c9b6174af8c91a Mon Sep 17 00:00:00 2001 From: oat9002 Date: Sun, 26 Apr 2026 16:28:55 +0700 Subject: [PATCH 09/10] =?UTF-8?q?=E2=9C=A8=20refactor=20Drawer=20and=20Men?= =?UTF-8?q?u=20components=20to=20remove=20unused=20props=20and=20simplify?= =?UTF-8?q?=20structure,=20adjusting=20font=20size=20in=20Menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/bases/Drawer.tsx | 8 ++------ src/components/menu/Menu.tsx | 24 ++++++------------------ src/components/setting/PageSetting.tsx | 3 +-- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/src/components/bases/Drawer.tsx b/src/components/bases/Drawer.tsx index b8ec58da..e47fb958 100644 --- a/src/components/bases/Drawer.tsx +++ b/src/components/bases/Drawer.tsx @@ -3,7 +3,6 @@ import styled from "styled-components"; import { motion, AnimatePresence } from "framer-motion"; interface BackgroundStyledProps { - $backdropOpacity?: number; onClick?: (event: React.MouseEvent) => void; } @@ -46,7 +45,7 @@ const Background = styled(motion.div)` top: 0; height: 100%; width: 100%; - background-color: rgba(0, 0, 0, ${(props) => props.$backdropOpacity ?? 0.8}); + background-color: rgba(0, 0, 0, 0.8); will-change: opacity, backdrop-filter; -webkit-backdrop-filter: blur(0px); pointer-events: auto; @@ -54,16 +53,14 @@ const Background = styled(motion.div)` interface OwnProps { preventCloseIdOrClassList?: string[]; - position?: "side" | "bottom"; open?: boolean; onOpenChange?: (open: boolean) => void; - backdropOpacity?: number; } type DrawerProps = React.PropsWithChildren; const Drawer: React.FC = (props) => { - const { open, onOpenChange, preventCloseIdOrClassList, backdropOpacity, children } = props; + const { open, onOpenChange, preventCloseIdOrClassList, children } = props; const isControlled = open !== undefined && onOpenChange !== undefined; const [internalIsShow, setInternalIsShow] = React.useState(false); const isShowPanel = isControlled ? open : internalIsShow; @@ -93,7 +90,6 @@ const Drawer: React.FC = (props) => { <> = (props) => { return ( <> - + ACCOUNTS @@ -152,14 +148,7 @@ const Menu: React.FC = (props) => { /> - + SETTINGS @@ -170,26 +159,25 @@ const Menu: React.FC = (props) => { Account - Category - Page - Sign out - v{props.version} + + v{props.version} + diff --git a/src/components/setting/PageSetting.tsx b/src/components/setting/PageSetting.tsx index e8805edd..e9f2a95c 100644 --- a/src/components/setting/PageSetting.tsx +++ b/src/components/setting/PageSetting.tsx @@ -20,8 +20,7 @@ const PageSetting: React.FC = () => { return ( - - + Change to dark theme Date: Sun, 26 Apr 2026 16:40:08 +0700 Subject: [PATCH 10/10] =?UTF-8?q?=E2=9C=A8=20add=20primaryIcon=20color=20t?= =?UTF-8?q?o=20constants=20and=20update=20Menu=20component=20to=20use=20ne?= =?UTF-8?q?w=20icon=20and=20color=20logic=20for=20account=20and=20settings?= =?UTF-8?q?=20tabs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/constants.ts | 1 + src/components/menu/Menu.tsx | 31 ++++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/common/constants.ts b/src/common/constants.ts index 9132d1c4..6b6a3178 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -1,3 +1,4 @@ export const color = { primary: "blue" as any, + primaryIcon: "#007aff" as any, }; diff --git a/src/components/menu/Menu.tsx b/src/components/menu/Menu.tsx index d0d48f85..f77c5438 100644 --- a/src/components/menu/Menu.tsx +++ b/src/components/menu/Menu.tsx @@ -2,10 +2,10 @@ import React from "react"; import { GearIcon, HomeIcon, - PersonIcon, ExitIcon, CardStackIcon, ReaderIcon, + IdCardIcon, } from "@radix-ui/react-icons"; import { Link, useLocation, useNavigate } from "react-router-dom"; import styled from "styled-components"; @@ -16,6 +16,7 @@ import Switch from "../bases/Switch"; import { Box, Flex, Grid, Separator, Text } from "@radix-ui/themes"; import { useAtom } from "jotai"; import { isHideBalanceOnMenuState } from "../../common/shareState"; +import { color } from "../../common/constants"; interface MenuProps { accounts: Account[]; @@ -104,6 +105,12 @@ const Menu: React.FC = (props) => { } }; + const handleDrawerTabClick = (tab: Extract) => { + setActiveTab((prev) => + prev === tab ? (location.pathname === "/" ? "home" : "none") : tab + ); + }; + return ( <> @@ -185,20 +192,26 @@ const Menu: React.FC = (props) => { - - + + Home - setActiveTab("account")}> - - + handleDrawerTabClick("account")}> + + Account - setActiveTab("settings")}> - - + handleDrawerTabClick("settings")}> + + Settings