import React, { cloneElement, useRef, forwardRef } from 'react'; import styled from 'styled-components'; import type { PropsWithChildren, ReactElement } from 'react'; import { useOutsideClick, useControlledState } from '@redocly/theme/core/hooks'; import { ChevronDownIcon } from '@redocly/theme/icons/ChevronDownIcon/ChevronDownIcon'; import { ChevronUpIcon } from '@redocly/theme/icons/ChevronUpIcon/ChevronUpIcon'; interface TriggerProps { onClick?: (event: React.UIEvent) => void; onKeyDown?: (event: React.KeyboardEvent) => void; icon?: React.ReactNode; iconPosition?: string; [key: string]: unknown; } export type DropdownProps = PropsWithChildren<{ trigger: React.ReactNode; triggerEvent?: 'click' | 'hover'; placement?: 'top' | 'bottom'; alignment?: 'start' | 'end'; active?: boolean; closeOnClick?: boolean; dataAttributes?: Record; className?: string; withArrow?: boolean; onClick?: (event: React.UIEvent) => void; onClose?: () => void; }>; export const Dropdown = forwardRef( ( { children, className, active, trigger, triggerEvent = 'click', closeOnClick = true, withArrow, dataAttributes, placement, alignment, onClick, onClose, }, ref, ) => { const dropdownRef = useRef(null); const [isOpen, setIsOpen] = useControlledState(false, active); const handleOpen = () => { setIsOpen(true); }; const handleClose = () => { setIsOpen(false); onClose?.(); }; const handleChildClick = () => { handleClose(); }; const handleToggle = (event: React.UIEvent) => { event.stopPropagation(); event.preventDefault(); setIsOpen(!isOpen); }; const handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter' || event.key === ' ') { handleToggle(event); } }; useOutsideClick((ref as React.RefObject) || dropdownRef, handleClose); const triggerChild = React.Children.only(trigger) as ReactElement; const dropdownTrigger = cloneElement(triggerChild, { onClick: triggerEvent === 'click' ? handleToggle : undefined, icon: withArrow ? isOpen ? : : undefined, ...(withArrow ? { iconPosition: 'right' } : {}), ...triggerChild.props, onKeyDown: triggerEvent === 'click' ? handleKeyDown : undefined, }); return ( {dropdownTrigger} {children} ); }, ); const DropdownWrapper = styled.div` --button-gap: var(--spacing-xxs); display: flex; gap: var(--spacing-xxs); align-items: center; justify-content: space-between; cursor: pointer; height: 100%; appearance: none; padding: 0; margin: 0; position: relative; text-decoration: none; `; const ChildrenWrapper = styled.div<{ placement?: string; alignment?: string; isOpen?: boolean }>` padding-top: var(--dropdown-menu-padding-top); position: absolute; top: ${({ placement }) => (placement === 'top' ? 'auto' : '100%')}; bottom: ${({ placement }) => (placement === 'top' ? '100%' : 'auto')}; left: ${({ alignment }) => (alignment === 'start' ? '0' : 'auto')}; right: ${({ alignment }) => (alignment === 'end' ? '0' : 'auto')}; display: ${({ isOpen }) => (isOpen ? 'block' : 'none')}; z-index: var(--dropdown-menu-z-index); `;