@spaced-out/ui-design-system
Version:
Sense UI components library
151 lines (141 loc) • 4.09 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 {includes} from 'lodash';
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 {Menu} from '../Menu';
import type {ElevationType} from '../Tooltip';
import {getElevationValue} from '../Tooltip';
import type {ButtonTabProps} from './ButtonTab';
import {ButtonTab} from './ButtonTab';
import css from './ButtonTabDropdown.module.css';
export type ButtonTabDropdownProps = {
...ButtonTabProps,
title: string,
elevation?: ElevationType,
dropdownClass?: string,
anchorPosition?: AnchorType,
...
};
export const ButtonTabDropdown = ({
title,
elevation = 'modal',
anchorPosition = 'bottom-end',
...buttonTabProps
}: ButtonTabDropdownProps): React.Node => {
const {
size,
children,
selectedButtonTabId,
onButtonTabSelect: onTabSelect,
dropdownClass,
} = buttonTabProps;
const childrenArray = React.Children.toArray(children);
const menuOptions = childrenArray.map((child) => {
const {
id,
children,
disabled,
iconName,
iconType,
classNames,
size: buttonSize,
} = child.props;
return {
key: id,
disabled,
classNames,
label: children,
iconLeft: iconName,
iconLeftType: iconType,
customComponent: children,
optionSize: buttonSize ?? size,
};
});
const moreTabSelectedId = includes(
menuOptions.map(({key}) => key),
selectedButtonTabId,
)
? 'more-tab'
: selectedButtonTabId;
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="ButtonTabDropdown"
className={classify(css.buttonTabDropDownWrapper, dropdownClass)}
>
<ButtonTab
{...buttonTabProps}
ref={mergeRefs([refs.setReference, triggerRef])}
selectedButtonTabId={moreTabSelectedId}
onButtonTabSelect={(id, e) => {
e?.stopPropagation();
onOpen();
}}
>
{title}
</ButtonTab>
{isOpen && (
<FloatingPortal>
<FloatingFocusManager
modal={false}
context={context}
initialFocus={refs.reference}
>
<div
className={css.menuWrapper}
ref={mergeRefs([refs.setFloating, boundaryRef])}
style={{
display: 'flex',
position: strategy,
top: y ?? spaceNone,
left: x ?? spaceNone,
'--menu-elevation': getElevationValue(elevation),
}}
>
<Menu
onSelect={(option, e) => {
onTabSelect && onTabSelect(option.key, e);
clickAway();
}}
size={size}
options={menuOptions}
onTabOut={clickAway}
selectedKeys={[selectedButtonTabId ?? '']}
/>
</div>
</FloatingFocusManager>
</FloatingPortal>
)}
</div>
)}
</ClickAway>
);
};