mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-12 23:31:02 +00:00
fc648bc7e4
- 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.
44 lines
1.8 KiB
TypeScript
44 lines
1.8 KiB
TypeScript
/**
|
||
* 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}`;
|
||
},
|
||
};
|
||
}
|