jfs-components 0.0.62 → 0.0.64

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 (255) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/lib/commonjs/components/Accordion/Accordion.js +1 -1
  3. package/lib/commonjs/components/ActionFooter/ActionFooter.js +1 -1
  4. package/lib/commonjs/components/ActionTile/ActionTile.js +2 -1
  5. package/lib/commonjs/components/AmountInput/AmountInput.js +2 -1
  6. package/lib/commonjs/components/AppBar/AppBar.js +1 -1
  7. package/lib/commonjs/components/Avatar/Avatar.js +184 -162
  8. package/lib/commonjs/components/AvatarGroup/AvatarGroup.js +1 -1
  9. package/lib/commonjs/components/Badge/Badge.js +2 -1
  10. package/lib/commonjs/components/Balance/Balance.js +2 -1
  11. package/lib/commonjs/components/BottomNav/BottomNav.js +2 -1
  12. package/lib/commonjs/components/BottomNavItem/BottomNavItem.js +106 -86
  13. package/lib/commonjs/components/Button/Button.js +190 -93
  14. package/lib/commonjs/components/ButtonGroup/ButtonGroup.js +1 -1
  15. package/lib/commonjs/components/Card/Card.js +2 -1
  16. package/lib/commonjs/components/CardCTA/CardCTA.js +1 -1
  17. package/lib/commonjs/components/CardProviderInfo/CardProviderInfo.js +1 -1
  18. package/lib/commonjs/components/Carousel/Carousel.js +3 -2
  19. package/lib/commonjs/components/Checkbox/Checkbox.js +2 -1
  20. package/lib/commonjs/components/ChipGroup/ChipGroup.js +1 -1
  21. package/lib/commonjs/components/ChipSelect/ChipSelect.js +2 -1
  22. package/lib/commonjs/components/DebitCard/DebitCard.js +1 -1
  23. package/lib/commonjs/components/Disclaimer/Disclaimer.js +2 -1
  24. package/lib/commonjs/components/Divider/Divider.js +2 -1
  25. package/lib/commonjs/components/Drawer/Drawer.js +109 -48
  26. package/lib/commonjs/components/EmptyState/EmptyState.js +2 -1
  27. package/lib/commonjs/components/FilterBar/FilterBar.js +1 -1
  28. package/lib/commonjs/components/Form/Form.js +2 -1
  29. package/lib/commonjs/components/FormField/FormField.js +3 -2
  30. package/lib/commonjs/components/HStack/HStack.js +1 -1
  31. package/lib/commonjs/components/HoldingsCard/HoldingsCard.js +2 -1
  32. package/lib/commonjs/components/IconButton/IconButton.js +118 -128
  33. package/lib/commonjs/components/IconCapsule/IconCapsule.js +61 -57
  34. package/lib/commonjs/components/InputSearch/InputSearch.js +7 -3
  35. package/lib/commonjs/components/LazyList/LazyList.js +1 -1
  36. package/lib/commonjs/components/LinearMeter/LinearMeter.js +3 -2
  37. package/lib/commonjs/components/ListGroup/ListGroup.js +1 -1
  38. package/lib/commonjs/components/ListItem/ListItem.js +190 -142
  39. package/lib/commonjs/components/MediaCard/MediaCard.js +3 -3
  40. package/lib/commonjs/components/MerchantProfile/MerchantProfile.js +2 -1
  41. package/lib/commonjs/components/MoneyValue/MoneyValue.js +2 -1
  42. package/lib/commonjs/components/NavArrow/NavArrow.js +82 -59
  43. package/lib/commonjs/components/NoteInput/NoteInput.js +2 -1
  44. package/lib/commonjs/components/Nudge/Nudge.js +1 -1
  45. package/lib/commonjs/components/Numpad/Numpad.js +2 -1
  46. package/lib/commonjs/components/OTP/OTP.js +1 -1
  47. package/lib/commonjs/components/PaymentFeedback/PaymentFeedback.js +2 -1
  48. package/lib/commonjs/components/Popup/Popup.js +2 -1
  49. package/lib/commonjs/components/ProductLabel/ProductLabel.js +2 -1
  50. package/lib/commonjs/components/ProgressBadge/ProgressBadge.js +2 -1
  51. package/lib/commonjs/components/RadioButton/RadioButton.js +2 -1
  52. package/lib/commonjs/components/RechargeCard/RechargeCard.js +2 -1
  53. package/lib/commonjs/components/Screen/Screen.js +1 -1
  54. package/lib/commonjs/components/Section/Section.js +500 -166
  55. package/lib/commonjs/components/SegmentedControl/SegmentedControl.js +3 -2
  56. package/lib/commonjs/components/StatItem/StatItem.js +2 -1
  57. package/lib/commonjs/components/StatusHero/StatusHero.js +2 -1
  58. package/lib/commonjs/components/Stepper/Step.js +2 -1
  59. package/lib/commonjs/components/Stepper/StepLabel.js +2 -1
  60. package/lib/commonjs/components/Stepper/Stepper.js +2 -1
  61. package/lib/commonjs/components/SupportText/SupportText.js +2 -1
  62. package/lib/commonjs/components/SupportText/SupportTextIcon.js +2 -1
  63. package/lib/commonjs/components/SwappableAmount/SwappableAmount.js +2 -1
  64. package/lib/commonjs/components/Tabs/TabItem.js +2 -1
  65. package/lib/commonjs/components/Tabs/Tabs.js +2 -1
  66. package/lib/commonjs/components/Text/Text.js +2 -1
  67. package/lib/commonjs/components/TextInput/TextInput.js +2 -2
  68. package/lib/commonjs/components/ThreadHero/ThreadHero.js +2 -1
  69. package/lib/commonjs/components/Title/Title.js +2 -1
  70. package/lib/commonjs/components/Toast/Toast.js +2 -1
  71. package/lib/commonjs/components/Toggle/Toggle.js +2 -1
  72. package/lib/commonjs/components/Tooltip/Tooltip.js +2 -1
  73. package/lib/commonjs/components/TransactionBubble/TransactionBubble.js +1 -1
  74. package/lib/commonjs/components/TransactionDetails/TransactionDetails.js +2 -2
  75. package/lib/commonjs/components/TransactionStatus/TransactionStatus.js +3 -2
  76. package/lib/commonjs/components/UpiHandle/UpiHandle.js +144 -110
  77. package/lib/commonjs/components/VStack/VStack.js +1 -1
  78. package/lib/commonjs/design-tokens/figma-variables-resolver.js +21 -3
  79. package/lib/commonjs/icons/registry.js +1 -1
  80. package/lib/commonjs/utils/react-utils.js +17 -0
  81. package/lib/module/components/Accordion/Accordion.js +2 -2
  82. package/lib/module/components/ActionFooter/ActionFooter.js +2 -2
  83. package/lib/module/components/ActionTile/ActionTile.js +2 -1
  84. package/lib/module/components/AmountInput/AmountInput.js +2 -1
  85. package/lib/module/components/AppBar/AppBar.js +2 -2
  86. package/lib/module/components/Avatar/Avatar.js +184 -162
  87. package/lib/module/components/AvatarGroup/AvatarGroup.js +2 -2
  88. package/lib/module/components/Badge/Badge.js +2 -1
  89. package/lib/module/components/Balance/Balance.js +2 -1
  90. package/lib/module/components/BottomNav/BottomNav.js +2 -1
  91. package/lib/module/components/BottomNavItem/BottomNavItem.js +108 -88
  92. package/lib/module/components/Button/Button.js +192 -95
  93. package/lib/module/components/ButtonGroup/ButtonGroup.js +2 -2
  94. package/lib/module/components/Card/Card.js +2 -1
  95. package/lib/module/components/CardCTA/CardCTA.js +2 -2
  96. package/lib/module/components/CardProviderInfo/CardProviderInfo.js +2 -2
  97. package/lib/module/components/Carousel/Carousel.js +3 -2
  98. package/lib/module/components/Checkbox/Checkbox.js +2 -1
  99. package/lib/module/components/ChipGroup/ChipGroup.js +2 -2
  100. package/lib/module/components/ChipSelect/ChipSelect.js +2 -1
  101. package/lib/module/components/DebitCard/DebitCard.js +2 -2
  102. package/lib/module/components/Disclaimer/Disclaimer.js +2 -1
  103. package/lib/module/components/Divider/Divider.js +2 -1
  104. package/lib/module/components/Drawer/Drawer.js +109 -48
  105. package/lib/module/components/EmptyState/EmptyState.js +2 -1
  106. package/lib/module/components/FilterBar/FilterBar.js +2 -2
  107. package/lib/module/components/Form/Form.js +2 -1
  108. package/lib/module/components/FormField/FormField.js +3 -2
  109. package/lib/module/components/HStack/HStack.js +2 -2
  110. package/lib/module/components/HoldingsCard/HoldingsCard.js +2 -1
  111. package/lib/module/components/IconButton/IconButton.js +120 -130
  112. package/lib/module/components/IconCapsule/IconCapsule.js +60 -57
  113. package/lib/module/components/InputSearch/InputSearch.js +7 -3
  114. package/lib/module/components/LazyList/LazyList.js +2 -2
  115. package/lib/module/components/LinearMeter/LinearMeter.js +3 -2
  116. package/lib/module/components/ListGroup/ListGroup.js +2 -2
  117. package/lib/module/components/ListItem/ListItem.js +194 -146
  118. package/lib/module/components/MediaCard/MediaCard.js +4 -2
  119. package/lib/module/components/MerchantProfile/MerchantProfile.js +2 -1
  120. package/lib/module/components/MoneyValue/MoneyValue.js +2 -1
  121. package/lib/module/components/NavArrow/NavArrow.js +82 -58
  122. package/lib/module/components/NoteInput/NoteInput.js +2 -1
  123. package/lib/module/components/Nudge/Nudge.js +2 -2
  124. package/lib/module/components/Numpad/Numpad.js +2 -1
  125. package/lib/module/components/OTP/OTP.js +2 -2
  126. package/lib/module/components/PaymentFeedback/PaymentFeedback.js +2 -1
  127. package/lib/module/components/Popup/Popup.js +2 -1
  128. package/lib/module/components/ProductLabel/ProductLabel.js +2 -1
  129. package/lib/module/components/ProgressBadge/ProgressBadge.js +2 -1
  130. package/lib/module/components/RadioButton/RadioButton.js +2 -1
  131. package/lib/module/components/RechargeCard/RechargeCard.js +2 -1
  132. package/lib/module/components/Screen/Screen.js +2 -2
  133. package/lib/module/components/Section/Section.js +503 -169
  134. package/lib/module/components/SegmentedControl/SegmentedControl.js +3 -2
  135. package/lib/module/components/StatItem/StatItem.js +2 -1
  136. package/lib/module/components/StatusHero/StatusHero.js +2 -1
  137. package/lib/module/components/Stepper/Step.js +2 -1
  138. package/lib/module/components/Stepper/StepLabel.js +2 -1
  139. package/lib/module/components/Stepper/Stepper.js +2 -1
  140. package/lib/module/components/SupportText/SupportText.js +2 -1
  141. package/lib/module/components/SupportText/SupportTextIcon.js +2 -1
  142. package/lib/module/components/SwappableAmount/SwappableAmount.js +2 -1
  143. package/lib/module/components/Tabs/TabItem.js +2 -1
  144. package/lib/module/components/Tabs/Tabs.js +2 -1
  145. package/lib/module/components/Text/Text.js +2 -1
  146. package/lib/module/components/TextInput/TextInput.js +3 -3
  147. package/lib/module/components/ThreadHero/ThreadHero.js +2 -1
  148. package/lib/module/components/Title/Title.js +2 -1
  149. package/lib/module/components/Toast/Toast.js +2 -1
  150. package/lib/module/components/Toggle/Toggle.js +2 -1
  151. package/lib/module/components/Tooltip/Tooltip.js +2 -1
  152. package/lib/module/components/TransactionBubble/TransactionBubble.js +2 -2
  153. package/lib/module/components/TransactionDetails/TransactionDetails.js +3 -3
  154. package/lib/module/components/TransactionStatus/TransactionStatus.js +3 -2
  155. package/lib/module/components/UpiHandle/UpiHandle.js +147 -113
  156. package/lib/module/components/VStack/VStack.js +2 -2
  157. package/lib/module/design-tokens/figma-variables-resolver.js +21 -3
  158. package/lib/module/icons/registry.js +1 -1
  159. package/lib/module/utils/react-utils.js +16 -0
  160. package/lib/typescript/src/components/Avatar/Avatar.d.ts +11 -17
  161. package/lib/typescript/src/components/BottomNavItem/BottomNavItem.d.ts +12 -8
  162. package/lib/typescript/src/components/Button/Button.d.ts +18 -1
  163. package/lib/typescript/src/components/IconButton/IconButton.d.ts +12 -29
  164. package/lib/typescript/src/components/IconCapsule/IconCapsule.d.ts +10 -18
  165. package/lib/typescript/src/components/InputSearch/InputSearch.d.ts +8 -3
  166. package/lib/typescript/src/components/ListItem/ListItem.d.ts +14 -1
  167. package/lib/typescript/src/components/NavArrow/NavArrow.d.ts +12 -11
  168. package/lib/typescript/src/components/Section/Section.d.ts +43 -48
  169. package/lib/typescript/src/components/UpiHandle/UpiHandle.d.ts +13 -12
  170. package/lib/typescript/src/icons/registry.d.ts +1 -1
  171. package/lib/typescript/src/utils/react-utils.d.ts +15 -0
  172. package/package.json +4 -6
  173. package/src/components/Accordion/Accordion.tsx +2 -2
  174. package/src/components/ActionFooter/ActionFooter.tsx +2 -2
  175. package/src/components/ActionTile/ActionTile.tsx +2 -1
  176. package/src/components/AmountInput/AmountInput.tsx +2 -1
  177. package/src/components/AppBar/AppBar.tsx +2 -2
  178. package/src/components/Avatar/Avatar.tsx +229 -158
  179. package/src/components/AvatarGroup/AvatarGroup.tsx +2 -2
  180. package/src/components/Badge/Badge.tsx +2 -1
  181. package/src/components/Balance/Balance.tsx +2 -1
  182. package/src/components/BottomNav/BottomNav.tsx +2 -1
  183. package/src/components/BottomNavItem/BottomNavItem.tsx +159 -88
  184. package/src/components/Button/Button.tsx +228 -101
  185. package/src/components/ButtonGroup/ButtonGroup.tsx +2 -2
  186. package/src/components/Card/Card.tsx +2 -1
  187. package/src/components/CardCTA/CardCTA.tsx +2 -2
  188. package/src/components/CardProviderInfo/CardProviderInfo.tsx +2 -2
  189. package/src/components/Carousel/Carousel.tsx +3 -2
  190. package/src/components/Checkbox/Checkbox.tsx +2 -1
  191. package/src/components/ChipGroup/ChipGroup.tsx +2 -2
  192. package/src/components/ChipSelect/ChipSelect.tsx +2 -1
  193. package/src/components/DebitCard/DebitCard.tsx +2 -2
  194. package/src/components/Disclaimer/Disclaimer.tsx +2 -1
  195. package/src/components/Divider/Divider.tsx +2 -1
  196. package/src/components/Drawer/Drawer.tsx +124 -58
  197. package/src/components/EmptyState/EmptyState.tsx +2 -1
  198. package/src/components/FilterBar/FilterBar.tsx +2 -2
  199. package/src/components/Form/Form.tsx +2 -1
  200. package/src/components/FormField/FormField.tsx +3 -2
  201. package/src/components/HStack/HStack.tsx +2 -2
  202. package/src/components/HoldingsCard/HoldingsCard.tsx +2 -1
  203. package/src/components/IconButton/IconButton.tsx +154 -126
  204. package/src/components/IconCapsule/IconCapsule.tsx +73 -54
  205. package/src/components/InputSearch/InputSearch.tsx +19 -5
  206. package/src/components/LazyList/LazyList.tsx +2 -2
  207. package/src/components/LinearMeter/LinearMeter.tsx +3 -2
  208. package/src/components/ListGroup/ListGroup.tsx +2 -2
  209. package/src/components/ListItem/ListItem.tsx +257 -187
  210. package/src/components/MediaCard/MediaCard.tsx +2 -1
  211. package/src/components/MerchantProfile/MerchantProfile.tsx +2 -1
  212. package/src/components/MoneyValue/MoneyValue.tsx +2 -1
  213. package/src/components/NavArrow/NavArrow.tsx +91 -58
  214. package/src/components/NoteInput/NoteInput.tsx +2 -1
  215. package/src/components/Nudge/Nudge.tsx +2 -2
  216. package/src/components/Numpad/Numpad.tsx +2 -1
  217. package/src/components/OTP/OTP.tsx +2 -2
  218. package/src/components/PaymentFeedback/PaymentFeedback.tsx +2 -1
  219. package/src/components/Popup/Popup.tsx +2 -1
  220. package/src/components/ProductLabel/ProductLabel.tsx +2 -1
  221. package/src/components/ProgressBadge/ProgressBadge.tsx +2 -2
  222. package/src/components/RadioButton/RadioButton.tsx +2 -1
  223. package/src/components/RechargeCard/RechargeCard.tsx +2 -1
  224. package/src/components/Screen/Screen.tsx +2 -2
  225. package/src/components/Section/Section.tsx +672 -176
  226. package/src/components/SegmentedControl/SegmentedControl.tsx +3 -2
  227. package/src/components/StatItem/StatItem.tsx +2 -1
  228. package/src/components/StatusHero/StatusHero.tsx +2 -1
  229. package/src/components/Stepper/Step.tsx +2 -1
  230. package/src/components/Stepper/StepLabel.tsx +2 -1
  231. package/src/components/Stepper/Stepper.tsx +2 -1
  232. package/src/components/SupportText/SupportText.tsx +2 -1
  233. package/src/components/SupportText/SupportTextIcon.tsx +2 -1
  234. package/src/components/SwappableAmount/SwappableAmount.tsx +2 -1
  235. package/src/components/Tabs/TabItem.tsx +2 -1
  236. package/src/components/Tabs/Tabs.tsx +2 -1
  237. package/src/components/Text/Text.tsx +2 -1
  238. package/src/components/TextInput/TextInput.tsx +3 -3
  239. package/src/components/ThreadHero/ThreadHero.tsx +2 -1
  240. package/src/components/Title/Title.tsx +2 -1
  241. package/src/components/Toast/Toast.tsx +2 -1
  242. package/src/components/Toggle/Toggle.tsx +2 -1
  243. package/src/components/Tooltip/Tooltip.tsx +2 -1
  244. package/src/components/TransactionBubble/TransactionBubble.tsx +2 -2
  245. package/src/components/TransactionDetails/TransactionDetails.tsx +3 -3
  246. package/src/components/TransactionStatus/TransactionStatus.tsx +3 -2
  247. package/src/components/UpiHandle/UpiHandle.tsx +193 -125
  248. package/src/components/VStack/VStack.tsx +2 -2
  249. package/src/design-tokens/figma-variables-resolver.ts +21 -3
  250. package/src/icons/registry.ts +1 -1
  251. package/src/utils/react-utils.ts +16 -0
  252. package/lib/typescript/App.d.ts +0 -2
  253. package/lib/typescript/index.d.ts +0 -2
  254. package/lib/typescript/metro.config.d.ts +0 -78
  255. package/lib/typescript/react-native.config.d.ts +0 -4
@@ -10,24 +10,69 @@ var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resol
10
10
  var _JFSThemeProvider = require("../../design-tokens/JFSThemeProvider");
11
11
  var _Icon = _interopRequireDefault(require("../../icons/Icon"));
12
12
  var _webPlatformUtils = require("../../utils/web-platform-utils");
13
+ var _reactUtils = require("../../utils/react-utils");
13
14
  var _jsxRuntime = require("react/jsx-runtime");
14
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
16
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
17
+ const IS_WEB = _reactNative.Platform.OS === 'web';
18
+ const IS_IOS = _reactNative.Platform.OS === 'ios';
19
+ const PRESS_DELAY = IS_IOS ? 130 : 0;
20
+ const pressedOverlayStyle = {
21
+ opacity: 0.7
22
+ };
23
+ const hoverOverlayStyle = {
24
+ opacity: 0.85
25
+ };
26
+ const focusOverlayStyle = {
27
+ borderBottomWidth: 2,
28
+ borderBottomColor: '#222'
29
+ };
30
+ function resolveBottomNavItemTokens(modes, disabled, iconColorOverride, iconSizeOverride) {
31
+ const gap = (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/gap', modes) ?? 6;
32
+ const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/fontFamily', modes) || 'JioType_Var:Medium';
33
+ const fontWeightRaw = (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/fontWeight', modes) ?? 500;
34
+ const fontWeight = typeof fontWeightRaw === 'number' ? fontWeightRaw.toString() : fontWeightRaw;
35
+ const fontSize = (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/fontSize', modes) ?? 11;
36
+ const lineHeight = (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/lineHeight', modes) ?? 14;
37
+ const labelColor = (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/label/color', modes) || '#0d0d0f';
38
+ const resolvedIconColor = iconColorOverride || (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/icon/color', modes) || '#ad8545';
39
+ const resolvedIconSize = iconSizeOverride ?? (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/icon/size', modes) ?? 24;
40
+ return {
41
+ baseContainerStyle: {
42
+ alignItems: 'center',
43
+ opacity: disabled ? 0.5 : 1
44
+ },
45
+ textStyle: {
46
+ color: labelColor,
47
+ fontFamily,
48
+ fontWeight: fontWeight,
49
+ fontSize,
50
+ lineHeight,
51
+ textAlign: 'center',
52
+ marginTop: gap
53
+ },
54
+ iconColor: resolvedIconColor,
55
+ iconSize: resolvedIconSize
56
+ };
57
+ }
58
+
16
59
  /**
17
60
  * Bottom navigation item with icon and label stacked vertically.
18
61
  *
19
- * All visual attributes resolve from Figma tokens via `getVariableByName`,
20
- * with the `modes` object passed straight through.
21
- *
22
- * @component
23
- * @param {Object} props
24
- * @param {string} [props.iconName="ic_home"] - Icon name from the registry.
25
- * ...
62
+ * Performance notes:
63
+ * - Token reads collapsed into a single `useMemo([modes, disabled, iconColor, iconSize])`.
64
+ * - Press visual via Pressable's `({ pressed })` style callback.
65
+ * - Hover and focus state are mirrored on web only (gated setters).
66
+ * - The previous version had no-op `onPressIn`/`onPressOut` handlers that
67
+ * forwarded to user callbacks but did nothing else they were still
68
+ * creating fresh closures every render and forcing Pressable to re-bind.
69
+ * Now stable via a ref-backed wrapper.
70
+ * - Wrapped in `React.memo`.
26
71
  */
27
72
  function BottomNavItem({
28
73
  iconName = 'ic_home',
29
74
  label = 'Home',
30
- modes: propModes = {},
75
+ modes: propModes = _reactUtils.EMPTY_MODES,
31
76
  onPress,
32
77
  disabled = false,
33
78
  style,
@@ -43,54 +88,21 @@ function BottomNavItem({
43
88
  const {
44
89
  modes: globalModes
45
90
  } = (0, _JFSThemeProvider.useTokens)();
46
- const modes = {
91
+ const modes = (0, _react.useMemo)(() => globalModes === _reactUtils.EMPTY_MODES && propModes === _reactUtils.EMPTY_MODES ? _reactUtils.EMPTY_MODES : {
47
92
  ...globalModes,
48
93
  ...propModes
49
- };
94
+ }, [globalModes, propModes]);
95
+ const tokens = (0, _react.useMemo)(() => resolveBottomNavItemTokens(modes, disabled, iconColorOverride, iconSizeOverride), [modes, disabled, iconColorOverride, iconSizeOverride]);
50
96
  const [isFocused, setIsFocused] = (0, _react.useState)(false);
51
97
  const [isHovered, setIsHovered] = (0, _react.useState)(false);
52
- const pressedStyle = {
53
- opacity: 0.7
54
- };
55
- const focusStyle = {
56
- borderBottomWidth: 2,
57
- borderBottomColor: '#222'
58
- };
59
- const hoverStyle = {
60
- opacity: 0.85
61
- };
62
- // Resolve spacing and typography
63
- const gap = (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/gap', modes) ?? 6;
64
- const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/fontFamily', modes) || 'JioType_Var:Medium';
65
- const fontWeightRaw = (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/fontWeight', modes) ?? 500;
66
- const fontWeight = typeof fontWeightRaw === 'number' ? fontWeightRaw.toString() : fontWeightRaw;
67
- const fontSize = (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/fontSize', modes) ?? 11;
68
- const lineHeight = (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/lineHeight', modes) ?? 14;
69
-
70
- // Resolve Label Color
71
- const labelColor = (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/label/color', modes) || '#0d0d0f';
72
-
73
- // Resolve icon appearance
74
- const resolvedIconColor = iconColorOverride || (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/icon/color', modes) || '#ad8545';
75
- const resolvedIconSize = iconSizeOverride ?? (0, _figmaVariablesResolver.getVariableByName)('bottomNavItem/icon/size', modes) ?? 24;
76
- const baseContainerStyle = {
77
- alignItems: 'center',
78
- opacity: disabled ? 0.5 : 1
79
- };
80
- const textStyle = {
81
- color: labelColor,
82
- fontFamily,
83
- fontWeight,
84
- fontSize,
85
- lineHeight,
86
- textAlign: 'center',
87
- marginTop: gap
88
- };
89
-
90
- // Use provided accessibilityLabel or fall back to label
98
+ const userHandlersRef = (0, _react.useRef)({});
99
+ userHandlersRef.current.onPressIn = rest?.onPressIn;
100
+ userHandlersRef.current.onPressOut = rest?.onPressOut;
101
+ userHandlersRef.current.onFocus = rest?.onFocus;
102
+ userHandlersRef.current.onBlur = rest?.onBlur;
103
+ userHandlersRef.current.onHoverIn = rest?.onHoverIn;
104
+ userHandlersRef.current.onHoverOut = rest?.onHoverOut;
91
105
  const defaultAccessibilityLabel = accessibilityLabel || label;
92
-
93
- // Get web platform support props (only used when onPress is defined)
94
106
  const webProps = (0, _webPlatformUtils.usePressableWebSupport)({
95
107
  restProps: rest,
96
108
  onPress: disabled ? undefined : onPress,
@@ -98,23 +110,50 @@ function BottomNavItem({
98
110
  accessibilityLabel: defaultAccessibilityLabel,
99
111
  webAccessibilityProps
100
112
  });
101
- const renderContent = () => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
113
+ const composedTextStyle = (0, _react.useMemo)(() => labelStyle ? [tokens.textStyle, labelStyle] : tokens.textStyle, [tokens.textStyle, labelStyle]);
114
+ const renderContent = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
102
115
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
103
116
  name: iconName,
104
- size: resolvedIconSize,
105
- color: resolvedIconColor,
117
+ size: tokens.iconSize,
118
+ color: tokens.iconColor,
106
119
  accessibilityElementsHidden: true,
107
120
  importantForAccessibility: "no"
108
121
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
109
- style: [textStyle, labelStyle],
122
+ style: composedTextStyle,
110
123
  accessibilityElementsHidden: true,
111
124
  importantForAccessibility: "no",
112
125
  children: label
113
126
  })]
114
127
  });
128
+ const staticContainerStyle = (0, _react.useMemo)(() => style ? [tokens.baseContainerStyle, style] : tokens.baseContainerStyle, [tokens.baseContainerStyle, style]);
129
+ const handlePressIn = (0, _react.useCallback)(e => {
130
+ userHandlersRef.current.onPressIn?.(e);
131
+ }, []);
132
+ const handlePressOut = (0, _react.useCallback)(e => {
133
+ userHandlersRef.current.onPressOut?.(e);
134
+ }, []);
135
+ const handleFocus = (0, _react.useCallback)(e => {
136
+ if (IS_WEB) setIsFocused(true);
137
+ userHandlersRef.current.onFocus?.(e);
138
+ }, []);
139
+ const handleBlur = (0, _react.useCallback)(e => {
140
+ if (IS_WEB) setIsFocused(false);
141
+ userHandlersRef.current.onBlur?.(e);
142
+ }, []);
143
+ const handleHoverIn = (0, _react.useCallback)(e => {
144
+ if (IS_WEB) setIsHovered(true);
145
+ userHandlersRef.current.onHoverIn?.(e);
146
+ }, []);
147
+ const handleHoverOut = (0, _react.useCallback)(e => {
148
+ if (IS_WEB) setIsHovered(false);
149
+ userHandlersRef.current.onHoverOut?.(e);
150
+ }, []);
151
+ const pressableStyle = (0, _react.useCallback)(({
152
+ pressed
153
+ }) => [tokens.baseContainerStyle, style, pressed && !disabled ? pressedOverlayStyle : null, isHovered && !disabled ? hoverOverlayStyle : null, isFocused && !disabled ? focusOverlayStyle : null], [tokens.baseContainerStyle, style, isHovered, isFocused, disabled]);
115
154
  if (!onPress) {
116
155
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
117
- style: [baseContainerStyle, style],
156
+ style: staticContainerStyle,
118
157
  accessibilityRole: "tab",
119
158
  accessibilityLabel: undefined,
120
159
  accessibilityHint: accessibilityHint,
@@ -123,7 +162,7 @@ function BottomNavItem({
123
162
  ...accessibilityState
124
163
  },
125
164
  ...rest,
126
- children: renderContent()
165
+ children: renderContent
127
166
  });
128
167
  }
129
168
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
@@ -137,35 +176,16 @@ function BottomNavItem({
137
176
  },
138
177
  onPress: onPress,
139
178
  disabled: disabled,
140
- onPressIn: e => {
141
- ;
142
- rest?.onPressIn?.(e);
143
- },
144
- onPressOut: e => {
145
- ;
146
- rest?.onPressOut?.(e);
147
- },
148
- onFocus: e => {
149
- setIsFocused(true);
150
- rest?.onFocus?.(e);
151
- },
152
- onBlur: e => {
153
- setIsFocused(false);
154
- rest?.onBlur?.(e);
155
- },
156
- onHoverIn: e => {
157
- setIsHovered(true);
158
- rest?.onHoverIn?.(e);
159
- },
160
- onHoverOut: e => {
161
- setIsHovered(false);
162
- rest?.onHoverOut?.(e);
163
- },
164
- style: ({
165
- pressed
166
- }) => [baseContainerStyle, style, pressed && !disabled ? pressedStyle : null, isHovered && !disabled ? hoverStyle : null, isFocused && !disabled ? focusStyle : null],
179
+ onPressIn: handlePressIn,
180
+ onPressOut: handlePressOut,
181
+ onFocus: handleFocus,
182
+ onBlur: handleBlur,
183
+ onHoverIn: handleHoverIn,
184
+ onHoverOut: handleHoverOut,
185
+ unstable_pressDelay: PRESS_DELAY,
186
+ style: pressableStyle,
167
187
  ...webProps,
168
- children: renderContent()
188
+ children: renderContent
169
189
  });
170
190
  }
171
- var _default = exports.default = BottomNavItem;
191
+ var _default = exports.default = /*#__PURE__*/_react.default.memo(BottomNavItem);
@@ -8,10 +8,120 @@ var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactNative = require("react-native");
9
9
  var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
10
10
  var _webPlatformUtils = require("../../utils/web-platform-utils");
11
+ var _reactUtils = require("../../utils/react-utils");
11
12
  var _Icon = _interopRequireDefault(require("../../icons/Icon"));
12
13
  var _jsxRuntime = require("react/jsx-runtime");
13
14
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
15
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
16
+ // ---------------------------------------------------------------------------
17
+ // Module-scope constants — never re-allocated per render.
18
+ // ---------------------------------------------------------------------------
19
+
20
+ const IS_WEB = _reactNative.Platform.OS === 'web';
21
+ const IS_IOS = _reactNative.Platform.OS === 'ios';
22
+
23
+ // iOS Pressable inside a ScrollView fires onPressIn on touchstart by default,
24
+ // which causes a visible "press" flash that snaps back when ScrollView claims
25
+ // the gesture. Delaying press by ~130ms lets the scroll responder cancel the
26
+ // gesture before the pressed visual is ever applied.
27
+ //
28
+ // Android uses the responder system + the touchable cancellation pipeline and
29
+ // does not need an extra delay; setting it to 0 keeps tap latency identical
30
+ // to today's behavior on Android.
31
+ const PRESS_DELAY = IS_IOS ? 130 : 0;
32
+ const containerLayoutStyle = {
33
+ flexDirection: 'row',
34
+ alignItems: 'center',
35
+ justifyContent: 'center'
36
+ };
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // Token resolution
40
+ // ---------------------------------------------------------------------------
41
+ //
42
+ // Memoizing all token reads in a single useMemo keyed on (modes, disabled)
43
+ // turns the previous ~21 cache lookups + arithmetic per render into a single
44
+ // resolve per (modes, disabled) tuple. With `EMPTY_MODES` as the default,
45
+ // the most common path (no modes prop) is resolved once for the whole app.
46
+ // ---------------------------------------------------------------------------
47
+
48
+ function resolveButtonTokens(modes, disabled) {
49
+ const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('button/background', modes) || '#cfa159';
50
+ const borderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', modes) || 'rgba(255,255,255,0)';
51
+ const borderWidth = (0, _figmaVariablesResolver.getVariableByName)('button/border/size', modes);
52
+ const radius = (0, _figmaVariablesResolver.getVariableByName)('button/radius', modes) || 9999;
53
+ const paddingHorizontal = (0, _figmaVariablesResolver.getVariableByName)('button/padding/horizontal', modes) || 20;
54
+ const paddingVertical = (0, _figmaVariablesResolver.getVariableByName)('button/padding/vertical', modes) || 12;
55
+ const gap = (0, _figmaVariablesResolver.getVariableByName)('button/gap', modes) || 8;
56
+ const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('button/fontFamily', modes) || 'System';
57
+ const fontWeightValue = (0, _figmaVariablesResolver.getVariableByName)('button/fontWeight', modes) || 700;
58
+ const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue;
59
+ const lineHeight = (0, _figmaVariablesResolver.getVariableByName)('button/lineHeight', modes) || 19;
60
+ const fontSize = (0, _figmaVariablesResolver.getVariableByName)('button/fontSize', modes) || 16;
61
+ const textColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', modes) || '#0f0d0a';
62
+ const iconSize = (0, _figmaVariablesResolver.getVariableByName)('button/icon/size', modes) ?? 18;
63
+
64
+ // Pressed tokens are always resolved so the Pressable `style` callback can
65
+ // apply the pressed visual without scheduling a React render.
66
+ const pressedModes = {
67
+ ...modes,
68
+ 'Button / State': 'Pressed'
69
+ };
70
+ const pressedBg = (0, _figmaVariablesResolver.getVariableByName)('button/background', pressedModes) || backgroundColor;
71
+ const pressedBorderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', pressedModes) || borderColor;
72
+ const pressedTextColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', pressedModes) || textColor;
73
+
74
+ // Hover tokens are only meaningful on web. Skipping them on native saves
75
+ // ~3 token lookups + an object spread per render on every Button.
76
+ let hoverContainer = null;
77
+ let hoverTextColor = null;
78
+ if (IS_WEB) {
79
+ const hoverModes = {
80
+ ...modes,
81
+ 'Button / State': 'Hover'
82
+ };
83
+ const hoverBg = (0, _figmaVariablesResolver.getVariableByName)('button/background', hoverModes) || backgroundColor;
84
+ const hoverBorderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', hoverModes) || borderColor;
85
+ hoverTextColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', hoverModes) || textColor;
86
+ hoverContainer = {
87
+ backgroundColor: hoverBg,
88
+ borderColor: hoverBorderColor
89
+ };
90
+ }
91
+ return {
92
+ container: {
93
+ ...containerLayoutStyle,
94
+ paddingHorizontal: paddingHorizontal,
95
+ paddingVertical: paddingVertical,
96
+ borderRadius: radius,
97
+ borderWidth: borderWidth ?? 1,
98
+ borderColor: borderColor,
99
+ backgroundColor: backgroundColor,
100
+ gap: gap,
101
+ opacity: disabled ? 0.5 : 1
102
+ },
103
+ baseLabel: {
104
+ color: textColor,
105
+ fontFamily: fontFamily,
106
+ fontWeight: fontWeight,
107
+ fontSize: fontSize,
108
+ lineHeight: lineHeight
109
+ },
110
+ hoverContainer,
111
+ pressedContainer: {
112
+ backgroundColor: pressedBg,
113
+ borderColor: pressedBorderColor
114
+ },
115
+ hoverTextColor,
116
+ pressedTextColor,
117
+ baseIconColor: textColor,
118
+ hoverIconColor: hoverTextColor,
119
+ pressedIconColor: pressedTextColor,
120
+ iconSize,
121
+ accessoryOffset: gap / 2
122
+ };
123
+ }
124
+
15
125
  /**
16
126
  * Button component that maps directly to the Figma design using design tokens.
17
127
  *
@@ -30,15 +140,31 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
30
140
  * @param {string} [props.accessibilityLabel] - Accessibility label for screen readers. If not provided, uses label or children text
31
141
  * @param {string} [props.accessibilityHint] - Additional accessibility hint for screen readers
32
142
  * @param {Object} [props.accessibilityState] - Additional accessibility state information
143
+ *
144
+ * Performance notes:
145
+ * - The pressed visual (background/border swap) is applied through the
146
+ * Pressable `style` callback, NOT a mirrored React state. This avoids the
147
+ * flicker that scrolling parents previously caused: when a ScrollView
148
+ * cancels a touch the host view simply reverts its style without React
149
+ * rendering. On iOS we additionally use `unstable_pressDelay` so the
150
+ * pressed visual is never even applied during a scroll-cancelled touch.
151
+ * - Hover state is only mirrored into React state on web (where hover events
152
+ * fire). On native the setter call is gated out so the component never
153
+ * re-renders due to hover.
154
+ * - All design-token reads are folded into a single `useMemo` keyed on
155
+ * `(modes, disabled)`.
156
+ * - The component is wrapped in `React.memo`. Callers that want to take
157
+ * advantage of this should pass stable `modes` (the default `EMPTY_MODES`
158
+ * is already stable) and stable callback props.
33
159
  */
34
- function Button({
160
+ function ButtonImpl({
35
161
  label = 'Button',
36
162
  children,
37
163
  renderContent,
38
164
  leading,
39
165
  trailing,
40
166
  icon,
41
- modes = {},
167
+ modes = _reactUtils.EMPTY_MODES,
42
168
  onPress,
43
169
  disabled = false,
44
170
  style,
@@ -49,85 +175,50 @@ function Button({
49
175
  webAccessibilityProps,
50
176
  ...rest
51
177
  }) {
52
- // Resolve design tokens with the provided modes
53
- const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('button/background', modes) || '#cfa159';
54
- const borderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', modes) || 'rgba(255,255,255,0)';
55
- const borderWidth = (0, _figmaVariablesResolver.getVariableByName)('button/border/size', modes);
56
- const radius = (0, _figmaVariablesResolver.getVariableByName)('button/radius', modes) || 9999;
57
- const paddingHorizontal = (0, _figmaVariablesResolver.getVariableByName)('button/padding/horizontal', modes) || 20;
58
- const paddingVertical = (0, _figmaVariablesResolver.getVariableByName)('button/padding/vertical', modes) || 12;
59
- const gap = (0, _figmaVariablesResolver.getVariableByName)('button/gap', modes) || 8;
60
- const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('button/fontFamily', modes) || 'System';
61
- const fontWeightValue = (0, _figmaVariablesResolver.getVariableByName)('button/fontWeight', modes) || 700;
62
- const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue;
63
- const lineHeight = (0, _figmaVariablesResolver.getVariableByName)('button/lineHeight', modes) || 19;
64
- const fontSize = (0, _figmaVariablesResolver.getVariableByName)('button/fontSize', modes) || 16;
65
- const textColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', modes) || '#0f0d0a';
66
- const iconColor = textColor;
67
- const iconSize = (0, _figmaVariablesResolver.getVariableByName)('button/icon/size', modes) ?? 18;
178
+ // Hover state is web-only in practice; the setter is gated so native taps
179
+ // never schedule a re-render through this hook.
68
180
  const [isHovered, setIsHovered] = (0, _react.useState)(false);
69
- const [isPressed, setIsPressed] = (0, _react.useState)(false);
70
- const hoverModes = {
71
- ...modes,
72
- "Button / State": "Hover"
73
- };
74
- const hoverBg = (0, _figmaVariablesResolver.getVariableByName)('button/background', hoverModes) || backgroundColor;
75
- const hoverBorderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', hoverModes) || borderColor;
76
- const hoverTextColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', hoverModes) || textColor;
77
- const hoverIconColor = hoverTextColor;
78
- const pressedModes = {
79
- ...modes,
80
- "Button / State": "Pressed"
81
- };
82
- const pressedBg = (0, _figmaVariablesResolver.getVariableByName)('button/background', pressedModes) || backgroundColor;
83
- const pressedBorderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', pressedModes) || borderColor;
84
- const pressedTextColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', pressedModes) || textColor;
85
- const pressedIconColor = pressedTextColor;
86
- const activeTextColor = isPressed && !disabled ? pressedTextColor : isHovered && !disabled ? hoverTextColor : textColor;
87
- const activeIconColor = isPressed && !disabled ? pressedIconColor : isHovered && !disabled ? hoverIconColor : iconColor;
88
- const baseLabelTextStyle = {
89
- color: activeTextColor,
90
- fontFamily,
91
- fontWeight,
92
- fontSize,
93
- lineHeight
94
- };
95
- const mergedLabelTextStyle = [baseLabelTextStyle, labelStyle];
181
+
182
+ // Mirror user-supplied handlers in a ref so our wrapper handlers can stay
183
+ // referentially stable (otherwise Pressable would re-bind every render).
184
+ const userHandlersRef = (0, _react.useRef)({});
185
+ userHandlersRef.current.onPressIn = rest?.onPressIn;
186
+ userHandlersRef.current.onPressOut = rest?.onPressOut;
187
+ userHandlersRef.current.onHoverIn = rest?.onHoverIn;
188
+ userHandlersRef.current.onHoverOut = rest?.onHoverOut;
189
+ const tokens = (0, _react.useMemo)(() => resolveButtonTokens(modes, disabled), [modes, disabled]);
190
+
191
+ // Active label color: base by default; hover override (web-only) when hovered.
192
+ // Press color is intentionally NOT applied to the label on native — applying
193
+ // it would require a React render per touch and re-introduce the flicker.
194
+ const activeLabelStyle = (0, _react.useMemo)(() => {
195
+ const hoverOverride = isHovered && !disabled && tokens.hoverTextColor ? {
196
+ color: tokens.hoverTextColor
197
+ } : null;
198
+ return [tokens.baseLabel, hoverOverride, labelStyle];
199
+ }, [tokens.baseLabel, tokens.hoverTextColor, isHovered, disabled, labelStyle]);
200
+ const activeIconColor = isHovered && !disabled && tokens.hoverIconColor ? tokens.hoverIconColor : tokens.baseIconColor;
96
201
  let content;
97
202
  if (renderContent) {
98
203
  // Preferred extension point: caller fully controls the inner node but we
99
204
  // still provide the token-derived text styles.
100
- content = renderContent(mergedLabelTextStyle);
205
+ content = renderContent(activeLabelStyle);
101
206
  } else if (children !== undefined && children !== null) {
102
207
  // Full-content override: label and labelStyle are intentionally ignored.
103
208
  content = children;
104
209
  } else {
105
210
  content = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
106
- style: mergedLabelTextStyle,
211
+ style: activeLabelStyle,
107
212
  numberOfLines: 1,
108
213
  children: label
109
214
  });
110
215
  }
111
- const containerBaseStyle = {
112
- flexDirection: 'row',
113
- alignItems: 'center',
114
- justifyContent: 'center',
115
- paddingHorizontal,
116
- paddingVertical,
117
- borderRadius: radius,
118
- borderWidth: borderWidth ?? 1,
119
- borderColor,
120
- backgroundColor,
121
- gap,
122
- opacity: disabled ? 0.5 : 1
123
- };
124
- const accessoryOffset = gap / 2;
125
216
  const leadingAccessoryStyle = (0, _react.useMemo)(() => ({
126
- marginRight: accessoryOffset
127
- }), [accessoryOffset]);
217
+ marginRight: tokens.accessoryOffset
218
+ }), [tokens.accessoryOffset]);
128
219
  const trailingAccessoryStyle = (0, _react.useMemo)(() => ({
129
- marginLeft: accessoryOffset
130
- }), [accessoryOffset]);
220
+ marginLeft: tokens.accessoryOffset
221
+ }), [tokens.accessoryOffset]);
131
222
 
132
223
  // Use provided accessibilityLabel, or fall back to label text
133
224
  const defaultAccessibilityLabel = accessibilityLabel || (typeof label === 'string' ? label : undefined);
@@ -140,14 +231,32 @@ function Button({
140
231
  accessibilityLabel: defaultAccessibilityLabel,
141
232
  webAccessibilityProps
142
233
  });
143
- const hoverStyle = {
144
- backgroundColor: hoverBg,
145
- borderColor: hoverBorderColor
146
- };
147
- const pressedStyle = {
148
- backgroundColor: pressedBg,
149
- borderColor: pressedBorderColor
150
- };
234
+
235
+ // Stable handler identities. The user's handlers are read through a ref so
236
+ // these wrappers do not need to be re-created when the user passes inline
237
+ // closures, which would defeat React.memo on Pressable.
238
+ const handlePressIn = (0, _react.useCallback)(e => {
239
+ userHandlersRef.current.onPressIn?.(e);
240
+ }, []);
241
+ const handlePressOut = (0, _react.useCallback)(e => {
242
+ userHandlersRef.current.onPressOut?.(e);
243
+ }, []);
244
+ const handleHoverIn = (0, _react.useCallback)(e => {
245
+ if (IS_WEB) setIsHovered(true);
246
+ userHandlersRef.current.onHoverIn?.(e);
247
+ }, []);
248
+ const handleHoverOut = (0, _react.useCallback)(e => {
249
+ if (IS_WEB) setIsHovered(false);
250
+ userHandlersRef.current.onHoverOut?.(e);
251
+ }, []);
252
+
253
+ // The Pressable style callback receives `pressed` from RN's gesture
254
+ // pipeline and applies the pressed visual to the host view directly,
255
+ // without going through React. This is the key to flicker-free behavior:
256
+ // a scroll-cancelled touch reverts the style on the native side only.
257
+ const styleCallback = (0, _react.useCallback)(({
258
+ pressed
259
+ }) => [tokens.container, isHovered && !disabled ? tokens.hoverContainer : null, pressed && !disabled ? tokens.pressedContainer : null, style], [tokens.container, tokens.hoverContainer, tokens.pressedContainer, isHovered, disabled, style]);
151
260
  if (__DEV__) {
152
261
  if (children && labelStyle) {
153
262
  console.warn('[Button] `children` was provided; `labelStyle` will be ignored. ' + 'Use `renderContent` if you need custom content that still reuses the label styles.');
@@ -170,25 +279,12 @@ function Button({
170
279
  ...(onPress !== undefined ? {
171
280
  onPress
172
281
  } : {}),
173
- onPressIn: e => {
174
- setIsPressed(true);
175
- rest?.onPressIn?.(e);
176
- },
177
- onPressOut: e => {
178
- setIsPressed(false);
179
- rest?.onPressOut?.(e);
180
- },
181
- onHoverIn: e => {
182
- setIsHovered(true);
183
- rest?.onHoverIn?.(e);
184
- },
185
- onHoverOut: e => {
186
- setIsHovered(false);
187
- rest?.onHoverOut?.(e);
188
- },
189
- style: ({
190
- pressed
191
- }) => [containerBaseStyle, isHovered && !disabled ? hoverStyle : null, (pressed || isPressed) && !disabled ? pressedStyle : null, style],
282
+ onPressIn: handlePressIn,
283
+ onPressOut: handlePressOut,
284
+ onHoverIn: handleHoverIn,
285
+ onHoverOut: handleHoverOut,
286
+ unstable_pressDelay: PRESS_DELAY,
287
+ style: styleCallback,
192
288
  ...webProps,
193
289
  children: [leading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
194
290
  style: leadingAccessoryStyle,
@@ -197,7 +293,7 @@ function Button({
197
293
  style: trailingAccessoryStyle,
198
294
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
199
295
  name: icon,
200
- size: iconSize,
296
+ size: tokens.iconSize,
201
297
  color: activeIconColor,
202
298
  accessibilityElementsHidden: true,
203
299
  importantForAccessibility: "no"
@@ -208,4 +304,5 @@ function Button({
208
304
  }) : null]
209
305
  });
210
306
  }
307
+ const Button = /*#__PURE__*/_react.default.memo(ButtonImpl);
211
308
  var _default = exports.default = Button;
@@ -30,7 +30,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
30
30
  */
31
31
  function ButtonGroup({
32
32
  children,
33
- modes = {},
33
+ modes = _reactUtils.EMPTY_MODES,
34
34
  style
35
35
  }) {
36
36
  // Resolve design tokens
@@ -8,6 +8,7 @@ exports.default = void 0;
8
8
  var _react = _interopRequireWildcard(require("react"));
9
9
  var _reactNative = require("react-native");
10
10
  var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
11
+ var _reactUtils = require("../../utils/react-utils");
11
12
  var _jsxRuntime = require("react/jsx-runtime");
12
13
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
13
14
  /**
@@ -31,7 +32,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
31
32
  function Card({
32
33
  media,
33
34
  children,
34
- modes = {},
35
+ modes = _reactUtils.EMPTY_MODES,
35
36
  mediaAspectRatio = 154 / 116,
36
37
  style
37
38
  }) {
@@ -19,7 +19,7 @@ function CardCTA({
19
19
  iconName = 'ic_upi_number',
20
20
  buttonLabel = 'Button',
21
21
  onPressButton,
22
- modes: propModes = {},
22
+ modes: propModes = _reactUtils.EMPTY_MODES,
23
23
  iconSlot,
24
24
  buttonSlot,
25
25
  style
@@ -22,7 +22,7 @@ function CardProviderInfo({
22
22
  label = 'Gold',
23
23
  imageSource,
24
24
  children,
25
- modes = {},
25
+ modes = _reactUtils.EMPTY_MODES,
26
26
  style
27
27
  }) {
28
28
  const background = (0, _figmaVariablesResolver.getVariableByName)('card/providerInfo/background', modes) ?? '#fef4e5';