diff --git a/src/common/assets/MakersDarkLogo.tsx b/src/common/assets/MakersDarkLogo.tsx index d824e119..fbaadd6b 100644 --- a/src/common/assets/MakersDarkLogo.tsx +++ b/src/common/assets/MakersDarkLogo.tsx @@ -1,6 +1,12 @@ -const MakersDarkLogo = () => { +const MakersDarkLogo = ({ className }: { className?: string }) => { return ( - + { +const MakersLogo = ({ className }: { className?: string }) => { return ( - + { +const NowsoptLogo = ({ className }: { className?: string }) => { return ( - + { + const DEVICE_TYPE = useDevice(); + return ( -
  • +
  • {path ? ( (amplitudeId ? track(amplitudeId) : null)} target={target}> {text} ) : ( -

    +

    {text}

    )} diff --git a/src/common/components/Layout/components/Header/Nav/MenuItem/style.css.ts b/src/common/components/Layout/components/Header/Nav/MenuItem/style.css.ts new file mode 100644 index 00000000..9a2932f3 --- /dev/null +++ b/src/common/components/Layout/components/Header/Nav/MenuItem/style.css.ts @@ -0,0 +1,75 @@ +import { style, styleVariants } from '@vanilla-extract/css'; + +import { theme } from 'styles/theme.css'; + +const menuItem = style({ + ...theme.font.HEADING_6_18_B, +}); + +export const menuItemVar = styleVariants({ + DESK: [ + menuItem, + { + color: theme.color.baseText, + }, + ], + TAB: [ + menuItem, + { + color: theme.color.grayButtonFill, + }, + ], + MOB: [ + menuItem, + { + color: theme.color.grayButtonFill, + }, + ], +}); + +const menuLink = style({ + textDecoration: `underline transparent 2px`, + textUnderlineOffset: 21, + transition: 'all 0.2s ease-out', + cursor: 'pointer', +}); + +export const menuLinkVar = styleVariants( + { + DESK: { + hover: { + textDecorationColor: theme.color.primary, + }, + active: { + color: theme.color.primary, + }, + }, + TAB: { + hover: { + color: theme.color.whiteButtonFill, + }, + active: { + color: theme.color.whiteButtonFill, + fontWeight: 'bolder', + }, + }, + MOB: { + hover: { + color: theme.color.whiteButtonFill, + }, + active: { + color: theme.color.whiteButtonFill, + fontWeight: 'bolder', + }, + }, + }, + ({ hover, active }) => [ + menuLink, + { + selectors: { + '&:hover:not([disabled])': hover, + '&.active': active, + }, + }, + ], +); diff --git a/src/common/components/Layout/components/Header/Nav/MenuList/index.tsx b/src/common/components/Layout/components/Header/Nav/MenuList/index.tsx new file mode 100644 index 00000000..c687a3ce --- /dev/null +++ b/src/common/components/Layout/components/Header/Nav/MenuList/index.tsx @@ -0,0 +1,73 @@ +import { reset, track } from '@amplitude/analytics-browser'; +import { useContext, useEffect, useState } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; + +import { useDevice } from '@hooks/useDevice'; +import { RecruitingInfoContext } from '@store/recruitingInfoContext'; + +import { dimmedBgVar, menuContainerVar, menuList, menuMobListVar } from './style.css'; +import { MENU_ITEMS, MENU_ITEMS_MAKERS } from '../../contants'; +import MenuItem from '../MenuItem'; + +const MenuList = ({ isMenuOpen, onClickMenuToggle }: { isMenuOpen?: boolean; onClickMenuToggle?: () => void }) => { + const DEVICE_TYPE = useDevice(); + const navigate = useNavigate(); + const { pathname } = useLocation(); + const [isShown, setIsShown] = useState(isMenuOpen); + const [animation, setAnimation] = useState<'open' | 'close'>(isMenuOpen ? 'open' : 'close'); + + useEffect(() => { + if (isMenuOpen) { + setIsShown(true); + setAnimation('open'); + } else { + setAnimation('close'); + const timer = setTimeout(() => setIsShown(false), 300); + return () => clearTimeout(timer); + } + }, [isMenuOpen]); + + const isSignedIn = localStorage.getItem('soptApplyAccessToken'); + + const { + recruitingInfo: { name, isMakers }, + } = useContext(RecruitingInfoContext); + const menuItems = isMakers ? MENU_ITEMS_MAKERS : MENU_ITEMS; + const handleLogout = () => { + track('click-gnb-logout'); + reset(); + localStorage.removeItem('soptApplyAccessToken'); + localStorage.removeItem('soptApplyAccessTokenExpiredTime'); + pathname === '/' ? window.location.reload() : navigate('/'); + }; + + if (onClickMenuToggle && !isShown) return null; + + return ( + + ); +}; + +export default MenuList; diff --git a/src/common/components/Layout/components/Header/Nav/MenuList/style.css.ts b/src/common/components/Layout/components/Header/Nav/MenuList/style.css.ts new file mode 100644 index 00000000..a4ba5e8a --- /dev/null +++ b/src/common/components/Layout/components/Header/Nav/MenuList/style.css.ts @@ -0,0 +1,75 @@ +import { style, styleVariants } from '@vanilla-extract/css'; + +import { Z_INDEX } from '@constants/zIndex'; +import { fadeIn, fadeInDown, fadeOut, fadeOutUp } from 'styles/animation.css'; +import { theme } from 'styles/theme.css'; + +export const menuContainerVar = styleVariants({ + open: { + animation: `${fadeInDown} 0.3s`, + }, + close: { + animation: `${fadeOutUp} 0.3s`, + animationFillMode: 'forwards', + }, +}); +export const menuList = style({ + display: 'flex', + alignItems: 'center', + gap: 40, +}); + +const menuMobList = style({ + display: 'flex', + flexDirection: 'column', + gap: 18, + position: 'fixed', + top: 0, + left: 0, + width: '100%', + backgroundColor: theme.color.blackBackground, + zIndex: Z_INDEX.gnbMenu, +}); + +export const menuMobListVar = styleVariants({ + TAB: [ + menuMobList, + { + padding: '92px 40px 40px 40px', + }, + ], + MOB: [ + menuMobList, + { + padding: '86px 20px 36px 20px', + }, + ], +}); + +export const dimmedBg = style({ + position: 'fixed', + top: 0, + left: 0, + width: '100%', + height: '100dvh', + backgroundColor: theme.color.backgroundDimmed, + zIndex: Z_INDEX.gnbBg, + + cursor: 'pointer', +}); + +export const dimmedBgVar = styleVariants({ + open: [ + dimmedBg, + { + animation: `${fadeIn} 0.3s`, + }, + ], + close: [ + dimmedBg, + { + animation: `${fadeOut} 0.3s`, + animationFillMode: 'forwards', + }, + ], +}); diff --git a/src/common/components/Layout/components/Header/Nav/index.tsx b/src/common/components/Layout/components/Header/Nav/index.tsx new file mode 100644 index 00000000..bc8e78a6 --- /dev/null +++ b/src/common/components/Layout/components/Header/Nav/index.tsx @@ -0,0 +1,24 @@ +import { IconMenu, IconXClose } from '@sopt-makers/icons'; +import { useContext } from 'react'; + +import { useDevice } from '@hooks/useDevice'; +import { ThemeContext } from '@store/themeContext'; +import { theme } from 'styles/theme.css'; + +import MenuList from './MenuList'; +import { menuIconVar } from './style.css'; + +const Nav = ({ isMenuOpen, onClickMenuToggle }: { isMenuOpen: boolean; onClickMenuToggle: () => void }) => { + const DEVICE_TYPE = useDevice(); + const { isLight } = useContext(ThemeContext); + + return DEVICE_TYPE !== 'DESK' ? ( + + {isMenuOpen ? : } + + ) : ( + + ); +}; + +export default Nav; diff --git a/src/common/components/Layout/components/Header/Nav/style.css.ts b/src/common/components/Layout/components/Header/Nav/style.css.ts new file mode 100644 index 00000000..5be0e10a --- /dev/null +++ b/src/common/components/Layout/components/Header/Nav/style.css.ts @@ -0,0 +1,23 @@ +import { style, styleVariants } from '@vanilla-extract/css'; + +const menuIcon = style({ + width: 24, + height: 24, + cursor: 'pointer', + position: 'absolute', +}); + +export const menuIconVar = styleVariants({ + TAB: [ + menuIcon, + { + right: 40, + }, + ], + MOB: [ + menuIcon, + { + right: 20, + }, + ], +}); diff --git a/src/common/components/Layout/components/Header/index.tsx b/src/common/components/Layout/components/Header/index.tsx index 4027736b..88eef736 100644 --- a/src/common/components/Layout/components/Header/index.tsx +++ b/src/common/components/Layout/components/Header/index.tsx @@ -1,69 +1,63 @@ -import { reset, track } from '@amplitude/analytics-browser'; -import { useContext } from 'react'; +import { useContext, useEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import MakersDarkLogo from '@assets/MakersDarkLogo'; import MakersLogo from '@assets/MakersLogo'; import NowsoptLogo from '@assets/NowsoptLogo'; +import { useDevice } from '@hooks/useDevice'; import { RecruitingInfoContext } from '@store/recruitingInfoContext'; import { ThemeContext } from '@store/themeContext'; -import { MENU_ITEMS, MENU_ITEMS_MAKERS } from './contants'; -import MenuItem from './MenuItem'; -import { container, logo, menuList } from './style.css'; +import Nav from './Nav'; +import MenuList from './Nav/MenuList'; +import { containerSizeVer, containerVar, logoVar } from './style.css'; const Header = () => { + const DEVICE_TYPE = useDevice(); + const [isMenuOpen, setIsMenuOpen] = useState(false); + const handleClickMenuToggle = () => { + setIsMenuOpen((prev) => !prev); + }; + const navigate = useNavigate(); const { pathname } = useLocation(); - const isSignedIn = localStorage.getItem('soptApplyAccessToken'); const { - recruitingInfo: { name, isMakers }, + recruitingInfo: { isMakers }, } = useContext(RecruitingInfoContext); const { isLight } = useContext(ThemeContext); - const menuItems = isMakers ? MENU_ITEMS_MAKERS : MENU_ITEMS; - const handleClickLogo = () => { pathname === '/' ? window.location.reload() : navigate('/'); }; - const handleLogout = () => { - track('click-gnb-logout'); - reset(); - localStorage.removeItem('soptApplyAccessToken'); - localStorage.removeItem('soptApplyAccessTokenExpiredTime'); - pathname === '/' ? window.location.reload() : navigate('/'); - }; + useEffect(() => { + if (DEVICE_TYPE === 'DESK') { + setIsMenuOpen(false); + } + }, [DEVICE_TYPE]); + + const logoVariant = logoVar[DEVICE_TYPE]; return ( <> {isMakers != undefined && ( -
    - - -
    + +