mirror of
https://github.com/rowyio/rowy.git
synced 2026-09-01 19:50:46 +02:00
Merge pull request #1108 from rowyio/fix/percentage-precision-error
Fix/percentage precision error
This commit is contained in:
@@ -2,6 +2,7 @@ import { IDisplayCellProps } from "@src/components/fields/types";
|
||||
|
||||
import { useTheme } from "@mui/material";
|
||||
import { resultColorsScale } from "@src/utils/color";
|
||||
import { multiply100WithPrecision } from "./utils";
|
||||
|
||||
export default function Percentage({ column, value }: IDisplayCellProps) {
|
||||
const theme = useTheme();
|
||||
@@ -34,7 +35,7 @@ export default function Percentage({ column, value }: IDisplayCellProps) {
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
{Math.round(percentage * 100)}%
|
||||
{multiply100WithPrecision(percentage)}%
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
import type { IEditorCellProps } from "@src/components/fields/types";
|
||||
import EditorCellTextField from "@src/components/Table/TableCell/EditorCellTextField";
|
||||
import { multiply100WithPrecision, divide100WithPrecision } from "./utils";
|
||||
|
||||
export default function Percentage(props: IEditorCellProps<number>) {
|
||||
return (
|
||||
<EditorCellTextField
|
||||
{...(props as any)}
|
||||
InputProps={{ type: "number", endAdornment: "%" }}
|
||||
value={typeof props.value === "number" ? props.value * 100 : props.value}
|
||||
onChange={(v) => props.onChange(Number(v) / 100)}
|
||||
value={
|
||||
typeof props.value === "number"
|
||||
? multiply100WithPrecision(props.value)
|
||||
: props.value
|
||||
}
|
||||
onChange={(v) => {
|
||||
props.onChange(divide100WithPrecision(Number(v)));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { ISettingsProps } from "@src/components/fields/types";
|
||||
import { Color, toColor } from "react-color-palette";
|
||||
import { fieldSx } from "@src/components/SideDrawer/utils";
|
||||
import { resultColorsScale, defaultColors } from "@src/utils/color";
|
||||
import { multiply100WithPrecision } from "./utils";
|
||||
|
||||
const colorLabels: { [key: string]: string } = {
|
||||
0: "Start",
|
||||
@@ -160,7 +161,7 @@ const Preview = ({ colors }: { colors: any }) => {
|
||||
}}
|
||||
/>
|
||||
<Typography style={{ position: "relative", zIndex: 1 }}>
|
||||
{Math.floor(value * 100)}%
|
||||
{multiply100WithPrecision(value)}%
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ISideDrawerFieldProps } from "@src/components/fields/types";
|
||||
import { TextField, InputAdornment, Box, useTheme } from "@mui/material";
|
||||
import { resultColorsScale } from "@src/utils/color";
|
||||
import { getFieldId } from "@src/components/SideDrawer/utils";
|
||||
import { multiply100WithPrecision } from "./utils";
|
||||
|
||||
export default function Percentage({
|
||||
column,
|
||||
@@ -20,7 +21,9 @@ export default function Percentage({
|
||||
margin="none"
|
||||
onChange={(e) => onChange(Number(e.target.value) / 100)}
|
||||
onBlur={onSubmit}
|
||||
value={typeof value === "number" ? value * 100 : value}
|
||||
value={
|
||||
typeof value === "number" ? multiply100WithPrecision(value) : value
|
||||
}
|
||||
id={getFieldId(column.key)}
|
||||
label=""
|
||||
hiddenLabel
|
||||
|
||||
126
src/components/fields/Percentage/utils.ts
Normal file
126
src/components/fields/Percentage/utils.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import { trim, trimEnd } from "lodash-es";
|
||||
|
||||
/**
|
||||
* Multiply a number by 100 and return a string without floating point error
|
||||
* by shifting the decimal point 2 places to the right as a string
|
||||
* e.g. floating point error: 0.07 * 100 === 7.000000000000001
|
||||
*
|
||||
* A few examples:
|
||||
*
|
||||
* let number = 0.07;
|
||||
* console.log(number, multiply100WithPrecision(number));
|
||||
* --> 7
|
||||
*
|
||||
* number = 0;
|
||||
* console.log(number, multiply100WithPrecision(number));
|
||||
* --> 0
|
||||
*
|
||||
* number = 0.1;
|
||||
* console.log(number, multiply100WithPrecision(number));
|
||||
* --> 10
|
||||
*
|
||||
* number = 0.001;
|
||||
* console.log(number, multiply100WithPrecision(number));
|
||||
* --> 0.1
|
||||
*
|
||||
* number = 0.00001;
|
||||
* console.log(number, multiply100WithPrecision(number));
|
||||
* --> 0.001
|
||||
*
|
||||
* number = 100;
|
||||
* console.log(number, multiply100WithPrecision(number));
|
||||
* --> 10000
|
||||
*
|
||||
* number = 1999.99;
|
||||
* console.log(number, multiply100WithPrecision(number));
|
||||
* --> 199999
|
||||
*
|
||||
* number = 1999.999;
|
||||
* console.log(number, multiply100WithPrecision(number));
|
||||
* --> 199999.9
|
||||
*
|
||||
* number = 0.25;
|
||||
* console.log(number, multiply100WithPrecision(number));
|
||||
* --> 25
|
||||
*
|
||||
* number = 0.15;
|
||||
* console.log(number, multiply100WithPrecision(number));
|
||||
* --> 15
|
||||
*
|
||||
* number = 1.23456789;
|
||||
* console.log(number, multiply100WithPrecision(number));
|
||||
* --> 123.456789
|
||||
*
|
||||
* number = 0.0000000001;
|
||||
* console.log(number, multiply100WithPrecision(number));
|
||||
* --> 1e-8
|
||||
*/
|
||||
export const multiply100WithPrecision = (value: number) => {
|
||||
if (value === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let valueString = value.toString();
|
||||
|
||||
// e.g 1e-10 becomes 1e-8
|
||||
if (valueString.includes("e")) {
|
||||
return value * 100;
|
||||
}
|
||||
|
||||
// if the number is integer, add .00
|
||||
if (!valueString.includes(".")) {
|
||||
valueString = valueString.concat(".00");
|
||||
}
|
||||
|
||||
let [before, after] = valueString.split(".");
|
||||
|
||||
// if after decimal has only 1 digit, pad a 0
|
||||
if (after.length === 1) {
|
||||
after = after.concat("0");
|
||||
}
|
||||
|
||||
let newNumber = `${before}${after.slice(0, 2)}.${after.slice(2)}`;
|
||||
newNumber = trimEnd(trim(newNumber, "0"), ".");
|
||||
if (newNumber.startsWith(".")) {
|
||||
newNumber = "0" + newNumber;
|
||||
}
|
||||
return Number(newNumber);
|
||||
};
|
||||
|
||||
/**
|
||||
* Divide a number by 100 and return a string without floating point error
|
||||
* by shifting the decimal point 2 places to the left as a string
|
||||
*/
|
||||
export const divide100WithPrecision = (value: number) => {
|
||||
if (value === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let valueString = value.toString();
|
||||
|
||||
// e.g 1e-10 becomes 1e-8
|
||||
if (valueString.includes("e")) {
|
||||
return value / 100;
|
||||
}
|
||||
|
||||
// add decimal if integer
|
||||
if (!valueString.includes(".")) {
|
||||
valueString = valueString + ".";
|
||||
}
|
||||
|
||||
let [before, after] = valueString.split(".");
|
||||
|
||||
// if before decimal has less than digit, pad 0
|
||||
if (before.length < 2) {
|
||||
before = "00" + before;
|
||||
}
|
||||
|
||||
let newNumber = `${before.slice(0, before.length - 2)}.${before.slice(
|
||||
before.length - 2
|
||||
)}${after}`;
|
||||
newNumber = trimEnd(trimEnd(newNumber, "0"), ".");
|
||||
if (newNumber.startsWith(".")) {
|
||||
newNumber = "0" + newNumber;
|
||||
}
|
||||
return Number(newNumber);
|
||||
};
|
||||
Reference in New Issue
Block a user