UNPKG

@canutin/svelte-currency-input

Version:

A fully-featured currency input component for Svelte 5 that handles formatting, localization, and validation

105 lines (104 loc) 4.35 kB
import { escapeRegExp } from './escapeRegExp'; import { getSuffix } from './getSuffix'; export const formatValue = (options) => { const { value: _value, decimalSeparator, intlConfig, decimalScale, prefix = '', suffix = '', roundValue = false } = options; if (_value === '' || _value === undefined) { return ''; } if (_value === '-') { return '-'; } const isNegative = new RegExp(`^\\d?-${prefix ? `${escapeRegExp(prefix)}?` : ''}\\d`).test(_value); let value = decimalSeparator !== '.' ? replaceDecimalSeparator(_value, decimalSeparator, isNegative) : _value; if (decimalSeparator && decimalSeparator !== '-' && value.startsWith(decimalSeparator)) { value = '0' + value; } const { locale, currency, ...formatOptions } = intlConfig || {}; const defaultNumberFormatOptions = { ...formatOptions, minimumFractionDigits: decimalScale || 0, maximumFractionDigits: roundValue && decimalScale !== undefined ? decimalScale : 20 }; const numberFormatter = intlConfig ? new Intl.NumberFormat(locale, { ...defaultNumberFormatOptions, ...(currency && { style: 'currency', currency }) }) : new Intl.NumberFormat(undefined, defaultNumberFormatOptions); const parts = numberFormatter.formatToParts(Number(value)); let formatted = replaceParts(parts, options); const intlSuffix = getSuffix(formatted, { ...options }); const includeDecimalSeparator = _value.slice(-1) === decimalSeparator ? decimalSeparator : ''; const [, decimals] = value.match(RegExp('\\d+\\.(\\d+)')) || []; if (decimalScale === undefined && decimals && decimalSeparator) { if (formatted.includes(decimalSeparator)) { formatted = formatted.replace(RegExp(`(\\d+)(${escapeRegExp(decimalSeparator)})(\\d+)`, 'g'), `$1$2${decimals}`); } else { if (intlSuffix && !suffix) { formatted = formatted.replace(intlSuffix, `${decimalSeparator}${decimals}${intlSuffix}`); } else { formatted = `${formatted}${decimalSeparator}${decimals}`; } } } if (suffix && includeDecimalSeparator) { return `${formatted}${includeDecimalSeparator}${suffix}`; } if (intlSuffix && includeDecimalSeparator) { return formatted.replace(intlSuffix, `${includeDecimalSeparator}${intlSuffix}`); } if (intlSuffix && suffix) { return formatted.replace(intlSuffix, `${includeDecimalSeparator}${suffix}`); } return [formatted, includeDecimalSeparator, suffix].join(''); }; const replaceDecimalSeparator = (value, decimalSeparator, isNegative) => { let newValue = value; if (decimalSeparator && decimalSeparator !== '.') { newValue = newValue.replace(RegExp(escapeRegExp(decimalSeparator), 'g'), '.'); if (isNegative && decimalSeparator === '-') { newValue = `-${newValue.slice(1)}`; } } return newValue; }; const replaceParts = (parts, { prefix, groupSeparator, decimalSeparator, decimalScale, disableGroupSeparators = false, roundValue = false }) => { return parts .reduce((prev, { type, value }, i) => { if (i === 0 && prefix) { if (type === 'minusSign') { return [value, prefix]; } if (type === 'currency') { return [...prev, prefix]; } return [prefix, value]; } if (type === 'currency') { return prefix ? prev : [...prev, value]; } if (type === 'group') { return !disableGroupSeparators ? [...prev, groupSeparator !== undefined ? groupSeparator : value] : prev; } if (type === 'decimal') { if (decimalScale !== undefined && decimalScale === 0) { return prev; } return [...prev, decimalSeparator !== undefined ? decimalSeparator : value]; } if (type === 'fraction') { if (roundValue) { return [...prev, value]; } return [...prev, decimalScale !== undefined ? value.slice(0, decimalScale) : value]; } return [...prev, value]; }, ['']) .join(''); };