/** * Advanced cursor position calculation for formatted numeric inputs. * Handles cursor preservation during formatting changes, insertion, and deletion operations. * * ## Algorithm Overview * * The cursor position calculation uses a "meaningful digit" approach: * 1. Count the number of actual digits (0-9) before the cursor, ignoring separators * 2. After formatting, find the position that has the same number of digits before it * 3. Apply adjustments for separator boundaries and special cases * * ## Processing Flow * * ``` * calculateCursorPositionAfterFormatting() * ├── Guard clauses (empty values, out of bounds) * ├── Compact notation detection (1k → 1000) * ├── Character mapping approach (optional, for complex transformations) * └── Route to handler based on operation type: * ├── handleDeletion() - for backspace/delete operations * │ ├── handleSeparatorDeletion() - cursor was on separator * │ ├── Calculate target digit count * │ └── Find new position + fine-tune for separators * └── handleInsertion() - for typing/paste operations * ├── Handle cursor at end * └── Find position maintaining digit-relative position * ``` * * ## Key Concepts * * - **Meaningful digits**: Numeric characters (0-9) that represent actual value * - **Separators**: Thousand separators that are formatting-only (not part of value) * - **Digit index**: The nth digit from the start (ignoring separators) * - **ChangeRange**: Info about what was typed/deleted to distinguish Delete vs Backspace * * ## Edge Cases Handled * * - Cursor at start/end of input * - Cursor on separator during deletion * - Delete key vs Backspace key (different cursor behavior) * - Compact notation expansion (1k → 1000) * - Integer/decimal part transitions * - Boundary constraints (prefix/suffix) * * @module cursor-position */ import type { ChangeRange } from './constants'; import { ThousandStyle } from '@/types'; /** * Type for character equivalence checking. * Returns true if two characters should be considered equivalent for cursor mapping. */ type IsCharacterEquivalent = (char1: string, char2: string, context: { oldValue: string; newValue: string; typedRange?: ChangeRange; oldIndex: number; newIndex: number; }) => boolean; /** * Options for cursor position calculation. */ export interface CursorPositionOptions { thousandSeparator?: string; decimalSeparator?: string; isCharacterEquivalent?: IsCharacterEquivalent; boundary?: boolean[]; } /** * Calculates the new cursor position after formatting is applied. * Uses digit index mapping to preserve cursor position relative to actual digits, * handling insertion and deletion differently. * * Supports character equivalence for cases where characters are transformed * (e.g., allowed decimal separators normalized to canonical separator). * * @param oldFormattedValue - The formatted value before the change * @param newFormattedValue - The formatted value after formatting * @param oldCursorPosition - The cursor position in the old formatted value * @param separator - The thousand separator character used in formatting * Will be removed in a future major version. Pass any value; it is ignored. * @param changeRange - Optional change range info to distinguish Delete vs Backspace * @param decimalSeparator - The decimal separator character (default: '.') * @param options - Additional options for cursor calculation * @returns The new cursor position in the new formatted value * * @example * // Typing that adds a comma * calculateCursorPositionAfterFormatting("100", "1,000", 3, ",") * // Returns: 5 (cursor after last zero) */ export declare function calculateCursorPositionAfterFormatting(oldFormattedValue: string, newFormattedValue: string, oldCursorPosition: number, separator: string, _groupStyle: ThousandStyle, changeRange?: ChangeRange, decimalSeparator?: string, options?: CursorPositionOptions): number; export {};