jfs-components 0.0.61 → 0.0.63

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 (259) hide show
  1. package/CHANGELOG.md +36 -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 +2 -1
  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 +2 -3
  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 +268 -156
  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/JFSThemeProvider.js +2 -38
  79. package/lib/commonjs/design-tokens/figma-variables-resolver.js +21 -3
  80. package/lib/commonjs/icons/registry.js +1 -1
  81. package/lib/commonjs/utils/react-utils.js +26 -3
  82. package/lib/module/components/Accordion/Accordion.js +2 -2
  83. package/lib/module/components/ActionFooter/ActionFooter.js +2 -2
  84. package/lib/module/components/ActionTile/ActionTile.js +2 -1
  85. package/lib/module/components/AmountInput/AmountInput.js +2 -1
  86. package/lib/module/components/AppBar/AppBar.js +2 -2
  87. package/lib/module/components/Avatar/Avatar.js +184 -162
  88. package/lib/module/components/AvatarGroup/AvatarGroup.js +2 -2
  89. package/lib/module/components/Badge/Badge.js +2 -1
  90. package/lib/module/components/Balance/Balance.js +2 -1
  91. package/lib/module/components/BottomNav/BottomNav.js +2 -1
  92. package/lib/module/components/BottomNavItem/BottomNavItem.js +108 -88
  93. package/lib/module/components/Button/Button.js +192 -95
  94. package/lib/module/components/ButtonGroup/ButtonGroup.js +2 -2
  95. package/lib/module/components/Card/Card.js +2 -1
  96. package/lib/module/components/CardCTA/CardCTA.js +2 -2
  97. package/lib/module/components/CardProviderInfo/CardProviderInfo.js +2 -2
  98. package/lib/module/components/Carousel/Carousel.js +3 -2
  99. package/lib/module/components/Checkbox/Checkbox.js +2 -1
  100. package/lib/module/components/ChipGroup/ChipGroup.js +2 -2
  101. package/lib/module/components/ChipSelect/ChipSelect.js +2 -1
  102. package/lib/module/components/DebitCard/DebitCard.js +2 -2
  103. package/lib/module/components/Disclaimer/Disclaimer.js +2 -1
  104. package/lib/module/components/Divider/Divider.js +2 -1
  105. package/lib/module/components/Drawer/Drawer.js +2 -1
  106. package/lib/module/components/EmptyState/EmptyState.js +2 -1
  107. package/lib/module/components/FilterBar/FilterBar.js +2 -2
  108. package/lib/module/components/Form/Form.js +2 -1
  109. package/lib/module/components/FormField/FormField.js +3 -2
  110. package/lib/module/components/HStack/HStack.js +2 -2
  111. package/lib/module/components/HoldingsCard/HoldingsCard.js +2 -1
  112. package/lib/module/components/IconButton/IconButton.js +120 -130
  113. package/lib/module/components/IconCapsule/IconCapsule.js +60 -57
  114. package/lib/module/components/InputSearch/InputSearch.js +7 -3
  115. package/lib/module/components/LazyList/LazyList.js +2 -2
  116. package/lib/module/components/LinearMeter/LinearMeter.js +3 -2
  117. package/lib/module/components/ListGroup/ListGroup.js +3 -4
  118. package/lib/module/components/ListItem/ListItem.js +194 -146
  119. package/lib/module/components/MediaCard/MediaCard.js +4 -2
  120. package/lib/module/components/MerchantProfile/MerchantProfile.js +2 -1
  121. package/lib/module/components/MoneyValue/MoneyValue.js +2 -1
  122. package/lib/module/components/NavArrow/NavArrow.js +82 -58
  123. package/lib/module/components/NoteInput/NoteInput.js +2 -1
  124. package/lib/module/components/Nudge/Nudge.js +2 -2
  125. package/lib/module/components/Numpad/Numpad.js +2 -1
  126. package/lib/module/components/OTP/OTP.js +2 -2
  127. package/lib/module/components/PaymentFeedback/PaymentFeedback.js +2 -1
  128. package/lib/module/components/Popup/Popup.js +2 -1
  129. package/lib/module/components/ProductLabel/ProductLabel.js +2 -1
  130. package/lib/module/components/ProgressBadge/ProgressBadge.js +2 -1
  131. package/lib/module/components/RadioButton/RadioButton.js +2 -1
  132. package/lib/module/components/RechargeCard/RechargeCard.js +2 -1
  133. package/lib/module/components/Screen/Screen.js +2 -2
  134. package/lib/module/components/Section/Section.js +271 -159
  135. package/lib/module/components/SegmentedControl/SegmentedControl.js +3 -2
  136. package/lib/module/components/StatItem/StatItem.js +2 -1
  137. package/lib/module/components/StatusHero/StatusHero.js +2 -1
  138. package/lib/module/components/Stepper/Step.js +2 -1
  139. package/lib/module/components/Stepper/StepLabel.js +2 -1
  140. package/lib/module/components/Stepper/Stepper.js +2 -1
  141. package/lib/module/components/SupportText/SupportText.js +2 -1
  142. package/lib/module/components/SupportText/SupportTextIcon.js +2 -1
  143. package/lib/module/components/SwappableAmount/SwappableAmount.js +2 -1
  144. package/lib/module/components/Tabs/TabItem.js +2 -1
  145. package/lib/module/components/Tabs/Tabs.js +2 -1
  146. package/lib/module/components/Text/Text.js +2 -1
  147. package/lib/module/components/TextInput/TextInput.js +3 -3
  148. package/lib/module/components/ThreadHero/ThreadHero.js +2 -1
  149. package/lib/module/components/Title/Title.js +2 -1
  150. package/lib/module/components/Toast/Toast.js +2 -1
  151. package/lib/module/components/Toggle/Toggle.js +2 -1
  152. package/lib/module/components/Tooltip/Tooltip.js +2 -1
  153. package/lib/module/components/TransactionBubble/TransactionBubble.js +2 -2
  154. package/lib/module/components/TransactionDetails/TransactionDetails.js +3 -3
  155. package/lib/module/components/TransactionStatus/TransactionStatus.js +3 -2
  156. package/lib/module/components/UpiHandle/UpiHandle.js +147 -113
  157. package/lib/module/components/VStack/VStack.js +2 -2
  158. package/lib/module/design-tokens/JFSThemeProvider.js +2 -35
  159. package/lib/module/design-tokens/figma-variables-resolver.js +21 -3
  160. package/lib/module/icons/registry.js +1 -1
  161. package/lib/module/utils/react-utils.js +25 -3
  162. package/lib/typescript/src/components/Avatar/Avatar.d.ts +11 -17
  163. package/lib/typescript/src/components/BottomNavItem/BottomNavItem.d.ts +12 -8
  164. package/lib/typescript/src/components/Button/Button.d.ts +18 -1
  165. package/lib/typescript/src/components/IconButton/IconButton.d.ts +12 -29
  166. package/lib/typescript/src/components/IconCapsule/IconCapsule.d.ts +10 -18
  167. package/lib/typescript/src/components/InputSearch/InputSearch.d.ts +8 -3
  168. package/lib/typescript/src/components/ListItem/ListItem.d.ts +14 -1
  169. package/lib/typescript/src/components/NavArrow/NavArrow.d.ts +12 -11
  170. package/lib/typescript/src/components/Section/Section.d.ts +2 -48
  171. package/lib/typescript/src/components/UpiHandle/UpiHandle.d.ts +13 -12
  172. package/lib/typescript/src/design-tokens/JFSThemeProvider.d.ts +0 -15
  173. package/lib/typescript/src/icons/registry.d.ts +1 -1
  174. package/lib/typescript/src/utils/react-utils.d.ts +15 -0
  175. package/package.json +4 -6
  176. package/src/components/Accordion/Accordion.tsx +2 -2
  177. package/src/components/ActionFooter/ActionFooter.tsx +2 -2
  178. package/src/components/ActionTile/ActionTile.tsx +2 -1
  179. package/src/components/AmountInput/AmountInput.tsx +2 -1
  180. package/src/components/AppBar/AppBar.tsx +2 -2
  181. package/src/components/Avatar/Avatar.tsx +229 -158
  182. package/src/components/AvatarGroup/AvatarGroup.tsx +2 -2
  183. package/src/components/Badge/Badge.tsx +2 -1
  184. package/src/components/Balance/Balance.tsx +2 -1
  185. package/src/components/BottomNav/BottomNav.tsx +2 -1
  186. package/src/components/BottomNavItem/BottomNavItem.tsx +159 -88
  187. package/src/components/Button/Button.tsx +228 -101
  188. package/src/components/ButtonGroup/ButtonGroup.tsx +2 -2
  189. package/src/components/Card/Card.tsx +2 -1
  190. package/src/components/CardCTA/CardCTA.tsx +2 -2
  191. package/src/components/CardProviderInfo/CardProviderInfo.tsx +2 -2
  192. package/src/components/Carousel/Carousel.tsx +3 -2
  193. package/src/components/Checkbox/Checkbox.tsx +2 -1
  194. package/src/components/ChipGroup/ChipGroup.tsx +2 -2
  195. package/src/components/ChipSelect/ChipSelect.tsx +2 -1
  196. package/src/components/DebitCard/DebitCard.tsx +2 -2
  197. package/src/components/Disclaimer/Disclaimer.tsx +2 -1
  198. package/src/components/Divider/Divider.tsx +2 -1
  199. package/src/components/Drawer/Drawer.tsx +2 -1
  200. package/src/components/EmptyState/EmptyState.tsx +2 -1
  201. package/src/components/FilterBar/FilterBar.tsx +2 -2
  202. package/src/components/Form/Form.tsx +2 -1
  203. package/src/components/FormField/FormField.tsx +3 -2
  204. package/src/components/HStack/HStack.tsx +2 -2
  205. package/src/components/HoldingsCard/HoldingsCard.tsx +2 -1
  206. package/src/components/IconButton/IconButton.tsx +154 -126
  207. package/src/components/IconCapsule/IconCapsule.tsx +73 -54
  208. package/src/components/InputSearch/InputSearch.tsx +19 -5
  209. package/src/components/LazyList/LazyList.tsx +2 -2
  210. package/src/components/LinearMeter/LinearMeter.tsx +3 -2
  211. package/src/components/ListGroup/ListGroup.tsx +4 -5
  212. package/src/components/ListItem/ListItem.tsx +257 -187
  213. package/src/components/MediaCard/MediaCard.tsx +2 -1
  214. package/src/components/MerchantProfile/MerchantProfile.tsx +2 -1
  215. package/src/components/MoneyValue/MoneyValue.tsx +2 -1
  216. package/src/components/NavArrow/NavArrow.tsx +91 -58
  217. package/src/components/NoteInput/NoteInput.tsx +2 -1
  218. package/src/components/Nudge/Nudge.tsx +2 -2
  219. package/src/components/Numpad/Numpad.tsx +2 -1
  220. package/src/components/OTP/OTP.tsx +2 -2
  221. package/src/components/PaymentFeedback/PaymentFeedback.tsx +2 -1
  222. package/src/components/Popup/Popup.tsx +2 -1
  223. package/src/components/ProductLabel/ProductLabel.tsx +2 -1
  224. package/src/components/ProgressBadge/ProgressBadge.tsx +2 -2
  225. package/src/components/RadioButton/RadioButton.tsx +2 -1
  226. package/src/components/RechargeCard/RechargeCard.tsx +2 -1
  227. package/src/components/Screen/Screen.tsx +2 -2
  228. package/src/components/Section/Section.tsx +323 -167
  229. package/src/components/SegmentedControl/SegmentedControl.tsx +3 -2
  230. package/src/components/StatItem/StatItem.tsx +2 -1
  231. package/src/components/StatusHero/StatusHero.tsx +2 -1
  232. package/src/components/Stepper/Step.tsx +2 -1
  233. package/src/components/Stepper/StepLabel.tsx +2 -1
  234. package/src/components/Stepper/Stepper.tsx +2 -1
  235. package/src/components/SupportText/SupportText.tsx +2 -1
  236. package/src/components/SupportText/SupportTextIcon.tsx +2 -1
  237. package/src/components/SwappableAmount/SwappableAmount.tsx +2 -1
  238. package/src/components/Tabs/TabItem.tsx +2 -1
  239. package/src/components/Tabs/Tabs.tsx +2 -1
  240. package/src/components/Text/Text.tsx +2 -1
  241. package/src/components/TextInput/TextInput.tsx +3 -3
  242. package/src/components/ThreadHero/ThreadHero.tsx +2 -1
  243. package/src/components/Title/Title.tsx +2 -1
  244. package/src/components/Toast/Toast.tsx +2 -1
  245. package/src/components/Toggle/Toggle.tsx +2 -1
  246. package/src/components/Tooltip/Tooltip.tsx +2 -1
  247. package/src/components/TransactionBubble/TransactionBubble.tsx +2 -2
  248. package/src/components/TransactionDetails/TransactionDetails.tsx +3 -3
  249. package/src/components/TransactionStatus/TransactionStatus.tsx +3 -2
  250. package/src/components/UpiHandle/UpiHandle.tsx +193 -125
  251. package/src/components/VStack/VStack.tsx +2 -2
  252. package/src/design-tokens/JFSThemeProvider.tsx +1 -37
  253. package/src/design-tokens/figma-variables-resolver.ts +21 -3
  254. package/src/icons/registry.ts +1 -1
  255. package/src/utils/react-utils.ts +29 -3
  256. package/lib/typescript/App.d.ts +0 -2
  257. package/lib/typescript/index.d.ts +0 -2
  258. package/lib/typescript/metro.config.d.ts +0 -78
  259. package/lib/typescript/react-native.config.d.ts +0 -4
@@ -1,7 +1,18 @@
1
- import React, { useMemo, useState } from 'react'
2
- import { Pressable, Text, View, type AccessibilityState, type StyleProp, type ViewStyle, type TextStyle } from 'react-native'
1
+ import React, { useCallback, useMemo, useRef, useState } from 'react'
2
+ import {
3
+ Platform,
4
+ Pressable,
5
+ Text,
6
+ View,
7
+ type AccessibilityState,
8
+ type PressableStateCallbackType,
9
+ type StyleProp,
10
+ type ViewStyle,
11
+ type TextStyle,
12
+ } from 'react-native'
3
13
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
14
  import { usePressableWebSupport, type SafePressableProps, type WebAccessibilityProps } from '../../utils/web-platform-utils'
15
+ import { EMPTY_MODES } from '../../utils/react-utils'
5
16
  import Icon from '../../icons/Icon'
6
17
 
7
18
  export type ButtonProps = SafePressableProps & {
@@ -45,6 +56,123 @@ export type ButtonProps = SafePressableProps & {
45
56
  webAccessibilityProps?: WebAccessibilityProps;
46
57
  };
47
58
 
59
+ // ---------------------------------------------------------------------------
60
+ // Module-scope constants — never re-allocated per render.
61
+ // ---------------------------------------------------------------------------
62
+
63
+ const IS_WEB = Platform.OS === 'web'
64
+ const IS_IOS = Platform.OS === 'ios'
65
+
66
+ // iOS Pressable inside a ScrollView fires onPressIn on touchstart by default,
67
+ // which causes a visible "press" flash that snaps back when ScrollView claims
68
+ // the gesture. Delaying press by ~130ms lets the scroll responder cancel the
69
+ // gesture before the pressed visual is ever applied.
70
+ //
71
+ // Android uses the responder system + the touchable cancellation pipeline and
72
+ // does not need an extra delay; setting it to 0 keeps tap latency identical
73
+ // to today's behavior on Android.
74
+ const PRESS_DELAY = IS_IOS ? 130 : 0
75
+
76
+ const containerLayoutStyle = {
77
+ flexDirection: 'row' as const,
78
+ alignItems: 'center' as const,
79
+ justifyContent: 'center' as const,
80
+ }
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Token resolution
84
+ // ---------------------------------------------------------------------------
85
+ //
86
+ // Memoizing all token reads in a single useMemo keyed on (modes, disabled)
87
+ // turns the previous ~21 cache lookups + arithmetic per render into a single
88
+ // resolve per (modes, disabled) tuple. With `EMPTY_MODES` as the default,
89
+ // the most common path (no modes prop) is resolved once for the whole app.
90
+ // ---------------------------------------------------------------------------
91
+
92
+ interface ButtonTokens {
93
+ container: ViewStyle;
94
+ baseLabel: TextStyle;
95
+ hoverContainer: ViewStyle | null;
96
+ pressedContainer: ViewStyle;
97
+ hoverTextColor: string | null;
98
+ pressedTextColor: string;
99
+ baseIconColor: string;
100
+ hoverIconColor: string | null;
101
+ pressedIconColor: string;
102
+ iconSize: number;
103
+ accessoryOffset: number;
104
+ }
105
+
106
+ function resolveButtonTokens(modes: Record<string, any>, disabled: boolean): ButtonTokens {
107
+ const backgroundColor = getVariableByName('button/background', modes) || '#cfa159'
108
+ const borderColor = getVariableByName('button/border/color', modes) || 'rgba(255,255,255,0)'
109
+ const borderWidth = getVariableByName('button/border/size', modes)
110
+ const radius = getVariableByName('button/radius', modes) || 9999
111
+ const paddingHorizontal = getVariableByName('button/padding/horizontal', modes) || 20
112
+ const paddingVertical = getVariableByName('button/padding/vertical', modes) || 12
113
+ const gap = getVariableByName('button/gap', modes) || 8
114
+
115
+ const fontFamily = getVariableByName('button/fontFamily', modes) || 'System'
116
+ const fontWeightValue = getVariableByName('button/fontWeight', modes) || 700
117
+ const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue
118
+ const lineHeight = getVariableByName('button/lineHeight', modes) || 19
119
+ const fontSize = getVariableByName('button/fontSize', modes) || 16
120
+ const textColor = getVariableByName('button/foreground', modes) || '#0f0d0a'
121
+ const iconSize = (getVariableByName('button/icon/size', modes) ?? 18) as number
122
+
123
+ // Pressed tokens are always resolved so the Pressable `style` callback can
124
+ // apply the pressed visual without scheduling a React render.
125
+ const pressedModes = { ...modes, 'Button / State': 'Pressed' }
126
+ const pressedBg = getVariableByName('button/background', pressedModes) || backgroundColor
127
+ const pressedBorderColor = getVariableByName('button/border/color', pressedModes) || borderColor
128
+ const pressedTextColor = (getVariableByName('button/foreground', pressedModes) || textColor) as string
129
+
130
+ // Hover tokens are only meaningful on web. Skipping them on native saves
131
+ // ~3 token lookups + an object spread per render on every Button.
132
+ let hoverContainer: ViewStyle | null = null
133
+ let hoverTextColor: string | null = null
134
+ if (IS_WEB) {
135
+ const hoverModes = { ...modes, 'Button / State': 'Hover' }
136
+ const hoverBg = getVariableByName('button/background', hoverModes) || backgroundColor
137
+ const hoverBorderColor = getVariableByName('button/border/color', hoverModes) || borderColor
138
+ hoverTextColor = (getVariableByName('button/foreground', hoverModes) || textColor) as string
139
+ hoverContainer = { backgroundColor: hoverBg as string, borderColor: hoverBorderColor as string }
140
+ }
141
+
142
+ return {
143
+ container: {
144
+ ...containerLayoutStyle,
145
+ paddingHorizontal: paddingHorizontal as number,
146
+ paddingVertical: paddingVertical as number,
147
+ borderRadius: radius as number,
148
+ borderWidth: (borderWidth ?? 1) as number,
149
+ borderColor: borderColor as string,
150
+ backgroundColor: backgroundColor as string,
151
+ gap: gap as number,
152
+ opacity: disabled ? 0.5 : 1,
153
+ },
154
+ baseLabel: {
155
+ color: textColor as string,
156
+ fontFamily: fontFamily as string,
157
+ fontWeight: fontWeight as TextStyle['fontWeight'],
158
+ fontSize: fontSize as number,
159
+ lineHeight: lineHeight as number,
160
+ },
161
+ hoverContainer,
162
+ pressedContainer: {
163
+ backgroundColor: pressedBg as string,
164
+ borderColor: pressedBorderColor as string,
165
+ },
166
+ hoverTextColor,
167
+ pressedTextColor,
168
+ baseIconColor: textColor as string,
169
+ hoverIconColor: hoverTextColor,
170
+ pressedIconColor: pressedTextColor,
171
+ iconSize,
172
+ accessoryOffset: (gap as number) / 2,
173
+ }
174
+ }
175
+
48
176
  /**
49
177
  * Button component that maps directly to the Figma design using design tokens.
50
178
  *
@@ -63,15 +191,31 @@ export type ButtonProps = SafePressableProps & {
63
191
  * @param {string} [props.accessibilityLabel] - Accessibility label for screen readers. If not provided, uses label or children text
64
192
  * @param {string} [props.accessibilityHint] - Additional accessibility hint for screen readers
65
193
  * @param {Object} [props.accessibilityState] - Additional accessibility state information
194
+ *
195
+ * Performance notes:
196
+ * - The pressed visual (background/border swap) is applied through the
197
+ * Pressable `style` callback, NOT a mirrored React state. This avoids the
198
+ * flicker that scrolling parents previously caused: when a ScrollView
199
+ * cancels a touch the host view simply reverts its style without React
200
+ * rendering. On iOS we additionally use `unstable_pressDelay` so the
201
+ * pressed visual is never even applied during a scroll-cancelled touch.
202
+ * - Hover state is only mirrored into React state on web (where hover events
203
+ * fire). On native the setter call is gated out so the component never
204
+ * re-renders due to hover.
205
+ * - All design-token reads are folded into a single `useMemo` keyed on
206
+ * `(modes, disabled)`.
207
+ * - The component is wrapped in `React.memo`. Callers that want to take
208
+ * advantage of this should pass stable `modes` (the default `EMPTY_MODES`
209
+ * is already stable) and stable callback props.
66
210
  */
67
- function Button({
211
+ function ButtonImpl({
68
212
  label = 'Button',
69
213
  children,
70
214
  renderContent,
71
215
  leading,
72
216
  trailing,
73
217
  icon,
74
- modes = {},
218
+ modes = EMPTY_MODES,
75
219
  onPress,
76
220
  disabled = false,
77
221
  style,
@@ -82,65 +226,57 @@ function Button({
82
226
  webAccessibilityProps,
83
227
  ...rest
84
228
  }: ButtonProps) {
85
- // Resolve design tokens with the provided modes
86
- const backgroundColor = getVariableByName('button/background', modes) || '#cfa159'
87
- const borderColor = getVariableByName('button/border/color', modes) || 'rgba(255,255,255,0)'
88
- const borderWidth = getVariableByName('button/border/size', modes)
89
- const radius = getVariableByName('button/radius', modes) || 9999
90
- const paddingHorizontal = getVariableByName('button/padding/horizontal', modes) || 20
91
- const paddingVertical = getVariableByName('button/padding/vertical', modes) || 12
92
- const gap = getVariableByName('button/gap', modes) || 8
93
-
94
- const fontFamily = getVariableByName('button/fontFamily', modes) || 'System'
95
- const fontWeightValue = getVariableByName('button/fontWeight', modes) || 700
96
- const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue
97
- const lineHeight = getVariableByName('button/lineHeight', modes) || 19
98
- const fontSize = getVariableByName('button/fontSize', modes) || 16
99
- const textColor = getVariableByName('button/foreground', modes) || '#0f0d0a'
100
- const iconColor = textColor
101
- const iconSize = getVariableByName('button/icon/size', modes) ?? 18
102
-
229
+ // Hover state is web-only in practice; the setter is gated so native taps
230
+ // never schedule a re-render through this hook.
103
231
  const [isHovered, setIsHovered] = useState(false)
104
- const [isPressed, setIsPressed] = useState(false)
105
232
 
106
- const hoverModes = { ...modes, "Button / State": "Hover" }
107
- const hoverBg = getVariableByName('button/background', hoverModes) || backgroundColor
108
- const hoverBorderColor = getVariableByName('button/border/color', hoverModes) || borderColor
109
- const hoverTextColor = getVariableByName('button/foreground', hoverModes) || textColor
110
- const hoverIconColor = hoverTextColor
233
+ // Mirror user-supplied handlers in a ref so our wrapper handlers can stay
234
+ // referentially stable (otherwise Pressable would re-bind every render).
235
+ const userHandlersRef = useRef<{
236
+ onPressIn?: (e: any) => void
237
+ onPressOut?: (e: any) => void
238
+ onHoverIn?: (e: any) => void
239
+ onHoverOut?: (e: any) => void
240
+ }>({})
241
+ userHandlersRef.current.onPressIn = (rest as any)?.onPressIn
242
+ userHandlersRef.current.onPressOut = (rest as any)?.onPressOut
243
+ userHandlersRef.current.onHoverIn = (rest as any)?.onHoverIn
244
+ userHandlersRef.current.onHoverOut = (rest as any)?.onHoverOut
111
245
 
112
- const pressedModes = { ...modes, "Button / State": "Pressed" }
113
- const pressedBg = getVariableByName('button/background', pressedModes) || backgroundColor
114
- const pressedBorderColor = getVariableByName('button/border/color', pressedModes) || borderColor
115
- const pressedTextColor = getVariableByName('button/foreground', pressedModes) || textColor
116
- const pressedIconColor = pressedTextColor
117
-
118
- const activeTextColor = isPressed && !disabled ? pressedTextColor : isHovered && !disabled ? hoverTextColor : textColor
119
- const activeIconColor = isPressed && !disabled ? pressedIconColor : isHovered && !disabled ? hoverIconColor : iconColor
246
+ const tokens = useMemo(
247
+ () => resolveButtonTokens(modes, disabled),
248
+ [modes, disabled]
249
+ )
120
250
 
121
- const baseLabelTextStyle: TextStyle = {
122
- color: activeTextColor,
123
- fontFamily,
124
- fontWeight,
125
- fontSize,
126
- lineHeight,
127
- }
251
+ // Active label color: base by default; hover override (web-only) when hovered.
252
+ // Press color is intentionally NOT applied to the label on native — applying
253
+ // it would require a React render per touch and re-introduce the flicker.
254
+ const activeLabelStyle = useMemo<StyleProp<TextStyle>>(() => {
255
+ const hoverOverride: TextStyle | null =
256
+ isHovered && !disabled && tokens.hoverTextColor
257
+ ? { color: tokens.hoverTextColor }
258
+ : null
259
+ return [tokens.baseLabel, hoverOverride, labelStyle]
260
+ }, [tokens.baseLabel, tokens.hoverTextColor, isHovered, disabled, labelStyle])
128
261
 
129
- const mergedLabelTextStyle: StyleProp<TextStyle> = [baseLabelTextStyle, labelStyle]
262
+ const activeIconColor =
263
+ isHovered && !disabled && tokens.hoverIconColor
264
+ ? tokens.hoverIconColor
265
+ : tokens.baseIconColor
130
266
 
131
267
  let content: React.ReactNode
132
268
 
133
269
  if (renderContent) {
134
270
  // Preferred extension point: caller fully controls the inner node but we
135
271
  // still provide the token-derived text styles.
136
- content = renderContent(mergedLabelTextStyle)
272
+ content = renderContent(activeLabelStyle)
137
273
  } else if (children !== undefined && children !== null) {
138
274
  // Full-content override: label and labelStyle are intentionally ignored.
139
275
  content = children
140
276
  } else {
141
277
  content = (
142
278
  <Text
143
- style={mergedLabelTextStyle}
279
+ style={activeLabelStyle}
144
280
  numberOfLines={1}
145
281
  >
146
282
  {label}
@@ -148,30 +284,14 @@ function Button({
148
284
  )
149
285
  }
150
286
 
151
- const containerBaseStyle = {
152
- flexDirection: 'row',
153
- alignItems: 'center',
154
- justifyContent: 'center',
155
- paddingHorizontal,
156
- paddingVertical,
157
- borderRadius: radius,
158
- borderWidth: borderWidth ?? 1,
159
- borderColor,
160
- backgroundColor,
161
- gap,
162
- opacity: disabled ? 0.5 : 1,
163
- }
164
-
165
- const accessoryOffset = gap / 2
166
-
167
- const leadingAccessoryStyle = useMemo(
168
- () => ({ marginRight: accessoryOffset }),
169
- [accessoryOffset],
287
+ const leadingAccessoryStyle = useMemo<ViewStyle>(
288
+ () => ({ marginRight: tokens.accessoryOffset }),
289
+ [tokens.accessoryOffset]
170
290
  )
171
291
 
172
- const trailingAccessoryStyle = useMemo(
173
- () => ({ marginLeft: accessoryOffset }),
174
- [accessoryOffset],
292
+ const trailingAccessoryStyle = useMemo<ViewStyle>(
293
+ () => ({ marginLeft: tokens.accessoryOffset }),
294
+ [tokens.accessoryOffset]
175
295
  )
176
296
 
177
297
  // Use provided accessibilityLabel, or fall back to label text
@@ -186,8 +306,37 @@ function Button({
186
306
  webAccessibilityProps,
187
307
  })
188
308
 
189
- const hoverStyle = { backgroundColor: hoverBg, borderColor: hoverBorderColor }
190
- const pressedStyle = { backgroundColor: pressedBg, borderColor: pressedBorderColor }
309
+ // Stable handler identities. The user's handlers are read through a ref so
310
+ // these wrappers do not need to be re-created when the user passes inline
311
+ // closures, which would defeat React.memo on Pressable.
312
+ const handlePressIn = useCallback((e: any) => {
313
+ userHandlersRef.current.onPressIn?.(e)
314
+ }, [])
315
+ const handlePressOut = useCallback((e: any) => {
316
+ userHandlersRef.current.onPressOut?.(e)
317
+ }, [])
318
+ const handleHoverIn = useCallback((e: any) => {
319
+ if (IS_WEB) setIsHovered(true)
320
+ userHandlersRef.current.onHoverIn?.(e)
321
+ }, [])
322
+ const handleHoverOut = useCallback((e: any) => {
323
+ if (IS_WEB) setIsHovered(false)
324
+ userHandlersRef.current.onHoverOut?.(e)
325
+ }, [])
326
+
327
+ // The Pressable style callback receives `pressed` from RN's gesture
328
+ // pipeline and applies the pressed visual to the host view directly,
329
+ // without going through React. This is the key to flicker-free behavior:
330
+ // a scroll-cancelled touch reverts the style on the native side only.
331
+ const styleCallback = useCallback(
332
+ ({ pressed }: PressableStateCallbackType): StyleProp<ViewStyle> => [
333
+ tokens.container,
334
+ isHovered && !disabled ? tokens.hoverContainer : null,
335
+ pressed && !disabled ? tokens.pressedContainer : null,
336
+ style,
337
+ ],
338
+ [tokens.container, tokens.hoverContainer, tokens.pressedContainer, isHovered, disabled, style]
339
+ )
191
340
 
192
341
  if (__DEV__) {
193
342
  if (children && labelStyle) {
@@ -216,28 +365,12 @@ function Button({
216
365
  }}
217
366
  disabled={disabled}
218
367
  {...(onPress !== undefined ? { onPress } : {})}
219
- onPressIn={(e: any) => {
220
- setIsPressed(true)
221
- ;(rest as any)?.onPressIn?.(e)
222
- }}
223
- onPressOut={(e: any) => {
224
- setIsPressed(false)
225
- ;(rest as any)?.onPressOut?.(e)
226
- }}
227
- onHoverIn={(e: any) => {
228
- setIsHovered(true)
229
- ; (rest as any)?.onHoverIn?.(e)
230
- }}
231
- onHoverOut={(e: any) => {
232
- setIsHovered(false)
233
- ; (rest as any)?.onHoverOut?.(e)
234
- }}
235
- style={({ pressed }) => [
236
- containerBaseStyle,
237
- isHovered && !disabled ? hoverStyle : null,
238
- (pressed || isPressed) && !disabled ? pressedStyle : null,
239
- style,
240
- ] as StyleProp<ViewStyle>}
368
+ onPressIn={handlePressIn}
369
+ onPressOut={handlePressOut}
370
+ onHoverIn={handleHoverIn}
371
+ onHoverOut={handleHoverOut}
372
+ unstable_pressDelay={PRESS_DELAY}
373
+ style={styleCallback}
241
374
  {...webProps}
242
375
  >
243
376
  {leading ? (
@@ -248,7 +381,7 @@ function Button({
248
381
  {content}
249
382
  {icon ? (
250
383
  <View style={trailingAccessoryStyle}>
251
- <Icon name={icon} size={iconSize as number} color={activeIconColor as string} accessibilityElementsHidden={true} importantForAccessibility="no" />
384
+ <Icon name={icon} size={tokens.iconSize} color={activeIconColor} accessibilityElementsHidden={true} importantForAccessibility="no" />
252
385
  </View>
253
386
  ) : trailing ? (
254
387
  <View style={trailingAccessoryStyle}>
@@ -259,12 +392,6 @@ function Button({
259
392
  )
260
393
  }
261
394
 
262
- export default Button
263
-
264
-
265
-
266
-
267
-
268
-
269
-
395
+ const Button = React.memo(ButtonImpl)
270
396
 
397
+ export default Button
@@ -5,7 +5,7 @@ import {
5
5
  type ViewStyle,
6
6
  } from 'react-native'
7
7
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
8
- import { flattenChildren } from '../../utils/react-utils'
8
+ import { EMPTY_MODES, flattenChildren } from '../../utils/react-utils'
9
9
  import IconButton from '../IconButton/IconButton'
10
10
 
11
11
  export type ButtonGroupProps = {
@@ -44,7 +44,7 @@ export type ButtonGroupProps = {
44
44
  */
45
45
  function ButtonGroup({
46
46
  children,
47
- modes = {},
47
+ modes = EMPTY_MODES,
48
48
  style,
49
49
  }: ButtonGroupProps) {
50
50
  // Resolve design tokens
@@ -1,6 +1,7 @@
1
1
  import React, { createContext, useContext, isValidElement, cloneElement, useMemo } from 'react';
2
2
  import { View, Text, StyleSheet, type ViewStyle, type TextStyle, type StyleProp } from 'react-native';
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
4
+ import { EMPTY_MODES } from '../../utils/react-utils';
4
5
 
5
6
  /**
6
7
  * Context to share 'modes' with child components like Card.Title and Card.SupportText.
@@ -50,7 +51,7 @@ export interface CardProps {
50
51
  export function Card({
51
52
  media,
52
53
  children,
53
- modes = {},
54
+ modes = EMPTY_MODES,
54
55
  mediaAspectRatio = 154 / 116,
55
56
  style,
56
57
  }: CardProps) {
@@ -2,7 +2,7 @@ import React from 'react'
2
2
  import { View, Text, type ViewStyle, type TextStyle, type StyleProp } from 'react-native'
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
4
  import { useTokens } from '../../design-tokens/JFSThemeProvider'
5
- import { cloneChildrenWithModes } from '../../utils/react-utils'
5
+ import { cloneChildrenWithModes, EMPTY_MODES } from '../../utils/react-utils'
6
6
  import IconCapsule from '../IconCapsule/IconCapsule'
7
7
  import Button from '../Button/Button'
8
8
 
@@ -33,7 +33,7 @@ function CardCTA({
33
33
  iconName = 'ic_upi_number',
34
34
  buttonLabel = 'Button',
35
35
  onPressButton,
36
- modes: propModes = {},
36
+ modes: propModes = EMPTY_MODES,
37
37
  iconSlot,
38
38
  buttonSlot,
39
39
  style,
@@ -2,7 +2,7 @@ import React from 'react'
2
2
  import { View, type ViewStyle, type StyleProp, type ImageSourcePropType } from 'react-native'
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
4
  import ProductLabel from '../ProductLabel/ProductLabel'
5
- import { cloneChildrenWithModes } from '../../utils/react-utils'
5
+ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils'
6
6
 
7
7
  export type CardProviderInfoProps = {
8
8
  /** Product name shown in the ProductLabel header. */
@@ -28,7 +28,7 @@ function CardProviderInfo({
28
28
  label = 'Gold',
29
29
  imageSource,
30
30
  children,
31
- modes = {},
31
+ modes = EMPTY_MODES,
32
32
  style,
33
33
  }: CardProviderInfoProps) {
34
34
  const background = getVariableByName('card/providerInfo/background', modes) ?? '#fef4e5'
@@ -19,6 +19,7 @@ import {
19
19
  type LayoutChangeEvent,
20
20
  } from 'react-native'
21
21
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
22
+ import { EMPTY_MODES } from '../../utils/react-utils'
22
23
 
23
24
  // ---------------------------------------------------------------------------
24
25
  // Context
@@ -34,7 +35,7 @@ interface CarouselContextValue {
34
35
  }
35
36
 
36
37
  const CarouselContext = createContext<CarouselContextValue>({
37
- modes: {},
38
+ modes: EMPTY_MODES,
38
39
  activeIndex: 0,
39
40
  totalItems: 0,
40
41
  goTo: () => { },
@@ -76,7 +77,7 @@ export interface CarouselProps {
76
77
 
77
78
  export function Carousel({
78
79
  children,
79
- modes = {},
80
+ modes = EMPTY_MODES,
80
81
  autoPlay = false,
81
82
  autoPlayInterval = 4000,
82
83
  showPagination = true,
@@ -7,6 +7,7 @@ import {
7
7
  } from 'react-native'
8
8
  import Svg, { Path } from 'react-native-svg'
9
9
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
10
+ import { EMPTY_MODES } from '../../utils/react-utils'
10
11
 
11
12
  /**
12
13
  * Tracks whether the last user interaction was a keyboard event (Tab).
@@ -81,7 +82,7 @@ function Checkbox({
81
82
  defaultChecked = false,
82
83
  onValueChange,
83
84
  disabled = false,
84
- modes = {},
85
+ modes = EMPTY_MODES,
85
86
  style,
86
87
  accessibilityLabel,
87
88
  }: CheckboxProps) {
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { View, type StyleProp, type ViewStyle } from 'react-native';
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
4
- import { flattenChildren } from '../../utils/react-utils';
4
+ import { EMPTY_MODES, flattenChildren } from '../../utils/react-utils';
5
5
 
6
6
  export type ChipGroupProps = {
7
7
  /**
@@ -30,7 +30,7 @@ export type ChipGroupProps = {
30
30
  */
31
31
  function ChipGroup({
32
32
  children,
33
- modes = {},
33
+ modes = EMPTY_MODES,
34
34
  style,
35
35
  testID,
36
36
  }: ChipGroupProps) {
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { View, Text, TouchableOpacity, type StyleProp, type ViewStyle, type TextStyle } from 'react-native';
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
4
+ import { EMPTY_MODES } from '../../utils/react-utils';
4
5
  import Icon from '../../icons/Icon';
5
6
 
6
7
  export type ChipSelectProps = {
@@ -45,7 +46,7 @@ function ChipSelect({
45
46
  label = 'Date',
46
47
  active = false,
47
48
  icon = 'ic_calendar_week',
48
- modes = {},
49
+ modes = EMPTY_MODES,
49
50
  style,
50
51
  labelSlot,
51
52
  onPress,
@@ -2,7 +2,7 @@ import React from 'react'
2
2
  import { View, Text, Image, type ViewStyle, type TextStyle, type ImageStyle, type StyleProp, type ImageSourcePropType } from 'react-native'
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
4
  import { useTokens } from '../../design-tokens/JFSThemeProvider'
5
- import { cloneChildrenWithModes } from '../../utils/react-utils'
5
+ import { cloneChildrenWithModes, EMPTY_MODES } from '../../utils/react-utils'
6
6
 
7
7
  const defaultCardArt = require('./cf64a67d5075caa8924d20b4e4e650d47b3ee08b.png')
8
8
 
@@ -38,7 +38,7 @@ function DebitCard({
38
38
  cardArtSource = defaultCardArt,
39
39
  bankLogoSlot,
40
40
  providerLogoSlot,
41
- modes: propModes = {},
41
+ modes: propModes = EMPTY_MODES,
42
42
  style,
43
43
  }: DebitCardProps) {
44
44
  const { modes: globalModes } = useTokens()
@@ -2,6 +2,7 @@ import React from 'react'
2
2
  import { View, Text, type StyleProp, type ViewStyle, type TextStyle } from 'react-native'
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
4
  import { useTokens } from '../../design-tokens/JFSThemeProvider'
5
+ import { EMPTY_MODES } from '../../utils/react-utils'
5
6
 
6
7
  type DisclaimerProps = {
7
8
  disclaimer?: string;
@@ -35,7 +36,7 @@ type DisclaimerProps = {
35
36
  */
36
37
  function Disclaimer({
37
38
  disclaimer = 'All financial services are provided by Jio Payments Bank Pvt. Ltd.',
38
- modes: propModes = {},
39
+ modes: propModes = EMPTY_MODES,
39
40
  style,
40
41
  textStyle,
41
42
  accessibilityLabel,
@@ -5,6 +5,7 @@ import {
5
5
  type StyleProp,
6
6
  } from 'react-native'
7
7
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
8
+ import { EMPTY_MODES } from '../../utils/react-utils'
8
9
 
9
10
  export type DividerDirection = 'horizontal' | 'vertical'
10
11
 
@@ -56,7 +57,7 @@ export type DividerProps = {
56
57
  */
57
58
  function Divider({
58
59
  direction = 'horizontal',
59
- modes = {},
60
+ modes = EMPTY_MODES,
60
61
  style,
61
62
  accessibilityLabel = undefined,
62
63
  }: DividerProps) {
@@ -15,6 +15,7 @@ import Animated, {
15
15
  withSpring,
16
16
  } from 'react-native-reanimated'
17
17
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
18
+ import { EMPTY_MODES } from '../../utils/react-utils'
18
19
 
19
20
 
20
21
  const AnimatedScrollView = Animated.createAnimatedComponent(ScrollView)
@@ -86,7 +87,7 @@ type DrawerProps = {
86
87
  */
87
88
  function Drawer({
88
89
 
89
- modes = {},
90
+ modes = EMPTY_MODES,
90
91
  style,
91
92
  title,
92
93
  header,
@@ -4,6 +4,7 @@ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
4
  import { useTokens } from '../../design-tokens/JFSThemeProvider'
5
5
  import IconCapsule from '../IconCapsule/IconCapsule'
6
6
  import Button from '../Button/Button'
7
+ import { EMPTY_MODES } from '../../utils/react-utils'
7
8
 
8
9
  export type EmptyStateProps = {
9
10
  /**
@@ -50,7 +51,7 @@ function EmptyState({
50
51
  showDescription = true,
51
52
  iconSlot,
52
53
  buttonSlot,
53
- modes: propModes = {},
54
+ modes: propModes = EMPTY_MODES,
54
55
  style,
55
56
  testID,
56
57
  }: EmptyStateProps) {