Every currency has its own number of decimals
The same amount in a dirham, a Kuwaiti dinar, a Pakistani rupee and a yen — and what each one is really worth in fils, paisa and none.
<div class="demo demo--wide">
<div class="demo-row">
<div class="demo-seg" id="currency" role="group" aria-label="Currency">
<button type="button" data-currency="AED" aria-pressed="true">AED</button>
<button type="button" data-currency="KWD">KWD</button>
<button type="button" data-currency="PKR">PKR</button>
<button type="button" data-currency="JPY">JPY</button>
</div>
<div class="demo-seg" id="locale" role="group" aria-label="Language">
<button type="button" data-locale="en-AE" aria-pressed="true">English</button>
<button type="button" data-locale="ar-EG">العربية</button>
<button type="button" data-locale="de-DE">Deutsch</button>
<button type="button" data-locale="en-IN">हिन्दी</button>
</div>
</div>
<form id="form" action="#" onsubmit="return false">
<input class="demo-input" id="total" name="total" placeholder="0.00">
</form>
<dl class="demo-kv" id="out"></dl>
<p class="demo-hint">Type <code>1234.56</code> into each. The dinar keeps three decimals; the yen
refuses the point rather than making your amount a hundred times bigger. <strong>PKR is the one
to look at</strong> — <code>Intl</code> says the rupee has no decimals, so a field built on it
would silently drop the paisa.</p>
</div>
import { createAmountInput, decimalsFor } from 'https://devix.pk/cdn/oss/amount-input@1.0.0/index.js';
const field = document.getElementById('total');
const out = document.getElementById('out');
let currency = 'AED';
let locale = 'en-AE';
// Declared before build() runs, because build() reads it to carry the amount
// across a rebuild.
let widget;
widget = build();
function build() {
const existing = widget?.getValue();
widget?.destroy();
const next = createAmountInput(field, {
currency,
locale,
showCurrency: true,
value: existing,
onChange: () => show(),
onCommit: () => show(),
});
show(next);
return next;
}
function show(instance = widget) {
const value = instance.read();
const posted = Object.fromEntries(new FormData(document.getElementById('form')).entries());
// What Intl would have told us, next to what the standard says.
const fromIntl = new Intl.NumberFormat('en', { style: 'currency', currency })
.resolvedOptions().maximumFractionDigits;
out.innerHTML = `
<div><dt>you would store</dt><dd>${value.decimal || '—'}</dd></div>
<div><dt>whole minor units</dt><dd>${value.minor || '—'}</dd></div>
<div><dt>decimals, from ISO 4217</dt><dd>${decimalsFor(currency)}</dd></div>
<div><dt>decimals, if we trusted Intl</dt><dd>${fromIntl}${fromIntl === decimalsFor(currency) ? '' : ' ← wrong'}</dd></div>
<div><dt>a plain form would post</dt><dd>${posted.total || '—'}</dd></div>
<div><dt>in the field</dt><dd>${value.text || '—'}</dd></div>`;
}
const segment = (id, key, apply) => {
const group = document.getElementById(id);
group.addEventListener('click', (event) => {
const button = event.target.closest('button');
if (!button) return;
for (const other of group.querySelectorAll('button')) other.setAttribute('aria-pressed', String(other === button));
apply(button.dataset[key]);
widget = build();
field.focus();
});
};
segment('currency', 'currency', (value) => { currency = value; });
segment('locale', 'locale', (value) => { locale = value; });
widget.setValue('1234.56');
show();
@import url('https://devix.pk/cdn/oss/amount-input@1.0.0/amount-input.min.css');
#total { width: 100%; font-size: 18px; }
.demo-kv dd { font-variant-numeric: tabular-nums; }