Skip to content

Commit

Permalink
feat: 온보딩 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
leejiho9898 committed Oct 20, 2023
1 parent f5979ba commit 1f14558
Show file tree
Hide file tree
Showing 17 changed files with 390 additions and 2 deletions.
Binary file added apps/jurumarble/public/images/VoteOnboardD01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/jurumarble/public/images/VoteOnboardD02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/jurumarble/public/images/VoteOnboardD03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/jurumarble/public/images/VoteOnboardD04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/jurumarble/public/images/VoteOnboardM01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/jurumarble/public/images/VoteOnboardM02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/jurumarble/public/images/VoteOnboardM03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/jurumarble/public/images/VoteOnboardM04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions apps/jurumarble/public/images/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,12 @@ export { default as DrinkCapacityMedium } from './DrinkCapacityMedium.png';
export { default as DrinkCapacityHigh } from './DrinkCapacityHigh.png';
export { default as ImgScroll } from './ImgScroll.png';
export { default as restaurantImg } from './restaurantImg.png';
export { default as Onboarding } from './onboarding.png';
export { default as DesktopOnboarding1 } from './VoteOnboardD01.png';
export { default as DesktopOnboarding2 } from './VoteOnboardD02.png';
export { default as DesktopOnboarding3 } from './VoteOnboardD03.png';
export { default as DesktopOnboarding4 } from './VoteOnboardD04.png';
export { default as MobileOnboarding1 } from './VoteOnboardM01.png';
export { default as MobileOnboarding2 } from './VoteOnboardM02.png';
export { default as MobileOnboarding3 } from './VoteOnboardM03.png';
export { default as MobileOnboarding4 } from './VoteOnboardM04.png';
Binary file added apps/jurumarble/public/images/onboarding.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions apps/jurumarble/src/app/main/components/Banner.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
'use client';

import { useEffect } from 'react';

import Path from 'lib/Path';
import userStorage from 'lib/utils/userStorage';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { MainBannerImage } from 'public/images';
import styled, { css } from 'styled-components';

function Banner() {
const router = useRouter();
useEffect(() => {
if (!userStorage.get() || !!localStorage.getItem('visited_home')) {
return;
}
router.push(Path.ONBOARDING_PAGE);
localStorage.setItem('visited_home', 'false');
}, []);
return (
<Container>
<Image
Expand Down
26 changes: 26 additions & 0 deletions apps/jurumarble/src/app/onboarding/components/BottomButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use client';

import { Button } from 'components/button';
import { getClassNames } from 'lib/styles/getClassNames';
import { useRouter } from 'next/navigation';

import styles from '../page.module.css';

const BottomButton = () => {
const router = useRouter();
const cx = getClassNames(styles);
return (
<div className={cx('bottom-wrapper')}>
<Button
width="100%"
height="56px"
variant="primary"
onClick={() => router.push('/')}
>
시작하기
</Button>
</div>
);
};

export default BottomButton;
28 changes: 28 additions & 0 deletions apps/jurumarble/src/app/onboarding/page.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.container {
width: 100%;
display: flex;
justify-content: center;
/* background-color: var(--bg_01); */
padding-bottom: 96px;
}

.container img {
width: 100%;
height: 100%;
object-fit: cover;
}

.img-wrapper {
max-width: 720px;
margin: 0 auto;
background-color: var(--white);
}

.bottom-wrapper {
position: fixed;
bottom: 0;
padding: 20px;
width: 100%;
max-width: 480px;
background-color: var(--white);
}
21 changes: 21 additions & 0 deletions apps/jurumarble/src/app/onboarding/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getClassNames } from 'lib/styles/getClassNames';
import Image from 'next/image';
import { Onboarding } from 'public/images';

import BottomButton from './components/BottomButton';
import styles from './page.module.css';

const OnboardingPage = () => {
const cx = getClassNames(styles);

return (
<div className={cx('container')}>
<div className={cx('img-wrapper')}>
<Image src={Onboarding} width={375} height={2112} alt="온보딩" />
</div>
<BottomButton />
</div>
);
};

export default OnboardingPage;
276 changes: 276 additions & 0 deletions apps/jurumarble/src/app/vote/components/OnboardingBottomsheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
import { forwardRef, useRef, useState } from 'react';

import { Portal } from 'components/index';
import { transitions } from 'lib/styles';
import Image, { StaticImageData } from 'next/image';
import {
DesktopOnboarding1,
DesktopOnboarding2,
DesktopOnboarding3,
DesktopOnboarding4,
MobileOnboarding1,
MobileOnboarding2,
MobileOnboarding3,
} from 'public/images';
import { SvgIcX } from 'src/assets/icons/components';
import styled, { css } from 'styled-components';

interface CardProps {
title: string;
description: string;
imgSrc: string | StaticImageData;
}

const Card = forwardRef<HTMLDivElement, CardProps>(
({ title, description, imgSrc }, ref) => {
return (
<CardWrapper ref={ref}>
<div className="title">{title}</div>
<div className="description">{description}</div>
<Image className="img" src={imgSrc} alt="img" />
</CardWrapper>
);
},
);

interface Props {
onToggleOnboarding: () => void;
}

const TAB_LIST = [
{ tabName: '후보 확대', id: 'enlarge' },
{ tabName: '후보 선택', id: 'select' },
{ tabName: '투표 이동', id: 'move' },
{ tabName: '자세히 보기', id: 'detail' },
];

const CARD_LIST = [
{
title: '후보를 확대해서 보기',
description: '마우스를 후보에 올리거나 좌우 방향키를 이용하세요.',
imgSrc: DesktopOnboarding1,
mobileImgSrc: MobileOnboarding1,
},
{
title: '투표 후보를 선택하기',
description: '원하는 후보를 클릭하세요.',
imgSrc: DesktopOnboarding2,
mobileImgSrc: MobileOnboarding2,
},
{
title: '자세한 내용을 확인하기',
description: '스크롤을 하거나 상하 방향키를 이용하세요.',
imgSrc: DesktopOnboarding3,
mobileImgSrc: MobileOnboarding3,
},
{
title: '자세히 보기',
description: '더보기 버튼을 클릭해주세요.',
imgSrc: DesktopOnboarding4,
mobileImgSrc: MobileOnboarding1,
},
];

const OnboardingBottomsheet = ({ onToggleOnboarding }: Props) => {
const [chip, setChip] = useState('mobile');

const card1Ref = useRef<HTMLDivElement>(null);
const card2Ref = useRef<HTMLDivElement>(null);
const card3Ref = useRef<HTMLDivElement>(null);
const card4Ref = useRef<HTMLDivElement>(null);

const cardRefs = [card1Ref, card2Ref, card3Ref, card4Ref];

// 탭 클릭시 ref로 이동하는 함수

const handleTabClick = (index: number) => {
cardRefs[index]?.current?.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'center',
});
};

return (
<Portal selector="#portal">
<BottomSheet>
<Inner>
<Exit onClick={onToggleOnboarding}>
<SvgIcX />
</Exit>
<Title>투표를 좀 더 재밌게 참여해 볼까요?</Title>
<Description>
투표는 여러가지 조작 방법을 통해 참여할 수 있어요.
</Description>
<TabWrapper>
{TAB_LIST.map(({ id, tabName }, index) => (
<Tab
key={id}
active={id === 'enlarge'}
onClick={() => handleTabClick(index)}
>
{tabName}
</Tab>
))}
</TabWrapper>
<ChipWrapper>
<Chip active={chip === 'mobile'} onClick={() => setChip('mobile')}>
모바일
</Chip>
<Chip
active={chip === 'desktop'}
onClick={() => setChip('desktop')}
>
PC
</Chip>
</ChipWrapper>
{CARD_LIST.map(
({ title, description, imgSrc, mobileImgSrc }, index) => (
<Card
key={title}
title={title}
description={description}
imgSrc={chip === 'mobile' ? imgSrc : mobileImgSrc}
ref={cardRefs[index]}
/>
),
)}
</Inner>
<Background onClick={onToggleOnboarding} />
</BottomSheet>
</Portal>
);
};

const BottomSheet = styled.div`
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 9999;
`;

const Inner = styled.div`
position: absolute;
z-index: 9999;
background-color: white;
bottom: 0;
right: 0;
left: 0;
margin: auto;
width: 100%;
max-width: 720px;
height: 90%;
animation: ${transitions.popInFromBottom} 0.4s ease-in-out;
border-radius: 16px 16px 0px 0px;
overflow-y: scroll;
&::-webkit-scrollbar {
display: none;
}
`;

const Background = styled.div`
display: block;
width: 100%;
height: 100%;
background-color: black;
position: absolute;
left: 0;
top: 0;
opacity: 0.4;
`;

const Title = styled.div`
display: flex;
${({ theme }) => css`
${theme.typography.headline02}
`}
justify-content: flex-start;
padding: 64px 20px 0 20px;
`;

const Description = styled.div`
display: flex;
${({ theme }) => css`
${theme.typography.body02}
color: ${theme.colors.black_02};
`}
padding:12px 20px 16px 20px;
`;
// padding: 26px 20px 20px 20px;
const Exit = styled.div`
position: absolute;
top: 26px;
right: 20px;
width: 24px;
height: 24px;
cursor: pointer;
`;

const TabWrapper = styled.div`
display: flex;
padding: 16px 20px 0 20px;
border-bottom: 1px solid ${({ theme }) => theme.colors.line_01};
`;

const Tab = styled.div<{ active: boolean }>`
padding: 16px 10px;
width: 25%;
text-align: center;
cursor: pointer;
${({ active, theme }) =>
active
? css`
${theme.typography.body01}
color: ${({ theme }) => theme.colors.black_01};
border-bottom: 3px solid ${({ theme }) => theme.colors.black_01};
`
: css`
${theme.typography.body02}
color: ${({ theme }) => theme.colors.black_03};
`}
`;

const ChipWrapper = styled.div`
padding: 28px 20px;
display: flex;
gap: 4px;
`;

const Chip = styled.div<{ active: boolean }>`
padding: 10px;
border-radius: 4px;
cursor: pointer;
${({ theme, active }) => css`
${theme.typography.caption_chip}
color: ${active ? theme.colors.white : theme.colors.black_02};
background-color: ${active ? theme.colors.black_02 : theme.colors.bg_01};
`}
`;

const CardWrapper = styled.div`
padding: 0 20px;
margin-bottom: 42px;
.title {
padding-bottom: 4px;
${({ theme }) => css`
${theme.typography.body01}
`}
}
.description {
${({ theme }) => css`
${theme.typography.body_long03}
padding-bottom: 16px;
`}
}
.img {
width: 100%;
height: 100%;
border-radius: 16px;
}
`;

export default OnboardingBottomsheet;
Loading

0 comments on commit 1f14558

Please sign in to comment.