/** * Editing engine for CurrencyInput — the half of the masked field that has no * DOM in it: given the text the field showed, the text the browser left behind * and where it put the caret, it returns the amount, the text to show, and * where the caret belongs. * * The field is a **fixed-scale mask**, not free text. Its display always carries * exactly `precision` fraction digits, which is what makes the two halves behave * differently: * * - the **integer part shifts** — deleting a digit closes the gap, so `2.233` * minus a digit is `233`; * - a **fraction slot is overwritten** — deleting a cent digit zeroes its slot * (`1,45` → `1,40` → `1,00`), so the separator never travels and the caret * stays where the user was aiming. * * Everything else follows from that: the separators are the mask's own * punctuation and are never content, and the caret is carried through * reformatting as a digit slot rather than a character offset — grouping * separators appear and disappear as the number grows, so an offset would drift * by one on every thousands boundary. * * @internal Engine functions are an internal API of the CurrencyInput * component. They are exported for testing, not as part of the public surface; * the supported entry point is the `CurrencyInput` component itself. */ /** The locale-driven shape of the field, everything the engine needs to know. */ export interface CurrencyMask { /** Fraction digits the value carries: 2 for EUR/USD, 0 for JPY, 3 for BHD. */ precision: number; /** The locale's decimal separator — the only character that opens the fraction. */ decimal: string; /** Renders a minor-unit amount the way the field shows it, grouping included. */ format: (minor: number) => string; /** * The locale's ten digits in ascending order, for the numbering systems that * are not written `0`–`9` (`ar`, `fa`, `bn`, `my`, …). ASCII digits are read * as well either way, so a Latin keyboard keeps working under those locales. * @default '0123456789' */ digits?: string; } /** Which keypress produced a deletion — it decides what a separator takes with it. */ export type DeletionKind = /** Backspace: reaches for the character to its left. */ 'backward' /** Delete: reaches for the character to its right. */ | 'forward' /** Cut, drag-out: the removed run is the selection, and nothing beside it. */ | 'exact'; /** One edit as the browser left it behind. */ export interface CurrencyEdit { /** The text the field showed before the browser touched it. */ previous: string; /** The text the browser left in the field. */ next: string; /** `selectionStart` after the edit. */ caret: number; /** Which keypress produced it. Only a deletion cares. @default 'backward' */ deletion?: DeletionKind; } export interface CurrencyEditResult { /** The amount in minor units, `null` for an empty field. */ value: number | null; /** The text the field must show. */ display: string; /** Where the caret belongs in `display`. */ caret: number; } /** Normalise a consumer-supplied precision to a usable digit count. */ export declare function fractionDigits(precision: number): number; /** * Apply one browser edit to the mask. It covers every operation a text field * performs — typing, backspace, forward delete, replacing a selection, cut, * paste, drop, autofill — without the engine having to know which one it was, * which is what lets the component stay on a single `input` handler. */ export declare function applyEdit(mask: CurrencyMask, edit: CurrencyEdit): CurrencyEditResult;