123 lines
4.9 KiB
JavaScript
123 lines
4.9 KiB
JavaScript
import { emptyStringWhenNumberNan } from './emptyStringWhenNumberNan.js';
|
|
import { getDecimalSeparator } from './getDecimalSeparator.js';
|
|
import { getGroupSeparator } from './getGroupSeparator.js';
|
|
import { getLocale } from './getLocale.js';
|
|
import { normalizeIntl } from './normalizeIntl.js';
|
|
import { normalSpaces } from './normalSpaces.js';
|
|
import { roundNumber } from './roundNumber.js';
|
|
|
|
/**
|
|
* Splits a number up in parts for integer, fraction, group, literal, decimal and currency.
|
|
*
|
|
* @param {number} number Number to split up
|
|
* @param {Object} options Intl options are available extended by roundMode
|
|
* @returns {Array} Array with parts
|
|
*/
|
|
export function formatNumberToParts(number, options) {
|
|
let parsedNumber = typeof number === 'string' ? parseFloat(number) : number;
|
|
const computedLocale = getLocale(options && options.locale);
|
|
// when parsedNumber is not a number we should return an empty string or returnIfNaN
|
|
if (Number.isNaN(parsedNumber)) {
|
|
return emptyStringWhenNumberNan(options && options.returnIfNaN);
|
|
}
|
|
// If roundMode is given the number is rounded based upon the mode
|
|
if (options && options.roundMode) {
|
|
parsedNumber = roundNumber(number, options.roundMode);
|
|
}
|
|
let formattedParts = [];
|
|
const formattedNumber = Intl.NumberFormat(computedLocale, options).format(parsedNumber);
|
|
const regexSymbol = /[A-Z.,\s0-9]/;
|
|
const regexCode = /[A-Z]/;
|
|
const regexMinusSign = /[-]/;
|
|
const regexNum = /[0-9]/;
|
|
const regexSeparator = /[.,]/;
|
|
const regexSpace = /[\s]/;
|
|
let currencyCode = '';
|
|
let numberPart = '';
|
|
let fraction = false;
|
|
for (let i = 0; i < formattedNumber.length; i += 1) {
|
|
// detect minusSign
|
|
if (regexMinusSign.test(formattedNumber[i])) {
|
|
formattedParts.push({ type: 'minusSign', value: formattedNumber[i] });
|
|
}
|
|
// detect numbers
|
|
if (regexNum.test(formattedNumber[i])) {
|
|
numberPart += formattedNumber[i];
|
|
}
|
|
// detect currency symbol
|
|
if (!regexSymbol.test(formattedNumber[i]) && !regexMinusSign.test(formattedNumber[i])) {
|
|
// Write number grouping
|
|
if (numberPart && !fraction) {
|
|
formattedParts.push({ type: 'integer', value: numberPart });
|
|
numberPart = '';
|
|
} else if (numberPart) {
|
|
formattedParts.push({ type: 'fraction', value: numberPart });
|
|
numberPart = '';
|
|
}
|
|
formattedParts.push({ type: 'currency', value: formattedNumber[i] });
|
|
}
|
|
// detect currency code
|
|
if (regexCode.test(formattedNumber[i])) {
|
|
currencyCode += formattedNumber[i];
|
|
// Write number grouping
|
|
if (numberPart && !fraction) {
|
|
formattedParts.push({ type: 'integer', value: numberPart });
|
|
numberPart = '';
|
|
} else if (numberPart) {
|
|
formattedParts.push({ type: 'fraction', value: numberPart });
|
|
numberPart = '';
|
|
}
|
|
if (currencyCode.length === 3) {
|
|
formattedParts.push({ type: 'currency', value: currencyCode });
|
|
currencyCode = '';
|
|
}
|
|
}
|
|
// detect dot and comma separators
|
|
if (regexSeparator.test(formattedNumber[i])) {
|
|
// Write number grouping
|
|
if (numberPart) {
|
|
formattedParts.push({ type: 'integer', value: numberPart });
|
|
numberPart = '';
|
|
}
|
|
const decimal = getDecimalSeparator();
|
|
if (formattedNumber[i] === decimal) {
|
|
formattedParts.push({ type: 'decimal', value: formattedNumber[i] });
|
|
fraction = true;
|
|
} else {
|
|
formattedParts.push({ type: 'group', value: formattedNumber[i] });
|
|
}
|
|
}
|
|
// detect literals (empty spaces) or space group separator
|
|
if (regexSpace.test(formattedNumber[i])) {
|
|
const group = getGroupSeparator();
|
|
const hasNumberPart = !!numberPart;
|
|
// Write number grouping
|
|
if (numberPart && !fraction) {
|
|
formattedParts.push({ type: 'integer', value: numberPart });
|
|
numberPart = '';
|
|
} else if (numberPart) {
|
|
formattedParts.push({ type: 'fraction', value: numberPart });
|
|
numberPart = '';
|
|
}
|
|
// If space equals the group separator it gets type group
|
|
if (normalSpaces(formattedNumber[i]) === group && hasNumberPart && !fraction) {
|
|
formattedParts.push({ type: 'group', value: formattedNumber[i] });
|
|
} else {
|
|
formattedParts.push({ type: 'literal', value: formattedNumber[i] });
|
|
}
|
|
}
|
|
// Numbers after the decimal sign are fractions, write the last
|
|
// fractions at the end of the number
|
|
if (fraction === true && i === formattedNumber.length - 1) {
|
|
// write last number part
|
|
if (numberPart) {
|
|
formattedParts.push({ type: 'fraction', value: numberPart });
|
|
}
|
|
// If there are no fractions but we reached the end write the numberpart as integer
|
|
} else if (i === formattedNumber.length - 1 && numberPart) {
|
|
formattedParts.push({ type: 'integer', value: numberPart });
|
|
}
|
|
}
|
|
formattedParts = normalizeIntl(formattedParts, options, computedLocale);
|
|
return formattedParts;
|
|
}
|