Shameem

June 8, 2023

Dropdown component using react.js tailwindcss and react-transition-group

import { useState, useRef } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { Transition } from 'react-transition-group';
import { IconChevronDown } from '@/components/icons';
import { useOnClickOutside } from '@/hooks/useClickOutside';
import { useWindowSize } from '@/hooks/useDimension';

/*
* label - dropdown button label
* subMenu - submenu array
* hoverTrigger - whether dropdown should open on hover on click
* className - tailwind classes need to pass to the dropdown
*/
export default function Dropdown({ label, subMenu, hoverTrigger, className, ...props }) {
  const router = useRouter();
  const { width } = useWindowSize();
  const [open, setOpen] = useState(false);
  const dropDown = useRef(null);
  useOnClickOutside(dropDown, () => setOpen(false));
  let classes = '';
  if (className) {
    classes += ' ' + className;
  }

  /* Dropdown animation */
  const dropDownMenu = useRef(null);
  const defaultClasses = `top-full w-[282px] rounded-lg origin-top-left bg-primary-bg transition-all duration-300 absolute left-0 z-20`;
  const openClasses = {
    entering: `opacity-100 delay-100 scale-100`,
    entered: `opacity-100 scale-100`,
    exiting: `opacity-0 scale-90`,
    exited: `opacity-0 scale-90`
  };

  const openClassesOverlay = {
    entering: `opacity-100 delay-100`,
    entered: `opacity-100`,
    exiting: `opacity-0`,
    exited: `opacity-0`
  };

  return (
    <div className={classes} ref={dropDown} onMouseEnter={() => hoverTrigger && setOpen(true)} onMouseLeave={() => hoverTrigger && setOpen(false)}>
      <button onClick={() => !hoverTrigger && setOpen((open) => !open)} className={`flex items-center text-3xs sm:text-sm px-1 sm:px-3 md:px-4 whitespace-nowrap noSelect leading-5 hover:md:text-white active:text-white font-medium transition-colors duration-200 w-full justify-center flex-col lg:flex-row bg-primary-bg relative z-30 ${Component ? `py-4 lg:py-5` : `py-5 lg:py-[22px]`} ${open ? `text-grey-text` : subMenu.map((sm) => sm.url).includes(router.pathname) ? `text-white/80` : `text-secondary-text`}`}>
        {label}
        {!hoverTrigger && <IconChevronDown />}
      </button>
      <Transition in={open} mountOnEnter timeout={500} unmountOnExit nodeRef={dropDownMenu}>
       {(state) => (
         <>
           <div ref={dropDownMenu} className={`${defaultClasses} ${openClasses[state]}`}>
             <ul className={`border p-3 border-[#191026] rounded-lg`}>
               {subMenu.map((menu, index) => {
                 return (
                   <li key={index} className="py-0.5">
                     <Link href={menu.url} onClick={() => setOpen(false)} className={`py-3 px-4 flex text-sm group leading-6 text-white hover:bg-[#191026] rounded transition-colors duration-200 noSelect ${router.pathname == menu.url ? `bg-[#191026]` : ``}`}>
                       <div className={`${menu.icon ? `pl-4` : ``}`}>
                          <h6>{menu.label}</h6>
                       </div>
                     </Link>
                   </li>
                 );
               })}
             </ul>
          </div>
        </>
      )}
    </Transition>
  </div>
 );
} 

Note: useOnClickOutside, useWindowSize hooks code will be in

Some usefull react hooks – window size, click outside, intersection observer

Top comments
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments