chronograf/ui/src/utils/formatting.ts

156 lines
4.5 KiB
TypeScript

import _ from 'lodash'
const KMB_LABELS: string[] = ['K', 'M', 'B', 'T', 'Q']
const KMG2_BIG_LABELS: string[] = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
const KMG2_SMALL_LABELS: string[] = ['m', 'u', 'n', 'p', 'f', 'a', 'z', 'y']
const pow = (base: number, exp: number): number => {
if (exp < 0) {
return 1.0 / Math.pow(base, -exp)
}
return Math.pow(base, exp)
}
const roundNum = (num, places): number => {
const shift = Math.pow(10, places)
return Math.round(num * shift) / shift
}
const floatFormat = (x: number, optPrecision: number): string => {
// Avoid invalid precision values; [1, 21] is the valid range.
const p = Math.min(Math.max(1, optPrecision || 2), 21)
// This is deceptively simple. The actual algorithm comes from:
//
// Max allowed length = p + 4
// where 4 comes from 'e+n' and '.'.
//
// Length of fixed format = 2 + y + p
// where 2 comes from '0.' and y = # of leading zeroes.
//
// Equating the two and solving for y yields y = 2, or 0.00xxxx which is
// 1.0e-3.
//
// Since the behavior of toPrecision() is identical for larger numbers, we
// don't have to worry about the other bound.
//
// Finally, the argument for toExponential() is the number of trailing digits,
// so we take off 1 for the value before the '.'.
return Math.abs(x) < 1.0e-3 && x !== 0.0
? x.toExponential(p - 1)
: x.toPrecision(p)
}
// taken from https://github.com/danvk/dygraphs/blob/aaec6de56dba8ed712fd7b9d949de47b46a76ccd/src/dygraph-utils.js#L1103
export const numberValueFormatter = (
x: number,
opts: (name: string) => number,
prefix: string,
suffix: string
): string => {
const sigFigs = opts('sigFigs')
if (sigFigs !== null) {
// User has opted for a fixed number of significant figures.
return floatFormat(x, sigFigs)
}
const digits = opts('digitsAfterDecimal')
const maxNumberWidth = opts('maxNumberWidth')
const kmb = opts('labelsKMB')
const kmg2 = opts('labelsKMG2')
let label
// switch to scientific notation if we underflow or overflow fixed display.
if (
x !== 0.0 &&
(Math.abs(x) >= Math.pow(10, maxNumberWidth) ||
Math.abs(x) < Math.pow(10, -digits))
) {
label = x.toExponential(digits)
} else {
label = `${roundNum(x, digits)}`
}
if (kmb || kmg2) {
let k
let kLabels = []
let mLabels = []
if (kmb) {
k = 1000
kLabels = KMB_LABELS
}
if (kmg2) {
if (kmb) {
console.error('Setting both labelsKMB and labelsKMG2. Pick one!')
}
k = 1024
kLabels = KMG2_BIG_LABELS
mLabels = KMG2_SMALL_LABELS
}
const absx = Math.abs(x)
let n = pow(k, kLabels.length)
for (let j = kLabels.length - 1; j >= 0; j -= 1, n /= k) {
if (absx >= n) {
label = roundNum(x / n, digits) + kLabels[j]
break
}
}
if (kmg2) {
const xParts = String(x.toExponential()).split('e-').map(Number)
if (xParts.length === 2 && xParts[1] >= 3 && xParts[1] <= 24) {
if (xParts[1] % 3 > 0) {
label = roundNum(xParts[0] / pow(10, xParts[1] % 3), digits)
} else {
label = Number(xParts[0]).toFixed(2)
}
label += mLabels[Math.floor(xParts[1] / 3) - 1]
}
}
}
return `${prefix}${label}${suffix}`
}
export const formatRPDuration = (duration: string | null): string => {
if (!duration) {
return
}
if (duration === '0' || duration === '0s') {
return '∞'
}
let adjustedTime = duration
const durationMatcher = /(?:(\d*)d)?(?:(\d*)h)?(?:(\d*)m)?(?:(\d*)s)?/
const result = duration.match(durationMatcher)
const days = _.get<string, string>(result, 1, '0')
const hours = _.get<string, string>(result, 2, '0')
const minutes = _.get<string, string>(result, 3, '0')
const seconds = _.get<string, string>(result, 4, '0')
const hoursInDay = 24
if (+days) {
adjustedTime = `${days}d`
adjustedTime += +hours === 0 ? '' : `${hours}h`
adjustedTime += +minutes === 0 ? '' : `${minutes}m`
adjustedTime += +seconds === 0 ? '' : `${seconds}s`
} else if (+hours > hoursInDay) {
const hoursRemainder = +hours % hoursInDay
const daysQuotient = (+hours - hoursRemainder) / hoursInDay
adjustedTime = `${daysQuotient}d`
adjustedTime += +hoursRemainder === 0 ? '' : `${hoursRemainder}h`
adjustedTime += +minutes === 0 ? '' : `${minutes}m`
adjustedTime += +seconds === 0 ? '' : `${seconds}s`
} else {
adjustedTime = `${hours}h`
adjustedTime += +minutes === 0 ? '' : `${minutes}m`
adjustedTime += +seconds === 0 ? '' : `${seconds}s`
}
return adjustedTime
}