Popper.tsx 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import React, { PureComponent } from 'react';
  2. import * as PopperJS from 'popper.js';
  3. import { Manager, Popper as ReactPopper, PopperArrowProps } from 'react-popper';
  4. import { Portal } from '@grafana/ui';
  5. import Transition from 'react-transition-group/Transition';
  6. import { PopperContent } from './PopperController';
  7. const defaultTransitionStyles = {
  8. transition: 'opacity 200ms linear',
  9. opacity: 0,
  10. };
  11. const transitionStyles: { [key: string]: object } = {
  12. exited: { opacity: 0 },
  13. entering: { opacity: 0 },
  14. entered: { opacity: 1, transitionDelay: '0s' },
  15. exiting: { opacity: 0, transitionDelay: '500ms' },
  16. };
  17. export type RenderPopperArrowFn = (
  18. props: {
  19. arrowProps: PopperArrowProps;
  20. placement: string;
  21. }
  22. ) => JSX.Element;
  23. interface Props extends React.HTMLAttributes<HTMLDivElement> {
  24. show: boolean;
  25. placement?: PopperJS.Placement;
  26. content: PopperContent<any>;
  27. referenceElement: PopperJS.ReferenceObject;
  28. wrapperClassName?: string;
  29. renderArrow?: RenderPopperArrowFn;
  30. }
  31. class Popper extends PureComponent<Props> {
  32. render() {
  33. const { show, placement, onMouseEnter, onMouseLeave, className, wrapperClassName, renderArrow } = this.props;
  34. const { content } = this.props;
  35. return (
  36. <Manager>
  37. <Transition in={show} timeout={100} mountOnEnter={true} unmountOnExit={true}>
  38. {transitionState => {
  39. return (
  40. <Portal>
  41. <ReactPopper
  42. placement={placement}
  43. referenceElement={this.props.referenceElement}
  44. // TODO: move modifiers config to popper controller
  45. modifiers={{ preventOverflow: { enabled: true, boundariesElement: 'window' } }}
  46. >
  47. {({ ref, style, placement, arrowProps }) => {
  48. return (
  49. <div
  50. onMouseEnter={onMouseEnter}
  51. onMouseLeave={onMouseLeave}
  52. ref={ref}
  53. style={{
  54. ...style,
  55. ...defaultTransitionStyles,
  56. ...transitionStyles[transitionState],
  57. }}
  58. data-placement={placement}
  59. className={`${wrapperClassName}`}
  60. >
  61. <div className={className}>
  62. {typeof content === 'string' ? content : React.cloneElement(content)}
  63. {renderArrow &&
  64. renderArrow({
  65. arrowProps,
  66. placement,
  67. })}
  68. </div>
  69. </div>
  70. );
  71. }}
  72. </ReactPopper>
  73. </Portal>
  74. );
  75. }}
  76. </Transition>
  77. </Manager>
  78. );
  79. }
  80. }
  81. export default Popper;