This commit is contained in:
Sebastian 2023-02-28 07:41:51 -03:00
parent ed7aa0806d
commit 04eec324bf
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
2 changed files with 85 additions and 86 deletions

View File

@ -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>
); );

View File

@ -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 {
//remove all but last dot const parsed = Amounts.parseOrThrow(`${currency}:${value.trim()}`);
const withoutDots = value.replace(/(\.)(?=.*\1)/g, "");
const parts = withoutDots.split(".");
setDecimalPlaces(parts.length === 1 ? undefined : parts[1].length);
//FIXME: should normalize before parsing
//parsing first add some restriction on the rage of the values
const parsed = parseValue(currency, withoutDots);
if (!parsed || parsed.value < 0) {
return previousValue;
}
const realValue = denormalize(parsed, unit); const realValue = denormalize(parsed, unit);
// console.log(real, unit, normal); if (handler.onInput) {
if (realValue && handler.onInput) {
handler.onInput(realValue); handler.onInput(realValue);
} }
return withoutDots; setError("");
} catch (e) { } catch (e) {
// do nothing setError(i18n.str`Amount is not valid`);
} }
return previousValue; setTextValue(value);
return value;
} }
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;
}