Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 55 additions & 35 deletions packages/devextreme/js/__internal/core/utils/m_math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ const inRange = function (value, minValue, maxValue) {
return value >= minValue && value <= maxValue;
};

function getExponent(value) {
return Math.abs(parseInt(value.toExponential().split('e')[1], 10));
function getExponent(value: number) {
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-unused-vars
const [_, exponentString] = value.toExponential().split('e');
return Math.abs(parseInt(exponentString, 10));
}

function getExponentialNotation(value) {
Expand All @@ -44,57 +46,75 @@ function multiplyInExponentialForm(value, exponentShift) {
return parseFloat(`${exponentialNotation.mantissa}e${exponentialNotation.exponent + exponentShift}`);
}

// T570217
function isEdgeBug() {
const value = 0.0003;
const correctValue = '0.000300';
const precisionValue = 3;
return correctValue !== value.toPrecision(precisionValue);
}
const EXP_TO_CHANGE_NOTATION = 7;
const MAX_PRECISION = 15;
const MIN_PRECISION = 7;

function adjust(value, interval?) {
let precision = getPrecision(interval || 0) + 2;
const separatedValue = value.toString().split('.');
const sourceValue = value;
function adjust(value: number, interval?: number): number {
const absValue = Math.abs(value);
let separatedAdjustedValue;
const isExponentValue = isExponential(value);
const integerPart = absValue > 1 ? 10 : 0;

if (separatedValue.length === 1) {
const precision = getPrecision(interval ?? 0) + 2;

const finalPrecision = precision > EXP_TO_CHANGE_NOTATION ? MAX_PRECISION : MIN_PRECISION;

const [integerValuePart, fractionalValuePart] = value.toString().split('.');
const sourceValue = value;

const isExponentValue = isExponential(value);

if (isExponentValue) {
return adjustExponential(value, finalPrecision);
}

if (!fractionalValuePart) {
return value;
}

if (!isExponentValue) {
if (isExponential(interval)) {
precision = separatedValue[0].length + getExponent(interval);
}
value = absValue;
value = value - Math.floor(value) + integerPart;
if (isExponential(interval)) {
const expPrecision = integerValuePart.length + getExponent(interval);
return parseFloat(sourceValue.toPrecision(expPrecision));
}

precision = (isEdgeBug() && (getExponent(value) > 6)) || precision > 7 ? 15 : 7; // fix toPrecision() bug in Edge (T570217)
const fractionalPart = absValue - Math.floor(absValue);
const adjustedValue = integerPart + fractionalPart;

if (!isExponentValue) {
separatedAdjustedValue = parseFloat(value.toPrecision(precision)).toString().split('.');
if (separatedAdjustedValue[0] === integerPart.toString()) {
return parseFloat(`${separatedValue[0]}.${separatedAdjustedValue[1]}`);
}
const separatedAdjustedValue = parseFloat(adjustedValue.toPrecision(finalPrecision)).toString().split('.');

const isIntPartNotChanged = separatedAdjustedValue[0] === integerPart.toString();
if (isIntPartNotChanged) {
return parseFloat(`${integerValuePart}.${separatedAdjustedValue[1]}`);
}

return parseFloat(sourceValue.toPrecision(finalPrecision));
}

function adjustExponential(value: number, precision: number) {
const expValue = value.toExponential();

// eslint-disable-next-line @stylistic/max-len
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/naming-convention
const [mantissa, _exponent] = expValue.split('e');

if (!mantissa.includes('.')) {
return parseFloat(expValue);
}
return parseFloat(sourceValue.toPrecision(precision));

return parseFloat(value.toPrecision(precision));
}

function getPrecision(value) {
function getPrecision(value: number) {
const str = value.toString();

if (str.indexOf('.') < 0) {
if (!str.includes('.')) {
return 0;
}

const mantissa = str.split('.');
const positionOfDelimiter = mantissa[1].indexOf('e');
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/naming-convention
const [_, fractionalPart] = str.split('.');
const positionOfDelimiter = fractionalPart.indexOf('e');

return positionOfDelimiter >= 0 ? positionOfDelimiter : mantissa[1].length;
return positionOfDelimiter >= 0 ? positionOfDelimiter : fractionalPart.length;
}

function getRoot(x, n) {
Expand Down Expand Up @@ -180,7 +200,7 @@ function getExponentLength(value) {
|| 0;
}

function roundFloatPart(value, digitsCount = 0) {
function roundFloatPart(value: number, digitsCount = 0): number {
return parseFloat(value.toFixed(digitsCount));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import {
getCaretWithOffset, isCaretInBoundaries,
} from './m_number_box.caret';
import {
adjustPercentValue, getNthOccurrence, getRealSeparatorIndex, splitByIndex,
adjustPercentValue, getNthOccurrence, getRealSeparatorIndex,
splitByIndex,
} from './m_utils';

const NUMBER_FORMATTER_NAMESPACE = 'dxNumberFormatter';
Expand Down
18 changes: 15 additions & 3 deletions packages/devextreme/js/__internal/ui/number_box/m_utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { adjust } from '@js/core/utils/math';
import { adjust, roundFloatPart } from '@js/core/utils/math';

const getRealSeparatorIndex = function (str) {
let quoteBalance = 0;
Expand Down Expand Up @@ -40,13 +40,25 @@ const splitByIndex = function (str, index) {
return [str.slice(0, index), str.slice(index + 1)];
};

const adjustPercentValue = function (rawValue, precision) {
return rawValue && adjust(rawValue / 100, precision);
const adjustPercentValue = function (rawValue, interval) {
if (!rawValue) {
return rawValue;
}

return adjust(rawValue / 100, interval / 100);
};

const roundFloatPartPercentValue = function (rawValue: number, precision: number) {
if (!rawValue) {
return rawValue;
}
return roundFloatPart(rawValue / 100, precision);
};

export {
adjustPercentValue,
getNthOccurrence,
getRealSeparatorIndex,
roundFloatPartPercentValue,
splitByIndex,
};
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export default {
const translateCategories = translate / interval;
const visibleCount = (that.visibleCategories || []).length;
let startCategoryIndex = parseInt((canvasOptions.startPointIndex || 0) + translateCategories + 0.5);
// @ts-expect-error
const categoriesLength = parseInt(adjust(canvasOptions.canvasLength / interval) + (stick ? 1 : 0)) || 1;
let endCategoryIndex;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export default {
if (value < rMin) {
offset = 0;
} else if (value > rMax) {
// @ts-expect-error
offset = dateUtils.addInterval(rMax, this._options.interval) - rMin;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ _Translator2d.prototype = {
break;
case 'semidiscrete':
script = intervalTranslator;
// @ts-expect-error
canvasOptions.ratioOfCanvasRange = canvasOptions.canvasLength / (dateUtils.addInterval(canvasOptions.rangeMaxVisible, options.interval) - canvasOptions.rangeMinVisible);
break;
case 'discrete':
Expand Down Expand Up @@ -541,8 +542,8 @@ _Translator2d.prototype = {
let newMin = canvasOptions.rangeMinVisible.valueOf() - correction;
let newMax = canvasOptions.rangeMaxVisible.valueOf() + correction;

newMin = isLogarithmic ? adjust(raiseToExt(newMin, canvasOptions.base)) : isDateTime ? new Date(newMin) : newMin;
newMax = isLogarithmic ? adjust(raiseToExt(newMax, canvasOptions.base)) : isDateTime ? new Date(newMax) : newMax;
newMin = (isLogarithmic ? adjust(raiseToExt(newMin, canvasOptions.base)) : isDateTime ? new Date(newMin) : newMin) as number;
newMax = (isLogarithmic ? adjust(raiseToExt(newMax, canvasOptions.base)) : isDateTime ? new Date(newMax) : newMax) as number;

return {
min: newMin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,14 @@ QUnit.module('format: percent format', moduleConfig, () => {
});
});

QUnit.test('It should be possible to type full decimal part when integer part is bigger than 100 and precision=5 (T1277123)', function(assert) {
this.instance.option('format', { type: 'percent', precision: 5 });
this.keyboard.type('123.12345').change();

assert.strictEqual(this.input.val(), '123.12345%', 'text is correct');
assert.strictEqual(this.instance.option('value'), 1.2312345, 'value is correct');
});

QUnit.test('It should be possible to use percent format with more than 7 fractional digits (T1277123)', function(assert) {
this.instance.option('format', '#0.###########%');
this.keyboard.type('0.123456789').change();
Expand Down
Loading