@yahoo/uds-mobile 2.24.0-beta.1 → 2.24.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/README.md +37 -1
  2. package/dist/components/Avatar/AvatarIcon.cjs +8 -2
  3. package/dist/components/Avatar/AvatarIcon.d.cts.map +1 -1
  4. package/dist/components/Avatar/AvatarIcon.d.ts.map +1 -1
  5. package/dist/components/Avatar/AvatarIcon.js +9 -3
  6. package/dist/components/Avatar/AvatarIcon.js.map +1 -1
  7. package/dist/components/Avatar/AvatarImage.cjs +9 -2
  8. package/dist/components/Avatar/AvatarImage.d.cts.map +1 -1
  9. package/dist/components/Avatar/AvatarImage.d.ts.map +1 -1
  10. package/dist/components/Avatar/AvatarImage.js +10 -3
  11. package/dist/components/Avatar/AvatarImage.js.map +1 -1
  12. package/dist/components/Avatar/AvatarText.cjs +8 -2
  13. package/dist/components/Avatar/AvatarText.d.cts.map +1 -1
  14. package/dist/components/Avatar/AvatarText.d.ts.map +1 -1
  15. package/dist/components/Avatar/AvatarText.js +9 -3
  16. package/dist/components/Avatar/AvatarText.js.map +1 -1
  17. package/dist/components/Avatar/types.d.cts +7 -3
  18. package/dist/components/Avatar/types.d.cts.map +1 -1
  19. package/dist/components/Avatar/types.d.ts +7 -3
  20. package/dist/components/Avatar/types.d.ts.map +1 -1
  21. package/dist/components/Avatar/useAvatarFontScale.cjs +16 -0
  22. package/dist/components/Avatar/useAvatarFontScale.d.cts +7 -0
  23. package/dist/components/Avatar/useAvatarFontScale.d.cts.map +1 -0
  24. package/dist/components/Avatar/useAvatarFontScale.d.ts +7 -0
  25. package/dist/components/Avatar/useAvatarFontScale.d.ts.map +1 -0
  26. package/dist/components/Avatar/useAvatarFontScale.js +16 -0
  27. package/dist/components/Avatar/useAvatarFontScale.js.map +1 -0
  28. package/dist/components/Avatar/utils.cjs +9 -6
  29. package/dist/components/Avatar/utils.d.cts +2 -2
  30. package/dist/components/Avatar/utils.d.cts.map +1 -1
  31. package/dist/components/Avatar/utils.d.ts +2 -2
  32. package/dist/components/Avatar/utils.d.ts.map +1 -1
  33. package/dist/components/Avatar/utils.js +9 -6
  34. package/dist/components/Avatar/utils.js.map +1 -1
  35. package/dist/components/Button/buttonTheme.cjs +16 -7
  36. package/dist/components/Button/buttonTheme.d.cts +13 -6
  37. package/dist/components/Button/buttonTheme.d.cts.map +1 -1
  38. package/dist/components/Button/buttonTheme.d.ts +13 -6
  39. package/dist/components/Button/buttonTheme.d.ts.map +1 -1
  40. package/dist/components/Button/buttonTheme.js +16 -8
  41. package/dist/components/Button/buttonTheme.js.map +1 -1
  42. package/dist/components/Button.cjs +25 -11
  43. package/dist/components/Button.d.cts.map +1 -1
  44. package/dist/components/Button.d.ts.map +1 -1
  45. package/dist/components/Button.js +25 -11
  46. package/dist/components/Button.js.map +1 -1
  47. package/dist/components/Checkbox.cjs +16 -6
  48. package/dist/components/Checkbox.d.cts.map +1 -1
  49. package/dist/components/Checkbox.d.ts.map +1 -1
  50. package/dist/components/Checkbox.js +16 -6
  51. package/dist/components/Checkbox.js.map +1 -1
  52. package/dist/components/Chip/ChipBase.cjs +24 -21
  53. package/dist/components/Chip/ChipBase.js +24 -21
  54. package/dist/components/Chip/ChipBase.js.map +1 -1
  55. package/dist/components/Icon.cjs +22 -6
  56. package/dist/components/Icon.d.cts +6 -5
  57. package/dist/components/Icon.d.cts.map +1 -1
  58. package/dist/components/Icon.d.ts +6 -5
  59. package/dist/components/Icon.d.ts.map +1 -1
  60. package/dist/components/Icon.js +22 -6
  61. package/dist/components/Icon.js.map +1 -1
  62. package/dist/components/IconButton.cjs +17 -6
  63. package/dist/components/IconButton.d.cts +6 -0
  64. package/dist/components/IconButton.d.cts.map +1 -1
  65. package/dist/components/IconButton.d.ts +6 -0
  66. package/dist/components/IconButton.d.ts.map +1 -1
  67. package/dist/components/IconButton.js +18 -7
  68. package/dist/components/IconButton.js.map +1 -1
  69. package/dist/components/Input.cjs +56 -18
  70. package/dist/components/Input.d.cts +5 -0
  71. package/dist/components/Input.d.cts.map +1 -1
  72. package/dist/components/Input.d.ts +5 -0
  73. package/dist/components/Input.d.ts.map +1 -1
  74. package/dist/components/Input.js +56 -18
  75. package/dist/components/Input.js.map +1 -1
  76. package/dist/components/InputHelpText.cjs +7 -1
  77. package/dist/components/InputHelpText.d.cts +2 -0
  78. package/dist/components/InputHelpText.d.cts.map +1 -1
  79. package/dist/components/InputHelpText.d.ts +2 -0
  80. package/dist/components/InputHelpText.d.ts.map +1 -1
  81. package/dist/components/InputHelpText.js +7 -1
  82. package/dist/components/InputHelpText.js.map +1 -1
  83. package/dist/components/Link.cjs +16 -1
  84. package/dist/components/Link.d.cts +6 -0
  85. package/dist/components/Link.d.cts.map +1 -1
  86. package/dist/components/Link.d.ts +6 -0
  87. package/dist/components/Link.d.ts.map +1 -1
  88. package/dist/components/Link.js +16 -1
  89. package/dist/components/Link.js.map +1 -1
  90. package/dist/components/Pagination/Pagination.cjs +18 -10
  91. package/dist/components/Pagination/Pagination.d.cts +2 -0
  92. package/dist/components/Pagination/Pagination.d.cts.map +1 -1
  93. package/dist/components/Pagination/Pagination.d.ts +2 -0
  94. package/dist/components/Pagination/Pagination.d.ts.map +1 -1
  95. package/dist/components/Pagination/Pagination.js +18 -10
  96. package/dist/components/Pagination/Pagination.js.map +1 -1
  97. package/dist/components/Pagination/PaginationEllipsis.cjs +8 -2
  98. package/dist/components/Pagination/PaginationEllipsis.d.cts.map +1 -1
  99. package/dist/components/Pagination/PaginationEllipsis.d.ts.map +1 -1
  100. package/dist/components/Pagination/PaginationEllipsis.js +9 -3
  101. package/dist/components/Pagination/PaginationEllipsis.js.map +1 -1
  102. package/dist/components/Pagination/PaginationItem.cjs +2 -1
  103. package/dist/components/Pagination/PaginationItem.js +2 -1
  104. package/dist/components/Pagination/PaginationItem.js.map +1 -1
  105. package/dist/components/Pagination/PaginationLink.cjs +2 -1
  106. package/dist/components/Pagination/PaginationLink.js +2 -1
  107. package/dist/components/Pagination/PaginationLink.js.map +1 -1
  108. package/dist/components/Pagination/PaginationNext.cjs +3 -1
  109. package/dist/components/Pagination/PaginationNext.js +3 -1
  110. package/dist/components/Pagination/PaginationNext.js.map +1 -1
  111. package/dist/components/Pagination/PaginationPrev.cjs +3 -1
  112. package/dist/components/Pagination/PaginationPrev.js +3 -1
  113. package/dist/components/Pagination/PaginationPrev.js.map +1 -1
  114. package/dist/components/Pagination/paginationContext.d.cts +2 -0
  115. package/dist/components/Pagination/paginationContext.d.cts.map +1 -1
  116. package/dist/components/Pagination/paginationContext.d.ts +2 -0
  117. package/dist/components/Pagination/paginationContext.d.ts.map +1 -1
  118. package/dist/components/Pagination/paginationContext.js.map +1 -1
  119. package/dist/components/Pagination/usePaginationControlStyles.cjs +8 -1
  120. package/dist/components/Pagination/usePaginationControlStyles.d.cts.map +1 -1
  121. package/dist/components/Pagination/usePaginationControlStyles.d.ts.map +1 -1
  122. package/dist/components/Pagination/usePaginationControlStyles.js +9 -2
  123. package/dist/components/Pagination/usePaginationControlStyles.js.map +1 -1
  124. package/dist/components/Radio/Radio.cjs +4 -3
  125. package/dist/components/Radio/Radio.js +4 -3
  126. package/dist/components/Radio/Radio.js.map +1 -1
  127. package/dist/components/Select/Select.cjs +29 -6
  128. package/dist/components/Select/Select.d.cts +2 -0
  129. package/dist/components/Select/Select.d.cts.map +1 -1
  130. package/dist/components/Select/Select.d.ts +2 -0
  131. package/dist/components/Select/Select.d.ts.map +1 -1
  132. package/dist/components/Select/Select.js +29 -6
  133. package/dist/components/Select/Select.js.map +1 -1
  134. package/dist/components/Select/SelectContent.cjs +31 -23
  135. package/dist/components/Select/SelectContent.d.cts.map +1 -1
  136. package/dist/components/Select/SelectContent.d.ts.map +1 -1
  137. package/dist/components/Select/SelectContent.js +32 -24
  138. package/dist/components/Select/SelectContent.js.map +1 -1
  139. package/dist/components/Select/SelectItem.cjs +25 -15
  140. package/dist/components/Select/SelectItem.d.cts.map +1 -1
  141. package/dist/components/Select/SelectItem.d.ts.map +1 -1
  142. package/dist/components/Select/SelectItem.js +25 -15
  143. package/dist/components/Select/SelectItem.js.map +1 -1
  144. package/dist/components/Select/SelectTrigger.cjs +27 -13
  145. package/dist/components/Select/SelectTrigger.d.cts.map +1 -1
  146. package/dist/components/Select/SelectTrigger.d.ts.map +1 -1
  147. package/dist/components/Select/SelectTrigger.js +27 -13
  148. package/dist/components/Select/SelectTrigger.js.map +1 -1
  149. package/dist/components/Select/types.d.cts +2 -0
  150. package/dist/components/Select/types.d.cts.map +1 -1
  151. package/dist/components/Select/types.d.ts +2 -0
  152. package/dist/components/Select/types.d.ts.map +1 -1
  153. package/dist/components/Switch.cjs +31 -14
  154. package/dist/components/Switch.d.cts.map +1 -1
  155. package/dist/components/Switch.d.ts.map +1 -1
  156. package/dist/components/Switch.js +31 -14
  157. package/dist/components/Switch.js.map +1 -1
  158. package/dist/components/Tabs/Tab.cjs +8 -4
  159. package/dist/components/Tabs/Tab.d.cts.map +1 -1
  160. package/dist/components/Tabs/Tab.d.ts.map +1 -1
  161. package/dist/components/Tabs/Tab.js +8 -4
  162. package/dist/components/Tabs/Tab.js.map +1 -1
  163. package/dist/components/Text.cjs +5 -0
  164. package/dist/components/Text.d.cts +6 -5
  165. package/dist/components/Text.d.cts.map +1 -1
  166. package/dist/components/Text.d.ts +6 -5
  167. package/dist/components/Text.d.ts.map +1 -1
  168. package/dist/components/Text.js +5 -0
  169. package/dist/components/Text.js.map +1 -1
  170. package/dist/components/UDSProvider.cjs +1 -1
  171. package/dist/components/UDSProvider.d.cts +4 -3
  172. package/dist/components/UDSProvider.d.cts.map +1 -1
  173. package/dist/components/UDSProvider.d.ts +4 -3
  174. package/dist/components/UDSProvider.d.ts.map +1 -1
  175. package/dist/components/UDSProvider.js +1 -1
  176. package/dist/components/UDSProvider.js.map +1 -1
  177. package/dist/fontScaling/FontScalingContext.cjs +35 -3
  178. package/dist/fontScaling/FontScalingContext.d.cts +20 -5
  179. package/dist/fontScaling/FontScalingContext.d.cts.map +1 -1
  180. package/dist/fontScaling/FontScalingContext.d.ts +20 -5
  181. package/dist/fontScaling/FontScalingContext.d.ts.map +1 -1
  182. package/dist/fontScaling/FontScalingContext.js +34 -5
  183. package/dist/fontScaling/FontScalingContext.js.map +1 -1
  184. package/dist/fontScaling/androidNonlinearFontScaling.cjs +250 -0
  185. package/dist/fontScaling/androidNonlinearFontScaling.d.cts +7 -0
  186. package/dist/fontScaling/androidNonlinearFontScaling.d.cts.map +1 -0
  187. package/dist/fontScaling/androidNonlinearFontScaling.d.ts +7 -0
  188. package/dist/fontScaling/androidNonlinearFontScaling.d.ts.map +1 -0
  189. package/dist/fontScaling/androidNonlinearFontScaling.js +251 -0
  190. package/dist/fontScaling/androidNonlinearFontScaling.js.map +1 -0
  191. package/dist/fontScaling/constants.cjs +37 -3
  192. package/dist/fontScaling/constants.d.cts +15 -4
  193. package/dist/fontScaling/constants.d.cts.map +1 -1
  194. package/dist/fontScaling/constants.d.ts +15 -4
  195. package/dist/fontScaling/constants.d.ts.map +1 -1
  196. package/dist/fontScaling/constants.js +37 -4
  197. package/dist/fontScaling/constants.js.map +1 -1
  198. package/dist/fontScaling/index.cjs +2 -1
  199. package/dist/fontScaling/index.d.cts +2 -2
  200. package/dist/fontScaling/index.d.ts +2 -2
  201. package/dist/fontScaling/index.js +3 -3
  202. package/dist/fontScaling/resolveMaxFontSizeMultiplier.cjs +1 -2
  203. package/dist/fontScaling/resolveMaxFontSizeMultiplier.d.cts +1 -2
  204. package/dist/fontScaling/resolveMaxFontSizeMultiplier.d.cts.map +1 -1
  205. package/dist/fontScaling/resolveMaxFontSizeMultiplier.d.ts +1 -2
  206. package/dist/fontScaling/resolveMaxFontSizeMultiplier.d.ts.map +1 -1
  207. package/dist/fontScaling/resolveMaxFontSizeMultiplier.js +1 -2
  208. package/dist/fontScaling/resolveMaxFontSizeMultiplier.js.map +1 -1
  209. package/dist/fontScaling/types.d.cts +11 -8
  210. package/dist/fontScaling/types.d.cts.map +1 -1
  211. package/dist/fontScaling/types.d.ts +11 -8
  212. package/dist/fontScaling/types.d.ts.map +1 -1
  213. package/dist/fontScaling/useFontScale.cjs +30 -4
  214. package/dist/fontScaling/useFontScale.d.cts +6 -12
  215. package/dist/fontScaling/useFontScale.d.cts.map +1 -1
  216. package/dist/fontScaling/useFontScale.d.ts +6 -12
  217. package/dist/fontScaling/useFontScale.d.ts.map +1 -1
  218. package/dist/fontScaling/useFontScale.js +31 -6
  219. package/dist/fontScaling/useFontScale.js.map +1 -1
  220. package/dist/jest/mocks/styles.cjs +14 -1
  221. package/dist/jest/mocks/styles.d.cts.map +1 -1
  222. package/dist/jest/mocks/styles.d.ts.map +1 -1
  223. package/dist/jest/mocks/styles.js +14 -1
  224. package/dist/jest/mocks/styles.js.map +1 -1
  225. package/dist/mobile/generated/fontMetrics.cjs +534 -534
  226. package/dist/mobile/generated/fontMetrics.js +534 -534
  227. package/dist/mobile/generated/fontMetrics.js.map +1 -1
  228. package/dist/portal.cjs +8 -2
  229. package/dist/portal.d.cts.map +1 -1
  230. package/dist/portal.d.ts.map +1 -1
  231. package/dist/portal.js +8 -2
  232. package/dist/portal.js.map +1 -1
  233. package/fonts/index.cjs +214 -214
  234. package/fonts/index.mjs +214 -214
  235. package/fonts/uds-icons.ttf +0 -0
  236. package/generated/fontMetrics.ts +534 -534
  237. package/package.json +2 -2
@@ -1,17 +1,23 @@
1
1
  /*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
2
- import { getMergedPaginationControlRootStyle, getMergedPaginationIconStyle, getMergedPaginationTextStyle, getPaginationControlMetrics } from "./paginationTheme.js";
2
+ import { useComponentFontScale } from "../../fontScaling/useFontScale.js";
3
+ import { usePaginationContext } from "./paginationContext.js";
4
+ import { getMergedPaginationControlRootStyle, getMergedPaginationIconStyle, getMergedPaginationTextStyle, getPaginationControlMetrics, getPaginationSizeLayerStyle } from "./paginationTheme.js";
3
5
  import { useMemo } from "react";
4
6
  import { StyleSheet } from "react-native";
5
7
  import { useUnistyles } from "react-native-unistyles";
6
8
  //#region src/components/Pagination/usePaginationControlStyles.ts
7
9
  function usePaginationControlStyles({ size, variant, isActive, pressed, layout = "page" }) {
8
10
  const { theme } = useUnistyles();
11
+ const { maxFontSizeMultiplier } = usePaginationContext();
9
12
  const active = isActive ? "on" : "off";
10
13
  const interaction = pressed ? "pressed" : "rest";
11
14
  const prevNextActive = "off";
15
+ const sizeTextStyle = getPaginationSizeLayerStyle(theme, size, "text");
16
+ const fontScaleFactor = useComponentFontScale(maxFontSizeMultiplier, typeof sizeTextStyle.lineHeight === "number" ? sizeTextStyle.lineHeight : sizeTextStyle.fontSize);
12
17
  return {
13
18
  rootStyle: useMemo(() => {
14
- const { controlSize, borderRadius } = getPaginationControlMetrics(theme, size);
19
+ const { controlSize: baseControlSize, borderRadius } = getPaginationControlMetrics(theme, size);
20
+ const controlSize = Math.round(baseControlSize * fontScaleFactor);
15
21
  const variantActive = layout === "prevNext" ? prevNextActive : active;
16
22
  return [
17
23
  layout === "page" ? paginationStaticStyles.pageRoot : paginationStaticStyles.prevNextRoot,
@@ -26,6 +32,7 @@ function usePaginationControlStyles({ size, variant, isActive, pressed, layout =
26
32
  ];
27
33
  }, [
28
34
  active,
35
+ fontScaleFactor,
29
36
  interaction,
30
37
  layout,
31
38
  size,
@@ -1 +1 @@
1
- {"version":3,"file":"usePaginationControlStyles.js","names":[],"sources":["../../../src/components/Pagination/usePaginationControlStyles.ts"],"sourcesContent":["import type { PaginationSize, PaginationVariant } from '@yahoo/uds-types';\nimport { useMemo } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { StyleSheet } from 'react-native';\n// eslint-disable-next-line uds/no-use-unistyles -- pagination token lookups for variant + size layers\nimport { useUnistyles } from 'react-native-unistyles';\n\nimport {\n getMergedPaginationControlRootStyle,\n getMergedPaginationIconStyle,\n getMergedPaginationTextStyle,\n getPaginationControlMetrics,\n} from './paginationTheme';\n\ninterface UsePaginationControlStylesOptions {\n size: PaginationSize;\n variant: PaginationVariant;\n isActive: boolean;\n pressed: boolean;\n layout?: 'page' | 'prevNext';\n}\n\nfunction usePaginationControlStyles({\n size,\n variant,\n isActive,\n pressed,\n layout = 'page',\n}: UsePaginationControlStylesOptions) {\n const { theme } = useUnistyles();\n const active: 'on' | 'off' = isActive ? 'on' : 'off';\n const interaction = pressed ? 'pressed' : 'rest';\n const prevNextActive: 'on' | 'off' = 'off';\n\n const rootStyle = useMemo<StyleProp<ViewStyle>>(() => {\n const { controlSize, borderRadius } = getPaginationControlMetrics(theme, size);\n const variantActive = layout === 'prevNext' ? prevNextActive : active;\n\n return [\n layout === 'page' ? paginationStaticStyles.pageRoot : paginationStaticStyles.prevNextRoot,\n getMergedPaginationControlRootStyle(theme, size, variant, variantActive, interaction),\n {\n borderRadius,\n ...(layout === 'page'\n ? { height: controlSize, width: controlSize }\n : { minHeight: controlSize }),\n },\n ];\n }, [active, interaction, layout, size, theme, variant]);\n\n const textStyle = useMemo<StyleProp<TextStyle>>(() => {\n const variantActive = layout === 'prevNext' ? prevNextActive : active;\n\n return [\n paginationStaticStyles.text,\n getMergedPaginationTextStyle(theme, size, variant, variantActive, interaction),\n ];\n }, [active, interaction, layout, size, theme, variant]);\n\n const iconStyle = useMemo<StyleProp<TextStyle>>(\n () =>\n getMergedPaginationIconStyle(\n theme,\n size,\n variant,\n layout === 'prevNext' ? prevNextActive : active,\n interaction,\n ),\n [active, interaction, layout, size, theme, variant],\n );\n\n return { rootStyle, textStyle, iconStyle };\n}\n\nconst paginationStaticStyles = StyleSheet.create({\n pageRoot: {\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n },\n prevNextRoot: {\n alignItems: 'center',\n flexDirection: 'row',\n flexShrink: 0,\n gap: 4,\n justifyContent: 'center',\n },\n text: {\n flexShrink: 0,\n fontVariant: ['tabular-nums'],\n textAlign: 'center',\n },\n});\n\nexport { usePaginationControlStyles };\n"],"mappings":";;;;;;AAsBA,SAAS,2BAA2B,EAClC,MACA,SACA,UACA,SACA,SAAS,UAC2B;CACpC,MAAM,EAAE,UAAU,cAAc;CAChC,MAAM,SAAuB,WAAW,OAAO;CAC/C,MAAM,cAAc,UAAU,YAAY;CAC1C,MAAM,iBAA+B;CAuCrC,OAAO;EAAE,WArCS,cAAoC;GACpD,MAAM,EAAE,aAAa,iBAAiB,4BAA4B,OAAO,KAAK;GAC9E,MAAM,gBAAgB,WAAW,aAAa,iBAAiB;GAE/D,OAAO;IACL,WAAW,SAAS,uBAAuB,WAAW,uBAAuB;IAC7E,oCAAoC,OAAO,MAAM,SAAS,eAAe,YAAY;IACrF;KACE;KACA,GAAI,WAAW,SACX;MAAE,QAAQ;MAAa,OAAO;MAAa,GAC3C,EAAE,WAAW,aAAa;KAC/B;IACF;KACA;GAAC;GAAQ;GAAa;GAAQ;GAAM;GAAO;GAAQ,CAuBpC;EAAE,WArBF,cAAoC;GACpD,MAAM,gBAAgB,WAAW,aAAa,iBAAiB;GAE/D,OAAO,CACL,uBAAuB,MACvB,6BAA6B,OAAO,MAAM,SAAS,eAAe,YAAY,CAC/E;KACA;GAAC;GAAQ;GAAa;GAAQ;GAAM;GAAO;GAAQ,CAczB;EAAE,WAZb,cAEd,6BACE,OACA,MACA,SACA,WAAW,aAAa,iBAAiB,QACzC,YACD,EACH;GAAC;GAAQ;GAAa;GAAQ;GAAM;GAAO;GAAQ,CAGb;EAAE;;AAG5C,MAAM,yBAAyB,WAAW,OAAO;CAC/C,UAAU;EACR,YAAY;EACZ,gBAAgB;EAChB,YAAY;EACb;CACD,cAAc;EACZ,YAAY;EACZ,eAAe;EACf,YAAY;EACZ,KAAK;EACL,gBAAgB;EACjB;CACD,MAAM;EACJ,YAAY;EACZ,aAAa,CAAC,eAAe;EAC7B,WAAW;EACZ;CACF,CAAC"}
1
+ {"version":3,"file":"usePaginationControlStyles.js","names":[],"sources":["../../../src/components/Pagination/usePaginationControlStyles.ts"],"sourcesContent":["import type { PaginationSize, PaginationVariant } from '@yahoo/uds-types';\nimport { useMemo } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { StyleSheet } from 'react-native';\n// eslint-disable-next-line uds/no-use-unistyles -- pagination token lookups for variant + size layers\nimport { useUnistyles } from 'react-native-unistyles';\n\nimport { useComponentFontScale } from '../../fontScaling/useFontScale';\nimport { usePaginationContext } from './paginationContext';\nimport {\n getMergedPaginationControlRootStyle,\n getMergedPaginationIconStyle,\n getMergedPaginationTextStyle,\n getPaginationControlMetrics,\n getPaginationSizeLayerStyle,\n} from './paginationTheme';\n\ninterface UsePaginationControlStylesOptions {\n size: PaginationSize;\n variant: PaginationVariant;\n isActive: boolean;\n pressed: boolean;\n layout?: 'page' | 'prevNext';\n}\n\nfunction usePaginationControlStyles({\n size,\n variant,\n isActive,\n pressed,\n layout = 'page',\n}: UsePaginationControlStylesOptions) {\n const { theme } = useUnistyles();\n const { maxFontSizeMultiplier } = usePaginationContext();\n const active: 'on' | 'off' = isActive ? 'on' : 'off';\n const interaction = pressed ? 'pressed' : 'rest';\n const prevNextActive: 'on' | 'off' = 'off';\n const sizeTextStyle = getPaginationSizeLayerStyle(theme, size, 'text') as TextStyle;\n const fontScaleFactor = useComponentFontScale(\n maxFontSizeMultiplier,\n typeof sizeTextStyle.lineHeight === 'number'\n ? sizeTextStyle.lineHeight\n : sizeTextStyle.fontSize,\n );\n\n const rootStyle = useMemo<StyleProp<ViewStyle>>(() => {\n const { controlSize: baseControlSize, borderRadius } = getPaginationControlMetrics(theme, size);\n const controlSize = Math.round(baseControlSize * fontScaleFactor);\n const variantActive = layout === 'prevNext' ? prevNextActive : active;\n\n return [\n layout === 'page' ? paginationStaticStyles.pageRoot : paginationStaticStyles.prevNextRoot,\n getMergedPaginationControlRootStyle(theme, size, variant, variantActive, interaction),\n {\n borderRadius,\n ...(layout === 'page'\n ? { height: controlSize, width: controlSize }\n : { minHeight: controlSize }),\n },\n ];\n }, [active, fontScaleFactor, interaction, layout, size, theme, variant]);\n\n const textStyle = useMemo<StyleProp<TextStyle>>(() => {\n const variantActive = layout === 'prevNext' ? prevNextActive : active;\n\n return [\n paginationStaticStyles.text,\n getMergedPaginationTextStyle(theme, size, variant, variantActive, interaction),\n ];\n }, [active, interaction, layout, size, theme, variant]);\n\n const iconStyle = useMemo<StyleProp<TextStyle>>(\n () =>\n getMergedPaginationIconStyle(\n theme,\n size,\n variant,\n layout === 'prevNext' ? prevNextActive : active,\n interaction,\n ),\n [active, interaction, layout, size, theme, variant],\n );\n\n return { rootStyle, textStyle, iconStyle };\n}\n\nconst paginationStaticStyles = StyleSheet.create({\n pageRoot: {\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n },\n prevNextRoot: {\n alignItems: 'center',\n flexDirection: 'row',\n flexShrink: 0,\n gap: 4,\n justifyContent: 'center',\n },\n text: {\n flexShrink: 0,\n fontVariant: ['tabular-nums'],\n textAlign: 'center',\n },\n});\n\nexport { usePaginationControlStyles };\n"],"mappings":";;;;;;;;AAyBA,SAAS,2BAA2B,EAClC,MACA,SACA,UACA,SACA,SAAS,UAC2B;CACpC,MAAM,EAAE,UAAU,cAAc;CAChC,MAAM,EAAE,0BAA0B,sBAAsB;CACxD,MAAM,SAAuB,WAAW,OAAO;CAC/C,MAAM,cAAc,UAAU,YAAY;CAC1C,MAAM,iBAA+B;CACrC,MAAM,gBAAgB,4BAA4B,OAAO,MAAM,OAAO;CACtE,MAAM,kBAAkB,sBACtB,uBACA,OAAO,cAAc,eAAe,WAChC,cAAc,aACd,cAAc,SACnB;CAwCD,OAAO;EAAE,WAtCS,cAAoC;GACpD,MAAM,EAAE,aAAa,iBAAiB,iBAAiB,4BAA4B,OAAO,KAAK;GAC/F,MAAM,cAAc,KAAK,MAAM,kBAAkB,gBAAgB;GACjE,MAAM,gBAAgB,WAAW,aAAa,iBAAiB;GAE/D,OAAO;IACL,WAAW,SAAS,uBAAuB,WAAW,uBAAuB;IAC7E,oCAAoC,OAAO,MAAM,SAAS,eAAe,YAAY;IACrF;KACE;KACA,GAAI,WAAW,SACX;MAAE,QAAQ;MAAa,OAAO;MAAa,GAC3C,EAAE,WAAW,aAAa;KAC/B;IACF;KACA;GAAC;GAAQ;GAAiB;GAAa;GAAQ;GAAM;GAAO;GAAQ,CAuBrD;EAAE,WArBF,cAAoC;GACpD,MAAM,gBAAgB,WAAW,aAAa,iBAAiB;GAE/D,OAAO,CACL,uBAAuB,MACvB,6BAA6B,OAAO,MAAM,SAAS,eAAe,YAAY,CAC/E;KACA;GAAC;GAAQ;GAAa;GAAQ;GAAM;GAAO;GAAQ,CAczB;EAAE,WAZb,cAEd,6BACE,OACA,MACA,SACA,WAAW,aAAa,iBAAiB,QACzC,YACD,EACH;GAAC;GAAQ;GAAa;GAAQ;GAAM;GAAO;GAAQ,CAGb;EAAE;;AAG5C,MAAM,yBAAyB,WAAW,OAAO;CAC/C,UAAU;EACR,YAAY;EACZ,gBAAgB;EAChB,YAAY;EACb;CACD,cAAc;EACZ,YAAY;EACZ,eAAe;EACf,YAAY;EACZ,KAAK;EACL,gBAAgB;EACjB;CACD,MAAM;EACJ,YAAY;EACZ,aAAa,CAAC,eAAe;EAC7B,WAAW;EACZ;CACF,CAAC"}
@@ -3,8 +3,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
4
4
  const require_index = require("../../motion-tokens/dist/index.cjs");
5
5
  const require_motion = require("../../motion.cjs");
6
- const require_fontScaling_useMaxFontSizeMultiplier = require("../../fontScaling/useMaxFontSizeMultiplier.cjs");
7
6
  const require_fontScaling_useFontScale = require("../../fontScaling/useFontScale.cjs");
7
+ const require_fontScaling_useMaxFontSizeMultiplier = require("../../fontScaling/useMaxFontSizeMultiplier.cjs");
8
8
  const require_components_Pressable = require("../Pressable.cjs");
9
9
  const require_components_FormLabel = require("../FormLabel.cjs");
10
10
  const require_components_Radio_RadioGroupContext = require("./RadioGroupContext.cjs");
@@ -54,8 +54,7 @@ const VARIANT_ERROR_MAP = {
54
54
  const Radio = (0, react.memo)(function Radio({ label, labelPosition = "start", variant: variantInput, size: sizeInput, value: valueProp, checked: checkedProp, hasError: hasErrorProp, reduceMotion: reduceMotionProp, defaultChecked, disabled: disabledProp, required, maxFontSizeMultiplier, onChange, ref, ...viewProps }) {
55
55
  const uid = `uds-radio-${(0, react.useId)()}`;
56
56
  const radioGroup = require_components_Radio_RadioGroupContext.useRadioGroupContext();
57
- const resolvedMaxFontSizeMultiplier = require_fontScaling_useMaxFontSizeMultiplier.useMaxFontSizeMultiplier("control", maxFontSizeMultiplier) ?? 0;
58
- const fontScaleFactor = require_fontScaling_useFontScale.useFontScale(resolvedMaxFontSizeMultiplier);
57
+ const resolvedMaxFontSizeMultiplier = require_fontScaling_useMaxFontSizeMultiplier.useMaxFontSizeMultiplier("control", maxFontSizeMultiplier);
59
58
  const disabled = Boolean(disabledProp || radioGroup?.disabled);
60
59
  const hasError = hasErrorProp ?? radioGroup?.hasError;
61
60
  const reduceMotion = reduceMotionProp ?? radioGroup?.reduceMotion;
@@ -110,6 +109,8 @@ const Radio = (0, react.memo)(function Radio({ label, labelPosition = "start", v
110
109
  value: valueState,
111
110
  pressed
112
111
  });
112
+ const radioBaseSize = generated_styles.radioStyles.radio.width;
113
+ const fontScaleFactor = require_fontScaling_useFontScale.useComponentFontScale(resolvedMaxFontSizeMultiplier, typeof radioBaseSize === "number" ? radioBaseSize : void 0);
113
114
  const rootStyle = (0, react.useMemo)(() => [generated_styles.radioStyles.root, { opacity: disabled ? .5 : 1 }], [generated_styles.radioStyles.root, disabled]);
114
115
  const radioBoxStyle = (0, react.useMemo)(() => {
115
116
  const { width, height } = generated_styles.radioStyles.radio;
@@ -1,8 +1,8 @@
1
1
  /*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
2
2
  import { SCALE_EFFECTS } from "../../motion-tokens/dist/index.js";
3
3
  import { BUTTON_SPRING_CONFIG } from "../../motion.js";
4
+ import { useComponentFontScale } from "../../fontScaling/useFontScale.js";
4
5
  import { useMaxFontSizeMultiplier } from "../../fontScaling/useMaxFontSizeMultiplier.js";
5
- import { useFontScale } from "../../fontScaling/useFontScale.js";
6
6
  import { Pressable as Pressable$1 } from "../Pressable.js";
7
7
  import { FormLabel } from "../FormLabel.js";
8
8
  import { useRadioGroupContext } from "./RadioGroupContext.js";
@@ -51,8 +51,7 @@ const VARIANT_ERROR_MAP = {
51
51
  const Radio = memo(function Radio({ label, labelPosition = "start", variant: variantInput, size: sizeInput, value: valueProp, checked: checkedProp, hasError: hasErrorProp, reduceMotion: reduceMotionProp, defaultChecked, disabled: disabledProp, required, maxFontSizeMultiplier, onChange, ref, ...viewProps }) {
52
52
  const uid = `uds-radio-${useId()}`;
53
53
  const radioGroup = useRadioGroupContext();
54
- const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier("control", maxFontSizeMultiplier) ?? 0;
55
- const fontScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier);
54
+ const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier("control", maxFontSizeMultiplier);
56
55
  const disabled = Boolean(disabledProp || radioGroup?.disabled);
57
56
  const hasError = hasErrorProp ?? radioGroup?.hasError;
58
57
  const reduceMotion = reduceMotionProp ?? radioGroup?.reduceMotion;
@@ -107,6 +106,8 @@ const Radio = memo(function Radio({ label, labelPosition = "start", variant: var
107
106
  value: valueState,
108
107
  pressed
109
108
  });
109
+ const radioBaseSize = radioStyles.radio.width;
110
+ const fontScaleFactor = useComponentFontScale(resolvedMaxFontSizeMultiplier, typeof radioBaseSize === "number" ? radioBaseSize : void 0);
110
111
  const rootStyle = useMemo(() => [radioStyles.root, { opacity: disabled ? .5 : 1 }], [radioStyles.root, disabled]);
111
112
  const radioBoxStyle = useMemo(() => {
112
113
  const { width, height } = radioStyles.radio;
@@ -1 +1 @@
1
- {"version":3,"file":"Radio.js","names":["Pressable"],"sources":["../../../src/components/Radio/Radio.tsx"],"sourcesContent":["import type { RadioVariant, UniversalRadioProps } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { memo, useCallback, useEffect, useId, useMemo, useState } from 'react';\nimport type { ViewProps } from 'react-native';\nimport { View } from 'react-native';\nimport Animated, { useAnimatedStyle, useDerivedValue, withSpring } from 'react-native-reanimated';\n\nimport { radioStyles } from '../../../generated/styles';\nimport { useFontScale } from '../../fontScaling/useFontScale';\nimport { useMaxFontSizeMultiplier } from '../../fontScaling/useMaxFontSizeMultiplier';\nimport { BUTTON_SPRING_CONFIG, SCALE_EFFECTS } from '../../motion';\nimport { FormLabel } from '../FormLabel';\nimport { Pressable } from '../Pressable';\nimport { useRadioGroupContext } from './RadioGroupContext';\n\n/* -------------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------------- */\n\n/** Derived from RadioValue - maps boolean to 'checked'/'unchecked' string literals */\ntype RadioValueState = 'checked' | 'unchecked';\n\ninterface RadioProps extends Omit<ViewProps, 'style'>, UniversalRadioProps {\n /** Ref to the underlying View */\n ref?: Ref<View>;\n /** Default checked state for uncontrolled mode */\n defaultChecked?: boolean;\n /** Whether the radio is disabled */\n disabled?: boolean;\n /** Whether the radio is required */\n required?: boolean;\n /**\n * Caps how far the circle, dot, and label grow with the OS text-size\n * setting; all three scale by the same factor. Set null to remove the cap;\n * override app-wide via UDSFontScalingProvider.\n * @default 2\n */\n maxFontSizeMultiplier?: number | null;\n /** Callback fired when the checked state changes */\n onChange?: (checked: boolean) => void;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Variant Mapping */\n/* -------------------------------------------------------------------------- */\n\n/** All style variants available in the generated styles (includes error variants) */\ntype RadioStyleVariant = 'primary' | 'secondary' | 'alert' | 'alert-secondary';\n\nconst VARIANT_ERROR_MAP: Record<RadioVariant, RadioStyleVariant> = {\n primary: 'alert',\n secondary: 'alert-secondary',\n};\n\n/* -------------------------------------------------------------------------- */\n/* Radio Component */\n/* -------------------------------------------------------------------------- */\n\n/**\n * **⚙️ A radio component**\n *\n * @description\n * A radio component allows users to select one option from a set.\n * Unlike checkbox, radio buttons cannot have multiple selected in a group,\n * allowing only one selection at a time.\n *\n * @category Form\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Radio } from '@yahoo/uds-mobile/Radio';\n *\n * <Radio label=\"Option A\" />\n * <Radio label=\"Option B\" checked={true} onChange={setChecked} />\n * ```\n *\n * @usage\n * - Forms: For selecting a single option from multiple choices\n * - Settings: For toggling between mutually exclusive preferences\n *\n * @accessibility\n * - Sets `accessibilityRole=\"radio\"` automatically\n * - Announces checked/unchecked state\n * - Label is used as accessibility label\n * - Supports `reduceMotion` for users who prefer reduced motion\n *\n * @see {@link Checkbox} for multi-select options\n * @see {@link Switch} for binary toggles\n */\nconst Radio = memo(function Radio({\n // Radio props\n label,\n labelPosition = 'start',\n variant: variantInput,\n size: sizeInput,\n value: valueProp,\n checked: checkedProp,\n hasError: hasErrorProp,\n reduceMotion: reduceMotionProp,\n // Native props\n defaultChecked,\n disabled: disabledProp,\n required,\n maxFontSizeMultiplier,\n onChange,\n ref,\n ...viewProps\n}: RadioProps) {\n const generatedId = useId();\n const uid = `uds-radio-${generatedId}`;\n const radioGroup = useRadioGroupContext();\n\n // One capped factor governs circle, dot, and label so they stay cohesive.\n const resolvedMaxFontSizeMultiplier =\n useMaxFontSizeMultiplier('control', maxFontSizeMultiplier) ?? 0;\n const fontScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier);\n\n const disabled = Boolean(disabledProp || radioGroup?.disabled);\n const hasError = hasErrorProp ?? radioGroup?.hasError;\n const reduceMotion = reduceMotionProp ?? radioGroup?.reduceMotion;\n const size = sizeInput ?? radioGroup?.size ?? 'md';\n const variantProp = variantInput ?? radioGroup?.variant ?? 'primary';\n const value = valueProp ?? '';\n\n /* --------------------------------- State ---------------------------------- */\n const isInRadioGroup = radioGroup !== undefined;\n const isControlled = checkedProp !== undefined;\n const [internalChecked, setInternalChecked] = useState<boolean>(\n isControlled ? checkedProp : (defaultChecked ?? false),\n );\n const [pressed, setPressed] = useState(false);\n\n // Use controlled value if provided, otherwise use internal state\n const checked = isInRadioGroup\n ? radioGroup.value === value\n : isControlled\n ? checkedProp\n : internalChecked;\n\n useEffect(() => {\n if (!__DEV__ || !isInRadioGroup || valueProp) {\n return;\n }\n\n console.warn(\n '[UDS Mobile] Radio: A Radio rendered inside RadioGroup requires a unique, non-empty value prop.',\n );\n }, [isInRadioGroup, valueProp]);\n\n /* ------------------------------ Derived Values ---------------------------- */\n const valueState: RadioValueState = useMemo(() => {\n return checked ? 'checked' : 'unchecked';\n }, [checked]);\n\n /* -------------------------------- Animation ------------------------------- */\n const shouldAnimate = !reduceMotion;\n\n // Scale animation for press feedback (disabled when reduceMotion is true)\n const scaleProgress = useDerivedValue(\n () =>\n shouldAnimate\n ? withSpring(pressed ? SCALE_EFFECTS.down : SCALE_EFFECTS.none, BUTTON_SPRING_CONFIG)\n : SCALE_EFFECTS.none, // No scale effect when reduceMotion is true\n [pressed, shouldAnimate],\n );\n\n const radioAnimatedStyle = useAnimatedStyle(() => ({\n transform: [{ scale: scaleProgress.value }],\n }));\n\n /* -------------------------------- Handlers -------------------------------- */\n const handlePress = useCallback(() => {\n if (disabled) {\n return;\n }\n\n if (isInRadioGroup) {\n if (radioGroup.value === value) {\n return;\n }\n\n radioGroup.setValue(value);\n onChange?.(true);\n return;\n }\n\n const newChecked = !checked;\n\n if (!isControlled) {\n setInternalChecked(newChecked);\n }\n\n onChange?.(newChecked);\n }, [disabled, isInRadioGroup, checked, isControlled, onChange, radioGroup, value]);\n\n const handlePressIn = useCallback(() => {\n setPressed(true);\n }, []);\n\n const handlePressOut = useCallback(() => {\n setPressed(false);\n }, []);\n\n /* --------------------------------- Styles --------------------------------- */\n // Compute the style variant (primary/secondary maps to alert variants when hasError)\n const styleVariant: RadioStyleVariant = hasError ? VARIANT_ERROR_MAP[variantProp] : variantProp;\n\n radioStyles.useVariants({\n size,\n variant: styleVariant, // primary, secondary, alert, alert-secondary\n value: valueState, // checked, unchecked\n pressed,\n });\n\n const rootStyle = useMemo(\n () => [radioStyles.root, { opacity: disabled ? 0.5 : 1 }],\n [radioStyles.root, disabled],\n );\n\n const radioBoxStyle = useMemo(() => {\n // The circle grows by the capped factor alongside its label; borderWidth\n // stays fixed (stroke weight doesn't scale in the OS's own controls).\n const { width, height } = radioStyles.radio;\n const scaledBox =\n fontScaleFactor !== 1 && typeof width === 'number' && typeof height === 'number'\n ? {\n width: Math.round(width * fontScaleFactor),\n height: Math.round(height * fontScaleFactor),\n }\n : undefined;\n\n return [\n radioStyles.radio,\n scaledBox,\n {\n alignItems: 'center' as const,\n justifyContent: 'center' as const,\n borderRadius: 9999,\n },\n radioAnimatedStyle,\n ];\n }, [radioStyles.radio, fontScaleFactor, radioAnimatedStyle]);\n\n // Inner circle size: fixed 8x8px to match web implementation\n const INNER_CIRCLE_SIZE = 8;\n\n const circleStyle = useMemo(\n () => [\n radioStyles.radioCircle,\n {\n width: Math.round(INNER_CIRCLE_SIZE * fontScaleFactor),\n height: Math.round(INNER_CIRCLE_SIZE * fontScaleFactor),\n borderRadius: 9999,\n },\n ],\n [radioStyles.radioCircle, fontScaleFactor],\n );\n\n /* ---------------------------- Render Helpers ------------------------------ */\n const labelContent = useMemo(() => {\n if (!label) {\n return null;\n }\n\n return (\n <FormLabel\n color=\"inherit\"\n variant=\"inherit\"\n label={label}\n required={required}\n showRequiredAsterisk={required}\n hasError={hasError}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={radioStyles.text}\n />\n );\n }, [label, required, hasError, resolvedMaxFontSizeMultiplier, radioStyles.text]);\n\n const a11yState = useMemo(\n () => ({\n disabled,\n checked,\n }),\n [disabled, checked],\n );\n\n /* --------------------------------- Render --------------------------------- */\n return (\n <Pressable\n ref={ref}\n nativeID={uid}\n onPress={handlePress}\n onPressIn={handlePressIn}\n onPressOut={handlePressOut}\n disabled={disabled}\n flexDirection={labelPosition === 'start' ? 'row' : 'row-reverse'}\n alignItems=\"center\"\n alignSelf=\"flex-start\"\n accessibilityRole=\"radio\"\n accessibilityState={a11yState}\n accessibilityLabel={typeof label === 'string' ? label : undefined}\n {...viewProps}\n style={rootStyle}\n >\n <Animated.View style={radioBoxStyle}>{checked && <View style={circleStyle} />}</Animated.View>\n\n {labelContent}\n </Pressable>\n );\n});\n\nRadio.displayName = 'Radio';\n\nexport { Radio, type RadioProps };\n"],"mappings":"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"}
1
+ {"version":3,"file":"Radio.js","names":["Pressable"],"sources":["../../../src/components/Radio/Radio.tsx"],"sourcesContent":["import type { RadioVariant, UniversalRadioProps } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { memo, useCallback, useEffect, useId, useMemo, useState } from 'react';\nimport type { ViewProps } from 'react-native';\nimport { View } from 'react-native';\nimport Animated, { useAnimatedStyle, useDerivedValue, withSpring } from 'react-native-reanimated';\n\nimport { radioStyles } from '../../../generated/styles';\nimport { useComponentFontScale } from '../../fontScaling/useFontScale';\nimport { useMaxFontSizeMultiplier } from '../../fontScaling/useMaxFontSizeMultiplier';\nimport { BUTTON_SPRING_CONFIG, SCALE_EFFECTS } from '../../motion';\nimport { FormLabel } from '../FormLabel';\nimport { Pressable } from '../Pressable';\nimport { useRadioGroupContext } from './RadioGroupContext';\n\n/* -------------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------------- */\n\n/** Derived from RadioValue - maps boolean to 'checked'/'unchecked' string literals */\ntype RadioValueState = 'checked' | 'unchecked';\n\ninterface RadioProps extends Omit<ViewProps, 'style'>, UniversalRadioProps {\n /** Ref to the underlying View */\n ref?: Ref<View>;\n /** Default checked state for uncontrolled mode */\n defaultChecked?: boolean;\n /** Whether the radio is disabled */\n disabled?: boolean;\n /** Whether the radio is required */\n required?: boolean;\n /**\n * Caps how far the circle, dot, and label grow with the OS text-size\n * setting; all three scale by the same factor. Set null to remove the cap;\n * override app-wide via UDSFontScalingProvider.\n * @default 2\n */\n maxFontSizeMultiplier?: number | null;\n /** Callback fired when the checked state changes */\n onChange?: (checked: boolean) => void;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Variant Mapping */\n/* -------------------------------------------------------------------------- */\n\n/** All style variants available in the generated styles (includes error variants) */\ntype RadioStyleVariant = 'primary' | 'secondary' | 'alert' | 'alert-secondary';\n\nconst VARIANT_ERROR_MAP: Record<RadioVariant, RadioStyleVariant> = {\n primary: 'alert',\n secondary: 'alert-secondary',\n};\n\n/* -------------------------------------------------------------------------- */\n/* Radio Component */\n/* -------------------------------------------------------------------------- */\n\n/**\n * **⚙️ A radio component**\n *\n * @description\n * A radio component allows users to select one option from a set.\n * Unlike checkbox, radio buttons cannot have multiple selected in a group,\n * allowing only one selection at a time.\n *\n * @category Form\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Radio } from '@yahoo/uds-mobile/Radio';\n *\n * <Radio label=\"Option A\" />\n * <Radio label=\"Option B\" checked={true} onChange={setChecked} />\n * ```\n *\n * @usage\n * - Forms: For selecting a single option from multiple choices\n * - Settings: For toggling between mutually exclusive preferences\n *\n * @accessibility\n * - Sets `accessibilityRole=\"radio\"` automatically\n * - Announces checked/unchecked state\n * - Label is used as accessibility label\n * - Supports `reduceMotion` for users who prefer reduced motion\n *\n * @see {@link Checkbox} for multi-select options\n * @see {@link Switch} for binary toggles\n */\nconst Radio = memo(function Radio({\n // Radio props\n label,\n labelPosition = 'start',\n variant: variantInput,\n size: sizeInput,\n value: valueProp,\n checked: checkedProp,\n hasError: hasErrorProp,\n reduceMotion: reduceMotionProp,\n // Native props\n defaultChecked,\n disabled: disabledProp,\n required,\n maxFontSizeMultiplier,\n onChange,\n ref,\n ...viewProps\n}: RadioProps) {\n const generatedId = useId();\n const uid = `uds-radio-${generatedId}`;\n const radioGroup = useRadioGroupContext();\n\n // One resolved cap governs circle, dot, and label. Android resolves the\n // geometry from the circle's base size on its nonlinear curve.\n const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier('control', maxFontSizeMultiplier);\n\n const disabled = Boolean(disabledProp || radioGroup?.disabled);\n const hasError = hasErrorProp ?? radioGroup?.hasError;\n const reduceMotion = reduceMotionProp ?? radioGroup?.reduceMotion;\n const size = sizeInput ?? radioGroup?.size ?? 'md';\n const variantProp = variantInput ?? radioGroup?.variant ?? 'primary';\n const value = valueProp ?? '';\n\n /* --------------------------------- State ---------------------------------- */\n const isInRadioGroup = radioGroup !== undefined;\n const isControlled = checkedProp !== undefined;\n const [internalChecked, setInternalChecked] = useState<boolean>(\n isControlled ? checkedProp : (defaultChecked ?? false),\n );\n const [pressed, setPressed] = useState(false);\n\n // Use controlled value if provided, otherwise use internal state\n const checked = isInRadioGroup\n ? radioGroup.value === value\n : isControlled\n ? checkedProp\n : internalChecked;\n\n useEffect(() => {\n if (!__DEV__ || !isInRadioGroup || valueProp) {\n return;\n }\n\n console.warn(\n '[UDS Mobile] Radio: A Radio rendered inside RadioGroup requires a unique, non-empty value prop.',\n );\n }, [isInRadioGroup, valueProp]);\n\n /* ------------------------------ Derived Values ---------------------------- */\n const valueState: RadioValueState = useMemo(() => {\n return checked ? 'checked' : 'unchecked';\n }, [checked]);\n\n /* -------------------------------- Animation ------------------------------- */\n const shouldAnimate = !reduceMotion;\n\n // Scale animation for press feedback (disabled when reduceMotion is true)\n const scaleProgress = useDerivedValue(\n () =>\n shouldAnimate\n ? withSpring(pressed ? SCALE_EFFECTS.down : SCALE_EFFECTS.none, BUTTON_SPRING_CONFIG)\n : SCALE_EFFECTS.none, // No scale effect when reduceMotion is true\n [pressed, shouldAnimate],\n );\n\n const radioAnimatedStyle = useAnimatedStyle(() => ({\n transform: [{ scale: scaleProgress.value }],\n }));\n\n /* -------------------------------- Handlers -------------------------------- */\n const handlePress = useCallback(() => {\n if (disabled) {\n return;\n }\n\n if (isInRadioGroup) {\n if (radioGroup.value === value) {\n return;\n }\n\n radioGroup.setValue(value);\n onChange?.(true);\n return;\n }\n\n const newChecked = !checked;\n\n if (!isControlled) {\n setInternalChecked(newChecked);\n }\n\n onChange?.(newChecked);\n }, [disabled, isInRadioGroup, checked, isControlled, onChange, radioGroup, value]);\n\n const handlePressIn = useCallback(() => {\n setPressed(true);\n }, []);\n\n const handlePressOut = useCallback(() => {\n setPressed(false);\n }, []);\n\n /* --------------------------------- Styles --------------------------------- */\n // Compute the style variant (primary/secondary maps to alert variants when hasError)\n const styleVariant: RadioStyleVariant = hasError ? VARIANT_ERROR_MAP[variantProp] : variantProp;\n\n radioStyles.useVariants({\n size,\n variant: styleVariant, // primary, secondary, alert, alert-secondary\n value: valueState, // checked, unchecked\n pressed,\n });\n\n const radioBaseSize = radioStyles.radio.width;\n const fontScaleFactor = useComponentFontScale(\n resolvedMaxFontSizeMultiplier,\n typeof radioBaseSize === 'number' ? radioBaseSize : undefined,\n );\n\n const rootStyle = useMemo(\n () => [radioStyles.root, { opacity: disabled ? 0.5 : 1 }],\n [radioStyles.root, disabled],\n );\n\n const radioBoxStyle = useMemo(() => {\n // The circle grows by the capped factor alongside its label; borderWidth\n // stays fixed (stroke weight doesn't scale in the OS's own controls).\n const { width, height } = radioStyles.radio;\n const scaledBox =\n fontScaleFactor !== 1 && typeof width === 'number' && typeof height === 'number'\n ? {\n width: Math.round(width * fontScaleFactor),\n height: Math.round(height * fontScaleFactor),\n }\n : undefined;\n\n return [\n radioStyles.radio,\n scaledBox,\n {\n alignItems: 'center' as const,\n justifyContent: 'center' as const,\n borderRadius: 9999,\n },\n radioAnimatedStyle,\n ];\n }, [radioStyles.radio, fontScaleFactor, radioAnimatedStyle]);\n\n // Inner circle size: fixed 8x8px to match web implementation\n const INNER_CIRCLE_SIZE = 8;\n\n const circleStyle = useMemo(\n () => [\n radioStyles.radioCircle,\n {\n width: Math.round(INNER_CIRCLE_SIZE * fontScaleFactor),\n height: Math.round(INNER_CIRCLE_SIZE * fontScaleFactor),\n borderRadius: 9999,\n },\n ],\n [radioStyles.radioCircle, fontScaleFactor],\n );\n\n /* ---------------------------- Render Helpers ------------------------------ */\n const labelContent = useMemo(() => {\n if (!label) {\n return null;\n }\n\n return (\n <FormLabel\n color=\"inherit\"\n variant=\"inherit\"\n label={label}\n required={required}\n showRequiredAsterisk={required}\n hasError={hasError}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={radioStyles.text}\n />\n );\n }, [label, required, hasError, resolvedMaxFontSizeMultiplier, radioStyles.text]);\n\n const a11yState = useMemo(\n () => ({\n disabled,\n checked,\n }),\n [disabled, checked],\n );\n\n /* --------------------------------- Render --------------------------------- */\n return (\n <Pressable\n ref={ref}\n nativeID={uid}\n onPress={handlePress}\n onPressIn={handlePressIn}\n onPressOut={handlePressOut}\n disabled={disabled}\n flexDirection={labelPosition === 'start' ? 'row' : 'row-reverse'}\n alignItems=\"center\"\n alignSelf=\"flex-start\"\n accessibilityRole=\"radio\"\n accessibilityState={a11yState}\n accessibilityLabel={typeof label === 'string' ? label : undefined}\n {...viewProps}\n style={rootStyle}\n >\n <Animated.View style={radioBoxStyle}>{checked && <View style={circleStyle} />}</Animated.View>\n\n {labelContent}\n </Pressable>\n );\n});\n\nRadio.displayName = 'Radio';\n\nexport { Radio, type RadioProps };\n"],"mappings":"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"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  require("../../_virtual/_rolldown/runtime.cjs");
4
4
  const require_components_HStack = require("../HStack.cjs");
5
+ const require_fontScaling_useMaxFontSizeMultiplier = require("../../fontScaling/useMaxFontSizeMultiplier.cjs");
5
6
  const require_components_Text = require("../Text.cjs");
6
7
  const require_components_InputHelpText = require("../InputHelpText.cjs");
7
8
  const require_components_VStack = require("../VStack.cjs");
@@ -35,8 +36,9 @@ let lodash_es = require("lodash-es");
35
36
  * </Select>
36
37
  * ```
37
38
  */
38
- const Select = (0, react.memo)(function Select({ label, helpText, helperTextIcon, placeholder, size = "md", disabled, required, hasError, readOnly, width = "100%", reduceMotion = false, startIcon, endIcon, value: valueProp, defaultValue = "", onChange, children, testID }) {
39
+ const Select = (0, react.memo)(function Select({ label, helpText, helperTextIcon, placeholder, size = "md", disabled, required, hasError, readOnly, width = "100%", maxFontSizeMultiplier, reduceMotion = false, startIcon, endIcon, value: valueProp, defaultValue = "", onChange, children, testID }) {
39
40
  const uid = `uds-select-${(0, react.useId)()}`;
41
+ const resolvedMaxFontSizeMultiplier = require_fontScaling_useMaxFontSizeMultiplier.useMaxFontSizeMultiplier("control", maxFontSizeMultiplier);
40
42
  const triggerRef = (0, react.useRef)(null);
41
43
  const [triggerRect, setTriggerRect] = (0, react.useState)(null);
42
44
  const [highlightedValue, setHighlightedValue] = (0, react.useState)(null);
@@ -54,16 +56,28 @@ const Select = (0, react.memo)(function Select({ label, helpText, helperTextIcon
54
56
  }
55
57
  });
56
58
  const registerItem = (0, react.useCallback)((itemValue, itemLabel) => {
57
- if (itemsRef.current.get(itemValue) === itemLabel) return;
58
- itemsRef.current.set(itemValue, itemLabel);
59
+ const existing = itemsRef.current.get(itemValue);
60
+ itemsRef.current.set(itemValue, {
61
+ label: itemLabel,
62
+ registrations: (existing?.registrations ?? 0) + 1
63
+ });
64
+ if (existing?.label === itemLabel) return;
59
65
  setItemsVersion((version) => version + 1);
60
66
  }, []);
61
67
  const unregisterItem = (0, react.useCallback)((itemValue) => {
62
- if (!itemsRef.current.has(itemValue)) return;
68
+ const existing = itemsRef.current.get(itemValue);
69
+ if (!existing) return;
70
+ if (existing.registrations > 1) {
71
+ itemsRef.current.set(itemValue, {
72
+ ...existing,
73
+ registrations: existing.registrations - 1
74
+ });
75
+ return;
76
+ }
63
77
  itemsRef.current.delete(itemValue);
64
78
  setItemsVersion((version) => version + 1);
65
79
  }, []);
66
- const getItemLabel = (0, react.useCallback)((itemValue) => itemsRef.current.get(itemValue), []);
80
+ const getItemLabel = (0, react.useCallback)((itemValue) => itemsRef.current.get(itemValue)?.label, []);
67
81
  const fieldContext = (0, react.useMemo)(() => ({
68
82
  size,
69
83
  disabled,
@@ -73,7 +87,8 @@ const Select = (0, react.memo)(function Select({ label, helpText, helperTextIcon
73
87
  width,
74
88
  reduceMotion,
75
89
  placeholder,
76
- uid
90
+ uid,
91
+ maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier
77
92
  }), [
78
93
  disabled,
79
94
  hasError,
@@ -81,6 +96,7 @@ const Select = (0, react.memo)(function Select({ label, helpText, helperTextIcon
81
96
  readOnly,
82
97
  reduceMotion,
83
98
  required,
99
+ resolvedMaxFontSizeMultiplier,
84
100
  size,
85
101
  uid,
86
102
  width
@@ -133,9 +149,13 @@ const Select = (0, react.memo)(function Select({ label, helpText, helperTextIcon
133
149
  alignItems: "flex-end",
134
150
  spacingBottom: "2",
135
151
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Text.Text, {
152
+ variant: "inherit",
153
+ maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
136
154
  style: generated_styles.inputStyles.label,
137
155
  children: content
138
156
  }), required && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Text.Text, {
157
+ variant: "inherit",
158
+ maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
139
159
  style: generated_styles.inputStyles.labelRequired,
140
160
  children: "*"
141
161
  })]
@@ -143,6 +163,7 @@ const Select = (0, react.memo)(function Select({ label, helpText, helperTextIcon
143
163
  }, [
144
164
  label,
145
165
  required,
166
+ resolvedMaxFontSizeMultiplier,
146
167
  generated_styles.inputStyles.label,
147
168
  generated_styles.inputStyles.labelRequired
148
169
  ]);
@@ -157,6 +178,7 @@ const Select = (0, react.memo)(function Select({ label, helpText, helperTextIcon
157
178
  readOnly,
158
179
  hasError,
159
180
  pressed: open,
181
+ maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
160
182
  children: content
161
183
  });
162
184
  }, [
@@ -166,6 +188,7 @@ const Select = (0, react.memo)(function Select({ label, helpText, helperTextIcon
166
188
  helperTextIcon,
167
189
  open,
168
190
  readOnly,
191
+ resolvedMaxFontSizeMultiplier,
169
192
  size,
170
193
  valueState
171
194
  ]);
@@ -16,6 +16,8 @@ interface SelectProps extends Omit<UniversalSelectProps<IconSlotType>, 'width'>
16
16
  onChange?: (value: string) => void;
17
17
  /** Container width. @default '100%' */
18
18
  width?: number | `${number}%` | '100%';
19
+ /** Caps text, icons, and field geometry at the control scale. Set null to remove the cap. @default 2 */
20
+ maxFontSizeMultiplier?: number | null;
19
21
  children?: ReactNode;
20
22
  testID?: string;
21
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Select.d.cts","names":[],"sources":["../../../src/components/Select/Select.tsx"],"mappings":";;;;;;;UAiBU,WAAA,SAAoB,IAAA,CAAK,oBAAA,CAAqB,YAAA;;EAEtD,WAAA;EAFQ;EAIR,KAAA;;EAEA,YAAA;EANiC;EAQjC,QAAA,IAAY,KAAA;EARgB;EAU5B,KAAA;EACA,QAAA,GAAW,SAAA;EACX,MAAA;AAAA;;;;;;;;;;;;;AAAM;;;;;;;;;;cAyBF,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
1
+ {"version":3,"file":"Select.d.cts","names":[],"sources":["../../../src/components/Select/Select.tsx"],"mappings":";;;;;;;UAkBU,WAAA,SAAoB,IAAA,CAAK,oBAAA,CAAqB,YAAA;;EAEtD,WAAA;EAFQ;EAIR,KAAA;;EAEA,YAAA;EANiC;EAQjC,QAAA,IAAY,KAAA;EARgB;EAU5B,KAAA;EAVgC;EAYhC,qBAAA;EACA,QAAA,GAAW,SAAA;EACX,MAAA;AAAA;;;;;;;;;;;;AAAM;;;;;;;;;;;cAyBF,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
@@ -16,6 +16,8 @@ interface SelectProps extends Omit<UniversalSelectProps<IconSlotType>, 'width'>
16
16
  onChange?: (value: string) => void;
17
17
  /** Container width. @default '100%' */
18
18
  width?: number | `${number}%` | '100%';
19
+ /** Caps text, icons, and field geometry at the control scale. Set null to remove the cap. @default 2 */
20
+ maxFontSizeMultiplier?: number | null;
19
21
  children?: ReactNode;
20
22
  testID?: string;
21
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Select.d.ts","names":[],"sources":["../../../src/components/Select/Select.tsx"],"mappings":";;;;;;;UAiBU,WAAA,SAAoB,IAAA,CAAK,oBAAA,CAAqB,YAAA;;EAEtD,WAAA;EAFQ;EAIR,KAAA;;EAEA,YAAA;EANiC;EAQjC,QAAA,IAAY,KAAA;EARgB;EAU5B,KAAA;EACA,QAAA,GAAW,SAAA;EACX,MAAA;AAAA;;;;;;;;;;;;;AAAM;;;;;;;;;;cAyBF,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
1
+ {"version":3,"file":"Select.d.ts","names":[],"sources":["../../../src/components/Select/Select.tsx"],"mappings":";;;;;;;UAkBU,WAAA,SAAoB,IAAA,CAAK,oBAAA,CAAqB,YAAA;;EAEtD,WAAA;EAFQ;EAIR,KAAA;;EAEA,YAAA;EANiC;EAQjC,QAAA,IAAY,KAAA;EARgB;EAU5B,KAAA;EAVgC;EAYhC,qBAAA;EACA,QAAA,GAAW,SAAA;EACX,MAAA;AAAA;;;;;;;;;;;;AAAM;;;;;;;;;;;cAyBF,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
@@ -1,5 +1,6 @@
1
1
  /*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
2
2
  import { HStack } from "../HStack.js";
3
+ import { useMaxFontSizeMultiplier } from "../../fontScaling/useMaxFontSizeMultiplier.js";
3
4
  import { Text } from "../Text.js";
4
5
  import { InputHelpText } from "../InputHelpText.js";
5
6
  import { VStack } from "../VStack.js";
@@ -33,8 +34,9 @@ import { isFunction } from "lodash-es";
33
34
  * </Select>
34
35
  * ```
35
36
  */
36
- const Select = memo(function Select({ label, helpText, helperTextIcon, placeholder, size = "md", disabled, required, hasError, readOnly, width = "100%", reduceMotion = false, startIcon, endIcon, value: valueProp, defaultValue = "", onChange, children, testID }) {
37
+ const Select = memo(function Select({ label, helpText, helperTextIcon, placeholder, size = "md", disabled, required, hasError, readOnly, width = "100%", maxFontSizeMultiplier, reduceMotion = false, startIcon, endIcon, value: valueProp, defaultValue = "", onChange, children, testID }) {
37
38
  const uid = `uds-select-${useId()}`;
39
+ const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier("control", maxFontSizeMultiplier);
38
40
  const triggerRef = useRef(null);
39
41
  const [triggerRect, setTriggerRect] = useState(null);
40
42
  const [highlightedValue, setHighlightedValue] = useState(null);
@@ -52,16 +54,28 @@ const Select = memo(function Select({ label, helpText, helperTextIcon, placehold
52
54
  }
53
55
  });
54
56
  const registerItem = useCallback((itemValue, itemLabel) => {
55
- if (itemsRef.current.get(itemValue) === itemLabel) return;
56
- itemsRef.current.set(itemValue, itemLabel);
57
+ const existing = itemsRef.current.get(itemValue);
58
+ itemsRef.current.set(itemValue, {
59
+ label: itemLabel,
60
+ registrations: (existing?.registrations ?? 0) + 1
61
+ });
62
+ if (existing?.label === itemLabel) return;
57
63
  setItemsVersion((version) => version + 1);
58
64
  }, []);
59
65
  const unregisterItem = useCallback((itemValue) => {
60
- if (!itemsRef.current.has(itemValue)) return;
66
+ const existing = itemsRef.current.get(itemValue);
67
+ if (!existing) return;
68
+ if (existing.registrations > 1) {
69
+ itemsRef.current.set(itemValue, {
70
+ ...existing,
71
+ registrations: existing.registrations - 1
72
+ });
73
+ return;
74
+ }
61
75
  itemsRef.current.delete(itemValue);
62
76
  setItemsVersion((version) => version + 1);
63
77
  }, []);
64
- const getItemLabel = useCallback((itemValue) => itemsRef.current.get(itemValue), []);
78
+ const getItemLabel = useCallback((itemValue) => itemsRef.current.get(itemValue)?.label, []);
65
79
  const fieldContext = useMemo(() => ({
66
80
  size,
67
81
  disabled,
@@ -71,7 +85,8 @@ const Select = memo(function Select({ label, helpText, helperTextIcon, placehold
71
85
  width,
72
86
  reduceMotion,
73
87
  placeholder,
74
- uid
88
+ uid,
89
+ maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier
75
90
  }), [
76
91
  disabled,
77
92
  hasError,
@@ -79,6 +94,7 @@ const Select = memo(function Select({ label, helpText, helperTextIcon, placehold
79
94
  readOnly,
80
95
  reduceMotion,
81
96
  required,
97
+ resolvedMaxFontSizeMultiplier,
82
98
  size,
83
99
  uid,
84
100
  width
@@ -131,9 +147,13 @@ const Select = memo(function Select({ label, helpText, helperTextIcon, placehold
131
147
  alignItems: "flex-end",
132
148
  spacingBottom: "2",
133
149
  children: [/* @__PURE__ */ jsx(Text, {
150
+ variant: "inherit",
151
+ maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
134
152
  style: inputStyles.label,
135
153
  children: content
136
154
  }), required && /* @__PURE__ */ jsx(Text, {
155
+ variant: "inherit",
156
+ maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
137
157
  style: inputStyles.labelRequired,
138
158
  children: "*"
139
159
  })]
@@ -141,6 +161,7 @@ const Select = memo(function Select({ label, helpText, helperTextIcon, placehold
141
161
  }, [
142
162
  label,
143
163
  required,
164
+ resolvedMaxFontSizeMultiplier,
144
165
  inputStyles.label,
145
166
  inputStyles.labelRequired
146
167
  ]);
@@ -155,6 +176,7 @@ const Select = memo(function Select({ label, helpText, helperTextIcon, placehold
155
176
  readOnly,
156
177
  hasError,
157
178
  pressed: open,
179
+ maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
158
180
  children: content
159
181
  });
160
182
  }, [
@@ -164,6 +186,7 @@ const Select = memo(function Select({ label, helpText, helperTextIcon, placehold
164
186
  helperTextIcon,
165
187
  open,
166
188
  readOnly,
189
+ resolvedMaxFontSizeMultiplier,
167
190
  size,
168
191
  valueState
169
192
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"Select.js","names":[],"sources":["../../../src/components/Select/Select.tsx"],"sourcesContent":["import type { UniversalSelectProps } from '@yahoo/uds-types';\nimport { isFunction } from 'lodash-es';\nimport type { ReactNode } from 'react';\nimport { memo, useCallback, useId, useMemo, useRef, useState } from 'react';\nimport type { View } from 'react-native';\n\nimport { inputStyles } from '../../../generated/styles';\nimport { HStack } from '../HStack';\nimport type { IconSlotType } from '../IconSlot';\nimport { InputHelpText } from '../InputHelpText';\nimport { useControllableState } from '../internal/Overlay';\nimport { Text } from '../Text';\nimport { VStack } from '../VStack';\nimport { SelectContext, SelectFieldContext } from './selectContext';\nimport { SelectTrigger } from './SelectTrigger';\nimport type { SelectContextValue, SelectFieldContextValue } from './types';\n\ninterface SelectProps extends Omit<UniversalSelectProps<IconSlotType>, 'width'> {\n /** Placeholder text shown when no value is selected. */\n placeholder?: string;\n /** Selected value for controlled usage. */\n value?: string;\n /** Initial value for uncontrolled usage. @default '' */\n defaultValue?: string;\n /** Called when the selected value changes. */\n onChange?: (value: string) => void;\n /** Container width. @default '100%' */\n width?: number | `${number}%` | '100%';\n children?: ReactNode;\n testID?: string;\n}\n\n/**\n * **⚙️ A composable Select component**\n *\n * @description\n * Select lets users pick one value from a list. Compose with `SelectContent` and\n * `SelectItem`, similar to `Tabs` with `TabList` and `Tab`.\n *\n * @category Form\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Select, SelectContent, SelectItem } from '@yahoo/uds-mobile/Select';\n *\n * <Select label=\"Country\" placeholder=\"Select a country\" defaultValue=\"us\">\n * <SelectContent>\n * <SelectItem value=\"us\">United States</SelectItem>\n * <SelectItem value=\"ca\">Canada</SelectItem>\n * </SelectContent>\n * </Select>\n * ```\n */\nconst Select = memo(function Select({\n label,\n helpText,\n helperTextIcon,\n placeholder,\n size = 'md',\n disabled,\n required,\n hasError,\n readOnly,\n width = '100%',\n reduceMotion = false,\n startIcon,\n endIcon,\n value: valueProp,\n defaultValue = '',\n onChange,\n children,\n testID,\n}: SelectProps) {\n const generatedId = useId();\n const uid = `uds-select-${generatedId}`;\n const triggerRef = useRef<View | null>(null);\n const [triggerRect, setTriggerRect] = useState<SelectContextValue['triggerRect']>(null);\n const [highlightedValue, setHighlightedValue] = useState<string | null>(null);\n const itemsRef = useRef(new Map<string, string>());\n const [, setItemsVersion] = useState(0);\n\n const [value, setValue] = useControllableState({\n value: valueProp,\n defaultValue,\n onChange,\n });\n\n const [open, setOpen] = useControllableState({\n defaultValue: false,\n onChange: (nextOpen) => {\n if (!nextOpen) {\n setHighlightedValue(null);\n }\n },\n });\n\n const registerItem = useCallback((itemValue: string, itemLabel: string) => {\n if (itemsRef.current.get(itemValue) === itemLabel) {\n return;\n }\n\n itemsRef.current.set(itemValue, itemLabel);\n setItemsVersion((version) => version + 1);\n }, []);\n\n const unregisterItem = useCallback((itemValue: string) => {\n if (!itemsRef.current.has(itemValue)) {\n return;\n }\n\n itemsRef.current.delete(itemValue);\n setItemsVersion((version) => version + 1);\n }, []);\n\n const getItemLabel = useCallback((itemValue: string) => itemsRef.current.get(itemValue), []);\n\n const fieldContext = useMemo<SelectFieldContextValue>(\n () => ({\n size,\n disabled,\n readOnly,\n required,\n hasError,\n width,\n reduceMotion,\n placeholder,\n uid,\n }),\n [disabled, hasError, placeholder, readOnly, reduceMotion, required, size, uid, width],\n );\n\n const contextValue = useMemo<SelectContextValue>(\n () => ({\n value,\n setValue,\n open,\n setOpen,\n triggerRef,\n triggerRect,\n setTriggerRect,\n registerItem,\n unregisterItem,\n getItemLabel,\n highlightedValue,\n setHighlightedValue,\n field: fieldContext,\n reduceMotion,\n }),\n [\n fieldContext,\n getItemLabel,\n highlightedValue,\n open,\n reduceMotion,\n registerItem,\n setOpen,\n setValue,\n triggerRect,\n unregisterItem,\n value,\n ],\n );\n\n const hasValue = value.length > 0;\n const valueState = hasValue ? 'filled' : 'empty';\n\n inputStyles.useVariants({\n size,\n value: valueState,\n pressed: open,\n readonly: readOnly,\n invalid: hasError,\n });\n\n const rootStyle = useMemo(() => [{ width, opacity: disabled ? 0.5 : 1 }], [disabled, width]);\n\n const labelContent = useMemo(() => {\n if (!label) {\n return null;\n }\n\n const content = isFunction(label) ? label() : label;\n return (\n <HStack columnGap=\"1\" alignItems=\"flex-end\" spacingBottom=\"2\">\n <Text style={inputStyles.label}>{content}</Text>\n {required && <Text style={inputStyles.labelRequired}>*</Text>}\n </HStack>\n );\n }, [label, required, inputStyles.label, inputStyles.labelRequired]);\n\n const helpTextContent = useMemo(() => {\n if (!helpText) {\n return null;\n }\n\n const content = isFunction(helpText) ? helpText() : helpText;\n return (\n <InputHelpText\n startIcon={helperTextIcon}\n size={size}\n isFilled={valueState === 'filled'}\n disabled={disabled}\n readOnly={readOnly}\n hasError={hasError}\n pressed={open}\n >\n {content}\n </InputHelpText>\n );\n }, [disabled, hasError, helpText, helperTextIcon, open, readOnly, size, valueState]);\n\n return (\n <SelectContext.Provider value={contextValue}>\n <SelectFieldContext.Provider value={fieldContext}>\n <VStack testID={testID} style={rootStyle}>\n {labelContent}\n\n <SelectTrigger startIcon={startIcon} endIcon={endIcon} />\n\n {helpTextContent}\n\n {children}\n </VStack>\n </SelectFieldContext.Provider>\n </SelectContext.Provider>\n );\n});\n\nSelect.displayName = 'Select';\n\nexport { Select, type SelectProps };\n"],"mappings":"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"}
1
+ {"version":3,"file":"Select.js","names":[],"sources":["../../../src/components/Select/Select.tsx"],"sourcesContent":["import type { UniversalSelectProps } from '@yahoo/uds-types';\nimport { isFunction } from 'lodash-es';\nimport type { ReactNode } from 'react';\nimport { memo, useCallback, useId, useMemo, useRef, useState } from 'react';\nimport type { View } from 'react-native';\n\nimport { inputStyles } from '../../../generated/styles';\nimport { useMaxFontSizeMultiplier } from '../../fontScaling/useMaxFontSizeMultiplier';\nimport { HStack } from '../HStack';\nimport type { IconSlotType } from '../IconSlot';\nimport { InputHelpText } from '../InputHelpText';\nimport { useControllableState } from '../internal/Overlay';\nimport { Text } from '../Text';\nimport { VStack } from '../VStack';\nimport { SelectContext, SelectFieldContext } from './selectContext';\nimport { SelectTrigger } from './SelectTrigger';\nimport type { SelectContextValue, SelectFieldContextValue } from './types';\n\ninterface SelectProps extends Omit<UniversalSelectProps<IconSlotType>, 'width'> {\n /** Placeholder text shown when no value is selected. */\n placeholder?: string;\n /** Selected value for controlled usage. */\n value?: string;\n /** Initial value for uncontrolled usage. @default '' */\n defaultValue?: string;\n /** Called when the selected value changes. */\n onChange?: (value: string) => void;\n /** Container width. @default '100%' */\n width?: number | `${number}%` | '100%';\n /** Caps text, icons, and field geometry at the control scale. Set null to remove the cap. @default 2 */\n maxFontSizeMultiplier?: number | null;\n children?: ReactNode;\n testID?: string;\n}\n\n/**\n * **⚙️ A composable Select component**\n *\n * @description\n * Select lets users pick one value from a list. Compose with `SelectContent` and\n * `SelectItem`, similar to `Tabs` with `TabList` and `Tab`.\n *\n * @category Form\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Select, SelectContent, SelectItem } from '@yahoo/uds-mobile/Select';\n *\n * <Select label=\"Country\" placeholder=\"Select a country\" defaultValue=\"us\">\n * <SelectContent>\n * <SelectItem value=\"us\">United States</SelectItem>\n * <SelectItem value=\"ca\">Canada</SelectItem>\n * </SelectContent>\n * </Select>\n * ```\n */\nconst Select = memo(function Select({\n label,\n helpText,\n helperTextIcon,\n placeholder,\n size = 'md',\n disabled,\n required,\n hasError,\n readOnly,\n width = '100%',\n maxFontSizeMultiplier,\n reduceMotion = false,\n startIcon,\n endIcon,\n value: valueProp,\n defaultValue = '',\n onChange,\n children,\n testID,\n}: SelectProps) {\n const generatedId = useId();\n const uid = `uds-select-${generatedId}`;\n const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier('control', maxFontSizeMultiplier);\n const triggerRef = useRef<View | null>(null);\n const [triggerRect, setTriggerRect] = useState<SelectContextValue['triggerRect']>(null);\n const [highlightedValue, setHighlightedValue] = useState<string | null>(null);\n // Android's portal can briefly overlap the hidden registry item and its visible copy.\n // Count registrations so one copy unmounting does not erase the selected value's label.\n const itemsRef = useRef(new Map<string, { label: string; registrations: number }>());\n const [, setItemsVersion] = useState(0);\n\n const [value, setValue] = useControllableState({\n value: valueProp,\n defaultValue,\n onChange,\n });\n\n const [open, setOpen] = useControllableState({\n defaultValue: false,\n onChange: (nextOpen) => {\n if (!nextOpen) {\n setHighlightedValue(null);\n }\n },\n });\n\n const registerItem = useCallback((itemValue: string, itemLabel: string) => {\n const existing = itemsRef.current.get(itemValue);\n itemsRef.current.set(itemValue, {\n label: itemLabel,\n registrations: (existing?.registrations ?? 0) + 1,\n });\n\n if (existing?.label === itemLabel) {\n return;\n }\n\n setItemsVersion((version) => version + 1);\n }, []);\n\n const unregisterItem = useCallback((itemValue: string) => {\n const existing = itemsRef.current.get(itemValue);\n if (!existing) {\n return;\n }\n\n if (existing.registrations > 1) {\n itemsRef.current.set(itemValue, {\n ...existing,\n registrations: existing.registrations - 1,\n });\n return;\n }\n\n itemsRef.current.delete(itemValue);\n setItemsVersion((version) => version + 1);\n }, []);\n\n const getItemLabel = useCallback(\n (itemValue: string) => itemsRef.current.get(itemValue)?.label,\n [],\n );\n\n const fieldContext = useMemo<SelectFieldContextValue>(\n () => ({\n size,\n disabled,\n readOnly,\n required,\n hasError,\n width,\n reduceMotion,\n placeholder,\n uid,\n maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,\n }),\n [\n disabled,\n hasError,\n placeholder,\n readOnly,\n reduceMotion,\n required,\n resolvedMaxFontSizeMultiplier,\n size,\n uid,\n width,\n ],\n );\n\n const contextValue = useMemo<SelectContextValue>(\n () => ({\n value,\n setValue,\n open,\n setOpen,\n triggerRef,\n triggerRect,\n setTriggerRect,\n registerItem,\n unregisterItem,\n getItemLabel,\n highlightedValue,\n setHighlightedValue,\n field: fieldContext,\n reduceMotion,\n }),\n [\n fieldContext,\n getItemLabel,\n highlightedValue,\n open,\n reduceMotion,\n registerItem,\n setOpen,\n setValue,\n triggerRect,\n unregisterItem,\n value,\n ],\n );\n\n const hasValue = value.length > 0;\n const valueState = hasValue ? 'filled' : 'empty';\n\n inputStyles.useVariants({\n size,\n value: valueState,\n pressed: open,\n readonly: readOnly,\n invalid: hasError,\n });\n\n const rootStyle = useMemo(() => [{ width, opacity: disabled ? 0.5 : 1 }], [disabled, width]);\n\n const labelContent = useMemo(() => {\n if (!label) {\n return null;\n }\n\n const content = isFunction(label) ? label() : label;\n return (\n <HStack columnGap=\"1\" alignItems=\"flex-end\" spacingBottom=\"2\">\n <Text\n variant=\"inherit\"\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={inputStyles.label}\n >\n {content}\n </Text>\n {required && (\n <Text\n variant=\"inherit\"\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={inputStyles.labelRequired}\n >\n *\n </Text>\n )}\n </HStack>\n );\n }, [\n label,\n required,\n resolvedMaxFontSizeMultiplier,\n inputStyles.label,\n inputStyles.labelRequired,\n ]);\n\n const helpTextContent = useMemo(() => {\n if (!helpText) {\n return null;\n }\n\n const content = isFunction(helpText) ? helpText() : helpText;\n return (\n <InputHelpText\n startIcon={helperTextIcon}\n size={size}\n isFilled={valueState === 'filled'}\n disabled={disabled}\n readOnly={readOnly}\n hasError={hasError}\n pressed={open}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n >\n {content}\n </InputHelpText>\n );\n }, [\n disabled,\n hasError,\n helpText,\n helperTextIcon,\n open,\n readOnly,\n resolvedMaxFontSizeMultiplier,\n size,\n valueState,\n ]);\n\n return (\n <SelectContext.Provider value={contextValue}>\n <SelectFieldContext.Provider value={fieldContext}>\n <VStack testID={testID} style={rootStyle}>\n {labelContent}\n\n <SelectTrigger startIcon={startIcon} endIcon={endIcon} />\n\n {helpTextContent}\n\n {children}\n </VStack>\n </SelectFieldContext.Provider>\n </SelectContext.Provider>\n );\n});\n\nSelect.displayName = 'Select';\n\nexport { Select, type SelectProps };\n"],"mappings":"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"}
@@ -31,7 +31,9 @@ const OFFSCREEN_REGISTRY_STYLE = react_native.StyleSheet.create({ registry: {
31
31
  * Floating listbox container for {@link SelectItem} options.
32
32
  */
33
33
  const SelectContent = (0, react.memo)(function SelectContent({ children, size: _size = "default", variant: _variant = "default", keepOpenOnInteractOutside = false, maxHeight = DEFAULT_MAX_HEIGHT, gutter = DEFAULT_OFFSET, sameWidth = true, style, testID }) {
34
- const { open, setOpen, triggerRect, reduceMotion } = require_components_Select_selectContext.useSelectContext();
34
+ const selectContext = require_components_Select_selectContext.useSelectContext();
35
+ const fieldContext = require_components_Select_selectContext.useSelectFieldContext();
36
+ const { open, setOpen, triggerRect, reduceMotion } = selectContext;
35
37
  const [contentSize, setContentSize] = (0, react.useState)({
36
38
  width: 0,
37
39
  height: 0
@@ -115,28 +117,34 @@ const SelectContent = (0, react.memo)(function SelectContent({ children, size: _
115
117
  open,
116
118
  dismissible: !keepOpenOnInteractOutside,
117
119
  onDismiss: dismiss,
118
- children: () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_reanimated.default.View, {
119
- testID,
120
- style: [
121
- contentStyle,
122
- animatedStyle,
123
- {
124
- overflow: "visible",
125
- zIndex: 1e3
126
- }
127
- ],
128
- onLayout: handleLayout,
129
- accessibilityRole: "menu",
130
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
131
- style: panelStyle,
132
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.ScrollView, {
133
- keyboardShouldPersistTaps: "handled",
134
- nestedScrollEnabled: true,
135
- style: { maxHeight },
136
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_VStack.VStack, {
137
- alignItems: "stretch",
138
- justifyContent: "flex-start",
139
- children
120
+ children: () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Select_selectContext.SelectContext.Provider, {
121
+ value: selectContext,
122
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Select_selectContext.SelectFieldContext.Provider, {
123
+ value: fieldContext,
124
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_reanimated.default.View, {
125
+ testID,
126
+ style: [
127
+ contentStyle,
128
+ animatedStyle,
129
+ {
130
+ overflow: "visible",
131
+ zIndex: 1e3
132
+ }
133
+ ],
134
+ onLayout: handleLayout,
135
+ accessibilityRole: "menu",
136
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
137
+ style: panelStyle,
138
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.ScrollView, {
139
+ keyboardShouldPersistTaps: "handled",
140
+ nestedScrollEnabled: true,
141
+ style: { maxHeight },
142
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_VStack.VStack, {
143
+ alignItems: "stretch",
144
+ justifyContent: "flex-start",
145
+ children
146
+ })
147
+ })
140
148
  })
141
149
  })
142
150
  })
@@ -1 +1 @@
1
- {"version":3,"file":"SelectContent.d.cts","names":[],"sources":["../../../src/components/Select/SelectContent.tsx"],"mappings":";;;;;;;UAmCU,kBAAA;EACR,QAAA,GAAW,SAAA;EACX,IAAA,GAAO,iBAAA;EACP,OAAA,GAAU,oBAAA;EAHgB;EAK1B,yBAAA;EAJW;EAMX,SAAA;EAJU;EAMV,MAAA;EAGQ;EADR,SAAA;EACA,KAAA,GAAQ,SAAA,CAAU,SAAA;EAClB,MAAA;AAAA;;;;;;cAQI,aAAA,EAAa,OAAA,CAAA,oBAAA,CAAA,kBAAA"}
1
+ {"version":3,"file":"SelectContent.d.cts","names":[],"sources":["../../../src/components/Select/SelectContent.tsx"],"mappings":";;;;;;;UAwCU,kBAAA;EACR,QAAA,GAAW,SAAA;EACX,IAAA,GAAO,iBAAA;EACP,OAAA,GAAU,oBAAA;EAHgB;EAK1B,yBAAA;EAJW;EAMX,SAAA;EAJU;EAMV,MAAA;EAGQ;EADR,SAAA;EACA,KAAA,GAAQ,SAAA,CAAU,SAAA;EAClB,MAAA;AAAA;;;;;;cAQI,aAAA,EAAa,OAAA,CAAA,oBAAA,CAAA,kBAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"SelectContent.d.ts","names":[],"sources":["../../../src/components/Select/SelectContent.tsx"],"mappings":";;;;;;;UAmCU,kBAAA;EACR,QAAA,GAAW,SAAA;EACX,IAAA,GAAO,iBAAA;EACP,OAAA,GAAU,oBAAA;EAHgB;EAK1B,yBAAA;EAJW;EAMX,SAAA;EAJU;EAMV,MAAA;EAGQ;EADR,SAAA;EACA,KAAA,GAAQ,SAAA,CAAU,SAAA;EAClB,MAAA;AAAA;;;;;;cAQI,aAAA,EAAa,OAAA,CAAA,oBAAA,CAAA,kBAAA"}
1
+ {"version":3,"file":"SelectContent.d.ts","names":[],"sources":["../../../src/components/Select/SelectContent.tsx"],"mappings":";;;;;;;UAwCU,kBAAA;EACR,QAAA,GAAW,SAAA;EACX,IAAA,GAAO,iBAAA;EACP,OAAA,GAAU,oBAAA;EAHgB;EAK1B,yBAAA;EAJW;EAMX,SAAA;EAJU;EAMV,MAAA;EAGQ;EADR,SAAA;EACA,KAAA,GAAQ,SAAA,CAAU,SAAA;EAClB,MAAA;AAAA;;;;;;cAQI,aAAA,EAAa,OAAA,CAAA,oBAAA,CAAA,kBAAA"}