-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Feat/#36 filter modal
- Loading branch information
Showing
14 changed files
with
368 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
src/components/CardCollection/FilterModal/IntimacySlider/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Range } from "react-range"; | ||
|
||
import { RangeTrack, St } from "./style"; | ||
|
||
interface IntimacySliderProps { | ||
min: number; | ||
max: number; | ||
step: number; | ||
|
||
price: number[]; | ||
onChange: (values: number[]) => void; | ||
} | ||
|
||
export default function IntimacySlider(props: IntimacySliderProps) { | ||
const { min, max, step, price, onChange } = props; | ||
|
||
return ( | ||
<St.IntimacySlider> | ||
<Range | ||
step={step} | ||
min={min} | ||
max={max} | ||
values={price} | ||
onChange={onChange} | ||
renderTrack={({ props, children }) => ( | ||
<RangeTrack {...props} min={min} max={max} price={price}> | ||
{children} | ||
</RangeTrack> | ||
)} | ||
renderThumb={({ props }) => <St.RangeThumb {...props} />} | ||
/> | ||
</St.IntimacySlider> | ||
); | ||
} |
44 changes: 44 additions & 0 deletions
44
src/components/CardCollection/FilterModal/IntimacySlider/style.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { getTrackBackground } from "react-range"; | ||
import styled from "styled-components"; | ||
|
||
interface RangeTrackProps { | ||
min: number; | ||
max: number; | ||
price: number[]; | ||
} | ||
|
||
export const St = { | ||
IntimacySlider: styled.div` | ||
height: 2.2rem; | ||
margin: 1.2rem 0 0.2rem; | ||
padding: 0 1.5rem; | ||
`, | ||
|
||
RangeThumb: styled.div` | ||
position: absolute; | ||
top: 0; | ||
width: 1.8em; | ||
height: 1.8rem; | ||
border-radius: 2.4rem; | ||
background-color: ${({ theme }) => theme.colors.white}; | ||
box-shadow: 0 0.1rem 0.4rem 0.1rem rgba(0, 0, 0, 0.25); | ||
`, | ||
}; | ||
|
||
export const RangeTrack = styled.div` | ||
position: relative; | ||
height: 0.8rem; | ||
width: 100%; | ||
border-radius: 0.4rem; | ||
background: ${(props: RangeTrackProps) => | ||
getTrackBackground({ | ||
values: props.price, | ||
colors: ["#19BE7E", "#ffffff"], | ||
min: props.min, | ||
max: props.max, | ||
})}; | ||
box-shadow: inset 0 0.1rem 0.1rem rgba(0, 0, 0, 0.25); | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import React, { useEffect, useState } from "react"; | ||
|
||
import Modal from "../../common/Modal"; | ||
import IntimacySlider from "./IntimacySlider"; | ||
import { St } from "./style"; | ||
|
||
interface FilterModalProps { | ||
closeHandler: () => void; | ||
} | ||
|
||
type FilterTags = { | ||
type: string; | ||
tags: string[]; | ||
}; | ||
|
||
const filterTags: FilterTags[] = [ | ||
{ | ||
type: "성별", | ||
tags: ["남", "여"], | ||
}, | ||
{ | ||
type: "연령대", | ||
tags: ["10대", "20대", "30대"], | ||
}, | ||
{ | ||
type: "술자리 유형", | ||
tags: ["개인", "커플", "친구", "단체"], | ||
}, | ||
]; | ||
|
||
const intimacyTags: string[] = ["상관없음", "새로워요", "친근해요", "절친해요"]; | ||
|
||
export default function FilterModal(props: FilterModalProps) { | ||
const { closeHandler } = props; | ||
const [checkedTags, setCheckedTags] = useState<Set<string>>(new Set()); // 체크한 태그들을 저장할 state | ||
const [intimacyValues, setIntimacyValues] = useState<number[]>([0]); // 친밀도 value | ||
// 태그를 눌렀을 때 함수 | ||
const toggleTag = (_tag: string) => { | ||
const tempCheckedTags = new Set([...checkedTags]); | ||
tempCheckedTags.has(_tag) ? tempCheckedTags.delete(_tag) : tempCheckedTags.add(_tag); | ||
setCheckedTags(tempCheckedTags); | ||
}; | ||
// 추천 시작하기를 눌렀을 때, 태그 정보들과 친밀도 정보를 담아주고 창닫기 | ||
const submitFilter = () => { | ||
const tempCheckedTags = new Set(checkedTags); | ||
tempCheckedTags.add(intimacyTags[intimacyValues[0]]); | ||
setCheckedTags(tempCheckedTags); | ||
closeHandler(); | ||
}; | ||
|
||
useEffect(() => { | ||
console.log(checkedTags); | ||
}, [checkedTags]); | ||
|
||
return ( | ||
<Modal closeHandler={closeHandler}> | ||
<St.ModalContentsWrapper> | ||
{filterTags.map((filterTag, idx) => ( | ||
<React.Fragment key={`filter-${idx}`}> | ||
<St.FilterTitle>{filterTag.type}</St.FilterTitle> | ||
<St.FilterTagsWrapper> | ||
{filterTag.tags.map((tag, index) => ( | ||
<St.FilterTag key={index} isactive={checkedTags.has(tag)} onClick={() => toggleTag(tag)}> | ||
{tag} | ||
</St.FilterTag> | ||
))} | ||
</St.FilterTagsWrapper> | ||
</React.Fragment> | ||
))} | ||
|
||
<St.FilterIntimacyWrapper> | ||
<St.FilterTitle>친밀도</St.FilterTitle> | ||
<IntimacySlider | ||
min={0} | ||
max={3} | ||
step={1} | ||
price={intimacyValues} | ||
onChange={(values: number[]) => { | ||
setIntimacyValues(values); | ||
}} | ||
/> | ||
<St.FilterIntimacyTagsWrapper> | ||
{intimacyTags.map((tag, index) => ( | ||
<St.FilterIntimacyTag isactive={index === intimacyValues[0]} key={index}> | ||
{tag} | ||
</St.FilterIntimacyTag> | ||
))} | ||
</St.FilterIntimacyTagsWrapper> | ||
</St.FilterIntimacyWrapper> | ||
</St.ModalContentsWrapper> | ||
|
||
<St.SubmitBtnWrapper> | ||
<St.SubmitBtn onClick={submitFilter} type="submit"> | ||
추천 시작하기 | ||
</St.SubmitBtn> | ||
</St.SubmitBtnWrapper> | ||
</Modal> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import styled from "styled-components"; | ||
|
||
export const St = { | ||
ModalContentsWrapper: styled.div` | ||
padding-top: 3.6rem; | ||
`, | ||
|
||
CloseBtn: styled.button` | ||
background-image: IcModalCloseBtn; | ||
`, | ||
|
||
FilterTitle: styled.strong` | ||
${({ theme }) => theme.fonts.body7} | ||
color: ${({ theme }) => theme.colors.black}; | ||
`, | ||
|
||
FilterTagsWrapper: styled.ul` | ||
display: flex; | ||
align-items: center; | ||
gap: 1rem; | ||
margin: 0.8rem 0 2.2rem; | ||
`, | ||
|
||
FilterTag: styled.li<{ isactive: boolean }>` | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
align-items: center; | ||
width: 6.2rem; | ||
height: 2.6rem; | ||
${({ theme }) => theme.fonts.btn3} | ||
background-color: ${({ isactive, theme }) => (isactive ? theme.colors.green : theme.colors.white)}; | ||
color: ${({ isactive, theme }) => (isactive ? theme.colors.white : theme.colors.black)}; | ||
border-radius: 6.3rem; | ||
`, | ||
|
||
FilterIntimacyWrapper: styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
margin-bottom: 4rem; | ||
`, | ||
|
||
FilterIntimacyRange: styled.input` | ||
margin: 1.2rem 0 0.2rem; | ||
`, | ||
|
||
FilterIntimacyTagsWrapper: styled.ul` | ||
display: flex; | ||
justify-content: space-between; | ||
`, | ||
|
||
FilterIntimacyTag: styled.li<{ isactive: boolean }>` | ||
${({ theme }) => theme.fonts.caption4} | ||
color: ${({ isactive, theme }) => (isactive ? theme.colors.green : theme.colors.gray600)}; | ||
`, | ||
|
||
SubmitBtnWrapper: styled.div` | ||
display: flex; | ||
justify-content: center; | ||
`, | ||
|
||
SubmitBtn: styled.button` | ||
${({ theme }) => theme.fonts.btn2}; | ||
background-color: ${({ theme }) => theme.colors.black}; | ||
color: ${({ theme }) => theme.colors.white}; | ||
width: 17.1rem; | ||
height: 3.3rem; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
border-radius: 6.6rem; | ||
`, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,25 @@ | ||
import { useState } from "react"; | ||
|
||
import Header from "../common/Header"; | ||
import CardSlider from "./CardSlider"; | ||
import FilterModal from "./FilterModal"; | ||
import { St } from "./style"; | ||
|
||
export default function CardCollection() { | ||
const [isOpened, setIsOpened] = useState<boolean>(false); | ||
|
||
const openModal = () => { | ||
setIsOpened(true); | ||
}; | ||
const closeModal = () => { | ||
setIsOpened(false); | ||
}; | ||
|
||
return ( | ||
<St.MainPage> | ||
<Header /> | ||
<CardSlider /> | ||
<CardSlider openHandler={openModal} /> | ||
{isOpened && <FilterModal closeHandler={closeModal} />} | ||
</St.MainPage> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { IcModalCloseBtn } from "../../../asset/icon"; | ||
import { St } from "./style"; | ||
|
||
type ModalContents = { | ||
closeHandler: () => void; | ||
children: React.ReactNode; | ||
}; | ||
|
||
export default function Modal({ closeHandler, children }: ModalContents) { | ||
return ( | ||
<St.Root> | ||
<St.Modal> | ||
<St.CloseBtn type="button"> | ||
<IcModalCloseBtn aria-label="필터 모달 닫기" onClick={closeHandler} /> | ||
</St.CloseBtn> | ||
<St.ModalContents>{children}</St.ModalContents> | ||
</St.Modal> | ||
</St.Root> | ||
); | ||
} |
Oops, something went wrong.