displayValue.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // Libraries
  2. import _ from 'lodash';
  3. import {
  4. Threshold,
  5. getMappedValue,
  6. FieldConfig,
  7. DisplayProcessor,
  8. DecimalInfo,
  9. DisplayValue,
  10. DecimalCount,
  11. } from '@grafana/data';
  12. // Utils
  13. import { getValueFormat } from './valueFormats/valueFormats';
  14. import { getColorFromHexRgbOrName } from './namedColorsPalette';
  15. // Types
  16. import { GrafanaTheme, GrafanaThemeType } from '../types';
  17. export interface DisplayValueOptions {
  18. field?: FieldConfig;
  19. // Context
  20. isUtc?: boolean;
  21. theme?: GrafanaTheme; // Will pick 'dark' if not defined
  22. }
  23. export function getDisplayProcessor(options?: DisplayValueOptions): DisplayProcessor {
  24. if (options && !_.isEmpty(options)) {
  25. const field = options.field ? options.field : {};
  26. const formatFunc = getValueFormat(field.unit || 'none');
  27. return (value: any) => {
  28. const { theme } = options;
  29. const { mappings, thresholds } = field;
  30. let color;
  31. let text = _.toString(value);
  32. let numeric = toNumber(value);
  33. let shouldFormat = true;
  34. if (mappings && mappings.length > 0) {
  35. const mappedValue = getMappedValue(mappings, value);
  36. if (mappedValue) {
  37. text = mappedValue.text;
  38. const v = toNumber(text);
  39. if (!isNaN(v)) {
  40. numeric = v;
  41. }
  42. shouldFormat = false;
  43. }
  44. }
  45. if (!isNaN(numeric)) {
  46. if (shouldFormat && !_.isBoolean(value)) {
  47. const { decimals, scaledDecimals } = getDecimalsForValue(value, field.decimals);
  48. text = formatFunc(numeric, decimals, scaledDecimals, options.isUtc);
  49. }
  50. if (thresholds && thresholds.length) {
  51. color = getColorFromThreshold(numeric, thresholds, theme);
  52. }
  53. }
  54. if (!text) {
  55. if (field && field.noValue) {
  56. text = field.noValue;
  57. } else {
  58. text = ''; // No data?
  59. }
  60. }
  61. return { text, numeric, color };
  62. };
  63. }
  64. return toStringProcessor;
  65. }
  66. /** Will return any value as a number or NaN */
  67. function toNumber(value: any): number {
  68. if (typeof value === 'number') {
  69. return value;
  70. }
  71. if (value === null || value === undefined || Array.isArray(value)) {
  72. return NaN; // lodash calls them 0
  73. }
  74. if (typeof value === 'boolean') {
  75. return value ? 1 : 0;
  76. }
  77. return _.toNumber(value);
  78. }
  79. function toStringProcessor(value: any): DisplayValue {
  80. return { text: _.toString(value), numeric: toNumber(value) };
  81. }
  82. export function getColorFromThreshold(value: number, thresholds: Threshold[], theme?: GrafanaTheme): string {
  83. const themeType = theme ? theme.type : GrafanaThemeType.Dark;
  84. if (thresholds.length === 1) {
  85. return getColorFromHexRgbOrName(thresholds[0].color, themeType);
  86. }
  87. const atThreshold = thresholds.filter(threshold => value === threshold.value)[0];
  88. if (atThreshold) {
  89. return getColorFromHexRgbOrName(atThreshold.color, themeType);
  90. }
  91. const belowThreshold = thresholds.filter(threshold => value > threshold.value);
  92. if (belowThreshold.length > 0) {
  93. const nearestThreshold = belowThreshold.sort((t1, t2) => t2.value - t1.value)[0];
  94. return getColorFromHexRgbOrName(nearestThreshold.color, themeType);
  95. }
  96. // Use the first threshold as the default color
  97. return getColorFromHexRgbOrName(thresholds[0].color, themeType);
  98. }
  99. export function getDecimalsForValue(value: number, decimalOverride?: DecimalCount): DecimalInfo {
  100. if (_.isNumber(decimalOverride)) {
  101. // It's important that scaledDecimals is null here
  102. return { decimals: decimalOverride, scaledDecimals: null };
  103. }
  104. const delta = value / 2;
  105. let dec = -Math.floor(Math.log(delta) / Math.LN10);
  106. const magn = Math.pow(10, -dec);
  107. const norm = delta / magn; // norm is between 1.0 and 10.0
  108. let size;
  109. if (norm < 1.5) {
  110. size = 1;
  111. } else if (norm < 3) {
  112. size = 2;
  113. // special case for 2.5, requires an extra decimal
  114. if (norm > 2.25) {
  115. size = 2.5;
  116. ++dec;
  117. }
  118. } else if (norm < 7.5) {
  119. size = 5;
  120. } else {
  121. size = 10;
  122. }
  123. size *= magn;
  124. // reduce starting decimals if not needed
  125. if (Math.floor(value) === value) {
  126. dec = 0;
  127. }
  128. const decimals = Math.max(0, dec);
  129. const scaledDecimals = decimals - Math.floor(Math.log(size) / Math.LN10) + 2;
  130. return { decimals, scaledDecimals };
  131. }