Files

147 lines
6.1 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:de7888fd00d7c2b69a752977ab166eb917e92dd391509e94d32511d630d11079
import { CONFIG } from '../../shared/config.js';
import { isMetal, metalPrimary, metalSecondary, fromBaseByFactor } from '../../shared/units.js';
import { esc } from './escape.js';
import { appSetting, baseCurrency } from '../app-settings.js';
import { localeTag } from '../i18n/index.js';
export {
isMetal, hasSubunit, toBaseByFactor, fromBaseByFactor,
mainToSub, subToMain, toSubunit, fromSubunit,
priceToBase, priceFromBase,
} from '../../shared/units.js';
export const fmtInt = n => Math.round(n).toLocaleString(localeTag());
export function parseLocaleNumber(text) {
const s = String(text ?? '').replace(/[^0-9.,-]/g, '');
if (!/\d/.test(s)) return NaN;
const decimal = new Intl.NumberFormat(localeTag()).formatToParts(1.5).find(p => p.type === 'decimal').value;
const group = decimal === ',' ? '.' : ',';
let whole = s, frac = '';
const at = s.lastIndexOf(decimal);
if (at >= 0) {
whole = s.slice(0, at);
frac = s.slice(at + 1);
} else {
const pieces = s.split(group);
const head = pieces[0].replace('-', '');
if (pieces.length === 2 && (pieces[1].length !== 3 || head === '' || head === '0')) [whole, frac] = pieces;
}
return Number(`${whole.replaceAll(group, '') || '0'}.${frac.replace(/[.,]/g, '') || '0'}`);
}
const MUNT = new Map();
function munt(currency) {
const code = String(currency).toUpperCase();
if (!CONFIG.PORTFOLIO.BASE_CURRENCIES.includes(code)) throw new Error(`Unknown currency: '${currency}'.`);
const key = `${localeTag()}|${code}`;
if (!MUNT.has(key)) {
const nf = new Intl.NumberFormat(localeTag(), { style: 'currency', currency: code, currencyDisplay: 'narrowSymbol' });
MUNT.set(key, {
sym: nf.formatToParts(0).find(p => p.type === 'currency').value,
dec: nf.resolvedOptions().maximumFractionDigits,
});
}
return MUNT.get(key);
}
export const currencySymbol = currency => munt(currency).sym;
export function currencyName(currency) {
munt(currency);
const naam = new Intl.DisplayNames([localeTag()], { type: 'currency' }).of(String(currency).toUpperCase());
return naam.charAt(0).toUpperCase() + naam.slice(1);
}
export function fmtMoney(value, currency, { decimals } = {}) {
const { sym, dec } = munt(currency);
if (decimals != null) {
return `${sym} ${Number(value).toLocaleString(localeTag(), { minimumFractionDigits: decimals, maximumFractionDigits: decimals })}`;
}
const abs = Math.abs(Number(value));
const maxDec = (abs > 0 && abs < 0.5 * Math.pow(10, -dec)) ? Math.min(8, dec - Math.floor(Math.log10(abs))) : dec;
return `${sym} ${Number(value).toLocaleString(localeTag(), { minimumFractionDigits: dec, maximumFractionDigits: maxDec })}`;
}
export const fmtBase = value => fmtMoney(value, baseCurrency());
export const pct = (fractie) => `${+(fractie * 100).toFixed(4)}%`;
export function fmtPct(fractie, { fine = false, signs = 'both' } = {}) {
if (typeof fractie !== 'number' || !Number.isFinite(fractie)) return '';
const tekst = d => Math.abs(fractie * 100).toLocaleString(localeTag(), { minimumFractionDigits: d, maximumFractionDigits: d });
const nul = s => !/[1-9]/.test(s);
let getal = tekst(1);
if (fine && nul(getal) && !nul(tekst(2))) getal = tekst(2);
const teken = nul(getal) ? '' : signFor(fractie >= 0, signs);
return `${teken}${getal}%`;
}
const signFor = (positief, signs) => (positief
? (signs === 'both' ? '+' : '')
: (signs === 'none' ? '' : '\u2212'));
export const colouredSigns = () => appSetting('result_signs');
export function fmtBaseSigned(value, signs) {
const v = Number(value) || 0;
if (Math.abs(v) < 0.005) return fmtBase(0);
const tekst = fmtBase(Math.abs(v));
const spatie = tekst.indexOf(' ');
return `${tekst.slice(0, spatie + 1)}${signFor(v > 0, signs)}${tekst.slice(spatie + 1)}`;
}
export function pctTone(fractie, opts = {}) {
const s = fmtPct(fractie, { ...opts, signs: 'both' });
return s.startsWith('+') ? 't-pos' : s.startsWith('\u2212') ? 't-neg' : '';
}
function priceDecimals(abs) {
if (abs >= 100) return 2;
if (abs >= 1) return 4;
return 8;
}
export function fmtPrice(value, currency) {
const { sym, dec } = munt(currency);
const n = Number(value);
const max = Math.max(dec, priceDecimals(Math.abs(n)));
return `${sym} ${n.toLocaleString(localeTag(), { minimumFractionDigits: dec, maximumFractionDigits: max })}`;
}
export const fmtBasePrice = value => fmtPrice(value, baseCurrency());
export const val = text => `<span class="val">${esc(text)}</span>`;
export const subline = html => (html ? `<div class="t-muted-cell t-subline">${html}</div>` : '');
const M = CONFIG.METALS;
function fmtMetal(amount, code) {
return `${fromBaseByFactor(amount, code).toLocaleString(localeTag(), { maximumFractionDigits: M.DISPLAY_DECIMALS })} ${code}`;
}
const isFiat = asset => !!asset && asset.kind === 'fiat';
const minDecimalen = (asset, dec) => isFiat(asset) ? dec : 0;
export const isBaseAsset = asset => !!asset && asset.symbol === baseCurrency();
export function fmtUnit(amount, asset) {
if (isMetal(asset)) return fmtMetal(amount, metalPrimary(asset));
const dec = asset.decimals ?? 0;
if (asset.displayUnit === 'subunit' && asset.subunit) return `${fmtInt(amount)} ${asset.subunit}`;
const main = amount / Math.pow(10, dec);
return `${main.toLocaleString(localeTag(), { minimumFractionDigits: minDecimalen(asset, dec), maximumFractionDigits: dec })} ${asset.unit}`;
}
export function fmtAmount(amount, asset) {
if (isMetal(asset)) {
const sec = metalSecondary(asset);
return { primary: fmtMetal(amount, metalPrimary(asset)), secondary: sec ? fmtMetal(amount, sec) : '' };
}
const dec = asset.decimals ?? 0;
const main = amount / Math.pow(10, dec);
const subStr = asset.subunit ? `${fmtInt(amount)} ${asset.subunit}` : '';
const mainStr = `${main.toLocaleString(localeTag(), { minimumFractionDigits: minDecimalen(asset, dec), maximumFractionDigits: dec })} ${asset.unit}`;
const useSub = asset.displayUnit === 'subunit' && asset.subunit;
return useSub ? { primary: subStr, secondary: mainStr } : { primary: mainStr, secondary: subStr };
}