preskok
uinumber-formatsource: ok · hit
Number Format
A number format component that allows you to format numbers in a variety of ways
npx shadcn add "https://ui-three-mu.vercel.app/r/number-format.json"free
No selection tags yet — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lightreact-number-format
Registry dependencies
https://ui-three-mu.vercel.app/r/create-ctx.jsonhttps://ui-three-mu.vercel.app/r/locale-context.json
Files4 · source cached 10/14/2026, 1:26:55 PM
registry/preskok/ui/preskok-ui/number-format/number-format.tsx
"use client"
import type { PropsWithChildren } from "react"
import React, { useRef } from "react"
import type { NumericFormatProps } from "react-number-format"
import { NumericFormat } from "react-number-format"
import { LocaleContext } from "@/components/ui/preskok-ui/locale-context/locale-context"
import type { Locale } from "@/components/ui/preskok-ui/locale-context/locale-helpers"
import { CurrencyCodeAndSymbolMap } from "@/components/ui/preskok-ui/locale-context/locale-helpers"
import {
NumberFormatContext,
useNumberFormatContext,
} from "@/components/ui/preskok-ui/number-format/number-format-context"
interface NumberFormatPropsUnified extends Omit<
NumericFormatProps,
"onChange" | "onPasteCapture" | "onValueChange"
> {
value: number | string | undefined | null
isCurrency?: boolean
onValue?: (value: number | null) => void
onCustomChangeHandler?: (value: number | null) => void
onCustomPasteCaptureHandler?: (value: number | null) => void
onCustomValueChangeHandler?: (values: {
floatValue?: number
value: string
}) => void
}
/**
* @description NumberFormat component is a unified wrapper around the NumericFormat component from react-number-format. It provides a consistent way to format numbers, handle changes, and paste events. It is advised to
* use the onValue callback to handle any other state change callbacks, because it abstracts the onChange, onPasteCapture and onValueChange handlers and also
* provide consistent behavior across different use cases.
*
* IMPORTANT: This component abstracts onChange, onPasteCapture and onValueChange handlers, allowing you to handle formatted values easily. Should you
* want to use custom handlers, you can pass them as props. If no handlers are provided, the component will log a warning in the console and will
* not process the value changes or pasted values.
*
* @param {number | string | undefined | null} value - The value to be formatted and displayed in the input field.
* @param {boolean} [isCurrency=false] - If true, the component will format the value as a currency. Defaults to false.
* @param {string} [suffix] - Optional suffix to be appended to the formatted value. If isCurrency is true, it defaults to the Euro currency symbol.
* @param {(value: number | null) => void} [onValue] - Callback function to handle the formatted value when it changes. IMPORTANT: This will abstract the onChange, onPasteCapture and onValueChange handlers!
* @param {(value: number | null) => void} [onCustomChangeHandler] - Custom change handler to process the value when it changes.
* @param {(values: { floatValue?: number; value: string }) => void} [onCustomValueChangeHandler] - Custom value change handler to process the values when they change.
* @param {(value: number | null) => void} [onCustomPasteCaptureHandler] - Custom paste handler to process the value when pasted.
*
* @returns {JSX.Element} - Returns a NumericFormat component with the provided props and handlers.
*/
export const NumberFormat: React.FC<NumberFormatPropsUnified> = ({
value,
isCurrency = false,
suffix,
onValue,
onCustomChangeHandler,
onCustomPasteCaptureHandler,
...rest
}) => {
// store the last valid value to revert to in case of out-of-range values
const lastValidValueRef = useRef<number | null>(null)
const {
withDefaultNumericFormatProps,
convertStringToNumber,
isNumberOutOfRange,
} = useNumberFormatContext()
const formatSuffix = isCurrency
? suffix || CurrencyCodeAndSymbolMap.EUR
: suffix
// Unified handler for value changes
const _handleValueChange = (values: {
floatValue?: number
value: string
}) => {
const { floatValue } = values
let finalFloatValue: number | null = floatValue ?? null
if (!isNumberOutOfRange(floatValue ?? null)) {
// no toast thrown, because it should be picked up by the onChange handler already
lastValidValueRef.current = floatValue ?? null
} else {
finalFloatValue = lastValidValueRef.current // Revert to last valid value
}
if (onValue) {
onValue(finalFloatValue)
} else if (onCustomChangeHandler) {
onCustomChangeHandler(finalFloatValue ?? null)
} else {
console.warn(
"No onValue or onCustomChangeHandler provided, value will not be processed."
)
}
}
// Unified handler for manual input changes (fallback)
const _handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const val = e.target.value
let convertToNumber = convertStringToNumber({
value: val,
})
if (!isNumberOutOfRange(convertToNumber)) {
lastValidValueRef.current = convertToNumber
} else {
// consider throwing a toast or letting the user know another way
convertToNumber = lastValidValueRef.current ?? null // Revert to last valid value
}
if (onValue) {
onValue(convertToNumber)
} else if (onCustomChangeHandler) {
onCustomChangeHandler(convertToNumber)
} else {
console.warn(
"No onValue or onCustomChangeHandler provided, value will not be processed."
)
}
}
// Unified handler for paste
const _handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
e.preventDefault()
const pastedValue = e.clipboardData.getData("text")
let tempPastedValue: string | null = null
if (pastedValue) {
tempPastedValue = pastedValue.replace(/[^0-9.,]/g, "")
const lastDotIndex = tempPastedValue.lastIndexOf(".")
const lastCommaIndex = tempPastedValue.lastIndexOf(",")
const lastSeparatorIndex = Math.max(lastDotIndex, lastCommaIndex)
if (lastSeparatorIndex !== -1) {
const integerPart = tempPastedValue
.substring(0, lastSeparatorIndex)
.replace(/[,.]/g, "")
const decimalPart = tempPastedValue
.substring(lastSeparatorIndex + 1)
.replace(/[,.]/g, "")
tempPastedValue = `${integerPart}.${decimalPart}`
} else {
tempPastedValue = tempPastedValue.replace(/[^0-9]/g, "")
}
}
let checkedPastedNumber =
tempPastedValue && !isNaN(parseFloat(tempPastedValue))
? parseFloat(tempPastedValue)
: null
if (!isNumberOutOfRange(checkedPastedNumber)) {
lastValidValueRef.current = checkedPastedNumber
} else {
// consider throwing a toast or letting the user know another way
checkedPastedNumber = lastValidValueRef.current // Revert to last valid value
}
if (onValue) {
onValue(checkedPastedNumber)
} else if (onCustomPasteCaptureHandler) {
onCustomPasteCaptureHandler(checkedPastedNumber)
} else {
console.warn(
"No onValue or onCustomPasteCaptureHandler provided, pasted value will not be processed."
)
}
}
return (
<NumericFormat
value={value}
suffix={formatSuffix}
onChange={_handleChange}
onValueChange={_handleValueChange}
onPasteCapture={_handlePaste}
{...withDefaultNumericFormatProps}
{...rest}
/>
)
}
export function NumberFormatProvider({ children }: PropsWithChildren) {
return (
<LocaleContext>
<NumberFormatContext>{children}</NumberFormatContext>
</LocaleContext>
)
}
interface NumberTextProps {
value: number
className?: string
intlOptions?: Intl.NumberFormatOptions
locale?: Locale
invalidNumberText?: string
}
export function NumberText({
value,
className,
intlOptions,
locale,
invalidNumberText,
}: NumberTextProps) {
const { formatNumberRaw } = useNumberFormatContext()
const res = formatNumberRaw(value, { locale, intlOptions, invalidNumberText })
return (
<span className={className}>
{res.value ?? res.invalidNumberText ?? ""}
</span>
)
}
export const useNumberFormat = useNumberFormatContext
registry/preskok/ui/preskok-ui/number-format/number-format-context.tsx
"use client"
import type { PropsWithChildren } from "react"
import React, { useCallback, useMemo } from "react"
import type { NumericFormatProps } from "react-number-format"
import { createCtx } from "@/lib/create-ctx"
import { useLocaleContext } from "@/components/ui/preskok-ui/locale-context/locale-context"
import { Locale } from "@/components/ui/preskok-ui/locale-context/locale-helpers"
import {
convertLocaleToNumericProps,
formatNumberToString,
} from "@/components/ui/preskok-ui/number-format/number-format-helpers"
const MAXIMUM_NUMBER_OF_DIGITS_ALLOWED = 12
const DEFAULT_NUMBER_OF_DECIMALS = 2
const DEFAULT_NUMERIC_FORMAT_OPTIONS: NumericFormatProps = {
displayType: "text",
thousandSeparator: ".",
decimalSeparator: ",",
allowNegative: true,
decimalScale: DEFAULT_NUMBER_OF_DECIMALS,
}
const FRENCH_NUMERIC_FORMAT_OPTIONS: NumericFormatProps = {
...DEFAULT_NUMERIC_FORMAT_OPTIONS,
thousandSeparator: " ",
decimalSeparator: ",",
}
const ENGLISH_NUMERIC_FORMAT_OPTIONS: NumericFormatProps = {
...DEFAULT_NUMERIC_FORMAT_OPTIONS,
thousandSeparator: ",",
decimalSeparator: ".",
}
const SLOVENE_NUMERIC_FORMAT_OPTIONS: NumericFormatProps = {
...DEFAULT_NUMERIC_FORMAT_OPTIONS,
thousandSeparator: ".",
decimalSeparator: ",",
}
const DEFAULT_LOCALE = Locale.SL
interface ConvertStringToNumberBasedOnLocaleProps {
value?: string | null
forcedLocale?: Locale
}
interface NumberFormatContextProviderProps {
formatNumberRaw: FormatNumberToStringFnSignature
withDefaultNumericFormatProps: NumericFormatProps
convertCustomLocaleToNumericProps: ConvertCustomLocaleToNumericPropsFnSignature
convertStringToNumber: ({
value,
forcedLocale,
}: ConvertStringToNumberBasedOnLocaleProps) => number | null
isNumberOutOfRange: (value: number | null) => boolean
}
export const [useNumberFormatContext, NumberFormatContextProvider] =
createCtx<NumberFormatContextProviderProps>()
export const NumberFormatContext: React.FC<PropsWithChildren> = ({
children,
}) => {
const { globalLocale } = useLocaleContext()
/**
* @description evaluateNumericFormatDefaults will return specific defaults for a given locale.
*/
const evaluateNumericFormatDefaults = () => {
switch (globalLocale) {
case "sl":
case "de":
case "it":
case "hr":
return {
...SLOVENE_NUMERIC_FORMAT_OPTIONS,
}
case "fr":
return {
...FRENCH_NUMERIC_FORMAT_OPTIONS,
}
case "en":
return {
...ENGLISH_NUMERIC_FORMAT_OPTIONS,
}
default:
return {
...SLOVENE_NUMERIC_FORMAT_OPTIONS,
}
}
}
/**
* @description formatNumberRaw will format a number to a string - this is just a wrapper around the helper function formatNumberToString. If the number is not valid, the function will return the default invalidNumberText string. Please
* note this function is meant to display numbers to the end user and will take locale and Intl.NumberFormatOptions into account (if any passed).
*
* Locale will default to 'sl' if not passed, otherwise the global locale will be taken into the account.
*
* In case the number is not valid (not finite) then a default invalidNumberText will be returned. Developers can further customize any of this via options object (not required).
*
* NOTE: This will achieve similar result to what withDefaultNumericFormatProps will forward to the react-number-format component, but this function will return a string instead of a component.
*
* @param value -> The number value to be formatted
* @param options -> The options object (FormatNumberToStringOptions)
* @returns {string}
*/
const formatNumberRaw: FormatNumberToStringFnSignature = useCallback(
(
value,
// oxlint-disable-next-line typescript/no-unnecessary-condition
options = { locale: globalLocale || DEFAULT_LOCALE }
): FormatNumberToStringReturnType => {
return formatNumberToString(value, options)
},
[globalLocale]
)
/**
* @description convertCustomLocaleToNumericProps is a memoized function that will convert a custom locale to a set of props for the react-number-format component. The goal
* of this function is to allow developers to get default props based on custom locale (otherwise the NumberFormat component will take global locale into account when using the withDefaultNumericFormatProps).
*
* @param {Locale} locale -> The locale to convert to props
* @returns {NumericFormatProps}
*/
const convertCustomLocaleToNumericProps = useCallback(
(locale: Locale): NumericFormatProps => {
return convertLocaleToNumericProps(locale)
},
[]
)
/**
* @description withDefaultNumericFormatProps is a memoized function that returns a default set of props for the react-number-format component. It will take
* current locale into account and return the appropriate props.
*
* EXAMPLE usage:
* <NumberFormat value={123445.44433} />
*
* The usage above will spread the default props into the component, but will also allow you to override them with your own props should you need to.
*
* VERY IMPORTANT: The 'withDefaultNumericFormatProps' will take global locale into account and cannot be overwritten. If you need to overwrite the locale you will need to provide a different
* set of props to the component! Please use the `convertCustomLocaleToNumericProps` function available on context.
*
* @returns default props for react-number-format component
*/
const withDefaultNumericFormatProps = evaluateNumericFormatDefaults()
/**
* @description This function will convert a string to a number based on the locale. It will replace the thousands separator with an empty string and the decimal separator with a dot. The
* locales are mapped against our internal settings for separators. This will work in conjunction with - but is not limited by - the NumberFormat component and will allow you to convert a string value to a number based on the locale.
*
* @param {string | null | undefined} value - The string value to be converted to a number based on the locale.
* @param {Locale} [forcedLocale] - The locale to use for conversion. If not provided, the global locale will be used.
* @returns {number | null} - Returns the parsed number or null if the input is invalid.
*/
const convertStringToNumberBasedOnLocale = useCallback(
({
value,
// oxlint-disable-next-line typescript/no-unnecessary-condition
forcedLocale = globalLocale || DEFAULT_LOCALE,
}: ConvertStringToNumberBasedOnLocaleProps): number | null => {
if (typeof value !== "string" || value.trim() === "") {
return null
}
const cleanedValue = value.replace(/[^0-9.,]/g, "")
let normalizedValue = cleanedValue
/**
* Convert the forcedLocale (or the globalLocale | DEFAULT_LOCALE) to numeric props to get the thousand and decimal separators as per our internal settings.
* This will allow us to normalize the value based on the locale.
*/
const { thousandSeparator, decimalSeparator } =
convertCustomLocaleToNumericProps(forcedLocale)
/**
* Replace the thousands separator with an empty string and the decimal separator with a dot
*/
normalizedValue =
cleanedValue
.replace(new RegExp(`\\${thousandSeparator}`, "g"), "")
.replace(new RegExp(`\\${decimalSeparator}`, "g"), ".") || ""
const parsedValue = parseFloat(normalizedValue)
if (isNaN(parsedValue) || !isFinite(parsedValue)) {
return null
}
return parsedValue
},
[convertCustomLocaleToNumericProps, globalLocale]
)
const isNumberOutOfRange = useCallback((value: number | null) => {
// We need to check if the value has no more than MAXIMUM_NUMBER_OF_DIGITS_ALLOWED digits. Please note that this limit only applies to the
// integer part of the number, not the decimal part.
// oxlint-disable-next-line typescript/no-unnecessary-condition
if (value === null || value === undefined) {
return false
}
const valueString = value.toString()
const integerPart = valueString.split(".")[0] // Get the integer part of the number
return integerPart.length > MAXIMUM_NUMBER_OF_DIGITS_ALLOWED
}, [])
const memoizedContextProps = useMemo(
() => ({
formatNumberRaw,
withDefaultNumericFormatProps,
convertCustomLocaleToNumericProps,
convertStringToNumber: convertStringToNumberBasedOnLocale,
isNumberOutOfRange,
}),
[
formatNumberRaw,
withDefaultNumericFormatProps,
convertCustomLocaleToNumericProps,
convertStringToNumberBasedOnLocale,
isNumberOutOfRange,
]
)
return (
<NumberFormatContextProvider value={memoizedContextProps}>
{children}
</NumberFormatContextProvider>
)
}
export type IsNumberValidAllowedTypes = string | number | null | undefined
export interface ProcessNumberReturnObj {
isNumberValid: boolean
value: number | null
}
export interface FormatNumberToStringOptions {
locale?: Locale
intlOptions?: Intl.NumberFormatOptions
invalidNumberText?: string
}
export type FormatNumberToStringFnSignature = (
value: number,
options?: FormatNumberToStringOptions
) => FormatNumberToStringReturnType
export type ConvertCustomLocaleToNumericPropsFnSignature = (
locale: Locale
) => NumericFormatProps
export interface FormatNumberToStringReturnType extends Pick<
FormatNumberToStringOptions,
"invalidNumberText"
> {
isValid: boolean
value: string | null
}
registry/preskok/ui/preskok-ui/number-format/number-format-helpers.ts
import type { NumericFormatProps } from "react-number-format"
import type {
Locale,
LocaleToBcp47Map,
} from "@/components/ui/preskok-ui/locale-context/locale-helpers"
import {
DEFAULT_INVALID_NUMBER_TEXT,
DEFAULT_LOCALE,
DEFAULT_NUMBER_OF_DECIMALS,
DEFAULT_NUMERIC_FORMAT_OPTIONS,
LOCALE_TO_BCP_47_MAP,
LOCALE_TO_NUMERIC_FORMAT_OPTIONS_MAP,
} from "@/components/ui/preskok-ui/locale-context/locale-helpers"
/**
* isNumberValid will check whether the passed value is a number. It leverages the Number.isFinite method which is the
* most consistent way to check whether a value is a number. Edge cases are handled correctly, thus NaN, undefined, null
* and Infinity are not considered numbers.
*
* @param {IsNumberValidAllowedTypes} value -> The value to be checked
* @returns {boolean}
*/
export const isNumberValid = (value: IsNumberValidAllowedTypes): boolean => {
return Number.isFinite(value)
}
/**
* convertStringToNumber will attempt to convert a string to a number. If the conversion is successful, the function will return the converted number. If the conversion is not
* successful, the function will return null.
*
* @param {string} value -> string value to be converted to a number
* @returns {number | null}
*/
export const convertStringToNumber = (value: string): number | null => {
const convertedStringValue = Number(value)
return isNumberValid(convertedStringValue) ? convertedStringValue : null
}
/**
* numberIsInteger will check whether the passed number is an integer and not a float.
*
* @param {number} value -> The value to be checked
* @returns {boolean}
*/
export const numberIsInteger = (value: number): boolean => {
return Number.isInteger(value)
}
/**
* numberIsPositive will check whether the passed number is positive.
*
* @param {number} value -> The value to be checked
* @returns {boolean}
*/
export const numberIsPositive = (value: number): boolean => {
return value >= 0
}
/**
* @description convertLocaleToBcp47 will convert a locale to BCP 47 format. If the locale is not found in the LOCALE_TO_BCP_47_MAP, the function will return an empty string.
*
* @param {Locale} locale -> The locale to be converted to BCP 47 format
* @returns {string} - either bcp 47 locale or empty string
*/
export const convertLocaleToBcp47 = (locale: Locale): LocaleToBcp47Map => {
const bcp47Map = LOCALE_TO_BCP_47_MAP
return bcp47Map[locale]
}
/**
* @description convertLocaleToNumericProps will convert a locale to numeric format options. If the locale is not found in the LOCALE_TO_NUMERIC_FORMAT_OPTIONS_MAP, the function will return
* DEFAULT_NUMERIC_FORMAT_OPTIONS.
*
* @param {`${Locale}`} locale -> The locale to be converted to numeric format options
* @returns {NumericFormatProps}
*/
export const convertLocaleToNumericProps = (
locale: Locale
): NumericFormatProps => {
return (
// oxlint-disable-next-line typescript/no-unnecessary-condition
LOCALE_TO_NUMERIC_FORMAT_OPTIONS_MAP[locale] ??
DEFAULT_NUMERIC_FORMAT_OPTIONS
)
}
/**
* processId will attempt to convert a string to a number and then check whether the number is valid and positive. This function is used to process the id parameter anywhere
* in the code. Explanation: for historic reasons we are oftentimes receiving a string as an id parameter. This function will attempt to convert the string to a number and then
* evaluate whether the number is valid, positive and is not a float, thus reflecting the fact that we are dealing with an id. If the id is valid, the function will return an
* object containing the value of the id and a boolean indicating that the id is valid.
*
* @param {IsNumberValidAllowedTypes} value -> The value to be processed
* @returns {ProcessNumberReturnObj}
*/
export const processId = (
value: IsNumberValidAllowedTypes
): ProcessNumberReturnObj => {
const processNumberResult: ProcessNumberReturnObj = {
isNumberValid: false,
value: null,
}
/**
* First we attempt to convert string to a number. If the value is not a string, we will simply use the value as is.
*/
const attemptConversion =
typeof value === "string" ? convertStringToNumber(value) : value
/**
* We check whether the value is a number and whether it is positive and an integer. If all of these conditions are met, we will return the value as a number.
*
* IMPORTANT: we are casting the passed value as number, because in theory the 'isNumberValid' will only be truthy if the value is an actual number. However, TypeScript
* is not able to infer that the value is a number, thus we need to cast it as number in the numberIsInteger and numberIsPositive functions. If another check would be added then
* extra caution needs to be exercised as it might break the logic!
*/
const isIdValid =
isNumberValid(attemptConversion) &&
numberIsInteger(attemptConversion!) &&
numberIsPositive(attemptConversion!)
if (isIdValid) {
processNumberResult.isNumberValid = true
processNumberResult.value = attemptConversion!
}
return processNumberResult
}
/**
* @description formatNumberToString will format a number to a string. If the number is not valid, the function will return the default invalidNumberText string. Please
* note this function is meant to display numbers to the end user and will take locale and Intl.NumberFormatOptions into account (if any passed).
*
* Locale will default to 'sl' if not passed.
*
* In case the number is not valid (not finite) then a default invalidNumberText will be returned. Developers can further customize any of this via options object (not required).
*
* @param value -> The number value to be formatted
* @param options -> The options object (FormatNumberToStringOptions)
* @returns {FormatNumberToStringReturnType} -> an object containing data regarding validity of number and the formatted number as string. There is also an option to retrieve a fallback text / value.
*/
export const formatNumberToString: FormatNumberToStringFnSignature = (
value: number,
options?: FormatNumberToStringOptions
): FormatNumberToStringReturnType => {
const isValid = isNumberValid(value)
/**
* The end result of the function. We will return this object.
*/
const formattedNumberResult: FormatNumberToStringReturnType = {
value: null,
isValid,
}
/**
* We will merge the passed options with the default options. This will ensure that the developer can override any of the default options.
*/
const defaultOptionsMerge: FormatNumberToStringOptions = {
locale: DEFAULT_LOCALE,
invalidNumberText: DEFAULT_INVALID_NUMBER_TEXT,
intlOptions: {
minimumFractionDigits: DEFAULT_NUMBER_OF_DECIMALS,
maximumFractionDigits: DEFAULT_NUMBER_OF_DECIMALS,
},
...options,
}
const { locale, intlOptions, invalidNumberText } = defaultOptionsMerge
/**
* Copy the invalid number text to the output object (in case developer provided custom version)
*/
if (invalidNumberText) {
formattedNumberResult.invalidNumberText = invalidNumberText
}
/**
* Perform formatting only if the number is valid and locale is provided.
*/
if (isValid && locale) {
const mappedLocale = convertLocaleToBcp47(locale)
if (mappedLocale) {
formattedNumberResult.value = value.toLocaleString(
mappedLocale,
intlOptions
)
}
}
return formattedNumberResult
}
export type IsNumberValidAllowedTypes = string | number | null | undefined
export interface ProcessNumberReturnObj {
isNumberValid: boolean
value: number | null
}
export interface FormatNumberToStringOptions {
locale?: Locale
intlOptions?: Intl.NumberFormatOptions
invalidNumberText?: string
}
export type FormatNumberToStringFnSignature = (
value: number,
options?: FormatNumberToStringOptions
) => FormatNumberToStringReturnType
export interface FormatNumberToStringReturnType extends Pick<
FormatNumberToStringOptions,
"invalidNumberText"
> {
isValid: boolean
value: string | null
}
registry/preskok/ui/preskok-ui/number-format/currency-helpers.ts
import { CurrencyCodeAndSymbolMap } from "../locale-context/locale-helpers"
type CurrencyCodeOrSymbolExpectedOutputOptions =
| "currency_code"
| "currency_symbol"
interface GetCurrencyCodeOrSymbolParams {
inputValue: string
expectedOutput?: CurrencyCodeOrSymbolExpectedOutputOptions
}
/**
* getCurrencyCodeOrSymbol is a helper function to get currency code or symbol from a string value. If no match is found
* we return an empty string.
*
* @param {string} inputValue -> string value that might be a currency code or symbol.
* @param {'currency_code' | 'currency_symbol'} expectedOutput -> optional expected output type (default: 'currency_code').
* @returns {string} - currency code or symbol or an empty string if no match is found
*/
export const getCurrencyCodeOrSymbol = ({
inputValue,
expectedOutput = "currency_code",
}: GetCurrencyCodeOrSymbolParams): string => {
let currencyCodeOrSymbolStringValue = ""
const trimmedAndLowerCaseInput = inputValue.trim().toLowerCase()
const currencyCodeOrSymbolLookup = Object.keys(CurrencyCodeAndSymbolMap).find(
(key) =>
key.toLowerCase() === trimmedAndLowerCaseInput ||
CurrencyCodeAndSymbolMap[
key as keyof typeof CurrencyCodeAndSymbolMap
].toLowerCase() === trimmedAndLowerCaseInput
)
if (currencyCodeOrSymbolLookup) {
if (expectedOutput === "currency_code") {
currencyCodeOrSymbolStringValue = currencyCodeOrSymbolLookup
} else {
currencyCodeOrSymbolStringValue =
CurrencyCodeAndSymbolMap[
currencyCodeOrSymbolLookup as keyof typeof CurrencyCodeAndSymbolMap
]
}
}
return currencyCodeOrSymbolStringValue
}