raqam 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/chunk-4BC3KFIL.js +2 -0
- package/dist/chunk-4BC3KFIL.js.map +1 -0
- package/dist/chunk-7D2IPNCM.cjs +2 -0
- package/dist/chunk-7D2IPNCM.cjs.map +1 -0
- package/dist/chunk-SQWLQ664.js +2 -0
- package/dist/chunk-SQWLQ664.js.map +1 -0
- package/dist/chunk-UUHQOMSX.cjs +2 -0
- package/dist/chunk-UUHQOMSX.cjs.map +1 -0
- package/dist/core.cjs +1 -1
- package/dist/core.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/react.cjs +1 -1
- package/dist/react.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-G7ACJUKH.js +0 -2
- package/dist/chunk-G7ACJUKH.js.map +0 -1
- package/dist/chunk-GAKIDRGS.cjs +0 -2
- package/dist/chunk-GAKIDRGS.cjs.map +0 -1
- package/dist/chunk-P6AWGQPY.js +0 -2
- package/dist/chunk-P6AWGQPY.js.map +0 -1
- package/dist/chunk-YUKLLK4G.cjs +0 -2
- package/dist/chunk-YUKLLK4G.cjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/formatter.ts","../src/core/parser.ts","../src/core/cursor.ts"],"names":["IS_PRODUCTION","warnedInvalidOptions","warnInvalidFormatOptions","err","detail","FORMATTER_CACHE_MAX","formatterCache","getFormatter","locale","options","key","cached","fmt","oldest","extractLocaleInfo","styledFmt","probeFmt","parts","decimalSeparator","groupingSeparator","minusSign","zero","part","rtlLocales","resolvedLocale","isRTL","resolveEffectiveFractions","opts","allowDecimal","createFormatter","intlOptions","intlFmt","cachedLocaleInfo","getLocaleInfo","formatToParts","value","result","format","formatted","formatResult","MAX_PARSE_INPUT","isIntermediateStripped","stripped","createParser","allowNegative","isPercent","currencySymbol","p","exponentSeparator","notation","sep","normalizeDigits","stripAffordances","raw","info","s","accountingMatch","st","sci","negative","parse","input","n","isIntermediate","isAccepted","ch","countAcceptedBefore","str","cursor","normalised","count","i","getCaretBoundary","formattedValue","len","boundary","computeNewCursorPosition","oldInput","oldCursor","newFormatted","inputType","acceptedCount","before","minusBeforeCaret","newHasMinus","normNew","pos","snapToBoundary"],"mappings":"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","file":"chunk-YUKLLK4G.cjs","sourcesContent":["import type { FormatResult, LocaleInfo } from \"./types.js\";\n\n// ── Internal ──────────────────────────────────────────────────────────────────\n\n// Locally declared so the dev-only gate type-checks without @types/node.\ndeclare const process: { env?: Record<string, string | undefined> } | undefined;\nconst IS_PRODUCTION =\n typeof process !== \"undefined\" &&\n typeof process.env !== \"undefined\" &&\n process.env.NODE_ENV === \"production\";\n\nlet warnedInvalidOptions = false;\n/** Warn once (dev only) when invalid formatOptions/locale force a fallback. */\nfunction warnInvalidFormatOptions(err: unknown): void {\n if (IS_PRODUCTION || warnedInvalidOptions) return;\n warnedInvalidOptions = true;\n const detail = err instanceof Error ? err.message : String(err);\n console.warn(\n `[raqam] Invalid formatOptions/locale — falling back to a safe formatter. Check your \\`locale\\` and \\`formatOptions\\` (e.g. style:'currency' requires a \\`currency\\` code). Original error: ${detail}`\n );\n}\n\n/** Probe value that will surface decimal AND grouping parts */\nconst PROBE_VALUE = 12345.6;\n\n/**\n * LRU cache of Intl.NumberFormat instances, keyed by locale + JSON.stringify\n * (options). Bounded so apps that vary formatOptions at high cardinality —\n * per-row currencies in a large table, per-keystroke fraction-digit changes —\n * don't grow it without limit for the process lifetime.\n */\nconst FORMATTER_CACHE_MAX = 256;\nconst formatterCache = new Map<string, Intl.NumberFormat>();\n\nfunction getFormatter(\n locale: string | undefined,\n options: Intl.NumberFormatOptions | undefined\n): Intl.NumberFormat {\n const key = `${locale ?? \"\"}::${JSON.stringify(options ?? {})}`;\n const cached = formatterCache.get(key);\n if (cached) {\n // Mark most-recently-used by re-inserting at the end of the Map's order.\n formatterCache.delete(key);\n formatterCache.set(key, cached);\n return cached;\n }\n // Invalid options/locale (e.g. style:\"currency\" without `currency`,\n // maximumFractionDigits out of range, a bad BCP-47 tag) make the constructor\n // throw. Don't let that crash the render/SSR tree — fall back to a safe\n // formatter (drop the offending options, then the locale) and warn in dev.\n let fmt: Intl.NumberFormat;\n try {\n fmt = new Intl.NumberFormat(locale, options);\n } catch (err) {\n warnInvalidFormatOptions(err);\n try {\n fmt = new Intl.NumberFormat(locale);\n } catch {\n fmt = new Intl.NumberFormat();\n }\n }\n formatterCache.set(key, fmt);\n // Evict the least-recently-used entry (the first key) when over capacity.\n if (formatterCache.size > FORMATTER_CACHE_MAX) {\n const oldest = formatterCache.keys().next().value;\n if (oldest !== undefined) formatterCache.delete(oldest);\n }\n return fmt;\n}\n\n/** Extract locale meta from formatToParts — never hardcoded. */\nfunction extractLocaleInfo(\n locale: string | undefined,\n options: Intl.NumberFormatOptions | undefined\n): LocaleInfo {\n const styledFmt = getFormatter(locale, options);\n\n // Probe the separators with a NEUTRAL decimal formatter (same locale +\n // numbering system, but plain decimal style with a forced fraction). The\n // styled formatter can hide them — e.g. percent scales the probe so it has no\n // fraction (decimal separator invisible) and a positive value never yields a\n // minusSign. A negative, fractional decimal probe always surfaces all four.\n const probeFmt = getFormatter(locale, {\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n numberingSystem: options?.numberingSystem,\n });\n const parts = probeFmt.formatToParts(-PROBE_VALUE);\n\n let decimalSeparator = \".\";\n let groupingSeparator = \",\";\n let minusSign = \"-\";\n let zero = \"0\";\n\n for (const part of parts) {\n if (part.type === \"decimal\") decimalSeparator = part.value;\n if (part.type === \"group\") groupingSeparator = part.value;\n if (part.type === \"minusSign\") minusSign = part.value;\n }\n\n // Detect locale zero digit\n for (const part of probeFmt.formatToParts(0)) {\n if (part.type === \"integer\") {\n zero = part.value;\n break;\n }\n }\n\n // RTL locales: Arabic / Hebrew / Persian / Urdu / Syriac etc.\n const rtlLocales = /^(ar|he|fa|ur|syc|nqo|ug|yi)/i;\n const resolvedLocale = styledFmt.resolvedOptions().locale;\n const isRTL = rtlLocales.test(resolvedLocale);\n\n return { decimalSeparator, groupingSeparator, minusSign, zero, isRTL };\n}\n\n/**\n * Resolve the effective fraction-digit settings for a field. When decimals are\n * disallowed, fraction padding/scale is forced to 0 so currency /\n * fixedDecimalScale never pad \".00\" (which the dot-strip would then re-read,\n * exploding the value). Shared by useNumberFieldState and useNumberField so the\n * editable display and the on-commit display stay in lockstep.\n */\nexport function resolveEffectiveFractions(opts: {\n allowDecimal?: boolean;\n minimumFractionDigits?: number;\n maximumFractionDigits?: number;\n fixedDecimalScale?: boolean;\n}): {\n effMinFrac: number | undefined;\n effMaxFrac: number | undefined;\n effFixedScale: boolean | undefined;\n} {\n const allowDecimal = opts.allowDecimal ?? true;\n return {\n effMinFrac: allowDecimal ? opts.minimumFractionDigits : 0,\n effMaxFrac: allowDecimal ? opts.maximumFractionDigits : 0,\n effFixedScale: allowDecimal ? opts.fixedDecimalScale : false,\n };\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport interface FormatterOptions {\n locale?: string;\n formatOptions?: Intl.NumberFormatOptions;\n prefix?: string;\n suffix?: string;\n minimumFractionDigits?: number;\n maximumFractionDigits?: number;\n fixedDecimalScale?: boolean;\n}\n\nexport interface Formatter {\n format(value: number): string;\n formatToParts(value: number): Intl.NumberFormatPart[];\n getLocaleInfo(): LocaleInfo;\n formatResult(value: number): FormatResult;\n}\n\n/**\n * Create a formatter instance. Intl.NumberFormat is cached — safe to call\n * on every render.\n */\nexport function createFormatter(opts: FormatterOptions): Formatter {\n // Merge fraction digit overrides into formatOptions\n const intlOptions: Intl.NumberFormatOptions = { ...opts.formatOptions };\n\n if (opts.minimumFractionDigits !== undefined) {\n intlOptions.minimumFractionDigits = opts.minimumFractionDigits;\n }\n if (opts.maximumFractionDigits !== undefined) {\n intlOptions.maximumFractionDigits = opts.maximumFractionDigits;\n }\n if (opts.fixedDecimalScale && opts.maximumFractionDigits !== undefined) {\n intlOptions.minimumFractionDigits = opts.maximumFractionDigits;\n intlOptions.maximumFractionDigits = opts.maximumFractionDigits;\n }\n\n const intlFmt = getFormatter(opts.locale, intlOptions);\n // Lazy — computed once on first call\n let cachedLocaleInfo: LocaleInfo | null = null;\n\n function getLocaleInfo(): LocaleInfo {\n if (!cachedLocaleInfo) {\n cachedLocaleInfo = extractLocaleInfo(opts.locale, intlOptions);\n }\n return cachedLocaleInfo;\n }\n\n function formatToParts(value: number): Intl.NumberFormatPart[] {\n const parts = intlFmt.formatToParts(value);\n if (!opts.prefix && !opts.suffix) return parts;\n\n const result: Intl.NumberFormatPart[] = [];\n if (opts.prefix) result.push({ type: \"literal\", value: opts.prefix });\n result.push(...parts);\n if (opts.suffix) result.push({ type: \"literal\", value: opts.suffix });\n return result;\n }\n\n function format(value: number): string {\n if (!Number.isFinite(value)) return \"\";\n const formatted = intlFmt.format(value);\n return (opts.prefix ?? \"\") + formatted + (opts.suffix ?? \"\");\n }\n\n function formatResult(value: number): FormatResult {\n const parts = formatToParts(value);\n const formatted = parts.map((p) => p.value).join(\"\");\n return { formatted, parts };\n }\n\n return { format, formatToParts, getLocaleInfo, formatResult };\n}\n","import { createFormatter } from \"./formatter.js\";\nimport { normalizeDigits } from \"./normalizer.js\";\nimport type { LocaleInfo, ParseResult } from \"./types.js\";\n\n/** Upper bound on input length accepted by parse(); see the guard in parse(). */\nconst MAX_PARSE_INPUT = 256;\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Given an ASCII-normalised numeric string (`stripped` — digits, an optional\n * leading \"-\", and at most one \".\"), report whether it is a valid-but-incomplete\n * value that must NOT be reformatted while the user is still typing:\n * \"1.\" trailing decimal separator\n * \"1.0\" trailing zero after decimal\n * \"1.50\" trailing zeros after decimal\n * \".5\" leading decimal point (normalised to \"0.5\" only on blur)\n *\n * This runs on the *stripped* string, so grouping separators, prefixes,\n * suffixes and currency symbols never break the detection.\n */\nfunction isIntermediateStripped(stripped: string, allowDecimal: boolean): boolean {\n if (!allowDecimal) return false;\n if (/^-0+$/.test(stripped)) return true; // \"-0\" — user may still type \"-0.5\"\n if (/\\.$/.test(stripped)) return true; // \"1.\", \"-1.\"\n if (/\\.\\d*0$/.test(stripped)) return true; // \"1.0\", \"1.50\", \"1.230\"\n if (/^-?\\.\\d+$/.test(stripped)) return true; // \".5\", \"-.5\"\n return false;\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport interface ParserOptions {\n locale?: string;\n formatOptions?: Intl.NumberFormatOptions;\n allowNegative?: boolean;\n allowDecimal?: boolean;\n prefix?: string;\n suffix?: string;\n}\n\nexport interface Parser {\n parse(input: string): ParseResult;\n isIntermediate(input: string): boolean;\n getLocaleInfo(): LocaleInfo;\n /**\n * Strip formatting affordances (grouping separators, currency symbol, prefix/\n * suffix, percent sign…) from `input`, returning the bare numeric string\n * (ASCII digits, an optional leading \"-\", and at most one \".\"). Trailing zeros\n * the user typed are preserved. Useful for deriving a precision-preserving raw\n * value from a formatted display string.\n */\n strip(input: string): string;\n}\n\n/**\n * Create a locale-aware parser. Separator characters are extracted from\n * Intl.NumberFormat — never hardcoded.\n */\nexport function createParser(opts: ParserOptions = {}): Parser {\n const allowNegative = opts.allowNegative ?? true;\n const allowDecimal = opts.allowDecimal ?? true;\n // Percent fields hold the *fraction* (Intl multiplies by 100 on display), so\n // typed digits must be divided by 100: typing \"50\" means 50% i.e. value 0.5.\n const isPercent = opts.formatOptions?.style === \"percent\";\n\n // Re-use the formatter to get locale info\n const fmt = createFormatter({\n locale: opts.locale,\n formatOptions: opts.formatOptions,\n prefix: opts.prefix,\n suffix: opts.suffix,\n });\n\n // The literal currency symbol (e.g. \"$\", \"€\", or Arabic \"ج.م.\" which embeds\n // ASCII dots). Captured so it can be removed wholesale before separator\n // handling — otherwise its dots would be mistaken for a decimal point.\n let currencySymbol = \"\";\n if (opts.formatOptions?.style === \"currency\") {\n try {\n currencySymbol = fmt\n .formatToParts(1)\n .filter((p) => p.type === \"currency\")\n .map((p) => p.value)\n .join(\"\");\n } catch {\n currencySymbol = \"\";\n }\n }\n\n // Locale's scientific/engineering exponent separator. Most locales use \"E\"\n // (handled directly by the exponent regex), but some localize it — ar \"أس\",\n // fa \"×۱۰^\", sv \"×10^\". Captured (digit-normalized) so it can be mapped to \"e\"\n // before exponent handling; otherwise the generic strip would delete it and\n // glue the exponent digits onto the mantissa (\"1.234×10^3\" -> \"1.234103\").\n let exponentSeparator = \"\";\n const notation = opts.formatOptions?.notation;\n if (notation === \"scientific\" || notation === \"engineering\") {\n try {\n const sep = normalizeDigits(\n fmt\n .formatToParts(1234)\n .filter((p) => p.type === \"exponentSeparator\")\n .map((p) => p.value)\n .join(\"\")\n );\n // Plain ASCII \"E\"/\"e\" is already handled by the exponent regex.\n if (sep && sep !== \"e\" && sep !== \"E\") exponentSeparator = sep;\n } catch {\n exponentSeparator = \"\";\n }\n }\n\n function getLocaleInfo(): LocaleInfo {\n return fmt.getLocaleInfo();\n }\n\n function stripAffordances(raw: string): string {\n const info = getLocaleInfo();\n\n // 1. Normalise non-Latin digits to ASCII\n let s = normalizeDigits(raw);\n\n // 1a. Strip Unicode bidi control marks. RTL locales' accounting currency\n // format prepends an invisible LRM/RLM (e.g. fa-IR: \"(ریال ۱٬۲۳۴)\") before\n // the \"(\", which would defeat the index-0-anchored accounting match below and\n // silently drop the negative sign.\n s = s.replace(/[--]/g, \"\");\n\n // 1a2. Map a localized scientific exponent separator (ar \"أس\", fa \"×۱۰^\",\n // sv \"×10^\") to \"e\" so the exponent handling below recognizes it instead of\n // the generic strip mangling it (\"1.234×10^3\" -> \"1.234103\").\n if (exponentSeparator) {\n s = s.split(exponentSeparator).join(\"e\");\n }\n\n // 1b. Remove the currency symbol wholesale (before separators are touched),\n // so a symbol containing ASCII dots — e.g. Arabic \"ج.م.\" — does not leave\n // stray \".\" that the numeric validation would reject.\n if (currencySymbol) {\n s = s.split(currencySymbol).join(\"\");\n }\n\n // 2. Accounting format: \"(1,234.56)\" or \"($1,234.56)\" → negative\n // Intl.NumberFormat with currencySign:\"accounting\" wraps negatives in parens\n const accountingMatch = s.match(/^\\((.+)\\)$/);\n if (accountingMatch) {\n s = `-${accountingMatch[1]}`;\n }\n\n // 3. Strip prefix / suffix\n if (opts.prefix && s.startsWith(opts.prefix)) {\n s = s.slice(opts.prefix.length);\n }\n if (opts.suffix && s.endsWith(opts.suffix)) {\n s = s.slice(0, -opts.suffix.length);\n }\n\n // 4. Strip grouping separators (escape special chars)\n if (info.groupingSeparator) {\n s = s.split(info.groupingSeparator).join(\"\");\n }\n\n // 5. Replace locale decimal separator with ASCII \".\"\n if (info.decimalSeparator !== \".\") {\n s = s.split(info.decimalSeparator).join(\".\");\n }\n\n // 6. Replace locale minus sign with ASCII \"-\"\n if (info.minusSign !== \"-\") {\n s = s.split(info.minusSign).join(\"-\");\n }\n // Also normalise the Unicode minus U+2212 (used by fa/sv/fi/nb… and often\n // pasted) so it is never stripped as a non-numeric character below.\n if (s.includes(\"−\")) {\n s = s.split(\"−\").join(\"-\");\n }\n\n // Run the exponent checks on a copy with surrounding whitespace removed — the\n // regexes are anchored, so \" 1e3 \" would otherwise miss and get its \"E\"\n // char-stripped to \"13\". Only leading/trailing spaces are trimmed (not\n // internal ones), so \"1.5 each\" stays a space-separated word, not \"1.5each\"\n // (which would look like a malformed exponent).\n const st = s.trim();\n\n // 7a. Preserve a clean exponent (e.g. \"1.234E3\") so scientific/engineering\n // notation round-trips through parse(): the generic char-strip below would\n // delete the \"E\", gluing mantissa and exponent into a wrong value\n // (\"1.234E3\" -> \"1.2343\"). A complete exponent may be followed by trailing\n // affordances — a percent sign (\"5E1%\"), a unit (\" km\", \" meters\"), etc. —\n // which are dropped here (parse() re-applies percent scaling via isPercent).\n // The remainder is only treated as an affordance when it carries no further\n // digit AND does not start with an exponent-syntax char (e/E/+/-): a real\n // affordance never starts with one, whereas \"1e3e\", \"1e3+\", \"1e2e3\" are\n // malformed/continued exponents that must fall through to the guard / strip.\n // Returning early also skips the minus-collapse (step 8) so a negative\n // exponent's sign survives; the mantissa carries at most one leading \"-\".\n // The mantissa allows a leading \"+\" too: signDisplay \"always\"/\"exceptZero\"\n // formats positives as \"+1.234E3\"; Number() accepts the \"+\" and the regular\n // path already tolerates a leading \"+\" (the strip removes it).\n const sci = st.match(/^([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+))[eE]([+-]?\\d+)(.*)$/);\n if (sci && !/\\d/.test(sci[3]) && !/^[eE+\\-]/.test(sci[3])) {\n return `${sci[1]}e${sci[2]}`;\n }\n\n // 7b. MALFORMED exponent: a valid mantissa immediately followed by \"e\"/\"E\"\n // that is not a complete scientific number (\"1e\", \"1e+\", \"1efoo\", \"1EUR\").\n // The strip below would silently delete the \"e\" and yield a wrong value (1),\n // so return the string with the marker intact — parse()'s exponent branch\n // then rejects it instead of mis-parsing.\n if (/^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)[eE]/.test(st)) {\n return st;\n }\n\n // 7. Strip currency symbol, percent sign, spaces that Intl might prepend/append\n // Strip any remaining non-numeric chars except digits, \".\", \"-\"\n // (handles currency prefixes/suffixes from Intl)\n s = s.replace(/[^\\d.\\-]/g, \"\").trim();\n\n // 8. Collapse minus signs to a single leading one, so stray minuses typed in\n // the middle (\"1-23\") or doubled (\"--5\") never invalidate the whole value.\n if (s.includes(\"-\")) {\n const negative = s.startsWith(\"-\");\n s = s.replace(/-/g, \"\");\n if (negative) s = `-${s}`;\n }\n\n return s;\n }\n\n function parse(input: string): ParseResult {\n if (!input || input.trim() === \"\") {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n // Bound the input length before any regex/scan work. Real numeric input is\n // short; an unbounded attacker-controlled string (e.g. a crafted paste)\n // could otherwise drive worst-case backtracking. 256 chars is far beyond any\n // legitimate number (≈250-digit precision) yet keeps all scans trivial.\n if (input.length > MAX_PARSE_INPUT) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n // Strip *first*, so intermediate detection never trips over grouping\n // separators, prefixes, suffixes or currency symbols.\n const stripped = stripAffordances(input);\n\n if (stripped === \"\") {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n // Lone minus — intermediate only if negatives are allowed.\n if (stripped === \"-\") {\n return { value: null, isValid: false, isIntermediate: allowNegative };\n }\n\n // Lone decimal point (\".\" or \"-.\") — intermediate only if decimals allowed.\n if (stripped === \".\" || stripped === \"-.\") {\n return {\n value: null,\n isValid: false,\n isIntermediate: allowDecimal && (stripped === \".\" || allowNegative),\n };\n }\n\n if (!allowNegative && stripped.startsWith(\"-\")) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n if (!allowDecimal && stripped.includes(\".\")) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n // Scientific / exponent notation, preserved by stripAffordances (step 7a).\n // Handle it explicitly: Number() reads it, but the plain validation regex\n // below would reject the \"e\". A malformed exponent is rejected rather than\n // silently corrupted.\n if (/[eE]/.test(stripped)) {\n if (!/^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)[eE][+-]?\\d+$/.test(stripped)) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n let n = Number(stripped);\n if (!Number.isFinite(n)) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n // A fractional exponent value (e.g. \"5e-1\" = 0.5) has no literal \".\" to\n // trip the allowDecimal guard above, so reject it here for parity with the\n // plain-decimal path when decimals are disallowed.\n if (!allowDecimal && !Number.isInteger(n)) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n if (Object.is(n, -0)) n = 0;\n if (isPercent && n !== 0) n = Number((n / 100).toPrecision(15));\n return { value: n, isValid: true, isIntermediate: false };\n }\n\n // Accept integers and decimals with the dot either trailing (\"1.\") or\n // leading (\".5\") so partially-typed values still yield a numeric value. The\n // pattern is written without an ambiguous alternation so it matches in linear\n // time (the empty / lone-\"-\" / lone-\".\" cases are handled above).\n if (!/^-?\\d*(?:\\.\\d*)?$/.test(stripped)) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n let n = Number.parseFloat(stripped);\n if (!Number.isFinite(n)) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n // Normalise negative zero so consumers never receive -0 from onChange.\n if (Object.is(n, -0)) n = 0;\n // Percent: divide by 100, snapping to canonical precision so the IEEE-754\n // division artifact (12.99/100 = 0.12990000000000002) never surfaces.\n if (isPercent && n !== 0) n = Number((n / 100).toPrecision(15));\n\n // A trailing-zero / trailing-dot / leading-dot value is still a real number\n // (so it is never wiped on blur) but must not be reformatted mid-typing.\n return {\n value: n,\n isValid: true,\n isIntermediate: isIntermediateStripped(stripped, allowDecimal),\n };\n }\n\n function isIntermediate(input: string): boolean {\n return parse(input).isIntermediate;\n }\n\n return { parse, isIntermediate, getLocaleInfo, strip: stripAffordances };\n}\n","import { normalizeDigits } from \"./normalizer.js\";\nimport type { CaretBoundary, LocaleInfo } from \"./types.js\";\n\n// ── Accepted-character helpers ────────────────────────────────────────────────\n\n/**\n * Returns true if `ch` is an \"accepted\" character — one that the user typed\n * intentionally and that contributes to the numeric value:\n * - ASCII digit 0-9\n * - The locale decimal separator\n * - The locale minus sign\n */\nfunction isAccepted(ch: string, info: LocaleInfo): boolean {\n if (ch >= \"0\" && ch <= \"9\") return true;\n if (ch === info.decimalSeparator) return true;\n if (ch === info.minusSign || ch === \"-\") return true;\n // ASCII \".\" stands in for the decimal point in locales whose separator is\n // non-ASCII (ar/fa: ٫) — but not when \".\" is the grouping separator (de-DE).\n if (ch === \".\" && info.decimalSeparator !== \".\" && info.groupingSeparator !== \".\") return true;\n return false;\n}\n\n/**\n * Count how many \"accepted\" characters appear before position `cursor`\n * in `str` (after normalising non-Latin digits to ASCII).\n */\nfunction countAcceptedBefore(str: string, cursor: number, info: LocaleInfo): number {\n const normalised = normalizeDigits(str);\n let count = 0;\n for (let i = 0; i < cursor && i < normalised.length; i++) {\n if (isAccepted(normalised[i]!, info)) count++;\n }\n return count;\n}\n\n// ── Caret boundary ────────────────────────────────────────────────────────────\n\n/**\n * Build a boolean array of length `formattedValue.length + 1`.\n * `true` → cursor may rest at this position.\n * `false` → cursor must snap away (sits inside a formatting-only character\n * such as a grouping separator or a currency prefix).\n *\n * Rules:\n * - Start and end positions are always valid.\n * - A position immediately AFTER a grouping separator is invalid (the\n * cursor would look like it's between two digits but moving left would\n * skip the comma).\n * - A position immediately BEFORE a grouping separator is valid.\n */\nexport function getCaretBoundary(formattedValue: string, info: LocaleInfo): CaretBoundary {\n const len = formattedValue.length;\n const boundary: CaretBoundary = new Array(len + 1).fill(true) as boolean[];\n\n for (let i = 0; i < len; i++) {\n const ch = formattedValue[i]!;\n if (ch === info.groupingSeparator) {\n // Position immediately after the grouping separator is invalid\n boundary[i + 1] = false;\n }\n }\n\n // First and last are always valid\n boundary[0] = true;\n boundary[len] = true;\n\n return boundary;\n}\n\n// ── Cursor computation ────────────────────────────────────────────────────────\n\n/**\n * Compute the new cursor position in `newFormatted` that corresponds\n * semantically to `oldCursor` in `oldInput`.\n *\n * Algorithm (3 stages from the spec):\n *\n * 1. Count accepted characters before `oldCursor` in `oldInput`.\n * 2. Adjust for backspace-over-separator edge case.\n * 3. Walk `newFormatted` to find the position where the same count of\n * accepted chars precede it; snap to the nearest valid boundary.\n *\n * @param oldInput The raw string the user just typed into (pre-format)\n * @param oldCursor selectionStart captured from the native event\n * @param newFormatted The formatted string we're about to display\n * @param info Locale separators\n * @param inputType e.nativeEvent.inputType (optional — for backspace detection)\n */\nexport function computeNewCursorPosition(\n oldInput: string,\n oldCursor: number,\n newFormatted: string,\n info: LocaleInfo,\n inputType?: string\n): number {\n const normalised = normalizeDigits(oldInput);\n\n // Stage 1: count accepted chars before cursor in old input\n let acceptedCount = countAcceptedBefore(normalised, oldCursor, info);\n\n // Stage 1b: accounting/parens negatives drop the minus sign. If a minus was\n // counted before the caret but the reformatted output represents the sign\n // without one (e.g. \"($1.00)\"), the accepted-char counts are off by one —\n // correct for it so integer digits don't land inside the fraction.\n const before = oldInput.slice(0, oldCursor);\n const minusBeforeCaret = before.includes(\"-\") || before.includes(info.minusSign);\n const newHasMinus = newFormatted.includes(\"-\") || newFormatted.includes(info.minusSign);\n if (minusBeforeCaret && !newHasMinus) {\n acceptedCount = Math.max(0, acceptedCount - 1);\n }\n\n // Stage 2: backspace on grouping separator — also delete the preceding digit\n if (\n inputType === \"deleteContentBackward\" &&\n oldCursor > 0 &&\n oldInput[oldCursor - 1] === info.groupingSeparator\n ) {\n // The separator was deleted but we want to remove the preceding digit too\n acceptedCount = Math.max(0, acceptedCount - 1);\n }\n\n // Stage 3: build boundary and walk new formatted string\n const boundary = getCaretBoundary(newFormatted, info);\n const normNew = normalizeDigits(newFormatted);\n let count = 0;\n let pos = 0;\n\n for (let i = 0; i < normNew.length; i++) {\n if (count === acceptedCount) {\n pos = i;\n break;\n }\n if (isAccepted(normNew[i]!, info)) count++;\n pos = i + 1;\n }\n\n // If we ran through the entire string without reaching the target count,\n // place the cursor at the end. Note: acceptedCount === 0 means we correctly\n // want pos = 0 (set in the loop), so we must NOT override that case.\n if (acceptedCount > 0 && count < acceptedCount) {\n pos = newFormatted.length;\n }\n\n // Snap to nearest valid boundary\n pos = snapToBoundary(pos, boundary);\n\n return pos;\n}\n\n/**\n * If `pos` is at a false (invalid) boundary position, find the nearest\n * true position. Prefers moving forward; falls back to backward.\n */\nfunction snapToBoundary(pos: number, boundary: CaretBoundary): number {\n if (boundary[pos]) return pos;\n\n // Try forward first\n for (let i = pos + 1; i < boundary.length; i++) {\n if (boundary[i]) return i;\n }\n // Fall back to backward\n for (let i = pos - 1; i >= 0; i--) {\n if (boundary[i]) return i;\n }\n return 0;\n}\n"]}
|