2022-07-26 14:19:30 +02:00
|
|
|
import { css } from '@emotion/css';
|
|
|
|
import { FocusScope } from '@react-aria/focus';
|
2022-09-20 11:47:31 +02:00
|
|
|
import React, { useRef, useState } from 'react';
|
2022-07-26 14:19:30 +02:00
|
|
|
import { usePopperTooltip } from 'react-popper-tooltip';
|
|
|
|
import { CSSTransition } from 'react-transition-group';
|
|
|
|
|
|
|
|
import { ReactUtils } from '../../utils';
|
|
|
|
import { Portal } from '../Portal/Portal';
|
|
|
|
import { TooltipPlacement } from '../Tooltip/types';
|
|
|
|
|
|
|
|
export interface Props {
|
|
|
|
overlay: React.ReactElement | (() => React.ReactElement);
|
|
|
|
placement?: TooltipPlacement;
|
2022-11-17 09:03:12 +00:00
|
|
|
children: React.ReactElement | ((isOpen: boolean) => React.ReactElement);
|
2022-07-26 14:19:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export const Dropdown = React.memo(({ children, overlay, placement }: Props) => {
|
|
|
|
const [show, setShow] = useState(false);
|
2022-09-20 11:47:31 +02:00
|
|
|
const transitionRef = useRef(null);
|
2022-07-26 14:19:30 +02:00
|
|
|
|
|
|
|
const { getArrowProps, getTooltipProps, setTooltipRef, setTriggerRef, visible } = usePopperTooltip({
|
|
|
|
visible: show,
|
|
|
|
placement: placement,
|
|
|
|
onVisibleChange: setShow,
|
|
|
|
interactive: true,
|
|
|
|
delayHide: 0,
|
|
|
|
delayShow: 0,
|
|
|
|
offset: [0, 8],
|
|
|
|
trigger: ['click'],
|
|
|
|
});
|
|
|
|
|
|
|
|
const animationDuration = 150;
|
|
|
|
const animationStyles = getStyles(animationDuration);
|
|
|
|
|
|
|
|
const onOverlayClicked = () => {
|
|
|
|
setShow(false);
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
2022-11-17 09:03:12 +00:00
|
|
|
{React.cloneElement(typeof children === 'function' ? children(visible) : children, {
|
2022-07-26 14:19:30 +02:00
|
|
|
ref: setTriggerRef,
|
|
|
|
})}
|
|
|
|
{visible && (
|
|
|
|
<Portal>
|
|
|
|
<FocusScope autoFocus>
|
|
|
|
<div ref={setTooltipRef} {...getTooltipProps()} onClick={onOverlayClicked}>
|
|
|
|
<div {...getArrowProps({ className: 'tooltip-arrow' })} />
|
|
|
|
<CSSTransition
|
2022-09-20 11:47:31 +02:00
|
|
|
nodeRef={transitionRef}
|
2022-07-26 14:19:30 +02:00
|
|
|
appear={true}
|
|
|
|
in={true}
|
|
|
|
timeout={{ appear: animationDuration, exit: 0, enter: 0 }}
|
|
|
|
classNames={animationStyles}
|
|
|
|
>
|
2022-09-20 11:47:31 +02:00
|
|
|
<div ref={transitionRef}>{ReactUtils.renderOrCallToRender(overlay)}</div>
|
2022-07-26 14:19:30 +02:00
|
|
|
</CSSTransition>
|
|
|
|
</div>
|
|
|
|
</FocusScope>
|
|
|
|
</Portal>
|
|
|
|
)}
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
Dropdown.displayName = 'Dropdown';
|
|
|
|
|
|
|
|
const getStyles = (duration: number) => {
|
|
|
|
return {
|
|
|
|
appear: css`
|
|
|
|
opacity: 0;
|
|
|
|
position: relative;
|
|
|
|
transform: scaleY(0.5);
|
|
|
|
transform-origin: top;
|
|
|
|
`,
|
|
|
|
appearActive: css`
|
|
|
|
opacity: 1;
|
|
|
|
transform: scaleY(1);
|
|
|
|
transition: transform ${duration}ms cubic-bezier(0.2, 0, 0.2, 1),
|
|
|
|
opacity ${duration}ms cubic-bezier(0.2, 0, 0.2, 1);
|
|
|
|
`,
|
|
|
|
};
|
|
|
|
};
|