@spaced-out/ui-design-system
Version:
Sense UI components library
124 lines (115 loc) • 3.56 kB
Flow
// @flow strict
import * as React from 'react';
import {
// $FlowFixMe[untyped-import]
autoUpdate,
// $FlowFixMe[untyped-import]
flip,
// $FlowFixMe[untyped-import]
FloatingFocusManager,
// $FlowFixMe[untyped-import]
FloatingPortal,
// $FlowFixMe[untyped-import]
offset,
// $FlowFixMe[untyped-import]
shift,
// $FlowFixMe[untyped-import]
useFloating,
} from '@floating-ui/react';
import {spaceNone, spaceXXSmall} from '../../../styles/variables/_space';
import {classify} from '../../../utils/classify';
import {ClickAway} from '../../../utils/click-away';
import {mergeRefs} from '../../../utils/merge-refs';
import type {AnchorType} from '../../ButtonDropdown';
import type {MenuOption, MenuProps} from '../../Menu';
import {Menu} from '../../Menu';
import type {ElevationType} from '../../Tooltip';
import {getElevationValue} from '../../Tooltip';
import type {TabProps} from '../Tab';
import {Tab} from '../Tab';
import css from './TabDropdown.module.css';
type ClassNames = $ReadOnly<{wrapper?: string}>;
export type TabDropdownProps = {
anchorPosition?: AnchorType,
size?: 'medium' | 'small',
props?: {
tab: TabProps,
menu: MenuProps,
},
onOptionSelect?: (option: MenuOption, ?SyntheticEvent<HTMLElement>) => mixed,
classNames?: ClassNames,
selected?: boolean,
disabled?: boolean,
elevation?: ElevationType,
};
export const TabDropdown = ({
anchorPosition = 'bottom-start',
size,
onOptionSelect,
props,
elevation = 'modal',
classNames,
}: TabDropdownProps): React.Node => {
const {x, y, refs, strategy, context} = useFloating({
open: true,
strategy: 'absolute',
placement: anchorPosition,
whileElementsMounted: autoUpdate,
middleware: [shift(), flip(), offset(parseInt(spaceXXSmall))],
});
return (
<ClickAway>
{({isOpen, onOpen, clickAway, boundaryRef, triggerRef}) => (
<div
data-testid="TabDropdown"
className={classify(css.tabDropdownContainer, classNames?.wrapper)}
>
<Tab
{...props?.tab}
size={size}
ref={mergeRefs([refs.setReference, triggerRef])}
onClick={(e) => {
e.stopPropagation();
onOpen();
}}
classNames={{
wrapper: css.dotTabWrapper,
iconTextWrap: css.dotTextWrap,
}}
/>
{isOpen && props?.menu && (
<FloatingPortal>
<FloatingFocusManager
modal={false}
context={context}
initialFocus={refs.reference}
>
<div
ref={mergeRefs([refs.setFloating, boundaryRef])}
className={css.menuWrapper}
style={{
display: 'flex',
position: strategy,
top: y ?? spaceNone,
left: x ?? spaceNone,
'--menu-elevation': getElevationValue(elevation),
}}
>
<Menu
{...props.menu}
onSelect={(option, e) => {
onOptionSelect && onOptionSelect(option, e);
clickAway();
}}
size={props.menu.size || size}
onTabOut={clickAway}
/>
</div>
</FloatingFocusManager>
</FloatingPortal>
)}
</div>
)}
</ClickAway>
);
};