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.
- package/LICENSE +21 -0
- package/README.md +44 -0
- package/lib/module/core/constants.js +21 -0
- package/lib/module/core/constants.js.map +1 -0
- package/lib/module/core/intlHelpers.js +310 -0
- package/lib/module/core/intlHelpers.js.map +1 -0
- package/lib/module/core/layout.js +71 -0
- package/lib/module/core/layout.js.map +1 -0
- package/lib/module/core/mask.js +50 -0
- package/lib/module/core/mask.js.map +1 -0
- package/lib/module/core/numerals/detection.js +105 -0
- package/lib/module/core/numerals/detection.js.map +1 -0
- package/lib/module/core/numerals/digits.js +128 -0
- package/lib/module/core/numerals/digits.js.map +1 -0
- package/lib/module/core/numerals/index.js +5 -0
- package/lib/module/core/numerals/index.js.map +1 -0
- package/lib/module/core/numerals/tables.js +114 -0
- package/lib/module/core/numerals/tables.js.map +1 -0
- package/lib/module/core/superscript.js +31 -0
- package/lib/module/core/superscript.js.map +1 -0
- package/lib/module/core/timeLayout.js +98 -0
- package/lib/module/core/timeLayout.js.map +1 -0
- package/lib/module/core/timeTypes.js +4 -0
- package/lib/module/core/timeTypes.js.map +1 -0
- package/lib/module/core/timing.js +45 -0
- package/lib/module/core/timing.js.map +1 -0
- package/lib/module/core/types.js +58 -0
- package/lib/module/core/types.js.map +1 -0
- package/lib/module/core/useAccessibilityAnnouncement.js +27 -0
- package/lib/module/core/useAccessibilityAnnouncement.js.map +1 -0
- package/lib/module/core/useAnimatedX.js +25 -0
- package/lib/module/core/useAnimatedX.js.map +1 -0
- package/lib/module/core/useAnimationLifecycle.js +37 -0
- package/lib/module/core/useAnimationLifecycle.js.map +1 -0
- package/lib/module/core/useCanAnimate.js +22 -0
- package/lib/module/core/useCanAnimate.js.map +1 -0
- package/lib/module/core/useContinuousSpin.js +89 -0
- package/lib/module/core/useContinuousSpin.js.map +1 -0
- package/lib/module/core/useDebouncedWidths.js +74 -0
- package/lib/module/core/useDebouncedWidths.js.map +1 -0
- package/lib/module/core/useDigitAnimation.js +138 -0
- package/lib/module/core/useDigitAnimation.js.map +1 -0
- package/lib/module/core/useFlowPipeline.js +85 -0
- package/lib/module/core/useFlowPipeline.js.map +1 -0
- package/lib/module/core/useFormattedValue.js +28 -0
- package/lib/module/core/useFormattedValue.js.map +1 -0
- package/lib/module/core/useLayoutDiff.js +59 -0
- package/lib/module/core/useLayoutDiff.js.map +1 -0
- package/lib/module/core/useNumberFormatting.js +158 -0
- package/lib/module/core/useNumberFormatting.js.map +1 -0
- package/lib/module/core/useSlotOpacity.js +53 -0
- package/lib/module/core/useSlotOpacity.js.map +1 -0
- package/lib/module/core/useTimeFormatting.js +74 -0
- package/lib/module/core/useTimeFormatting.js.map +1 -0
- package/lib/module/core/useTimingResolution.js +21 -0
- package/lib/module/core/useTimingResolution.js.map +1 -0
- package/lib/module/core/useWorkletFormatting.js +49 -0
- package/lib/module/core/useWorkletFormatting.js.map +1 -0
- package/lib/module/core/utils.js +132 -0
- package/lib/module/core/utils.js.map +1 -0
- package/lib/module/core/warnings.js +10 -0
- package/lib/module/core/warnings.js.map +1 -0
- package/lib/module/index.js +7 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/native/DigitSlot.js +163 -0
- package/lib/module/native/DigitSlot.js.map +1 -0
- package/lib/module/native/NumberFlow.js +244 -0
- package/lib/module/native/NumberFlow.js.map +1 -0
- package/lib/module/native/SymbolSlot.js +52 -0
- package/lib/module/native/SymbolSlot.js.map +1 -0
- package/lib/module/native/TimeFlow.js +270 -0
- package/lib/module/native/TimeFlow.js.map +1 -0
- package/lib/module/native/index.js +5 -0
- package/lib/module/native/index.js.map +1 -0
- package/lib/module/native/renderSlots.js +108 -0
- package/lib/module/native/renderSlots.js.map +1 -0
- package/lib/module/native/types.js +4 -0
- package/lib/module/native/types.js.map +1 -0
- package/lib/module/native/useMeasuredGlyphMetrics.js +156 -0
- package/lib/module/native/useMeasuredGlyphMetrics.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/skia/DigitSlot.js +171 -0
- package/lib/module/skia/DigitSlot.js.map +1 -0
- package/lib/module/skia/SkiaNumberFlow.js +430 -0
- package/lib/module/skia/SkiaNumberFlow.js.map +1 -0
- package/lib/module/skia/SkiaTimeFlow.js +226 -0
- package/lib/module/skia/SkiaTimeFlow.js.map +1 -0
- package/lib/module/skia/SymbolSlot.js +92 -0
- package/lib/module/skia/SymbolSlot.js.map +1 -0
- package/lib/module/skia/index.js +6 -0
- package/lib/module/skia/index.js.map +1 -0
- package/lib/module/skia/renderSlots.js +131 -0
- package/lib/module/skia/renderSlots.js.map +1 -0
- package/lib/module/skia/useGlyphMetrics.js +72 -0
- package/lib/module/skia/useGlyphMetrics.js.map +1 -0
- package/lib/module/skia/useScrubbing.js +165 -0
- package/lib/module/skia/useScrubbing.js.map +1 -0
- package/lib/module/skia/useSkiaFont.js +23 -0
- package/lib/module/skia/useSkiaFont.js.map +1 -0
- package/lib/typescript/core/constants.d.ts +15 -0
- package/lib/typescript/core/constants.d.ts.map +1 -0
- package/lib/typescript/core/intlHelpers.d.ts +10 -0
- package/lib/typescript/core/intlHelpers.d.ts.map +1 -0
- package/lib/typescript/core/layout.d.ts +22 -0
- package/lib/typescript/core/layout.d.ts.map +1 -0
- package/lib/typescript/core/mask.d.ts +18 -0
- package/lib/typescript/core/mask.d.ts.map +1 -0
- package/lib/typescript/core/numerals/detection.d.ts +17 -0
- package/lib/typescript/core/numerals/detection.d.ts.map +1 -0
- package/lib/typescript/core/numerals/digits.d.ts +43 -0
- package/lib/typescript/core/numerals/digits.d.ts.map +1 -0
- package/lib/typescript/core/numerals/index.d.ts +3 -0
- package/lib/typescript/core/numerals/index.d.ts.map +1 -0
- package/lib/typescript/core/numerals/tables.d.ts +32 -0
- package/lib/typescript/core/numerals/tables.d.ts.map +1 -0
- package/lib/typescript/core/superscript.d.ts +16 -0
- package/lib/typescript/core/superscript.d.ts.map +1 -0
- package/lib/typescript/core/timeLayout.d.ts +19 -0
- package/lib/typescript/core/timeLayout.d.ts.map +1 -0
- package/lib/typescript/core/timeTypes.d.ts +80 -0
- package/lib/typescript/core/timeTypes.d.ts.map +1 -0
- package/lib/typescript/core/timing.d.ts +6 -0
- package/lib/typescript/core/timing.d.ts.map +1 -0
- package/lib/typescript/core/types.d.ts +165 -0
- package/lib/typescript/core/types.d.ts.map +1 -0
- package/lib/typescript/core/useAccessibilityAnnouncement.d.ts +10 -0
- package/lib/typescript/core/useAccessibilityAnnouncement.d.ts.map +1 -0
- package/lib/typescript/core/useAnimatedX.d.ts +9 -0
- package/lib/typescript/core/useAnimatedX.d.ts.map +1 -0
- package/lib/typescript/core/useAnimationLifecycle.d.ts +14 -0
- package/lib/typescript/core/useAnimationLifecycle.d.ts.map +1 -0
- package/lib/typescript/core/useCanAnimate.d.ts +14 -0
- package/lib/typescript/core/useCanAnimate.d.ts.map +1 -0
- package/lib/typescript/core/useContinuousSpin.d.ts +23 -0
- package/lib/typescript/core/useContinuousSpin.d.ts.map +1 -0
- package/lib/typescript/core/useDebouncedWidths.d.ts +17 -0
- package/lib/typescript/core/useDebouncedWidths.d.ts.map +1 -0
- package/lib/typescript/core/useDigitAnimation.d.ts +38 -0
- package/lib/typescript/core/useDigitAnimation.d.ts.map +1 -0
- package/lib/typescript/core/useFlowPipeline.d.ts +46 -0
- package/lib/typescript/core/useFlowPipeline.d.ts.map +1 -0
- package/lib/typescript/core/useFormattedValue.d.ts +14 -0
- package/lib/typescript/core/useFormattedValue.d.ts.map +1 -0
- package/lib/typescript/core/useLayoutDiff.d.ts +18 -0
- package/lib/typescript/core/useLayoutDiff.d.ts.map +1 -0
- package/lib/typescript/core/useNumberFormatting.d.ts +18 -0
- package/lib/typescript/core/useNumberFormatting.d.ts.map +1 -0
- package/lib/typescript/core/useSlotOpacity.d.ts +18 -0
- package/lib/typescript/core/useSlotOpacity.d.ts.map +1 -0
- package/lib/typescript/core/useTimeFormatting.d.ts +22 -0
- package/lib/typescript/core/useTimeFormatting.d.ts.map +1 -0
- package/lib/typescript/core/useTimingResolution.d.ts +13 -0
- package/lib/typescript/core/useTimingResolution.d.ts.map +1 -0
- package/lib/typescript/core/useWorkletFormatting.d.ts +14 -0
- package/lib/typescript/core/useWorkletFormatting.d.ts.map +1 -0
- package/lib/typescript/core/utils.d.ts +44 -0
- package/lib/typescript/core/utils.d.ts.map +1 -0
- package/lib/typescript/core/warnings.d.ts +2 -0
- package/lib/typescript/core/warnings.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +8 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/native/DigitSlot.d.ts +27 -0
- package/lib/typescript/native/DigitSlot.d.ts.map +1 -0
- package/lib/typescript/native/NumberFlow.d.ts +3 -0
- package/lib/typescript/native/NumberFlow.d.ts.map +1 -0
- package/lib/typescript/native/SymbolSlot.d.ts +19 -0
- package/lib/typescript/native/SymbolSlot.d.ts.map +1 -0
- package/lib/typescript/native/TimeFlow.d.ts +3 -0
- package/lib/typescript/native/TimeFlow.d.ts.map +1 -0
- package/lib/typescript/native/index.d.ts +3 -0
- package/lib/typescript/native/index.d.ts.map +1 -0
- package/lib/typescript/native/renderSlots.d.ts +31 -0
- package/lib/typescript/native/renderSlots.d.ts.map +1 -0
- package/lib/typescript/native/types.d.ts +36 -0
- package/lib/typescript/native/types.d.ts.map +1 -0
- package/lib/typescript/native/useMeasuredGlyphMetrics.d.ts +8 -0
- package/lib/typescript/native/useMeasuredGlyphMetrics.d.ts.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/skia/DigitSlot.d.ts +35 -0
- package/lib/typescript/skia/DigitSlot.d.ts.map +1 -0
- package/lib/typescript/skia/SkiaNumberFlow.d.ts +3 -0
- package/lib/typescript/skia/SkiaNumberFlow.d.ts.map +1 -0
- package/lib/typescript/skia/SkiaTimeFlow.d.ts +3 -0
- package/lib/typescript/skia/SkiaTimeFlow.d.ts.map +1 -0
- package/lib/typescript/skia/SymbolSlot.d.ts +26 -0
- package/lib/typescript/skia/SymbolSlot.d.ts.map +1 -0
- package/lib/typescript/skia/index.d.ts +6 -0
- package/lib/typescript/skia/index.d.ts.map +1 -0
- package/lib/typescript/skia/renderSlots.d.ts +40 -0
- package/lib/typescript/skia/renderSlots.d.ts.map +1 -0
- package/lib/typescript/skia/useGlyphMetrics.d.ts +16 -0
- package/lib/typescript/skia/useGlyphMetrics.d.ts.map +1 -0
- package/lib/typescript/skia/useScrubbing.d.ts +59 -0
- package/lib/typescript/skia/useScrubbing.d.ts.map +1 -0
- package/lib/typescript/skia/useSkiaFont.d.ts +13 -0
- package/lib/typescript/skia/useSkiaFont.d.ts.map +1 -0
- package/package.json +104 -0
- package/src/core/constants.ts +20 -0
- package/src/core/intlHelpers.ts +351 -0
- package/src/core/layout.ts +108 -0
- package/src/core/mask.ts +72 -0
- package/src/core/numerals/detection.ts +112 -0
- package/src/core/numerals/digits.ts +102 -0
- package/src/core/numerals/index.ts +9 -0
- package/src/core/numerals/tables.ts +112 -0
- package/src/core/superscript.ts +27 -0
- package/src/core/timeLayout.ts +119 -0
- package/src/core/timeTypes.ts +88 -0
- package/src/core/timing.ts +60 -0
- package/src/core/types.ts +189 -0
- package/src/core/useAccessibilityAnnouncement.ts +27 -0
- package/src/core/useAnimatedX.ts +30 -0
- package/src/core/useAnimationLifecycle.ts +54 -0
- package/src/core/useCanAnimate.ts +21 -0
- package/src/core/useContinuousSpin.ts +112 -0
- package/src/core/useDebouncedWidths.ts +93 -0
- package/src/core/useDigitAnimation.ts +192 -0
- package/src/core/useFlowPipeline.ts +126 -0
- package/src/core/useFormattedValue.ts +32 -0
- package/src/core/useLayoutDiff.ts +71 -0
- package/src/core/useNumberFormatting.ts +164 -0
- package/src/core/useSlotOpacity.ts +66 -0
- package/src/core/useTimeFormatting.ts +95 -0
- package/src/core/useTimingResolution.ts +47 -0
- package/src/core/useWorkletFormatting.ts +59 -0
- package/src/core/utils.ts +149 -0
- package/src/core/warnings.ts +8 -0
- package/src/index.ts +15 -0
- package/src/native/DigitSlot.tsx +203 -0
- package/src/native/NumberFlow.tsx +287 -0
- package/src/native/SymbolSlot.tsx +68 -0
- package/src/native/TimeFlow.tsx +287 -0
- package/src/native/index.ts +2 -0
- package/src/native/renderSlots.tsx +150 -0
- package/src/native/types.ts +40 -0
- package/src/native/useMeasuredGlyphMetrics.tsx +205 -0
- package/src/skia/DigitSlot.tsx +221 -0
- package/src/skia/SkiaNumberFlow.tsx +506 -0
- package/src/skia/SkiaTimeFlow.tsx +257 -0
- package/src/skia/SymbolSlot.tsx +120 -0
- package/src/skia/index.ts +5 -0
- package/src/skia/renderSlots.tsx +180 -0
- package/src/skia/useGlyphMetrics.ts +79 -0
- package/src/skia/useScrubbing.ts +223 -0
- 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
|