valueFormats.ts 3.8 KB

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