nishant-design-system
Version:
Sense UI components library
150 lines (140 loc) • 4.17 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]
shift,
// $FlowFixMe[untyped-import]
useFloating,
} from '@floating-ui/react-dom';
import {spaceNone, spaceXXSmall} from '../../styles/variables/_space';
import {classify} from '../../utils/classify';
import {ClickAway} from '../../utils/click-away';
import type {UnstyledButtonProps} from '../Button';
import {UnstyledButton} from '../Button';
import type {AnchorType} from '../ButtonDropdown';
import {Icon} from '../Icon';
import type {MenuOption, MenuProps} from '../Menu';
import {Menu} from '../Menu';
import {Truncate} from '../Truncate';
import css from './InlineDropdown.module.css';
type ClassNames = $ReadOnly<{
buttonWrapper?: string,
dropdownContainer?: string,
}>;
export type InlineDropdownProps = {
...UnstyledButtonProps,
classNames?: ClassNames,
menu?: MenuProps,
anchorPosition?: AnchorType,
onOptionSelect?: (option: MenuOption) => mixed,
onMenuOpen?: () => mixed,
onMenuClose?: () => mixed,
size?: 'medium' | 'small' | 'extraSmall',
...
};
export const InlineDropdown: React$AbstractComponent<
InlineDropdownProps,
HTMLDivElement,
> = React.forwardRef<InlineDropdownProps, HTMLDivElement>(
(
{
anchorPosition = 'bottom-start',
size = 'medium',
onOptionSelect,
menu,
classNames,
disabled,
onMenuOpen,
onMenuClose,
children,
...restButtonProps
}: InlineDropdownProps,
ref,
): React.Node => {
const menuBtnRef = React.useRef(null);
React.useImperativeHandle(ref, () => 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="InlineDropdown"
className={classify(
css.inlineDropdownContainer,
classNames?.dropdownContainer,
)}
ref={menuBtnRef}
>
<UnstyledButton
{...restButtonProps}
disabled={disabled}
ref={reference}
onClick={(e) => {
e.stopPropagation();
onOpen();
}}
className={classify(
css.inlineButton,
{
[css.disabled]: disabled,
},
css[size],
classNames?.buttonWrapper,
)}
>
<Truncate>{children}</Truncate>
<Icon
name={isOpen ? 'caret-up' : 'caret-down'}
size="small"
type="solid"
className={classify({
[css.disabled]: disabled,
})}
/>
</UnstyledButton>
{isOpen && menu && (
<div
onClickCapture={cancelNext}
ref={floating}
style={{
display: 'flex',
position: strategy,
top: y ?? spaceNone,
left: x ?? spaceNone,
}}
>
<Menu
{...menu}
onSelect={(option) => {
onOptionSelect && onOptionSelect(option);
if (
!menu.optionsVariant ||
menu.optionsVariant === 'normal'
) {
clickAway();
}
}}
size={menu.size || 'medium'}
/>
</div>
)}
</div>
)}
</ClickAway>
);
},
);