You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

117 lines
3.2 KiB

  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import { toShortNumber, pluralReady, DECIMAL_UNITS } from '../utils/numbers';
  4. import { FormattedMessage, FormattedNumber } from 'react-intl';
  5. // @ts-check
  6. /**
  7. * @callback ShortNumberRenderer
  8. * @param {JSX.Element} displayNumber Number to display
  9. * @param {number} pluralReady Number used for pluralization
  10. * @returns {JSX.Element} Final render of number
  11. */
  12. /**
  13. * @typedef {object} ShortNumberProps
  14. * @property {number} value Number to display in short variant
  15. * @property {ShortNumberRenderer} [renderer]
  16. * Custom renderer for numbers, provided as a prop. If another renderer
  17. * passed as a child of this component, this prop won't be used.
  18. * @property {ShortNumberRenderer} [children]
  19. * Custom renderer for numbers, provided as a child. If another renderer
  20. * passed as a prop of this component, this one will be used instead.
  21. */
  22. /**
  23. * Component that renders short big number to a shorter version
  24. *
  25. * @param {ShortNumberProps} param0 Props for the component
  26. * @returns {JSX.Element} Rendered number
  27. */
  28. function ShortNumber({ value, renderer, children }) {
  29. const shortNumber = toShortNumber(value);
  30. const [, division] = shortNumber;
  31. // eslint-disable-next-line eqeqeq
  32. if (children != null && renderer != null) {
  33. console.warn('Both renderer prop and renderer as a child provided. This is a mistake and you really should fix that. Only renderer passed as a child will be used.');
  34. }
  35. // eslint-disable-next-line eqeqeq
  36. const customRenderer = children != null ? children : renderer;
  37. const displayNumber = <ShortNumberCounter value={shortNumber} />;
  38. // eslint-disable-next-line eqeqeq
  39. return customRenderer != null
  40. ? customRenderer(displayNumber, pluralReady(value, division))
  41. : displayNumber;
  42. }
  43. ShortNumber.propTypes = {
  44. value: PropTypes.number.isRequired,
  45. renderer: PropTypes.func,
  46. children: PropTypes.func,
  47. };
  48. /**
  49. * @typedef {object} ShortNumberCounterProps
  50. * @property {import('../utils/number').ShortNumber} value Short number
  51. */
  52. /**
  53. * Renders short number into corresponding localizable react fragment
  54. *
  55. * @param {ShortNumberCounterProps} param0 Props for the component
  56. * @returns {JSX.Element} FormattedMessage ready to be embedded in code
  57. */
  58. function ShortNumberCounter({ value }) {
  59. const [rawNumber, unit, maxFractionDigits = 0] = value;
  60. const count = (
  61. <FormattedNumber
  62. value={rawNumber}
  63. maximumFractionDigits={maxFractionDigits}
  64. />
  65. );
  66. let values = { count, rawNumber };
  67. switch (unit) {
  68. case DECIMAL_UNITS.THOUSAND: {
  69. return (
  70. <FormattedMessage
  71. id='units.short.thousand'
  72. defaultMessage='{count}K'
  73. values={values}
  74. />
  75. );
  76. }
  77. case DECIMAL_UNITS.MILLION: {
  78. return (
  79. <FormattedMessage
  80. id='units.short.million'
  81. defaultMessage='{count}M'
  82. values={values}
  83. />
  84. );
  85. }
  86. case DECIMAL_UNITS.BILLION: {
  87. return (
  88. <FormattedMessage
  89. id='units.short.billion'
  90. defaultMessage='{count}B'
  91. values={values}
  92. />
  93. );
  94. }
  95. // Not sure if we should go farther - @Sasha-Sorokin
  96. default: return count;
  97. }
  98. }
  99. ShortNumberCounter.propTypes = {
  100. value: PropTypes.arrayOf(PropTypes.number),
  101. };
  102. export default React.memo(ShortNumber);