147 lines
6.1 KiB
JavaScript
147 lines
6.1 KiB
JavaScript
// 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 };
|
||
|
|
}
|