87 lines
2.2 KiB
TypeScript
87 lines
2.2 KiB
TypeScript
export function asNumber(value: string | number | null | undefined) {
|
|
if (value === null || value === undefined) {
|
|
return 0;
|
|
}
|
|
|
|
const parsed = typeof value === 'number' ? value : Number(value);
|
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
}
|
|
|
|
type NumberScale = {
|
|
divisor: number;
|
|
suffix: string;
|
|
};
|
|
|
|
const NUMBER_SCALES: NumberScale[] = [
|
|
{ divisor: 1, suffix: '' },
|
|
{ divisor: 1_000, suffix: 'K' },
|
|
{ divisor: 1_000_000, suffix: 'M' },
|
|
{ divisor: 1_000_000_000, suffix: 'B' }
|
|
];
|
|
|
|
type FormatScaledNumberOptions = {
|
|
minimumFractionDigits?: number;
|
|
maximumFractionDigits?: number;
|
|
};
|
|
|
|
export function formatScaledNumber(
|
|
value: string | number | null | undefined,
|
|
options: FormatScaledNumberOptions = {}
|
|
) {
|
|
const {
|
|
minimumFractionDigits = 0,
|
|
maximumFractionDigits = 1
|
|
} = options;
|
|
|
|
const numeric = asNumber(value);
|
|
const absolute = Math.abs(numeric);
|
|
|
|
let scaleIndex = 0;
|
|
if (absolute >= 1_000_000_000) {
|
|
scaleIndex = 3;
|
|
} else if (absolute >= 1_000_000) {
|
|
scaleIndex = 2;
|
|
} else if (absolute >= 1_000) {
|
|
scaleIndex = 1;
|
|
}
|
|
|
|
let scaledAbsolute = absolute / NUMBER_SCALES[scaleIndex].divisor;
|
|
|
|
if (
|
|
Number(scaledAbsolute.toFixed(maximumFractionDigits)) >= 1_000
|
|
&& scaleIndex < NUMBER_SCALES.length - 1
|
|
) {
|
|
scaleIndex += 1;
|
|
scaledAbsolute = absolute / NUMBER_SCALES[scaleIndex].divisor;
|
|
}
|
|
|
|
const scaled = numeric < 0 ? -scaledAbsolute : scaledAbsolute;
|
|
const formatted = new Intl.NumberFormat('en-US', {
|
|
minimumFractionDigits,
|
|
maximumFractionDigits
|
|
}).format(scaled);
|
|
|
|
return `${formatted}${NUMBER_SCALES[scaleIndex].suffix}`;
|
|
}
|
|
|
|
export function formatCurrency(value: string | number | null | undefined) {
|
|
return new Intl.NumberFormat('en-US', {
|
|
style: 'currency',
|
|
currency: 'USD',
|
|
maximumFractionDigits: 2
|
|
}).format(asNumber(value));
|
|
}
|
|
|
|
export function formatPercent(value: string | number | null | undefined) {
|
|
return `${asNumber(value).toFixed(2)}%`;
|
|
}
|
|
|
|
export function formatCompactCurrency(value: string | number | null | undefined) {
|
|
return new Intl.NumberFormat('en-US', {
|
|
style: 'currency',
|
|
currency: 'USD',
|
|
notation: 'compact',
|
|
maximumFractionDigits: 2
|
|
}).format(asNumber(value));
|
|
}
|