Rewrite `<ShortNumber />` as FC and TS (#25492)
parent
e0d230fb37
commit
20e85c0e83
10 changed files with 98 additions and 123 deletions
@ -1,115 +0,0 @@ |
|||||||
import PropTypes from 'prop-types'; |
|
||||||
import { memo } from 'react'; |
|
||||||
|
|
||||||
import { FormattedMessage, FormattedNumber } from 'react-intl'; |
|
||||||
|
|
||||||
import { toShortNumber, pluralReady, DECIMAL_UNITS } from '../utils/numbers'; |
|
||||||
|
|
||||||
// @ts-check |
|
||||||
|
|
||||||
/** |
|
||||||
* @callback ShortNumberRenderer |
|
||||||
* @param {JSX.Element} displayNumber Number to display |
|
||||||
* @param {number} pluralReady Number used for pluralization |
|
||||||
* @returns {JSX.Element} Final render of number |
|
||||||
*/ |
|
||||||
|
|
||||||
/** |
|
||||||
* @typedef {object} ShortNumberProps |
|
||||||
* @property {number} value Number to display in short variant |
|
||||||
* @property {ShortNumberRenderer} [renderer] |
|
||||||
* Custom renderer for numbers, provided as a prop. If another renderer |
|
||||||
* passed as a child of this component, this prop won't be used. |
|
||||||
* @property {ShortNumberRenderer} [children] |
|
||||||
* Custom renderer for numbers, provided as a child. If another renderer |
|
||||||
* passed as a prop of this component, this one will be used instead. |
|
||||||
*/ |
|
||||||
|
|
||||||
/** |
|
||||||
* Component that renders short big number to a shorter version |
|
||||||
* @param {ShortNumberProps} param0 Props for the component |
|
||||||
* @returns {JSX.Element} Rendered number |
|
||||||
*/ |
|
||||||
function ShortNumber({ value, renderer, children }) { |
|
||||||
const shortNumber = toShortNumber(value); |
|
||||||
const [, division] = shortNumber; |
|
||||||
|
|
||||||
if (children != null && renderer != null) { |
|
||||||
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.'); |
|
||||||
} |
|
||||||
|
|
||||||
const customRenderer = children != null ? children : renderer; |
|
||||||
|
|
||||||
const displayNumber = <ShortNumberCounter value={shortNumber} />; |
|
||||||
|
|
||||||
return customRenderer != null |
|
||||||
? customRenderer(displayNumber, pluralReady(value, division)) |
|
||||||
: displayNumber; |
|
||||||
} |
|
||||||
|
|
||||||
ShortNumber.propTypes = { |
|
||||||
value: PropTypes.number.isRequired, |
|
||||||
renderer: PropTypes.func, |
|
||||||
children: PropTypes.func, |
|
||||||
}; |
|
||||||
|
|
||||||
/** |
|
||||||
* @typedef {object} ShortNumberCounterProps |
|
||||||
* @property {import('../utils/number').ShortNumber} value Short number |
|
||||||
*/ |
|
||||||
|
|
||||||
/** |
|
||||||
* Renders short number into corresponding localizable react fragment |
|
||||||
* @param {ShortNumberCounterProps} param0 Props for the component |
|
||||||
* @returns {JSX.Element} FormattedMessage ready to be embedded in code |
|
||||||
*/ |
|
||||||
function ShortNumberCounter({ value }) { |
|
||||||
const [rawNumber, unit, maxFractionDigits = 0] = value; |
|
||||||
|
|
||||||
const count = ( |
|
||||||
<FormattedNumber |
|
||||||
value={rawNumber} |
|
||||||
maximumFractionDigits={maxFractionDigits} |
|
||||||
/> |
|
||||||
); |
|
||||||
|
|
||||||
let values = { count, rawNumber }; |
|
||||||
|
|
||||||
switch (unit) { |
|
||||||
case DECIMAL_UNITS.THOUSAND: { |
|
||||||
return ( |
|
||||||
<FormattedMessage |
|
||||||
id='units.short.thousand' |
|
||||||
defaultMessage='{count}K' |
|
||||||
values={values} |
|
||||||
/> |
|
||||||
); |
|
||||||
} |
|
||||||
case DECIMAL_UNITS.MILLION: { |
|
||||||
return ( |
|
||||||
<FormattedMessage |
|
||||||
id='units.short.million' |
|
||||||
defaultMessage='{count}M' |
|
||||||
values={values} |
|
||||||
/> |
|
||||||
); |
|
||||||
} |
|
||||||
case DECIMAL_UNITS.BILLION: { |
|
||||||
return ( |
|
||||||
<FormattedMessage |
|
||||||
id='units.short.billion' |
|
||||||
defaultMessage='{count}B' |
|
||||||
values={values} |
|
||||||
/> |
|
||||||
); |
|
||||||
} |
|
||||||
// Not sure if we should go farther - @Sasha-Sorokin |
|
||||||
default: return count; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
ShortNumberCounter.propTypes = { |
|
||||||
value: PropTypes.arrayOf(PropTypes.number), |
|
||||||
}; |
|
||||||
|
|
||||||
export default memo(ShortNumber); |
|
@ -0,0 +1,90 @@ |
|||||||
|
import { memo } from 'react'; |
||||||
|
|
||||||
|
import { FormattedMessage, FormattedNumber } from 'react-intl'; |
||||||
|
|
||||||
|
import { toShortNumber, pluralReady, DECIMAL_UNITS } from '../utils/numbers'; |
||||||
|
|
||||||
|
type ShortNumberRenderer = ( |
||||||
|
displayNumber: JSX.Element, |
||||||
|
pluralReady: number |
||||||
|
) => JSX.Element; |
||||||
|
|
||||||
|
interface ShortNumberProps { |
||||||
|
value: number; |
||||||
|
renderer?: ShortNumberRenderer; |
||||||
|
children?: ShortNumberRenderer; |
||||||
|
} |
||||||
|
|
||||||
|
export const ShortNumberRenderer: React.FC<ShortNumberProps> = ({ |
||||||
|
value, |
||||||
|
renderer, |
||||||
|
children, |
||||||
|
}) => { |
||||||
|
const shortNumber = toShortNumber(value); |
||||||
|
const [, division] = shortNumber; |
||||||
|
|
||||||
|
if (children && renderer) { |
||||||
|
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.' |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
const customRenderer = children || renderer || null; |
||||||
|
|
||||||
|
const displayNumber = <ShortNumberCounter value={shortNumber} />; |
||||||
|
|
||||||
|
return ( |
||||||
|
customRenderer?.(displayNumber, pluralReady(value, division)) || |
||||||
|
displayNumber |
||||||
|
); |
||||||
|
}; |
||||||
|
export const ShortNumber = memo(ShortNumberRenderer); |
||||||
|
|
||||||
|
interface ShortNumberCounterProps { |
||||||
|
value: number[]; |
||||||
|
} |
||||||
|
const ShortNumberCounter: React.FC<ShortNumberCounterProps> = ({ value }) => { |
||||||
|
const [rawNumber, unit, maxFractionDigits = 0] = value; |
||||||
|
|
||||||
|
const count = ( |
||||||
|
<FormattedNumber |
||||||
|
value={rawNumber} |
||||||
|
maximumFractionDigits={maxFractionDigits} |
||||||
|
/> |
||||||
|
); |
||||||
|
|
||||||
|
const values = { count, rawNumber }; |
||||||
|
|
||||||
|
switch (unit) { |
||||||
|
case DECIMAL_UNITS.THOUSAND: { |
||||||
|
return ( |
||||||
|
<FormattedMessage |
||||||
|
id='units.short.thousand' |
||||||
|
defaultMessage='{count}K' |
||||||
|
values={values} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
case DECIMAL_UNITS.MILLION: { |
||||||
|
return ( |
||||||
|
<FormattedMessage |
||||||
|
id='units.short.million' |
||||||
|
defaultMessage='{count}M' |
||||||
|
values={values} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
case DECIMAL_UNITS.BILLION: { |
||||||
|
return ( |
||||||
|
<FormattedMessage |
||||||
|
id='units.short.billion' |
||||||
|
defaultMessage='{count}B' |
||||||
|
values={values} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
// Not sure if we should go farther - @Sasha-Sorokin
|
||||||
|
default: |
||||||
|
return count; |
||||||
|
} |
||||||
|
}; |
Loading…
Reference in new issue