import type { PropsWithChildren } from 'react' import React, { useRef, useMemo, useState, useEffect, useCallback } from 'react' import DropdownContext from '../Dropdown/contexts/DropdownContext' export interface DropdownProps { /** * Event emitted when the Dropdown is closed. */ onDismiss?(): void /** * A boolean value that represents the state of the Dropdown. */ isOpen?: boolean /** * ID to identify Dropdown. */ id?: string } const Dropdown = ({ children, isOpen: isOpenControlled, onDismiss, id = 'fs-dropdown', }: PropsWithChildren) => { const [isOpenInternal, setIsOpenInternal] = useState(false) const dropdownItemsRef = useRef([]) const selectedDropdownItemIndexRef = useRef(0) const dropdownTriggerRef = useRef(null) const isOpen = isOpenControlled ?? isOpenInternal const close = useCallback(() => { setIsOpenInternal(false) onDismiss?.() }, [onDismiss]) const open = () => { setIsOpenInternal(true) } const toggle = useCallback(() => { setIsOpenInternal((currentIsOpen) => { if (currentIsOpen) { onDismiss?.() dropdownTriggerRef.current?.focus() } return !currentIsOpen }) }, [onDismiss]) const addDropdownTriggerRef = useCallback( (ref: T) => { dropdownTriggerRef.current = ref }, [] ) useEffect(() => { setIsOpenInternal(isOpenControlled ?? false) }, [isOpenControlled]) useEffect(() => { isOpen && dropdownItemsRef?.current[0]?.focus() }, [isOpen]) useEffect(() => { let firstClick = true const event = (e: MouseEvent) => { const wasSomeItemClicked = dropdownItemsRef?.current.some( (item) => e.target === item || item.contains(e.target as Node) ) if (firstClick) { firstClick = false return } !wasSomeItemClicked && close() } if (isOpen) { document.addEventListener('click', event) } else { document.removeEventListener('click', event) } return () => { document.removeEventListener('click', event) } }, [close, isOpen]) const value = useMemo(() => { return { isOpen, close, open, toggle, dropdownTriggerRef, addDropdownTriggerRef, selectedDropdownItemIndexRef, dropdownItemsRef, id, } }, [isOpen, close, toggle, addDropdownTriggerRef, id]) return ( {children} ) } export default Dropdown