아코디언(Accordion) 메뉴 만들기

신동훈·2022년 7월 19일
0

components

목록 보기
1/3

interface props {
    title?: string;
    contents?: any;
}
//styled-component
const Container = styled.div`
    display: flex;
    position: relative;
    flex-direction: column;
    justify-content: center;
    border-radius: 4px;
    border: 1px solid silver;
`;

const Title = styled.div`
    display: flex;
    align-items: center;
    height: 32px;
    margin: 0 32px 0 8px;
`;

//버튼을 위에 상시 노출하기 위해서 posotion을 absolute
const AccBtn = styled.div`
    top: 8px;
    right: 8px;
    font-size: 14px;
    position: absolute;
`;

const ContentsWrapper = styled.div`
    height: 0; 
//height를 0%로 하면 렌더링할 때 안의 내용이 보인다(?)
    width: 100%;
    overflow: hidden;
    transition: height 0.35s ease;
`;

const Contents = styled.div``;

const Accordion = (props: props) => {
    const parentRef = useRef<HTMLDivElement>(null);
    const childRef = useRef<HTMLDivElement>(null);
    const [isCollapse, setIsCollapse] = useState(false);

    const parentRefHeight = parentRef.current?.style.height ?? "0px";
    const buttonText = parentRefHeight === "0px" ? "열기" : "닫기";

    const handleBtn = useCallback(
        (event) => {
            event.stopPropagation();
            if (parentRef.current === null || childRef.current === null) {
                return;
            }
            if (parentRef.current.clientHeight > 0) {
                parentRef.current.style.height = '0'
                parentRef.current.style.background = "green";
            } else {
                parentRef.current.style.height = `${childRef.current.clientHeight}px`;
                parentRef.current.style.background = "yellow";
            }
            setIsCollapse(!isCollapse);
        },
        [isCollapse]
    );

    return (
        <Container>
            <Title onClick={handleBtn}>
                {props.title}
                <AccBtn>{buttonText}</AccBtn>
            </Title>
            <ContentsWrapper ref={parentRef}>
                <Contents ref={childRef}>{props.contents}</Contents>
            </ContentsWrapper>
        </Container>
    );
}

export default React.memo(Accordion);

출처:Jacob's Development Blog (https://code-masterjung.tistory.com/) (를 살짝 변형)

공부해볼 것
1. useRef란? hook...? html 엘리먼트에 접근...??
2. useCallback이란?
3. '??'연산자: null or undefined와 연관이 있다?
4. stopPropagation: 부모태그로의 이벤트 전파를 stop 중지(버블업 방지)
5. current 속성이란?
6. clientHeight란? padding을 포함하지만, 수평 스크롤바의 높이, 경계선, 또는 margin은 포함하지 않는 높이

profile
독학 정리

0개의 댓글