Files
TREK/client/native-shell/amount.ts
T
Maurice fc648bc7e4 fix: green checks after the first-screen redesign and the safe-area floor
- native-shell/amount.ts: the no-break spaces in the price pattern were
  written out as characters; \s already matches them.
- widgets.ts: the Live Activity's lead time stays inside the module.
- i18n: the count strings of the new plan tiles replace the removed ones
  on NOT_PLURAL, and the address placeholders of the non-Latin locales
  are their own instead of English copies.
- mobile.css: the side safe-area margins of the floating bars use
  margin-inline, swapped for right to left, instead of physical sides.
2026-11-12 14:36:13 +01:00

44 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* The amount inside a price as the store writes it ("4,90 €", "$4.99",
* "CHF 12.90", "1.290 ₽"), and a way to write another amount the same way: the
* same currency, the same decimal mark, the same digit grouping. The plan picker
* counts a price up to its new value with it, and turns a yearly price into what
* it makes a month, without knowing the store's locale.
*/
interface Amount {
/** The amount as a number, in the price's own unit (4.9 for "4,90 €"). */
value: number;
/** The price written for another amount, in the same format. */
render(value: number): string;
}
// Digits with the marks stores put between them: . , any space (\s takes the
// no-break ones too) and the apostrophes Swiss formats group thousands with.
const NUMBER = /\d[\d.,\s'’]*\d|\d/;
export function amountIn(label: string): Amount | null {
const match = NUMBER.exec(label);
if (!match) return null;
const raw = match[0];
// The decimal mark is the last . or , followed by one or two digits to the end.
const decimal = /([.,])(\d{1,2})$/.exec(raw);
const decimals = decimal ? decimal[2].length : 0;
const mark = decimal ? decimal[1] : '';
const whole = decimal ? raw.slice(0, raw.length - decimal[0].length) : raw;
const group = /[.,\s'’]/.exec(whole)?.[0] ?? '';
const digits = whole.replace(/\D/g, '') + (decimal ? `.${decimal[2]}` : '');
const value = Number(digits);
if (!Number.isFinite(value)) return null;
const before = label.slice(0, match.index);
const after = label.slice(match.index + raw.length);
return {
value,
render(next: number) {
const fixed = Math.max(0, next).toFixed(decimals);
const [int, frac] = fixed.split('.');
const grouped = group ? int.replace(/\B(?=(\d{3})+(?!\d))/g, group) : int;
return `${before}${grouped}${frac !== undefined ? mark + frac : ''}${after}`;
},
};
}