🌐 Web/React

[React + TypeScript] select 직접 구현하기(2)

dlalwl_jpg 2024. 1. 24. 16:28

https://studybook.tistory.com/104

 

[React + TypeScript] select 직접 구현하기(1)

select tag를 커스텀하여 디자인을 변경하는 방법도 있지만 디자인 요소가 많이 수정되어야 할 것 같아 직접 구현해볼 것이다. 처음에는 react-select라이브러리를 사용하려고 했으나 라이브러리를

studybook.tistory.com

위 글에서 이어서 select를 직접 구현할 것이다.

UI를 좀 변경하여 위 글에서 코드가 조금 변경된 부분이 있을 수 있다..!


1. 선택한 option값 보이기(value값 전달하기)

각 선택한 옵션값이 label에 보이도록 구현할 것이다.


GroupType.tsx

각 Option을 클릭하면 handleClickedGroupType()에 값을 전달한다.

그럼 setGroupType()으로 전달받은 Option값을 저장하고 label에 선택된 groupType을 보이도록 한다.

그리고 setIsOption()을 false로 바꿔 Option을 선택하면 Option박스가 닫히도록 한다.


2. 박스 열림 유무에 따른 화살표 방향 바르게 구현하기

박스가 닫혀 있을 때는 화살표가 아래 방향으로 되어 있고 클릭해서 박스가 열리면

아래처럼 화살표 방향이 위로 바뀌도록 구현할 것이다.


GroupType.tsx

isOption 값을 toggle을 이용하며 SignUpForm.style.ts에 전달한다.

true라면 Option박스가 열린 것이므로 화살표 방향을 위쪽으로, false라면 Option박스가 닫힌 것이므로 화살표 방향을 아래쪽으로 만들 것이다.


SignUpForm.style.ts

&:after를 이용하여 원래의 기본 화살표를 설정해주고, toggle이 true가 되면 transform을 이용하여 180도 회전시켰다.

1행의 {css}를 꼭 추가해야 위와 같이 css적용이 가능하다.

여기서 기존 코드에 없었던 ${props => props.theme.colors.red1} 등이 나오는데 theme 파일을 이용해 색깔 전달로 변경되어 그냥 원하는 색깔을 넣는 거랑 똑같다.


전체 코드

GroupType.tsx

import * as S from './SignUpForm.style';
import { useState } from 'react';

function GroupType(){

    //그룹 분류 option열기
    const [isOption, setIsOption] = useState(false);
	const onClickLabel = () => {
        setIsOption(!isOption);
	};

    //그룹 분류 value값 설정하기
    const [groupType, setGroupType] = useState("동아리");
    const handleClickedGroupType = (type: string) => {
        setGroupType(type);
        setIsOption(!isOption);
    };

    return(
        <div>
            <S.groupTypeBox toggle={isOption}>
                <S.groupTypeLabel onClick={onClickLabel}>{groupType}</S.groupTypeLabel>
                <S.groupTypeUl toggle={isOption}>
                    <S.groupTypeLiTop onClick={(e) => handleClickedGroupType("동아리")}>동아리</S.groupTypeLiTop>
                    <S.groupTypeLi onClick={(e) => handleClickedGroupType("학생회")}>학생회</S.groupTypeLi>
                    <S.groupTypeLi onClick={(e) => handleClickedGroupType("학술 모임")}>학술 모임</S.groupTypeLi>
                    <S.groupTypeLiBottom onClick={(e) => handleClickedGroupType("기타")}>기타</S.groupTypeLiBottom>
                </S.groupTypeUl>
            </S.groupTypeBox>
        </div>
    );
}

export default GroupType;

 

SignUpForm.style.ts

import styled, { css } from 'styled-components'

//그룹 분류 select 박스
export const groupTypeBox = styled.div<{toggle:boolean}>`
    position: relative;
    margin-top: 8px;
    width: 352px;
    height: 47px;
    border-radius: 11px;
    border: 2px solid ${props=>(props.toggle ? `${props.theme.colors.red1}`: `${props.theme.colors.gray2}`)};
    padding: 15px;
    
    &:hover{
        border: 2px solid ${props => props.theme.colors.red1};
    }

    // toggle이 true일 때 180도 회전
    &:after {
        content: '';
        position: absolute;
        top: 35%;
        right: 16px;
        width: 14px;
        height: 14px;
        background: url('/images/groupTypePolygon.svg') no-repeat center center;

        // toggle이 true일 때 180도 회전
        ${(props) =>
            props.toggle &&
            css`
                transform: rotate(180deg);
            `}
`;

//그룹 분류 라벨
export const groupTypeLabel = styled.div`
    display: flex;
    border: 0 none;
    background: transparent;
    cursor: pointer;
    font-size: 15px;
    font-weight: 600;
`;

//그룹 분류 선택리스트 박스(option박스)
export const groupTypeUl = styled.ul<{toggle:boolean}>`
    width: 352px;
    height: 185px;
    border-radius: 11px;
    border: 2px solid ${props => props.theme.colors.red1};
    position: absolute;
    background: white;
    margin: 22px 0 0 -16px; //label기준으로 위치 조정
    cursor: pointer;
    display: ${props=>(props.toggle ? 'null': 'none')};
`;

//그룹 분류 option
export const groupTypeLi = styled.li`
    font-size: 15px;
    font-weight: 600;
    height: 45.5px;
    padding: 15px 15px;

    //마우스 닿으면 색변경
    &:hover{
        background: ${props => props.theme.colors.red4};
    }
`;

//그룹 분류 option(동아리)
export const groupTypeLiTop = styled(groupTypeLi)`
    border-radius: 9px 9px 0px 0px;
`;

//그룹 분류 option(기타)
export const groupTypeLiBottom = styled(groupTypeLi)`
    border-radius: 0px 0px 9px 9px;
`;