Creating an accordion menu in Next.js comes with the challenge of implementing 3 different modes for an element:
- The first mode is default, where no click event has occurred:
.mainLi_default
- In the second mode, the first click event triggers the opening of the menu with a specific style:
.mainLi_open
- When a click event occurs on the same element again, the menu must close with a different specific style:
.mainLi_recentOpen
export default function Menu() {
const context = useContext(navItems);
const listItems = context.listItems;
const [selectItemId, setSelectItemId] = useState();
function selectItemHandler(id, item) {
setSelectItemId(id);
}
return (
<>
{listItems.map((menuItems) => {
return (
<ul key={menuItems.id} className={`${styles.list}`}>
<li
id={menuItems.id}
className={`${styles.mainLi} ${menuItems.id == selectItemId ? styles.mainLi_open : styles.mainLi_default}`}
onClick={() => {
selectItemHandler(menuItems.id);
}}
>
<div className={`${styles.options}`}>
<span>{menuItems.icon}</span>
<span>{menuItems.title}</span>
<span>
<IoIosArrowDown />
</span>
</div>
<ul className={styles.subMenu}>
{menuItems.subItems.map((subitem) => {
return (
<li key={subitem.id}>
<Link href={subitem.path}>{subitem.title}</Link>
</li>
);
})}
</ul>
</li>
</ul>
);
})}
</>
);
}
/* used inside : nav-menu/menu/index.js */
.list {
@apply flex w-full flex-col px-4 py-2;
}
.mainLi {
@apply w-full;
}
.mainLi .options {
@apply flex w-full cursor-pointer items-center justify-start gap-2 font-R-bold capitalize;
}
.mainLi .options span:nth-child(1) {
@apply text-20;
}
.mainLi .options span:nth-child(2) {
@apply text-16;
}
.mainLi .options span:nth-child(3) {
@apply ml-auto text-20;
}
.mainLi .subMenu {
@apply mt-4 flex w-full flex-col gap-2 px-6 font-R-regular capitalize text-secondary;
}
/* menu status classes */
/* Start default status (default) */
.mainLi_default .options span:nth-child(1) {
@apply text-mute;
}
.mainLi_default .options span:nth-child(2) {
@apply text-mute;
}
.mainLi_default .options span:nth-child(3) {
@apply text-mute;
}
.mainLi_default .subMenu {
@apply hidden text-secondary;
}
/* End default status (default) */
/* Start open status */
.mainLi_open .options span:nth-child(1) {
@apply text-primary;
}
.mainLi_open .options span:nth-child(2) {
@apply text-primary;
}
.mainLi_open .options span:nth-child(3) {
@apply rotate-180 text-secondary;
}
.mainLi_open .subMenu {
@apply flex text-secondary;
}
/* End open status */
/* START recentOpen status */
.mainLi_recentOpen .options span:nth-child(1) {
@apply text-secondary;
}
.mainLi_recentOpen .options span:nth-child(2) {
@apply text-secondary;
}
.mainLi_recentOpen .options span:nth-child(3) {
@apply rotate-180 text-secondary;
}
.mainLi_recentOpen .subMenu {
@apply hidden text-secondary;
}
/* End recentOpen status */
/* menu status classes */
I have successfully implemented the first and second modes, but I am currently working on figuring out how to manage the last mode.