valueFormats.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import { getCategories } from './categories';
  2. import { DecimalCount } from '../../types';
  3. export type ValueFormatter = (
  4. value: number,
  5. decimals?: DecimalCount,
  6. scaledDecimals?: DecimalCount,
  7. isUtc?: boolean
  8. ) => string;
  9. export interface ValueFormat {
  10. name: string;
  11. id: string;
  12. fn: ValueFormatter;
  13. }
  14. export interface ValueFormatCategory {
  15. name: string;
  16. formats: ValueFormat[];
  17. }
  18. interface ValueFormatterIndex {
  19. [id: string]: ValueFormatter;
  20. }
  21. // Globals & formats cache
  22. let categories: ValueFormatCategory[] = [];
  23. const index: ValueFormatterIndex = {};
  24. let hasBuiltIndex = false;
  25. export function toFixed(value: number, decimals?: DecimalCount): string {
  26. if (value === null) {
  27. return '';
  28. }
  29. const factor = decimals ? Math.pow(10, Math.max(0, decimals)) : 1;
  30. const formatted = String(Math.round(value * factor) / factor);
  31. // if exponent return directly
  32. if (formatted.indexOf('e') !== -1 || value === 0) {
  33. return formatted;
  34. }
  35. // If tickDecimals was specified, ensure that we have exactly that
  36. // much precision; otherwise default to the value's own precision.
  37. if (decimals != null) {
  38. const decimalPos = formatted.indexOf('.');
  39. const precision = decimalPos === -1 ? 0 : formatted.length - decimalPos - 1;
  40. if (precision < decimals) {
  41. return (precision ? formatted : formatted + '.') + String(factor).substr(1, decimals - precision);
  42. }
  43. }
  44. return formatted;
  45. }
  46. export function toFixedScaled(
  47. value: number,
  48. decimals?: DecimalCount,
  49. scaledDecimals?: DecimalCount,
  50. additionalDecimals?: DecimalCount,
  51. ext?: string
  52. ) {
  53. if (scaledDecimals) {
  54. if (additionalDecimals) {
  55. return toFixed(value, scaledDecimals + additionalDecimals) + ext;
  56. } else {
  57. return toFixed(value, scaledDecimals) + ext;
  58. }
  59. }
  60. return toFixed(value, decimals) + ext;
  61. }
  62. export function toFixedUnit(unit: string): ValueFormatter {
  63. return (size: number, decimals?: DecimalCount) => {
  64. if (size === null) {
  65. return '';
  66. }
  67. return toFixed(size, decimals) + ' ' + unit;
  68. };
  69. }
  70. // Formatter which scales the unit string geometrically according to the given
  71. // numeric factor. Repeatedly scales the value down by the factor until it is
  72. // less than the factor in magnitude, or the end of the array is reached.
  73. export function scaledUnits(factor: number, extArray: string[]) {
  74. return (size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount) => {
  75. if (size === null) {
  76. return '';
  77. }
  78. let steps = 0;
  79. const limit = extArray.length;
  80. while (Math.abs(size) >= factor) {
  81. steps++;
  82. size /= factor;
  83. if (steps >= limit) {
  84. return 'NA';
  85. }
  86. }
  87. if (steps > 0 && scaledDecimals !== null && scaledDecimals !== undefined) {
  88. decimals = scaledDecimals + 3 * steps;
  89. }
  90. return toFixed(size, decimals) + extArray[steps];
  91. };
  92. }
  93. export function locale(value: number, decimals: DecimalCount) {
  94. if (value == null) {
  95. return '';
  96. }
  97. return value.toLocaleString(undefined, { maximumFractionDigits: decimals as number });
  98. }
  99. export function simpleCountUnit(symbol: string) {
  100. const units = ['', 'K', 'M', 'B', 'T'];
  101. const scaler = scaledUnits(1000, units);
  102. return (size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount) => {
  103. if (size === null) {
  104. return '';
  105. }
  106. const scaled = scaler(size, decimals, scaledDecimals);
  107. return scaled + ' ' + symbol;
  108. };
  109. }
  110. function buildFormats() {
  111. categories = getCategories();
  112. for (const cat of categories) {
  113. for (const format of cat.formats) {
  114. index[format.id] = format.fn;
  115. }
  116. }
  117. hasBuiltIndex = true;
  118. }
  119. export function getValueFormat(id: string): ValueFormatter {
  120. if (!hasBuiltIndex) {
  121. buildFormats();
  122. }
  123. return index[id];
  124. }
  125. export function getValueFormatterIndex(): ValueFormatterIndex {
  126. if (!hasBuiltIndex) {
  127. buildFormats();
  128. }
  129. return index;
  130. }
  131. export function getValueFormats() {
  132. if (!hasBuiltIndex) {
  133. buildFormats();
  134. }
  135. return categories.map(cat => {
  136. return {
  137. text: cat.name,
  138. submenu: cat.formats.map(format => {
  139. return {
  140. text: format.name,
  141. value: format.id,
  142. };
  143. }),
  144. };
  145. });
  146. }