nishant-design-system
Version:
Sense UI components library
128 lines (117 loc) • 3.55 kB
Flow
// @flow strict
import * as React from 'react';
import {
// $FlowFixMe[untyped-import]
autoUpdate,
// $FlowFixMe[untyped-import]
flip,
// $FlowFixMe[untyped-import]
offset,
// $FlowFixMe[untyped-import]
useFloating,
} from '@floating-ui/react-dom';
import {colorBackgroundTertiary} from '../../styles/variables/_color';
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 {InputProps} from '../Input';
import {Input} from '../Input';
import type {MenuOption, MenuProps} from '../Menu';
import {Menu} from '../Menu';
import css from './Dropdown.module.css';
type ClassNames = $ReadOnly<{wrapper?: string, box?: string}>;
export type DropdownProps = {
...InputProps,
classNames?: ClassNames,
onChange?: (option: MenuOption) => mixed,
onMenuOpen?: () => mixed,
onMenuClose?: () => mixed,
dropdownInputText?: string,
menu?: MenuProps,
...
};
export const Dropdown: React$AbstractComponent<
DropdownProps,
HTMLInputElement,
> = React.forwardRef<DropdownProps, HTMLInputElement>(
(
{
size = 'medium',
classNames,
placeholder = 'Select...',
onChange,
menu,
onMenuOpen,
onMenuClose,
dropdownInputText = '',
...inputProps
}: DropdownProps,
ref,
): React.Node => {
const dropdownRef = React.useRef();
const {x, y, reference, floating, strategy} = useFloating({
strategy: 'absolute',
placement: 'bottom-start',
whileElementsMounted: autoUpdate,
middleware: [flip(), offset(parseInt(spaceXXSmall))],
});
const onMenuToggle = (isOpen: boolean) => {
isOpen ? onMenuOpen && onMenuOpen() : onMenuClose && onMenuClose();
};
return (
<ClickAway onChange={onMenuToggle}>
{({isOpen, onOpen, cancelNext, clickAway}) => (
<div
data-testid="Dropdown"
className={classify(css.dropdownContainer, classNames?.wrapper)}
ref={dropdownRef}
>
<Input
{...inputProps}
boxRef={reference}
size={size}
placeholder={placeholder}
value={dropdownInputText}
classNames={{box: css.inputBox}}
iconRightName={isOpen ? 'angle-up' : 'angle-down'}
readOnly
onContainerClick={(e) => {
e.stopPropagation();
onOpen();
}}
ref={ref}
/>
{isOpen && menu && (
<div
onClickCapture={cancelNext}
ref={floating}
style={{
position: strategy,
top: y ?? spaceNone,
left: x ?? spaceNone,
width: sizeFluid,
backgroundColor: colorBackgroundTertiary,
}}
>
<Menu
{...menu}
onSelect={(option) => {
onChange && onChange(option);
if (
!menu.optionsVariant ||
menu.optionsVariant === 'normal'
) {
clickAway();
}
}}
size={menu.size || size}
/>
</div>
)}
</div>
)}
</ClickAway>
);
},
);