src/components/atoms/Dropdown/Dropdown.tsx (75 lines of code) (raw):

'use client'; import { useState, useRef, useEffect } from 'react'; import { clsx } from 'clsx'; import Image from 'next/image'; import IDropdownOption from '@/interfaces/IDropdownOption'; import styles from './Dropdown.module.scss'; import ArrowIcon from '../../../assets/svg/arrow.svg'; import assertIsNode from '../../../utils/assertIsNode'; interface Props<Option extends IDropdownOption> { options: Option[]; onChange?: (optionId: Option) => void; defaultOptionId: string; } export default function Dropdown<Option extends IDropdownOption>({ options, onChange, defaultOptionId, }: Props<Option>) { const [isOpen, setIsOpen] = useState(false); const [selectedOptionId, setSelectedOptionId] = useState(defaultOptionId); const toggleContainer = useRef<HTMLDivElement>(null); const selectedOption = options.find((option) => option.id === selectedOptionId); const handleClickOutside = (event: Event) => { assertIsNode(event.target); if (toggleContainer.current && !toggleContainer.current.contains(event.target)) { setIsOpen(false); } }; const handleOptionClick = (option: Option) => { setSelectedOptionId(option.id); setIsOpen(false); if (onChange) { onChange(option); } }; useEffect(() => { window.addEventListener('click', handleClickOutside, true); return () => { window.removeEventListener('click', handleClickOutside, true); }; }); return ( <div ref={toggleContainer} className={styles.dropdown}> <button type="button" className={styles.button} onClick={() => setIsOpen(!isOpen)}> <img className={styles.image} src={selectedOption?.image} alt={selectedOption?.text} /> {selectedOption?.text}{' '} <ArrowIcon className={clsx(styles.arrow, isOpen && styles.arrowOpen)} /> </button> {isOpen && ( <ul className={styles.options}> {options.map((option) => ( <li key={option.id} className={styles.option}> <button type="button" onClick={() => handleOptionClick(option)} className={styles.optionButton} > {option.image && ( <Image className={styles.optionImage} src={option.image} height={18} width={18} alt={option.text} /> )} {option.text} </button> </li> ))} </ul> )} </div> ); }