mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
82 lines
2.2 KiB
TypeScript
82 lines
2.2 KiB
TypeScript
![]() |
import { css } from '@emotion/css';
|
||
|
import { FocusScope } from '@react-aria/focus';
|
||
|
import React, { useState } from 'react';
|
||
|
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;
|
||
|
children: React.ReactElement;
|
||
|
}
|
||
|
|
||
|
export const Dropdown = React.memo(({ children, overlay, placement }: Props) => {
|
||
|
const [show, setShow] = useState(false);
|
||
|
|
||
|
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 (
|
||
|
<>
|
||
|
{React.cloneElement(children, {
|
||
|
ref: setTriggerRef,
|
||
|
})}
|
||
|
{visible && (
|
||
|
<Portal>
|
||
|
<FocusScope autoFocus>
|
||
|
<div ref={setTooltipRef} {...getTooltipProps()} onClick={onOverlayClicked}>
|
||
|
<div {...getArrowProps({ className: 'tooltip-arrow' })} />
|
||
|
<CSSTransition
|
||
|
appear={true}
|
||
|
in={true}
|
||
|
timeout={{ appear: animationDuration, exit: 0, enter: 0 }}
|
||
|
classNames={animationStyles}
|
||
|
>
|
||
|
{ReactUtils.renderOrCallToRender(overlay)}
|
||
|
</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);
|
||
|
`,
|
||
|
};
|
||
|
};
|