config.ts 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import { Dispatch, MutableRefObject, SetStateAction } from 'react';
  2. import { CartesianCoords2D } from '@grafana/data';
  3. import { UPlotConfigBuilder } from '@grafana/ui';
  4. import { positionTooltip } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin';
  5. export type HoverEvent = {
  6. xIndex: number;
  7. yIndex: number;
  8. pageX: number;
  9. pageY: number;
  10. };
  11. type SetupConfigParams = {
  12. config: UPlotConfigBuilder;
  13. onUPlotClick: () => void;
  14. setFocusedSeriesIdx: Dispatch<SetStateAction<number | null>>;
  15. setFocusedPointIdx: Dispatch<SetStateAction<number | null>>;
  16. setCoords: Dispatch<SetStateAction<CartesianCoords2D | null>>;
  17. setHover: Dispatch<SetStateAction<HoverEvent | undefined>>;
  18. isToolTipOpen: MutableRefObject<boolean>;
  19. };
  20. // This applies config hooks to setup tooltip listener. Ideally this could happen in the same `prepConfig` function
  21. // however the GraphNG structures do not allow access to the `setHover` callback
  22. export const setupConfig = ({
  23. config,
  24. onUPlotClick,
  25. setFocusedSeriesIdx,
  26. setFocusedPointIdx,
  27. setCoords,
  28. setHover,
  29. isToolTipOpen,
  30. }: SetupConfigParams): UPlotConfigBuilder => {
  31. config.addHook('init', (u) => {
  32. u.root.parentElement?.addEventListener('click', onUPlotClick);
  33. u.over.addEventListener('mouseleave', () => {
  34. if (!isToolTipOpen.current) {
  35. setCoords(null);
  36. }
  37. });
  38. });
  39. let rect: DOMRect;
  40. // rect of .u-over (grid area)
  41. config.addHook('syncRect', (u, r) => {
  42. rect = r;
  43. });
  44. const tooltipInterpolator = config.getTooltipInterpolator();
  45. if (tooltipInterpolator) {
  46. config.addHook('setCursor', (u) => {
  47. tooltipInterpolator(
  48. setFocusedSeriesIdx,
  49. setFocusedPointIdx,
  50. (clear) => {
  51. if (clear && !isToolTipOpen.current) {
  52. setCoords(null);
  53. return;
  54. }
  55. if (!rect) {
  56. return;
  57. }
  58. const { x, y } = positionTooltip(u, rect);
  59. if (x !== undefined && y !== undefined && !isToolTipOpen.current) {
  60. setCoords({ x, y });
  61. }
  62. },
  63. u
  64. );
  65. });
  66. }
  67. config.addHook('setLegend', (u) => {
  68. if (!isToolTipOpen.current) {
  69. setFocusedPointIdx(u.legend.idx!);
  70. }
  71. if (u.cursor.idxs != null) {
  72. for (let i = 0; i < u.cursor.idxs.length; i++) {
  73. const sel = u.cursor.idxs[i];
  74. if (sel != null) {
  75. const hover: HoverEvent = {
  76. xIndex: sel,
  77. yIndex: 0,
  78. pageX: rect.left + u.cursor.left!,
  79. pageY: rect.top + u.cursor.top!,
  80. };
  81. if (!isToolTipOpen.current || !hover) {
  82. setHover(hover);
  83. }
  84. return; // only show the first one
  85. }
  86. }
  87. }
  88. });
  89. config.addHook('setSeries', (_, idx) => {
  90. if (!isToolTipOpen.current) {
  91. setFocusedSeriesIdx(idx);
  92. }
  93. });
  94. return config;
  95. };