Popper.tsx 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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, scheduleUpdate }) => {
  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'
  63. ? content
  64. : React.cloneElement(content, {
  65. updatePopperPosition: scheduleUpdate,
  66. })}
  67. {renderArrow &&
  68. renderArrow({
  69. arrowProps,
  70. placement,
  71. })}
  72. </div>
  73. </div>
  74. );
  75. }}
  76. </ReactPopper>
  77. </Portal>
  78. );
  79. }}
  80. </Transition>
  81. </Manager>
  82. );
  83. }
  84. }
  85. export default Popper;