jfs-components 0.0.43 → 0.0.45

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 (106) hide show
  1. package/lib/commonjs/components/AmountInput/AmountInput.js +82 -0
  2. package/lib/commonjs/components/AmountInput/index.js +13 -0
  3. package/lib/commonjs/components/Button/Button.js +45 -28
  4. package/lib/commonjs/components/CardProviderInfo/CardProviderInfo.js +76 -0
  5. package/lib/commonjs/components/Checkbox/Checkbox.js +208 -0
  6. package/lib/commonjs/components/EmptyState/EmptyState.js +2 -1
  7. package/lib/commonjs/components/MoneyValue/MoneyValue.js +81 -49
  8. package/lib/commonjs/components/NoteInput/NoteInput.js +120 -0
  9. package/lib/commonjs/components/NoteInput/index.js +13 -0
  10. package/lib/commonjs/components/Numpad/Numpad.js +108 -0
  11. package/lib/commonjs/components/OTP/OTP.js +242 -0
  12. package/lib/commonjs/components/PortfolioHero/PortfolioHero.js +78 -0
  13. package/lib/commonjs/components/ProductLabel/ProductLabel.js +50 -0
  14. package/lib/commonjs/components/ProgressBadge/ProgressBadge.js +130 -0
  15. package/lib/commonjs/components/ProgressBadge/index.js +25 -0
  16. package/lib/commonjs/components/StatItem/StatItem.js +61 -0
  17. package/lib/commonjs/components/StatusHero/StatusHero.js +148 -0
  18. package/lib/commonjs/components/SwappableAmount/SwappableAmount.js +71 -0
  19. package/lib/commonjs/components/Tabs/TabItem.js +79 -0
  20. package/lib/commonjs/components/Tabs/Tabs.js +88 -0
  21. package/lib/commonjs/components/Text/Text.js +38 -0
  22. package/lib/commonjs/components/Toggle/Toggle.js +102 -0
  23. package/lib/commonjs/components/index.js +105 -0
  24. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -0
  25. package/lib/commonjs/design-tokens/figma-variables-resolver.js +1 -1
  26. package/lib/commonjs/icons/registry.js +1 -1
  27. package/lib/module/components/AmountInput/AmountInput.js +77 -0
  28. package/lib/module/components/AmountInput/index.js +3 -0
  29. package/lib/module/components/Button/Button.js +44 -28
  30. package/lib/module/components/CardProviderInfo/CardProviderInfo.js +71 -0
  31. package/lib/module/components/Checkbox/Checkbox.js +205 -0
  32. package/lib/module/components/EmptyState/EmptyState.js +2 -1
  33. package/lib/module/components/MoneyValue/MoneyValue.js +81 -49
  34. package/lib/module/components/NoteInput/NoteInput.js +115 -0
  35. package/lib/module/components/NoteInput/index.js +3 -0
  36. package/lib/module/components/Numpad/Numpad.js +103 -0
  37. package/lib/module/components/OTP/OTP.js +236 -0
  38. package/lib/module/components/PortfolioHero/PortfolioHero.js +73 -0
  39. package/lib/module/components/ProductLabel/ProductLabel.js +45 -0
  40. package/lib/module/components/ProgressBadge/ProgressBadge.js +125 -0
  41. package/lib/module/components/ProgressBadge/index.js +4 -0
  42. package/lib/module/components/StatItem/StatItem.js +56 -0
  43. package/lib/module/components/StatusHero/StatusHero.js +142 -0
  44. package/lib/module/components/SwappableAmount/SwappableAmount.js +66 -0
  45. package/lib/module/components/Tabs/TabItem.js +74 -0
  46. package/lib/module/components/Tabs/Tabs.js +78 -0
  47. package/lib/module/components/Text/Text.js +33 -0
  48. package/lib/module/components/Toggle/Toggle.js +97 -0
  49. package/lib/module/components/index.js +16 -1
  50. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -0
  51. package/lib/module/design-tokens/figma-variables-resolver.js +1 -1
  52. package/lib/module/icons/registry.js +1 -1
  53. package/lib/typescript/src/components/AmountInput/AmountInput.d.ts +23 -0
  54. package/lib/typescript/src/components/AmountInput/index.d.ts +3 -0
  55. package/lib/typescript/src/components/Button/Button.d.ts +6 -1
  56. package/lib/typescript/src/components/CardProviderInfo/CardProviderInfo.d.ts +24 -0
  57. package/lib/typescript/src/components/Checkbox/Checkbox.d.ts +30 -0
  58. package/lib/typescript/src/components/EmptyState/EmptyState.d.ts +6 -1
  59. package/lib/typescript/src/components/MoneyValue/MoneyValue.d.ts +18 -26
  60. package/lib/typescript/src/components/NoteInput/NoteInput.d.ts +23 -0
  61. package/lib/typescript/src/components/NoteInput/index.d.ts +3 -0
  62. package/lib/typescript/src/components/Numpad/Numpad.d.ts +35 -0
  63. package/lib/typescript/src/components/OTP/OTP.d.ts +36 -0
  64. package/lib/typescript/src/components/PortfolioHero/PortfolioHero.d.ts +21 -0
  65. package/lib/typescript/src/components/ProductLabel/ProductLabel.d.ts +14 -0
  66. package/lib/typescript/src/components/ProgressBadge/ProgressBadge.d.ts +36 -0
  67. package/lib/typescript/src/components/ProgressBadge/index.d.ts +3 -0
  68. package/lib/typescript/src/components/StatItem/StatItem.d.ts +21 -0
  69. package/lib/typescript/src/components/StatusHero/StatusHero.d.ts +47 -0
  70. package/lib/typescript/src/components/SwappableAmount/SwappableAmount.d.ts +22 -0
  71. package/lib/typescript/src/components/Tabs/TabItem.d.ts +29 -0
  72. package/lib/typescript/src/components/Tabs/Tabs.d.ts +44 -0
  73. package/lib/typescript/src/components/Text/Text.d.ts +14 -0
  74. package/lib/typescript/src/components/Toggle/Toggle.d.ts +29 -0
  75. package/lib/typescript/src/components/index.d.ts +15 -0
  76. package/lib/typescript/src/icons/registry.d.ts +1 -1
  77. package/package.json +1 -1
  78. package/src/components/AmountInput/AmountInput.tsx +81 -0
  79. package/src/components/AmountInput/index.ts +2 -0
  80. package/src/components/Button/Button.tsx +40 -20
  81. package/src/components/CardProviderInfo/CardProviderInfo.tsx +81 -0
  82. package/src/components/Checkbox/Checkbox.tsx +238 -0
  83. package/src/components/EmptyState/EmptyState.tsx +7 -1
  84. package/src/components/MoneyValue/MoneyValue.tsx +134 -79
  85. package/src/components/NoteInput/NoteInput.tsx +146 -0
  86. package/src/components/NoteInput/index.ts +2 -0
  87. package/src/components/Numpad/Numpad.tsx +162 -0
  88. package/src/components/OTP/OTP.tsx +275 -0
  89. package/src/components/PortfolioHero/PortfolioHero.tsx +91 -0
  90. package/src/components/ProductLabel/ProductLabel.tsx +58 -0
  91. package/src/components/ProgressBadge/ProgressBadge.tsx +172 -0
  92. package/src/components/ProgressBadge/index.ts +2 -0
  93. package/src/components/StatItem/StatItem.tsx +71 -0
  94. package/src/components/StatusHero/StatusHero.tsx +156 -0
  95. package/src/components/SwappableAmount/SwappableAmount.tsx +92 -0
  96. package/src/components/Tabs/TabItem.tsx +96 -0
  97. package/src/components/Tabs/Tabs.tsx +105 -0
  98. package/src/components/Text/Text.tsx +48 -0
  99. package/src/components/Toggle/Toggle.tsx +122 -0
  100. package/src/components/index.ts +15 -0
  101. package/src/design-tokens/Coin Variables-variables-full.json +1 -0
  102. package/src/design-tokens/figma-variables-resolver.ts +1 -1
  103. package/src/icons/registry.ts +1 -1
  104. package/lib/commonjs/design-tokens/JFS Variables-variables-full.json +0 -1
  105. package/lib/module/design-tokens/JFS Variables-variables-full.json +0 -1
  106. package/src/design-tokens/JFS Variables-variables-full.json +0 -1
@@ -0,0 +1,156 @@
1
+ import React, { isValidElement, cloneElement, type ReactNode } from 'react'
2
+ import { View, Text, type ViewStyle, type TextStyle } from 'react-native'
3
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
+ import { useTokens } from '../../design-tokens/JFSThemeProvider'
5
+ import IconCapsule from '../IconCapsule/IconCapsule'
6
+ import MoneyValue from '../MoneyValue/MoneyValue'
7
+
8
+ export type StatusHeroProps = {
9
+ /**
10
+ * Custom media slot content.
11
+ * Defaults to an IconCapsule above a MoneyValue.
12
+ * Modes are automatically injected if the slot is a valid React element.
13
+ */
14
+ renderMedia?: ReactNode
15
+ /** Title text displayed below the media slot */
16
+ title?: string
17
+ /** Whether to render the title */
18
+ showTitle?: boolean
19
+ /** Body/subtitle text displayed below the title */
20
+ subtitle?: string
21
+ /** Icon name used in the default media slot */
22
+ iconName?: string
23
+ /** Monetary value shown in the default MoneyValue inside the media slot */
24
+ value?: string
25
+ /** Currency symbol or ISO code for the default MoneyValue */
26
+ currency?: string
27
+ /** Mode configuration for design tokens */
28
+ modes?: Record<string, any>
29
+ style?: ViewStyle
30
+ }
31
+
32
+ /**
33
+ * StatusHero component that displays a hero section for payment/transaction status screens.
34
+ *
35
+ * Contains a media slot (defaults to IconCapsule + MoneyValue) and a content area
36
+ * with an optional title and a subtitle. All visual values are resolved from Figma
37
+ * design tokens via `getVariableByName`.
38
+ *
39
+ * @component
40
+ * @example
41
+ * ```tsx
42
+ * <StatusHero
43
+ * iconName="ic_confirm"
44
+ * value="50"
45
+ * currency="INR"
46
+ * title="You're set to make payments"
47
+ * subtitle="₹50 will be auto-invested daily, stay consistent & stay golden."
48
+ * modes={{ 'Color Mode': 'Light' }}
49
+ * />
50
+ * ```
51
+ */
52
+ export default function StatusHero({
53
+ renderMedia,
54
+ title = "You're set to make payments",
55
+ showTitle = true,
56
+ subtitle = '₹50 will be auto-invested daily,\nstay consistent & stay golden.',
57
+ iconName = 'ic_confirm',
58
+ value = '50',
59
+ currency = 'INR',
60
+ modes: propModes = {},
61
+ style,
62
+ }: StatusHeroProps) {
63
+ const { modes: globalModes } = useTokens()
64
+ const modes = { ...globalModes, ...propModes }
65
+
66
+ // Container
67
+ const gap = Number(getVariableByName('statusHero/gap', modes)) || 12
68
+ const padding = Number(getVariableByName('statusHero/padding', modes)) || 8
69
+
70
+ // Media slot wrap (gap between icon and money value in default slot)
71
+ const slotWrapGap = Number(getVariableByName('statusHero/slotWrap/gap', modes)) || 46
72
+
73
+ // Content wrap
74
+ const contentWrapGap = Number(getVariableByName('statusHero/contentWrap/gap', modes)) || 12
75
+
76
+ // Title
77
+ const titleColor = getVariableByName('statusHero/title/foreground', modes) || '#0c0d10'
78
+ const titleFontSize = Number(getVariableByName('statusHero/title/fontSize', modes)) || 20
79
+ const titleFontFamily = getVariableByName('statusHero/title/fontFamily', modes) || 'System'
80
+ const titleLineHeight = Number(getVariableByName('statusHero/title/lineHeight', modes)) || 22
81
+ const titleFontWeight = getVariableByName('statusHero/title/fontWeight', modes) || '700'
82
+
83
+ // Body
84
+ const bodyColor = getVariableByName('statusHero/body/foreground', modes) || '#24262b'
85
+ const bodyFontSize = Number(getVariableByName('statusHero/body/fontSize', modes)) || 14
86
+ const bodyFontFamily = getVariableByName('statusHero/body/fontFamily', modes) || 'System'
87
+ const bodyLineHeight = Number(getVariableByName('statusHero/body/lineHeight', modes)) || 17
88
+ const bodyFontWeight = getVariableByName('statusHero/body/fontWeight', modes) || '500'
89
+
90
+ const containerStyle: ViewStyle = {
91
+ flexDirection: 'column',
92
+ alignItems: 'center',
93
+ justifyContent: 'center',
94
+ gap,
95
+ padding,
96
+ }
97
+
98
+ const defaultMediaWrapStyle: ViewStyle = {
99
+ flexDirection: 'column',
100
+ alignItems: 'center',
101
+ gap: slotWrapGap,
102
+ }
103
+
104
+ const contentWrapStyle: ViewStyle = {
105
+ flexDirection: 'column',
106
+ alignItems: 'center',
107
+ gap: contentWrapGap,
108
+ }
109
+
110
+ const titleStyle: TextStyle = {
111
+ color: titleColor,
112
+ fontSize: titleFontSize,
113
+ fontFamily: titleFontFamily,
114
+ lineHeight: titleLineHeight,
115
+ fontWeight: String(titleFontWeight) as TextStyle['fontWeight'],
116
+ textAlign: 'center',
117
+ }
118
+
119
+ const bodyStyle: TextStyle = {
120
+ color: bodyColor,
121
+ fontSize: bodyFontSize,
122
+ fontFamily: bodyFontFamily,
123
+ lineHeight: bodyLineHeight,
124
+ fontWeight: String(bodyFontWeight) as TextStyle['fontWeight'],
125
+ textAlign: 'center',
126
+ }
127
+
128
+ // Inject modes into the provided slot if it's a valid React element
129
+ const mediaContent = isValidElement(renderMedia)
130
+ ? cloneElement(renderMedia as React.ReactElement<any>, { modes })
131
+ : renderMedia
132
+
133
+ const defaultMediaContent = (
134
+ <View style={defaultMediaWrapStyle}>
135
+ <IconCapsule
136
+ iconName={iconName}
137
+ modes={{ ...modes, 'Icon Capsule Size': 'L', Emphasis: 'High', AppearanceBrand: 'Primary' }}
138
+ />
139
+ <MoneyValue
140
+ value={value}
141
+ currency={currency}
142
+ modes={{ ...modes, Context3: 'Balance' }}
143
+ />
144
+ </View>
145
+ )
146
+
147
+ return (
148
+ <View style={[containerStyle, style]}>
149
+ {mediaContent ?? defaultMediaContent}
150
+ <View style={contentWrapStyle}>
151
+ {showTitle && title ? <Text style={titleStyle}>{title}</Text> : null}
152
+ {subtitle ? <Text style={bodyStyle}>{subtitle}</Text> : null}
153
+ </View>
154
+ </View>
155
+ )
156
+ }
@@ -0,0 +1,92 @@
1
+ import React from 'react'
2
+ import { View, Text, type ViewStyle, type TextStyle } from 'react-native'
3
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
+ import Button from '../Button/Button'
5
+
6
+ export type SwappableAmountProps = {
7
+ /** Large display value (e.g. "49g"). */
8
+ value?: string
9
+ /** Whether to show the schedule button above the value. */
10
+ schedule?: boolean
11
+ /** Label text for the schedule button (e.g. "Weekly on Mondays"). */
12
+ scheduleLabel?: string
13
+ /** Label text for the amount/swap button (e.g. "₹5100"). */
14
+ amountLabel?: string
15
+ /** Callback when the schedule button is pressed. */
16
+ onSchedulePress?: () => void
17
+ /** Callback when the amount/swap button is pressed. */
18
+ onAmountPress?: () => void
19
+ /** Modes configuration for design token resolution. */
20
+ modes?: Record<string, any>
21
+ /** Container style override. */
22
+ style?: ViewStyle
23
+ }
24
+
25
+ function SwappableAmount({
26
+ value = '49g',
27
+ schedule = false,
28
+ scheduleLabel = 'Weekly on Mondays',
29
+ amountLabel = '₹5100',
30
+ onSchedulePress,
31
+ onAmountPress,
32
+ modes = {},
33
+ style,
34
+ }: SwappableAmountProps) {
35
+ const gap = getVariableByName('swappableAmount/gap', modes) ?? 24
36
+ const padding = getVariableByName('swappableAmount/padding', modes) ?? 8
37
+
38
+ const foreground = getVariableByName('swappableAmount/foreground', modes) ?? '#0d0d0f'
39
+ const fontSize = getVariableByName('swappableAmount/fontSize', modes) ?? 56
40
+ const fontFamily = getVariableByName('swappableAmount/fontFamily', modes) ?? 'System'
41
+ const fontWeight = getVariableByName('swappableAmount/fontWeight', modes) ?? 900
42
+ const lineHeight = getVariableByName('swappableAmount/lineHeight', modes) ?? 56
43
+
44
+ const containerStyle: ViewStyle = {
45
+ flexDirection: 'column',
46
+ alignItems: 'center',
47
+ justifyContent: 'center',
48
+ gap: gap as number,
49
+ padding: padding as number,
50
+ }
51
+
52
+ const valueTextStyle: TextStyle = {
53
+ color: foreground as string,
54
+ fontSize: fontSize as number,
55
+ fontFamily: fontFamily as string,
56
+ fontWeight: String(fontWeight) as TextStyle['fontWeight'],
57
+ lineHeight: lineHeight as number,
58
+ }
59
+
60
+ return (
61
+ <View style={[containerStyle, style]}>
62
+ {schedule && (
63
+ <Button
64
+ label={scheduleLabel}
65
+ icon="ic_calendar_week"
66
+ onPress={onSchedulePress}
67
+ modes={{
68
+ ...modes,
69
+ AppearanceBrand: 'Neutral',
70
+ Emphasis: 'Low',
71
+ 'Button / Size': 'S',
72
+ }}
73
+ />
74
+ )}
75
+
76
+ <Text style={valueTextStyle}>{value}</Text>
77
+
78
+ <Button
79
+ label={amountLabel}
80
+ icon="ic_data_in_out"
81
+ onPress={onAmountPress}
82
+ modes={{
83
+ ...modes,
84
+ Emphasis: 'Medium',
85
+ AppearanceBrand: 'Secondary',
86
+ }}
87
+ />
88
+ </View>
89
+ )
90
+ }
91
+
92
+ export default SwappableAmount
@@ -0,0 +1,96 @@
1
+ import React from 'react'
2
+ import {
3
+ Pressable,
4
+ Text,
5
+ View,
6
+ type StyleProp,
7
+ type ViewStyle,
8
+ type TextStyle,
9
+ } from 'react-native'
10
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
11
+
12
+ export interface TabItemProps {
13
+ /** Label text to display */
14
+ label?: string
15
+ /** Whether this tab item is currently active/selected */
16
+ active?: boolean
17
+ /** Callback when this tab item is pressed */
18
+ onPress?: () => void
19
+ /** Design token modes for theming */
20
+ modes?: Record<string, any>
21
+ /** Override container styles */
22
+ style?: StyleProp<ViewStyle>
23
+ /** Override label text styles */
24
+ labelStyle?: StyleProp<TextStyle>
25
+ /** Accessibility label for screen readers */
26
+ accessibilityLabel?: string
27
+ }
28
+
29
+ /**
30
+ * Individual tab item used inside the Tabs container.
31
+ *
32
+ * Supports idle and active states driven by design tokens.
33
+ * When active, a bottom indicator bar is shown beneath the label.
34
+ *
35
+ * @component
36
+ * @param {TabItemProps} props
37
+ */
38
+ function TabItem({
39
+ label = 'Tab item',
40
+ active = false,
41
+ onPress,
42
+ modes = {},
43
+ style,
44
+ labelStyle,
45
+ accessibilityLabel,
46
+ }: TabItemProps) {
47
+ const paddingVertical = (getVariableByName('tabItem/padding/vertical', modes) ?? 8) as number
48
+ const fontFamily = (getVariableByName('tabItem/label/fontFamily', modes) ?? 'JioType Var') as string
49
+ const fontSize = (getVariableByName('tabItem/label/size', modes) ?? 14) as number
50
+ const lineHeight = (getVariableByName('tabItem/label/lineHeight', modes) ?? 17) as number
51
+
52
+ const idleLabelColor = (getVariableByName('tabItem/idle/label/color', modes) ?? '#303338') as string
53
+ const activeLabelColor = (getVariableByName('tabItem/active/label/color', modes) ?? '#cea15a') as string
54
+
55
+ const labelColor = active ? activeLabelColor : idleLabelColor
56
+
57
+ const containerStyle: ViewStyle = {
58
+ flexDirection: 'column',
59
+ alignItems: 'center',
60
+ justifyContent: 'center',
61
+ paddingVertical,
62
+ }
63
+
64
+ const textStyle: TextStyle = {
65
+ color: labelColor,
66
+ fontFamily,
67
+ fontSize,
68
+ lineHeight,
69
+ fontWeight: '500',
70
+ }
71
+
72
+ const indicatorStyle: ViewStyle = {
73
+ position: 'absolute',
74
+ bottom: 0,
75
+ left: 0,
76
+ right: 0,
77
+ height: 2,
78
+ backgroundColor: activeLabelColor,
79
+ borderRadius: 1,
80
+ }
81
+
82
+ return (
83
+ <Pressable
84
+ style={({ pressed }) => [containerStyle, pressed && { opacity: 0.7 }, style]}
85
+ onPress={onPress}
86
+ accessibilityRole="tab"
87
+ accessibilityState={{ selected: active }}
88
+ accessibilityLabel={accessibilityLabel ?? label}
89
+ >
90
+ <Text style={[textStyle, labelStyle]}>{label}</Text>
91
+ {active && <View style={indicatorStyle} />}
92
+ </Pressable>
93
+ )
94
+ }
95
+
96
+ export default TabItem
@@ -0,0 +1,105 @@
1
+ import React from 'react'
2
+ import {
3
+ ScrollView,
4
+ View,
5
+ type StyleProp,
6
+ type ViewStyle,
7
+ } from 'react-native'
8
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
9
+ import TabItem from './TabItem'
10
+
11
+ export interface TabsProps {
12
+ /**
13
+ * Tab item children. Each child should be a <TabItem> component.
14
+ * Modes are automatically forwarded to all TabItem children.
15
+ */
16
+ children: React.ReactNode
17
+ /** Design token modes for theming */
18
+ modes?: Record<string, any>
19
+ /**
20
+ * When true, the tabs row scrolls horizontally (useful for many items).
21
+ * @default false
22
+ */
23
+ scrollable?: boolean
24
+ /** Override container styles */
25
+ style?: StyleProp<ViewStyle>
26
+ }
27
+
28
+ /**
29
+ * Tabs container component that lays out TabItem children horizontally.
30
+ *
31
+ * The "Tab items" slot maps to React children — each child should be a
32
+ * `<TabItem>` element. The `modes` prop is automatically forwarded to
33
+ * every TabItem child so theming is consistent.
34
+ *
35
+ * @component
36
+ * @param {TabsProps} props
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ * const [activeIndex, setActiveIndex] = useState(0);
41
+ *
42
+ * <Tabs modes={{ 'Color Mode': 'Light' }}>
43
+ * <TabItem label="Tab 1" active={activeIndex === 0} onPress={() => setActiveIndex(0)} />
44
+ * <TabItem label="Tab 2" active={activeIndex === 1} onPress={() => setActiveIndex(1)} />
45
+ * <TabItem label="Tab 3" active={activeIndex === 2} onPress={() => setActiveIndex(2)} />
46
+ * </Tabs>
47
+ * ```
48
+ */
49
+ function Tabs({
50
+ children,
51
+ modes = {},
52
+ scrollable = false,
53
+ style,
54
+ }: TabsProps) {
55
+ const gap = (getVariableByName('tabs/gap', modes) ?? 16) as number
56
+ const paddingTop = (getVariableByName('tabs/padding/top', modes) ?? 0) as number
57
+ const paddingBottom = (getVariableByName('tabs/padding/bottom', modes) ?? 0) as number
58
+ const paddingLeft = (getVariableByName('tabs/padding/left', modes) ?? 0) as number
59
+ const paddingRight = (getVariableByName('tabs/padding/right', modes) ?? 0) as number
60
+
61
+ // Forward modes to all TabItem children
62
+ const enhancedChildren = React.Children.map(children, (child) => {
63
+ if (React.isValidElement(child) && child.type === TabItem) {
64
+ const childElement = child as React.ReactElement<any>;
65
+ return React.cloneElement(childElement, {
66
+ modes: { ...modes, ...(childElement.props.modes ?? {}) },
67
+ })
68
+ }
69
+ return child
70
+ })
71
+
72
+ const containerStyle: ViewStyle = {
73
+ flexDirection: 'row',
74
+ gap,
75
+ paddingTop,
76
+ paddingBottom,
77
+ paddingLeft,
78
+ paddingRight,
79
+ }
80
+
81
+ if (scrollable) {
82
+ return (
83
+ <ScrollView
84
+ horizontal
85
+ showsHorizontalScrollIndicator={false}
86
+ accessibilityRole="tablist"
87
+ contentContainerStyle={[containerStyle, style]}
88
+ >
89
+ {enhancedChildren}
90
+ </ScrollView>
91
+ )
92
+ }
93
+
94
+ return (
95
+ <View
96
+ style={[containerStyle, style]}
97
+ accessibilityRole="tablist"
98
+ >
99
+ {enhancedChildren}
100
+ </View>
101
+ )
102
+ }
103
+
104
+ export { TabItem }
105
+ export default Tabs
@@ -0,0 +1,48 @@
1
+ import React from 'react'
2
+ import { Text as RNText, type TextStyle, type StyleProp } from 'react-native'
3
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
+
5
+ export type TextProps = {
6
+ /** The text content to display. */
7
+ text?: string
8
+ /** Modes configuration for design token resolution. */
9
+ modes?: Record<string, any>
10
+ /** Style override for the text. */
11
+ style?: StyleProp<TextStyle>
12
+ /** Number of lines to limit the text to. */
13
+ numberOfLines?: number
14
+ }
15
+
16
+ function Text({
17
+ text = 'Korem ipsum ',
18
+ modes = {},
19
+ style,
20
+ numberOfLines,
21
+ }: TextProps) {
22
+ const foreground = getVariableByName('text/foreground', modes) ?? '#000000'
23
+ const fontFamily = getVariableByName('text/fontFamily', modes) ?? 'JioType'
24
+ const fontSize = getVariableByName('text/fontSize', modes) ?? 14
25
+ const fontWeight = getVariableByName('text/fontWeight', modes) ?? '500'
26
+ const lineHeight = getVariableByName('text/lineHeight', modes) ?? 20
27
+ const letterSpacing = getVariableByName('text/letterSpacing', modes) ?? -0.5
28
+
29
+ const textStyle: TextStyle = {
30
+ color: foreground as string,
31
+ fontFamily: fontFamily as string,
32
+ fontSize: fontSize as number,
33
+ fontWeight: String(fontWeight) as TextStyle['fontWeight'],
34
+ lineHeight: lineHeight as number,
35
+ letterSpacing: letterSpacing as number,
36
+ }
37
+
38
+ return (
39
+ <RNText
40
+ style={[textStyle, style]}
41
+ numberOfLines={numberOfLines}
42
+ >
43
+ {text}
44
+ </RNText>
45
+ )
46
+ }
47
+
48
+ export default Text
@@ -0,0 +1,122 @@
1
+ import React, { useCallback, useState } from 'react'
2
+ import {
3
+ Pressable,
4
+ View,
5
+ Platform,
6
+ type StyleProp,
7
+ type ViewStyle,
8
+ } from 'react-native'
9
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
10
+
11
+ export interface ToggleProps {
12
+ /** Whether the toggle is on (controlled) */
13
+ value?: boolean
14
+ /** Initial value (uncontrolled) */
15
+ defaultValue?: boolean
16
+ /** Callback when the toggle state changes */
17
+ onValueChange?: (value: boolean) => void
18
+ /** Whether the toggle is disabled */
19
+ disabled?: boolean
20
+ /** Design token modes for theming */
21
+ modes?: Record<string, any>
22
+ /** Override container styles */
23
+ style?: StyleProp<ViewStyle>
24
+ /** Accessibility label for screen readers */
25
+ accessibilityLabel?: string
26
+ }
27
+
28
+ /**
29
+ * Toggle (switch) component that maps directly to the Figma design using design tokens.
30
+ *
31
+ * Supports controlled and uncontrolled usage, on/off states, and disabled state.
32
+ * All styling is driven by design tokens with optional mode overrides.
33
+ *
34
+ * @component
35
+ * @param {ToggleProps} props
36
+ */
37
+ function Toggle({
38
+ value: controlledValue,
39
+ defaultValue = false,
40
+ onValueChange,
41
+ disabled = false,
42
+ modes = {},
43
+ style,
44
+ accessibilityLabel,
45
+ }: ToggleProps) {
46
+ const isControlled = controlledValue !== undefined
47
+ const [internalValue, setInternalValue] = useState(defaultValue)
48
+ const isOn = isControlled ? controlledValue : internalValue
49
+
50
+ const toggleState = disabled
51
+ ? (isOn ? 'Disabled On' : 'Disabled Off')
52
+ : (isOn ? 'On' : 'Off')
53
+ const resolvedModes = { ...modes, 'Toggle States': toggleState }
54
+
55
+ const width = getVariableByName('toggle/width', resolvedModes) ?? 52
56
+ const radius = getVariableByName('toggle/radius', resolvedModes) ?? 100
57
+ const paddingTrack = getVariableByName('toggle/padding/track', resolvedModes) ?? 3
58
+ const background = getVariableByName('toggle/background', resolvedModes) ?? (isOn ? '#5d00b5' : '#c7c7cc')
59
+
60
+ const thumbSize = getVariableByName('toggle/thumb/size', resolvedModes) ?? 25
61
+ const thumbRadius = getVariableByName('toggle/thumb/radius', resolvedModes) ?? 100
62
+ const thumbBackground = getVariableByName('toggle/thumb/background', resolvedModes) ?? '#ffffff'
63
+ const thumbShadowColor = getVariableByName('toggle/thumb/shadow/color', resolvedModes) ?? 'rgba(0,0,0,0.18)'
64
+ const thumbShadowRadius = getVariableByName('toggle/thumb/shadow/radius', resolvedModes) ?? 4
65
+ const thumbShadowOffsetX = getVariableByName('toggle/thumb/shadow/offsetX', resolvedModes) ?? 0
66
+ const thumbShadowOffsetY = getVariableByName('toggle/thumb/shadow/offsetY', resolvedModes) ?? 2
67
+
68
+ const trackHeight = (thumbSize as number) + (paddingTrack as number) * 2
69
+
70
+ const handlePress = useCallback(() => {
71
+ if (disabled) return
72
+ const next = !isOn
73
+ if (!isControlled) {
74
+ setInternalValue(next)
75
+ }
76
+ onValueChange?.(next)
77
+ }, [disabled, isOn, isControlled, onValueChange])
78
+
79
+ const trackStyle: ViewStyle = {
80
+ width: width as number,
81
+ height: trackHeight,
82
+ borderRadius: radius as number,
83
+ backgroundColor: background as string,
84
+ padding: paddingTrack as number,
85
+ justifyContent: 'center',
86
+ alignItems: isOn ? 'flex-end' : 'flex-start',
87
+ opacity: disabled ? 0.5 : 1,
88
+ }
89
+
90
+ const thumbShadow: ViewStyle = Platform.OS === 'web'
91
+ ? { boxShadow: `${thumbShadowOffsetX}px ${thumbShadowOffsetY}px ${thumbShadowRadius}px 0px ${thumbShadowColor}` } as ViewStyle
92
+ : {
93
+ shadowColor: thumbShadowColor as string,
94
+ shadowOffset: { width: thumbShadowOffsetX as number, height: thumbShadowOffsetY as number },
95
+ shadowRadius: thumbShadowRadius as number,
96
+ shadowOpacity: 1,
97
+ elevation: 4,
98
+ }
99
+
100
+ const thumbStyle: ViewStyle = {
101
+ width: thumbSize as number,
102
+ height: thumbSize as number,
103
+ borderRadius: thumbRadius as number,
104
+ backgroundColor: thumbBackground as string,
105
+ ...thumbShadow,
106
+ }
107
+
108
+ return (
109
+ <Pressable
110
+ style={[trackStyle, style]}
111
+ onPress={handlePress}
112
+ disabled={disabled}
113
+ accessibilityRole="switch"
114
+ accessibilityState={{ checked: isOn, disabled }}
115
+ accessibilityLabel={accessibilityLabel}
116
+ >
117
+ <View style={thumbStyle} />
118
+ </Pressable>
119
+ )
120
+ }
121
+
122
+ export default Toggle
@@ -7,6 +7,7 @@ export { default as BottomNav } from './BottomNav/BottomNav';
7
7
  export { default as BottomNavItem } from './BottomNavItem/BottomNavItem';
8
8
  export { default as Button, type ButtonProps } from './Button/Button';
9
9
  export { default as Card } from './Card/Card';
10
+ export { default as Checkbox, type CheckboxProps } from './Checkbox/Checkbox';
10
11
  export { default as CardFeedback } from './CardFeedback/CardFeedback';
11
12
  export { default as Disclaimer } from './Disclaimer/Disclaimer';
12
13
  export { default as Divider, type DividerProps, type DividerDirection } from './Divider/Divider';
@@ -25,7 +26,9 @@ export { default as ListItem } from './ListItem/ListItem';
25
26
  export { default as MediaCard } from './MediaCard/MediaCard';
26
27
  export { default as MerchantProfile, type MerchantProfileProps } from './MerchantProfile/MerchantProfile';
27
28
  export { default as MoneyValue } from './MoneyValue/MoneyValue';
29
+ export { default as NoteInput } from './NoteInput/NoteInput';
28
30
  export { default as NavArrow } from './NavArrow/NavArrow';
31
+ export { default as Numpad, type NumpadProps, type NumpadKeyValue } from './Numpad/Numpad';
29
32
  export { default as PageTitle } from './PageTitle/PageTitle';
30
33
  export { default as Screen, type ScreenProps } from './Screen/Screen';
31
34
  export { default as Section } from './Section/Section';
@@ -33,6 +36,7 @@ export { default as Stepper } from './Stepper/Stepper';
33
36
  export { Step } from './Stepper/Step';
34
37
  export { StepLabel } from './Stepper/StepLabel';
35
38
  export { default as TextInput } from './TextInput/TextInput';
39
+ export { default as StatusHero, type StatusHeroProps } from './StatusHero/StatusHero';
36
40
  export { default as ThreadHero, type ThreadHeroProps } from './ThreadHero/ThreadHero';
37
41
  export { Tooltip } from './Tooltip/Tooltip';
38
42
 
@@ -46,11 +50,22 @@ export { default as Accordion, type AccordionProps } from './Accordion/Accordion
46
50
  export { default as ActionTile, type ActionTileProps } from './ActionTile/ActionTile';
47
51
  export { default as Balance, type BalanceProps } from './Balance/Balance';
48
52
  export { default as ButtonGroup, type ButtonGroupProps } from './ButtonGroup/ButtonGroup';
53
+ export { default as CardProviderInfo, type CardProviderInfoProps } from './CardProviderInfo/CardProviderInfo';
49
54
  export { default as ChipSelect, type ChipSelectProps } from './ChipSelect/ChipSelect';
50
55
  export { default as InputSearch, type InputSearchProps } from './InputSearch/InputSearch';
51
56
  export { default as SupportText, type SupportTextProps } from './SupportText/SupportText';
52
57
  export { default as SupportTextIcon, type SupportTextIconProps } from './SupportText/SupportTextIcon';
53
58
  export { default as RadioButton, type RadioButtonProps } from './RadioButton/RadioButton';
59
+ export { default as Tabs, type TabsProps } from './Tabs/Tabs';
60
+ export { default as TabItem, type TabItemProps } from './Tabs/TabItem';
54
61
  export { default as Toast, type ToastProps } from './Toast/Toast';
55
62
  export { default as ToastProvider, type ToastProviderProps } from './Toast/ToastProvider';
56
63
  export { useToast, addToast, closeToast, closeAll, type ToastOptions, type ToastEntry, type ToastPlacement } from './Toast/useToast';
64
+ export { default as AmountInput, type AmountInputProps } from './AmountInput/AmountInput';
65
+ export { default as PortfolioHero, type PortfolioHeroProps } from './PortfolioHero/PortfolioHero';
66
+ export { default as ProductLabel, type ProductLabelProps } from './ProductLabel/ProductLabel';
67
+ export { default as SwappableAmount, type SwappableAmountProps } from './SwappableAmount/SwappableAmount';
68
+ export { default as OTP, type OTPProps } from './OTP/OTP';
69
+ export { default as StatItem, type StatItemProps } from './StatItem/StatItem';
70
+ export { default as Text, type TextProps } from './Text/Text';
71
+ export { default as Toggle, type ToggleProps } from './Toggle/Toggle';