Spreadsheet-UX op een echte relationele engine
Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.
/**
* The currency a workspace writes money in.
*
* One setting for the whole workspace rather than one per column: a business
* keeps its books in a currency, not per field, and a workspace where the
* invoice total is in euros and the line price in dollars is a bug however it
* got that way. Columns stay `currency`-typed and simply read in whatever the
* workspace is settled on.
*
* The code is stored, never the symbol — `EUR` outlives whatever glyph a
* browser decides to render it with, and it is what `Intl` wants anyway.
*/
export const DEFAULT_CURRENCY = 'EUR';
export interface CurrencyOption {
code: string;
name: string;
}
/**
* The list the settings dialog offers. Not exhaustive, and deliberately not:
* anything ISO 4217 accepts is storable, so a workspace that needs a currency
* missing here is never stuck — it is just not one click away.
*/
export const CURRENCIES: CurrencyOption[] = [
{ code: 'EUR', name: 'Euro' },
{ code: 'USD', name: 'US dollar' },
{ code: 'GBP', name: 'Pound sterling' },
{ code: 'CHF', name: 'Swiss franc' },
{ code: 'SEK', name: 'Swedish krona' },
{ code: 'NOK', name: 'Norwegian krone' },
{ code: 'DKK', name: 'Danish krone' },
{ code: 'PLN', name: 'Polish złoty' },
{ code: 'CZK', name: 'Czech koruna' },
{ code: 'CAD', name: 'Canadian dollar' },
{ code: 'AUD', name: 'Australian dollar' },
{ code: 'JPY', name: 'Japanese yen' },
{ code: 'INR', name: 'Indian rupee' },
{ code: 'BRL', name: 'Brazilian real' },
];
/** Whether `value` is shaped like an ISO 4217 code, which is all `Intl` asks. */
export function isCurrencyCode(value: string): boolean {
return /^[A-Za-z]{3}$/.test(value);
}
const symbols = new Map<string, string>();
/**
* The short sign for a currency — `€`, `$`, `kr` — for places too small to
* hold a formatted amount, like the type marker on a column header.
*
* Falls back to the code itself, which is what `Intl` does for currencies with
* no symbol in the first place.
*/
export function currencySymbol(code: string): string {
const cached = symbols.get(code);
if (cached !== undefined) return cached;
let symbol = code;
try {
const parts = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: code,
currencyDisplay: 'narrowSymbol',
}).formatToParts(0);
symbol = parts.find((p) => p.type === 'currency')?.value ?? code;
} catch {
// An unusable code renders as itself rather than taking the page down.
}
symbols.set(code, symbol);
return symbol;
}