DataHoverView.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import { css } from '@emotion/css';
  2. import React from 'react';
  3. import {
  4. arrayUtils,
  5. DataFrame,
  6. Field,
  7. formattedValueToString,
  8. getFieldDisplayName,
  9. GrafanaTheme2,
  10. LinkModel,
  11. } from '@grafana/data';
  12. import { SortOrder } from '@grafana/schema';
  13. import { LinkButton, useStyles2, VerticalGroup } from '@grafana/ui';
  14. export interface Props {
  15. data?: DataFrame; // source data
  16. rowIndex?: number | null; // the hover row
  17. columnIndex?: number | null; // the hover column
  18. sortOrder?: SortOrder;
  19. }
  20. export const DataHoverView = ({ data, rowIndex, columnIndex, sortOrder }: Props) => {
  21. const styles = useStyles2(getStyles);
  22. if (!data || rowIndex == null) {
  23. return null;
  24. }
  25. const visibleFields = data.fields.filter((f) => !Boolean(f.config.custom?.hideFrom?.tooltip));
  26. if (visibleFields.length === 0) {
  27. return null;
  28. }
  29. const displayValues: Array<[string, any, string]> = [];
  30. const links: Array<LinkModel<Field>> = [];
  31. const linkLookup = new Set<string>();
  32. for (const f of visibleFields) {
  33. const v = f.values.get(rowIndex);
  34. const disp = f.display ? f.display(v) : { text: `${v}`, numeric: +v };
  35. if (f.getLinks) {
  36. f.getLinks({ calculatedValue: disp, valueRowIndex: rowIndex }).forEach((link) => {
  37. const key = `${link.title}/${link.href}`;
  38. if (!linkLookup.has(key)) {
  39. links.push(link);
  40. linkLookup.add(key);
  41. }
  42. });
  43. }
  44. displayValues.push([getFieldDisplayName(f, data), v, formattedValueToString(disp)]);
  45. }
  46. if (sortOrder && sortOrder !== SortOrder.None) {
  47. displayValues.sort((a, b) => arrayUtils.sortValues(sortOrder)(a[1], b[1]));
  48. }
  49. return (
  50. <table className={styles.infoWrap}>
  51. <tbody>
  52. {displayValues.map((v, i) => (
  53. <tr key={`${i}/${rowIndex}`} className={i === columnIndex ? styles.highlight : ''}>
  54. <th>{v[0]}:</th>
  55. <td>{v[2]}</td>
  56. </tr>
  57. ))}
  58. {links.length > 0 && (
  59. <tr>
  60. <td colSpan={2}>
  61. <VerticalGroup>
  62. {links.map((link, i) => (
  63. <LinkButton
  64. key={i}
  65. icon={'external-link-alt'}
  66. target={link.target}
  67. href={link.href}
  68. onClick={link.onClick}
  69. fill="text"
  70. style={{ width: '100%' }}
  71. >
  72. {link.title}
  73. </LinkButton>
  74. ))}
  75. </VerticalGroup>
  76. </td>
  77. </tr>
  78. )}
  79. </tbody>
  80. </table>
  81. );
  82. };
  83. const getStyles = (theme: GrafanaTheme2) => ({
  84. infoWrap: css`
  85. padding: 8px;
  86. th {
  87. font-weight: ${theme.typography.fontWeightMedium};
  88. padding: ${theme.spacing(0.25, 2)};
  89. }
  90. `,
  91. highlight: css`
  92. background: ${theme.colors.action.hover};
  93. `,
  94. });