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은 포함하지 않는 높이