728x90
import React, { useState, useEffect, useRef } from 'react'
import './App.css'
import Dropdown from './Dropdown'
const dropdownMenu = {
'Home': ['home-1', 'home-2'],
'About': ['about-1', 'about-2', 'about-3'],
'Contact': ['contact-1']
}
function App(){
const [page, setPage] = useState('') // 현재 선택한 메뉴 저장
const [layout, setLayout] = useState({}) // 현재 드롭다운 위치 저장
const [target, setTarget] = useState(null) // 현재 선택한 타겟 저장
const [resize, setResize] = useState(false) // 브라우저 크기 조정중인지 판단
const menus = dropdownMenu[page] // 드롭다운 메뉴
const sx = {backgroundColor: 'orange', color: 'red' } // 드롭다운 스타일 설정
const itemStyle = {backgroundColor: 'black', color: '#ccc'} // 드롭다운 메뉴 호버스타일 설정
const dropdown = useRef(null)
const openDropdown = (e) => {
e.stopPropagation()
const {x, bottom} = e.target.getBoundingClientRect()
setPage(e.target.innerText)
setLayout({x, y: bottom })
setTarget(e.target)
}
const closeDropdown = () => {
setPage('')
setLayout({})
setTarget(null)
}
const changePosition = () => {
if(target){
const {x, bottom} = target.getBoundingClientRect()
setLayout({x, y: bottom })
setResize(prevResize => !prevResize)
}
}
const setDropdown = (e) => {
if(!dropdown.current.contains(e.target)){ // 드롭다운 바깥 영역을 클릭한 경우
closeDropdown()
}
}
useEffect(() => { // 메뉴닫기
window.addEventListener('click', setDropdown)
return () => window.removeEventListener('click', setDropdown)
}, [])
useEffect(() => { // 드롭다운 위치조정
window.addEventListener('resize', changePosition)
return () => window.removeEventListener('resize', changePosition)
}, [target])
return (
<div className='App'>
<Dropdown ref={dropdown} page={page} menus={menus} layout={layout} closeDropdown={closeDropdown} sx={sx} itemStyle={itemStyle} duration={300} resize={resize}/>
<nav>
<ul>
{Object.keys(dropdownMenu).map((menu, id) => <li key={id} onClick={openDropdown}>{menu}</li>)}
</ul>
</nav>
</div>
)
}
export default App
App.js 파일을 위와 같이 작성한다.
.App{
text-align: center;
}
ul{
list-style: none;
}
nav > ul{
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
}
nav > ul > li{
margin-right: 2rem;
cursor: pointer;
}
nav > ul > li:hover{
color: orange;
}
App.css 파일을 위와 같이 작성한다.
import React, { useState, useEffect, forwardRef } from 'react'
import './Dropdown.css'
const Dropdown = forwardRef(({ page, menus, layout, closeDropdown, sx, itemStyle, duration, resize }, ref) => {
const [fade, setFade] = useState("") // 액티브 클래스
const [id, setId] = useState(null) // 현재 호버된 메뉴
const [pos, setPos] = useState({x: 0, y: 0}) // 드롭다운 위치
const [items, setItems] = useState(null) // 메뉴목록
const handleClick = () =>{
closeDropdown()
setId(null)
}
useEffect(() => {
let timer = null
if(page){
timer = setTimeout(() => {
setFade('active')
setPos({...layout}) // 드롭다운이 사라진후 위치변경
setItems(menus) // 드롭다운이 사라진후 메뉴변경
}, duration) // 클린업과 동시에 실행되지 않도록 시간차를 둠
}
return () => {
clearTimeout(timer)
setFade("")
}
}, [page])
useEffect(() => {
setPos({...layout}) // 브라우저 크기 조정시 드롭다운 위치변경
}, [resize])
return (
<div ref={ref} className={`dropdown-container ${fade}`} style={{...sx, left: `${pos.x}px`, top: `${pos.y}px`}}>
<ul>
{Array.isArray(items) && items.length > 0 && items.map((menu, idx) =>
(<li key={idx} style={id == idx ? itemStyle: {}} id={idx} onClick={handleClick} onMouseEnter={(e) => setId(e.target.id)} onMouseLeave={() => setId(null)}>{menu}</li>))}
</ul>
</div>
)
})
export default Dropdown
Dropdown.js 파일을 위와 같이 작성한다.
.dropdown-container{
padding: .5rem 0rem;
border: 1px solid #eee;
border-radius: 5px;
opacity: 0;
position: absolute;
transition: opacity .3s linear;
}
.dropdown-container.active{
opacity: 1;
}
.dropdown-container > ul{
list-style: none;
margin: 0; padding: 0;
}
.dropdown-container > ul > li {
cursor: pointer;
padding: .3rem 1rem;
}
.dropdown-container > ul > li:hover{
background-color: #eee;
}
Dropdown.css 파일을 위와 같이 작성한다.
728x90
'프론트엔드 > 컴포넌트' 카테고리의 다른 글
모달창 드래그앤 드롭 (0) | 2024.06.18 |
---|---|
슬라이드 넘길때 텍스트 애니메이션 적용하기 (0) | 2024.02.24 |
위로 증가하는 바 그래프 (0) | 2024.02.16 |
돋보기 효과 (0) | 2024.02.14 |
입력창에서 숫자만 입력 가능하게 하기 (36) | 2023.08.24 |