Skip to content

Commit

Permalink
Merge pull request #31 from GlowTales/feat/#8
Browse files Browse the repository at this point in the history
[FEAT] 온보딩 퍼블리싱 및 동작 구현
  • Loading branch information
gominzip authored Aug 24, 2024
2 parents 1381dbc + 79802b7 commit fae8690
Show file tree
Hide file tree
Showing 30 changed files with 576 additions and 49 deletions.
Binary file added public/correct.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 public/learningFinish.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 public/onboardingFinish.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 public/wrong.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/components/common/FinishScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const FinishScreen = () => {
return <div>FinishScreen</div>;
};

export default FinishScreen;
12 changes: 8 additions & 4 deletions src/components/common/NextBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ import { CommonBtn } from "./common.styled";
interface NextBtnProps {
isActive: boolean;
text: string;
width: string;
width?: string;
handleBtn: () => void;
}

const NextBtn = ({ width, isActive, text, handleBtn }: NextBtnProps) => {
const NextBtn = (props: NextBtnProps) => {
return (
<CommonBtn width={width} onClick={handleBtn} isActive={isActive}>
{text}
<CommonBtn
width={props.width}
onClick={props.handleBtn}
isActive={props.isActive}
>
{props.text}
</CommonBtn>
);
};
Expand Down
25 changes: 25 additions & 0 deletions src/components/common/progressBar/ProgressBar.styeld.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import styled from "styled-components";

export const ProgressBarWrapper = styled.div`
display: flex;
align-items: center;
width: 100%;
height: 14px;
border-radius: 20px;
background: #ededed;
margin-top: 2rem;
position: relative;
`;

export const Progress = styled.div<{ $percentage: number }>`
background-color: #ffd600;
border-top-left-radius: 20px;
border-bottom-left-radius: 20px;
border-top-right-radius: ${(props) => (props.$percentage >= 99 ? 20 : 0)}px;
border-bottom-right-radius: ${(props) =>
props.$percentage >= 99 ? 20 : 0}px;
height: 100%;
width: ${(props) => props.$percentage}%;
transition: width 0.5s ease-in-out;
position: relative;
`;
15 changes: 15 additions & 0 deletions src/components/common/progressBar/ProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as S from "./ProgressBar.styeld";

interface ProgressBarProps {
percentage: number;
}

const ProgressBar = (props: ProgressBarProps) => {
return (
<S.ProgressBarWrapper>
<S.Progress $percentage={props.percentage} />
</S.ProgressBarWrapper>
);
};

export default ProgressBar;
16 changes: 16 additions & 0 deletions src/components/common/selectOption/SelectBtn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { SelectBtnProps } from "@type/selectList";
import * as S from "./SelectOptionList.styled";
import { colorSets } from "@utils/defaultData";

const SelectBtn = (props: SelectBtnProps) => {
const colorSet = colorSets[props.colorName];

return (
<S.SelectBtnWrapper $colorSet={colorSet} onClick={props.onClick}>
{props.text}
{props.imgURL && <S.SelectIcon src={props.imgURL} alt="선택지" />}
</S.SelectBtnWrapper>
);
};

export default SelectBtn;
35 changes: 35 additions & 0 deletions src/components/common/selectOption/SelectOptionList.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ColorSet } from "@type/selectList";
import styled from "styled-components";

export const SelectOptionContainer = styled.div<{ width?: string }>`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
gap: 1rem;
width: ${({ width }) => (width ? width : "100%")};
`;

export const SelectBtnWrapper = styled.div<{ $colorSet: ColorSet }>`
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 1.5rem;
box-sizing: border-box;
width: 100%;
height: 5rem;
font-size: 2rem;
font-weight: 700;
background-color: ${({ $colorSet }) => $colorSet.background};
color: ${({ $colorSet }) => $colorSet.color};
border: 1px solid ${({ $colorSet }) => $colorSet.border};
border-radius: 6px;
`;

export const SelectIcon = styled.img`
width: 2.3rem;
height: 2.3rem;
margin-right: 10px;
vertical-align: middle;
`;
52 changes: 52 additions & 0 deletions src/components/common/selectOption/SelectOptionList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { SelectListProps } from "@type/selectList";
import SelectBtn from "./SelectBtn";
import * as S from "./SelectOptionList.styled";

const SelectOptionList = (props: SelectListProps) => {
const getColorName = (state: string) => {
switch (state) {
case "correct":
return "green";
case "wrong":
return "red";
case "selected":
return "yellow";
case "default":
default:
return "gray";
}
};

const getImgURL = (state: string) => {
switch (state) {
case "correct":
return "/correct.png";
case "wrong":
return "/wrong.png";
case "selected":
return "";
default:
return "";
}
};

const handleSelect = (value: string | number | null) => {
props.setter(value);
};

return (
<S.SelectOptionContainer width={props.width}>
{props.selectList.map((element) => (
<SelectBtn
key={element.text}
text={element.text}
colorName={getColorName(element.state)}
imgURL={getImgURL(element.state)}
onClick={() => handleSelect(element.value)}
/>
))}
</S.SelectOptionContainer>
);
};

export default SelectOptionList;
10 changes: 5 additions & 5 deletions src/components/login/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CommonWrapper } from "@common/common.styled";
import { LoginBtn, LogoBox, Title } from "./Login.styled";
import * as S from "./Login.styled";
import { kakaoURL } from "./kakaoLoginConfig";

const Login = () => {
Expand All @@ -9,17 +9,17 @@ const Login = () => {

return (
<CommonWrapper>
<Title>{`{메인소개 멘트}`}</Title>
<LogoBox>{`{로고이미지}`}</LogoBox>
<LoginBtn>
<S.Title>{`{메인소개 멘트}`}</S.Title>
<S.LogoBox>{`{로고이미지}`}</S.LogoBox>
<S.LoginBtn>
<img id="login-bubble" src="./loginBubble.png" alt="3초 로그인" />
<img
id="login-btn"
src="./loginbtn.png"
alt="카카오 로그인 버튼"
onClick={toLogin}
/>
</LoginBtn>
</S.LoginBtn>
</CommonWrapper>
);
};
Expand Down
88 changes: 88 additions & 0 deletions src/components/onboarding/AgeInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { colorSets } from "@utils/defaultData";
import React, { ChangeEvent, KeyboardEvent } from "react";
import styled from "styled-components";

interface AgeInputProps {
age: string | number | null;
setter: (value: number | null) => void;
}

const AgeInput = ({ age, setter }: AgeInputProps) => {
const [isEditing, setIsEditing] = React.useState<boolean>(false);

const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
const newValue = value === "" ? null : Number(value);
setter(newValue);
};

const handleBlur = () => {
setIsEditing(false);
};

const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
if (event.key === "Enter") {
setIsEditing(false);
}
};

return (
<Container $age={age}>
{isEditing ? (
<Input
type="number"
value={age ?? ""}
onChange={handleChange}
onBlur={handleBlur}
onKeyDown={handleKeyDown}
autoFocus
/>
) : (
<Text onClick={() => setIsEditing(true)}>
{age !== null ? `만 ${age}세` : "만"}
</Text>
)}
</Container>
);
};

export default AgeInput;

const Container = styled.div<{ $age?: any }>`
display: flex;
align-items: center;
padding: 0 1rem;
box-sizing: border-box;
width: 50%;
height: 4.5rem;
font-size: 2rem;
font-weight: 700;
border: 1px solid
${({ $age }) =>
$age ? colorSets["yellow"].border : colorSets["gray"].border};
background-color: ${({ $age }) =>
$age ? colorSets["yellow"].background : "none"};
border-radius: 6px;
position: relative;
`;

const Text = styled.div`
cursor: pointer;
width: 100%;
line-height: 4.5rem;
padding: 0 1rem;
`;

const Input = styled.input`
font-size: 2rem;
width: 100%;
box-sizing: border-box;
padding: 0 1.5rem;
border: none;
border-radius: 6px;
position: absolute;
top: 0;
left: 0;
height: 100%;
background-color: transparent;
`;
21 changes: 21 additions & 0 deletions src/components/onboarding/SelectAge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import AgeInput from "./AgeInput";
import * as S from "./onboarding.styled";

interface SelectAgeProps {
age: string | number | null;
setter: (value: string | number | null) => void;
}

const SelectAge = ({ age, setter }: SelectAgeProps) => {
return (
<S.Container>
<S.Title>학습자의 나이는 몇살인가요?</S.Title>
<S.SubContainer>
<S.SubTitle>만 나이</S.SubTitle>
<AgeInput age={age} setter={setter} />
</S.SubContainer>
</S.Container>
);
};

export default SelectAge;
21 changes: 21 additions & 0 deletions src/components/onboarding/SelectLanguage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Dropdown from "@components/common/dropDown/Dropdown";
import * as S from "./onboarding.styled";
import { nationElements } from "@utils/defaultData";

interface SelectLanguageProps {
setter: (value: string | number | null) => void;
}

const SelectLanguage = ({ setter }: SelectLanguageProps) => {
return (
<S.Container>
<S.Title>어떤 언어를 배우시나요?</S.Title>
<S.SubContainer>
<S.SubTitle>언어</S.SubTitle>
<Dropdown selectList={nationElements} setter={setter} />
</S.SubContainer>
</S.Container>
);
};

export default SelectLanguage;
39 changes: 39 additions & 0 deletions src/components/onboarding/SelectLevel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as S from "./onboarding.styled";
import useSelectLevel from "@hooks/useSelectLevel";
import { nationElements } from "@utils/defaultData";
import SelectOptionList from "@common/selectOption/SelectOptionList";

interface SelectLevelProps {
languageId: number | string;
setter: (value: string | number | null) => void;
}

const SelectLevel = ({ languageId, setter }: SelectLevelProps) => {
const { selectedLevel, setSelectedLevel, levelOptions } = useSelectLevel();
const language = nationElements.find(
(element) => element.value === languageId
);

const handleOptionChange = (value: string | number | null) => {
setSelectedLevel(value);
setter(value);
};

return (
<S.Container>
<S.Title>{language?.text}를 얼마나 알고 계신가요?</S.Title>
<S.SubContainer>
<SelectOptionList
selectList={levelOptions.map((option) => ({
text: option.text,
value: option.value,
state: selectedLevel === option.value ? "selected" : "default",
}))}
setter={handleOptionChange}
/>
</S.SubContainer>
</S.Container>
);
};

export default SelectLevel;
Loading

0 comments on commit fae8690

Please sign in to comment.