diff options
Diffstat (limited to 'app/assets/javascripts/lib/utils/unit_format/formatter_factory.js')
-rw-r--r-- | app/assets/javascripts/lib/utils/unit_format/formatter_factory.js | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/app/assets/javascripts/lib/utils/unit_format/formatter_factory.js b/app/assets/javascripts/lib/utils/unit_format/formatter_factory.js new file mode 100644 index 00000000000..432a9254558 --- /dev/null +++ b/app/assets/javascripts/lib/utils/unit_format/formatter_factory.js @@ -0,0 +1,119 @@ +/** + * Formats a number as string using `toLocaleString`. + * + * @param {Number} number to be converted + * @param {params} Parameters + * @param {params.fractionDigits} Number of decimal digits + * to display, defaults to using `toLocaleString` defaults. + * @param {params.maxLength} Max output char lenght at the + * expense of precision, if the output is longer than this, + * the formatter switches to using exponential notation. + * @param {params.factor} Value is multiplied by this factor, + * useful for value normalization. + * @returns Formatted value + */ +function formatNumber( + value, + { fractionDigits = undefined, valueFactor = 1, style = undefined, maxLength = undefined }, +) { + if (value === null) { + return ''; + } + + const num = value * valueFactor; + const formatted = num.toLocaleString(undefined, { + minimumFractionDigits: fractionDigits, + maximumFractionDigits: fractionDigits, + style, + }); + + if (maxLength !== undefined && formatted.length > maxLength) { + // 123456 becomes 1.23e+8 + return num.toExponential(2); + } + return formatted; +} + +/** + * Formats a number as a string scaling it up according to units. + * + * While the number is scaled down, the units are scaled up. + * + * @param {Array} List of units of the scale + * @param {Number} unitFactor - Factor of the scale for each + * unit after which the next unit is used scaled. + */ +const scaledFormatter = (units, unitFactor = 1000) => { + if (unitFactor === 0) { + return new RangeError(`unitFactor cannot have the value 0.`); + } + + return (value, fractionDigits) => { + if (value === null) { + return ''; + } + if ( + value === Number.NEGATIVE_INFINITY || + value === Number.POSITIVE_INFINITY || + Number.isNaN(value) + ) { + return value.toLocaleString(undefined); + } + + let num = value; + let scale = 0; + const limit = units.length; + + while (Math.abs(num) >= unitFactor) { + scale += 1; + num /= unitFactor; + + if (scale >= limit) { + return 'NA'; + } + } + + const unit = units[scale]; + + return `${formatNumber(num, { fractionDigits })}${unit}`; + }; +}; + +/** + * Returns a function that formats a number as a string. + */ +export const numberFormatter = (style = 'decimal', valueFactor = 1) => { + return (value, fractionDigits, maxLength) => { + return `${formatNumber(value, { fractionDigits, maxLength, valueFactor, style })}`; + }; +}; + +/** + * Returns a function that formats a number as a string with a suffix. + */ +export const suffixFormatter = (unit = '', valueFactor = 1) => { + return (value, fractionDigits, maxLength) => { + const length = maxLength !== undefined ? maxLength - unit.length : undefined; + return `${formatNumber(value, { fractionDigits, maxLength: length, valueFactor })}${unit}`; + }; +}; + +/** + * Returns a function that formats a number scaled using SI units notation. + */ +export const scaledSIFormatter = (unit = '', prefixOffset = 0) => { + const fractional = ['y', 'z', 'a', 'f', 'p', 'n', 'ยต', 'm']; + const multiplicative = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; + const symbols = [...fractional, '', ...multiplicative]; + + const units = symbols.slice(fractional.length + prefixOffset).map(prefix => { + return `${prefix}${unit}`; + }); + + if (!units.length) { + // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + throw new RangeError('The unit cannot be converted, please try a different scale'); + } + + return scaledFormatter(units); +}; |