diff --git a/wallet/src/ui/app/components/menu/button/MenuButton.module.scss b/wallet/src/ui/app/components/menu/button/MenuButton.module.scss new file mode 100644 index 0000000000000..9a3ab8a0536bc --- /dev/null +++ b/wallet/src/ui/app/components/menu/button/MenuButton.module.scss @@ -0,0 +1,55 @@ +@use '_variables' as v; + +$btn-width: 20px; +$btn-height: 14px; +$line-height: 2px; + +.button { + background: none; + outline: none; + border: none; + width: $btn-width; + height: $btn-height; + position: relative; + cursor: pointer; + padding: 0; +} + +.line { + width: $btn-width; + height: $line-height; + background-color: v.use(v.$colors-menu-btn-color); + transition-property: opacity, transform, top; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + display: block; + position: absolute; + border-radius: 10px; + + &.line-1 { + top: 0; + } + + &.line-2 { + top: calc(50% - ($line-height / 2)); + } + + &.line-3 { + bottom: 0; + } +} + +.open { + > .line-1 { + top: calc(50% - ($line-height / 2)); + transform: rotate(45deg); + } + + > .line-2 { + transform: rotate(-45deg); + } + + > .line-3 { + opacity: 0; + } +} diff --git a/wallet/src/ui/app/components/menu/button/index.tsx b/wallet/src/ui/app/components/menu/button/index.tsx new file mode 100644 index 0000000000000..9d01588c3b6b6 --- /dev/null +++ b/wallet/src/ui/app/components/menu/button/index.tsx @@ -0,0 +1,31 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import cl from 'classnames'; +import { memo } from 'react'; +import { Link } from 'react-router-dom'; + +import { useMenuIsOpen, useNextMenuUrl } from '_components/menu/hooks'; + +import st from './MenuButton.module.scss'; + +export type MenuButtonProps = { + className?: string; +}; + +function MenuButton({ className }: MenuButtonProps) { + const isOpen = useMenuIsOpen(); + const menuUrl = useNextMenuUrl(!isOpen, '/'); + return ( + + + + + + ); +} + +export default memo(MenuButton); diff --git a/wallet/src/ui/app/components/menu/content/MenuContent.module.scss b/wallet/src/ui/app/components/menu/content/MenuContent.module.scss new file mode 100644 index 0000000000000..e107d129f4db6 --- /dev/null +++ b/wallet/src/ui/app/components/menu/content/MenuContent.module.scss @@ -0,0 +1,25 @@ +@use '_values/colors'; +@use '_variables' as v; + +.container { + position: absolute; + width: 100%; + height: 100%; +} + +.backdrop { + position: absolute; + width: 100%; + height: 100%; + background-color: colors.$gray-100; + opacity: 0.8; +} + +.content { + position: relative; + padding: 20px 25px 25px; + background-color: v.use(v.$colors-main-content-background); + border-radius: 20px; + max-height: 100%; + overflow-y: auto; +} diff --git a/wallet/src/ui/app/components/menu/content/index.tsx b/wallet/src/ui/app/components/menu/content/index.tsx new file mode 100644 index 0000000000000..bbb2a6c692452 --- /dev/null +++ b/wallet/src/ui/app/components/menu/content/index.tsx @@ -0,0 +1,56 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import { useCallback } from 'react'; +import { Navigate, Route, Routes, useNavigate } from 'react-router-dom'; + +import Settings from './settings'; +import { + useMenuIsOpen, + useMenuUrl, + useNextMenuUrl, +} from '_components/menu/hooks'; +import { useOnKeyboardEvent } from '_hooks'; + +import type { MouseEvent } from 'react'; + +import st from './MenuContent.module.scss'; + +const CLOSE_KEY_CODES: string[] = ['Escape']; + +function MenuContent() { + const isOpen = useMenuIsOpen(); + const menuUrl = useMenuUrl(); + const menuHomeUrl = useNextMenuUrl(true, '/settings'); + const closeMenuUrl = useNextMenuUrl(false); + const navigate = useNavigate(); + const handleOnCloseMenu = useCallback( + (e: KeyboardEvent | MouseEvent) => { + if (isOpen) { + e.preventDefault(); + navigate(closeMenuUrl); + } + }, + [isOpen, navigate, closeMenuUrl] + ); + useOnKeyboardEvent('keydown', CLOSE_KEY_CODES, handleOnCloseMenu, isOpen); + if (!isOpen) { + return null; + } + return ( +
+
+
+ + } /> + } + /> + +
+
+ ); +} + +export default MenuContent; diff --git a/wallet/src/ui/app/pages/home/settings/SettingsPage.module.scss b/wallet/src/ui/app/components/menu/content/settings/SettingsPage.module.scss similarity index 100% rename from wallet/src/ui/app/pages/home/settings/SettingsPage.module.scss rename to wallet/src/ui/app/components/menu/content/settings/SettingsPage.module.scss diff --git a/wallet/src/ui/app/pages/home/settings/index.tsx b/wallet/src/ui/app/components/menu/content/settings/index.tsx similarity index 98% rename from wallet/src/ui/app/pages/home/settings/index.tsx rename to wallet/src/ui/app/components/menu/content/settings/index.tsx index 29858a91a8156..69d01b8d08f6e 100644 --- a/wallet/src/ui/app/pages/home/settings/index.tsx +++ b/wallet/src/ui/app/components/menu/content/settings/index.tsx @@ -20,7 +20,7 @@ import type { SerializedError } from '@reduxjs/toolkit'; import st from './SettingsPage.module.scss'; -function SettingsPage() { +function Settings() { const [logoutInProgress, setLogoutInProgress] = useState(false); const [mintInProgress, setMintInProgress] = useState(false); const [mintStatus, setMintStatus] = useState(null); @@ -114,4 +114,4 @@ function SettingsPage() { ); } -export default SettingsPage; +export default Settings; diff --git a/wallet/src/ui/app/components/menu/hooks.ts b/wallet/src/ui/app/components/menu/hooks.ts new file mode 100644 index 0000000000000..1360d932fd746 --- /dev/null +++ b/wallet/src/ui/app/components/menu/hooks.ts @@ -0,0 +1,40 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import { useMemo } from 'react'; +import { useLocation, useSearchParams } from 'react-router-dom'; + +const MENU_PARAM = 'menu'; + +export function useMenuUrl() { + const [searchParams] = useSearchParams(); + if (searchParams.has(MENU_PARAM)) { + return searchParams.get(MENU_PARAM) || '/'; + } + return false; +} + +export function useMenuIsOpen() { + const [searchParams] = useSearchParams(); + return searchParams.has(MENU_PARAM); +} + +/** + * Get the URL that contains the background page and the menu location + * + * @param isOpen Indicates if the menu will be open + * @param nextMenuLocation The location within the menu + */ +export function useNextMenuUrl(isOpen: boolean, nextMenuLocation = '/') { + const [searchParams] = useSearchParams(); + const { pathname } = useLocation(); + return useMemo(() => { + if (isOpen) { + searchParams.set(MENU_PARAM, nextMenuLocation); + } else { + searchParams.delete(MENU_PARAM); + } + const search = searchParams.toString(); + return `${pathname}${search ? '?' : ''}${search}`; + }, [isOpen, nextMenuLocation, searchParams, pathname]); +} diff --git a/wallet/src/ui/app/components/menu/index.tsx b/wallet/src/ui/app/components/menu/index.tsx new file mode 100644 index 0000000000000..93c40e17c2981 --- /dev/null +++ b/wallet/src/ui/app/components/menu/index.tsx @@ -0,0 +1,5 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +export { default as MenuButton } from './button'; +export { default as MenuContent } from './content'; diff --git a/wallet/src/ui/app/components/navigation/index.tsx b/wallet/src/ui/app/components/navigation/index.tsx index 5b20f0e392d21..33c240aa88563 100644 --- a/wallet/src/ui/app/components/navigation/index.tsx +++ b/wallet/src/ui/app/components/navigation/index.tsx @@ -36,10 +36,6 @@ function Navigation({ className }: NavigationProps) { Activity - - - Settings - ); } diff --git a/wallet/src/ui/app/hooks/index.ts b/wallet/src/ui/app/hooks/index.ts index 8aacd191d0c94..8552ebae9b6a5 100644 --- a/wallet/src/ui/app/hooks/index.ts +++ b/wallet/src/ui/app/hooks/index.ts @@ -10,3 +10,4 @@ export { default as useMediaUrl } from './useMediaUrl'; export { default as useSuiObjectFields } from './useSuiObjectFields'; export { default as useNumberDelimiters } from './useNumberDelimiters'; export { default as useOnClickOutside } from './useOnClickOutside'; +export { default as useOnKeyboardEvent } from './useOnKeyboardEvent'; diff --git a/wallet/src/ui/app/hooks/useOnKeyboardEvent.ts b/wallet/src/ui/app/hooks/useOnKeyboardEvent.ts new file mode 100644 index 0000000000000..606aa21e9cbf0 --- /dev/null +++ b/wallet/src/ui/app/hooks/useOnKeyboardEvent.ts @@ -0,0 +1,29 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import { useEffect } from 'react'; + +function useOnKeyboardEvent( + eventType: K, + keys: string[], + handler: (e: KeyboardEvent) => void, + enabled = true +) { + useEffect(() => { + if (enabled) { + const listener = (e: KeyboardEvent) => { + if (keys.includes(e.key)) { + handler(e); + } + }; + + document.addEventListener(eventType, listener); + + return () => { + document.removeEventListener(eventType, listener); + }; + } + }, [eventType, keys, handler, enabled]); +} + +export default useOnKeyboardEvent; diff --git a/wallet/src/ui/app/index.tsx b/wallet/src/ui/app/index.tsx index b18e9f4d3125f..4059b776047cd 100644 --- a/wallet/src/ui/app/index.tsx +++ b/wallet/src/ui/app/index.tsx @@ -9,7 +9,6 @@ import { useAppDispatch, useAppSelector } from '_hooks'; import { DappTxApprovalPage } from '_pages/dapp-tx-approval'; import HomePage, { NftsPage, - SettingsPage, StakePage, TokensPage, TransactionDetailsPage, @@ -49,7 +48,6 @@ const App = () => { } /> } /> } /> - } /> } /> } /> } /> diff --git a/wallet/src/ui/app/pages/home/Home.module.scss b/wallet/src/ui/app/pages/home/Home.module.scss index 9b6bade7971c8..604ed100c3e5e 100644 --- a/wallet/src/ui/app/pages/home/Home.module.scss +++ b/wallet/src/ui/app/pages/home/Home.module.scss @@ -13,10 +13,14 @@ flex-flow: row nowrap; align-items: center; padding: 0 15px; - justify-content: center; + justify-content: space-between; min-height: 52px; } +.menu-button { + justify-self: flex-end; +} + .content { display: flex; flex-flow: column nowrap; diff --git a/wallet/src/ui/app/pages/home/index.tsx b/wallet/src/ui/app/pages/home/index.tsx index 98c4df207aae0..aea0db0156711 100644 --- a/wallet/src/ui/app/pages/home/index.tsx +++ b/wallet/src/ui/app/pages/home/index.tsx @@ -7,6 +7,7 @@ import { of, filter, switchMap, from, defer, repeat } from 'rxjs'; import Loading from '_components/loading'; import Logo from '_components/logo'; +import { MenuButton, MenuContent } from '_components/menu'; import Navigation from '_components/navigation'; import { useInitializedGuard, useAppDispatch } from '_hooks'; import PageLayout from '_pages/layout'; @@ -38,13 +39,16 @@ const HomePage = () => {
+ +
+
@@ -54,7 +58,6 @@ const HomePage = () => { export default HomePage; export { default as NftsPage } from './nfts'; -export { default as SettingsPage } from './settings'; export { default as StakePage } from './stake'; export { default as TokensPage } from './tokens'; export { default as TransactionDetailsPage } from './transaction-details'; diff --git a/wallet/src/ui/styles/themes/dark.scss b/wallet/src/ui/styles/themes/dark.scss index 7fe7d6016e16d..4492a6903cb89 100644 --- a/wallet/src/ui/styles/themes/dark.scss +++ b/wallet/src/ui/styles/themes/dark.scss @@ -15,4 +15,5 @@ $values: ( c-var.$nav-item-highlighted-color: c-val.$white, c-var.$nav-item-icon-highlighted-color: c-val.$sui-blue, c-var.$nav-item-icon-highlighted-gradient-color: none, + c-var.$menu-btn-color: c-val.$white, ); diff --git a/wallet/src/ui/styles/themes/light.scss b/wallet/src/ui/styles/themes/light.scss index caca5bd7599b3..cacb897ab18e1 100644 --- a/wallet/src/ui/styles/themes/light.scss +++ b/wallet/src/ui/styles/themes/light.scss @@ -20,4 +20,5 @@ $values: ( c-var.$nav-item-icon-highlighted-color: transparent, c-var.$nav-item-icon-highlighted-gradient-color: linear-gradient(135deg, #589aea 0%, #4c75a6 100%), + c-var.$menu-btn-color: c-val.$gray-70, ); diff --git a/wallet/src/ui/styles/variables/colors.scss b/wallet/src/ui/styles/variables/colors.scss index b3d1709d82974..f16f66f07b448 100644 --- a/wallet/src/ui/styles/variables/colors.scss +++ b/wallet/src/ui/styles/variables/colors.scss @@ -9,3 +9,4 @@ $nav-item-color: '--colors-nav-item-color'; $nav-item-highlighted-color: '--colors-nav-item-highlighted-color'; $nav-item-icon-highlighted-color: '--colors-nav-item-icon-highlighted-color'; $nav-item-icon-highlighted-gradient-color: '--colors-nav-item-icon-highlighted-gradient-color'; +$menu-btn-color: '--colors-menu-btn-color';