[React + Typescript + emotion] Dropdown 컴포넌트 제작
2024. 8. 13. 17:10
기본적으로 dropdown컴포넌트에 필요한 props로는 options와 선택한 키값(selectedKey)과 변경되는 handleChange 함수가 필요했다.
그리고 완성작엔 드롭다운 하나만 보여주지만, 실제론 여러 개를 사용 중이라
다른 드롭다운을 클릭하면 기존껀 꺼져야 하고, 뒷 배경을 클릭해도 꺼지게 만들고 싶었다.
// Dropdown.tsx
import { useEffect, useRef, useState } from "react";
import * as S from "./Dropdown.styled";
import { CheckIcon, DropdownArrowIcon } from "assets";
interface DropdownProps {
options: readonly { label: string; key: string }[];
handleChange: (key: string) => void;
selectedKey?: string;
}
const Dropdown = ({ options, handleChange, selectedKey }: DropdownProps) => {
const [isOpen, setIsOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState<string | null>(null);
const dropMenuRef = useRef<HTMLDivElement | null>(null);
// 다른 구역 선택 시, 기존 dropdown close
useEffect(() => {
const handleClickOutside = (e: Event) => {
const target = e.target as HTMLElement;
if (!dropMenuRef?.current) return;
if (isOpen && !dropMenuRef?.current.contains(target)) {
setIsOpen(false);
}
};
document.addEventListener("click", handleClickOutside);
return () => document.removeEventListener("click", handleClickOutside);
}, [isOpen]);
useEffect(() => {
// 받아온 데이터 선택값
const getOption = options.find((option) => option.key === selectedKey);
if (getOption) {
setSelectedOption(getOption.label);
}
}, [selectedKey, options]);
const handleToggle = () => {
setIsOpen(!isOpen);
};
const handleSelect = (label: string, key: string) => {
setSelectedOption(label);
handleChange && handleChange(key);
setIsOpen(false);
};
return (
<S.DropdownContainer ref={dropMenuRef}>
<S.SelectContainer onClick={handleToggle}>
<S.SelectedValue> {selectedOption || "선택하기"}</S.SelectedValue>
<DropdownArrowIcon css={S.arrowIcon} />
{isOpen && (
<S.DropdownList>
{options.map((option, index) => (
<S.DropdownItem
key={index}
onClick={() => handleSelect(option.label, option.key)}
>
{option.label}
<CheckIcon css={S.checkIcon} />
</S.DropdownItem>
))}
</S.DropdownList>
)}
</S.SelectContainer>
</S.DropdownContainer>
);
};
export default Dropdown;
css에서는 hover 될 때, 체크표시가 같이 되어야 하는데, hover가 아닐 땐 체크표시를 white로 해서, 안 보이는 척(ㅎㅎ) 만들었다.
배경색이 있다면 배경색과 동일하게 변경하면 될 것 같다.
// Dropdown.styled.ts
import { css } from "@emotion/react";
import styled from "@emotion/styled";
export const DropdownContainer = styled.div`
${({ theme }) => css`
width: 320px;
height: 48px;
border: 1px solid ${theme.colors.gray600};
border-radius: 6px;
display: flex;
position: relative;
flex-direction: column;
`}
`;
export const SelectContainer = styled.div`
display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
cursor: pointer;
`;
export const SelectedValue = styled.div`
${({ theme }) => css`
${theme.fonts.subTitle_regular_16};
padding-left: 12px;
flex: 1;
`}
`;
export const arrowIcon = css`
margin-right: 8px;
cursor: pointer;
`;
export const DropdownList = styled.ul`
${({ theme }) => css`
position: absolute;
background-color: ${theme.colors.white};
box-shadow: 0 0 30px ${theme.colors.gray400};
width: 320px;
top: 102%;
left: 0;
margin: 0;
padding: 0;
list-style: none;
z-index: 1;
`}
`;
export const DropdownItem = styled.li`
${({ theme }) => css`
${theme.fonts.subTitle_bold_16};
height: 54.01px;
padding-left: 20px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
&:hover {
background-color: ${theme.colors.pink100};
color: ${theme.colors.pink900};
svg {
& path {
fill: ${theme.colors.pink900};
}
}
}
svg {
& path {
fill: ${theme.colors.white};
}
}
&:last-child {
border-bottom: none;
}
`}
`;
export const checkIcon = css`
margin-right: 20px;
`;
'FrontEnd > Component' 카테고리의 다른 글
[React + Typescript + emotion] RangeSlider 컴포넌트 제작 (1) | 2024.08.15 |
---|---|
[React + Typescript + emotion] Tag 컴포넌트 제작 (1) | 2024.08.14 |
[React + Typescript + emotion] Badge 컴포넌트 제작 (0) | 2024.08.09 |
[React + Typescript + emotion] Toggle 컴포넌트 제작 (0) | 2024.08.08 |
[React + Typescript + emotion] Button 컴포넌트 제작 (0) | 2024.07.19 |