Skip to content

Commit

Permalink
[FEAT] 모바일 사이드 nav bar 구현 (#129)
Browse files Browse the repository at this point in the history
* feat: 모바일 사이드 네이바 구현

* feat: 700이상으로 width 늘어날때 모바일 버전 헤더 지우기

* feat: 코드리뷰 수정

* feat: 코드리뷰 수정
  • Loading branch information
haejinyun authored Nov 21, 2024
1 parent 689f557 commit 1a4edda
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 111 deletions.
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@types/react-slick": "^0.23.13",
"@vanilla-extract/css": "^1.15.5",
"@vanilla-extract/next-plugin": "^2.4.5",
"@vanilla-extract/recipes": "^0.5.5",
"axios": "^1.7.7",
"dompurify": "^3.1.7",
"lucide-react": "^0.460.0",
Expand Down
13 changes: 0 additions & 13 deletions src/components/HeaderSection/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@

import { useSearchParams } from 'next/navigation';

// 현재 알림 기능 api 없음으로 주석처리
// import Alarm from '@/components/Header/components/Alarm';
// import SlideSideNav from '@/components/SlideSideNav';
// import useBooleanState from '@/hooks/useBooleanState';

import useGetCategory from '@/apis/queryHooks/category/useGetCategory';
import { Category } from '@/apis/types/category';
import MaxLayout from '@/components/MaxLayout';
Expand All @@ -17,27 +12,19 @@ import UserHeader from '../UserHeader';
import * as S from './Header.css';

function Header() {
// const { value, setTrue, setFalse } = useBooleanState(false);
const searchParams = useSearchParams();
const pickCategory = Number(searchParams.get('categoryId'));

const { data } = useGetCategory({ targetCategoryId: pickCategory });

// console.log(data?.result_data);
// console.log('newData', newData);

if (!data) return null;

return (
<header className={S.stickyHeader}>
<MaxLayout>
<div className={S.container}>
<UserHeader />
{/* <UserHeader setTrue={setTrue} /> */}
<CategoryHeader data={data as Category[]} />
{/* <SlideSideNav isOpen={value} onClose={setFalse}>
<Alarm content="준비중입니다!" />
</SlideSideNav> */}
</div>
</MaxLayout>
</header>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,62 @@
'use client';

import { useSearchParams } from 'next/navigation';
import { useEffect } from 'react';

// 현재 알림 기능 api 없음으로 주석처리
// import Alarm from '@/components/Header/components/Alarm';
// import SlideSideNav from '@/components/SlideSideNav';
// import useBooleanState from '@/hooks/useBooleanState';
import { useSearchParams } from 'next/navigation';

import useGetUser from '@/apis/queryHooks/User/useGetUser';
import useGetCategory from '@/apis/queryHooks/category/useGetCategory';
import { Category } from '@/apis/types/category';
import SlideSideNav from '@/components/SlideSideNav';
import useBooleanState from '@/hooks/useBooleanState';

import MaxLayout from '../../../MaxLayout';
import CategoryHeader from '../CategoryHeader';
import MobileSlideNav from '../MobileSlideNav';
import MobileUserHeader from '../MobileUserHeader';

import * as S from './MobileHeader.css';

function MobileHeader() {
const searchParams = useSearchParams();
const pickCategory = Number(searchParams.get('categoryId'));
const { value: navState, setTrue: openNav, setFalse: closeNav } = useBooleanState(false);

const { data } = useGetCategory({ targetCategoryId: pickCategory });
const { data: userInfo } = useGetUser();

useEffect(() => {
const handleResize = () => {
if (window.innerWidth > 700) {
closeNav();
}
};

window.addEventListener('resize', handleResize);

return () => {
window.removeEventListener('resize', handleResize);
};
}, [closeNav]);

if (!data) return null;

return (
<header className={S.stickyHeader}>
<MaxLayout>
<div className={S.container}>
<MobileUserHeader />
<MobileUserHeader openNav={openNav} />
<CategoryHeader data={data as Category[]} />
<SlideSideNav isOpen={navState} onClose={closeNav} type="mobile">
<MobileSlideNav
isLogin={!!userInfo}
userProfileImage={userInfo?.profile_image_url}
userNickname={userInfo?.nick_name}
userEmail={userInfo?.email}
userHeartCount={userInfo?.like_count}
onClose={closeNav}
/>
</SlideSideNav>
</div>
</MaxLayout>
</header>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { style, styleVariants } from '@vanilla-extract/css';

import colors from '@/styles/color';

export const container = style({ padding: '0 16px', display: 'flex', flexDirection: 'column' });

export const logo = style({ fontSize: '24px', color: colors.primary9, marginBottom: '24px' });

export const division = style({
padding: '0px',
height: '1px',
border: 'none',
backgroundColor: 'rgb(234, 237, 239)',
margin: '20px 0px',
});

export const hrTitle = style({ fontSize: '14px', color: colors.gray12 });

export const userSection = style({ display: 'flex', flexDirection: 'column', gap: '8px' });

export const userWrapper = style({
display: 'flex',
gap: '4px',
});

export const profileImage = style({
borderRadius: '50%',
});

export const authWrapper = style({
display: 'flex',
gap: '8px',
marginTop: '16px',
});

export const userName = style({
fontSize: '14px',
lineHeight: '20px',
color: colors.gray11,
});

export const buttonBase = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
width: '100%',
height: '40px',
borderRadius: '4px',
});

export const likeCount = style({
display: 'flex',
justifyContent: 'center',
color: colors.primary11,
backgroundColor: colors.gray3,
minWidth: '30px',
padding: '4px 8px',
borderRadius: '20px',
marginLeft: '12px',
});

export const normalNavButtonBase = style({
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
width: '100%',
textAlign: 'left',
height: '40px',
borderRadius: '4px',
fontSize: '14px',
color: colors.gray11,
padding: '0 8px',
boxSizing: 'border-box',
':hover': {
backgroundColor: colors.gray3,
},
});

export const bottomNavButtonBase = style({
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
width: '100%',
textAlign: 'left',
borderRadius: '4px',
fontSize: '12px',
color: colors.gray11,
padding: '4px 8px',
boxSizing: 'border-box',
});

export const button = styleVariants({
login: [
buttonBase,
{
fontSize: '12px',
color: colors.primary9,
backgroundColor: colors.white,
border: `1px solid ${colors.primary9}`,
},
],
join: [
buttonBase,
{
fontSize: '12px',
backgroundColor: colors.primary9,
color: colors.white,
border: `1px solid ${colors.primary9}`,
},
],
uploadAuction: [
buttonBase,
{
fontSize: '14px',
color: colors.primary9,
backgroundColor: colors.primary3,
border: `1px solid ${colors.primary9}`,
},
],
});
109 changes: 109 additions & 0 deletions src/components/HeaderSection/components/MobileSlideNav/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { UserIcon } from 'lucide-react';
import Image from 'next/image';
import Link from 'next/link';

import usePostLogout from '@/apis/queryHooks/Auth/usePostLogout';
import colors from '@/styles/color';

import * as S from './MobileSlideNav.css';

interface MobileSlideNavProps {
isLogin: boolean;
onClose: () => void;
userProfileImage?: string | null;
userNickname?: string | null;
userEmail?: string;
userHeartCount?: number;
}

function MobileSlideNav({
isLogin,
onClose,
userProfileImage,
userNickname,
userEmail,
userHeartCount,
}: MobileSlideNavProps) {
const { mutate: logout } = usePostLogout();

const handleLogout = () => {
logout();
onClose();
};

return (
<div className={S.container}>
<Link href="/" onClick={onClose}>
<div className={S.logo}>OMOCHA</div>
</Link>
<div>
{isLogin ? (
<div className={S.userSection}>
<div className={S.userWrapper}>
{userProfileImage ? (
<Image
className={S.profileImage}
src={`${process.env.NEXT_PUBLIC_S3_URL}${userProfileImage}`}
width={100}
height={100}
priority
alt="프로필 이미지"
/>
) : (
<UserIcon
size={20}
strokeWidth={1}
stroke={colors.gray9}
style={{ borderRadius: '50%', border: `1px solid ${colors.gray9}` }}
/>
)}
<span className={S.userName}>{userNickname}</span>
</div>
<div className={S.userWrapper}>
<span className={S.userName}>{userEmail}</span>
</div>
{/* <Link href="/mypage/heart" onClick={onClose}>
<div className={S.userWrapper}>
<span className={S.userName}>찜 개수: </span>
<span className={S.userName}>{userHeartCount}</span>
</div>
</Link> */}
</div>
) : (
<div className={S.authWrapper}>
<Link href="/login" className={S.button.login} onClick={onClose}>
로그인
</Link>
<Link href="/join" className={S.button.join} onClick={onClose}>
회원가입
</Link>
</div>
)}
</div>
<hr className={S.division} />
<Link href="/create" className={S.button.uploadAuction} onClick={onClose}>
경매 등록
</Link>
<hr className={S.division} />
<Link href="/mypage/profile" className={S.normalNavButtonBase} onClick={onClose}>
회원 정보
</Link>
<Link href="/mypage/heart" className={S.normalNavButtonBase} onClick={onClose}>
<>
<div className={S.likeCount}>{userHeartCount}</div>
</>
</Link>
<Link href="/mypage/record" className={S.normalNavButtonBase} onClick={onClose}>
거래 내역
</Link>
<hr className={S.division} />
{isLogin && (
<button className={S.bottomNavButtonBase} type="button" onClick={handleLogout}>
로그아웃
</button>
)}
</div>
);
}

export default MobileSlideNav;
Loading

0 comments on commit 1a4edda

Please sign in to comment.