diff --git a/src/components/Output.tsx b/src/components/Output.tsx
index d1fd52c57..eaec706cb 100644
--- a/src/components/Output.tsx
+++ b/src/components/Output.tsx
@@ -84,6 +84,124 @@ type StyleProps = {
withBaseFont?: boolean;
};
+export const formatNumber = (params: ElementProps) => {
+ const {
+ value,
+ showSign = ShowSign.Negative,
+ useGrouping = true,
+ type,
+ locale = navigator.language || 'en-US',
+ fractionDigits,
+ roundingMode = BigNumber.ROUND_HALF_UP,
+ } = params;
+ const { decimal: LOCALE_DECIMAL_SEPARATOR, group: LOCALE_GROUP_SEPARATOR } =
+ useLocaleSeparators();
+
+ const valueBN = MustBigNumber(value).abs();
+ const isNegative = MustBigNumber(value).isNegative();
+ const isPositive = MustBigNumber(value).isPositive() && !MustBigNumber(value).isZero();
+
+ const sign: string | undefined = {
+ [ShowSign.Both]: isNegative ? UNICODE.MINUS : isPositive ? UNICODE.PLUS : undefined,
+ [ShowSign.Negative]: isNegative ? UNICODE.MINUS : undefined,
+ [ShowSign.None]: undefined,
+ }[showSign];
+
+ const format = {
+ decimalSeparator: LOCALE_DECIMAL_SEPARATOR,
+ ...(useGrouping
+ ? {
+ groupSeparator: LOCALE_GROUP_SEPARATOR,
+ groupSize: 3,
+ secondaryGroupSize: 0,
+ fractionGroupSeparator: ' ',
+ fractionGroupSize: 0,
+ }
+ : {}),
+ };
+
+ let formattedString: string = 'NaN';
+
+ switch (type) {
+ case OutputType.CompactNumber:
+ if (!isNumber(value)) {
+ throw new Error('value must be a number for compact number output');
+ }
+
+ formattedString = Intl.NumberFormat(locale, {
+ style: 'decimal',
+ notation: 'compact',
+ maximumSignificantDigits: 3,
+ })
+ .format(Math.abs(value))
+ .toLowerCase();
+ break;
+ case OutputType.Number:
+ formattedString = valueBN.toFormat(fractionDigits ?? 0, roundingMode, {
+ ...format,
+ });
+ break;
+ case OutputType.Fiat:
+ formattedString = valueBN.toFormat(fractionDigits ?? USD_DECIMALS, roundingMode, {
+ ...format,
+ prefix: '$',
+ });
+ break;
+ case OutputType.SmallFiat:
+ formattedString = valueBN.toFormat(fractionDigits ?? SMALL_USD_DECIMALS, roundingMode, {
+ ...format,
+ prefix: '$',
+ });
+ break;
+ case OutputType.CompactFiat:
+ if (!isNumber(value)) {
+ throw new Error('value must be a number for compact fiat output');
+ }
+ formattedString = Intl.NumberFormat(locale, {
+ style: 'currency',
+ currency: 'USD',
+ notation: 'compact',
+ maximumSignificantDigits: 3,
+ })
+ .format(Math.abs(value))
+ .toLowerCase();
+ break;
+ case OutputType.Asset:
+ formattedString = valueBN.toFormat(fractionDigits ?? TOKEN_DECIMALS, roundingMode, {
+ ...format,
+ });
+ break;
+ case OutputType.Percent:
+ formattedString = valueBN
+ .times(100)
+ .toFormat(fractionDigits ?? PERCENT_DECIMALS, roundingMode, {
+ ...format,
+ suffix: '%',
+ });
+ break;
+ case OutputType.SmallPercent:
+ formattedString = valueBN
+ .times(100)
+ .toFormat(fractionDigits ?? SMALL_PERCENT_DECIMALS, roundingMode, {
+ ...format,
+ suffix: '%',
+ });
+ break;
+ case OutputType.Multiple:
+ formattedString = valueBN.toFormat(fractionDigits ?? LEVERAGE_DECIMALS, roundingMode, {
+ ...format,
+ suffix: '×',
+ });
+ break;
+ }
+
+ return {
+ sign,
+ format,
+ formattedString,
+ };
+};
+
export type OutputProps = ElementProps & StyleProps;
export const Output = ({
@@ -110,8 +228,6 @@ export const Output = ({
const selectedLocale = useSelector(getSelectedLocale);
const stringGetter = useStringGetter();
const isDetailsLoading = useContext(LoadingContext);
- const { decimal: LOCALE_DECIMAL_SEPARATOR, group: LOCALE_GROUP_SEPARATOR } =
- useLocaleSeparators();
if (isLoading || isDetailsLoading) {
return ;
@@ -213,28 +329,20 @@ export const Output = ({
case OutputType.SmallPercent:
case OutputType.Multiple: {
const hasValue = value !== null && value !== undefined;
- const valueBN = MustBigNumber(value).abs();
- const isNegative = MustBigNumber(value).isNegative();
- const isPositive = MustBigNumber(value).isPositive() && !MustBigNumber(value).isZero();
-
- const sign: string | undefined = {
- [ShowSign.Both]: isNegative ? UNICODE.MINUS : isPositive ? UNICODE.PLUS : undefined,
- [ShowSign.Negative]: isNegative ? UNICODE.MINUS : undefined,
- [ShowSign.None]: undefined,
- }[showSign];
-
- const format = {
- decimalSeparator: LOCALE_DECIMAL_SEPARATOR,
- ...(useGrouping
- ? {
- groupSeparator: LOCALE_GROUP_SEPARATOR,
- groupSize: 3,
- secondaryGroupSize: 0,
- fractionGroupSeparator: ' ',
- fractionGroupSize: 0,
- }
- : {}),
- };
+
+ const { sign, formattedString } = formatNumber({
+ type,
+ value,
+ isLoading,
+ fractionDigits,
+ showSign,
+ useGrouping,
+ roundingMode,
+ relativeTimeFormatOptions,
+ timeOptions,
+ withParentheses,
+ locale,
+ });
return (
<$Number
@@ -260,102 +368,35 @@ export const Output = ({
throw new Error('value must be a number for compact number output');
}
- return (
-
- );
+ return ;
},
[OutputType.Number]: () => (
-
+
),
[OutputType.Fiat]: () => (
-
+
),
[OutputType.SmallFiat]: () => (
-
+
),
[OutputType.CompactFiat]: () => {
if (!isNumber(value)) {
throw new Error('value must be a number for compact fiat output');
}
- return (
-
- );
+ return ;
},
[OutputType.Asset]: () => (
-
+
),
[OutputType.Percent]: () => (
-
+
),
[OutputType.SmallPercent]: () => (
-
+
),
[OutputType.Multiple]: () => (
-
+
),
}[type]()}
{slotRight}
diff --git a/src/views/ExportHistoryDropdown.tsx b/src/views/ExportHistoryDropdown.tsx
index 22c9f3ff0..bc104af89 100644
--- a/src/views/ExportHistoryDropdown.tsx
+++ b/src/views/ExportHistoryDropdown.tsx
@@ -8,13 +8,13 @@ import { AnalyticsEvent } from '@/constants/analytics';
import { ButtonAction, ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
-import { useLocaleSeparators, useStringGetter } from '@/hooks';
+import { useStringGetter } from '@/hooks/useStringGetter';
import { Button } from '@/components/Button';
import { Checkbox } from '@/components/Checkbox';
import { DropdownMenu } from '@/components/DropdownMenu';
import { Icon, IconName } from '@/components/Icon';
-import { OutputType, formatNumber, formatTimestamp } from '@/components/Output';
+import { OutputType, formatNumber } from '@/components/Output';
import { getSubaccountFills, getSubaccountTransfers } from '@/state/accountSelectors';
import { getSelectedLocale } from '@/state/localizationSelectors';
@@ -23,12 +23,15 @@ import { track } from '@/lib/analytics';
import { exportCSV } from '@/lib/csv';
import { MustBigNumber } from '@/lib/numbers';
-export const ExportHistoryDropdown = () => {
+interface ExportHistoryDropdownProps {
+ className?: string;
+}
+
+export const ExportHistoryDropdown = (props: ExportHistoryDropdownProps) => {
const selectedLocale = useSelector(getSelectedLocale);
const stringGetter = useStringGetter();
const allTransfers = useSelector(getSubaccountTransfers, shallowEqual) ?? [];
const allFills = useSelector(getSubaccountFills, shallowEqual) ?? [];
- const { decimal: localeDecimalSeparator, group: localeGroupSeparator } = useLocaleSeparators();
const [checkedTrades, setCheckedTrades] = useState(true);
const [checkedTransfers, setCheckedTransfers] = useState(true);
@@ -38,21 +41,11 @@ export const ExportHistoryDropdown = () => {
const { sign: feeSign, formattedString: feeString } = formatNumber({
type: OutputType.Fiat,
value: fill.fee,
- localeDecimalSeparator,
- localeGroupSeparator,
});
const { sign: totalSign, formattedString: totalString } = formatNumber({
type: OutputType.Fiat,
value: MustBigNumber(fill.price).times(fill.size),
- localeDecimalSeparator,
- localeGroupSeparator,
- });
-
- const { displayString } = formatTimestamp({
- type: OutputType.DateTime,
- value: fill.createdAtMilliseconds,
- locale: selectedLocale,
});
const sideKey = {
@@ -65,7 +58,10 @@ export const ExportHistoryDropdown = () => {
liquidity:
fill.resources.liquidityStringKey &&
stringGetter({ key: fill.resources.liquidityStringKey }),
- time: displayString,
+ time: new Date(fill.createdAtMilliseconds).toLocaleString(selectedLocale, {
+ dateStyle: 'short',
+ timeStyle: 'short',
+ }),
amount: fill.size,
fee: feeSign ? `${feeSign}${feeString}` : feeString,
total: totalSign ? `${totalSign}${totalString}` : totalString,
@@ -77,7 +73,7 @@ export const ExportHistoryDropdown = () => {
: '',
};
}),
- [allFills, stringGetter, localeDecimalSeparator, localeGroupSeparator]
+ [allFills, selectedLocale, stringGetter]
);
const transfers = useMemo(
@@ -86,18 +82,13 @@ export const ExportHistoryDropdown = () => {
const { sign, formattedString } = formatNumber({
type: OutputType.Fiat,
value: transfer.amount,
- localeDecimalSeparator,
- localeGroupSeparator,
- });
-
- const { displayString } = formatTimestamp({
- type: OutputType.DateTime,
- value: transfer.updatedAtMilliseconds,
- locale: selectedLocale,
});
return {
- time: displayString,
+ time: new Date(transfer.updatedAtMilliseconds).toLocaleString(selectedLocale, {
+ dateStyle: 'short',
+ timeStyle: 'short',
+ }),
action:
transfer.resources.typeStringKey &&
stringGetter({ key: transfer.resources.typeStringKey }),
@@ -107,7 +98,7 @@ export const ExportHistoryDropdown = () => {
transaction: transfer.transactionHash,
};
}),
- [allTransfers, stringGetter, localeDecimalSeparator, localeGroupSeparator]
+ [allTransfers, selectedLocale, stringGetter]
);
const exportTrades = useCallback(() => {
@@ -255,6 +246,7 @@ export const ExportHistoryDropdown = () => {
onSelect: exportData,
},
]}
+ {...props}
>
{stringGetter({ key: STRING_KEYS.EXPORT })}