diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index 4d9b9a4b948..cd46a5b6424 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -1 +1,2 @@ export * from './processTimeSeries'; +export * from './valueFormats'; diff --git a/packages/grafana-ui/src/utils/valueFormats.ts b/packages/grafana-ui/src/utils/valueFormats.ts new file mode 100644 index 00000000000..d76db20fb0b --- /dev/null +++ b/packages/grafana-ui/src/utils/valueFormats.ts @@ -0,0 +1,104 @@ +type ValueFormatter = (value: number, decimals?: number, scaledDecimals?: number) => string; + +interface ValueFormat { + name: string; + id: string; + fn: ValueFormatter; +} + +interface ValueFormatCategory { + name: string; + formats: ValueFormat[]; +} + +interface ValueFormatterIndex { + [id: string]: ValueFormatter; +} + +// Globals & formats cache +let categories: ValueFormatCategory[] = []; +const index: ValueFormatterIndex = {}; +let hasBuildIndex = false; + +function toFixed(value: number, decimals?: number): string { + if (value === null) { + return ''; + } + + const factor = decimals ? Math.pow(10, Math.max(0, decimals)) : 1; + const formatted = String(Math.round(value * factor) / factor); + + // if exponent return directly + if (formatted.indexOf('e') !== -1 || value === 0) { + return formatted; + } + + // If tickDecimals was specified, ensure that we have exactly that + // much precision; otherwise default to the value's own precision. + if (decimals != null) { + const decimalPos = formatted.indexOf('.'); + const precision = decimalPos === -1 ? 0 : formatted.length - decimalPos - 1; + if (precision < decimals) { + return (precision ? formatted : formatted + '.') + String(factor).substr(1, decimals - precision); + } + } + + return formatted; +} + +function buildFormats() { + categories = [ + { + name: 'none', + formats: [ + { + name: 'short', + id: 'short', + fn: toFixed, + }, + ], + }, + ]; + + for (const cat of categories) { + for (const format of cat.formats) { + index[format.id] = format.fn; + } + } + + hasBuildIndex = true; +} + +export function getValueFormat(id: string): ValueFormatter { + if (!hasBuildIndex) { + buildFormats(); + } + + return index[id]; +} + +export function getValueFormatterIndex(): ValueFormatterIndex { + if (!hasBuildIndex) { + buildFormats(); + } + + return index; +} + +export function getUnitFormats() { + if (!hasBuildIndex) { + buildFormats(); + } + + return categories.map(cat => { + return { + text: cat.name, + submenu: cat.formats.map(format => { + return { + text: format.name, + value: format.id, + }; + }), + }; + }); +} diff --git a/public/app/core/utils/kbn.ts b/public/app/core/utils/kbn.ts index d32844c44ed..2fae6300d16 100644 --- a/public/app/core/utils/kbn.ts +++ b/public/app/core/utils/kbn.ts @@ -1,5 +1,6 @@ import _ from 'lodash'; import moment from 'moment'; +import { getValueFormat, getValueFormatterIndex } from '@grafana/ui'; const kbn: any = {}; @@ -1218,4 +1219,24 @@ kbn.getUnitFormats = () => { ]; }; +if (typeof Proxy !== "undefined") { + kbn.valueFormats = new Proxy(kbn.valueFormats, { + get(target, name, receiver) { + if (typeof name !== 'string') { + throw {message: `Value format ${String(name)} is not a string` }; + } + + const formatter = getValueFormat(name); + if (formatter) { + return formatter; + } + + // default to look here + return Reflect.get(target, name, receiver); + } + }); +} else { + kbn.valueFormats = getValueFormatterIndex(); +} + export default kbn; diff --git a/public/app/plugins/panel/graph2/GraphPanel.tsx b/public/app/plugins/panel/graph2/GraphPanel.tsx index a08276e5179..3429200d52b 100644 --- a/public/app/plugins/panel/graph2/GraphPanel.tsx +++ b/public/app/plugins/panel/graph2/GraphPanel.tsx @@ -3,8 +3,14 @@ import _ from 'lodash'; import React, { PureComponent } from 'react'; import colors from 'app/core/utils/colors'; -// Components & Types -import { Graph, PanelProps, NullValueMode, processTimeSeries } from '@grafana/ui'; +// Utils +import { processTimeSeries } from '@grafana/ui/src/utils'; + +// Components +import { Graph } from '@grafana/ui'; + +// Types +import { PanelProps, NullValueMode } from '@grafana/ui/src/types'; import { Options } from './types'; interface Props extends PanelProps {}