jfs-components 0.1.0 → 0.1.8

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 (392) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/lib/commonjs/components/AmountInput/AmountInput.js +8 -5
  3. package/lib/commonjs/components/AreaLineChart/AreaLineChart.js +1 -1
  4. package/lib/commonjs/components/Balance/Balance.js +17 -12
  5. package/lib/commonjs/components/BenefitCard/BenefitCard.js +231 -0
  6. package/lib/commonjs/components/Card/Card.js +2 -1
  7. package/lib/commonjs/components/CardFeedback/CardFeedback.js +2 -1
  8. package/lib/commonjs/components/CcCard/CcCard.js +470 -0
  9. package/lib/commonjs/components/Checkbox/Checkbox.js +6 -4
  10. package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +4 -3
  11. package/lib/commonjs/components/CompareTable/CompareTable.js +372 -0
  12. package/lib/commonjs/components/ComparisonBar/ComparisonBar.js +266 -0
  13. package/lib/commonjs/components/Drawer/Drawer.js +13 -4
  14. package/lib/commonjs/components/DropdownInput/DropdownInput.js +36 -4
  15. package/lib/commonjs/components/FormField/FormField.js +4 -3
  16. package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +62 -2
  17. package/lib/commonjs/components/Image/Image.js +11 -5
  18. package/lib/commonjs/components/InputSearch/InputSearch.js +6 -4
  19. package/lib/commonjs/components/LottiePlayer/loadNativeLottieView.js +8 -13
  20. package/lib/commonjs/components/NoteInput/NoteInput.js +6 -5
  21. package/lib/commonjs/components/Overlay/Overlay.js +92 -0
  22. package/lib/commonjs/components/PdpCcCard/PdpCcCard.js +273 -0
  23. package/lib/commonjs/components/PlanComparisonCard/PlanComparisonCard.js +2 -1
  24. package/lib/commonjs/components/ProductMerchandisingCard/GlassFill.js +263 -0
  25. package/lib/commonjs/components/ProductMerchandisingCard/GlassFill.web.js +116 -0
  26. package/lib/commonjs/components/ProductMerchandisingCard/ProductMerchandisingCard.js +353 -0
  27. package/lib/commonjs/components/ProjectionMarker/ProjectionMarker.js +161 -0
  28. package/lib/commonjs/components/Radio/Radio.js +5 -5
  29. package/lib/commonjs/components/Slider/Slider.js +473 -0
  30. package/lib/commonjs/components/TextInput/TextInput.js +15 -9
  31. package/lib/commonjs/components/TextSegment/TextSegment.js +118 -0
  32. package/lib/commonjs/components/docs/modeControls.js +116 -0
  33. package/lib/commonjs/components/index.js +70 -0
  34. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  35. package/lib/commonjs/design-tokens/JFSThemeProvider.js +1 -1
  36. package/lib/commonjs/design-tokens/figma-modes.generated.js +420 -0
  37. package/lib/commonjs/design-tokens/index.js +11 -0
  38. package/lib/commonjs/icons/registry.js +1 -1
  39. package/lib/commonjs/utils/react-utils.js +22 -0
  40. package/lib/module/components/Accordion/Accordion.js +1 -2
  41. package/lib/module/components/AmountInput/AmountInput.js +6 -4
  42. package/lib/module/components/AreaLineChart/AreaLineChart.js +1 -1
  43. package/lib/module/components/Balance/Balance.js +18 -13
  44. package/lib/module/components/BenefitCard/BenefitCard.js +225 -0
  45. package/lib/module/components/Card/Card.js +1 -2
  46. package/lib/module/components/CardFeedback/CardFeedback.js +1 -2
  47. package/lib/module/components/CcCard/CcCard.js +464 -0
  48. package/lib/module/components/Checkbox/Checkbox.js +6 -6
  49. package/lib/module/components/CheckboxItem/CheckboxItem.js +5 -4
  50. package/lib/module/components/CompareTable/CompareTable.js +367 -0
  51. package/lib/module/components/ComparisonBar/ComparisonBar.js +260 -0
  52. package/lib/module/components/Drawer/Drawer.js +12 -4
  53. package/lib/module/components/DropdownInput/DropdownInput.js +37 -5
  54. package/lib/module/components/FormField/FormField.js +5 -4
  55. package/lib/module/components/FullscreenModal/FullscreenModal.js +64 -5
  56. package/lib/module/components/Image/Image.js +11 -5
  57. package/lib/module/components/InputSearch/InputSearch.js +6 -4
  58. package/lib/module/components/InstitutionBadge/InstitutionBadge.js +1 -2
  59. package/lib/module/components/LottiePlayer/loadNativeLottieView.js +8 -13
  60. package/lib/module/components/MoneyValue/MoneyValue.js +1 -2
  61. package/lib/module/components/NoteInput/NoteInput.js +7 -6
  62. package/lib/module/components/OTP/OTP.js +1 -2
  63. package/lib/module/components/Overlay/Overlay.js +87 -0
  64. package/lib/module/components/PdpCcCard/PdpCcCard.js +267 -0
  65. package/lib/module/components/PlanComparisonCard/PlanComparisonCard.js +1 -2
  66. package/lib/module/components/PoweredByLabel/PoweredByLabel.js +1 -2
  67. package/lib/module/components/ProductMerchandisingCard/GlassFill.js +257 -0
  68. package/lib/module/components/ProductMerchandisingCard/GlassFill.web.js +111 -0
  69. package/lib/module/components/ProductMerchandisingCard/ProductMerchandisingCard.js +347 -0
  70. package/lib/module/components/ProjectionMarker/ProjectionMarker.js +156 -0
  71. package/lib/module/components/Radio/Radio.js +5 -4
  72. package/lib/module/components/RechargeCard/RechargeCard.js +1 -2
  73. package/lib/module/components/Section/Section.js +1 -2
  74. package/lib/module/components/Slider/Slider.js +468 -0
  75. package/lib/module/components/TextInput/TextInput.js +16 -12
  76. package/lib/module/components/TextSegment/TextSegment.js +113 -0
  77. package/lib/module/components/UpiHandle/UpiHandle.js +1 -2
  78. package/lib/module/components/docs/modeControls.js +111 -0
  79. package/lib/module/components/index.js +10 -0
  80. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  81. package/lib/module/design-tokens/JFSThemeProvider.js +1 -1
  82. package/lib/module/design-tokens/figma-modes.generated.js +416 -0
  83. package/lib/module/design-tokens/index.js +2 -1
  84. package/lib/module/icons/registry.js +1 -1
  85. package/lib/module/utils/react-utils.js +21 -1
  86. package/lib/typescript/src/components/Accordion/Accordion.d.ts +2 -1
  87. package/lib/typescript/src/components/AccordionCheckbox/AccordionCheckbox.d.ts +2 -1
  88. package/lib/typescript/src/components/AccountCard/AccountCard.d.ts +2 -1
  89. package/lib/typescript/src/components/ActionFooter/ActionFooter.d.ts +2 -1
  90. package/lib/typescript/src/components/ActionTile/ActionTile.d.ts +2 -1
  91. package/lib/typescript/src/components/AllocationComparisonChart/AllocationComparisonChart.d.ts +2 -1
  92. package/lib/typescript/src/components/AmountInput/AmountInput.d.ts +5 -3
  93. package/lib/typescript/src/components/AppBar/AppBar.d.ts +2 -1
  94. package/lib/typescript/src/components/AreaLineChart/AreaLineChart.d.ts +3 -2
  95. package/lib/typescript/src/components/Attached/Attached.d.ts +2 -1
  96. package/lib/typescript/src/components/Avatar/Avatar.d.ts +2 -1
  97. package/lib/typescript/src/components/AvatarGroup/AvatarGroup.d.ts +2 -1
  98. package/lib/typescript/src/components/Badge/Badge.d.ts +2 -1
  99. package/lib/typescript/src/components/Balance/Balance.d.ts +2 -1
  100. package/lib/typescript/src/components/BenefitCard/BenefitCard.d.ts +93 -0
  101. package/lib/typescript/src/components/BottomNav/BottomNav.d.ts +2 -1
  102. package/lib/typescript/src/components/BottomNavItem/BottomNavItem.d.ts +2 -1
  103. package/lib/typescript/src/components/BrandChip/BrandChip.d.ts +2 -1
  104. package/lib/typescript/src/components/BubbleChart/BubbleChart.d.ts +2 -1
  105. package/lib/typescript/src/components/Button/Button.d.ts +2 -1
  106. package/lib/typescript/src/components/ButtonGroup/ButtonGroup.d.ts +2 -1
  107. package/lib/typescript/src/components/Card/Card.d.ts +3 -2
  108. package/lib/typescript/src/components/CardAdvisory/CardAdvisory.d.ts +2 -1
  109. package/lib/typescript/src/components/CardBankAccount/CardBankAccount.d.ts +2 -1
  110. package/lib/typescript/src/components/CardCTA/CardCTA.d.ts +2 -1
  111. package/lib/typescript/src/components/CardFeedback/CardFeedback.d.ts +7 -6
  112. package/lib/typescript/src/components/CardFinancialCondition/CardFinancialCondition.d.ts +2 -1
  113. package/lib/typescript/src/components/CardInsight/CardInsight.d.ts +2 -1
  114. package/lib/typescript/src/components/CardProviderInfo/CardProviderInfo.d.ts +2 -1
  115. package/lib/typescript/src/components/Carousel/Carousel.d.ts +4 -3
  116. package/lib/typescript/src/components/CcCard/CcCard.d.ts +137 -0
  117. package/lib/typescript/src/components/Checkbox/Checkbox.d.ts +5 -3
  118. package/lib/typescript/src/components/CheckboxGroup/CheckboxGroup.d.ts +2 -1
  119. package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +4 -3
  120. package/lib/typescript/src/components/ChipGroup/ChipGroup.d.ts +2 -1
  121. package/lib/typescript/src/components/ChipSelect/ChipSelect.d.ts +2 -1
  122. package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +2 -1
  123. package/lib/typescript/src/components/CircularProgressBarDoted/CircularProgressBarDoted.d.ts +2 -1
  124. package/lib/typescript/src/components/CircularRating/CircularRating.d.ts +2 -1
  125. package/lib/typescript/src/components/ClusterBubble/ClusterBubble.d.ts +2 -1
  126. package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +88 -0
  127. package/lib/typescript/src/components/ComparisonBar/ComparisonBar.d.ts +118 -0
  128. package/lib/typescript/src/components/CoverageBarComparison/CoverageBarComparison.d.ts +3 -2
  129. package/lib/typescript/src/components/CoverageRing/CoverageRing.d.ts +2 -1
  130. package/lib/typescript/src/components/DebitCard/DebitCard.d.ts +2 -1
  131. package/lib/typescript/src/components/Disclaimer/Disclaimer.d.ts +2 -1
  132. package/lib/typescript/src/components/Divider/Divider.d.ts +2 -1
  133. package/lib/typescript/src/components/DonutChart/DonutChart.d.ts +4 -3
  134. package/lib/typescript/src/components/DonutChartSummary/DonutChartSummary.d.ts +3 -2
  135. package/lib/typescript/src/components/Drawer/Drawer.d.ts +15 -1
  136. package/lib/typescript/src/components/Dropdown/Dropdown.d.ts +3 -2
  137. package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +22 -2
  138. package/lib/typescript/src/components/EmptyState/EmptyState.d.ts +2 -1
  139. package/lib/typescript/src/components/ExpandableCheckbox/ExpandableCheckbox.d.ts +2 -1
  140. package/lib/typescript/src/components/FilterBar/FilterBar.d.ts +3 -2
  141. package/lib/typescript/src/components/Form/Form.d.ts +2 -1
  142. package/lib/typescript/src/components/FormField/FormField.d.ts +4 -3
  143. package/lib/typescript/src/components/FullscreenModal/FullscreenModal.d.ts +2 -1
  144. package/lib/typescript/src/components/Gauge/Gauge.d.ts +2 -1
  145. package/lib/typescript/src/components/HStack/HStack.d.ts +2 -1
  146. package/lib/typescript/src/components/HoldingsCard/HoldingsCard.d.ts +2 -1
  147. package/lib/typescript/src/components/Icon/Icon.d.ts +2 -1
  148. package/lib/typescript/src/components/IconButton/IconButton.d.ts +2 -1
  149. package/lib/typescript/src/components/IconCapsule/IconCapsule.d.ts +2 -1
  150. package/lib/typescript/src/components/Image/Image.d.ts +17 -1
  151. package/lib/typescript/src/components/InputSearch/InputSearch.d.ts +25 -3
  152. package/lib/typescript/src/components/InstitutionBadge/InstitutionBadge.d.ts +2 -1
  153. package/lib/typescript/src/components/LazyList/LazyList.d.ts +2 -1
  154. package/lib/typescript/src/components/LinearMeter/LinearMeter.d.ts +3 -2
  155. package/lib/typescript/src/components/LinearProgress/LinearProgress.d.ts +2 -1
  156. package/lib/typescript/src/components/ListGroup/ListGroup.d.ts +2 -1
  157. package/lib/typescript/src/components/ListItem/ListItem.d.ts +2 -1
  158. package/lib/typescript/src/components/LottieIntroBlock/LottieIntroBlock.d.ts +2 -1
  159. package/lib/typescript/src/components/LottiePlayer/LottiePlayer.d.ts +2 -1
  160. package/lib/typescript/src/components/LottiePlayer/LottiePlayer.web.d.ts +2 -1
  161. package/lib/typescript/src/components/MediaCard/MediaCard.d.ts +6 -5
  162. package/lib/typescript/src/components/MerchantProfile/MerchantProfile.d.ts +2 -1
  163. package/lib/typescript/src/components/MessageField/MessageField.d.ts +2 -1
  164. package/lib/typescript/src/components/MetricLegendItem/MetricLegendItem.d.ts +2 -1
  165. package/lib/typescript/src/components/MoneyValue/MoneyValue.d.ts +2 -1
  166. package/lib/typescript/src/components/MonthlyStatusGrid/MonthlyStatusGrid.d.ts +4 -3
  167. package/lib/typescript/src/components/NavArrow/NavArrow.d.ts +2 -1
  168. package/lib/typescript/src/components/NoteInput/NoteInput.d.ts +21 -3
  169. package/lib/typescript/src/components/Nudge/Nudge.d.ts +2 -1
  170. package/lib/typescript/src/components/Numpad/Numpad.d.ts +2 -1
  171. package/lib/typescript/src/components/OTP/OTP.d.ts +3 -2
  172. package/lib/typescript/src/components/Overlay/Overlay.d.ts +52 -0
  173. package/lib/typescript/src/components/PageHero/PageHero.d.ts +2 -1
  174. package/lib/typescript/src/components/PaymentFeedback/PaymentFeedback.d.ts +2 -1
  175. package/lib/typescript/src/components/PdpCcCard/PdpCcCard.d.ts +84 -0
  176. package/lib/typescript/src/components/PlanComparisonCard/PlanComparisonCard.d.ts +2 -1
  177. package/lib/typescript/src/components/Popup/Popup.d.ts +2 -1
  178. package/lib/typescript/src/components/PortfolioHero/PortfolioHero.d.ts +2 -1
  179. package/lib/typescript/src/components/PoweredByLabel/PoweredByLabel.d.ts +2 -1
  180. package/lib/typescript/src/components/ProductLabel/ProductLabel.d.ts +2 -1
  181. package/lib/typescript/src/components/ProductMerchandisingCard/GlassFill.d.ts +56 -0
  182. package/lib/typescript/src/components/ProductMerchandisingCard/GlassFill.web.d.ts +27 -0
  183. package/lib/typescript/src/components/ProductMerchandisingCard/ProductMerchandisingCard.d.ts +81 -0
  184. package/lib/typescript/src/components/ProductOverview/ProductOverview.d.ts +2 -1
  185. package/lib/typescript/src/components/ProgressBadge/ProgressBadge.d.ts +2 -1
  186. package/lib/typescript/src/components/ProjectionMarker/ProjectionMarker.d.ts +82 -0
  187. package/lib/typescript/src/components/Radio/Radio.d.ts +5 -3
  188. package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +2 -2
  189. package/lib/typescript/src/components/RangeTrack/RangeTrack.d.ts +3 -2
  190. package/lib/typescript/src/components/RechargeCard/RechargeCard.d.ts +2 -1
  191. package/lib/typescript/src/components/SavingsGoalSummary/SavingsGoalSummary.d.ts +2 -1
  192. package/lib/typescript/src/components/Screen/Screen.d.ts +2 -1
  193. package/lib/typescript/src/components/Section/Section.d.ts +3 -2
  194. package/lib/typescript/src/components/SegmentedControl/SegmentedControl.d.ts +2 -1
  195. package/lib/typescript/src/components/SegmentedTrack/SegmentedTrack.d.ts +4 -3
  196. package/lib/typescript/src/components/Slider/Slider.d.ts +99 -0
  197. package/lib/typescript/src/components/Slot/Slot.d.ts +2 -1
  198. package/lib/typescript/src/components/Spinner/Spinner.d.ts +2 -1
  199. package/lib/typescript/src/components/StatGroup/StatGroup.d.ts +2 -1
  200. package/lib/typescript/src/components/StatItem/StatItem.d.ts +2 -1
  201. package/lib/typescript/src/components/StatusHero/StatusHero.d.ts +2 -1
  202. package/lib/typescript/src/components/Stepper/Step.d.ts +2 -1
  203. package/lib/typescript/src/components/Stepper/StepLabel.d.ts +2 -1
  204. package/lib/typescript/src/components/Stepper/Stepper.d.ts +2 -1
  205. package/lib/typescript/src/components/StrengthIndicator/StrengthIndicator.d.ts +2 -1
  206. package/lib/typescript/src/components/SuggestiveSearch/SuggestiveSearch.d.ts +2 -1
  207. package/lib/typescript/src/components/SummaryTile/SummaryTile.d.ts +2 -1
  208. package/lib/typescript/src/components/SupportText/SupportText.d.ts +2 -1
  209. package/lib/typescript/src/components/SupportText/SupportTextIcon.d.ts +2 -1
  210. package/lib/typescript/src/components/SwappableAmount/SwappableAmount.d.ts +2 -1
  211. package/lib/typescript/src/components/Tabs/TabItem.d.ts +2 -1
  212. package/lib/typescript/src/components/Tabs/Tabs.d.ts +2 -1
  213. package/lib/typescript/src/components/TestimonialsCard/TestimonialsCard.d.ts +2 -1
  214. package/lib/typescript/src/components/Text/Text.d.ts +2 -1
  215. package/lib/typescript/src/components/TextInput/TextInput.d.ts +12 -31
  216. package/lib/typescript/src/components/TextSegment/TextSegment.d.ts +100 -0
  217. package/lib/typescript/src/components/ThreadHero/ThreadHero.d.ts +2 -1
  218. package/lib/typescript/src/components/Title/Title.d.ts +2 -1
  219. package/lib/typescript/src/components/Toast/Toast.d.ts +2 -1
  220. package/lib/typescript/src/components/Toast/ToastProvider.d.ts +2 -1
  221. package/lib/typescript/src/components/Toast/useToast.d.ts +3 -2
  222. package/lib/typescript/src/components/Toggle/Toggle.d.ts +2 -1
  223. package/lib/typescript/src/components/Tooltip/Tooltip.d.ts +2 -1
  224. package/lib/typescript/src/components/TransactionBubble/TransactionBubble.d.ts +2 -1
  225. package/lib/typescript/src/components/TransactionDetails/TransactionDetails.d.ts +3 -2
  226. package/lib/typescript/src/components/TransactionStatus/TransactionStatus.d.ts +2 -1
  227. package/lib/typescript/src/components/UpiHandle/UpiHandle.d.ts +2 -1
  228. package/lib/typescript/src/components/VStack/VStack.d.ts +2 -1
  229. package/lib/typescript/src/components/docs/modeControls.d.ts +28 -0
  230. package/lib/typescript/src/components/index.d.ts +11 -1
  231. package/lib/typescript/src/design-tokens/JFSThemeProvider.d.ts +4 -3
  232. package/lib/typescript/src/design-tokens/figma-modes.generated.d.ts +284 -0
  233. package/lib/typescript/src/design-tokens/index.d.ts +1 -0
  234. package/lib/typescript/src/icons/registry.d.ts +1 -1
  235. package/lib/typescript/src/skeleton/Skeleton.d.ts +2 -1
  236. package/lib/typescript/src/utils/react-utils.d.ts +12 -1
  237. package/package.json +4 -2
  238. package/src/components/Accordion/Accordion.tsx +2 -1
  239. package/src/components/AccordionCheckbox/AccordionCheckbox.tsx +2 -1
  240. package/src/components/AccountCard/AccountCard.tsx +2 -1
  241. package/src/components/ActionFooter/ActionFooter.tsx +2 -1
  242. package/src/components/ActionTile/ActionTile.tsx +2 -1
  243. package/src/components/AllocationComparisonChart/AllocationComparisonChart.tsx +2 -1
  244. package/src/components/AmountInput/AmountInput.tsx +9 -6
  245. package/src/components/AppBar/AppBar.tsx +2 -1
  246. package/src/components/AreaLineChart/AreaLineChart.tsx +8 -7
  247. package/src/components/Attached/Attached.tsx +2 -1
  248. package/src/components/Avatar/Avatar.tsx +3 -2
  249. package/src/components/AvatarGroup/AvatarGroup.tsx +2 -1
  250. package/src/components/Badge/Badge.tsx +2 -1
  251. package/src/components/Balance/Balance.tsx +18 -12
  252. package/src/components/BenefitCard/BenefitCard.tsx +309 -0
  253. package/src/components/BottomNav/BottomNav.tsx +2 -1
  254. package/src/components/BottomNavItem/BottomNavItem.tsx +3 -2
  255. package/src/components/BrandChip/BrandChip.tsx +3 -2
  256. package/src/components/BubbleChart/BubbleChart.tsx +2 -1
  257. package/src/components/Button/Button.tsx +3 -2
  258. package/src/components/ButtonGroup/ButtonGroup.tsx +2 -1
  259. package/src/components/Card/Card.tsx +4 -3
  260. package/src/components/CardAdvisory/CardAdvisory.tsx +3 -2
  261. package/src/components/CardBankAccount/CardBankAccount.tsx +2 -1
  262. package/src/components/CardCTA/CardCTA.tsx +3 -2
  263. package/src/components/CardFeedback/CardFeedback.tsx +11 -10
  264. package/src/components/CardFinancialCondition/CardFinancialCondition.tsx +3 -2
  265. package/src/components/CardInsight/CardInsight.tsx +2 -1
  266. package/src/components/CardProviderInfo/CardProviderInfo.tsx +2 -1
  267. package/src/components/Carousel/Carousel.tsx +5 -4
  268. package/src/components/CcCard/CcCard.tsx +598 -0
  269. package/src/components/Checkbox/Checkbox.tsx +7 -5
  270. package/src/components/CheckboxGroup/CheckboxGroup.tsx +2 -1
  271. package/src/components/CheckboxItem/CheckboxItem.tsx +7 -5
  272. package/src/components/ChipGroup/ChipGroup.tsx +2 -1
  273. package/src/components/ChipSelect/ChipSelect.tsx +2 -1
  274. package/src/components/CircularProgressBar/CircularProgressBar.tsx +2 -1
  275. package/src/components/CircularProgressBarDoted/CircularProgressBarDoted.tsx +2 -1
  276. package/src/components/CircularRating/CircularRating.tsx +3 -2
  277. package/src/components/ClusterBubble/ClusterBubble.tsx +2 -1
  278. package/src/components/CompareTable/CompareTable.tsx +477 -0
  279. package/src/components/ComparisonBar/ComparisonBar.tsx +356 -0
  280. package/src/components/CoverageBarComparison/CoverageBarComparison.tsx +3 -2
  281. package/src/components/CoverageRing/CoverageRing.tsx +2 -1
  282. package/src/components/DebitCard/DebitCard.tsx +2 -1
  283. package/src/components/Disclaimer/Disclaimer.tsx +2 -1
  284. package/src/components/Divider/Divider.tsx +2 -1
  285. package/src/components/DonutChart/DonutChart.tsx +6 -5
  286. package/src/components/DonutChartSummary/DonutChartSummary.tsx +3 -2
  287. package/src/components/Drawer/Drawer.tsx +21 -1
  288. package/src/components/Dropdown/Dropdown.tsx +4 -3
  289. package/src/components/DropdownInput/DropdownInput.tsx +60 -7
  290. package/src/components/EmptyState/EmptyState.tsx +2 -1
  291. package/src/components/ExpandableCheckbox/ExpandableCheckbox.tsx +2 -1
  292. package/src/components/FilterBar/FilterBar.tsx +3 -2
  293. package/src/components/Form/Form.tsx +2 -1
  294. package/src/components/FormField/FormField.tsx +8 -6
  295. package/src/components/FullscreenModal/FullscreenModal.tsx +68 -10
  296. package/src/components/Gauge/Gauge.tsx +2 -1
  297. package/src/components/HStack/HStack.tsx +2 -1
  298. package/src/components/HoldingsCard/HoldingsCard.tsx +2 -1
  299. package/src/components/Icon/Icon.tsx +3 -2
  300. package/src/components/IconButton/IconButton.tsx +3 -2
  301. package/src/components/IconCapsule/IconCapsule.tsx +3 -2
  302. package/src/components/Image/Image.tsx +29 -5
  303. package/src/components/InputSearch/InputSearch.tsx +11 -7
  304. package/src/components/InstitutionBadge/InstitutionBadge.tsx +3 -2
  305. package/src/components/LazyList/LazyList.tsx +2 -1
  306. package/src/components/LinearMeter/LinearMeter.tsx +3 -2
  307. package/src/components/LinearProgress/LinearProgress.tsx +2 -1
  308. package/src/components/ListGroup/ListGroup.tsx +2 -1
  309. package/src/components/ListItem/ListItem.tsx +3 -2
  310. package/src/components/LottieIntroBlock/LottieIntroBlock.tsx +2 -1
  311. package/src/components/LottiePlayer/LottiePlayer.tsx +3 -2
  312. package/src/components/LottiePlayer/LottiePlayer.web.tsx +3 -2
  313. package/src/components/LottiePlayer/loadNativeLottieView.tsx +9 -13
  314. package/src/components/MediaCard/MediaCard.tsx +7 -6
  315. package/src/components/MerchantProfile/MerchantProfile.tsx +2 -1
  316. package/src/components/MessageField/MessageField.tsx +3 -2
  317. package/src/components/MetricLegendItem/MetricLegendItem.tsx +2 -1
  318. package/src/components/MoneyValue/MoneyValue.tsx +2 -1
  319. package/src/components/MonthlyStatusGrid/MonthlyStatusGrid.tsx +5 -4
  320. package/src/components/NavArrow/NavArrow.tsx +3 -2
  321. package/src/components/NoteInput/NoteInput.tsx +10 -7
  322. package/src/components/Nudge/Nudge.tsx +3 -2
  323. package/src/components/Numpad/Numpad.tsx +2 -1
  324. package/src/components/OTP/OTP.tsx +3 -2
  325. package/src/components/Overlay/Overlay.tsx +114 -0
  326. package/src/components/PageHero/PageHero.tsx +2 -1
  327. package/src/components/PaymentFeedback/PaymentFeedback.tsx +2 -1
  328. package/src/components/PdpCcCard/PdpCcCard.tsx +356 -0
  329. package/src/components/PlanComparisonCard/PlanComparisonCard.tsx +2 -1
  330. package/src/components/Popup/Popup.tsx +2 -1
  331. package/src/components/PortfolioHero/PortfolioHero.tsx +2 -1
  332. package/src/components/PoweredByLabel/PoweredByLabel.tsx +2 -1
  333. package/src/components/ProductLabel/ProductLabel.tsx +2 -1
  334. package/src/components/ProductMerchandisingCard/GlassFill.tsx +276 -0
  335. package/src/components/ProductMerchandisingCard/GlassFill.web.tsx +127 -0
  336. package/src/components/ProductMerchandisingCard/ProductMerchandisingCard.tsx +423 -0
  337. package/src/components/ProductOverview/ProductOverview.tsx +2 -1
  338. package/src/components/ProgressBadge/ProgressBadge.tsx +2 -1
  339. package/src/components/ProjectionMarker/ProjectionMarker.tsx +277 -0
  340. package/src/components/Radio/Radio.tsx +7 -5
  341. package/src/components/RangeTrack/RangeTrack.tsx +3 -2
  342. package/src/components/RechargeCard/RechargeCard.tsx +2 -1
  343. package/src/components/SavingsGoalSummary/SavingsGoalSummary.tsx +2 -1
  344. package/src/components/Screen/Screen.tsx +2 -1
  345. package/src/components/Section/Section.tsx +6 -5
  346. package/src/components/SegmentedControl/SegmentedControl.tsx +3 -2
  347. package/src/components/SegmentedTrack/SegmentedTrack.tsx +5 -4
  348. package/src/components/Slider/Slider.tsx +628 -0
  349. package/src/components/Slot/Slot.tsx +2 -1
  350. package/src/components/Spinner/Spinner.tsx +2 -1
  351. package/src/components/StatGroup/StatGroup.tsx +3 -2
  352. package/src/components/StatItem/StatItem.tsx +2 -1
  353. package/src/components/StatusHero/StatusHero.tsx +2 -1
  354. package/src/components/Stepper/Step.tsx +2 -1
  355. package/src/components/Stepper/StepLabel.tsx +2 -1
  356. package/src/components/Stepper/Stepper.tsx +2 -1
  357. package/src/components/StrengthIndicator/StrengthIndicator.tsx +2 -1
  358. package/src/components/SuggestiveSearch/SuggestiveSearch.tsx +4 -3
  359. package/src/components/SummaryTile/SummaryTile.tsx +2 -1
  360. package/src/components/SupportText/SupportText.tsx +2 -1
  361. package/src/components/SupportText/SupportTextIcon.tsx +2 -1
  362. package/src/components/SwappableAmount/SwappableAmount.tsx +2 -1
  363. package/src/components/Tabs/TabItem.tsx +2 -1
  364. package/src/components/Tabs/Tabs.tsx +2 -1
  365. package/src/components/TestimonialsCard/TestimonialsCard.tsx +2 -1
  366. package/src/components/Text/Text.tsx +2 -1
  367. package/src/components/TextInput/TextInput.tsx +18 -13
  368. package/src/components/TextSegment/TextSegment.tsx +166 -0
  369. package/src/components/ThreadHero/ThreadHero.tsx +2 -1
  370. package/src/components/Title/Title.tsx +2 -1
  371. package/src/components/Toast/Toast.tsx +2 -1
  372. package/src/components/Toast/ToastProvider.tsx +2 -1
  373. package/src/components/Toast/useToast.ts +3 -2
  374. package/src/components/Toggle/Toggle.tsx +2 -1
  375. package/src/components/Tooltip/Tooltip.tsx +3 -2
  376. package/src/components/TransactionBubble/TransactionBubble.tsx +2 -1
  377. package/src/components/TransactionDetails/TransactionDetails.tsx +3 -2
  378. package/src/components/TransactionStatus/TransactionStatus.tsx +3 -2
  379. package/src/components/UpiHandle/UpiHandle.tsx +3 -2
  380. package/src/components/VStack/VStack.tsx +2 -1
  381. package/src/components/docs/modeControls.tsx +122 -0
  382. package/src/components/index.ts +11 -1
  383. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  384. package/src/design-tokens/JFSThemeProvider.tsx +4 -3
  385. package/src/design-tokens/figma-modes.generated.ts +425 -0
  386. package/src/design-tokens/index.ts +1 -0
  387. package/src/icons/registry.ts +1 -1
  388. package/src/skeleton/Skeleton.tsx +2 -1
  389. package/src/utils/react-utils.ts +26 -2
  390. package/lib/typescript/scripts/extract-component-tokens.d.ts +0 -9
  391. package/lib/typescript/scripts/generate-component-docs.d.ts +0 -9
  392. package/lib/typescript/scripts/generate-icon-registry.d.ts +0 -3
@@ -1,6 +1,8 @@
1
1
  import React, {
2
+ forwardRef,
2
3
  useCallback,
3
4
  useEffect,
5
+ useImperativeHandle,
4
6
  useMemo,
5
7
  useRef,
6
8
  useState,
@@ -27,6 +29,7 @@ import Icon from '../../icons/Icon'
27
29
  import SupportText from '../SupportText/SupportText'
28
30
  import type { SupportTextStatus } from '../SupportText/SupportTextIcon'
29
31
  import Dropdown, { DropdownItem, type DropdownItemProps } from '../Dropdown/Dropdown'
32
+ import type { Modes } from '../../design-tokens'
30
33
 
31
34
  const IS_WEB = Platform.OS === 'web'
32
35
 
@@ -51,6 +54,28 @@ export type DropdownInputOption = {
51
54
 
52
55
  type Rect = { x: number; y: number; width: number; height: number }
53
56
 
57
+ /**
58
+ * Imperative handle exposed via `ref`. Lets the consumer drive the dropdown
59
+ * from outside — e.g. close it when an Android hardware/system button is
60
+ * pressed, or open it programmatically from a sibling control.
61
+ */
62
+ export type DropdownInputHandle = {
63
+ /** Opens the options menu (no-op when disabled / read-only). */
64
+ open: () => void
65
+ /** Closes the options menu. */
66
+ close: () => void
67
+ /** Toggles the options menu open/closed. */
68
+ toggle: () => void
69
+ /** Moves focus to the trigger (web). */
70
+ focus: () => void
71
+ /** Removes focus from the trigger (web). */
72
+ blur: () => void
73
+ /** Measures the trigger in window coordinates. */
74
+ measureInWindow: (
75
+ callback: (x: number, y: number, width: number, height: number) => void
76
+ ) => void
77
+ }
78
+
54
79
  export type DropdownInputProps = {
55
80
  /** Label rendered above the input. */
56
81
  label?: string
@@ -131,7 +156,7 @@ export type DropdownInputProps = {
131
156
  /** Whether tapping the backdrop closes the menu. */
132
157
  closeOnBackdropPress?: boolean
133
158
  /** Modes for design token resolution. */
134
- modes?: Record<string, any>
159
+ modes?: Modes
135
160
  /** Style overrides for the outermost wrapper. */
136
161
  style?: StyleProp<ViewStyle>
137
162
  /** Style overrides for the input row. */
@@ -152,7 +177,7 @@ export type DropdownInputProps = {
152
177
  // Token resolution
153
178
  // ---------------------------------------------------------------------------
154
179
 
155
- function useChevronTokens(modes: Record<string, any>) {
180
+ function useChevronTokens(modes: Modes) {
156
181
  return useMemo(() => {
157
182
  const iconSize =
158
183
  parseInt(getVariableByName('input/iconSize', modes), 10) || 32
@@ -172,7 +197,7 @@ function toNumber(value: unknown, fallback: number): number {
172
197
  return fallback
173
198
  }
174
199
 
175
- function useFormFieldTokens(modes: Record<string, any>) {
200
+ function useFormFieldTokens(modes: Modes) {
176
201
  return useMemo(() => {
177
202
  const labelColor =
178
203
  (getVariableByName('formField/label/color', modes) as string) ||
@@ -306,7 +331,7 @@ function collectOptionsFromChildren(
306
331
  // Component
307
332
  // ---------------------------------------------------------------------------
308
333
 
309
- function DropdownInput({
334
+ const DropdownInput = forwardRef<DropdownInputHandle, DropdownInputProps>(function DropdownInput({
310
335
  label,
311
336
  placeholder = 'Select an option',
312
337
  items,
@@ -337,7 +362,7 @@ function DropdownInput({
337
362
  accessibilityHint,
338
363
  onFocus,
339
364
  onBlur,
340
- }: DropdownInputProps) {
365
+ }: DropdownInputProps, ref: React.Ref<DropdownInputHandle>) {
341
366
  // ---------------- Modes ----------------
342
367
  const { modes: globalModes } = useTokens()
343
368
  const baseModes = useMemo(
@@ -496,6 +521,34 @@ function DropdownInput({
496
521
  [measure]
497
522
  )
498
523
 
524
+ // ---------------- Imperative handle ----------------
525
+ useImperativeHandle(
526
+ ref,
527
+ () => ({
528
+ open: () => {
529
+ if (isDisabled || isReadOnly) return
530
+ measure()
531
+ setOpenState(true)
532
+ },
533
+ close: closeMenu,
534
+ toggle: () => {
535
+ if (isDisabled || isReadOnly) return
536
+ measure()
537
+ toggleMenu()
538
+ },
539
+ focus: () => {
540
+ ;(triggerRef.current as unknown as { focus?: () => void })?.focus?.()
541
+ },
542
+ blur: () => {
543
+ ;(triggerRef.current as unknown as { blur?: () => void })?.blur?.()
544
+ },
545
+ measureInWindow: (callback) => {
546
+ triggerRef.current?.measureInWindow(callback)
547
+ },
548
+ }),
549
+ [isDisabled, isReadOnly, measure, setOpenState, closeMenu, toggleMenu]
550
+ )
551
+
499
552
  // ---------------- Popup positioning ----------------
500
553
  const [menuSize, setMenuSize] = useState<{
501
554
  width: number
@@ -829,7 +882,7 @@ function DropdownInput({
829
882
  transparent
830
883
  statusBarTranslucent
831
884
  navigationBarTranslucent
832
- animationType="fade"
885
+ animationType="none"
833
886
  onRequestClose={closeMenu}
834
887
  >
835
888
  <Pressable
@@ -862,7 +915,7 @@ function DropdownInput({
862
915
  </Modal>
863
916
  </View>
864
917
  )
865
- }
918
+ })
866
919
 
867
920
  const webNoOutline: any = {
868
921
  outlineStyle: 'none',
@@ -5,6 +5,7 @@ import { useTokens } from '../../design-tokens/JFSThemeProvider'
5
5
  import IconCapsule from '../IconCapsule/IconCapsule'
6
6
  import Button from '../Button/Button'
7
7
  import { EMPTY_MODES } from '../../utils/react-utils'
8
+ import type { Modes } from '../../design-tokens'
8
9
 
9
10
  export type EmptyStateProps = {
10
11
  /**
@@ -35,7 +36,7 @@ export type EmptyStateProps = {
35
36
  /**
36
37
  * Mode configuration for theming
37
38
  */
38
- modes?: Record<string, any>;
39
+ modes?: Modes;
39
40
  style?: StyleProp<ViewStyle>;
40
41
  testID?: string;
41
42
  }
@@ -10,6 +10,7 @@ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
10
10
  import { EMPTY_MODES } from '../../utils/react-utils'
11
11
  import Checkbox from '../Checkbox/Checkbox'
12
12
  import Button from '../Button/Button'
13
+ import type { Modes } from '../../design-tokens'
13
14
 
14
15
  export type ExpandableCheckboxProps = {
15
16
  /** Long text label rendered next to the checkbox. */
@@ -35,7 +36,7 @@ export type ExpandableCheckboxProps = {
35
36
  /** Number of lines to show when collapsed. Defaults to `1`. */
36
37
  collapsedLines?: number
37
38
  /** Design token modes for theming (e.g. `{ 'Color Mode': 'Light' }`). */
38
- modes?: Record<string, any>
39
+ modes?: Modes
39
40
  /** Override outer container styles. */
40
41
  style?: StyleProp<ViewStyle>
41
42
  /** Override the label text styles. */
@@ -3,12 +3,13 @@ import { View, type StyleProp, type ViewStyle } from 'react-native'
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
4
  import TextInput from '../TextInput/TextInput'
5
5
  import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils'
6
+ import type { Modes } from '../../design-tokens'
6
7
 
7
8
  type RenderInputArgs = {
8
9
  placeholder: string;
9
10
  value: string;
10
11
  onChangeText?: ((text: string) => void) | undefined;
11
- modes: Record<string, any>;
12
+ modes: Modes;
12
13
  accessibilityLabel: string;
13
14
  accessibilityHint?: string | undefined;
14
15
  onFocus?: (e: any) => void;
@@ -30,7 +31,7 @@ type FilterBarProps = {
30
31
  * respect the higher-level FilterBar API.
31
32
  */
32
33
  renderInput?: (args: RenderInputArgs) => React.ReactNode;
33
- modes?: Record<string, any>;
34
+ modes?: Modes;
34
35
  style?: StyleProp<ViewStyle>;
35
36
  accessibilityLabel?: string;
36
37
  accessibilityHint?: string;
@@ -3,6 +3,7 @@ import { View, type StyleProp, type ViewStyle } from 'react-native'
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
4
  import { useTokens } from '../../design-tokens/JFSThemeProvider'
5
5
  import { EMPTY_MODES } from '../../utils/react-utils'
6
+ import type { Modes } from '../../design-tokens'
6
7
 
7
8
  type FormContextValue = {
8
9
  validationErrors: Record<string, string | string[]>;
@@ -19,7 +20,7 @@ export type FormProps = {
19
20
  children: React.ReactNode;
20
21
  validationErrors?: Record<string, string | string[]>;
21
22
  onSubmit?: () => void;
22
- modes?: Record<string, any>;
23
+ modes?: Modes;
23
24
  style?: StyleProp<ViewStyle>;
24
25
  accessibilityLabel?: string;
25
26
  testID?: string;
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useMemo, useState } from 'react'
1
+ import React, { forwardRef, useCallback, useMemo, useState } from 'react'
2
2
  import {
3
3
  View,
4
4
  Text,
@@ -15,6 +15,7 @@ import SupportText from '../SupportText/SupportText'
15
15
  import type { SupportTextStatus } from '../SupportText/SupportTextIcon'
16
16
  import Icon from '../../icons/Icon'
17
17
  import { useFormContext } from '../Form/Form'
18
+ import type { Modes } from '../../design-tokens'
18
19
 
19
20
  export type FormFieldType = 'text' | 'password' | 'email' | 'search' | 'number' | 'phone' | 'url'
20
21
 
@@ -76,7 +77,7 @@ export type FormFieldProps = {
76
77
  /** When true, focuses the input on mount. */
77
78
  autoFocus?: boolean
78
79
  /** Modes for design token resolution. */
79
- modes?: Record<string, any>
80
+ modes?: Modes
80
81
  /** Style overrides for the outermost wrapper. */
81
82
  style?: StyleProp<ViewStyle>
82
83
  /** Style overrides for the input row container. */
@@ -116,7 +117,7 @@ function toFontWeight(value: unknown, fallback: TextStyle['fontWeight']): TextSt
116
117
  return fallback
117
118
  }
118
119
 
119
- function useFormFieldTokens(modes: Record<string, any>) {
120
+ function useFormFieldTokens(modes: Modes) {
120
121
  return useMemo(() => {
121
122
  // Wrapper
122
123
  const gap = toNumber(getVariableByName('formField/gap', modes), 8)
@@ -300,7 +301,7 @@ function firstError(error: string | string[] | undefined): string | undefined {
300
301
  // Component
301
302
  // ---------------------------------------------------------------------------
302
303
 
303
- function FormField({
304
+ const FormField = forwardRef<RNTextInput, FormFieldProps>(function FormField({
304
305
  label,
305
306
  placeholder,
306
307
  value,
@@ -328,7 +329,7 @@ function FormField({
328
329
  accessibilityLabel,
329
330
  accessibilityHint,
330
331
  testID,
331
- }: FormFieldProps) {
332
+ }: FormFieldProps, ref: React.Ref<RNTextInput>) {
332
333
  // -- Form context integration -------------------------------------------
333
334
  const formCtx = useFormContext()
334
335
  const formError =
@@ -551,6 +552,7 @@ function FormField({
551
552
  </View>
552
553
  )}
553
554
  <RNTextInput
555
+ ref={ref}
554
556
  style={[inputTextStyles, inputTextStyle]}
555
557
  value={value ?? ''}
556
558
  onChangeText={handleChangeText}
@@ -589,6 +591,6 @@ function FormField({
589
591
  )}
590
592
  </View>
591
593
  )
592
- }
594
+ })
593
595
 
594
596
  export default FormField
@@ -1,8 +1,9 @@
1
- import React, { useMemo, useRef } from 'react'
1
+ import React, { useMemo, useRef, useState, useCallback } from 'react'
2
2
  import {
3
3
  View,
4
4
  Text,
5
5
  Animated,
6
+ type LayoutChangeEvent,
6
7
  type StyleProp,
7
8
  type ViewStyle,
8
9
  type TextStyle,
@@ -16,6 +17,7 @@ import Disclaimer from '../Disclaimer/Disclaimer'
16
17
  import IconButton from '../IconButton/IconButton'
17
18
  import ActionFooter from '../ActionFooter/ActionFooter'
18
19
  import Slot from '../Slot/Slot'
20
+ import type { Modes } from '../../design-tokens'
19
21
 
20
22
  // ---------------------------------------------------------------------------
21
23
  // Forced modes
@@ -99,7 +101,7 @@ export type FullscreenModalProps = {
99
101
  * and `context5` is always forced to `'Fullscreen Modal'` (non-overridable).
100
102
  * The resolved modes cascade to the body, hero media, and the `ActionFooter`.
101
103
  */
102
- modes?: Record<string, any>
104
+ modes?: Modes
103
105
  /** Style overrides for the outer container. */
104
106
  style?: StyleProp<ViewStyle>
105
107
  /** Style overrides for the transparent body wrapper. */
@@ -118,7 +120,7 @@ type HeroTextProps = {
118
120
  headline?: string
119
121
  supportingText?: string
120
122
  priceText?: string
121
- modes: Record<string, any>
123
+ modes: Modes
122
124
  }
123
125
 
124
126
  function HeroText({ eyebrow, headline, supportingText, priceText, modes }: HeroTextProps) {
@@ -300,11 +302,57 @@ function FullscreenModal({
300
302
  )
301
303
  const heroTranslateY = useMemo(() => Animated.multiply(scrollY, -1), [scrollY])
302
304
 
303
- const processedHeroMedia = useMemo(
304
- () =>
305
- heroMedia ? cloneChildrenWithModes(heroMedia, modes, FULLSCREEN_MODAL_FORCED_MODES) : null,
306
- [heroMedia, modes]
307
- )
305
+ // The hero media is a full-bleed background pinned to the top of the modal.
306
+ // Its wrapper is absolutely positioned (`top/left/right: 0`) so it never
307
+ // contributes to scroll height. We measure the modal's own width so we can
308
+ // give the hero media a DEFINITE { width, height } box (see
309
+ // `processedHeroMedia` below) — relying on the media's own `width: '100%'` +
310
+ // `aspectRatio` is unreliable inside an indefinite-height absolute box (RN's
311
+ // `<Image>` falls back to its intrinsic width and leaves a gap), so we size
312
+ // it explicitly here instead.
313
+ const [containerWidth, setContainerWidth] = useState(0)
314
+ const onContainerLayout = useCallback((e: LayoutChangeEvent) => {
315
+ const w = e.nativeEvent.layout.width
316
+ setContainerWidth((prev) => (prev !== w ? w : prev))
317
+ }, [])
318
+
319
+ const processedHeroMedia = useMemo(() => {
320
+ if (!heroMedia) return null
321
+ // Defer rendering the hero until we have measured the modal width. This
322
+ // matters for image sharpness: React Native decodes a bitmap once, on the
323
+ // first render, and caches it. If we rendered the media before knowing the
324
+ // final box, that first decode would be sized/sampled for the wrong box and
325
+ // the cached (downsampled) bitmap would then just be scaled up — blurry.
326
+ // Rendering only once the explicit { width, height } box is known means the
327
+ // very first decode is already full-resolution for the correct box.
328
+ if (containerWidth <= 0) return null
329
+ const withModes = cloneChildrenWithModes(heroMedia, modes, FULLSCREEN_MODAL_FORCED_MODES)
330
+
331
+ // Force the hero to fill the modal width edge to edge, top-aligned, with the
332
+ // height derived from the media's native aspect ratio.
333
+ //
334
+ // Why we inject explicit width/height instead of relying on the media's own
335
+ // `width: '100%'` + `aspectRatio`: the hero wrapper is absolutely positioned
336
+ // with an INDEFINITE height, and in that layout context React Native's
337
+ // `<Image>` falls back to its INTRINSIC width (so a 361px-wide asset renders
338
+ // 361px wide and leaves a gap on the right) rather than stretching to the
339
+ // parent. By computing `height = containerWidth / ratio` here and passing a
340
+ // DEFINITE { width, height } box (which makes `<Image>` skip aspectRatio and
341
+ // cover-fit into that exact box), the hero always fills the width with the
342
+ // correct ratio-derived height.
343
+ return React.Children.map(withModes, (child) => {
344
+ if (!React.isValidElement(child)) return child
345
+ const props = child.props as { ratio?: number; width?: unknown; height?: unknown }
346
+ const ratio = typeof props.ratio === 'number' && props.ratio > 0 ? props.ratio : undefined
347
+ // Only size media that exposes a numeric `ratio` and hasn't already been
348
+ // given an explicit box by the caller.
349
+ if (ratio == null || props.width != null || props.height != null) return child
350
+ return React.cloneElement(child, {
351
+ width: containerWidth,
352
+ height: containerWidth / ratio,
353
+ } as Partial<typeof props>)
354
+ })
355
+ }, [heroMedia, modes, containerWidth])
308
356
 
309
357
  const processedChildren = useMemo(
310
358
  () =>
@@ -369,7 +417,7 @@ function FullscreenModal({
369
417
  }
370
418
 
371
419
  return (
372
- <View style={[rootStyle, style]} testID={testID}>
420
+ <View style={[rootStyle, style]} testID={testID} onLayout={onContainerLayout}>
373
421
  {/*
374
422
  * Layout model:
375
423
  * - `processedHeroMedia` is a ROOT-LEVEL full-bleed background pinned to
@@ -387,7 +435,17 @@ function FullscreenModal({
387
435
  */}
388
436
  {processedHeroMedia ? (
389
437
  <Animated.View
390
- style={[heroBackgroundStyle, { transform: [{ translateY: heroTranslateY }] }]}
438
+ style={[
439
+ heroBackgroundStyle,
440
+ // Give the absolute wrapper a DEFINITE width (the measured modal
441
+ // width) so the media's `width: '100%'` + `aspectRatio` resolves to
442
+ // a true edge-to-edge fill with a ratio-derived height, top-aligned.
443
+ // Before the first layout pass `containerWidth` is 0 — fall back to
444
+ // stretching via `left/right: 0` so there is no flash of a mis-sized
445
+ // image.
446
+ containerWidth > 0 ? { width: containerWidth } : null,
447
+ { transform: [{ translateY: heroTranslateY }] },
448
+ ]}
391
449
  pointerEvents="none"
392
450
  >
393
451
  {processedHeroMedia}
@@ -5,6 +5,7 @@ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
5
5
  import { useTokens } from '../../design-tokens/JFSThemeProvider'
6
6
  import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils'
7
7
  import SupportText, { type SupportTextProps } from '../SupportText/SupportText'
8
+ import type { Modes } from '../../design-tokens'
8
9
 
9
10
  type GaugeBaseProps = Omit<React.ComponentProps<typeof View>, 'children' | 'style'>
10
11
 
@@ -32,7 +33,7 @@ export type GaugeProps = GaugeBaseProps & {
32
33
  /** Hides default support text when no custom readout slot is provided. */
33
34
  showSupportText?: boolean
34
35
  /** Design token modes forwarded to token lookups and slot children. */
35
- modes?: Record<string, any>
36
+ modes?: Modes
36
37
  /** Slot rendered in the center of the gauge arc. Receives `modes` recursively. */
37
38
  children?: React.ReactNode
38
39
  /** Container style override. */
@@ -3,6 +3,7 @@ import { View, StyleProp, ViewStyle, ViewProps } from 'react-native'
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
4
  import { useTokens } from '../../design-tokens/JFSThemeProvider'
5
5
  import { cloneChildrenWithModes, EMPTY_MODES } from '../../utils/react-utils'
6
+ import type { Modes } from '../../design-tokens'
6
7
 
7
8
  export interface HStackProps extends ViewProps {
8
9
  /**
@@ -35,7 +36,7 @@ export interface HStackProps extends ViewProps {
35
36
  /**
36
37
  * Modes object to override default variable values.
37
38
  */
38
- modes?: Record<string, any>;
39
+ modes?: Modes;
39
40
  }
40
41
 
41
42
  /**
@@ -11,6 +11,7 @@ import {
11
11
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
12
12
  import Radio from '../Radio/Radio'
13
13
  import { EMPTY_MODES } from '../../utils/react-utils'
14
+ import type { Modes } from '../../design-tokens'
14
15
 
15
16
  export interface HoldingsCardDetailItem {
16
17
  label: string
@@ -33,7 +34,7 @@ export interface HoldingsCardProps {
33
34
  /** Called when the card is pressed (toggles selection). */
34
35
  onPress?: () => void
35
36
  /** Modes object for design-token resolution. */
36
- modes?: Record<string, any>
37
+ modes?: Modes
37
38
  /** Custom style for the outer container. */
38
39
  style?: StyleProp<ViewStyle>
39
40
  /** Test ID for testing. */
@@ -10,6 +10,7 @@ import { useTokens } from '../../design-tokens/JFSThemeProvider'
10
10
  import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils'
11
11
  import BaseIcon from '../../icons/Icon'
12
12
  import type { UnifiedSource } from '../../utils/MediaSource'
13
+ import type { Modes } from '../../design-tokens'
13
14
 
14
15
  type IconProps = AccessibilityProps & {
15
16
  /**
@@ -43,7 +44,7 @@ type IconProps = AccessibilityProps & {
43
44
  * from the `icon/size` design token.
44
45
  */
45
46
  size?: number
46
- modes?: Record<string, any>
47
+ modes?: Modes
47
48
  style?: StyleProp<ViewStyle>
48
49
  }
49
50
 
@@ -53,7 +54,7 @@ interface IconTokens {
53
54
  iconSize: number
54
55
  }
55
56
 
56
- function resolveIconTokens(modes: Record<string, any>): IconTokens {
57
+ function resolveIconTokens(modes: Modes): IconTokens {
57
58
  const iconColor = (getVariableByName('icon/color', modes) || '#ad8444') as string
58
59
  const iconSize = (getVariableByName('icon/size', modes) || 18) as number
59
60
  const paddingLeft = (getVariableByName('icon/padding/left', modes) || 0) as number
@@ -14,6 +14,7 @@ import { EMPTY_MODES } from '../../utils/react-utils'
14
14
  import type { UnifiedSource } from '../../utils/MediaSource'
15
15
  import Skeleton from '../../skeleton/Skeleton'
16
16
  import { useSkeleton } from '../../skeleton/SkeletonGroup'
17
+ import type { Modes } from '../../design-tokens'
17
18
 
18
19
  type IconButtonProps = SafePressableProps & {
19
20
  /** Built-in icon name from the registry (default state). */
@@ -26,7 +27,7 @@ type IconButtonProps = SafePressableProps & {
26
27
  * follows design tokens just like a built-in icon. See {@link UnifiedSource}.
27
28
  */
28
29
  source?: UnifiedSource;
29
- modes?: Record<string, any>;
30
+ modes?: Modes;
30
31
  onPress?: () => void;
31
32
  disabled?: boolean;
32
33
  style?: StyleProp<ViewStyle>;
@@ -96,7 +97,7 @@ interface IconButtonTokens {
96
97
  iconSize: number;
97
98
  }
98
99
 
99
- function resolveIconButtonTokens(modes: Record<string, any>, disabled: boolean): IconButtonTokens {
100
+ function resolveIconButtonTokens(modes: Modes, disabled: boolean): IconButtonTokens {
100
101
  const radiusRaw = (getVariableByName('iconButton/radius', modes) ?? 9999) as number
101
102
  const padding = (getVariableByName('iconButton/padding', modes) ?? 12) as number
102
103
  const backgroundColor = (getVariableByName('iconButton/background', modes) ?? '#cfa159') as string
@@ -5,6 +5,7 @@ import { useTokens } from '../../design-tokens/JFSThemeProvider'
5
5
  import { EMPTY_MODES } from '../../utils/react-utils'
6
6
  import Icon from '../../icons/Icon'
7
7
  import type { UnifiedSource } from '../../utils/MediaSource'
8
+ import type { Modes } from '../../design-tokens'
8
9
 
9
10
  type IconCapsuleProps = {
10
11
  iconName?: string;
@@ -17,7 +18,7 @@ type IconCapsuleProps = {
17
18
  * {@link UnifiedSource}.
18
19
  */
19
20
  source?: UnifiedSource;
20
- modes?: Record<string, any>;
21
+ modes?: Modes;
21
22
  accessibilityLabel?: string;
22
23
  accessibilityRole?: string;
23
24
  } & React.ComponentProps<typeof View>;
@@ -28,7 +29,7 @@ interface IconCapsuleTokens {
28
29
  iconSize: number;
29
30
  }
30
31
 
31
- function resolveIconCapsuleTokens(modes: Record<string, any>): IconCapsuleTokens {
32
+ function resolveIconCapsuleTokens(modes: Modes): IconCapsuleTokens {
32
33
  const size = (getVariableByName('iconCapsule/size', modes) || 42) as number
33
34
  const radiusRaw = (getVariableByName('iconCapsule/radius', modes) || 9999) as number
34
35
  const backgroundColor = (getVariableByName('iconCapsule/background', modes) || '#cfa159') as string
@@ -8,6 +8,8 @@ import {
8
8
  type ViewStyle,
9
9
  type ImageResizeMode,
10
10
  } from 'react-native'
11
+
12
+ type ImageResizeMethod = 'auto' | 'resize' | 'scale' | 'none'
11
13
  import Skeleton from '../../skeleton/Skeleton'
12
14
  import { useSkeleton } from '../../skeleton/SkeletonGroup'
13
15
 
@@ -33,6 +35,21 @@ export type ImageProps = {
33
35
  ratio?: number | undefined
34
36
  /** How the image is fit inside its box. Defaults to `'cover'`. */
35
37
  resizeMode?: ImageResizeMode | undefined
38
+ /**
39
+ * (Android only) How the bitmap is resampled when the source resolution
40
+ * differs from the rendered box.
41
+ *
42
+ * RN's own default is `'auto'`, which for large source images silently picks
43
+ * `'resize'`: a pre-decode software downsample that DESTROYS detail and makes
44
+ * big hero/background images look soft/pixelated even when the asset is
45
+ * high-res. To avoid that, this component defaults to:
46
+ * - `'none'` when an explicit `width` + `height` box is set (full-bleed
47
+ * hero/background usage — full resolution, maximum sharpness), and
48
+ * - `'scale'` otherwise (GPU scaling — high quality and memory-safe for
49
+ * ordinary aspect-ratio images).
50
+ * Pass an explicit value to override. No-op on iOS/web.
51
+ */
52
+ resizeMethod?: ImageResizeMethod | undefined
36
53
  /**
37
54
  * Optional explicit width. If omitted and a `ratio` is set, the image
38
55
  * defaults to `width: '100%'` so it fills its parent.
@@ -96,6 +113,7 @@ function Image({
96
113
  imageSource,
97
114
  ratio = DEFAULT_RATIO,
98
115
  resizeMode = 'cover',
116
+ resizeMethod,
99
117
  width,
100
118
  height,
101
119
  borderRadius,
@@ -107,11 +125,16 @@ function Image({
107
125
  }: ImageProps) {
108
126
  const source = useMemo(() => normalizeSource(imageSource), [imageSource])
109
127
 
128
+ // Explicit { width, height } means a "fill an exact box" layout — typically a
129
+ // full-bleed hero/background where the asset is high-res and sharpness
130
+ // matters most. There, default to `'none'` (full-resolution, no pre-decode
131
+ // downsample) so detail is never thrown away. Aspect-ratio images keep the
132
+ // memory-safe `'scale'` default. A caller-supplied `resizeMethod` always wins.
133
+ const isExplicitBox = width != null && height != null
134
+ const effectiveResizeMethod: ImageResizeMethod =
135
+ resizeMethod ?? (isExplicitBox ? 'none' : 'scale')
136
+
110
137
  const layoutStyle: ImageStyle = useMemo(() => {
111
- // If the caller has fully specified width AND height, they're doing a
112
- // non-aspect layout (e.g. "fill the parent") — respect that and skip
113
- // `aspectRatio` so it doesn't conflict.
114
- const isExplicitBox = width != null && height != null
115
138
  const s: ImageStyle = {
116
139
  width: width ?? '100%',
117
140
  ...(isExplicitBox
@@ -120,7 +143,7 @@ function Image({
120
143
  }
121
144
  if (borderRadius != null) s.borderRadius = borderRadius
122
145
  return s
123
- }, [ratio, width, height, borderRadius])
146
+ }, [ratio, width, height, borderRadius, isExplicitBox])
124
147
 
125
148
  const { active: groupActive } = useSkeleton()
126
149
  const isLoading = loading ?? groupActive
@@ -146,6 +169,7 @@ function Image({
146
169
  source={source}
147
170
  style={[layoutStyle, style]}
148
171
  resizeMode={resizeMode}
172
+ resizeMethod={effectiveResizeMethod}
149
173
  accessibilityLabel={accessibilityLabel}
150
174
  accessibilityElementsHidden={accessibilityElementsHidden}
151
175
  importantForAccessibility={importantForAccessibility}