/* This file is part of GNU Taler (C) 2022 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ import { amountFractionalBase, amountFractionalLength, AmountJson, amountMaxValue, Amounts, Result, TranslatedString, } from "@gnu-taler/taler-util"; import { Fragment, h, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { AmountFieldHandler } from "../mui/handlers.js"; import { TextField } from "../mui/TextField.js"; const HIGH_DENOM_SYMBOL = ["", "K", "M", "G", "T", "P"]; const LOW_DENOM_SYMBOL = ["", "m", "mm", "n", "p", "f"]; /** * Show normalized value based on the currency unit */ export function AmountField({ label, handler, lowestDenom = 1, highestDenom = 1, required, }: { label: TranslatedString; lowestDenom?: number; highestDenom?: number; required?: boolean; handler: AmountFieldHandler; }): VNode { const { i18n } = useTranslationContext(); const [unit, setUnit] = useState(1); const [error, setError] = useState(""); const normal = normalize(handler.value, unit); const previousValue = Amounts.stringifyValue(normal); const [textValue, setTextValue] = useState(previousValue); useEffect(() => { setTextValue(previousValue); }, [previousValue]); function updateUnit(newUnit: number) { setUnit(newUnit); const newNorm = normalize(handler.value, newUnit); setTextValue(Amounts.stringifyValue(newNorm)); } const currency = handler.value.currency; const currencyLabels = buildLabelsForCurrency( currency, lowestDenom, highestDenom, ); function positiveAmount(value: string): string { if (!value) { if (handler.onInput) { handler.onInput(Amounts.zeroOfCurrency(currency)); } } else try { const parsed = Amounts.parseOrThrow(`${currency}:${value.trim()}`); const realValue = denormalize(parsed, unit); if (handler.onInput) { handler.onInput(realValue); } setError(""); } catch (e) { setError(i18n.str`Amount is not valid`); } setTextValue(value); return value; } return ( {currency} ) : ( ) } value={textValue} disabled={!handler.onInput} onInput={positiveAmount} /> {error &&
{error}
}
); } /** * 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 * @param amount * @param unit * @returns */ function denormalize(amount: AmountJson, unit: number): AmountJson { if (unit === 1 || Amounts.isZero(amount)) return amount; const result = unit < 1 ? Amounts.divide(amount, 1 / unit) : Amounts.mult(amount, unit).amount; return result; } /** * Return the amount in the current unit. * If the value is 20000 and the unit is kilo == 1000 and the returned value will be amount / unit * * @param amount * @param unit * @returns */ function normalize(amount: AmountJson, unit: number): AmountJson { if (unit === 1 || Amounts.isZero(amount)) return amount; const result = unit < 1 ? Amounts.mult(amount, 1 / unit).amount : Amounts.divide(amount, unit); 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; }