UNPKG

nishant-design-system

Version:
162 lines (150 loc) 4.42 kB
// @flow strict import * as React from 'react'; import { // $FlowFixMe[untyped-import] autoUpdate, // $FlowFixMe[untyped-import] flip, // $FlowFixMe[untyped-import] offset, // $FlowFixMe[untyped-import] shift, // $FlowFixMe[untyped-import] useFloating, } from '@floating-ui/react-dom'; import {sizeFluid} from '../../styles/variables/_size'; import {spaceNone, spaceXXSmall} from '../../styles/variables/_space'; import {classify} from '../../utils/classify'; import {ClickAway} from '../../utils/click-away'; import type {ButtonProps} from '../Button'; import {Button} from '../Button'; import type {MenuOption, MenuProps} from '../Menu'; import {Menu} from '../Menu'; import css from './ButtonDropdown.module.css'; export const ANCHOR_POSITION_TYPE = Object.freeze({ top: 'top', topStart: 'top-start', topEnd: 'top-end', bottom: 'bottom', bottomStart: 'bottom-start', bottomEnd: 'bottom-end', }); export type AnchorType = $Values<typeof ANCHOR_POSITION_TYPE>; type ClassNames = $ReadOnly<{ buttonWrapper?: string, dropdownContainer?: string, buttonIcon?: string, }>; export type ButtonDropdownProps = { ...ButtonProps, classNames?: ClassNames, menu?: MenuProps, anchorPosition?: AnchorType, onOptionSelect?: (option: MenuOption) => mixed, onMenuOpen?: () => mixed, onMenuClose?: () => mixed, ... }; export const ButtonDropdown: React$AbstractComponent< ButtonDropdownProps, HTMLDivElement, > = React.forwardRef<ButtonDropdownProps, HTMLDivElement>( ( { anchorPosition = 'bottom-start', size = 'medium', onOptionSelect, menu, classNames, disabled, onMenuOpen, onMenuClose, children, iconRightName, iconRightType = 'solid', isFluid, ...restButtonProps }: ButtonDropdownProps, forwardRef, ) => { const menuBtnRef = React.useRef(null); React.useImperativeHandle(forwardRef, () => menuBtnRef.current); const {x, y, reference, floating, strategy} = useFloating({ strategy: 'absolute', placement: anchorPosition, whileElementsMounted: autoUpdate, middleware: [shift(), flip(), offset(parseInt(spaceXXSmall))], }); const onMenuToggle = (isOpen: boolean) => { isOpen ? onMenuOpen && onMenuOpen() : onMenuClose && onMenuClose(); }; return ( <ClickAway onChange={onMenuToggle}> {({isOpen, onOpen, cancelNext, clickAway}) => ( <div data-testid="ButtonDropdown" className={classify( css.buttonDropdownContainer, { [css.isFluid]: isFluid === true, }, classNames?.dropdownContainer, )} ref={menuBtnRef} > <Button {...restButtonProps} iconRightName={ children ? iconRightName || (isOpen ? 'caret-up' : 'caret-down') : iconRightName } iconRightType={iconRightType} disabled={disabled} size={size} ref={reference} onClick={(e) => { e.stopPropagation(); onOpen(); }} isFluid={isFluid} classNames={{ wrapper: classNames?.buttonWrapper, icon: classNames?.buttonIcon, }} > {children} </Button> {isOpen && menu && ( <div onClickCapture={cancelNext} ref={floating} style={{ display: 'flex', position: strategy, top: y ?? spaceNone, left: x ?? spaceNone, ...(isFluid && {width: sizeFluid}), }} > <Menu {...menu} onSelect={(option) => { onOptionSelect && onOptionSelect(option); if ( !menu.optionsVariant || menu.optionsVariant === 'normal' ) { clickAway(); } }} size={menu.size || size} /> </div> )} </div> )} </ClickAway> ); }, );