UNPKG

@syncfusion/react-base

Version:

A common package of core React base, methods and class definitions

376 lines (375 loc) 14.3 kB
import { isUndefined, throwError, isNullOrUndefined, extend } from '../util'; import { defaultCurrencyCode } from '../internationalization'; import { IntlBase as base } from './intl-base'; import { ParserBase as parser } from './parser-base'; /** * Error text mappings for significant and fraction digits. */ const errorText = { 'ms': 'minimumSignificantDigits', 'ls': 'maximumSignificantDigits', 'mf': 'minimumFractionDigits', 'lf': 'maximumFractionDigits' }; /** * Percent sign constant. */ const percentSign = 'percentSign'; /** * Minus sign constant. */ const minusSign = 'minusSign'; /** * Mapper array for numeric symbols. */ const mapper = ['infinity', 'nan', 'group', 'decimal', 'exponential']; /** * Custom object structure for NumberFormat with all methods and properties. */ export const NumberFormat = (() => { /** * Returns the formatter function for given skeleton. * * @param {string} culture - Specifies the culture name to be used for formatting. * @param {NumberFormatOptions} option - Specifies the format in which number will be formatted. * @param {Object} cldr - Specifies the global cldr data collection. * @returns {Function} Returns the formatter function for the given number format options. */ function numberFormatter(culture, option, cldr) { const fOptions = extend({}, option); let cOptions = {}; const dOptions = {}; let symbolPattern; const dependable = base.getDependables(cldr, culture, '', true); dOptions.numberMapper = parser.getNumberMapper(dependable.parserObject, parser.getNumberingSystem(cldr), true); dOptions.currencySymbol = base.getCurrencySymbol(dependable.numericObject, fOptions.currency || defaultCurrencyCode, option.altSymbol, option.ignoreCurrency); dOptions.percentSymbol = (dOptions).numberMapper.numberSymbols[`${percentSign}`]; dOptions.minusSymbol = (dOptions).numberMapper.numberSymbols[`${minusSign}`]; const symbols = dOptions.numberMapper.numberSymbols; if (option.format && !base.formatRegex.test(option.format)) { cOptions = base.customFormat(option.format, dOptions, dependable.numericObject); if (!isUndefined(fOptions.useGrouping) && fOptions.useGrouping) { fOptions.useGrouping = cOptions.pData.useGrouping; } } else { extend(fOptions, base.getProperNumericSkeleton(option.format || 'N')); fOptions.isCurrency = fOptions.type === 'currency'; fOptions.isPercent = fOptions.type === 'percent'; symbolPattern = base.getSymbolPattern(fOptions.type, dOptions.numberMapper.numberSystem, dependable.numericObject, fOptions.isAccount); fOptions.groupOne = checkValueRange(fOptions.maximumSignificantDigits, fOptions.minimumSignificantDigits, true); checkValueRange(fOptions.maximumFractionDigits, fOptions.minimumFractionDigits, false, true); if (!isUndefined(fOptions.fractionDigits)) { fOptions.minimumFractionDigits = fOptions.maximumFractionDigits = fOptions.fractionDigits; } if (isUndefined(fOptions.useGrouping)) { fOptions.useGrouping = true; } if (fOptions.isCurrency) { symbolPattern = symbolPattern.replace(/\u00A4/g, base.defaultCurrency); } const split = symbolPattern.split(';'); cOptions.nData = base.getFormatData(split[1] || '-' + split[0], true, dOptions.currencySymbol); cOptions.pData = base.getFormatData(split[0], false, dOptions.currencySymbol); if (fOptions.useGrouping) { fOptions.groupSeparator = symbols[mapper[2]]; fOptions.groupData = getGroupingDetails(split[0]); } const minFrac = isUndefined(fOptions.minimumFractionDigits); if (minFrac) { fOptions.minimumFractionDigits = cOptions.nData.minimumFraction; } if (isUndefined(fOptions.maximumFractionDigits)) { const mval = cOptions.nData.maximumFraction; fOptions.maximumFractionDigits = isUndefined(mval) && fOptions.isPercent ? 0 : mval; } const mfrac = fOptions.minimumFractionDigits; const lfrac = fOptions.maximumFractionDigits; if (!isUndefined(mfrac) && !isUndefined(lfrac)) { if (mfrac > lfrac) { fOptions.maximumFractionDigits = mfrac; } } } extend(cOptions.nData, fOptions); extend(cOptions.pData, fOptions); return (value) => { if (isNaN(value)) { return symbols[mapper[1]]; } else if (!isFinite(value)) { return symbols[mapper[0]]; } return intNumberFormatter(value, cOptions, dOptions, option); }; } /** * Returns grouping details for the pattern provided. * * @param {string} pattern ? * @returns {GroupDetails} Returns an object containing primary and secondary grouping details */ function getGroupingDetails(pattern) { const ret = {}; const match = pattern.match(base.negativeDataRegex); if (match && match[4]) { const pattern = match[4]; const p = pattern.lastIndexOf(','); if (p !== -1) { const temp = pattern.split('.')[0]; ret.primary = (temp.length - p) - 1; const s = pattern.lastIndexOf(',', p - 1); if (s !== -1) { ret.secondary = p - 1 - s; } } } return ret; } /** * Checks if the provided integer range is valid. * * @param {number} val1 ? * @param {number} val2 ? * @param {boolean} checkbothExist ? * @param {boolean} isFraction ? * @returns {boolean} ? */ function checkValueRange(val1, val2, checkbothExist, isFraction) { const decide = isFraction ? 'f' : 's'; let dint = 0; const str1 = (errorText)['l' + decide]; const str2 = (errorText)['m' + decide]; if (!isUndefined(val1)) { checkRange(val1, str1, isFraction); dint++; } if (!isUndefined(val2)) { checkRange(val2, str2, isFraction); dint++; } if (dint === 2) { if (val1 < val2) { throwError(str2 + 'specified must be less than the' + str1); } else { return true; } } else if (checkbothExist && dint === 1) { throwError('Both' + str2 + 'and' + str2 + 'must be present'); } return false; } /** * Validates if a value is within a specified range. * * @param {number} val ? * @param {string} text ? * @param {boolean} isFraction ? * @returns {void} */ function checkRange(val, text, isFraction) { const range = isFraction ? [0, 20] : [1, 21]; if (val < range[0] || val > range[1]) { throwError(text + 'value must be within the range' + range[0] + 'to' + range[1]); } } /** * Formats numeric strings based on options. * * @param {number} value ? * @param {GenericFormatOptions} fOptions ? * @param {CommonOptions} dOptions ? * @param {NumberFormatOptions} [option] ? * @returns {string} ? */ function intNumberFormatter(value, fOptions, dOptions, option) { let curData; if (isUndefined(fOptions.nData.type)) { return null; } else { if (value < 0) { value = value * -1; curData = fOptions.nData; } else if (value === 0) { curData = fOptions.zeroData || fOptions.pData; } else { curData = fOptions.pData; } let fValue = ''; if (curData.isPercent) { value = value * 100; } if (curData.groupOne) { fValue = processSignificantDigits(value, curData.minimumSignificantDigits, curData.maximumSignificantDigits); } else { fValue = processFraction(value, curData.minimumFractionDigits, curData.maximumFractionDigits, option); if (curData.minimumIntegerDigits) { fValue = processMinimumIntegers(fValue, curData.minimumIntegerDigits); } if (dOptions.isCustomFormat && curData.minimumFractionDigits < curData.maximumFractionDigits && /\d+\.\d+/.test(fValue)) { const temp = fValue.split('.'); let decimalPart = temp[1]; const len = decimalPart.length; for (let i = len - 1; i >= 0; i--) { if (decimalPart[parseInt(i.toString(), 10)] === '0' && i >= curData.minimumFractionDigits) { decimalPart = decimalPart.slice(0, i); } else { break; } } fValue = temp[0] + '.' + decimalPart; } } if (curData.type === 'scientific') { fValue = value.toExponential(curData.maximumFractionDigits); fValue = fValue.replace('e', dOptions.numberMapper.numberSymbols[mapper[4]]); } fValue = fValue.replace('.', (dOptions).numberMapper.numberSymbols[mapper[3]]); fValue = curData.format === '#,###,,;(#,###,,)' ? customPivotFormat(parseInt(fValue, 10)) : fValue; if (curData.useGrouping) { fValue = groupNumbers(fValue, curData.groupData.primary, curData.groupSeparator || ',', (dOptions).numberMapper.numberSymbols[mapper[3]] || '.', curData.groupData.secondary); } fValue = parser.convertValueParts(fValue, base.latnParseRegex, dOptions.numberMapper.mapper); if (curData.nlead === 'N/A') { return curData.nlead; } else { if (fValue === '0' && option && option.format === '0') { return fValue + curData.nend; } return curData.nlead + fValue + curData.nend; } } } /** * Processes significant digits for a value. * * @param {number} value ? * @param {number} min ? * @param {number} max ? * @returns {string} ? */ function processSignificantDigits(value, min, max) { let temp = value + ''; let tn; const length = temp.length; if (length < min) { return value.toPrecision(min); } else { temp = value.toPrecision(max); tn = +temp; return tn + ''; } } /** * Groups numeric strings based on separator and levels. * * @param {string} val ? * @param {number} level1 ? * @param {string} sep ? * @param {string} decimalSymbol ? * @param {number} level2 ? * @returns {string} ? */ function groupNumbers(val, level1, sep, decimalSymbol, level2) { let flag = !isNullOrUndefined(level2) && level2 !== 0; const split = val.split(decimalSymbol); const prefix = split[0]; let length = prefix.length; let str = ''; while (length > level1) { str = prefix.slice(length - level1, length) + (str.length ? (sep + str) : ''); length -= level1; if (flag) { level1 = level2; flag = false; } } split[0] = prefix.slice(0, length) + (str.length ? sep : '') + str; return split.join(decimalSymbol); } /** * Processes the fraction part of the numeric value. * * @param {number} value ? * @param {number} min ? * @param {number} max ? * @param {NumberFormatOptions} [option] ? * @returns {string} ? */ function processFraction(value, min, max, option) { const temp = (value + '').split('.')[1]; const length = temp ? temp.length : 0; if (min && length < min) { let ret = ''; if (length === 0) { ret = value.toFixed(min); } else { ret += value; for (let j = 0; j < min - length; j++) { ret += '0'; } return ret; } return value.toFixed(min); } else if (!isNullOrUndefined(max) && (length > max || max === 0)) { return value.toFixed(max); } let str = value + ''; if (str[0] === '0' && option && option.format === '###.00') { str = str.slice(1); } return str; } /** * Processes integer part ensuring minimum digit count. * * @param {string} value ? * @param {number} min ? * @returns {string} ? */ function processMinimumIntegers(value, min) { const temp = value.split('.'); let lead = temp[0]; const len = lead.length; if (len < min) { for (let i = 0; i < min - len; i++) { lead = '0' + lead; } temp[0] = lead; } return temp.join('.'); } /** * Formats for pivot tables specifically. * * @param {number} value ? * @returns {string} ? */ function customPivotFormat(value) { if (value >= 500000) { value /= 1000000; const decimal = value.toString().split('.')[1]; return decimal && +decimal.substring(0, 1) >= 5 ? Math.ceil(value).toString() : Math.floor(value).toString(); } return ''; } return { getGroupingDetails, numberFormatter }; })();