@yahoo/uds-mobile 2.24.0-beta.5 → 2.24.0-beta.7
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 -1
- package/dist/components/Avatar/AvatarIcon.cjs +2 -2
- package/dist/components/Avatar/AvatarIcon.js +2 -2
- package/dist/components/Avatar/AvatarIcon.js.map +1 -1
- package/dist/components/Avatar/AvatarImage.cjs +2 -2
- package/dist/components/Avatar/AvatarImage.js +2 -2
- package/dist/components/Avatar/AvatarImage.js.map +1 -1
- package/dist/components/Avatar/AvatarText.cjs +2 -2
- package/dist/components/Avatar/AvatarText.js +2 -2
- package/dist/components/Avatar/AvatarText.js.map +1 -1
- package/dist/components/Avatar/useAvatarFontScale.cjs +16 -0
- package/dist/components/Avatar/useAvatarFontScale.d.cts +7 -0
- package/dist/components/Avatar/useAvatarFontScale.d.cts.map +1 -0
- package/dist/components/Avatar/useAvatarFontScale.d.ts +7 -0
- package/dist/components/Avatar/useAvatarFontScale.d.ts.map +1 -0
- package/dist/components/Avatar/useAvatarFontScale.js +16 -0
- package/dist/components/Avatar/useAvatarFontScale.js.map +1 -0
- package/dist/components/Button/buttonTheme.cjs +10 -6
- package/dist/components/Button/buttonTheme.d.cts +10 -5
- package/dist/components/Button/buttonTheme.d.cts.map +1 -1
- package/dist/components/Button/buttonTheme.d.ts +10 -5
- package/dist/components/Button/buttonTheme.d.ts.map +1 -1
- package/dist/components/Button/buttonTheme.js +10 -6
- package/dist/components/Button/buttonTheme.js.map +1 -1
- package/dist/components/Button.cjs +17 -8
- package/dist/components/Button.js +17 -8
- package/dist/components/Button.js.map +1 -1
- package/dist/components/Checkbox.cjs +4 -2
- package/dist/components/Checkbox.js +4 -2
- package/dist/components/Checkbox.js.map +1 -1
- package/dist/components/Chip/ChipBase.cjs +22 -19
- package/dist/components/Chip/ChipBase.js +22 -19
- package/dist/components/Chip/ChipBase.js.map +1 -1
- package/dist/components/Icon.cjs +3 -2
- package/dist/components/Icon.d.cts.map +1 -1
- package/dist/components/Icon.d.ts.map +1 -1
- package/dist/components/Icon.js +3 -2
- package/dist/components/Icon.js.map +1 -1
- package/dist/components/Input.cjs +1 -1
- package/dist/components/Input.js +1 -1
- package/dist/components/Input.js.map +1 -1
- package/dist/components/Link.cjs +10 -1
- package/dist/components/Link.d.cts +6 -0
- package/dist/components/Link.d.cts.map +1 -1
- package/dist/components/Link.d.ts +6 -0
- package/dist/components/Link.d.ts.map +1 -1
- package/dist/components/Link.js +10 -1
- package/dist/components/Link.js.map +1 -1
- package/dist/components/Pagination/Pagination.cjs +18 -10
- package/dist/components/Pagination/Pagination.d.cts +2 -0
- package/dist/components/Pagination/Pagination.d.cts.map +1 -1
- package/dist/components/Pagination/Pagination.d.ts +2 -0
- package/dist/components/Pagination/Pagination.d.ts.map +1 -1
- package/dist/components/Pagination/Pagination.js +18 -10
- package/dist/components/Pagination/Pagination.js.map +1 -1
- package/dist/components/Pagination/PaginationEllipsis.cjs +8 -2
- package/dist/components/Pagination/PaginationEllipsis.d.cts.map +1 -1
- package/dist/components/Pagination/PaginationEllipsis.d.ts.map +1 -1
- package/dist/components/Pagination/PaginationEllipsis.js +9 -3
- package/dist/components/Pagination/PaginationEllipsis.js.map +1 -1
- package/dist/components/Pagination/PaginationItem.cjs +2 -1
- package/dist/components/Pagination/PaginationItem.js +2 -1
- package/dist/components/Pagination/PaginationItem.js.map +1 -1
- package/dist/components/Pagination/PaginationLink.cjs +2 -1
- package/dist/components/Pagination/PaginationLink.js +2 -1
- package/dist/components/Pagination/PaginationLink.js.map +1 -1
- package/dist/components/Pagination/PaginationNext.cjs +3 -1
- package/dist/components/Pagination/PaginationNext.js +3 -1
- package/dist/components/Pagination/PaginationNext.js.map +1 -1
- package/dist/components/Pagination/PaginationPrev.cjs +3 -1
- package/dist/components/Pagination/PaginationPrev.js +3 -1
- package/dist/components/Pagination/PaginationPrev.js.map +1 -1
- package/dist/components/Pagination/paginationContext.d.cts +2 -0
- package/dist/components/Pagination/paginationContext.d.cts.map +1 -1
- package/dist/components/Pagination/paginationContext.d.ts +2 -0
- package/dist/components/Pagination/paginationContext.d.ts.map +1 -1
- package/dist/components/Pagination/paginationContext.js.map +1 -1
- package/dist/components/Pagination/usePaginationControlStyles.cjs +8 -1
- package/dist/components/Pagination/usePaginationControlStyles.d.cts.map +1 -1
- package/dist/components/Pagination/usePaginationControlStyles.d.ts.map +1 -1
- package/dist/components/Pagination/usePaginationControlStyles.js +9 -2
- package/dist/components/Pagination/usePaginationControlStyles.js.map +1 -1
- package/dist/components/Radio/Radio.cjs +2 -1
- package/dist/components/Radio/Radio.js +2 -1
- package/dist/components/Radio/Radio.js.map +1 -1
- package/dist/components/Select/Select.cjs +16 -4
- package/dist/components/Select/Select.js +16 -4
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/SelectContent.cjs +31 -23
- package/dist/components/Select/SelectContent.d.cts.map +1 -1
- package/dist/components/Select/SelectContent.d.ts.map +1 -1
- package/dist/components/Select/SelectContent.js +32 -24
- package/dist/components/Select/SelectContent.js.map +1 -1
- package/dist/components/Select/SelectItem.cjs +6 -6
- package/dist/components/Select/SelectItem.js +6 -6
- package/dist/components/Select/SelectItem.js.map +1 -1
- package/dist/components/Select/SelectTrigger.cjs +1 -1
- package/dist/components/Select/SelectTrigger.js +1 -1
- package/dist/components/Select/SelectTrigger.js.map +1 -1
- package/dist/components/Switch.cjs +4 -2
- package/dist/components/Switch.js +4 -2
- package/dist/components/Switch.js.map +1 -1
- package/dist/components/Tabs/Tab.cjs +1 -1
- package/dist/components/Tabs/Tab.js +1 -1
- package/dist/components/Tabs/Tab.js.map +1 -1
- package/dist/fontScaling/androidNonlinearFontScaling.cjs +174 -0
- package/dist/fontScaling/androidNonlinearFontScaling.d.cts +7 -0
- package/dist/fontScaling/androidNonlinearFontScaling.d.cts.map +1 -0
- package/dist/fontScaling/androidNonlinearFontScaling.d.ts +7 -0
- package/dist/fontScaling/androidNonlinearFontScaling.d.ts.map +1 -0
- package/dist/fontScaling/androidNonlinearFontScaling.js +175 -0
- package/dist/fontScaling/androidNonlinearFontScaling.js.map +1 -0
- package/dist/fontScaling/constants.cjs +4 -3
- package/dist/fontScaling/constants.d.cts +4 -3
- package/dist/fontScaling/constants.d.cts.map +1 -1
- package/dist/fontScaling/constants.d.ts +4 -3
- package/dist/fontScaling/constants.d.ts.map +1 -1
- package/dist/fontScaling/constants.js +4 -3
- package/dist/fontScaling/constants.js.map +1 -1
- package/dist/fontScaling/types.d.cts +3 -2
- package/dist/fontScaling/types.d.cts.map +1 -1
- package/dist/fontScaling/types.d.ts +3 -2
- package/dist/fontScaling/types.d.ts.map +1 -1
- package/dist/fontScaling/useFontScale.cjs +13 -4
- package/dist/fontScaling/useFontScale.d.cts +8 -3
- package/dist/fontScaling/useFontScale.d.cts.map +1 -1
- package/dist/fontScaling/useFontScale.d.ts +8 -3
- package/dist/fontScaling/useFontScale.d.ts.map +1 -1
- package/dist/fontScaling/useFontScale.js +14 -5
- package/dist/fontScaling/useFontScale.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
`@yahoo/uds-mobile` brings UDS to React Native. It provides:
|
|
20
20
|
|
|
21
|
-
- **Pre-built Components**: Avatar, Badge, Button, Checkbox, Chip, Divider, Icon, IconButton, Image, Input, InputHelpText, Link, Radio, RadioGroup, Switch, Tabs, Text, and layout primitives (Box, VStack, HStack, Screen)
|
|
21
|
+
- **Pre-built Components**: Avatar, Badge, Button, Checkbox, Chip, Divider, Icon, IconButton, Image, Input, InputHelpText, Link, Pagination, Radio, RadioGroup, Select, Switch, Tabs, Text, and layout primitives (Box, VStack, HStack, Screen)
|
|
22
22
|
- **Theming**: Full light/dark mode support with automatic system preference detection
|
|
23
23
|
- **Design Token Integration**: Colors, typography, spacing, and motion configs synced from UDS tokens
|
|
24
24
|
- **Animations**: Smooth, physics-based animations using Reanimated with motion parity to web
|
|
@@ -505,6 +505,11 @@ See the [Accessibility and text scaling guide](https://uds.build/docs/mobile/acc
|
|
|
505
505
|
default cap table, per-component overrides, custom-component hooks, known limits, and device test
|
|
506
506
|
matrix.
|
|
507
507
|
|
|
508
|
+
When upgrading from manual scaling workarounds, remove app-level geometry multiplication only after
|
|
509
|
+
comparing the screen with UDS defaults; applying both can make controls oversized. App-owned
|
|
510
|
+
composite controls should use `useFontScale(cap, baseSize)`. As a temporary rollback,
|
|
511
|
+
`fontScaling={{ enabled: false }}` restores unrestricted native scaling.
|
|
512
|
+
|
|
508
513
|
## Fonts
|
|
509
514
|
|
|
510
515
|
### Font Handling
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
require("../../_virtual/_rolldown/runtime.cjs");
|
|
4
4
|
const require_components_Box = require("../Box.cjs");
|
|
5
|
-
const require_fontScaling_useFontScale = require("../../fontScaling/useFontScale.cjs");
|
|
6
5
|
const require_fontScaling_useMaxFontSizeMultiplier = require("../../fontScaling/useMaxFontSizeMultiplier.cjs");
|
|
7
6
|
const require_components_IconSlot = require("../IconSlot.cjs");
|
|
7
|
+
const require_components_Avatar_useAvatarFontScale = require("./useAvatarFontScale.cjs");
|
|
8
8
|
const require_components_Avatar_utils = require("./utils.cjs");
|
|
9
9
|
let react = require("react");
|
|
10
10
|
let react_native = require("react-native");
|
|
@@ -13,11 +13,11 @@ let generated_styles = require("../../../generated/styles");
|
|
|
13
13
|
//#region src/components/Avatar/AvatarIcon.tsx
|
|
14
14
|
const AvatarIcon = (0, react.memo)(function AvatarIcon({ size = "md", variant = require_components_Avatar_utils.DEFAULT_AVATAR_VARIANT, icon, customSize, maxFontSizeMultiplier, style, accessibilityLabel, alt, ref, ...viewProps }) {
|
|
15
15
|
const resolvedMaxFontSizeMultiplier = require_fontScaling_useMaxFontSizeMultiplier.useMaxFontSizeMultiplier("control", maxFontSizeMultiplier) ?? 0;
|
|
16
|
-
const fontScaleFactor = require_fontScaling_useFontScale.useFontScale(resolvedMaxFontSizeMultiplier);
|
|
17
16
|
generated_styles.avatarStyles.useVariants({
|
|
18
17
|
size,
|
|
19
18
|
icon: variant
|
|
20
19
|
});
|
|
20
|
+
const fontScaleFactor = require_components_Avatar_useAvatarFontScale.useAvatarFontScale(resolvedMaxFontSizeMultiplier);
|
|
21
21
|
const rootStyle = (0, react.useMemo)(() => [
|
|
22
22
|
generated_styles.avatarStyles.root,
|
|
23
23
|
avatarStaticStyles.root,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
2
|
import { Box } from "../Box.js";
|
|
3
|
-
import { useFontScale } from "../../fontScaling/useFontScale.js";
|
|
4
3
|
import { useMaxFontSizeMultiplier } from "../../fontScaling/useMaxFontSizeMultiplier.js";
|
|
5
4
|
import { IconSlot } from "../IconSlot.js";
|
|
5
|
+
import { useAvatarFontScale } from "./useAvatarFontScale.js";
|
|
6
6
|
import { DEFAULT_AVATAR_VARIANT, getAvatarSizeStyle, getDefaultAvatarIcon } from "./utils.js";
|
|
7
7
|
import { memo, useMemo } from "react";
|
|
8
8
|
import { StyleSheet } from "react-native";
|
|
@@ -11,11 +11,11 @@ import { avatarStyles } from "../../../generated/styles";
|
|
|
11
11
|
//#region src/components/Avatar/AvatarIcon.tsx
|
|
12
12
|
const AvatarIcon = memo(function AvatarIcon({ size = "md", variant = DEFAULT_AVATAR_VARIANT, icon, customSize, maxFontSizeMultiplier, style, accessibilityLabel, alt, ref, ...viewProps }) {
|
|
13
13
|
const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier("control", maxFontSizeMultiplier) ?? 0;
|
|
14
|
-
const fontScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier);
|
|
15
14
|
avatarStyles.useVariants({
|
|
16
15
|
size,
|
|
17
16
|
icon: variant
|
|
18
17
|
});
|
|
18
|
+
const fontScaleFactor = useAvatarFontScale(resolvedMaxFontSizeMultiplier);
|
|
19
19
|
const rootStyle = useMemo(() => [
|
|
20
20
|
avatarStyles.root,
|
|
21
21
|
avatarStaticStyles.root,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AvatarIcon.js","names":[],"sources":["../../../src/components/Avatar/AvatarIcon.tsx"],"sourcesContent":["import { memo, useMemo } from 'react';\nimport { StyleSheet } from 'react-native';\n\nimport { avatarStyles } from '../../../generated/styles';\nimport {
|
|
1
|
+
{"version":3,"file":"AvatarIcon.js","names":[],"sources":["../../../src/components/Avatar/AvatarIcon.tsx"],"sourcesContent":["import { memo, useMemo } from 'react';\nimport { StyleSheet } from 'react-native';\n\nimport { avatarStyles } from '../../../generated/styles';\nimport { useMaxFontSizeMultiplier } from '../../fontScaling/useMaxFontSizeMultiplier';\nimport { Box } from '../Box';\nimport { IconSlot } from '../IconSlot';\nimport type { AvatarIconProps } from './types';\nimport { useAvatarFontScale } from './useAvatarFontScale';\nimport {\n DEFAULT_AVATAR_SIZE,\n DEFAULT_AVATAR_VARIANT,\n getAvatarSizeStyle,\n getDefaultAvatarIcon,\n} from './utils';\n\nconst AvatarIcon = memo(function AvatarIcon({\n size = DEFAULT_AVATAR_SIZE,\n variant = DEFAULT_AVATAR_VARIANT,\n icon,\n customSize,\n maxFontSizeMultiplier,\n style,\n accessibilityLabel,\n alt,\n ref,\n ...viewProps\n}: AvatarIconProps) {\n const resolvedMaxFontSizeMultiplier =\n useMaxFontSizeMultiplier('control', maxFontSizeMultiplier) ?? 0;\n avatarStyles.useVariants({ size, icon: variant });\n const fontScaleFactor = useAvatarFontScale(resolvedMaxFontSizeMultiplier);\n\n const rootStyle = useMemo(\n () => [\n avatarStyles.root,\n avatarStaticStyles.root,\n getAvatarSizeStyle(avatarStyles.root, customSize, fontScaleFactor),\n style,\n ],\n [avatarStyles.root, customSize, fontScaleFactor, style],\n );\n\n return (\n <Box\n ref={ref}\n accessibilityRole=\"image\"\n accessibilityLabel={accessibilityLabel ?? alt}\n {...viewProps}\n style={rootStyle}\n >\n <IconSlot\n icon={icon ?? getDefaultAvatarIcon(variant)}\n variant=\"fill\"\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={avatarStyles.icon}\n />\n </Box>\n );\n});\n\nAvatarIcon.displayName = 'AvatarIcon';\n\nconst avatarStaticStyles = StyleSheet.create({\n root: {\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'hidden',\n },\n});\n\nexport { AvatarIcon };\n"],"mappings":";;;;;;;;;;;AAgBA,MAAM,aAAa,KAAK,SAAS,WAAW,EAC1C,OAAA,MACA,UAAU,wBACV,MACA,YACA,uBACA,OACA,oBACA,KACA,KACA,GAAG,aACe;CAClB,MAAM,gCACJ,yBAAyB,WAAW,sBAAsB,IAAI;CAChE,aAAa,YAAY;EAAE;EAAM,MAAM;EAAS,CAAC;CACjD,MAAM,kBAAkB,mBAAmB,8BAA8B;CAEzE,MAAM,YAAY,cACV;EACJ,aAAa;EACb,mBAAmB;EACnB,mBAAmB,aAAa,MAAM,YAAY,gBAAgB;EAClE;EACD,EACD;EAAC,aAAa;EAAM;EAAY;EAAiB;EAAM,CACxD;CAED,OACE,oBAAC,KAAD;EACO;EACL,mBAAkB;EAClB,oBAAoB,sBAAsB;EAC1C,GAAI;EACJ,OAAO;YAEP,oBAAC,UAAD;GACE,MAAM,QAAQ,qBAAqB,QAAQ;GAC3C,SAAQ;GACR,uBAAuB;GACvB,OAAO,aAAa;GACpB,CAAA;EACE,CAAA;EAER;AAEF,WAAW,cAAc;AAEzB,MAAM,qBAAqB,WAAW,OAAO,EAC3C,MAAM;CACJ,YAAY;CACZ,gBAAgB;CAChB,UAAU;CACX,EACF,CAAC"}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
require("../../_virtual/_rolldown/runtime.cjs");
|
|
4
4
|
const require_components_Box = require("../Box.cjs");
|
|
5
|
-
const require_fontScaling_useFontScale = require("../../fontScaling/useFontScale.cjs");
|
|
6
5
|
const require_fontScaling_useMaxFontSizeMultiplier = require("../../fontScaling/useMaxFontSizeMultiplier.cjs");
|
|
6
|
+
const require_components_Avatar_useAvatarFontScale = require("./useAvatarFontScale.cjs");
|
|
7
7
|
const require_components_Avatar_utils = require("./utils.cjs");
|
|
8
8
|
const require_components_Avatar_AvatarIcon = require("./AvatarIcon.cjs");
|
|
9
9
|
const require_components_Avatar_AvatarText = require("./AvatarText.cjs");
|
|
@@ -14,13 +14,13 @@ let generated_styles = require("../../../generated/styles");
|
|
|
14
14
|
//#region src/components/Avatar/AvatarImage.tsx
|
|
15
15
|
const AvatarImage = (0, react.memo)(function AvatarImage({ size = "md", variant = require_components_Avatar_utils.DEFAULT_AVATAR_VARIANT, src, alt, fallback, icon, abbreviationStrategy, customSize, maxFontSizeMultiplier, style, accessibilityLabel, ref, ...viewProps }) {
|
|
16
16
|
const resolvedMaxFontSizeMultiplier = require_fontScaling_useMaxFontSizeMultiplier.useMaxFontSizeMultiplier("control", maxFontSizeMultiplier) ?? 0;
|
|
17
|
-
const fontScaleFactor = require_fontScaling_useFontScale.useFontScale(resolvedMaxFontSizeMultiplier);
|
|
18
17
|
const [imageError, setImageError] = (0, react.useState)(false);
|
|
19
18
|
const shouldShowImage = Boolean(src) && !imageError;
|
|
20
19
|
generated_styles.avatarStyles.useVariants({
|
|
21
20
|
size,
|
|
22
21
|
image: variant
|
|
23
22
|
});
|
|
23
|
+
const fontScaleFactor = require_components_Avatar_useAvatarFontScale.useAvatarFontScale(resolvedMaxFontSizeMultiplier);
|
|
24
24
|
const rootStyle = (0, react.useMemo)(() => [
|
|
25
25
|
generated_styles.avatarStyles.root,
|
|
26
26
|
avatarStaticStyles.root,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
2
|
import { Box } from "../Box.js";
|
|
3
|
-
import { useFontScale } from "../../fontScaling/useFontScale.js";
|
|
4
3
|
import { useMaxFontSizeMultiplier } from "../../fontScaling/useMaxFontSizeMultiplier.js";
|
|
4
|
+
import { useAvatarFontScale } from "./useAvatarFontScale.js";
|
|
5
5
|
import { DEFAULT_AVATAR_VARIANT, getAvatarSizeStyle } from "./utils.js";
|
|
6
6
|
import { AvatarIcon } from "./AvatarIcon.js";
|
|
7
7
|
import { AvatarText } from "./AvatarText.js";
|
|
@@ -12,13 +12,13 @@ import { avatarStyles } from "../../../generated/styles";
|
|
|
12
12
|
//#region src/components/Avatar/AvatarImage.tsx
|
|
13
13
|
const AvatarImage = memo(function AvatarImage({ size = "md", variant = DEFAULT_AVATAR_VARIANT, src, alt, fallback, icon, abbreviationStrategy, customSize, maxFontSizeMultiplier, style, accessibilityLabel, ref, ...viewProps }) {
|
|
14
14
|
const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier("control", maxFontSizeMultiplier) ?? 0;
|
|
15
|
-
const fontScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier);
|
|
16
15
|
const [imageError, setImageError] = useState(false);
|
|
17
16
|
const shouldShowImage = Boolean(src) && !imageError;
|
|
18
17
|
avatarStyles.useVariants({
|
|
19
18
|
size,
|
|
20
19
|
image: variant
|
|
21
20
|
});
|
|
21
|
+
const fontScaleFactor = useAvatarFontScale(resolvedMaxFontSizeMultiplier);
|
|
22
22
|
const rootStyle = useMemo(() => [
|
|
23
23
|
avatarStyles.root,
|
|
24
24
|
avatarStaticStyles.root,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AvatarImage.js","names":["RNImage"],"sources":["../../../src/components/Avatar/AvatarImage.tsx"],"sourcesContent":["import { memo, useMemo, useState } from 'react';\nimport { Image as RNImage, StyleSheet } from 'react-native';\n\nimport { avatarStyles } from '../../../generated/styles';\nimport {
|
|
1
|
+
{"version":3,"file":"AvatarImage.js","names":["RNImage"],"sources":["../../../src/components/Avatar/AvatarImage.tsx"],"sourcesContent":["import { memo, useMemo, useState } from 'react';\nimport { Image as RNImage, StyleSheet } from 'react-native';\n\nimport { avatarStyles } from '../../../generated/styles';\nimport { useMaxFontSizeMultiplier } from '../../fontScaling/useMaxFontSizeMultiplier';\nimport { Box } from '../Box';\nimport { AvatarIcon } from './AvatarIcon';\nimport { AvatarText } from './AvatarText';\nimport type { AvatarImageProps } from './types';\nimport { useAvatarFontScale } from './useAvatarFontScale';\nimport { DEFAULT_AVATAR_SIZE, DEFAULT_AVATAR_VARIANT, getAvatarSizeStyle } from './utils';\n\nconst AvatarImage = memo(function AvatarImage({\n size = DEFAULT_AVATAR_SIZE,\n variant = DEFAULT_AVATAR_VARIANT,\n src,\n alt,\n fallback,\n icon,\n abbreviationStrategy,\n customSize,\n maxFontSizeMultiplier,\n style,\n accessibilityLabel,\n ref,\n ...viewProps\n}: AvatarImageProps) {\n const resolvedMaxFontSizeMultiplier =\n useMaxFontSizeMultiplier('control', maxFontSizeMultiplier) ?? 0;\n const [imageError, setImageError] = useState(false);\n const shouldShowImage = Boolean(src) && !imageError;\n\n avatarStyles.useVariants({ size, image: variant });\n const fontScaleFactor = useAvatarFontScale(resolvedMaxFontSizeMultiplier);\n\n const rootStyle = useMemo(\n () => [\n avatarStyles.root,\n avatarStaticStyles.root,\n getAvatarSizeStyle(avatarStyles.root, customSize, fontScaleFactor),\n style,\n ],\n [avatarStyles.root, customSize, fontScaleFactor, style],\n );\n\n if (!shouldShowImage) {\n if (fallback || alt) {\n return (\n <AvatarText\n ref={ref}\n size={size}\n variant={variant}\n name={alt}\n initials={fallback}\n abbreviationStrategy={abbreviationStrategy}\n customSize={customSize}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n accessibilityLabel={accessibilityLabel ?? alt ?? fallback}\n style={style}\n {...viewProps}\n />\n );\n }\n\n return (\n <AvatarIcon\n ref={ref}\n size={size}\n variant={variant}\n icon={icon}\n customSize={customSize}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n accessibilityLabel={accessibilityLabel ?? alt}\n style={style}\n {...viewProps}\n />\n );\n }\n\n return (\n <Box\n ref={ref}\n accessibilityRole=\"image\"\n accessibilityLabel={accessibilityLabel ?? alt}\n {...viewProps}\n style={rootStyle}\n >\n <RNImage\n source={{ uri: src }}\n accessibilityLabel={alt}\n onError={() => setImageError(true)}\n style={avatarStaticStyles.image}\n />\n </Box>\n );\n});\n\nAvatarImage.displayName = 'AvatarImage';\n\nconst avatarStaticStyles = StyleSheet.create({\n root: {\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'hidden',\n },\n image: {\n height: '100%',\n width: '100%',\n },\n});\n\nexport { AvatarImage };\n"],"mappings":";;;;;;;;;;;;AAYA,MAAM,cAAc,KAAK,SAAS,YAAY,EAC5C,OAAA,MACA,UAAU,wBACV,KACA,KACA,UACA,MACA,sBACA,YACA,uBACA,OACA,oBACA,KACA,GAAG,aACgB;CACnB,MAAM,gCACJ,yBAAyB,WAAW,sBAAsB,IAAI;CAChE,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,kBAAkB,QAAQ,IAAI,IAAI,CAAC;CAEzC,aAAa,YAAY;EAAE;EAAM,OAAO;EAAS,CAAC;CAClD,MAAM,kBAAkB,mBAAmB,8BAA8B;CAEzE,MAAM,YAAY,cACV;EACJ,aAAa;EACb,mBAAmB;EACnB,mBAAmB,aAAa,MAAM,YAAY,gBAAgB;EAClE;EACD,EACD;EAAC,aAAa;EAAM;EAAY;EAAiB;EAAM,CACxD;CAED,IAAI,CAAC,iBAAiB;EACpB,IAAI,YAAY,KACd,OACE,oBAAC,YAAD;GACO;GACC;GACG;GACT,MAAM;GACN,UAAU;GACY;GACV;GACZ,uBAAuB;GACvB,oBAAoB,sBAAsB,OAAO;GAC1C;GACP,GAAI;GACJ,CAAA;EAIN,OACE,oBAAC,YAAD;GACO;GACC;GACG;GACH;GACM;GACZ,uBAAuB;GACvB,oBAAoB,sBAAsB;GACnC;GACP,GAAI;GACJ,CAAA;;CAIN,OACE,oBAAC,KAAD;EACO;EACL,mBAAkB;EAClB,oBAAoB,sBAAsB;EAC1C,GAAI;EACJ,OAAO;YAEP,oBAACA,OAAD;GACE,QAAQ,EAAE,KAAK,KAAK;GACpB,oBAAoB;GACpB,eAAe,cAAc,KAAK;GAClC,OAAO,mBAAmB;GAC1B,CAAA;EACE,CAAA;EAER;AAEF,YAAY,cAAc;AAE1B,MAAM,qBAAqB,WAAW,OAAO;CAC3C,MAAM;EACJ,YAAY;EACZ,gBAAgB;EAChB,UAAU;EACX;CACD,OAAO;EACL,QAAQ;EACR,OAAO;EACR;CACF,CAAC"}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
require("../../_virtual/_rolldown/runtime.cjs");
|
|
4
4
|
const require_components_Box = require("../Box.cjs");
|
|
5
|
-
const require_fontScaling_useFontScale = require("../../fontScaling/useFontScale.cjs");
|
|
6
5
|
const require_fontScaling_useMaxFontSizeMultiplier = require("../../fontScaling/useMaxFontSizeMultiplier.cjs");
|
|
7
6
|
const require_components_Text = require("../Text.cjs");
|
|
7
|
+
const require_components_Avatar_useAvatarFontScale = require("./useAvatarFontScale.cjs");
|
|
8
8
|
const require_components_Avatar_utils = require("./utils.cjs");
|
|
9
9
|
let react = require("react");
|
|
10
10
|
let react_native = require("react-native");
|
|
@@ -13,11 +13,11 @@ let generated_styles = require("../../../generated/styles");
|
|
|
13
13
|
//#region src/components/Avatar/AvatarText.tsx
|
|
14
14
|
const AvatarText = (0, react.memo)(function AvatarText({ size = "md", variant = require_components_Avatar_utils.DEFAULT_AVATAR_VARIANT, name, initials, abbreviationStrategy, customSize, maxFontSizeMultiplier, style, accessibilityLabel, ref, ...viewProps }) {
|
|
15
15
|
const resolvedMaxFontSizeMultiplier = require_fontScaling_useMaxFontSizeMultiplier.useMaxFontSizeMultiplier("control", maxFontSizeMultiplier) ?? 0;
|
|
16
|
-
const fontScaleFactor = require_fontScaling_useFontScale.useFontScale(resolvedMaxFontSizeMultiplier);
|
|
17
16
|
generated_styles.avatarStyles.useVariants({
|
|
18
17
|
size,
|
|
19
18
|
text: variant
|
|
20
19
|
});
|
|
20
|
+
const fontScaleFactor = require_components_Avatar_useAvatarFontScale.useAvatarFontScale(resolvedMaxFontSizeMultiplier);
|
|
21
21
|
const rootStyle = (0, react.useMemo)(() => [
|
|
22
22
|
generated_styles.avatarStyles.root,
|
|
23
23
|
avatarStaticStyles.root,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
2
|
import { Box } from "../Box.js";
|
|
3
|
-
import { useFontScale } from "../../fontScaling/useFontScale.js";
|
|
4
3
|
import { useMaxFontSizeMultiplier } from "../../fontScaling/useMaxFontSizeMultiplier.js";
|
|
5
4
|
import { Text as Text$1 } from "../Text.js";
|
|
5
|
+
import { useAvatarFontScale } from "./useAvatarFontScale.js";
|
|
6
6
|
import { DEFAULT_AVATAR_VARIANT, generateAvatarAbbreviation, getAvatarSizeStyle } from "./utils.js";
|
|
7
7
|
import { memo, useMemo } from "react";
|
|
8
8
|
import { StyleSheet } from "react-native";
|
|
@@ -11,11 +11,11 @@ import { avatarStyles } from "../../../generated/styles";
|
|
|
11
11
|
//#region src/components/Avatar/AvatarText.tsx
|
|
12
12
|
const AvatarText = memo(function AvatarText({ size = "md", variant = DEFAULT_AVATAR_VARIANT, name, initials, abbreviationStrategy, customSize, maxFontSizeMultiplier, style, accessibilityLabel, ref, ...viewProps }) {
|
|
13
13
|
const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier("control", maxFontSizeMultiplier) ?? 0;
|
|
14
|
-
const fontScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier);
|
|
15
14
|
avatarStyles.useVariants({
|
|
16
15
|
size,
|
|
17
16
|
text: variant
|
|
18
17
|
});
|
|
18
|
+
const fontScaleFactor = useAvatarFontScale(resolvedMaxFontSizeMultiplier);
|
|
19
19
|
const rootStyle = useMemo(() => [
|
|
20
20
|
avatarStyles.root,
|
|
21
21
|
avatarStaticStyles.root,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AvatarText.js","names":["Text"],"sources":["../../../src/components/Avatar/AvatarText.tsx"],"sourcesContent":["import { memo, useMemo } from 'react';\nimport { StyleSheet } from 'react-native';\n\nimport { avatarStyles } from '../../../generated/styles';\nimport {
|
|
1
|
+
{"version":3,"file":"AvatarText.js","names":["Text"],"sources":["../../../src/components/Avatar/AvatarText.tsx"],"sourcesContent":["import { memo, useMemo } from 'react';\nimport { StyleSheet } from 'react-native';\n\nimport { avatarStyles } from '../../../generated/styles';\nimport { useMaxFontSizeMultiplier } from '../../fontScaling/useMaxFontSizeMultiplier';\nimport { Box } from '../Box';\nimport { Text } from '../Text';\nimport type { AvatarTextProps } from './types';\nimport { useAvatarFontScale } from './useAvatarFontScale';\nimport {\n DEFAULT_AVATAR_SIZE,\n DEFAULT_AVATAR_VARIANT,\n generateAvatarAbbreviation,\n getAvatarSizeStyle,\n} from './utils';\n\nconst AvatarText = memo(function AvatarText({\n size = DEFAULT_AVATAR_SIZE,\n variant = DEFAULT_AVATAR_VARIANT,\n name,\n initials,\n abbreviationStrategy,\n customSize,\n maxFontSizeMultiplier,\n style,\n accessibilityLabel,\n ref,\n ...viewProps\n}: AvatarTextProps) {\n const resolvedMaxFontSizeMultiplier =\n useMaxFontSizeMultiplier('control', maxFontSizeMultiplier) ?? 0;\n avatarStyles.useVariants({ size, text: variant });\n const fontScaleFactor = useAvatarFontScale(resolvedMaxFontSizeMultiplier);\n\n const rootStyle = useMemo(\n () => [\n avatarStyles.root,\n avatarStaticStyles.root,\n getAvatarSizeStyle(avatarStyles.root, customSize, fontScaleFactor),\n style,\n ],\n [avatarStyles.root, customSize, fontScaleFactor, style],\n );\n\n return (\n <Box\n ref={ref}\n accessibilityRole=\"image\"\n accessibilityLabel={accessibilityLabel ?? name ?? initials}\n {...viewProps}\n style={rootStyle}\n >\n <Text\n color=\"inherit\"\n variant=\"inherit\"\n numberOfLines={1}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={avatarStyles.text}\n >\n {initials || generateAvatarAbbreviation(name, abbreviationStrategy)}\n </Text>\n </Box>\n );\n});\n\nAvatarText.displayName = 'AvatarText';\n\nconst avatarStaticStyles = StyleSheet.create({\n root: {\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'hidden',\n },\n});\n\nexport { AvatarText };\n"],"mappings":";;;;;;;;;;;AAgBA,MAAM,aAAa,KAAK,SAAS,WAAW,EAC1C,OAAA,MACA,UAAU,wBACV,MACA,UACA,sBACA,YACA,uBACA,OACA,oBACA,KACA,GAAG,aACe;CAClB,MAAM,gCACJ,yBAAyB,WAAW,sBAAsB,IAAI;CAChE,aAAa,YAAY;EAAE;EAAM,MAAM;EAAS,CAAC;CACjD,MAAM,kBAAkB,mBAAmB,8BAA8B;CAEzE,MAAM,YAAY,cACV;EACJ,aAAa;EACb,mBAAmB;EACnB,mBAAmB,aAAa,MAAM,YAAY,gBAAgB;EAClE;EACD,EACD;EAAC,aAAa;EAAM;EAAY;EAAiB;EAAM,CACxD;CAED,OACE,oBAAC,KAAD;EACO;EACL,mBAAkB;EAClB,oBAAoB,sBAAsB,QAAQ;EAClD,GAAI;EACJ,OAAO;YAEP,oBAACA,QAAD;GACE,OAAM;GACN,SAAQ;GACR,eAAe;GACf,uBAAuB;GACvB,OAAO,aAAa;aAEnB,YAAY,2BAA2B,MAAM,qBAAqB;GAC9D,CAAA;EACH,CAAA;EAER;AAEF,WAAW,cAAc;AAEzB,MAAM,qBAAqB,WAAW,OAAO,EAC3C,MAAM;CACJ,YAAY;CACZ,gBAAgB;CAChB,UAAU;CACX,EACF,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
require("../../_virtual/_rolldown/runtime.cjs");
|
|
4
|
+
const require_fontScaling_useFontScale = require("../../fontScaling/useFontScale.cjs");
|
|
5
|
+
let generated_styles = require("../../../generated/styles");
|
|
6
|
+
//#region src/components/Avatar/useAvatarFontScale.ts
|
|
7
|
+
/** Keep every avatar content type on one size-aware scale that contains text and icons. */
|
|
8
|
+
function useAvatarFontScale(maxFontSizeMultiplier) {
|
|
9
|
+
const textStyle = generated_styles.avatarStyles.text;
|
|
10
|
+
const iconStyle = generated_styles.avatarStyles.icon;
|
|
11
|
+
const textScaleFactor = require_fontScaling_useFontScale.useFontScale(maxFontSizeMultiplier, (typeof textStyle.lineHeight === "number" ? textStyle.lineHeight : void 0) ?? (typeof textStyle.fontSize === "number" ? textStyle.fontSize : void 0));
|
|
12
|
+
const iconScaleFactor = require_fontScaling_useFontScale.useFontScale(maxFontSizeMultiplier, (typeof iconStyle.fontSize === "number" ? iconStyle.fontSize : void 0) ?? (typeof iconStyle.lineHeight === "number" ? iconStyle.lineHeight : void 0));
|
|
13
|
+
return Math.max(textScaleFactor, iconScaleFactor);
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
exports.useAvatarFontScale = useAvatarFontScale;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/components/Avatar/useAvatarFontScale.d.ts
|
|
3
|
+
/** Keep every avatar content type on one size-aware scale that contains text and icons. */
|
|
4
|
+
declare function useAvatarFontScale(maxFontSizeMultiplier: number): number;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { useAvatarFontScale };
|
|
7
|
+
//# sourceMappingURL=useAvatarFontScale.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAvatarFontScale.d.cts","names":[],"sources":["../../../src/components/Avatar/useAvatarFontScale.ts"],"mappings":";;;iBAMS,kBAAA,CAAmB,qBAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/components/Avatar/useAvatarFontScale.d.ts
|
|
3
|
+
/** Keep every avatar content type on one size-aware scale that contains text and icons. */
|
|
4
|
+
declare function useAvatarFontScale(maxFontSizeMultiplier: number): number;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { useAvatarFontScale };
|
|
7
|
+
//# sourceMappingURL=useAvatarFontScale.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAvatarFontScale.d.ts","names":[],"sources":["../../../src/components/Avatar/useAvatarFontScale.ts"],"mappings":";;;iBAMS,kBAAA,CAAmB,qBAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
|
+
import { useFontScale } from "../../fontScaling/useFontScale.js";
|
|
3
|
+
import { avatarStyles } from "../../../generated/styles";
|
|
4
|
+
//#region src/components/Avatar/useAvatarFontScale.ts
|
|
5
|
+
/** Keep every avatar content type on one size-aware scale that contains text and icons. */
|
|
6
|
+
function useAvatarFontScale(maxFontSizeMultiplier) {
|
|
7
|
+
const textStyle = avatarStyles.text;
|
|
8
|
+
const iconStyle = avatarStyles.icon;
|
|
9
|
+
const textScaleFactor = useFontScale(maxFontSizeMultiplier, (typeof textStyle.lineHeight === "number" ? textStyle.lineHeight : void 0) ?? (typeof textStyle.fontSize === "number" ? textStyle.fontSize : void 0));
|
|
10
|
+
const iconScaleFactor = useFontScale(maxFontSizeMultiplier, (typeof iconStyle.fontSize === "number" ? iconStyle.fontSize : void 0) ?? (typeof iconStyle.lineHeight === "number" ? iconStyle.lineHeight : void 0));
|
|
11
|
+
return Math.max(textScaleFactor, iconScaleFactor);
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { useAvatarFontScale };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=useAvatarFontScale.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAvatarFontScale.js","names":[],"sources":["../../../src/components/Avatar/useAvatarFontScale.ts"],"sourcesContent":["import type { TextStyle } from 'react-native';\n\nimport { avatarStyles } from '../../../generated/styles';\nimport { useFontScale } from '../../fontScaling/useFontScale';\n\n/** Keep every avatar content type on one size-aware scale that contains text and icons. */\nfunction useAvatarFontScale(maxFontSizeMultiplier: number): number {\n const textStyle = avatarStyles.text as TextStyle;\n const iconStyle = avatarStyles.icon as TextStyle;\n const textScaleFactor = useFontScale(\n maxFontSizeMultiplier,\n (typeof textStyle.lineHeight === 'number' ? textStyle.lineHeight : undefined) ??\n (typeof textStyle.fontSize === 'number' ? textStyle.fontSize : undefined),\n );\n const iconScaleFactor = useFontScale(\n maxFontSizeMultiplier,\n (typeof iconStyle.fontSize === 'number' ? iconStyle.fontSize : undefined) ??\n (typeof iconStyle.lineHeight === 'number' ? iconStyle.lineHeight : undefined),\n );\n\n return Math.max(textScaleFactor, iconScaleFactor);\n}\n\nexport { useAvatarFontScale };\n"],"mappings":";;;;;AAMA,SAAS,mBAAmB,uBAAuC;CACjE,MAAM,YAAY,aAAa;CAC/B,MAAM,YAAY,aAAa;CAC/B,MAAM,kBAAkB,aACtB,wBACC,OAAO,UAAU,eAAe,WAAW,UAAU,aAAa,KAAA,OAChE,OAAO,UAAU,aAAa,WAAW,UAAU,WAAW,KAAA,GAClE;CACD,MAAM,kBAAkB,aACtB,wBACC,OAAO,UAAU,aAAa,WAAW,UAAU,WAAW,KAAA,OAC5D,OAAO,UAAU,eAAe,WAAW,UAAU,aAAa,KAAA,GACtE;CAED,OAAO,KAAK,IAAI,iBAAiB,gBAAgB"}
|
|
@@ -47,10 +47,11 @@ function getSafeIconCell(fontSize) {
|
|
|
47
47
|
/**
|
|
48
48
|
* Safe native content metrics while retaining the token-derived target control height.
|
|
49
49
|
*
|
|
50
|
-
* `fontScaleFactor`
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
50
|
+
* `fontScaleFactor` pre-scales container metrics only. A single number keeps
|
|
51
|
+
* the legacy linear behavior; distinct label/icon factors let Android 14+
|
|
52
|
+
* geometry follow its size-dependent nonlinear font curve. The returned
|
|
53
|
+
* `labelLineHeight`/`iconCellHeight` stay unscaled because native Text applies
|
|
54
|
+
* the same cap.
|
|
54
55
|
*/
|
|
55
56
|
function getButtonControlMetrics(theme, size, fontScaleFactor = 1) {
|
|
56
57
|
const rootStyle = getLayerStyle(theme, buttonSizePath(size, "root"));
|
|
@@ -60,11 +61,14 @@ function getButtonControlMetrics(theme, size, fontScaleFactor = 1) {
|
|
|
60
61
|
const iconSize = getIconSize(iconStyle);
|
|
61
62
|
const fontSize = typeof textStyle.fontSize === "number" ? textStyle.fontSize : iconSize;
|
|
62
63
|
const tokenLineHeight = typeof textStyle.lineHeight === "number" ? textStyle.lineHeight : fontSize;
|
|
63
|
-
const
|
|
64
|
+
const labelScaleFactor = typeof fontScaleFactor === "number" ? fontScaleFactor : fontScaleFactor.label;
|
|
65
|
+
const iconScaleFactor = typeof fontScaleFactor === "number" ? fontScaleFactor : fontScaleFactor.icon;
|
|
66
|
+
const iconCellScaleFactor = typeof fontScaleFactor === "number" ? fontScaleFactor : fontScaleFactor.iconCell ?? fontScaleFactor.icon;
|
|
67
|
+
const targetContentHeight = Math.max(iconSize * iconScaleFactor, tokenLineHeight * labelScaleFactor);
|
|
64
68
|
const minHeight = Math.ceil(paddingVertical * 2 + targetContentHeight);
|
|
65
69
|
const labelLineHeight = getSafeLabelLineHeight(textStyle, iconSize);
|
|
66
70
|
const iconCellHeight = getSafeIconCell(iconSize);
|
|
67
|
-
const safeContentHeight = Math.max(labelLineHeight, iconCellHeight
|
|
71
|
+
const safeContentHeight = Math.max(labelLineHeight * labelScaleFactor, iconCellHeight * iconCellScaleFactor);
|
|
68
72
|
return {
|
|
69
73
|
minHeight,
|
|
70
74
|
labelLineHeight,
|
|
@@ -11,12 +11,17 @@ declare function getSafeIconCell(fontSize: number): number;
|
|
|
11
11
|
/**
|
|
12
12
|
* Safe native content metrics while retaining the token-derived target control height.
|
|
13
13
|
*
|
|
14
|
-
* `fontScaleFactor`
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* `fontScaleFactor` pre-scales container metrics only. A single number keeps
|
|
15
|
+
* the legacy linear behavior; distinct label/icon factors let Android 14+
|
|
16
|
+
* geometry follow its size-dependent nonlinear font curve. The returned
|
|
17
|
+
* `labelLineHeight`/`iconCellHeight` stay unscaled because native Text applies
|
|
18
|
+
* the same cap.
|
|
18
19
|
*/
|
|
19
|
-
declare function getButtonControlMetrics(theme: ButtonTheme, size: ButtonSize, fontScaleFactor?: number
|
|
20
|
+
declare function getButtonControlMetrics(theme: ButtonTheme, size: ButtonSize, fontScaleFactor?: number | {
|
|
21
|
+
label: number;
|
|
22
|
+
icon: number;
|
|
23
|
+
iconCell?: number;
|
|
24
|
+
}): {
|
|
20
25
|
minHeight: number;
|
|
21
26
|
labelLineHeight: number;
|
|
22
27
|
iconCellHeight: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buttonTheme.d.cts","names":[],"sources":["../../../src/components/Button/buttonTheme.ts"],"mappings":";;;;;KAKK,WAAA;EAEH,UAAA,EAAY,MAAA;AAAA;;iBA4DE,eAAA,CAAgB,QAAA;;;AAAhC;;;;;
|
|
1
|
+
{"version":3,"file":"buttonTheme.d.cts","names":[],"sources":["../../../src/components/Button/buttonTheme.ts"],"mappings":";;;;;KAKK,WAAA;EAEH,UAAA,EAAY,MAAA;AAAA;;iBA4DE,eAAA,CAAgB,QAAA;;;AAAhC;;;;;AAyBA;;iBAAgB,uBAAA,CACd,KAAA,EAAO,WAAA,EACP,IAAA,EAAM,UAAA,EACN,eAAA;EAA4B,KAAA;EAAe,IAAA;EAAc,QAAA;AAAA;EAEzD,SAAA;EACA,eAAA;EACA,cAAA;EACA,eAAA;AAAA;;;;;;;iBA4Cc,2BAAA,CACd,KAAA,EAAO,WAAA,EACP,IAAA,EAAM,cAAA,EACN,eAAA;EAEA,SAAA;EACA,cAAA;EACA,eAAA;EACA,iBAAA;AAAA;;iBA4Bc,kBAAA,CAAmB,SAAA,EAAW,SAAA,EAAW,eAAA,WAA0B,SAAA"}
|
|
@@ -11,12 +11,17 @@ declare function getSafeIconCell(fontSize: number): number;
|
|
|
11
11
|
/**
|
|
12
12
|
* Safe native content metrics while retaining the token-derived target control height.
|
|
13
13
|
*
|
|
14
|
-
* `fontScaleFactor`
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* `fontScaleFactor` pre-scales container metrics only. A single number keeps
|
|
15
|
+
* the legacy linear behavior; distinct label/icon factors let Android 14+
|
|
16
|
+
* geometry follow its size-dependent nonlinear font curve. The returned
|
|
17
|
+
* `labelLineHeight`/`iconCellHeight` stay unscaled because native Text applies
|
|
18
|
+
* the same cap.
|
|
18
19
|
*/
|
|
19
|
-
declare function getButtonControlMetrics(theme: ButtonTheme, size: ButtonSize, fontScaleFactor?: number
|
|
20
|
+
declare function getButtonControlMetrics(theme: ButtonTheme, size: ButtonSize, fontScaleFactor?: number | {
|
|
21
|
+
label: number;
|
|
22
|
+
icon: number;
|
|
23
|
+
iconCell?: number;
|
|
24
|
+
}): {
|
|
20
25
|
minHeight: number;
|
|
21
26
|
labelLineHeight: number;
|
|
22
27
|
iconCellHeight: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buttonTheme.d.ts","names":[],"sources":["../../../src/components/Button/buttonTheme.ts"],"mappings":";;;;;KAKK,WAAA;EAEH,UAAA,EAAY,MAAA;AAAA;;iBA4DE,eAAA,CAAgB,QAAA;;;AAAhC;;;;;
|
|
1
|
+
{"version":3,"file":"buttonTheme.d.ts","names":[],"sources":["../../../src/components/Button/buttonTheme.ts"],"mappings":";;;;;KAKK,WAAA;EAEH,UAAA,EAAY,MAAA;AAAA;;iBA4DE,eAAA,CAAgB,QAAA;;;AAAhC;;;;;AAyBA;;iBAAgB,uBAAA,CACd,KAAA,EAAO,WAAA,EACP,IAAA,EAAM,UAAA,EACN,eAAA;EAA4B,KAAA;EAAe,IAAA;EAAc,QAAA;AAAA;EAEzD,SAAA;EACA,eAAA;EACA,cAAA;EACA,eAAA;AAAA;;;;;;;iBA4Cc,2BAAA,CACd,KAAA,EAAO,WAAA,EACP,IAAA,EAAM,cAAA,EACN,eAAA;EAEA,SAAA;EACA,cAAA;EACA,eAAA;EACA,iBAAA;AAAA;;iBA4Bc,kBAAA,CAAmB,SAAA,EAAW,SAAA,EAAW,eAAA,WAA0B,SAAA"}
|
|
@@ -46,10 +46,11 @@ function getSafeIconCell(fontSize) {
|
|
|
46
46
|
/**
|
|
47
47
|
* Safe native content metrics while retaining the token-derived target control height.
|
|
48
48
|
*
|
|
49
|
-
* `fontScaleFactor`
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
49
|
+
* `fontScaleFactor` pre-scales container metrics only. A single number keeps
|
|
50
|
+
* the legacy linear behavior; distinct label/icon factors let Android 14+
|
|
51
|
+
* geometry follow its size-dependent nonlinear font curve. The returned
|
|
52
|
+
* `labelLineHeight`/`iconCellHeight` stay unscaled because native Text applies
|
|
53
|
+
* the same cap.
|
|
53
54
|
*/
|
|
54
55
|
function getButtonControlMetrics(theme, size, fontScaleFactor = 1) {
|
|
55
56
|
const rootStyle = getLayerStyle(theme, buttonSizePath(size, "root"));
|
|
@@ -59,11 +60,14 @@ function getButtonControlMetrics(theme, size, fontScaleFactor = 1) {
|
|
|
59
60
|
const iconSize = getIconSize(iconStyle);
|
|
60
61
|
const fontSize = typeof textStyle.fontSize === "number" ? textStyle.fontSize : iconSize;
|
|
61
62
|
const tokenLineHeight = typeof textStyle.lineHeight === "number" ? textStyle.lineHeight : fontSize;
|
|
62
|
-
const
|
|
63
|
+
const labelScaleFactor = typeof fontScaleFactor === "number" ? fontScaleFactor : fontScaleFactor.label;
|
|
64
|
+
const iconScaleFactor = typeof fontScaleFactor === "number" ? fontScaleFactor : fontScaleFactor.icon;
|
|
65
|
+
const iconCellScaleFactor = typeof fontScaleFactor === "number" ? fontScaleFactor : fontScaleFactor.iconCell ?? fontScaleFactor.icon;
|
|
66
|
+
const targetContentHeight = Math.max(iconSize * iconScaleFactor, tokenLineHeight * labelScaleFactor);
|
|
63
67
|
const minHeight = Math.ceil(paddingVertical * 2 + targetContentHeight);
|
|
64
68
|
const labelLineHeight = getSafeLabelLineHeight(textStyle, iconSize);
|
|
65
69
|
const iconCellHeight = getSafeIconCell(iconSize);
|
|
66
|
-
const safeContentHeight = Math.max(labelLineHeight, iconCellHeight
|
|
70
|
+
const safeContentHeight = Math.max(labelLineHeight * labelScaleFactor, iconCellHeight * iconCellScaleFactor);
|
|
67
71
|
return {
|
|
68
72
|
minHeight,
|
|
69
73
|
labelLineHeight,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buttonTheme.js","names":[],"sources":["../../../src/components/Button/buttonTheme.ts"],"sourcesContent":["import type { ButtonSize, IconButtonSize } from '@yahoo/uds-types';\nimport type { TextStyle, ViewStyle } from 'react-native';\n\nimport { fontMetrics } from '../../../generated/fontMetrics';\n\ntype ButtonTheme = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n components: Record<string, any>;\n};\n\nconst SHARED_BUTTON_ICONBUTTON_SIZES = new Set<IconButtonSize>(['xs', 'sm', 'md', 'lg']);\n// Covers common native font boxes (roughly 1.2em) plus pixel-rounding headroom.\nconst UNKNOWN_FONT_LINE_HEIGHT_RATIO = 1.3;\n\nfunction buttonSizePath(size: ButtonSize, layer: 'root' | 'icon' | 'rootText'): string {\n return `button/size/${size}/${layer}/rest`;\n}\n\nfunction iconButtonSizePath(size: IconButtonSize, layer: 'root' | 'icon'): string {\n return `iconButton/size/${size}/${layer}/rest`;\n}\n\nfunction getLayerStyle(theme: ButtonTheme, path: string): ViewStyle | TextStyle {\n const style = theme.components[path];\n return style && typeof style === 'object' ? style : {};\n}\n\nfunction getIconSize(iconStyle: TextStyle): number {\n return (\n (typeof iconStyle.fontSize === 'number' ? iconStyle.fontSize : undefined) ??\n (typeof iconStyle.lineHeight === 'number' ? iconStyle.lineHeight : undefined) ??\n 16\n );\n}\n\nfunction getVerticalPadding(rootStyle: ViewStyle): number {\n return (\n (typeof rootStyle.paddingVertical === 'number' ? rootStyle.paddingVertical : undefined) ??\n (typeof rootStyle.padding === 'number' ? rootStyle.padding : undefined) ??\n 0\n );\n}\n\nfunction getMetricHeight(value: number, unitsPerEm: number, fontSize: number): number {\n return Math.ceil((Math.max(0, value) / unitsPerEm) * fontSize);\n}\n\nfunction getSafeLabelLineHeight(textStyle: TextStyle, fallbackFontSize: number): number {\n const fontSize = typeof textStyle.fontSize === 'number' ? textStyle.fontSize : fallbackFontSize;\n const tokenLineHeight =\n typeof textStyle.lineHeight === 'number' ? textStyle.lineHeight : fontSize;\n const metrics =\n typeof textStyle.fontFamily === 'string' ? fontMetrics[textStyle.fontFamily] : undefined;\n\n if (!metrics || metrics.unitsPerEm <= 0) {\n return Math.max(tokenLineHeight, Math.ceil(fontSize * UNKNOWN_FONT_LINE_HEIGHT_RATIO));\n }\n\n const metricsLineHeight =\n getMetricHeight(metrics.ascent, metrics.unitsPerEm, fontSize) +\n getMetricHeight(-metrics.descent, metrics.unitsPerEm, fontSize) +\n getMetricHeight(metrics.lineGap, metrics.unitsPerEm, fontSize);\n\n return Math.max(tokenLineHeight, metricsLineHeight);\n}\n\n/** Safe line box for the icon font's em square plus any bbox overflow. */\nexport function getSafeIconCell(fontSize: number): number {\n const metrics = fontMetrics['uds-icons'];\n if (!metrics || metrics.unitsPerEm <= 0) {\n return Math.ceil(fontSize * UNKNOWN_FONT_LINE_HEIGHT_RATIO);\n }\n\n const overflowAboveEm = Math.max(0, metrics.bbox.maxY - metrics.unitsPerEm);\n const overflowBelowEm = Math.max(0, -metrics.bbox.minY);\n\n return (\n Math.ceil(fontSize) +\n getMetricHeight(overflowAboveEm, metrics.unitsPerEm, fontSize) +\n getMetricHeight(overflowBelowEm, metrics.unitsPerEm, fontSize)\n );\n}\n\n/**\n * Safe native content metrics while retaining the token-derived target control height.\n *\n * `fontScaleFactor` (the capped OS font scale, >= 1) pre-scales container\n * metrics only. The returned `labelLineHeight`/`iconCellHeight` stay unscaled\n * because they go into Text/Icon styles, which native multiplies by the same\n * capped factor — container math must mirror that, not double-apply it.\n */\nexport function getButtonControlMetrics(\n theme: ButtonTheme,\n size: ButtonSize,\n fontScaleFactor = 1,\n): {\n minHeight: number;\n labelLineHeight: number;\n iconCellHeight: number;\n paddingVertical: number;\n} {\n const rootStyle = getLayerStyle(theme, buttonSizePath(size, 'root')) as ViewStyle;\n const iconStyle = getLayerStyle(theme, buttonSizePath(size, 'icon')) as TextStyle;\n const textStyle = getLayerStyle(theme, buttonSizePath(size, 'rootText')) as TextStyle;\n const paddingVertical = getVerticalPadding(rootStyle);\n const iconSize = getIconSize(iconStyle);\n const fontSize = typeof textStyle.fontSize === 'number' ? textStyle.fontSize : iconSize;\n const tokenLineHeight =\n typeof textStyle.lineHeight === 'number' ? textStyle.lineHeight : fontSize;\n const targetContentHeight = Math.max(iconSize, tokenLineHeight) * fontScaleFactor;\n const minHeight = Math.ceil(paddingVertical * 2 + targetContentHeight);\n const labelLineHeight = getSafeLabelLineHeight(textStyle, iconSize);\n const iconCellHeight = getSafeIconCell(iconSize);\n const safeContentHeight = Math.max(labelLineHeight, iconCellHeight) * fontScaleFactor;\n\n return {\n minHeight,\n labelLineHeight,\n iconCellHeight,\n paddingVertical: Math.max(0, (minHeight - safeContentHeight) / 2),\n };\n}\n\n/**\n * Safe icon cell within the token-derived control size; explicit token height (match-button-height opt-in) wins as the target.\n *\n * `fontScaleFactor` behaves as in {@link getButtonControlMetrics}; an explicit\n * token height stays fixed, with padding absorbing the scaled content.\n */\nexport function getIconButtonControlMetrics(\n theme: ButtonTheme,\n size: IconButtonSize,\n fontScaleFactor = 1,\n): {\n minHeight: number;\n iconCellHeight: number;\n paddingVertical: number;\n hasExplicitHeight: boolean;\n} {\n const rootStyle = getLayerStyle(theme, iconButtonSizePath(size, 'root')) as ViewStyle;\n const iconStyle = getLayerStyle(theme, iconButtonSizePath(size, 'icon')) as TextStyle;\n const iconSize = getIconSize(iconStyle);\n const tokenLineHeight =\n typeof iconStyle.lineHeight === 'number' ? iconStyle.lineHeight : iconSize;\n const paddingVertical = getVerticalPadding(rootStyle);\n const explicitHeight = typeof rootStyle.height === 'number' ? rootStyle.height : undefined;\n const hasExplicitHeight =\n SHARED_BUTTON_ICONBUTTON_SIZES.has(size) && explicitHeight !== undefined;\n const minHeight = hasExplicitHeight\n ? (explicitHeight as number)\n : Math.ceil(paddingVertical * 2 + Math.max(iconSize, tokenLineHeight) * fontScaleFactor);\n const iconCellHeight = getSafeIconCell(iconSize);\n\n return {\n minHeight,\n iconCellHeight,\n paddingVertical: Math.max(\n 0,\n (minHeight - Math.max(iconSize, iconCellHeight) * fontScaleFactor) / 2,\n ),\n hasExplicitHeight,\n };\n}\n\n/** Apply the metric-safe line height after the token typography styles. */\nexport function getButtonTextStyle(textStyle: TextStyle, labelLineHeight: number): TextStyle {\n return {\n ...textStyle,\n lineHeight: labelLineHeight,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"buttonTheme.js","names":[],"sources":["../../../src/components/Button/buttonTheme.ts"],"sourcesContent":["import type { ButtonSize, IconButtonSize } from '@yahoo/uds-types';\nimport type { TextStyle, ViewStyle } from 'react-native';\n\nimport { fontMetrics } from '../../../generated/fontMetrics';\n\ntype ButtonTheme = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n components: Record<string, any>;\n};\n\nconst SHARED_BUTTON_ICONBUTTON_SIZES = new Set<IconButtonSize>(['xs', 'sm', 'md', 'lg']);\n// Covers common native font boxes (roughly 1.2em) plus pixel-rounding headroom.\nconst UNKNOWN_FONT_LINE_HEIGHT_RATIO = 1.3;\n\nfunction buttonSizePath(size: ButtonSize, layer: 'root' | 'icon' | 'rootText'): string {\n return `button/size/${size}/${layer}/rest`;\n}\n\nfunction iconButtonSizePath(size: IconButtonSize, layer: 'root' | 'icon'): string {\n return `iconButton/size/${size}/${layer}/rest`;\n}\n\nfunction getLayerStyle(theme: ButtonTheme, path: string): ViewStyle | TextStyle {\n const style = theme.components[path];\n return style && typeof style === 'object' ? style : {};\n}\n\nfunction getIconSize(iconStyle: TextStyle): number {\n return (\n (typeof iconStyle.fontSize === 'number' ? iconStyle.fontSize : undefined) ??\n (typeof iconStyle.lineHeight === 'number' ? iconStyle.lineHeight : undefined) ??\n 16\n );\n}\n\nfunction getVerticalPadding(rootStyle: ViewStyle): number {\n return (\n (typeof rootStyle.paddingVertical === 'number' ? rootStyle.paddingVertical : undefined) ??\n (typeof rootStyle.padding === 'number' ? rootStyle.padding : undefined) ??\n 0\n );\n}\n\nfunction getMetricHeight(value: number, unitsPerEm: number, fontSize: number): number {\n return Math.ceil((Math.max(0, value) / unitsPerEm) * fontSize);\n}\n\nfunction getSafeLabelLineHeight(textStyle: TextStyle, fallbackFontSize: number): number {\n const fontSize = typeof textStyle.fontSize === 'number' ? textStyle.fontSize : fallbackFontSize;\n const tokenLineHeight =\n typeof textStyle.lineHeight === 'number' ? textStyle.lineHeight : fontSize;\n const metrics =\n typeof textStyle.fontFamily === 'string' ? fontMetrics[textStyle.fontFamily] : undefined;\n\n if (!metrics || metrics.unitsPerEm <= 0) {\n return Math.max(tokenLineHeight, Math.ceil(fontSize * UNKNOWN_FONT_LINE_HEIGHT_RATIO));\n }\n\n const metricsLineHeight =\n getMetricHeight(metrics.ascent, metrics.unitsPerEm, fontSize) +\n getMetricHeight(-metrics.descent, metrics.unitsPerEm, fontSize) +\n getMetricHeight(metrics.lineGap, metrics.unitsPerEm, fontSize);\n\n return Math.max(tokenLineHeight, metricsLineHeight);\n}\n\n/** Safe line box for the icon font's em square plus any bbox overflow. */\nexport function getSafeIconCell(fontSize: number): number {\n const metrics = fontMetrics['uds-icons'];\n if (!metrics || metrics.unitsPerEm <= 0) {\n return Math.ceil(fontSize * UNKNOWN_FONT_LINE_HEIGHT_RATIO);\n }\n\n const overflowAboveEm = Math.max(0, metrics.bbox.maxY - metrics.unitsPerEm);\n const overflowBelowEm = Math.max(0, -metrics.bbox.minY);\n\n return (\n Math.ceil(fontSize) +\n getMetricHeight(overflowAboveEm, metrics.unitsPerEm, fontSize) +\n getMetricHeight(overflowBelowEm, metrics.unitsPerEm, fontSize)\n );\n}\n\n/**\n * Safe native content metrics while retaining the token-derived target control height.\n *\n * `fontScaleFactor` pre-scales container metrics only. A single number keeps\n * the legacy linear behavior; distinct label/icon factors let Android 14+\n * geometry follow its size-dependent nonlinear font curve. The returned\n * `labelLineHeight`/`iconCellHeight` stay unscaled because native Text applies\n * the same cap.\n */\nexport function getButtonControlMetrics(\n theme: ButtonTheme,\n size: ButtonSize,\n fontScaleFactor: number | { label: number; icon: number; iconCell?: number } = 1,\n): {\n minHeight: number;\n labelLineHeight: number;\n iconCellHeight: number;\n paddingVertical: number;\n} {\n const rootStyle = getLayerStyle(theme, buttonSizePath(size, 'root')) as ViewStyle;\n const iconStyle = getLayerStyle(theme, buttonSizePath(size, 'icon')) as TextStyle;\n const textStyle = getLayerStyle(theme, buttonSizePath(size, 'rootText')) as TextStyle;\n const paddingVertical = getVerticalPadding(rootStyle);\n const iconSize = getIconSize(iconStyle);\n const fontSize = typeof textStyle.fontSize === 'number' ? textStyle.fontSize : iconSize;\n const tokenLineHeight =\n typeof textStyle.lineHeight === 'number' ? textStyle.lineHeight : fontSize;\n const labelScaleFactor =\n typeof fontScaleFactor === 'number' ? fontScaleFactor : fontScaleFactor.label;\n const iconScaleFactor =\n typeof fontScaleFactor === 'number' ? fontScaleFactor : fontScaleFactor.icon;\n const iconCellScaleFactor =\n typeof fontScaleFactor === 'number'\n ? fontScaleFactor\n : (fontScaleFactor.iconCell ?? fontScaleFactor.icon);\n const targetContentHeight = Math.max(\n iconSize * iconScaleFactor,\n tokenLineHeight * labelScaleFactor,\n );\n const minHeight = Math.ceil(paddingVertical * 2 + targetContentHeight);\n const labelLineHeight = getSafeLabelLineHeight(textStyle, iconSize);\n const iconCellHeight = getSafeIconCell(iconSize);\n const safeContentHeight = Math.max(\n labelLineHeight * labelScaleFactor,\n iconCellHeight * iconCellScaleFactor,\n );\n\n return {\n minHeight,\n labelLineHeight,\n iconCellHeight,\n paddingVertical: Math.max(0, (minHeight - safeContentHeight) / 2),\n };\n}\n\n/**\n * Safe icon cell within the token-derived control size; explicit token height (match-button-height opt-in) wins as the target.\n *\n * `fontScaleFactor` behaves as in {@link getButtonControlMetrics}; an explicit\n * token height stays fixed, with padding absorbing the scaled content.\n */\nexport function getIconButtonControlMetrics(\n theme: ButtonTheme,\n size: IconButtonSize,\n fontScaleFactor = 1,\n): {\n minHeight: number;\n iconCellHeight: number;\n paddingVertical: number;\n hasExplicitHeight: boolean;\n} {\n const rootStyle = getLayerStyle(theme, iconButtonSizePath(size, 'root')) as ViewStyle;\n const iconStyle = getLayerStyle(theme, iconButtonSizePath(size, 'icon')) as TextStyle;\n const iconSize = getIconSize(iconStyle);\n const tokenLineHeight =\n typeof iconStyle.lineHeight === 'number' ? iconStyle.lineHeight : iconSize;\n const paddingVertical = getVerticalPadding(rootStyle);\n const explicitHeight = typeof rootStyle.height === 'number' ? rootStyle.height : undefined;\n const hasExplicitHeight =\n SHARED_BUTTON_ICONBUTTON_SIZES.has(size) && explicitHeight !== undefined;\n const minHeight = hasExplicitHeight\n ? (explicitHeight as number)\n : Math.ceil(paddingVertical * 2 + Math.max(iconSize, tokenLineHeight) * fontScaleFactor);\n const iconCellHeight = getSafeIconCell(iconSize);\n\n return {\n minHeight,\n iconCellHeight,\n paddingVertical: Math.max(\n 0,\n (minHeight - Math.max(iconSize, iconCellHeight) * fontScaleFactor) / 2,\n ),\n hasExplicitHeight,\n };\n}\n\n/** Apply the metric-safe line height after the token typography styles. */\nexport function getButtonTextStyle(textStyle: TextStyle, labelLineHeight: number): TextStyle {\n return {\n ...textStyle,\n lineHeight: labelLineHeight,\n };\n}\n"],"mappings":"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"}
|
|
@@ -90,13 +90,9 @@ function AnimatedIconSlot({ children, visible, iconSize, iconCellHeight, gap })
|
|
|
90
90
|
const Button = (0, react.memo)(function Button({ variant = "primary", size = "md", iconVariant = "outline", startIcon, endIcon, loading, disabled, width: _width, children, style, accessibilityLabel, accessibilityHint, disableEffects = false, maxFontSizeMultiplier, onPressIn, onPressOut, ref, ...props }) {
|
|
91
91
|
const shouldAnimate = !disableEffects;
|
|
92
92
|
const resolvedMaxFontSizeMultiplier = require_fontScaling_useMaxFontSizeMultiplier.useMaxFontSizeMultiplier("control", maxFontSizeMultiplier) ?? 0;
|
|
93
|
-
const fontScaleFactor = require_fontScaling_useFontScale.useFontScale(resolvedMaxFontSizeMultiplier);
|
|
94
93
|
const { theme } = (0, react_native_unistyles.useUnistyles)();
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
size,
|
|
98
|
-
fontScaleFactor
|
|
99
|
-
]);
|
|
94
|
+
const baseMetrics = (0, react.useMemo)(() => require_components_Button_buttonTheme.getButtonControlMetrics(theme, size), [theme, size]);
|
|
95
|
+
const labelScaleFactor = require_fontScaling_useFontScale.useFontScale(resolvedMaxFontSizeMultiplier, baseMetrics.labelLineHeight);
|
|
100
96
|
const [pressed, setPressed] = (0, react.useState)(false);
|
|
101
97
|
generated_styles.buttonStyles.useVariants({
|
|
102
98
|
size,
|
|
@@ -106,6 +102,19 @@ const Button = (0, react.memo)(function Button({ variant = "primary", size = "md
|
|
|
106
102
|
});
|
|
107
103
|
const buttonGap = generated_styles.buttonStyles.root.gap;
|
|
108
104
|
const iconSize = generated_styles.buttonStyles.icon.fontSize;
|
|
105
|
+
const iconScaleFactor = require_fontScaling_useFontScale.useFontScale(resolvedMaxFontSizeMultiplier, iconSize);
|
|
106
|
+
const iconCellScaleFactor = require_fontScaling_useFontScale.useFontScale(resolvedMaxFontSizeMultiplier, baseMetrics.iconCellHeight);
|
|
107
|
+
const { minHeight, labelLineHeight, iconCellHeight, paddingVertical } = (0, react.useMemo)(() => require_components_Button_buttonTheme.getButtonControlMetrics(theme, size, {
|
|
108
|
+
label: labelScaleFactor,
|
|
109
|
+
icon: iconScaleFactor,
|
|
110
|
+
iconCell: iconCellScaleFactor
|
|
111
|
+
}), [
|
|
112
|
+
theme,
|
|
113
|
+
size,
|
|
114
|
+
labelScaleFactor,
|
|
115
|
+
iconScaleFactor,
|
|
116
|
+
iconCellScaleFactor
|
|
117
|
+
]);
|
|
109
118
|
const animatedTheme = (0, react_native_unistyles_reanimated.useAnimatedTheme)();
|
|
110
119
|
const scale = (0, react_native_reanimated.useSharedValue)(require_index.SCALE_EFFECTS.none);
|
|
111
120
|
const handlePressIn = (0, react.useCallback)((event) => {
|
|
@@ -155,8 +164,8 @@ const Button = (0, react.memo)(function Button({ variant = "primary", size = "md
|
|
|
155
164
|
const showStartIcon = !!startIcon && !loading;
|
|
156
165
|
const showEndIcon = !!endIcon && !loading;
|
|
157
166
|
const iconSizeToken = generated_styles.buttonStyles.icon.iconSizeToken ?? "sm";
|
|
158
|
-
const scaledIconSize = Math.round(iconSize *
|
|
159
|
-
const scaledIconCellHeight = Math.ceil(iconCellHeight *
|
|
167
|
+
const scaledIconSize = Math.round(iconSize * iconScaleFactor);
|
|
168
|
+
const scaledIconCellHeight = Math.ceil(iconCellHeight * iconCellScaleFactor);
|
|
160
169
|
const startContent = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnimatedIconSlot, {
|
|
161
170
|
visible: showLoading || showStartIcon,
|
|
162
171
|
iconSize: scaledIconSize,
|
|
@@ -87,13 +87,9 @@ function AnimatedIconSlot({ children, visible, iconSize, iconCellHeight, gap })
|
|
|
87
87
|
const Button = memo(function Button({ variant = "primary", size = "md", iconVariant = "outline", startIcon, endIcon, loading, disabled, width: _width, children, style, accessibilityLabel, accessibilityHint, disableEffects = false, maxFontSizeMultiplier, onPressIn, onPressOut, ref, ...props }) {
|
|
88
88
|
const shouldAnimate = !disableEffects;
|
|
89
89
|
const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier("control", maxFontSizeMultiplier) ?? 0;
|
|
90
|
-
const fontScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier);
|
|
91
90
|
const { theme } = useUnistyles();
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
size,
|
|
95
|
-
fontScaleFactor
|
|
96
|
-
]);
|
|
91
|
+
const baseMetrics = useMemo(() => getButtonControlMetrics(theme, size), [theme, size]);
|
|
92
|
+
const labelScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier, baseMetrics.labelLineHeight);
|
|
97
93
|
const [pressed, setPressed] = useState(false);
|
|
98
94
|
buttonStyles.useVariants({
|
|
99
95
|
size,
|
|
@@ -103,6 +99,19 @@ const Button = memo(function Button({ variant = "primary", size = "md", iconVari
|
|
|
103
99
|
});
|
|
104
100
|
const buttonGap = buttonStyles.root.gap;
|
|
105
101
|
const iconSize = buttonStyles.icon.fontSize;
|
|
102
|
+
const iconScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier, iconSize);
|
|
103
|
+
const iconCellScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier, baseMetrics.iconCellHeight);
|
|
104
|
+
const { minHeight, labelLineHeight, iconCellHeight, paddingVertical } = useMemo(() => getButtonControlMetrics(theme, size, {
|
|
105
|
+
label: labelScaleFactor,
|
|
106
|
+
icon: iconScaleFactor,
|
|
107
|
+
iconCell: iconCellScaleFactor
|
|
108
|
+
}), [
|
|
109
|
+
theme,
|
|
110
|
+
size,
|
|
111
|
+
labelScaleFactor,
|
|
112
|
+
iconScaleFactor,
|
|
113
|
+
iconCellScaleFactor
|
|
114
|
+
]);
|
|
106
115
|
const animatedTheme = useAnimatedTheme();
|
|
107
116
|
const scale = useSharedValue(SCALE_EFFECTS.none);
|
|
108
117
|
const handlePressIn = useCallback((event) => {
|
|
@@ -152,8 +161,8 @@ const Button = memo(function Button({ variant = "primary", size = "md", iconVari
|
|
|
152
161
|
const showStartIcon = !!startIcon && !loading;
|
|
153
162
|
const showEndIcon = !!endIcon && !loading;
|
|
154
163
|
const iconSizeToken = buttonStyles.icon.iconSizeToken ?? "sm";
|
|
155
|
-
const scaledIconSize = Math.round(iconSize *
|
|
156
|
-
const scaledIconCellHeight = Math.ceil(iconCellHeight *
|
|
164
|
+
const scaledIconSize = Math.round(iconSize * iconScaleFactor);
|
|
165
|
+
const scaledIconCellHeight = Math.ceil(iconCellHeight * iconCellScaleFactor);
|
|
157
166
|
const startContent = /* @__PURE__ */ jsx(AnimatedIconSlot, {
|
|
158
167
|
visible: showLoading || showStartIcon,
|
|
159
168
|
iconSize: scaledIconSize,
|