fix #7731
This commit is contained in:
parent
ed7aa0806d
commit
04eec324bf
@ -31,7 +31,11 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function RenderAmount(): VNode {
|
function RenderAmount(): VNode {
|
||||||
const [value, setValue] = useState<AmountJson | undefined>(undefined);
|
const [value, setValue] = useState<AmountJson | undefined>({
|
||||||
|
currency: "USD",
|
||||||
|
value: 1,
|
||||||
|
fraction: 0,
|
||||||
|
});
|
||||||
|
|
||||||
const error = value === undefined ? undefined : undefined;
|
const error = value === undefined ? undefined : undefined;
|
||||||
|
|
||||||
@ -53,7 +57,10 @@ function RenderAmount(): VNode {
|
|||||||
handler={handler}
|
handler={handler}
|
||||||
/>
|
/>
|
||||||
<p>
|
<p>
|
||||||
<pre>{JSON.stringify(value, undefined, 2)}</pre>
|
<pre>
|
||||||
|
value : {value?.value} <br />
|
||||||
|
fraction : {value?.fraction}
|
||||||
|
</pre>
|
||||||
</p>
|
</p>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
@ -25,6 +25,7 @@ import {
|
|||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { Fragment, h, VNode } from "preact";
|
import { Fragment, h, VNode } from "preact";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
|
import { useTranslationContext } from "../context/translation.js";
|
||||||
import { AmountFieldHandler } from "../mui/handlers.js";
|
import { AmountFieldHandler } from "../mui/handlers.js";
|
||||||
import { TextField } from "../mui/TextField.js";
|
import { TextField } from "../mui/TextField.js";
|
||||||
|
|
||||||
@ -47,80 +48,49 @@ export function AmountField({
|
|||||||
required?: boolean;
|
required?: boolean;
|
||||||
handler: AmountFieldHandler;
|
handler: AmountFieldHandler;
|
||||||
}): VNode {
|
}): VNode {
|
||||||
|
const { i18n } = useTranslationContext();
|
||||||
const [unit, setUnit] = useState(1);
|
const [unit, setUnit] = useState(1);
|
||||||
const [decimalPlaces, setDecimalPlaces] = useState<number | undefined>(
|
const [error, setError] = useState<string>("");
|
||||||
undefined,
|
|
||||||
);
|
const normal = normalize(handler.value, unit);
|
||||||
|
const previousValue = Amounts.stringifyValue(normal);
|
||||||
|
|
||||||
|
const [textValue, setTextValue] = useState<string>(previousValue);
|
||||||
|
|
||||||
|
function updateUnit(newUnit: number) {
|
||||||
|
setUnit(newUnit);
|
||||||
|
const newNorm = normalize(handler.value, newUnit);
|
||||||
|
setTextValue(Amounts.stringifyValue(newNorm));
|
||||||
|
}
|
||||||
|
|
||||||
const currency = handler.value.currency;
|
const currency = handler.value.currency;
|
||||||
|
|
||||||
let hd = Math.floor(Math.log10(highestDenom || 1) / 3);
|
const currencyLabels = buildLabelsForCurrency(
|
||||||
let ld = Math.ceil((-1 * Math.log10(lowestDenom || 1)) / 3);
|
currency,
|
||||||
|
lowestDenom,
|
||||||
const currencyLabels: Array<{ name: string; unit: number }> = [
|
highestDenom,
|
||||||
{
|
);
|
||||||
name: currency,
|
|
||||||
unit: 1,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
while (hd > 0) {
|
|
||||||
currencyLabels.push({
|
|
||||||
name: `${HIGH_DENOM_SYMBOL[hd]}${currency}`,
|
|
||||||
unit: Math.pow(10, hd * 3),
|
|
||||||
});
|
|
||||||
hd--;
|
|
||||||
}
|
|
||||||
while (ld > 0) {
|
|
||||||
currencyLabels.push({
|
|
||||||
name: `${LOW_DENOM_SYMBOL[ld]}${currency}`,
|
|
||||||
unit: Math.pow(10, -1 * ld * 3),
|
|
||||||
});
|
|
||||||
ld--;
|
|
||||||
}
|
|
||||||
|
|
||||||
const previousValue = Amounts.stringifyValue(handler.value, decimalPlaces);
|
|
||||||
|
|
||||||
const normal = normalize(handler.value, unit) ?? handler.value;
|
|
||||||
|
|
||||||
let textValue = Amounts.stringifyValue(normal, decimalPlaces);
|
|
||||||
if (decimalPlaces === 0) {
|
|
||||||
textValue += ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
function positiveAmount(value: string): string {
|
function positiveAmount(value: string): string {
|
||||||
// setDotAtTheEnd(value.endsWith("."));
|
|
||||||
// const dotAtTheEnd = value.endsWith(".");
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
if (handler.onInput) {
|
if (handler.onInput) {
|
||||||
handler.onInput(Amounts.zeroOfCurrency(currency));
|
handler.onInput(Amounts.zeroOfCurrency(currency));
|
||||||
}
|
}
|
||||||
return "";
|
} else
|
||||||
}
|
try {
|
||||||
try {
|
const parsed = Amounts.parseOrThrow(`${currency}:${value.trim()}`);
|
||||||
//remove all but last dot
|
|
||||||
const withoutDots = value.replace(/(\.)(?=.*\1)/g, "");
|
|
||||||
const parts = withoutDots.split(".");
|
|
||||||
setDecimalPlaces(parts.length === 1 ? undefined : parts[1].length);
|
|
||||||
|
|
||||||
//FIXME: should normalize before parsing
|
const realValue = denormalize(parsed, unit);
|
||||||
//parsing first add some restriction on the rage of the values
|
|
||||||
const parsed = parseValue(currency, withoutDots);
|
|
||||||
|
|
||||||
if (!parsed || parsed.value < 0) {
|
if (handler.onInput) {
|
||||||
return previousValue;
|
handler.onInput(realValue);
|
||||||
|
}
|
||||||
|
setError("");
|
||||||
|
} catch (e) {
|
||||||
|
setError(i18n.str`Amount is not valid`);
|
||||||
}
|
}
|
||||||
|
setTextValue(value);
|
||||||
const realValue = denormalize(parsed, unit);
|
return value;
|
||||||
|
|
||||||
// console.log(real, unit, normal);
|
|
||||||
if (realValue && handler.onInput) {
|
|
||||||
handler.onInput(realValue);
|
|
||||||
}
|
|
||||||
return withoutDots;
|
|
||||||
} catch (e) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
return previousValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -149,7 +119,7 @@ export function AmountField({
|
|||||||
disabled={!handler.onInput}
|
disabled={!handler.onInput}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const unit = Number.parseFloat(e.currentTarget.value);
|
const unit = Number.parseFloat(e.currentTarget.value);
|
||||||
setUnit(unit);
|
updateUnit(unit);
|
||||||
}}
|
}}
|
||||||
value={String(unit)}
|
value={String(unit)}
|
||||||
style={{
|
style={{
|
||||||
@ -171,29 +141,11 @@ export function AmountField({
|
|||||||
disabled={!handler.onInput}
|
disabled={!handler.onInput}
|
||||||
onInput={positiveAmount}
|
onInput={positiveAmount}
|
||||||
/>
|
/>
|
||||||
|
{error && <div style={{ color: "red" }}>{error}</div>}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseValue(currency: string, s: string): AmountJson | undefined {
|
|
||||||
const [intPart, fractPart] = s.split(".");
|
|
||||||
const tailPart = !fractPart
|
|
||||||
? "0"
|
|
||||||
: fractPart.substring(0, amountFractionalLength);
|
|
||||||
|
|
||||||
const value = Number.parseInt(intPart, 10);
|
|
||||||
const parsedTail = Number.parseFloat(`.${tailPart}`);
|
|
||||||
if (Number.isNaN(value) || Number.isNaN(parsedTail)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
if (value > amountMaxValue) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fraction = Math.round(amountFractionalBase * parsedTail);
|
|
||||||
return { currency, fraction, value };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the real value of a normalized unit
|
* Return the real value of a normalized unit
|
||||||
* If the value is 20 and the unit is kilo == 1000 the returned value will be amount * 1000
|
* If the value is 20 and the unit is kilo == 1000 the returned value will be amount * 1000
|
||||||
@ -201,7 +153,7 @@ function parseValue(currency: string, s: string): AmountJson | undefined {
|
|||||||
* @param unit
|
* @param unit
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function denormalize(amount: AmountJson, unit: number): AmountJson | undefined {
|
function denormalize(amount: AmountJson, unit: number): AmountJson {
|
||||||
if (unit === 1 || Amounts.isZero(amount)) return amount;
|
if (unit === 1 || Amounts.isZero(amount)) return amount;
|
||||||
const result =
|
const result =
|
||||||
unit < 1
|
unit < 1
|
||||||
@ -218,7 +170,7 @@ function denormalize(amount: AmountJson, unit: number): AmountJson | undefined {
|
|||||||
* @param unit
|
* @param unit
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function normalize(amount: AmountJson, unit: number): AmountJson | undefined {
|
function normalize(amount: AmountJson, unit: number): AmountJson {
|
||||||
if (unit === 1 || Amounts.isZero(amount)) return amount;
|
if (unit === 1 || Amounts.isZero(amount)) return amount;
|
||||||
const result =
|
const result =
|
||||||
unit < 1
|
unit < 1
|
||||||
@ -226,3 +178,43 @@ function normalize(amount: AmountJson, unit: number): AmountJson | undefined {
|
|||||||
: Amounts.divide(amount, unit);
|
: Amounts.divide(amount, unit);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take every label in HIGH_DENOM_SYMBOL and LOW_DENOM_SYMBOL and create
|
||||||
|
* which create the corresponding unit multiplier
|
||||||
|
* @param currency
|
||||||
|
* @param lowestDenom
|
||||||
|
* @param highestDenom
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function buildLabelsForCurrency(
|
||||||
|
currency: string,
|
||||||
|
lowestDenom: number,
|
||||||
|
highestDenom: number,
|
||||||
|
): Array<{ name: string; unit: number }> {
|
||||||
|
let hd = Math.floor(Math.log10(highestDenom || 1) / 3);
|
||||||
|
let ld = Math.ceil((-1 * Math.log10(lowestDenom || 1)) / 3);
|
||||||
|
|
||||||
|
const result: Array<{ name: string; unit: number }> = [
|
||||||
|
{
|
||||||
|
name: currency,
|
||||||
|
unit: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
while (hd > 0) {
|
||||||
|
result.push({
|
||||||
|
name: `${HIGH_DENOM_SYMBOL[hd]}${currency}`,
|
||||||
|
unit: Math.pow(10, hd * 3),
|
||||||
|
});
|
||||||
|
hd--;
|
||||||
|
}
|
||||||
|
while (ld > 0) {
|
||||||
|
result.push({
|
||||||
|
name: `${LOW_DENOM_SYMBOL[ld]}${currency}`,
|
||||||
|
unit: Math.pow(10, -1 * ld * 3),
|
||||||
|
});
|
||||||
|
ld--;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user