lion/packages/ui/components/input-amount/src/parsers.js
gerjanvangeest e8e9c07ec5
fix(input-amount): returns Unparseable as a modelValue if a wrong value has been entered (#2242)
* fix(input-amount): returns Unparseable as a modelValue if a wrong value has been entered

* fix(input-amount): do not break when a large amount has been entered

* Update docs/components/input-amount/overview.md

Co-authored-by: Thijs Louisse <thijs.louisse@ing.com>

* Update packages/ui/components/input-amount/test/parsers.test.js

Co-authored-by: Thijs Louisse <thijs.louisse@ing.com>

---------

Co-authored-by: Thijs Louisse <thijs.louisse@ing.com>
2024-04-10 11:20:48 +02:00

56 lines
1.9 KiB
JavaScript

import { parseNumber, getFractionDigits } from '@lion/ui/localize-no-side-effects.js';
/**
* @typedef {import('../../localize/types/LocalizeMixinTypes.js').FormatNumberOptions} FormatOptions
*/
/**
* Rounding problem can be avoided by using numbers represented in exponential notation
* @param {number} value to be rounded up
* @param {number | undefined} decimals amount of decimals to keep
* @return {number} new value with rounded up decimals
*/
function round(value, decimals) {
const numberContainsExponent = value?.toString().includes('e');
if (typeof decimals === 'undefined' || numberContainsExponent) {
return Number(value);
}
return Number(`${Math.round(Number(`${value}e${decimals}`))}e-${decimals}`);
}
/**
* Uses `parseNumber()` to parses a number string and returns the best possible javascript number.
* Rounds up the number with the correct amount of decimals according to the currency.
*
* @example
* parseAmount('1,234.56', {currency: 'EUR'}); => 1234.56
* parseAmount('1,234.56', {currency: 'JPY'}); => 1235
* parseAmount('1,234.56', {currency: 'JOD'}); => 1234.560
*
* @param {string} value Number to be parsed
* @param {FormatOptions} [givenOptions] Locale Options
*/
export function parseAmount(value, givenOptions) {
const unmatchedInput = value.match(/[^0-9,.\- ]/g);
// for the full paste behavior documentation:
// ./docs/components/input-amount/use-cases.md#paste-behavior
if (unmatchedInput && givenOptions?.mode !== 'pasted') {
return undefined;
}
const number = parseNumber(value, givenOptions);
if (typeof number !== 'number' || Number.isNaN(number)) {
return undefined;
}
/** @type {FormatOptions} */
const options = {
...givenOptions,
};
if (options.currency && typeof options.maximumFractionDigits === 'undefined') {
options.maximumFractionDigits = getFractionDigits(options.currency);
}
return round(number, options.maximumFractionDigits);
}