@blueprintjs/core
Version:
Core styles & components
155 lines • 7.42 kB
JavaScript
/*
* Copyright 2018 Palantir Technologies, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { clamp } from "../../common/utils";
/** Returns the `decimal` number separator based on locale */
function getDecimalSeparator(locale) {
var testNumber = 1.9;
var testText = testNumber.toLocaleString(locale);
var one = (1).toLocaleString(locale);
var nine = (9).toLocaleString(locale);
var pattern = "".concat(one, "(.+)").concat(nine);
var result = new RegExp(pattern).exec(testText);
return (result && result[1]) || ".";
}
export function toLocaleString(num, locale) {
if (locale === void 0) { locale = "en-US"; }
return sanitizeNumericInput(num.toLocaleString(locale), locale);
}
export function clampValue(value, min, max) {
// defaultProps won't work if the user passes in null, so just default
// to +/- infinity here instead, as a catch-all.
var adjustedMin = min != null ? min : -Infinity;
var adjustedMax = max != null ? max : Infinity;
return clamp(value, adjustedMin, adjustedMax);
}
export function getValueOrEmptyValue(value) {
if (value === void 0) { value = ""; }
return value.toString();
}
/** Transform the localized character (ex. "") to a javascript recognizable string number (ex. "10.99") */
function transformLocalizedNumberToStringNumber(character, locale) {
var charactersMap = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(function (value) { return value.toLocaleString(locale); });
var jsNumber = charactersMap.indexOf(character);
if (jsNumber !== -1) {
return jsNumber;
}
else {
return character;
}
}
/** Transforms the localized number (ex. "10,99") to a javascript recognizable string number (ex. "10.99") */
export function parseStringToStringNumber(value, locale) {
var valueAsString = "" + value;
if (parseFloat(valueAsString).toString() === value.toString()) {
return value.toString();
}
if (locale !== undefined) {
var decimalSeparator = getDecimalSeparator(locale);
var sanitizedString = sanitizeNumericInput(valueAsString, locale);
return sanitizedString
.split("")
.map(function (character) { return transformLocalizedNumberToStringNumber(character, locale); })
.join("")
.replace(decimalSeparator, ".");
}
return value.toString();
}
/** Returns `true` if the string represents a valid numeric value, like "1e6". */
export function isValueNumeric(value, locale) {
// checking if a string is numeric in Typescript is a big pain, because
// we can't simply toss a string parameter to isFinite. below is the
// essential approach that jQuery uses, which involves subtracting a
// parsed numeric value from the string representation of the value. we
// need to cast the value to the `any` type to allow this operation
// between dissimilar types.
var stringToStringNumber = parseStringToStringNumber(value, locale);
return value != null && stringToStringNumber - parseFloat(stringToStringNumber) + 1 >= 0;
}
export function isValidNumericKeyboardEvent(e, locale) {
// unit tests may not include e.key. don't bother disabling those events.
if (e.key == null) {
return true;
}
// allow modified key strokes that may involve letters and other
// non-numeric/invalid characters (Cmd + A, Cmd + C, Cmd + V, Cmd + X).
if (e.ctrlKey || e.altKey || e.metaKey) {
return true;
}
// keys that print a single character when pressed have a `key` name of
// length 1. every other key has a longer `key` name (e.g. "Backspace",
// "ArrowUp", "Shift"). since none of those keys can print a character
// to the field--and since they may have important native behaviors
// beyond printing a character--we don't want to disable their effects.
var isSingleCharKey = e.key.length === 1;
if (!isSingleCharKey) {
return true;
}
// now we can simply check that the single character that wants to be printed
// is a floating-point number character that we're allowed to print.
return isFloatingPointNumericCharacter(e.key, locale);
}
/**
* A regex that matches a string of length 1 (i.e. a standalone character)
* if and only if it is a floating-point number character as defined by W3C:
* https://www.w3.org/TR/2012/WD-html-markup-20120329/datatypes.html#common.data.float
*
* Floating-point number characters are the only characters that can be
* printed within a default input[type="number"]. This component should
* behave the same way when this.props.allowNumericCharactersOnly = true.
* See here for the input[type="number"].value spec:
* https://www.w3.org/TR/2012/WD-html-markup-20120329/input.number.html#input.number.attrs.value
*/
function isFloatingPointNumericCharacter(character, locale) {
if (locale !== undefined) {
var decimalSeparator = getDecimalSeparator(locale).replace(".", "\\.");
var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(function (value) { return value.toLocaleString(locale); }).join("");
var localeFloatingPointNumericCharacterRegex = new RegExp("^[Ee" + numbers + "\\+\\-" + decimalSeparator + "]$");
return localeFloatingPointNumericCharacterRegex.test(character);
}
else {
var floatingPointNumericCharacterRegex = /^[Ee0-9\+\-\.]$/;
return floatingPointNumericCharacterRegex.test(character);
}
}
/**
* Round the value to have _up to_ the specified maximum precision.
*
* This differs from `toFixed(5)` in that trailing zeroes are not added on
* more precise values, resulting in shorter strings.
*/
export function toMaxPrecision(value, maxPrecision) {
// round the value to have the specified maximum precision (toFixed is the wrong choice,
// because it would show trailing zeros in the decimal part out to the specified precision)
// source: http://stackoverflow.com/a/18358056/5199574
var scaleFactor = Math.pow(10, maxPrecision);
return Math.round(value * scaleFactor) / scaleFactor;
}
/**
* Convert Japanese full-width numbers, e.g. '5', to ASCII, e.g. '5'
* This should be called before performing any other numeric string input validation.
*/
function convertFullWidthNumbersToAscii(value) {
return value.replace(/[\uFF10-\uFF19]/g, function (m) { return String.fromCharCode(m.charCodeAt(0) - 0xfee0); });
}
/**
* Convert full-width (Japanese) numbers to ASCII, and strip all characters that are not valid floating-point numeric characters
*/
export function sanitizeNumericInput(value, locale) {
var valueChars = convertFullWidthNumbersToAscii(value).split("");
var sanitizedValueChars = valueChars.filter(function (valueChar) { return isFloatingPointNumericCharacter(valueChar, locale); });
return sanitizedValueChars.join("");
}
//# sourceMappingURL=numericInputUtils.js.map