Skip to content

Commit

Permalink
[Feat] 필터 메뉴 UI 구현 #32
Browse files Browse the repository at this point in the history
  • Loading branch information
soonki-98 committed Nov 27, 2022
1 parent 5906901 commit 0a25955
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 7 deletions.
4 changes: 4 additions & 0 deletions src/assets/icons/close.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
134 changes: 127 additions & 7 deletions src/components/pages/map/FilterMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,64 @@
import { filterAtom } from '@recoil/map';
import Image from 'next/image';
import { useEffect, useRef } from 'react';
import { useRecoilState } from 'recoil';
import styled, { css } from 'styled-components';
import closeImg from '../../../assets/icons/close.svg';
import FilterSection from './FilterSection';

function FilterMenu() {
const [filterState, setFilterState] = useRecoilState(filterAtom);
const filterRef = useRef<HTMLDivElement>(null);

const closeFilter = (e: MouseEvent) => {
const closeClickOutSide = (e: MouseEvent) => {
if (!filterRef.current) return;
if (filterState.isOpen && !filterRef.current.contains(e.target as HTMLElement)) {
setFilterState({ ...filterState, isOpen: false });
closeFilter();
}
};

const closeFilter = () => {
setFilterState({ ...filterState, isOpen: false });
};

useEffect(() => {
document.addEventListener('click', closeFilter);
return () => document.removeEventListener('click', closeFilter);
document.addEventListener('click', closeClickOutSide);
return () => document.removeEventListener('click', closeClickOutSide);
}, [filterState.isOpen]);
return <Wrapper {...filterState} ref={filterRef}></Wrapper>;
return (
<Wrapper {...filterState} ref={filterRef}>
<Title>
<LogoBox onClick={closeFilter}>
<Image src={closeImg} className="close-icon" />
</LogoBox>
<h1>필터</h1>
</Title>
<SectionWrapper>
<FilterSection filterType="mood">
<MoodFilter>시끌벅적한</MoodFilter>
<MoodFilter>고급스러운</MoodFilter>
<MoodFilter>격식있는</MoodFilter>
<MoodFilter>이색적인</MoodFilter>
<MoodFilter>깔끔한</MoodFilter>
<MoodFilter>조용한</MoodFilter>
<MoodFilter>뷰가 예쁜</MoodFilter>
</FilterSection>
<FilterSection filterType="drink">
<DrinkFilter />
<DrinkFilter />
<DrinkFilter />
<DrinkFilter />
<DrinkFilter />
</FilterSection>
</SectionWrapper>
<ButtonWrapper>
<Button className="cancel" onClick={closeFilter}>
취소
</Button>
<Button className="submit">적용</Button>
</ButtonWrapper>
</Wrapper>
);
}

export default FilterMenu;
Expand All @@ -27,12 +67,12 @@ const Wrapper = styled.div<{ isOpen: boolean }>`
position: fixed;
bottom: 0;
width: 100%;
height: 80vh;
background-color: #fff;
background: #fafafa;
z-index: 500;
transition: 0.5s;
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.15);
border-radius: 16px 16px 0px 0px;
max-height: 80vh;
${({ isOpen }) => {
if (isOpen) {
return css`
Expand All @@ -45,3 +85,83 @@ const Wrapper = styled.div<{ isOpen: boolean }>`
}
}}
`;

const Title = styled.header`
position: relative;
padding: 12px;
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.15);
color: #000;
text-align: center;
background: #fff;
border-radius: 16px 16px 0px 0px;
h1 {
font-size: 18px;
line-height: 24px;
margin: 0;
}
.close-icon {
position: absolute;
top: 50%;
left: 16px;
z-index: 400;
width: 18px;
height: 18px;
}
`;

const LogoBox = styled.button`
position: absolute;
top: 50%;
transform: translateY(-50%);
background: #fff;
left: 16px;
border: 1px solid;
cursor: pointer;
`;

const SectionWrapper = styled.div`
padding: 16px;
`;

const DrinkFilter = styled.li`
aspect-ratio: 1/1;
background-color: #fff;
border: 1px solid #f0f0f0;
border-radius: 50%;
`;

const MoodFilter = styled.li`
padding: 8px 16px;
border: 1px solid #f0f0f0;
background-color: #fff;
border-radius: 24px;
color: #000;
font-weight: 500;
font-size: 12px;
line-height: 16px;
`;

const ButtonWrapper = styled.div`
display: flex;
gap: 16px;
justify-content: center;
align-items: center;
padding: 16px;
`;

const Button = styled.button`
width: 100%;
padding: 14px 0;
border-radius: 26px;
border: 1px solid #f0f0f0;
cursor: pointer;
&.cancel {
background: #f0f0f0;
color: #000;
}
&.submit {
background: #715ae4;
color: #f0f0f0;
}
`;
66 changes: 66 additions & 0 deletions src/components/pages/map/FilterSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import styled, { css } from 'styled-components';

interface IFilterSectionProps {
filterType: 'mood' | 'drink';
children: React.ReactNode;
}

function FilterSection({ filterType, children }: IFilterSectionProps) {
return (
<Wrapper>
<FilterName>{filterType === 'mood' ? '술집 분위기' : '술 종류'}</FilterName>
<FilterList filterType={filterType}>{children}</FilterList>
</Wrapper>
);
}

export default FilterSection;

const Wrapper = styled.section`
padding: 8px 0 24px 0;
border-bottom: 0.5px solid #d9d9d9;
`;

const FilterName = styled.h2`
margin: 0;
color: #000;
font-weight: 400;
font-size: 16px;
line-height: 24px;
`;

const FilterList = styled.ul<{ filterType: 'mood' | 'drink' }>`
margin-top: 16px;
${({ filterType }) => {
switch (filterType) {
case 'drink':
return css`
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 26px;
`;
case 'mood':
return css`
display: flex;
flex-wrap: wrap;
gap: 12px;
`;
default:
return css`
gap: 12px;
`;
}
}}
`;

const ButtonWrapper = styled.div`
display: flex;
gap: 16px;
justify-content: center;
align-items: center;
`;

const Button = styled.button`
width: 100%;
padding: 14px 0;
`;

0 comments on commit 0a25955

Please sign in to comment.