115 lines
3.3 KiB
JavaScript
115 lines
3.3 KiB
JavaScript
import { useState, useCallback } from 'react';
|
|
import { NavLink, useLocation } from 'react-router-dom';
|
|
import { navLinks } from '../routes';
|
|
import './BottomNav.css';
|
|
|
|
const PRIMARY_PATHS = ['/', '/lotto', '/stock', '/travel'];
|
|
|
|
// Vertical dots (three circles) icon for "more"
|
|
function MoreDotsIcon() {
|
|
return (
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="22"
|
|
height="22"
|
|
viewBox="0 0 22 22"
|
|
fill="currentColor"
|
|
aria-hidden="true"
|
|
>
|
|
<circle cx="11" cy="4.5" r="1.8" />
|
|
<circle cx="11" cy="11" r="1.8" />
|
|
<circle cx="11" cy="17.5" r="1.8" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
const primaryLinks = navLinks.filter((link) =>
|
|
PRIMARY_PATHS.includes(link.path)
|
|
);
|
|
// Preserve the order defined in PRIMARY_PATHS
|
|
const orderedPrimaryLinks = PRIMARY_PATHS.map((p) =>
|
|
primaryLinks.find((l) => l.path === p)
|
|
).filter(Boolean);
|
|
|
|
const moreLinks = navLinks.filter(
|
|
(link) => !PRIMARY_PATHS.includes(link.path)
|
|
);
|
|
|
|
export default function BottomNav() {
|
|
const [moreOpen, setMoreOpen] = useState(false);
|
|
const location = useLocation();
|
|
|
|
const openMore = useCallback(() => setMoreOpen(true), []);
|
|
const closeMore = useCallback(() => setMoreOpen(false), []);
|
|
const toggleMore = useCallback(() => setMoreOpen((prev) => !prev), []);
|
|
|
|
// Highlight the "more" button when the current path belongs to moreLinks
|
|
const isMoreActive =
|
|
moreOpen || moreLinks.some((link) => location.pathname === link.path);
|
|
|
|
return (
|
|
<>
|
|
{/* Backdrop */}
|
|
<div
|
|
className={`bottom-nav__more-overlay${moreOpen ? ' is-open' : ''}`}
|
|
onClick={closeMore}
|
|
aria-hidden="true"
|
|
/>
|
|
|
|
{/* More panel */}
|
|
<div
|
|
className={`bottom-nav__more-panel${moreOpen ? ' is-open' : ''}`}
|
|
role="menu"
|
|
aria-label="더보기 메뉴"
|
|
>
|
|
{moreLinks.map((link) => (
|
|
<NavLink
|
|
key={link.id}
|
|
to={link.path}
|
|
className={({ isActive }) =>
|
|
`bottom-nav__more-item${isActive ? ' is-active' : ''}`
|
|
}
|
|
onClick={closeMore}
|
|
role="menuitem"
|
|
>
|
|
<span className="bottom-nav__icon">{link.icon}</span>
|
|
<span className="bottom-nav__label">{link.label}</span>
|
|
</NavLink>
|
|
))}
|
|
</div>
|
|
|
|
{/* Bottom nav bar */}
|
|
<nav className="bottom-nav" aria-label="하단 내비게이션">
|
|
{orderedPrimaryLinks.map((link) => (
|
|
<NavLink
|
|
key={link.id}
|
|
to={link.path}
|
|
end={link.path === '/'}
|
|
className={({ isActive }) =>
|
|
`bottom-nav__item${isActive ? ' is-active' : ''}`
|
|
}
|
|
>
|
|
<span className="bottom-nav__icon">{link.icon}</span>
|
|
<span className="bottom-nav__label">{link.label}</span>
|
|
</NavLink>
|
|
))}
|
|
|
|
{/* More button */}
|
|
<button
|
|
type="button"
|
|
className={`bottom-nav__item${isMoreActive ? ' is-active' : ''}`}
|
|
onClick={toggleMore}
|
|
aria-expanded={moreOpen}
|
|
aria-haspopup="menu"
|
|
aria-label="더보기"
|
|
>
|
|
<span className="bottom-nav__icon">
|
|
<MoreDotsIcon />
|
|
</span>
|
|
<span className="bottom-nav__label">더보기</span>
|
|
</button>
|
|
</nav>
|
|
</>
|
|
);
|
|
}
|