-
드롭다운 메뉴프론트엔드/컴포넌트 2024. 5. 31. 09:53728x90
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