number-flow-react-native 0.1.0

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.
Files changed (245) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/lib/module/core/constants.js +21 -0
  4. package/lib/module/core/constants.js.map +1 -0
  5. package/lib/module/core/intlHelpers.js +310 -0
  6. package/lib/module/core/intlHelpers.js.map +1 -0
  7. package/lib/module/core/layout.js +71 -0
  8. package/lib/module/core/layout.js.map +1 -0
  9. package/lib/module/core/mask.js +50 -0
  10. package/lib/module/core/mask.js.map +1 -0
  11. package/lib/module/core/numerals/detection.js +105 -0
  12. package/lib/module/core/numerals/detection.js.map +1 -0
  13. package/lib/module/core/numerals/digits.js +128 -0
  14. package/lib/module/core/numerals/digits.js.map +1 -0
  15. package/lib/module/core/numerals/index.js +5 -0
  16. package/lib/module/core/numerals/index.js.map +1 -0
  17. package/lib/module/core/numerals/tables.js +114 -0
  18. package/lib/module/core/numerals/tables.js.map +1 -0
  19. package/lib/module/core/superscript.js +31 -0
  20. package/lib/module/core/superscript.js.map +1 -0
  21. package/lib/module/core/timeLayout.js +98 -0
  22. package/lib/module/core/timeLayout.js.map +1 -0
  23. package/lib/module/core/timeTypes.js +4 -0
  24. package/lib/module/core/timeTypes.js.map +1 -0
  25. package/lib/module/core/timing.js +45 -0
  26. package/lib/module/core/timing.js.map +1 -0
  27. package/lib/module/core/types.js +58 -0
  28. package/lib/module/core/types.js.map +1 -0
  29. package/lib/module/core/useAccessibilityAnnouncement.js +27 -0
  30. package/lib/module/core/useAccessibilityAnnouncement.js.map +1 -0
  31. package/lib/module/core/useAnimatedX.js +25 -0
  32. package/lib/module/core/useAnimatedX.js.map +1 -0
  33. package/lib/module/core/useAnimationLifecycle.js +37 -0
  34. package/lib/module/core/useAnimationLifecycle.js.map +1 -0
  35. package/lib/module/core/useCanAnimate.js +22 -0
  36. package/lib/module/core/useCanAnimate.js.map +1 -0
  37. package/lib/module/core/useContinuousSpin.js +89 -0
  38. package/lib/module/core/useContinuousSpin.js.map +1 -0
  39. package/lib/module/core/useDebouncedWidths.js +74 -0
  40. package/lib/module/core/useDebouncedWidths.js.map +1 -0
  41. package/lib/module/core/useDigitAnimation.js +138 -0
  42. package/lib/module/core/useDigitAnimation.js.map +1 -0
  43. package/lib/module/core/useFlowPipeline.js +85 -0
  44. package/lib/module/core/useFlowPipeline.js.map +1 -0
  45. package/lib/module/core/useFormattedValue.js +28 -0
  46. package/lib/module/core/useFormattedValue.js.map +1 -0
  47. package/lib/module/core/useLayoutDiff.js +59 -0
  48. package/lib/module/core/useLayoutDiff.js.map +1 -0
  49. package/lib/module/core/useNumberFormatting.js +158 -0
  50. package/lib/module/core/useNumberFormatting.js.map +1 -0
  51. package/lib/module/core/useSlotOpacity.js +53 -0
  52. package/lib/module/core/useSlotOpacity.js.map +1 -0
  53. package/lib/module/core/useTimeFormatting.js +74 -0
  54. package/lib/module/core/useTimeFormatting.js.map +1 -0
  55. package/lib/module/core/useTimingResolution.js +21 -0
  56. package/lib/module/core/useTimingResolution.js.map +1 -0
  57. package/lib/module/core/useWorkletFormatting.js +49 -0
  58. package/lib/module/core/useWorkletFormatting.js.map +1 -0
  59. package/lib/module/core/utils.js +132 -0
  60. package/lib/module/core/utils.js.map +1 -0
  61. package/lib/module/core/warnings.js +10 -0
  62. package/lib/module/core/warnings.js.map +1 -0
  63. package/lib/module/index.js +7 -0
  64. package/lib/module/index.js.map +1 -0
  65. package/lib/module/native/DigitSlot.js +163 -0
  66. package/lib/module/native/DigitSlot.js.map +1 -0
  67. package/lib/module/native/NumberFlow.js +244 -0
  68. package/lib/module/native/NumberFlow.js.map +1 -0
  69. package/lib/module/native/SymbolSlot.js +52 -0
  70. package/lib/module/native/SymbolSlot.js.map +1 -0
  71. package/lib/module/native/TimeFlow.js +270 -0
  72. package/lib/module/native/TimeFlow.js.map +1 -0
  73. package/lib/module/native/index.js +5 -0
  74. package/lib/module/native/index.js.map +1 -0
  75. package/lib/module/native/renderSlots.js +108 -0
  76. package/lib/module/native/renderSlots.js.map +1 -0
  77. package/lib/module/native/types.js +4 -0
  78. package/lib/module/native/types.js.map +1 -0
  79. package/lib/module/native/useMeasuredGlyphMetrics.js +156 -0
  80. package/lib/module/native/useMeasuredGlyphMetrics.js.map +1 -0
  81. package/lib/module/package.json +1 -0
  82. package/lib/module/skia/DigitSlot.js +171 -0
  83. package/lib/module/skia/DigitSlot.js.map +1 -0
  84. package/lib/module/skia/SkiaNumberFlow.js +430 -0
  85. package/lib/module/skia/SkiaNumberFlow.js.map +1 -0
  86. package/lib/module/skia/SkiaTimeFlow.js +226 -0
  87. package/lib/module/skia/SkiaTimeFlow.js.map +1 -0
  88. package/lib/module/skia/SymbolSlot.js +92 -0
  89. package/lib/module/skia/SymbolSlot.js.map +1 -0
  90. package/lib/module/skia/index.js +6 -0
  91. package/lib/module/skia/index.js.map +1 -0
  92. package/lib/module/skia/renderSlots.js +131 -0
  93. package/lib/module/skia/renderSlots.js.map +1 -0
  94. package/lib/module/skia/useGlyphMetrics.js +72 -0
  95. package/lib/module/skia/useGlyphMetrics.js.map +1 -0
  96. package/lib/module/skia/useScrubbing.js +165 -0
  97. package/lib/module/skia/useScrubbing.js.map +1 -0
  98. package/lib/module/skia/useSkiaFont.js +23 -0
  99. package/lib/module/skia/useSkiaFont.js.map +1 -0
  100. package/lib/typescript/core/constants.d.ts +15 -0
  101. package/lib/typescript/core/constants.d.ts.map +1 -0
  102. package/lib/typescript/core/intlHelpers.d.ts +10 -0
  103. package/lib/typescript/core/intlHelpers.d.ts.map +1 -0
  104. package/lib/typescript/core/layout.d.ts +22 -0
  105. package/lib/typescript/core/layout.d.ts.map +1 -0
  106. package/lib/typescript/core/mask.d.ts +18 -0
  107. package/lib/typescript/core/mask.d.ts.map +1 -0
  108. package/lib/typescript/core/numerals/detection.d.ts +17 -0
  109. package/lib/typescript/core/numerals/detection.d.ts.map +1 -0
  110. package/lib/typescript/core/numerals/digits.d.ts +43 -0
  111. package/lib/typescript/core/numerals/digits.d.ts.map +1 -0
  112. package/lib/typescript/core/numerals/index.d.ts +3 -0
  113. package/lib/typescript/core/numerals/index.d.ts.map +1 -0
  114. package/lib/typescript/core/numerals/tables.d.ts +32 -0
  115. package/lib/typescript/core/numerals/tables.d.ts.map +1 -0
  116. package/lib/typescript/core/superscript.d.ts +16 -0
  117. package/lib/typescript/core/superscript.d.ts.map +1 -0
  118. package/lib/typescript/core/timeLayout.d.ts +19 -0
  119. package/lib/typescript/core/timeLayout.d.ts.map +1 -0
  120. package/lib/typescript/core/timeTypes.d.ts +80 -0
  121. package/lib/typescript/core/timeTypes.d.ts.map +1 -0
  122. package/lib/typescript/core/timing.d.ts +6 -0
  123. package/lib/typescript/core/timing.d.ts.map +1 -0
  124. package/lib/typescript/core/types.d.ts +165 -0
  125. package/lib/typescript/core/types.d.ts.map +1 -0
  126. package/lib/typescript/core/useAccessibilityAnnouncement.d.ts +10 -0
  127. package/lib/typescript/core/useAccessibilityAnnouncement.d.ts.map +1 -0
  128. package/lib/typescript/core/useAnimatedX.d.ts +9 -0
  129. package/lib/typescript/core/useAnimatedX.d.ts.map +1 -0
  130. package/lib/typescript/core/useAnimationLifecycle.d.ts +14 -0
  131. package/lib/typescript/core/useAnimationLifecycle.d.ts.map +1 -0
  132. package/lib/typescript/core/useCanAnimate.d.ts +14 -0
  133. package/lib/typescript/core/useCanAnimate.d.ts.map +1 -0
  134. package/lib/typescript/core/useContinuousSpin.d.ts +23 -0
  135. package/lib/typescript/core/useContinuousSpin.d.ts.map +1 -0
  136. package/lib/typescript/core/useDebouncedWidths.d.ts +17 -0
  137. package/lib/typescript/core/useDebouncedWidths.d.ts.map +1 -0
  138. package/lib/typescript/core/useDigitAnimation.d.ts +38 -0
  139. package/lib/typescript/core/useDigitAnimation.d.ts.map +1 -0
  140. package/lib/typescript/core/useFlowPipeline.d.ts +46 -0
  141. package/lib/typescript/core/useFlowPipeline.d.ts.map +1 -0
  142. package/lib/typescript/core/useFormattedValue.d.ts +14 -0
  143. package/lib/typescript/core/useFormattedValue.d.ts.map +1 -0
  144. package/lib/typescript/core/useLayoutDiff.d.ts +18 -0
  145. package/lib/typescript/core/useLayoutDiff.d.ts.map +1 -0
  146. package/lib/typescript/core/useNumberFormatting.d.ts +18 -0
  147. package/lib/typescript/core/useNumberFormatting.d.ts.map +1 -0
  148. package/lib/typescript/core/useSlotOpacity.d.ts +18 -0
  149. package/lib/typescript/core/useSlotOpacity.d.ts.map +1 -0
  150. package/lib/typescript/core/useTimeFormatting.d.ts +22 -0
  151. package/lib/typescript/core/useTimeFormatting.d.ts.map +1 -0
  152. package/lib/typescript/core/useTimingResolution.d.ts +13 -0
  153. package/lib/typescript/core/useTimingResolution.d.ts.map +1 -0
  154. package/lib/typescript/core/useWorkletFormatting.d.ts +14 -0
  155. package/lib/typescript/core/useWorkletFormatting.d.ts.map +1 -0
  156. package/lib/typescript/core/utils.d.ts +44 -0
  157. package/lib/typescript/core/utils.d.ts.map +1 -0
  158. package/lib/typescript/core/warnings.d.ts +2 -0
  159. package/lib/typescript/core/warnings.d.ts.map +1 -0
  160. package/lib/typescript/index.d.ts +8 -0
  161. package/lib/typescript/index.d.ts.map +1 -0
  162. package/lib/typescript/native/DigitSlot.d.ts +27 -0
  163. package/lib/typescript/native/DigitSlot.d.ts.map +1 -0
  164. package/lib/typescript/native/NumberFlow.d.ts +3 -0
  165. package/lib/typescript/native/NumberFlow.d.ts.map +1 -0
  166. package/lib/typescript/native/SymbolSlot.d.ts +19 -0
  167. package/lib/typescript/native/SymbolSlot.d.ts.map +1 -0
  168. package/lib/typescript/native/TimeFlow.d.ts +3 -0
  169. package/lib/typescript/native/TimeFlow.d.ts.map +1 -0
  170. package/lib/typescript/native/index.d.ts +3 -0
  171. package/lib/typescript/native/index.d.ts.map +1 -0
  172. package/lib/typescript/native/renderSlots.d.ts +31 -0
  173. package/lib/typescript/native/renderSlots.d.ts.map +1 -0
  174. package/lib/typescript/native/types.d.ts +36 -0
  175. package/lib/typescript/native/types.d.ts.map +1 -0
  176. package/lib/typescript/native/useMeasuredGlyphMetrics.d.ts +8 -0
  177. package/lib/typescript/native/useMeasuredGlyphMetrics.d.ts.map +1 -0
  178. package/lib/typescript/package.json +1 -0
  179. package/lib/typescript/skia/DigitSlot.d.ts +35 -0
  180. package/lib/typescript/skia/DigitSlot.d.ts.map +1 -0
  181. package/lib/typescript/skia/SkiaNumberFlow.d.ts +3 -0
  182. package/lib/typescript/skia/SkiaNumberFlow.d.ts.map +1 -0
  183. package/lib/typescript/skia/SkiaTimeFlow.d.ts +3 -0
  184. package/lib/typescript/skia/SkiaTimeFlow.d.ts.map +1 -0
  185. package/lib/typescript/skia/SymbolSlot.d.ts +26 -0
  186. package/lib/typescript/skia/SymbolSlot.d.ts.map +1 -0
  187. package/lib/typescript/skia/index.d.ts +6 -0
  188. package/lib/typescript/skia/index.d.ts.map +1 -0
  189. package/lib/typescript/skia/renderSlots.d.ts +40 -0
  190. package/lib/typescript/skia/renderSlots.d.ts.map +1 -0
  191. package/lib/typescript/skia/useGlyphMetrics.d.ts +16 -0
  192. package/lib/typescript/skia/useGlyphMetrics.d.ts.map +1 -0
  193. package/lib/typescript/skia/useScrubbing.d.ts +59 -0
  194. package/lib/typescript/skia/useScrubbing.d.ts.map +1 -0
  195. package/lib/typescript/skia/useSkiaFont.d.ts +13 -0
  196. package/lib/typescript/skia/useSkiaFont.d.ts.map +1 -0
  197. package/package.json +104 -0
  198. package/src/core/constants.ts +20 -0
  199. package/src/core/intlHelpers.ts +351 -0
  200. package/src/core/layout.ts +108 -0
  201. package/src/core/mask.ts +72 -0
  202. package/src/core/numerals/detection.ts +112 -0
  203. package/src/core/numerals/digits.ts +102 -0
  204. package/src/core/numerals/index.ts +9 -0
  205. package/src/core/numerals/tables.ts +112 -0
  206. package/src/core/superscript.ts +27 -0
  207. package/src/core/timeLayout.ts +119 -0
  208. package/src/core/timeTypes.ts +88 -0
  209. package/src/core/timing.ts +60 -0
  210. package/src/core/types.ts +189 -0
  211. package/src/core/useAccessibilityAnnouncement.ts +27 -0
  212. package/src/core/useAnimatedX.ts +30 -0
  213. package/src/core/useAnimationLifecycle.ts +54 -0
  214. package/src/core/useCanAnimate.ts +21 -0
  215. package/src/core/useContinuousSpin.ts +112 -0
  216. package/src/core/useDebouncedWidths.ts +93 -0
  217. package/src/core/useDigitAnimation.ts +192 -0
  218. package/src/core/useFlowPipeline.ts +126 -0
  219. package/src/core/useFormattedValue.ts +32 -0
  220. package/src/core/useLayoutDiff.ts +71 -0
  221. package/src/core/useNumberFormatting.ts +164 -0
  222. package/src/core/useSlotOpacity.ts +66 -0
  223. package/src/core/useTimeFormatting.ts +95 -0
  224. package/src/core/useTimingResolution.ts +47 -0
  225. package/src/core/useWorkletFormatting.ts +59 -0
  226. package/src/core/utils.ts +149 -0
  227. package/src/core/warnings.ts +8 -0
  228. package/src/index.ts +15 -0
  229. package/src/native/DigitSlot.tsx +203 -0
  230. package/src/native/NumberFlow.tsx +287 -0
  231. package/src/native/SymbolSlot.tsx +68 -0
  232. package/src/native/TimeFlow.tsx +287 -0
  233. package/src/native/index.ts +2 -0
  234. package/src/native/renderSlots.tsx +150 -0
  235. package/src/native/types.ts +40 -0
  236. package/src/native/useMeasuredGlyphMetrics.tsx +205 -0
  237. package/src/skia/DigitSlot.tsx +221 -0
  238. package/src/skia/SkiaNumberFlow.tsx +506 -0
  239. package/src/skia/SkiaTimeFlow.tsx +257 -0
  240. package/src/skia/SymbolSlot.tsx +120 -0
  241. package/src/skia/index.ts +5 -0
  242. package/src/skia/renderSlots.tsx +180 -0
  243. package/src/skia/useGlyphMetrics.ts +79 -0
  244. package/src/skia/useScrubbing.ts +223 -0
  245. package/src/skia/useSkiaFont.ts +25 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Antonín Wingender
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ <p align="center">
2
+ <img src="https://github.com/Rednegniw/number-flow-react-native/raw/main/.github/assets/demo.gif" alt="number-flow-react-native demo" width="300" />
3
+ </p>
4
+
5
+ <h1 align="center">number-flow-react-native</h1>
6
+
7
+ <p align="center">
8
+ An animated number component for React Native.
9
+ <br />
10
+ Inspired by <a href="https://number-flow.barvian.me/">NumberFlow</a>.
11
+ </p>
12
+
13
+ <p align="center">
14
+ <a href="https://www.npmjs.com/package/number-flow-react-native"><img src="https://img.shields.io/npm/v/number-flow-react-native" alt="npm version" /></a>
15
+ <a href="https://github.com/Rednegniw/number-flow-react-native/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/number-flow-react-native" alt="license" /></a>
16
+ </p>
17
+
18
+ ---
19
+
20
+ For full documentation, visit **[DOCS_URL](DOCS_URL)**.
21
+
22
+ ## Quick start
23
+
24
+ ```bash
25
+ npm install number-flow-react-native react-native-reanimated
26
+ ```
27
+
28
+ ```tsx
29
+ import { NumberFlow } from "number-flow-react-native/native";
30
+
31
+ <NumberFlow
32
+ value={1234.56}
33
+ format={{ style: "currency", currency: "USD" }}
34
+ style={{ fontSize: 32, color: "#000" }}
35
+ />;
36
+ ```
37
+
38
+ ## Attribution
39
+
40
+ This library is a React Native reimplementation inspired by [NumberFlow](https://number-flow.barvian.me/) by [Maxwell Barvian](https://github.com/barvian). The animation patterns, easing curves, and digit-rolling approach are adapted from the original web implementation. All code is original — no source code is shared.
41
+
42
+ ## License
43
+
44
+ MIT
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ export const MAX_SLOTS = 20;
4
+ export const DIGIT_COUNT = 10;
5
+
6
+ // Fallback character set for glyph measurement when the actual format characters are unknown
7
+ export const MEASURABLE_CHARS = "0123456789.,%+-$/:! €£¥₩abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~°";
8
+
9
+ /**
10
+ * Horizontal mask fade width as a ratio of lineHeight.
11
+ * Matches web NumberFlow's --number-flow-mask-width: 0.5em
12
+ */
13
+ export const MASK_WIDTH_RATIO = 0.5;
14
+
15
+ /**
16
+ * Superscript exponent rendering scale derived from OpenType font metrics.
17
+ * Inter font OS/2 table: ySuperscriptYSize = 1229/2048 ≈ 0.60.
18
+ * Consistent with TeX (\defaultscriptratio=0.7) and general typography convention (60-70%).
19
+ */
20
+ export const SUPERSCRIPT_SCALE = 0.6;
21
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["MAX_SLOTS","DIGIT_COUNT","MEASURABLE_CHARS","MASK_WIDTH_RATIO","SUPERSCRIPT_SCALE"],"sourceRoot":"../../../src","sources":["core/constants.ts"],"mappings":";;AAAA,OAAO,MAAMA,SAAS,GAAG,EAAE;AAE3B,OAAO,MAAMC,WAAW,GAAG,EAAE;;AAE7B;AACA,OAAO,MAAMC,gBAAgB,GAC3B,gFAAgF;;AAElF;AACA;AACA;AACA;AACA,OAAO,MAAMC,gBAAgB,GAAG,GAAG;;AAEnC;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,iBAAiB,GAAG,GAAG","ignoreList":[]}
@@ -0,0 +1,310 @@
1
+ "use strict";
2
+
3
+ import { MEASURABLE_CHARS } from "./constants.js";
4
+ import { detectNumberingSystem, detectOutputZeroCodePoint, getDigitStrings, getZeroCodePoint, isLocaleDigit } from "./numerals/index.js";
5
+
6
+ /**
7
+ * Avoids expensive new Intl.NumberFormat() on every render when callers pass
8
+ * inline format objects (which create new references each render).
9
+ */
10
+ const formatterCache = new Map();
11
+ export function getOrCreateFormatter(locales, format) {
12
+ const key = JSON.stringify([locales, format]);
13
+ let cached = formatterCache.get(key);
14
+ if (!cached) {
15
+ cached = new Intl.NumberFormat(locales, format);
16
+ formatterCache.set(key, cached);
17
+ }
18
+ return cached;
19
+ }
20
+
21
+ /**
22
+ * Probes the formatter with a sample number that exercises group separators,
23
+ * decimal separator, and currency/percent symbols. Returns only characters NOT
24
+ * already in MEASURABLE_CHARS — this keeps the result stable across different
25
+ * prefix/suffix combinations and prevents native measurement cache invalidation.
26
+ */
27
+ const formatCharsCache = new Map();
28
+ export function getFormatCharacters(locales, format, prefix = "", suffix = "") {
29
+ const key = JSON.stringify([locales, format, prefix, suffix]);
30
+ const cached = formatCharsCache.get(key);
31
+ if (cached !== undefined) return cached;
32
+ const formatter = getOrCreateFormatter(locales, format);
33
+ const numberingSystem = detectNumberingSystem(locales, format);
34
+ const zeroCP = getZeroCodePoint(numberingSystem);
35
+
36
+ // Sample that exercises: group separators, decimal, sign, large integers
37
+ const probes = [1234567.89, -1234567.89];
38
+ const chars = new Set();
39
+ for (const probe of probes) {
40
+ for (const ch of formatter.format(probe)) {
41
+ const code = ch.charCodeAt(0);
42
+ // Skip digits in both Latin and the locale's numbering system
43
+ const isLatinDigit = code >= 48 && code <= 57;
44
+ const isLocale = isLocaleDigit(code, zeroCP);
45
+ if (!isLatinDigit && !isLocale) chars.add(ch);
46
+ }
47
+ }
48
+ // Scientific/engineering notation replaces E with ×10 in our display
49
+ if (format?.notation === "scientific" || format?.notation === "engineering") {
50
+ chars.add("\u00D7"); // × (multiplication sign)
51
+ }
52
+
53
+ // Add locale digit strings so they get measured
54
+ const digitStrings = getDigitStrings(numberingSystem);
55
+ for (const ds of digitStrings) chars.add(ds);
56
+
57
+ // Also include prefix/suffix chars.
58
+ for (const ch of prefix) chars.add(ch);
59
+ for (const ch of suffix) chars.add(ch);
60
+
61
+ /**
62
+ * Only return chars NOT already in MEASURABLE_CHARS — this keeps the result
63
+ * stable across prefix/suffix changes and avoids native measurement cache misses.
64
+ */
65
+ const result = Array.from(chars).filter(c => !MEASURABLE_CHARS.includes(c)).join("");
66
+ formatCharsCache.set(key, result);
67
+ return result;
68
+ }
69
+ const decimalSepCache = new Map();
70
+ function detectDecimalSeparator(locales) {
71
+ const key = JSON.stringify(locales);
72
+ const cached = decimalSepCache.get(key);
73
+ if (cached) return cached;
74
+ let sep = ".";
75
+ try {
76
+ const fmt = new Intl.NumberFormat(locales, {
77
+ minimumFractionDigits: 1,
78
+ maximumFractionDigits: 1,
79
+ useGrouping: false
80
+ });
81
+ const str = fmt.format(1.5);
82
+ const zeroCP = detectOutputZeroCodePoint(str);
83
+ for (const ch of str) {
84
+ const code = ch.charCodeAt(0);
85
+ if (!isLocaleDigit(code, zeroCP) && !(code >= 48 && code <= 57)) {
86
+ sep = ch;
87
+ break;
88
+ }
89
+ }
90
+ } catch {}
91
+ decimalSepCache.set(key, sep);
92
+ return sep;
93
+ }
94
+
95
+ /**
96
+ * Parses the mantissa portion of a formatted number string into typed parts.
97
+ * Handles integer digits, fraction digits, decimal separators, signs, and group separators.
98
+ */
99
+ function parseMantissa(mantissa, decimalSep, parts, zeroCodePoint = 48) {
100
+ /**
101
+ * Search from right — in some locales the decimal sep char is also
102
+ * used as a group separator, so leftmost match could be wrong.
103
+ */
104
+ let decimalPos = -1;
105
+ for (let i = mantissa.length - 1; i >= 0; i--) {
106
+ if (mantissa[i] === decimalSep) {
107
+ let hasDigitAfter = false;
108
+ for (let j = i + 1; j < mantissa.length; j++) {
109
+ if (isLocaleDigit(mantissa[j].charCodeAt(0), zeroCodePoint)) {
110
+ hasDigitAfter = true;
111
+ break;
112
+ }
113
+ }
114
+ if (hasDigitAfter) {
115
+ decimalPos = i;
116
+ break;
117
+ }
118
+ }
119
+ }
120
+ let buf = "";
121
+ let inFraction = false;
122
+ const flush = () => {
123
+ if (buf) {
124
+ parts.push({
125
+ type: inFraction ? "fraction" : "integer",
126
+ value: buf
127
+ });
128
+ buf = "";
129
+ }
130
+ };
131
+ for (let i = 0; i < mantissa.length; i++) {
132
+ const ch = mantissa[i];
133
+ if (i === decimalPos) {
134
+ flush();
135
+ parts.push({
136
+ type: "decimal",
137
+ value: ch
138
+ });
139
+ inFraction = true;
140
+ continue;
141
+ }
142
+ if (isLocaleDigit(ch.charCodeAt(0), zeroCodePoint)) {
143
+ buf += ch;
144
+ } else if (ch === "-") {
145
+ flush();
146
+ parts.push({
147
+ type: "minusSign",
148
+ value: ch
149
+ });
150
+ } else if (ch === "+") {
151
+ flush();
152
+ parts.push({
153
+ type: "plusSign",
154
+ value: ch
155
+ });
156
+ } else if (!inFraction) {
157
+ flush();
158
+ const prevDigit = i > 0 && isLocaleDigit(mantissa[i - 1].charCodeAt(0), zeroCodePoint);
159
+ const nextDigit = i < mantissa.length - 1 && isLocaleDigit(mantissa[i + 1].charCodeAt(0), zeroCodePoint);
160
+ parts.push({
161
+ type: prevDigit && nextDigit ? "group" : "literal",
162
+ value: ch
163
+ });
164
+ } else {
165
+ flush();
166
+ parts.push({
167
+ type: "literal",
168
+ value: ch
169
+ });
170
+ }
171
+ }
172
+ flush();
173
+ }
174
+
175
+ /**
176
+ * Parses a formatted number string (with optional E exponent) into typed parts.
177
+ * Separated from fallbackFormatToParts so it can be reused for polyfill strings.
178
+ */
179
+ function parseNumberString(formatted, decimalSep, zeroCodePoint = 48) {
180
+ // Detect exponent separator (E or e) — split into mantissa + exponent
181
+ let exponentPos = -1;
182
+ for (let i = 0; i < formatted.length; i++) {
183
+ if (formatted[i] === "E" || formatted[i] === "e") {
184
+ exponentPos = i;
185
+ break;
186
+ }
187
+ }
188
+ const mantissa = exponentPos >= 0 ? formatted.slice(0, exponentPos) : formatted;
189
+ const parts = [];
190
+ parseMantissa(mantissa, decimalSep, parts, zeroCodePoint);
191
+ if (exponentPos >= 0) {
192
+ parts.push({
193
+ type: "exponentSeparator",
194
+ value: formatted[exponentPos]
195
+ });
196
+ let expBuf = "";
197
+ for (let i = exponentPos + 1; i < formatted.length; i++) {
198
+ const ch = formatted[i];
199
+ if (ch === "-") {
200
+ parts.push({
201
+ type: "exponentMinusSign",
202
+ value: ch
203
+ });
204
+ } else if (ch === "+") {
205
+ parts.push({
206
+ type: "exponentPlusSign",
207
+ value: ch
208
+ });
209
+ } else if (isLocaleDigit(ch.charCodeAt(0), zeroCodePoint)) {
210
+ expBuf += ch;
211
+ }
212
+ }
213
+ if (expBuf) {
214
+ parts.push({
215
+ type: "exponentInteger",
216
+ value: expBuf
217
+ });
218
+ }
219
+ }
220
+ return parts;
221
+ }
222
+
223
+ /**
224
+ * Hermes has Intl.NumberFormat but may lack formatToParts(). This fallback
225
+ * uses format() and parses the resulting string into typed parts.
226
+ */
227
+ function fallbackFormatToParts(formatter, value, locales) {
228
+ const formatted = formatter.format(value);
229
+ const zeroCp = detectOutputZeroCodePoint(formatted);
230
+ return parseNumberString(formatted, detectDecimalSeparator(locales), zeroCp);
231
+ }
232
+
233
+ /**
234
+ * Manually computes an engineering notation string for values where the
235
+ * platform's Intl.NumberFormat doesn't support notation: "engineering"
236
+ * (notably iOS Hermes, which uses NSNumberFormatter under the hood).
237
+ *
238
+ * Engineering notation: exponent is always a multiple of 3, mantissa has 1-3 integer digits.
239
+ */
240
+ function computeEngineeringString(value, resolved) {
241
+ if (value === 0) return "0E0";
242
+ const negative = value < 0;
243
+ const abs = Math.abs(value);
244
+ const logFloor = Math.floor(Math.log10(abs));
245
+ const exp = 3 * Math.floor(logFloor / 3);
246
+ const mantissa = abs / 10 ** exp;
247
+ const maxFrac = resolved.maximumFractionDigits ?? 0;
248
+ const minFrac = resolved.minimumFractionDigits ?? 0;
249
+ let mantissaStr = mantissa.toFixed(maxFrac);
250
+
251
+ // Trim trailing zeros beyond minFrac
252
+ const dotIdx = mantissaStr.indexOf(".");
253
+ if (dotIdx >= 0) {
254
+ let end = mantissaStr.length;
255
+ const minEnd = dotIdx + 1 + minFrac;
256
+ while (end > minEnd && mantissaStr[end - 1] === "0") {
257
+ end--;
258
+ }
259
+
260
+ // Remove the dot if no fraction digits remain
261
+ if (end <= dotIdx + 1 && minFrac === 0) {
262
+ end = dotIdx;
263
+ }
264
+ mantissaStr = mantissaStr.slice(0, end);
265
+ }
266
+ const sign = negative ? "-" : "";
267
+ return `${sign}${mantissaStr}E${exp}`;
268
+ }
269
+
270
+ /**
271
+ * Safe wrapper around formatToParts that handles:
272
+ * 1. Missing formatToParts (Hermes fallback)
273
+ * 2. Broken formatToParts that returns all "literal" parts (non-Latin locales on Hermes)
274
+ * 3. Missing engineering notation support (iOS Hermes)
275
+ */
276
+ export function safeFormatToParts(formatter, value, locales) {
277
+ let parts;
278
+ if (typeof formatter.formatToParts === "function") {
279
+ parts = formatter.formatToParts(value);
280
+
281
+ /**
282
+ * Hermes may return formatToParts with all parts as "literal" for
283
+ * non-Latin locales, or misclassify digit characters. Validate that
284
+ * at least one "integer" or "fraction" part exists for non-zero values.
285
+ * If not, fall back to our manual parser which detects digits by codepoint.
286
+ */
287
+ if (value !== 0) {
288
+ const hasDigitParts = parts.some(p => p.type === "integer" || p.type === "fraction");
289
+ if (!hasDigitParts) {
290
+ parts = fallbackFormatToParts(formatter, value, locales);
291
+ }
292
+ }
293
+ } else {
294
+ parts = fallbackFormatToParts(formatter, value, locales);
295
+ }
296
+
297
+ // iOS Hermes uses NSNumberFormatter which doesn't support engineering notation.
298
+ // It silently falls back to decimal formatting, producing no exponent parts.
299
+ // Detect this and manually compute the engineering representation.
300
+ const hasExponent = parts.some(p => p.type === "exponentSeparator");
301
+ if (!hasExponent && Number.isFinite(value)) {
302
+ const notation = formatter.resolvedOptions().notation;
303
+ if (notation === "engineering") {
304
+ const str = computeEngineeringString(value, formatter.resolvedOptions());
305
+ return parseNumberString(str, ".");
306
+ }
307
+ }
308
+ return parts;
309
+ }
310
+ //# sourceMappingURL=intlHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["MEASURABLE_CHARS","detectNumberingSystem","detectOutputZeroCodePoint","getDigitStrings","getZeroCodePoint","isLocaleDigit","formatterCache","Map","getOrCreateFormatter","locales","format","key","JSON","stringify","cached","get","Intl","NumberFormat","set","formatCharsCache","getFormatCharacters","prefix","suffix","undefined","formatter","numberingSystem","zeroCP","probes","chars","Set","probe","ch","code","charCodeAt","isLatinDigit","isLocale","add","notation","digitStrings","ds","result","Array","from","filter","c","includes","join","decimalSepCache","detectDecimalSeparator","sep","fmt","minimumFractionDigits","maximumFractionDigits","useGrouping","str","parseMantissa","mantissa","decimalSep","parts","zeroCodePoint","decimalPos","i","length","hasDigitAfter","j","buf","inFraction","flush","push","type","value","prevDigit","nextDigit","parseNumberString","formatted","exponentPos","slice","expBuf","fallbackFormatToParts","zeroCp","computeEngineeringString","resolved","negative","abs","Math","logFloor","floor","log10","exp","maxFrac","minFrac","mantissaStr","toFixed","dotIdx","indexOf","end","minEnd","sign","safeFormatToParts","formatToParts","hasDigitParts","some","p","hasExponent","Number","isFinite","resolvedOptions"],"sourceRoot":"../../../src","sources":["core/intlHelpers.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+
3
+ import { SUPERSCRIPT_SCALE } from "./constants.js";
4
+ import { isDigitChar, localeDigitValue } from "./numerals/index.js";
5
+
6
+ /**
7
+ * Assigns x positions to each entry based on text alignment.
8
+ * Mutates the `x` field of each entry in place.
9
+ * Worklet-safe. When `precomputedContentWidth` is provided, skips the sum loop.
10
+ */
11
+ export function assignXPositions(chars, totalWidth, textAlign, precomputedContentWidth) {
12
+ "worklet";
13
+
14
+ let contentWidth = precomputedContentWidth ?? 0;
15
+ if (precomputedContentWidth === undefined) {
16
+ for (const entry of chars) contentWidth += entry.width;
17
+ }
18
+ let startX = 0;
19
+ if (textAlign === "right") startX = totalWidth - contentWidth;else if (textAlign === "center") startX = (totalWidth - contentWidth) / 2;
20
+ let currentX = startX;
21
+ for (const entry of chars) {
22
+ entry.x = currentX;
23
+ currentX += entry.width;
24
+ }
25
+ }
26
+ export function computeKeyedLayout(parts, metrics, totalWidth, textAlign, localeDigitStrings) {
27
+ const chars = [];
28
+ for (const part of parts) {
29
+ const isSuperscript = part.key.startsWith("exponentInteger:") || part.key.startsWith("exponentSign:");
30
+
31
+ // For digit parts, look up the width of the locale digit character
32
+ // (what DigitSlot actually renders) rather than the format output character.
33
+ // On Hermes, format() may output Latin "4" but DigitSlot renders e.g. "四".
34
+ let displayChar = part.char;
35
+ if (part.type === "digit" && localeDigitStrings && part.digitValue >= 0) {
36
+ displayChar = localeDigitStrings[part.digitValue];
37
+ }
38
+ const rawWidth = metrics.charWidths[displayChar] ?? metrics.maxDigitWidth;
39
+ const width = isSuperscript ? rawWidth * SUPERSCRIPT_SCALE : rawWidth;
40
+ chars.push({
41
+ key: part.key,
42
+ char: part.char,
43
+ isDigit: part.type === "digit",
44
+ digitValue: part.digitValue,
45
+ x: 0,
46
+ width,
47
+ superscript: isSuperscript
48
+ });
49
+ }
50
+ assignXPositions(chars, totalWidth, textAlign);
51
+ return chars;
52
+ }
53
+ export function computeStringLayout(text, metrics, totalWidth, textAlign, zeroCodePoint = 48) {
54
+ const chars = [];
55
+ for (let i = 0; i < text.length; i++) {
56
+ const char = text[i];
57
+ const digit = isDigitChar(char, zeroCodePoint);
58
+ const width = metrics.charWidths[char] ?? metrics.maxDigitWidth;
59
+ chars.push({
60
+ key: `pos:${i}`,
61
+ char,
62
+ isDigit: digit,
63
+ digitValue: digit ? localeDigitValue(char.charCodeAt(0), zeroCodePoint) : -1,
64
+ x: 0,
65
+ width
66
+ });
67
+ }
68
+ assignXPositions(chars, totalWidth, textAlign);
69
+ return chars;
70
+ }
71
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["SUPERSCRIPT_SCALE","isDigitChar","localeDigitValue","assignXPositions","chars","totalWidth","textAlign","precomputedContentWidth","contentWidth","undefined","entry","width","startX","currentX","x","computeKeyedLayout","parts","metrics","localeDigitStrings","part","isSuperscript","key","startsWith","displayChar","char","type","digitValue","rawWidth","charWidths","maxDigitWidth","push","isDigit","superscript","computeStringLayout","text","zeroCodePoint","i","length","digit","charCodeAt"],"sourceRoot":"../../../src","sources":["core/layout.ts"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+
3
+ // Target gradient size as a ratio of lineHeight.
4
+ // The gradient will be at least this tall, expanding the container if needed.
5
+ const TARGET_GRADIENT_RATIO = 0.2;
6
+
7
+ // Small inset so the gradient doesn't start right at the glyph boundary.
8
+ const PADDING_PX = 2;
9
+ /**
10
+ * Computes adaptive vertical mask heights based on the actual characters
11
+ * currently displayed.
12
+ *
13
+ * The gradient starts at the edge of the visible glyph content and extends
14
+ * outward. If the "dead zone" inside lineHeight is smaller than the target
15
+ * gradient size, the container expands beyond lineHeight.
16
+ */
17
+ export function computeAdaptiveMaskHeights(layout, exitingEntries, metrics) {
18
+ let maxAscent = 0;
19
+ let maxDescent = 0;
20
+ const processChar = char => {
21
+ const bounds = metrics.charBounds[char];
22
+ if (!bounds) return;
23
+ if (bounds.top < maxAscent) maxAscent = bounds.top;
24
+ if (bounds.bottom > maxDescent) maxDescent = bounds.bottom;
25
+ };
26
+ for (const entry of layout) {
27
+ if (entry.superscript) continue;
28
+ processChar(entry.char);
29
+ }
30
+ for (const [, entry] of exitingEntries) {
31
+ if (entry.superscript) continue;
32
+ processChar(entry.char);
33
+ }
34
+ const targetGradient = TARGET_GRADIENT_RATIO * metrics.lineHeight;
35
+
36
+ // Dead zone: space within lineHeight not occupied by glyph content.
37
+ const deadZoneTop = Math.max(0, -metrics.ascent - -maxAscent - PADDING_PX);
38
+ const deadZoneBottom = Math.max(0, metrics.descent - maxDescent - PADDING_PX);
39
+
40
+ // If the dead zone is smaller than the target, expand the container.
41
+ const expansionTop = Math.max(0, targetGradient - deadZoneTop);
42
+ const expansionBottom = Math.max(0, targetGradient - deadZoneBottom);
43
+ return {
44
+ top: deadZoneTop + expansionTop,
45
+ bottom: deadZoneBottom + expansionBottom,
46
+ expansionTop,
47
+ expansionBottom
48
+ };
49
+ }
50
+ //# sourceMappingURL=mask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TARGET_GRADIENT_RATIO","PADDING_PX","computeAdaptiveMaskHeights","layout","exitingEntries","metrics","maxAscent","maxDescent","processChar","char","bounds","charBounds","top","bottom","entry","superscript","targetGradient","lineHeight","deadZoneTop","Math","max","ascent","deadZoneBottom","descent","expansionTop","expansionBottom"],"sourceRoot":"../../../src","sources":["core/mask.ts"],"mappings":";;AAGA;AACA;AACA,MAAMA,qBAAqB,GAAG,GAAG;;AAEjC;AACA,MAAMC,UAAU,GAAG,CAAC;AAapB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,0BAA0BA,CACxCC,MAAoB,EACpBC,cAAuC,EACvCC,OAAqB,EACR;EACb,IAAIC,SAAS,GAAG,CAAC;EACjB,IAAIC,UAAU,GAAG,CAAC;EAElB,MAAMC,WAAW,GAAIC,IAAY,IAAK;IACpC,MAAMC,MAAM,GAAGL,OAAO,CAACM,UAAU,CAACF,IAAI,CAAC;IACvC,IAAI,CAACC,MAAM,EAAE;IAEb,IAAIA,MAAM,CAACE,GAAG,GAAGN,SAAS,EAAEA,SAAS,GAAGI,MAAM,CAACE,GAAG;IAClD,IAAIF,MAAM,CAACG,MAAM,GAAGN,UAAU,EAAEA,UAAU,GAAGG,MAAM,CAACG,MAAM;EAC5D,CAAC;EAED,KAAK,MAAMC,KAAK,IAAIX,MAAM,EAAE;IAC1B,IAAIW,KAAK,CAACC,WAAW,EAAE;IACvBP,WAAW,CAACM,KAAK,CAACL,IAAI,CAAC;EACzB;EAEA,KAAK,MAAM,GAAGK,KAAK,CAAC,IAAIV,cAAc,EAAE;IACtC,IAAIU,KAAK,CAACC,WAAW,EAAE;IACvBP,WAAW,CAACM,KAAK,CAACL,IAAI,CAAC;EACzB;EAEA,MAAMO,cAAc,GAAGhB,qBAAqB,GAAGK,OAAO,CAACY,UAAU;;EAEjE;EACA,MAAMC,WAAW,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC,EAAE,CAACf,OAAO,CAACgB,MAAM,GAAG,CAACf,SAAS,GAAGL,UAAU,CAAC;EAC1E,MAAMqB,cAAc,GAAGH,IAAI,CAACC,GAAG,CAAC,CAAC,EAAEf,OAAO,CAACkB,OAAO,GAAGhB,UAAU,GAAGN,UAAU,CAAC;;EAE7E;EACA,MAAMuB,YAAY,GAAGL,IAAI,CAACC,GAAG,CAAC,CAAC,EAAEJ,cAAc,GAAGE,WAAW,CAAC;EAC9D,MAAMO,eAAe,GAAGN,IAAI,CAACC,GAAG,CAAC,CAAC,EAAEJ,cAAc,GAAGM,cAAc,CAAC;EAEpE,OAAO;IACLV,GAAG,EAAEM,WAAW,GAAGM,YAAY;IAC/BX,MAAM,EAAES,cAAc,GAAGG,eAAe;IACxCD,YAAY;IACZC;EACF,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+
3
+ import { hanidecDigitValue } from "./digits.js";
4
+ import { CLDR_DEFAULT_NUMBERING, HANIDEC_ZERO, LATIN_ZERO, ZERO_CODEPOINTS } from "./tables.js";
5
+ const numberingSystemCache = new Map();
6
+
7
+ /**
8
+ * Looks up the CLDR-expected numbering system for a locale.
9
+ * Checks explicit -u-nu- extension, then exact match (e.g. "ar-EG"),
10
+ * then language subtag (e.g. "ar").
11
+ * Returns undefined if the locale defaults to "latn".
12
+ */
13
+ function getExpectedNumberingSystem(locales) {
14
+ if (!locales) return undefined;
15
+ const tag = String(locales);
16
+
17
+ // Parse explicit Unicode extension: "th-TH-u-nu-thai" → "thai"
18
+ const nuMatch = tag.match(/-u-nu-([a-z]+)/);
19
+ if (nuMatch) {
20
+ return nuMatch[1] === "latn" ? undefined : nuMatch[1];
21
+ }
22
+ const exact = CLDR_DEFAULT_NUMBERING[tag];
23
+ if (exact) return exact;
24
+
25
+ // Try language subtag only (strip region): "ar-EG" → "ar"
26
+ const dashIdx = tag.indexOf("-");
27
+ if (dashIdx > 0) {
28
+ return CLDR_DEFAULT_NUMBERING[tag.slice(0, dashIdx)];
29
+ }
30
+ return undefined;
31
+ }
32
+
33
+ /**
34
+ * Scans a formatted number string and detects which numbering system's
35
+ * digits are actually present. Returns the zero codepoint of the detected
36
+ * system, or LATIN_ZERO if only Latin digits (or no digits) are found.
37
+ *
38
+ * Handles hanidec (non-contiguous ideographs) separately from contiguous systems.
39
+ */
40
+ export function detectOutputZeroCodePoint(formattedStr) {
41
+ for (let i = 0; i < formattedStr.length; i++) {
42
+ const code = formattedStr.charCodeAt(i);
43
+
44
+ // Skip Latin digits — we want to detect non-Latin systems
45
+ if (code >= LATIN_ZERO && code <= LATIN_ZERO + 9) continue;
46
+
47
+ // Check hanidec (non-contiguous codepoints)
48
+ if (hanidecDigitValue(code) >= 0) return HANIDEC_ZERO;
49
+
50
+ // Check all contiguous systems (skip latn and hanidec)
51
+ for (const system in ZERO_CODEPOINTS) {
52
+ if (system === "latn" || system === "hanidec") continue;
53
+ const zeroCp = ZERO_CODEPOINTS[system];
54
+ if (code >= zeroCp && code <= zeroCp + 9) return zeroCp;
55
+ }
56
+ }
57
+ return LATIN_ZERO;
58
+ }
59
+
60
+ /**
61
+ * Detects the numbering system for a locale/format combination.
62
+ *
63
+ * Strategy: query the platform first (resolvedOptions), then verify by
64
+ * formatting a probe. If the platform reports "latn" but the CLDR table
65
+ * says otherwise (common on Hermes), use the CLDR value.
66
+ */
67
+ export function detectNumberingSystem(locales, format) {
68
+ const key = JSON.stringify([locales, format]);
69
+ const cached = numberingSystemCache.get(key);
70
+ if (cached) return cached;
71
+ const formatter = new Intl.NumberFormat(locales ?? undefined, format);
72
+ const platformSystem = formatter.resolvedOptions().numberingSystem;
73
+
74
+ // If the platform reports a known non-latn system, trust it
75
+ if (platformSystem && platformSystem !== "latn") {
76
+ numberingSystemCache.set(key, platformSystem);
77
+ return platformSystem;
78
+ }
79
+
80
+ // Platform says "latn" or undefined — check if the locale expects something else
81
+ const expected = getExpectedNumberingSystem(locales);
82
+ if (!expected) {
83
+ numberingSystemCache.set(key, "latn");
84
+ return "latn";
85
+ }
86
+
87
+ // Verify by checking the actual formatted output
88
+ const probe = formatter.format(1234567890);
89
+ const outputZeroCp = detectOutputZeroCodePoint(probe);
90
+ if (outputZeroCp !== LATIN_ZERO) {
91
+ // Platform does output non-Latin digits; find the matching system name
92
+ for (const system in ZERO_CODEPOINTS) {
93
+ if (ZERO_CODEPOINTS[system] === outputZeroCp) {
94
+ numberingSystemCache.set(key, system);
95
+ return system;
96
+ }
97
+ }
98
+ }
99
+
100
+ // Platform truly outputs Latin digits — use the CLDR expected system
101
+ // so digitStrings render the correct locale characters on the wheel
102
+ numberingSystemCache.set(key, expected);
103
+ return expected;
104
+ }
105
+ //# sourceMappingURL=detection.js.map