Socket
Socket
Sign inDemoInstall

@internationalized/number

Package Overview
Dependencies
Maintainers
2
Versions
670
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@internationalized/number - npm Package Compare versions

Comparing version 3.0.0-nightly.2703 to 3.0.0-nightly-086ad3115-240911

dist/import.mjs

439

dist/main.js

@@ -1,7 +0,11 @@

var _babelRuntimeHelpersExtends = $parcel$interopDefault(require("@babel/runtime/helpers/extends"));
var $0c1d5654b62fc485$exports = require("./NumberFormatter.main.js");
var $d68f3f4c684426c6$exports = require("./NumberParser.main.js");
function $parcel$interopDefault(a) {
return a && a.__esModule ? a.default : a;
function $parcel$export(e, n, v, s) {
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
}
$parcel$export(module.exports, "NumberFormatter", () => $0c1d5654b62fc485$exports.NumberFormatter);
$parcel$export(module.exports, "NumberParser", () => $d68f3f4c684426c6$exports.NumberParser);
/*

@@ -17,433 +21,6 @@ * Copyright 2020 Adobe. All rights reserved.

* governing permissions and limitations under the License.
*/
let $b6bf6c42eb93d35942f2d0c9c4a9aa06$var$formatterCache = new Map();
let $b6bf6c42eb93d35942f2d0c9c4a9aa06$var$supportsSignDisplay = false;
*/
try {
// @ts-ignore
$b6bf6c42eb93d35942f2d0c9c4a9aa06$var$supportsSignDisplay = new Intl.NumberFormat('de-DE', {
signDisplay: 'exceptZero'
}).resolvedOptions().signDisplay === 'exceptZero'; // eslint-disable-next-line no-empty
} catch (e) {}
let $b6bf6c42eb93d35942f2d0c9c4a9aa06$var$supportsUnit = false;
try {
// @ts-ignore
$b6bf6c42eb93d35942f2d0c9c4a9aa06$var$supportsUnit = new Intl.NumberFormat('de-DE', {
style: 'unit',
unit: 'degree'
}).resolvedOptions().style === 'unit'; // eslint-disable-next-line no-empty
} catch (e) {} // Polyfill for units since Safari doesn't support them yet. See https://bugs.webkit.org/show_bug.cgi?id=215438.
// Currently only polyfilling the unit degree in narrow format for ColorSlider in our supported locales.
// Values were determined by switching to each locale manually in Chrome.
const $b6bf6c42eb93d35942f2d0c9c4a9aa06$var$UNITS = {
degree: {
narrow: {
default: '°',
'ja-JP': ' 度',
'zh-TW': '度',
'sl-SI': ' °' // Arabic?? But Safari already doesn't use Arabic digits so might be ok...
// https://bugs.webkit.org/show_bug.cgi?id=218139
}
}
};
/**
* A wrapper around Intl.NumberFormat providing additional options, polyfills, and caching for performance.
*/
class NumberFormatter {
constructor(locale, options) {
if (options === void 0) {
options = {};
}
this.numberFormatter = void 0;
this.options = void 0;
this.numberFormatter = $b6bf6c42eb93d35942f2d0c9c4a9aa06$var$getCachedNumberFormatter(locale, options);
this.options = options;
}
format(value) {
let res = '';
if (!$b6bf6c42eb93d35942f2d0c9c4a9aa06$var$supportsSignDisplay && this.options.signDisplay != null) {
res = $b6bf6c42eb93d35942f2d0c9c4a9aa06$export$numberFormatSignDisplayPolyfill(this.numberFormatter, this.options.signDisplay, value);
} else {
res = this.numberFormatter.format(value);
}
if (this.options.style === 'unit' && !$b6bf6c42eb93d35942f2d0c9c4a9aa06$var$supportsUnit) {
var _UNITS$unit;
let {
unit,
unitDisplay = 'short',
locale
} = this.resolvedOptions();
let values = (_UNITS$unit = $b6bf6c42eb93d35942f2d0c9c4a9aa06$var$UNITS[unit]) == null ? void 0 : _UNITS$unit[unitDisplay];
res += values[locale] || values.default;
}
return res;
}
formatToParts(value) {
// TODO: implement signDisplay for formatToParts
// @ts-ignore
return this.numberFormatter.formatToParts(value);
}
resolvedOptions() {
let options = this.numberFormatter.resolvedOptions();
if (!$b6bf6c42eb93d35942f2d0c9c4a9aa06$var$supportsSignDisplay && this.options.signDisplay != null) {
options = _babelRuntimeHelpersExtends({}, options, {
signDisplay: this.options.signDisplay
});
}
if (!$b6bf6c42eb93d35942f2d0c9c4a9aa06$var$supportsUnit && this.options.style === 'unit') {
options = _babelRuntimeHelpersExtends({}, options, {
style: 'unit',
unit: this.options.unit,
unitDisplay: this.options.unitDisplay
});
}
return options;
}
}
exports.NumberFormatter = NumberFormatter;
function $b6bf6c42eb93d35942f2d0c9c4a9aa06$var$getCachedNumberFormatter(locale, options) {
if (options === void 0) {
options = {};
}
let {
numberingSystem
} = options;
if (numberingSystem && locale.indexOf('-u-nu-') === -1) {
locale = locale + "-u-nu-" + numberingSystem;
}
if (options.style === 'unit' && !$b6bf6c42eb93d35942f2d0c9c4a9aa06$var$supportsUnit) {
var _UNITS$unit2;
let {
unit,
unitDisplay = 'short'
} = options;
if (!unit) {
throw new Error('unit option must be provided with style: "unit"');
}
if (!((_UNITS$unit2 = $b6bf6c42eb93d35942f2d0c9c4a9aa06$var$UNITS[unit]) != null && _UNITS$unit2[unitDisplay])) {
throw new Error("Unsupported unit " + unit + " with unitDisplay = " + unitDisplay);
}
options = _babelRuntimeHelpersExtends({}, options, {
style: 'decimal'
});
}
let cacheKey = locale + (options ? Object.entries(options).sort((a, b) => a[0] < b[0] ? -1 : 1).join() : '');
if ($b6bf6c42eb93d35942f2d0c9c4a9aa06$var$formatterCache.has(cacheKey)) {
return $b6bf6c42eb93d35942f2d0c9c4a9aa06$var$formatterCache.get(cacheKey);
}
let numberFormatter = new Intl.NumberFormat(locale, options);
$b6bf6c42eb93d35942f2d0c9c4a9aa06$var$formatterCache.set(cacheKey, numberFormatter);
return numberFormatter;
}
/** @private - exported for tests */
function $b6bf6c42eb93d35942f2d0c9c4a9aa06$export$numberFormatSignDisplayPolyfill(numberFormat, signDisplay, num) {
if (signDisplay === 'auto') {
return numberFormat.format(num);
} else if (signDisplay === 'never') {
return numberFormat.format(Math.abs(num));
} else {
let needsPositiveSign = false;
if (signDisplay === 'always') {
needsPositiveSign = num > 0 || Object.is(num, 0);
} else if (signDisplay === 'exceptZero') {
if (Object.is(num, -0) || Object.is(num, 0)) {
num = Math.abs(num);
} else {
needsPositiveSign = num > 0;
}
}
if (needsPositiveSign) {
let negative = numberFormat.format(-num);
let noSign = numberFormat.format(num); // ignore RTL/LTR marker character
let minus = negative.replace(noSign, '').replace(/\u200e|\u061C/, '');
if ([...minus].length !== 1) {
console.warn('@react-aria/i18n polyfill for NumberFormat signDisplay: Unsupported case');
}
let positive = negative.replace(noSign, '!!!').replace(minus, '+').replace('!!!', noSign);
return positive;
} else {
return numberFormat.format(num);
}
}
}
const $cbc296943be4217832cce78b76b49b59$var$CURRENCY_SIGN_REGEX = new RegExp('^.*\\(.*\\).*$');
const $cbc296943be4217832cce78b76b49b59$var$NUMBERING_SYSTEMS = ['latn', 'arab', 'hanidec'];
/**
* A NumberParser can be used perform locale aware parsing of numbers from Unicode strings,
* as well as validation of partial user input. Automatically detects the numbering system
* used in the input, and supports parsing decimals, percentages, currency values, and units
* according to the locale.
*/
class NumberParser {
constructor(locale, options) {
if (options === void 0) {
options = {};
}
this.locale = void 0;
this.options = void 0;
this.locale = locale;
this.options = options;
}
/**
* Parses the given string to a number. Returns NaN if a valid number could not be parsed.
*/
parse(value) {
return $cbc296943be4217832cce78b76b49b59$var$getNumberParserImpl(this.locale, this.options, value).parse(value);
}
/**
* Returns whether the given string could potentially be a valid number. This should be used to
* validate user input as the user types. If a `minValue` or `maxValue` is provided, the validity
* of the minus/plus sign characters can be checked.
*/
isValidPartialNumber(value, minValue, maxValue) {
return $cbc296943be4217832cce78b76b49b59$var$getNumberParserImpl(this.locale, this.options, value).isValidPartialNumber(value, minValue, maxValue);
}
/**
* Returns a numbering system for which the given string is valid in the current locale.
* If no numbering system could be detected, the default numbering system for the current
* locale is returned.
*/
getNumberingSystem(value) {
return $cbc296943be4217832cce78b76b49b59$var$getNumberParserImpl(this.locale, this.options, value).options.numberingSystem;
}
}
exports.NumberParser = NumberParser;
const $cbc296943be4217832cce78b76b49b59$var$numberParserCache = new Map();
function $cbc296943be4217832cce78b76b49b59$var$getNumberParserImpl(locale, options, value) {
// First try the default numbering system for the provided locale
let defaultParser = $cbc296943be4217832cce78b76b49b59$var$getCachedNumberParser(locale, options); // If that doesn't match, and the locale doesn't include a hard coded numbering system,
// try each of the other supported numbering systems until we find one that matches.
if (!locale.includes('-u-nu-') && !defaultParser.isValidPartialNumber(value)) {
for (let numberingSystem of $cbc296943be4217832cce78b76b49b59$var$NUMBERING_SYSTEMS) {
if (numberingSystem !== defaultParser.options.numberingSystem) {
let parser = $cbc296943be4217832cce78b76b49b59$var$getCachedNumberParser(locale + '-u-nu-' + numberingSystem, options);
if (parser.isValidPartialNumber(value)) {
return parser;
}
}
}
}
return defaultParser;
}
function $cbc296943be4217832cce78b76b49b59$var$getCachedNumberParser(locale, options) {
let cacheKey = locale + (options ? Object.entries(options).sort((a, b) => a[0] < b[0] ? -1 : 1).join() : '');
let parser = $cbc296943be4217832cce78b76b49b59$var$numberParserCache.get(cacheKey);
if (!parser) {
parser = new $cbc296943be4217832cce78b76b49b59$var$NumberParserImpl(locale, options);
$cbc296943be4217832cce78b76b49b59$var$numberParserCache.set(cacheKey, parser);
}
return parser;
} // The actual number parser implementation. Instances of this class are cached
// based on the locale, options, and detected numbering system.
class $cbc296943be4217832cce78b76b49b59$var$NumberParserImpl {
constructor(locale, options) {
if (options === void 0) {
options = {};
}
this.formatter = void 0;
this.options = void 0;
this.symbols = void 0;
this.formatter = new Intl.NumberFormat(locale, options);
this.options = this.formatter.resolvedOptions();
this.symbols = $cbc296943be4217832cce78b76b49b59$var$getSymbols(this.formatter, this.options, options);
}
parse(value) {
// to parse the number, we need to remove anything that isn't actually part of the number, for example we want '-10.40' not '-10.40 USD'
let fullySanitizedValue = this.sanitize(value); // Remove group characters, and replace decimal points and numerals with ASCII values.
fullySanitizedValue = $cbc296943be4217832cce78b76b49b59$var$replaceAll(fullySanitizedValue, this.symbols.group, '').replace(this.symbols.decimal, '.').replace(this.symbols.minusSign, '-').replace(this.symbols.numeral, this.symbols.index);
let newValue = fullySanitizedValue ? +fullySanitizedValue : NaN;
if (isNaN(newValue)) {
return NaN;
} // accounting will always be stripped to a positive number, so if it's accounting and has a () around everything, then we need to make it negative again
if (this.options.currencySign === 'accounting' && $cbc296943be4217832cce78b76b49b59$var$CURRENCY_SIGN_REGEX.test(value)) {
newValue = -1 * newValue;
} // when reading the number, if it's a percent, then it should be interpreted as being divided by 100
if (this.options.style === 'percent') {
var _this$options$maximum;
newValue /= 100; // after dividing to get the percent value, javascript may get .0210999999 instead of .0211, so fix the number of fraction digits
newValue = +newValue.toFixed(((_this$options$maximum = this.options.maximumFractionDigits) != null ? _this$options$maximum : 0) + 2);
}
return newValue;
}
sanitize(value) {
// Remove literals and whitespace, which are allowed anywhere in the string
value = value.replace(this.symbols.literals, ''); // Replace the ASCII minus sign with the minus sign used in the current locale
// so that both are allowed in case the user's keyboard doesn't have the locale's minus sign.
value = value.replace('-', this.symbols.minusSign); // In arab numeral system, their decimal character is 1643, but most keyboards don't type that
// instead they use the , (44) character or apparently the (1548) character.
if (this.options.numberingSystem === 'arab') {
value = value.replace(',', this.symbols.decimal);
value = value.replace(String.fromCharCode(1548), this.symbols.decimal);
value = $cbc296943be4217832cce78b76b49b59$var$replaceAll(value, '.', this.symbols.group);
} // fr-FR group character is char code 8239, but that's not a key on the french keyboard,
// so allow 'period' as a group char and replace it with a space
if (this.options.locale === 'fr-FR') {
value = $cbc296943be4217832cce78b76b49b59$var$replaceAll(value, '.', String.fromCharCode(8239));
}
return value;
}
isValidPartialNumber(value, minValue, maxValue) {
if (minValue === void 0) {
minValue = -Infinity;
}
if (maxValue === void 0) {
maxValue = Infinity;
}
value = this.sanitize(value); // Remove minus or plus sign, which must be at the start of the string.
if (value.startsWith(this.symbols.minusSign) && minValue < 0) {
value = value.slice(this.symbols.minusSign.length);
} else if (this.symbols.plusSign && value.startsWith(this.symbols.plusSign) && maxValue > 0) {
value = value.slice(this.symbols.plusSign.length);
} // Numbers cannot start with a group separator
if (value.startsWith(this.symbols.group)) {
return false;
} // Remove numerals, groups, and decimals
value = $cbc296943be4217832cce78b76b49b59$var$replaceAll(value, this.symbols.group, '').replace(this.symbols.numeral, '').replace(this.symbols.decimal, ''); // The number is valid if there are no remaining characters
return value.length === 0;
}
}
const $cbc296943be4217832cce78b76b49b59$var$nonLiteralParts = new Set(['decimal', 'fraction', 'integer', 'minusSign', 'plusSign', 'group']);
function $cbc296943be4217832cce78b76b49b59$var$getSymbols(formatter, intlOptions, originalOptions) {
var _allParts$find$value, _allParts$find, _posAllParts$find, _allParts$find2, _allParts$find3;
// Note: some locale's don't add a group symbol until there is a ten thousands place
let allParts = formatter.formatToParts(-10000.1);
let posAllParts = formatter.formatToParts(10000.1);
let singularParts = formatter.formatToParts(1);
let minusSign = (_allParts$find$value = (_allParts$find = allParts.find(p => p.type === 'minusSign')) == null ? void 0 : _allParts$find.value) != null ? _allParts$find$value : '-';
let plusSign = (_posAllParts$find = posAllParts.find(p => p.type === 'plusSign')) == null ? void 0 : _posAllParts$find.value; // Safari does not support the signDisplay option, but our number parser polyfills it.
// If no plus sign was returned, but the original options contained signDisplay, default to the '+' character.
// @ts-ignore
if (!plusSign && ((originalOptions == null ? void 0 : originalOptions.signDisplay) === 'exceptZero' || (originalOptions == null ? void 0 : originalOptions.signDisplay) === 'always')) {
plusSign = '+';
}
let decimal = (_allParts$find2 = allParts.find(p => p.type === 'decimal')) == null ? void 0 : _allParts$find2.value;
let group = (_allParts$find3 = allParts.find(p => p.type === 'group')) == null ? void 0 : _allParts$find3.value; // this set is also for a regex, it's all literals that might be in the string we want to eventually parse that
// don't contribute to the numerical value
let pluralLiterals = allParts.filter(p => !$cbc296943be4217832cce78b76b49b59$var$nonLiteralParts.has(p.type)).map(p => $cbc296943be4217832cce78b76b49b59$var$escapeRegex(p.value));
let singularLiterals = singularParts.filter(p => !$cbc296943be4217832cce78b76b49b59$var$nonLiteralParts.has(p.type)).map(p => $cbc296943be4217832cce78b76b49b59$var$escapeRegex(p.value));
let sortedLiterals = [...new Set([...singularLiterals, ...pluralLiterals])].sort((a, b) => b.length - a.length);
let literals = new RegExp(sortedLiterals.join('|') + "|[\\p{White_Space}]", 'gu'); // These are for replacing non-latn characters with the latn equivalent
let numerals = [...new Intl.NumberFormat(intlOptions.locale, {
useGrouping: false
}).format(9876543210)].reverse();
let indexes = new Map(numerals.map((d, i) => [d, i]));
let numeral = new RegExp("[" + numerals.join('') + "]", 'g');
let index = d => String(indexes.get(d));
return {
minusSign,
plusSign,
decimal,
group,
literals,
numeral,
index
};
}
function $cbc296943be4217832cce78b76b49b59$var$replaceAll(str, find, replace) {
// @ts-ignore
if (str.replaceAll) {
// @ts-ignore
return str.replaceAll(find, replace);
}
return str.split(find).join(replace);
}
function $cbc296943be4217832cce78b76b49b59$var$escapeRegex(string) {
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
}
//# sourceMappingURL=main.js.map

@@ -1,2 +0,3 @@

import _babelRuntimeHelpersEsmExtends from "@babel/runtime/helpers/esm/extends";
import {NumberFormatter as $488c6ddbf4ef74c2$export$cc77c4ff7e8673c5} from "./NumberFormatter.module.js";
import {NumberParser as $6c7bd7858deea686$export$cd11ab140839f11d} from "./NumberParser.module.js";

@@ -13,429 +14,7 @@ /*

* governing permissions and limitations under the License.
*/
let $fe87f22deac4debf3eab2ca6a89602ab$var$formatterCache = new Map();
let $fe87f22deac4debf3eab2ca6a89602ab$var$supportsSignDisplay = false;
*/
try {
// @ts-ignore
$fe87f22deac4debf3eab2ca6a89602ab$var$supportsSignDisplay = new Intl.NumberFormat('de-DE', {
signDisplay: 'exceptZero'
}).resolvedOptions().signDisplay === 'exceptZero'; // eslint-disable-next-line no-empty
} catch (e) {}
let $fe87f22deac4debf3eab2ca6a89602ab$var$supportsUnit = false;
try {
// @ts-ignore
$fe87f22deac4debf3eab2ca6a89602ab$var$supportsUnit = new Intl.NumberFormat('de-DE', {
style: 'unit',
unit: 'degree'
}).resolvedOptions().style === 'unit'; // eslint-disable-next-line no-empty
} catch (e) {} // Polyfill for units since Safari doesn't support them yet. See https://bugs.webkit.org/show_bug.cgi?id=215438.
// Currently only polyfilling the unit degree in narrow format for ColorSlider in our supported locales.
// Values were determined by switching to each locale manually in Chrome.
const $fe87f22deac4debf3eab2ca6a89602ab$var$UNITS = {
degree: {
narrow: {
default: '°',
'ja-JP': ' 度',
'zh-TW': '度',
'sl-SI': ' °' // Arabic?? But Safari already doesn't use Arabic digits so might be ok...
// https://bugs.webkit.org/show_bug.cgi?id=218139
}
}
};
/**
* A wrapper around Intl.NumberFormat providing additional options, polyfills, and caching for performance.
*/
export class NumberFormatter {
constructor(locale, options) {
if (options === void 0) {
options = {};
}
this.numberFormatter = void 0;
this.options = void 0;
this.numberFormatter = $fe87f22deac4debf3eab2ca6a89602ab$var$getCachedNumberFormatter(locale, options);
this.options = options;
}
format(value) {
let res = '';
if (!$fe87f22deac4debf3eab2ca6a89602ab$var$supportsSignDisplay && this.options.signDisplay != null) {
res = $fe87f22deac4debf3eab2ca6a89602ab$export$numberFormatSignDisplayPolyfill(this.numberFormatter, this.options.signDisplay, value);
} else {
res = this.numberFormatter.format(value);
}
if (this.options.style === 'unit' && !$fe87f22deac4debf3eab2ca6a89602ab$var$supportsUnit) {
var _UNITS$unit;
let {
unit,
unitDisplay = 'short',
locale
} = this.resolvedOptions();
let values = (_UNITS$unit = $fe87f22deac4debf3eab2ca6a89602ab$var$UNITS[unit]) == null ? void 0 : _UNITS$unit[unitDisplay];
res += values[locale] || values.default;
}
return res;
}
formatToParts(value) {
// TODO: implement signDisplay for formatToParts
// @ts-ignore
return this.numberFormatter.formatToParts(value);
}
resolvedOptions() {
let options = this.numberFormatter.resolvedOptions();
if (!$fe87f22deac4debf3eab2ca6a89602ab$var$supportsSignDisplay && this.options.signDisplay != null) {
options = _babelRuntimeHelpersEsmExtends({}, options, {
signDisplay: this.options.signDisplay
});
}
if (!$fe87f22deac4debf3eab2ca6a89602ab$var$supportsUnit && this.options.style === 'unit') {
options = _babelRuntimeHelpersEsmExtends({}, options, {
style: 'unit',
unit: this.options.unit,
unitDisplay: this.options.unitDisplay
});
}
return options;
}
}
function $fe87f22deac4debf3eab2ca6a89602ab$var$getCachedNumberFormatter(locale, options) {
if (options === void 0) {
options = {};
}
let {
numberingSystem
} = options;
if (numberingSystem && locale.indexOf('-u-nu-') === -1) {
locale = locale + "-u-nu-" + numberingSystem;
}
if (options.style === 'unit' && !$fe87f22deac4debf3eab2ca6a89602ab$var$supportsUnit) {
var _UNITS$unit2;
let {
unit,
unitDisplay = 'short'
} = options;
if (!unit) {
throw new Error('unit option must be provided with style: "unit"');
}
if (!((_UNITS$unit2 = $fe87f22deac4debf3eab2ca6a89602ab$var$UNITS[unit]) != null && _UNITS$unit2[unitDisplay])) {
throw new Error("Unsupported unit " + unit + " with unitDisplay = " + unitDisplay);
}
options = _babelRuntimeHelpersEsmExtends({}, options, {
style: 'decimal'
});
}
let cacheKey = locale + (options ? Object.entries(options).sort((a, b) => a[0] < b[0] ? -1 : 1).join() : '');
if ($fe87f22deac4debf3eab2ca6a89602ab$var$formatterCache.has(cacheKey)) {
return $fe87f22deac4debf3eab2ca6a89602ab$var$formatterCache.get(cacheKey);
}
let numberFormatter = new Intl.NumberFormat(locale, options);
$fe87f22deac4debf3eab2ca6a89602ab$var$formatterCache.set(cacheKey, numberFormatter);
return numberFormatter;
}
/** @private - exported for tests */
function $fe87f22deac4debf3eab2ca6a89602ab$export$numberFormatSignDisplayPolyfill(numberFormat, signDisplay, num) {
if (signDisplay === 'auto') {
return numberFormat.format(num);
} else if (signDisplay === 'never') {
return numberFormat.format(Math.abs(num));
} else {
let needsPositiveSign = false;
if (signDisplay === 'always') {
needsPositiveSign = num > 0 || Object.is(num, 0);
} else if (signDisplay === 'exceptZero') {
if (Object.is(num, -0) || Object.is(num, 0)) {
num = Math.abs(num);
} else {
needsPositiveSign = num > 0;
}
}
if (needsPositiveSign) {
let negative = numberFormat.format(-num);
let noSign = numberFormat.format(num); // ignore RTL/LTR marker character
let minus = negative.replace(noSign, '').replace(/\u200e|\u061C/, '');
if ([...minus].length !== 1) {
console.warn('@react-aria/i18n polyfill for NumberFormat signDisplay: Unsupported case');
}
let positive = negative.replace(noSign, '!!!').replace(minus, '+').replace('!!!', noSign);
return positive;
} else {
return numberFormat.format(num);
}
}
}
const $e1a22841ad5113a054c8ebe55b24e1a$var$CURRENCY_SIGN_REGEX = new RegExp('^.*\\(.*\\).*$');
const $e1a22841ad5113a054c8ebe55b24e1a$var$NUMBERING_SYSTEMS = ['latn', 'arab', 'hanidec'];
/**
* A NumberParser can be used perform locale aware parsing of numbers from Unicode strings,
* as well as validation of partial user input. Automatically detects the numbering system
* used in the input, and supports parsing decimals, percentages, currency values, and units
* according to the locale.
*/
export class NumberParser {
constructor(locale, options) {
if (options === void 0) {
options = {};
}
this.locale = void 0;
this.options = void 0;
this.locale = locale;
this.options = options;
}
/**
* Parses the given string to a number. Returns NaN if a valid number could not be parsed.
*/
parse(value) {
return $e1a22841ad5113a054c8ebe55b24e1a$var$getNumberParserImpl(this.locale, this.options, value).parse(value);
}
/**
* Returns whether the given string could potentially be a valid number. This should be used to
* validate user input as the user types. If a `minValue` or `maxValue` is provided, the validity
* of the minus/plus sign characters can be checked.
*/
isValidPartialNumber(value, minValue, maxValue) {
return $e1a22841ad5113a054c8ebe55b24e1a$var$getNumberParserImpl(this.locale, this.options, value).isValidPartialNumber(value, minValue, maxValue);
}
/**
* Returns a numbering system for which the given string is valid in the current locale.
* If no numbering system could be detected, the default numbering system for the current
* locale is returned.
*/
getNumberingSystem(value) {
return $e1a22841ad5113a054c8ebe55b24e1a$var$getNumberParserImpl(this.locale, this.options, value).options.numberingSystem;
}
}
const $e1a22841ad5113a054c8ebe55b24e1a$var$numberParserCache = new Map();
function $e1a22841ad5113a054c8ebe55b24e1a$var$getNumberParserImpl(locale, options, value) {
// First try the default numbering system for the provided locale
let defaultParser = $e1a22841ad5113a054c8ebe55b24e1a$var$getCachedNumberParser(locale, options); // If that doesn't match, and the locale doesn't include a hard coded numbering system,
// try each of the other supported numbering systems until we find one that matches.
if (!locale.includes('-u-nu-') && !defaultParser.isValidPartialNumber(value)) {
for (let numberingSystem of $e1a22841ad5113a054c8ebe55b24e1a$var$NUMBERING_SYSTEMS) {
if (numberingSystem !== defaultParser.options.numberingSystem) {
let parser = $e1a22841ad5113a054c8ebe55b24e1a$var$getCachedNumberParser(locale + '-u-nu-' + numberingSystem, options);
if (parser.isValidPartialNumber(value)) {
return parser;
}
}
}
}
return defaultParser;
}
function $e1a22841ad5113a054c8ebe55b24e1a$var$getCachedNumberParser(locale, options) {
let cacheKey = locale + (options ? Object.entries(options).sort((a, b) => a[0] < b[0] ? -1 : 1).join() : '');
let parser = $e1a22841ad5113a054c8ebe55b24e1a$var$numberParserCache.get(cacheKey);
if (!parser) {
parser = new $e1a22841ad5113a054c8ebe55b24e1a$var$NumberParserImpl(locale, options);
$e1a22841ad5113a054c8ebe55b24e1a$var$numberParserCache.set(cacheKey, parser);
}
return parser;
} // The actual number parser implementation. Instances of this class are cached
// based on the locale, options, and detected numbering system.
class $e1a22841ad5113a054c8ebe55b24e1a$var$NumberParserImpl {
constructor(locale, options) {
if (options === void 0) {
options = {};
}
this.formatter = void 0;
this.options = void 0;
this.symbols = void 0;
this.formatter = new Intl.NumberFormat(locale, options);
this.options = this.formatter.resolvedOptions();
this.symbols = $e1a22841ad5113a054c8ebe55b24e1a$var$getSymbols(this.formatter, this.options, options);
}
parse(value) {
// to parse the number, we need to remove anything that isn't actually part of the number, for example we want '-10.40' not '-10.40 USD'
let fullySanitizedValue = this.sanitize(value); // Remove group characters, and replace decimal points and numerals with ASCII values.
fullySanitizedValue = $e1a22841ad5113a054c8ebe55b24e1a$var$replaceAll(fullySanitizedValue, this.symbols.group, '').replace(this.symbols.decimal, '.').replace(this.symbols.minusSign, '-').replace(this.symbols.numeral, this.symbols.index);
let newValue = fullySanitizedValue ? +fullySanitizedValue : NaN;
if (isNaN(newValue)) {
return NaN;
} // accounting will always be stripped to a positive number, so if it's accounting and has a () around everything, then we need to make it negative again
if (this.options.currencySign === 'accounting' && $e1a22841ad5113a054c8ebe55b24e1a$var$CURRENCY_SIGN_REGEX.test(value)) {
newValue = -1 * newValue;
} // when reading the number, if it's a percent, then it should be interpreted as being divided by 100
if (this.options.style === 'percent') {
var _this$options$maximum;
newValue /= 100; // after dividing to get the percent value, javascript may get .0210999999 instead of .0211, so fix the number of fraction digits
newValue = +newValue.toFixed(((_this$options$maximum = this.options.maximumFractionDigits) != null ? _this$options$maximum : 0) + 2);
}
return newValue;
}
sanitize(value) {
// Remove literals and whitespace, which are allowed anywhere in the string
value = value.replace(this.symbols.literals, ''); // Replace the ASCII minus sign with the minus sign used in the current locale
// so that both are allowed in case the user's keyboard doesn't have the locale's minus sign.
value = value.replace('-', this.symbols.minusSign); // In arab numeral system, their decimal character is 1643, but most keyboards don't type that
// instead they use the , (44) character or apparently the (1548) character.
if (this.options.numberingSystem === 'arab') {
value = value.replace(',', this.symbols.decimal);
value = value.replace(String.fromCharCode(1548), this.symbols.decimal);
value = $e1a22841ad5113a054c8ebe55b24e1a$var$replaceAll(value, '.', this.symbols.group);
} // fr-FR group character is char code 8239, but that's not a key on the french keyboard,
// so allow 'period' as a group char and replace it with a space
if (this.options.locale === 'fr-FR') {
value = $e1a22841ad5113a054c8ebe55b24e1a$var$replaceAll(value, '.', String.fromCharCode(8239));
}
return value;
}
isValidPartialNumber(value, minValue, maxValue) {
if (minValue === void 0) {
minValue = -Infinity;
}
if (maxValue === void 0) {
maxValue = Infinity;
}
value = this.sanitize(value); // Remove minus or plus sign, which must be at the start of the string.
if (value.startsWith(this.symbols.minusSign) && minValue < 0) {
value = value.slice(this.symbols.minusSign.length);
} else if (this.symbols.plusSign && value.startsWith(this.symbols.plusSign) && maxValue > 0) {
value = value.slice(this.symbols.plusSign.length);
} // Numbers cannot start with a group separator
if (value.startsWith(this.symbols.group)) {
return false;
} // Remove numerals, groups, and decimals
value = $e1a22841ad5113a054c8ebe55b24e1a$var$replaceAll(value, this.symbols.group, '').replace(this.symbols.numeral, '').replace(this.symbols.decimal, ''); // The number is valid if there are no remaining characters
return value.length === 0;
}
}
const $e1a22841ad5113a054c8ebe55b24e1a$var$nonLiteralParts = new Set(['decimal', 'fraction', 'integer', 'minusSign', 'plusSign', 'group']);
function $e1a22841ad5113a054c8ebe55b24e1a$var$getSymbols(formatter, intlOptions, originalOptions) {
var _allParts$find$value, _allParts$find, _posAllParts$find, _allParts$find2, _allParts$find3;
// Note: some locale's don't add a group symbol until there is a ten thousands place
let allParts = formatter.formatToParts(-10000.1);
let posAllParts = formatter.formatToParts(10000.1);
let singularParts = formatter.formatToParts(1);
let minusSign = (_allParts$find$value = (_allParts$find = allParts.find(p => p.type === 'minusSign')) == null ? void 0 : _allParts$find.value) != null ? _allParts$find$value : '-';
let plusSign = (_posAllParts$find = posAllParts.find(p => p.type === 'plusSign')) == null ? void 0 : _posAllParts$find.value; // Safari does not support the signDisplay option, but our number parser polyfills it.
// If no plus sign was returned, but the original options contained signDisplay, default to the '+' character.
// @ts-ignore
if (!plusSign && ((originalOptions == null ? void 0 : originalOptions.signDisplay) === 'exceptZero' || (originalOptions == null ? void 0 : originalOptions.signDisplay) === 'always')) {
plusSign = '+';
}
let decimal = (_allParts$find2 = allParts.find(p => p.type === 'decimal')) == null ? void 0 : _allParts$find2.value;
let group = (_allParts$find3 = allParts.find(p => p.type === 'group')) == null ? void 0 : _allParts$find3.value; // this set is also for a regex, it's all literals that might be in the string we want to eventually parse that
// don't contribute to the numerical value
let pluralLiterals = allParts.filter(p => !$e1a22841ad5113a054c8ebe55b24e1a$var$nonLiteralParts.has(p.type)).map(p => $e1a22841ad5113a054c8ebe55b24e1a$var$escapeRegex(p.value));
let singularLiterals = singularParts.filter(p => !$e1a22841ad5113a054c8ebe55b24e1a$var$nonLiteralParts.has(p.type)).map(p => $e1a22841ad5113a054c8ebe55b24e1a$var$escapeRegex(p.value));
let sortedLiterals = [...new Set([...singularLiterals, ...pluralLiterals])].sort((a, b) => b.length - a.length);
let literals = new RegExp(sortedLiterals.join('|') + "|[\\p{White_Space}]", 'gu'); // These are for replacing non-latn characters with the latn equivalent
let numerals = [...new Intl.NumberFormat(intlOptions.locale, {
useGrouping: false
}).format(9876543210)].reverse();
let indexes = new Map(numerals.map((d, i) => [d, i]));
let numeral = new RegExp("[" + numerals.join('') + "]", 'g');
let index = d => String(indexes.get(d));
return {
minusSign,
plusSign,
decimal,
group,
literals,
numeral,
index
};
}
function $e1a22841ad5113a054c8ebe55b24e1a$var$replaceAll(str, find, replace) {
// @ts-ignore
if (str.replaceAll) {
// @ts-ignore
return str.replaceAll(find, replace);
}
return str.split(find).join(replace);
}
function $e1a22841ad5113a054c8ebe55b24e1a$var$escapeRegex(string) {
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
}
export {$488c6ddbf4ef74c2$export$cc77c4ff7e8673c5 as NumberFormatter, $6c7bd7858deea686$export$cd11ab140839f11d as NumberParser};
//# sourceMappingURL=module.js.map

@@ -5,2 +5,5 @@ export interface NumberFormatOptions extends Intl.NumberFormatOptions {

}
interface NumberRangeFormatPart extends Intl.NumberFormatPart {
source: 'startRange' | 'endRange' | 'shared';
}
/**

@@ -11,9 +14,16 @@ * A wrapper around Intl.NumberFormat providing additional options, polyfills, and caching for performance.

constructor(locale: string, options?: NumberFormatOptions);
/** Formats a number value as a string, according to the locale and options provided to the constructor. */
format(value: number): string;
/** Formats a number to an array of parts such as separators, digits, punctuation, and more. */
formatToParts(value: number): Intl.NumberFormatPart[];
/** Formats a number range as a string. */
formatRange(start: number, end: number): string;
/** Formats a number range as an array of parts. */
formatRangeToParts(start: number, end: number): NumberRangeFormatPart[];
/** Returns the resolved formatting options based on the values passed to the constructor. */
resolvedOptions(): Intl.ResolvedNumberFormatOptions;
}
/**
* A NumberParser can be used perform locale aware parsing of numbers from Unicode strings,
* as well as validation of partial user input. Automatically detects the numbering system
* A NumberParser can be used to perform locale-aware parsing of numbers from Unicode strings,
* as well as validation of partial user input. It automatically detects the numbering system
* used in the input, and supports parsing decimals, percentages, currency values, and units

@@ -23,4 +33,2 @@ * according to the locale.

export class NumberParser {
locale: string;
options: Intl.NumberFormatOptions;
constructor(locale: string, options?: Intl.NumberFormatOptions);

@@ -27,0 +35,0 @@ /**

{
"name": "@internationalized/number",
"version": "3.0.0-nightly.2703+ac27232f",
"version": "3.0.0-nightly-086ad3115-240911",
"description": "Internationalized number formatting and parsing utilities",

@@ -8,2 +8,7 @@ "license": "Apache-2.0",

"module": "dist/module.js",
"exports": {
"types": "./dist/types.d.ts",
"import": "./dist/import.mjs",
"require": "./dist/main.js"
},
"types": "dist/types.d.ts",

@@ -21,3 +26,3 @@ "source": "src/index.ts",

"dependencies": {
"@babel/runtime": "^7.6.2"
"@swc/helpers": "^0.5.0"
},

@@ -27,3 +32,3 @@ "publishConfig": {

},
"gitHead": "ac27232f81bc0b9430efbedb154131b7f0d26352"
}
"stableVersion": "3.5.3"
}
# @internationalized/number
This package is part of [react-spectrum](https://github.com/adobe/react-spectrum). See the repo for more details.
## NumberParser
The `NumberParser` class can be used perform locale-aware parsing of numbers from Unicode strings,
as well as validation of partial user input. It automatically detects the numbering system
used in the input, and supports parsing decimals, percentages, currency values, and units
according to the locale.
### Parsing
```js
import {NumberParser} from '@internationalized/number';
let parser = new NumberParser('en-US', {style: 'percent'});
parser.parse('10%'); // -> 0.1
```
### Validation
```js
import {NumberParser} from '@internationalized/number';
let parser = new NumberParser('en-US', {style: 'unit', unit: 'inch'});
parser.isValidPartialNumber('10 '); // -> true
parser.isValidPartialNumber('10 in'); // -> true
parser.isValidPartialNumber('10 i'); // -> false
parser.isValidPartialNumber('10 x'); // -> false
```
### Detecting the numbering system
```js
import {NumberParser} from '@internationalized/number';
let parser = new NumberParser('en-US', {style: 'decimal'});
parser.getNumberingSystem('١٢') // -> 'arabic'
```
## NumberFormatter
The `NumberFormatter` class is a wrapper around [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) providing additional options, polyfills, and caching for performance. It provides the exact same interface as Intl.NumberFormat, so it is a drop-in replacement. Please see the MDN docs linked above for more details.
We currently polyfill the following features:
* The `signDisplay` option
* The `unit` style, currently only for the `degree` unit in the `narrow` style

@@ -50,2 +50,6 @@ /*

interface NumberRangeFormatPart extends Intl.NumberFormatPart {
source: 'startRange' | 'endRange' | 'shared'
}
/**

@@ -63,2 +67,3 @@ * A wrapper around Intl.NumberFormat providing additional options, polyfills, and caching for performance.

/** Formats a number value as a string, according to the locale and options provided to the constructor. */
format(value: number): string {

@@ -74,2 +79,5 @@ let res = '';

let {unit, unitDisplay = 'short', locale} = this.resolvedOptions();
if (!unit) {
return res;
}
let values = UNITS[unit]?.[unitDisplay];

@@ -82,2 +90,3 @@ res += values[locale] || values.default;

/** Formats a number to an array of parts such as separators, digits, punctuation, and more. */
formatToParts(value: number): Intl.NumberFormatPart[] {

@@ -89,2 +98,40 @@ // TODO: implement signDisplay for formatToParts

/** Formats a number range as a string. */
formatRange(start: number, end: number): string {
// @ts-ignore
if (typeof this.numberFormatter.formatRange === 'function') {
// @ts-ignore
return this.numberFormatter.formatRange(start, end);
}
if (end < start) {
throw new RangeError('End date must be >= start date');
}
// Very basic fallback for old browsers.
return `${this.format(start)} – ${this.format(end)}`;
}
/** Formats a number range as an array of parts. */
formatRangeToParts(start: number, end: number): NumberRangeFormatPart[] {
// @ts-ignore
if (typeof this.numberFormatter.formatRangeToParts === 'function') {
// @ts-ignore
return this.numberFormatter.formatRangeToParts(start, end);
}
if (end < start) {
throw new RangeError('End date must be >= start date');
}
let startParts = this.numberFormatter.formatToParts(start);
let endParts = this.numberFormatter.formatToParts(end);
return [
...startParts.map(p => ({...p, source: 'startRange'} as NumberRangeFormatPart)),
{type: 'literal', value: ' – ', source: 'shared'},
...endParts.map(p => ({...p, source: 'endRange'} as NumberRangeFormatPart))
];
}
/** Returns the resolved formatting options based on the values passed to the constructor. */
resolvedOptions(): Intl.ResolvedNumberFormatOptions {

@@ -106,4 +153,7 @@ let options = this.numberFormatter.resolvedOptions();

let {numberingSystem} = options;
if (numberingSystem && locale.indexOf('-u-nu-') === -1) {
locale = `${locale}-u-nu-${numberingSystem}`;
if (numberingSystem && locale.includes('-nu-')) {
if (!locale.includes('-u-')) {
locale += '-u-';
}
locale += `-nu-${numberingSystem}`;
}

@@ -124,3 +174,3 @@

if (formatterCache.has(cacheKey)) {
return formatterCache.get(cacheKey);
return formatterCache.get(cacheKey)!;
}

@@ -134,3 +184,3 @@

/** @private - exported for tests */
export function numberFormatSignDisplayPolyfill(numberFormat: Intl.NumberFormat, signDisplay: 'always' | 'exceptZero' | 'auto' | 'never', num: number) {
export function numberFormatSignDisplayPolyfill(numberFormat: Intl.NumberFormat, signDisplay: string, num: number) {
if (signDisplay === 'auto') {

@@ -137,0 +187,0 @@ return numberFormat.format(num);

@@ -13,7 +13,9 @@ /*

import {NumberFormatter} from './NumberFormatter';
interface Symbols {
minusSign: string,
plusSign: string,
decimal: string,
group: string,
minusSign?: string,
plusSign?: string,
decimal?: string,
group?: string,
literals: RegExp,

@@ -28,4 +30,4 @@ numeral: RegExp,

/**
* A NumberParser can be used perform locale aware parsing of numbers from Unicode strings,
* as well as validation of partial user input. Automatically detects the numbering system
* A NumberParser can be used to perform locale-aware parsing of numbers from Unicode strings,
* as well as validation of partial user input. It automatically detects the numbering system
* used in the input, and supports parsing decimals, percentages, currency values, and units

@@ -35,4 +37,4 @@ * according to the locale.

export class NumberParser {
locale: string;
options: Intl.NumberFormatOptions;
private locale: string;
private options: Intl.NumberFormatOptions;

@@ -77,6 +79,6 @@ constructor(locale: string, options: Intl.NumberFormatOptions = {}) {

// try each of the other supported numbering systems until we find one that matches.
if (!locale.includes('-u-nu-') && !defaultParser.isValidPartialNumber(value)) {
if (!locale.includes('-nu-') && !defaultParser.isValidPartialNumber(value)) {
for (let numberingSystem of NUMBERING_SYSTEMS) {
if (numberingSystem !== defaultParser.options.numberingSystem) {
let parser = getCachedNumberParser(locale + '-u-nu-' + numberingSystem, options);
let parser = getCachedNumberParser(locale + (locale.includes('-u-') ? '-nu-' : '-u-nu-') + numberingSystem, options);
if (parser.isValidPartialNumber(value)) {

@@ -109,7 +111,12 @@ return parser;

symbols: Symbols;
locale: string;
constructor(locale: string, options: Intl.NumberFormatOptions = {}) {
this.locale = locale;
this.formatter = new Intl.NumberFormat(locale, options);
this.options = this.formatter.resolvedOptions();
this.symbols = getSymbols(this.formatter, this.options, options);
this.symbols = getSymbols(locale, this.formatter, this.options, options);
if (this.options.style === 'percent' && ((this.options.minimumFractionDigits ?? 0) > 18 || (this.options.maximumFractionDigits ?? 0) > 18)) {
console.warn('NumberParser cannot handle percentages with greater than 18 decimal places, please reduce the number in your options.');
}
}

@@ -121,8 +128,37 @@

// Remove group characters, and replace decimal points and numerals with ASCII values.
fullySanitizedValue = replaceAll(fullySanitizedValue, this.symbols.group, '')
.replace(this.symbols.decimal, '.')
.replace(this.symbols.minusSign, '-')
.replace(this.symbols.numeral, this.symbols.index);
if (this.symbols.group) {
// Remove group characters, and replace decimal points and numerals with ASCII values.
fullySanitizedValue = replaceAll(fullySanitizedValue, this.symbols.group, '');
}
if (this.symbols.decimal) {
fullySanitizedValue = fullySanitizedValue.replace(this.symbols.decimal!, '.');
}
if (this.symbols.minusSign) {
fullySanitizedValue = fullySanitizedValue.replace(this.symbols.minusSign!, '-');
}
fullySanitizedValue = fullySanitizedValue.replace(this.symbols.numeral, this.symbols.index);
if (this.options.style === 'percent') {
// javascript is bad at dividing by 100 and maintaining the same significant figures, so perform it on the string before parsing
let isNegative = fullySanitizedValue.indexOf('-');
fullySanitizedValue = fullySanitizedValue.replace('-', '');
let index = fullySanitizedValue.indexOf('.');
if (index === -1) {
index = fullySanitizedValue.length;
}
fullySanitizedValue = fullySanitizedValue.replace('.', '');
if (index - 2 === 0) {
fullySanitizedValue = `0.${fullySanitizedValue}`;
} else if (index - 2 === -1) {
fullySanitizedValue = `0.0${fullySanitizedValue}`;
} else if (index - 2 === -2) {
fullySanitizedValue = '0.00';
} else {
fullySanitizedValue = `${fullySanitizedValue.slice(0, index - 2)}.${fullySanitizedValue.slice(index - 2)}`;
}
if (isNegative > -1) {
fullySanitizedValue = `-${fullySanitizedValue}`;
}
}
let newValue = fullySanitizedValue ? +fullySanitizedValue : NaN;

@@ -133,2 +169,13 @@ if (isNaN(newValue)) {

if (this.options.style === 'percent') {
// extra step for rounding percents to what our formatter would output
let options = {
...this.options,
style: 'decimal' as const,
minimumFractionDigits: Math.min((this.options.minimumFractionDigits ?? 0) + 2, 20),
maximumFractionDigits: Math.min((this.options.maximumFractionDigits ?? 0) + 2, 20)
};
return (new NumberParser(this.locale, options)).parse(new NumberFormatter(this.locale, options).format(newValue));
}
// accounting will always be stripped to a positive number, so if it's accounting and has a () around everything, then we need to make it negative again

@@ -139,9 +186,2 @@ if (this.options.currencySign === 'accounting' && CURRENCY_SIGN_REGEX.test(value)) {

// when reading the number, if it's a percent, then it should be interpreted as being divided by 100
if (this.options.style === 'percent') {
newValue /= 100;
// after dividing to get the percent value, javascript may get .0210999999 instead of .0211, so fix the number of fraction digits
newValue = +newValue.toFixed((this.options.maximumFractionDigits ?? 0) + 2);
}
return newValue;

@@ -156,3 +196,5 @@ }

// so that both are allowed in case the user's keyboard doesn't have the locale's minus sign.
value = value.replace('-', this.symbols.minusSign);
if (this.symbols.minusSign) {
value = value.replace('-', this.symbols.minusSign);
}

@@ -162,5 +204,9 @@ // In arab numeral system, their decimal character is 1643, but most keyboards don't type that

if (this.options.numberingSystem === 'arab') {
value = value.replace(',', this.symbols.decimal);
value = value.replace(String.fromCharCode(1548), this.symbols.decimal);
value = replaceAll(value, '.', this.symbols.group);
if (this.symbols.decimal) {
value = value.replace(',', this.symbols.decimal);
value = value.replace(String.fromCharCode(1548), this.symbols.decimal);
}
if (this.symbols.group) {
value = replaceAll(value, '.', this.symbols.group);
}
}

@@ -181,3 +227,3 @@

// Remove minus or plus sign, which must be at the start of the string.
if (value.startsWith(this.symbols.minusSign) && minValue < 0) {
if (this.symbols.minusSign && value.startsWith(this.symbols.minusSign) && minValue < 0) {
value = value.slice(this.symbols.minusSign.length);

@@ -189,10 +235,19 @@ } else if (this.symbols.plusSign && value.startsWith(this.symbols.plusSign) && maxValue > 0) {

// Numbers cannot start with a group separator
if (value.startsWith(this.symbols.group)) {
if (this.symbols.group && value.startsWith(this.symbols.group)) {
return false;
}
// Numbers that can't have any decimal values fail if a decimal character is typed
if (this.symbols.decimal && value.indexOf(this.symbols.decimal) > -1 && this.options.maximumFractionDigits === 0) {
return false;
}
// Remove numerals, groups, and decimals
value = replaceAll(value, this.symbols.group, '')
.replace(this.symbols.numeral, '')
.replace(this.symbols.decimal, '');
if (this.symbols.group) {
value = replaceAll(value, this.symbols.group, '');
}
value = value.replace(this.symbols.numeral, '');
if (this.symbols.decimal) {
value = value.replace(this.symbols.decimal, '');
}

@@ -206,7 +261,16 @@ // The number is valid if there are no remaining characters

function getSymbols(formatter: Intl.NumberFormat, intlOptions: Intl.ResolvedNumberFormatOptions, originalOptions: Intl.NumberFormatOptions): Symbols {
// This list is derived from https://www.unicode.org/cldr/charts/43/supplemental/language_plural_rules.html#comparison and includes
// all unique numbers which we need to check in order to determine all the plural forms for a given locale.
// See: https://github.com/adobe/react-spectrum/pull/5134/files#r1337037855 for used script
const pluralNumbers = [
0, 4, 2, 1, 11, 20, 3, 7, 100, 21, 0.1, 1.1
];
function getSymbols(locale: string, formatter: Intl.NumberFormat, intlOptions: Intl.ResolvedNumberFormatOptions, originalOptions: Intl.NumberFormatOptions): Symbols {
// formatter needs access to all decimal places in order to generate the correct literal strings for the plural set
let symbolFormatter = new Intl.NumberFormat(locale, {...intlOptions, minimumSignificantDigits: 1, maximumSignificantDigits: 21});
// Note: some locale's don't add a group symbol until there is a ten thousands place
let allParts = formatter.formatToParts(-10000.1);
let posAllParts = formatter.formatToParts(10000.1);
let singularParts = formatter.formatToParts(1);
let allParts = symbolFormatter.formatToParts(-10000.111);
let posAllParts = symbolFormatter.formatToParts(10000.111);
let pluralParts = pluralNumbers.map(n => symbolFormatter.formatToParts(n));

@@ -223,3 +287,7 @@ let minusSign = allParts.find(p => p.type === 'minusSign')?.value ?? '-';

let decimal = allParts.find(p => p.type === 'decimal')?.value;
// If maximumSignificantDigits is 1 (the minimum) then we won't get decimal characters out of the above formatters
// Percent also defaults to 0 fractionDigits, so we need to make a new one that isn't percent to get an accurate decimal
let decimalParts = new Intl.NumberFormat(locale, {...intlOptions, minimumFractionDigits: 2, maximumFractionDigits: 2}).formatToParts(0.001);
let decimal = decimalParts.find(p => p.type === 'decimal')?.value;
let group = allParts.find(p => p.type === 'group')?.value;

@@ -229,7 +297,10 @@

// don't contribute to the numerical value
let pluralLiterals = allParts.filter(p => !nonLiteralParts.has(p.type)).map(p => escapeRegex(p.value));
let singularLiterals = singularParts.filter(p => !nonLiteralParts.has(p.type)).map(p => escapeRegex(p.value));
let sortedLiterals = [...new Set([...singularLiterals, ...pluralLiterals])].sort((a, b) => b.length - a.length);
let literals = new RegExp(`${sortedLiterals.join('|')}|[\\p{White_Space}]`, 'gu');
let allPartsLiterals = allParts.filter(p => !nonLiteralParts.has(p.type)).map(p => escapeRegex(p.value));
let pluralPartsLiterals = pluralParts.flatMap(p => p.filter(p => !nonLiteralParts.has(p.type)).map(p => escapeRegex(p.value)));
let sortedLiterals = [...new Set([...allPartsLiterals, ...pluralPartsLiterals])].sort((a, b) => b.length - a.length);
let literals = sortedLiterals.length === 0 ?
new RegExp('[\\p{White_Space}]', 'gu') :
new RegExp(`${sortedLiterals.join('|')}|[\\p{White_Space}]`, 'gu');
// These are for replacing non-latn characters with the latn equivalent

@@ -255,3 +326,3 @@ let numerals = [...new Intl.NumberFormat(intlOptions.locale, {useGrouping: false}).format(9876543210)].reverse();

function escapeRegex(string: string) {
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc