PanelHeaderMenuTrigger.tsx 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. import React, { FC, HTMLAttributes, MouseEvent, ReactElement, useCallback, useState } from 'react';
  2. import { CartesianCoords2D } from '@grafana/data';
  3. interface PanelHeaderMenuTriggerApi {
  4. panelMenuOpen: boolean;
  5. closeMenu: () => void;
  6. }
  7. interface Props extends HTMLAttributes<HTMLDivElement> {
  8. children: (props: PanelHeaderMenuTriggerApi) => ReactElement;
  9. }
  10. export const PanelHeaderMenuTrigger: FC<Props> = ({ children, ...divProps }) => {
  11. const [clickCoordinates, setClickCoordinates] = useState<CartesianCoords2D>({ x: 0, y: 0 });
  12. const [panelMenuOpen, setPanelMenuOpen] = useState<boolean>(false);
  13. const onMenuToggle = useCallback(
  14. (event: MouseEvent<HTMLDivElement>) => {
  15. if (!isClick(clickCoordinates, eventToClickCoordinates(event))) {
  16. return;
  17. }
  18. setPanelMenuOpen(!panelMenuOpen);
  19. },
  20. [clickCoordinates, panelMenuOpen, setPanelMenuOpen]
  21. );
  22. const onMouseDown = useCallback(
  23. (event: MouseEvent<HTMLDivElement>) => {
  24. setClickCoordinates(eventToClickCoordinates(event));
  25. },
  26. [setClickCoordinates]
  27. );
  28. return (
  29. <header {...divProps} className="panel-title-container" onClick={onMenuToggle} onMouseDown={onMouseDown}>
  30. {children({ panelMenuOpen, closeMenu: () => setPanelMenuOpen(false) })}
  31. </header>
  32. );
  33. };
  34. function isClick(current: CartesianCoords2D, clicked: CartesianCoords2D): boolean {
  35. return clicked.x === current.x && clicked.y === current.y;
  36. }
  37. function eventToClickCoordinates(event: MouseEvent<HTMLDivElement>): CartesianCoords2D {
  38. return {
  39. x: Math.floor(event.clientX),
  40. y: Math.floor(event.clientY),
  41. };
  42. }