jfs-components 0.0.71 → 0.0.73

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 (141) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/lib/commonjs/components/AccordionCheckbox/AccordionCheckbox.js +239 -0
  3. package/lib/commonjs/components/BrandChip/BrandChip.js +149 -0
  4. package/lib/commonjs/components/CardAdvisory/CardAdvisory.js +2 -2
  5. package/lib/commonjs/components/CardBankAccount/CardBankAccount.js +213 -0
  6. package/lib/commonjs/components/CardFinancialCondition/CardFinancialCondition.js +213 -0
  7. package/lib/commonjs/components/CardInsight/CardInsight.js +166 -0
  8. package/lib/commonjs/components/Carousel/Carousel.js +9 -7
  9. package/lib/commonjs/components/CheckboxGroup/CheckboxGroup.js +67 -0
  10. package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +125 -0
  11. package/lib/commonjs/components/CircularProgressBar/CircularProgressBar.js +56 -9
  12. package/lib/commonjs/components/CoverageBarComparison/CoverageBarComparison.js +272 -0
  13. package/lib/commonjs/components/CoverageRing/CoverageRing.js +141 -0
  14. package/lib/commonjs/components/DonutChart/DonutChart.js +309 -0
  15. package/lib/commonjs/components/DonutChartSummary/DonutChartSummary.js +155 -0
  16. package/lib/commonjs/components/HoldingsCard/HoldingsCard.js +2 -2
  17. package/lib/commonjs/components/InstitutionBadge/InstitutionBadge.js +132 -0
  18. package/lib/commonjs/components/LinearMeter/LinearMeter.js +9 -28
  19. package/lib/commonjs/components/LinearProgress/LinearProgress.js +68 -0
  20. package/lib/commonjs/components/MetricLegendItem/MetricLegendItem.js +95 -0
  21. package/lib/commonjs/components/MonthlyStatusGrid/MonthlyStatusGrid.js +286 -0
  22. package/lib/commonjs/components/OTP/OTP.js +381 -37
  23. package/lib/commonjs/components/ProductOverview/ProductOverview.js +147 -0
  24. package/lib/commonjs/components/Radio/Radio.js +194 -0
  25. package/lib/commonjs/components/RadioButton/RadioButton.js +21 -188
  26. package/lib/commonjs/components/RangeTrack/RangeTrack.js +269 -0
  27. package/lib/commonjs/components/SavingsGoalSummary/SavingsGoalSummary.js +181 -0
  28. package/lib/commonjs/components/SegmentedTrack/SegmentedTrack.js +171 -0
  29. package/lib/commonjs/components/StatGroup/StatGroup.js +128 -0
  30. package/lib/commonjs/components/StatItem/StatItem.js +65 -35
  31. package/lib/commonjs/components/StrengthIndicator/StrengthIndicator.js +157 -0
  32. package/lib/commonjs/components/SummaryTile/SummaryTile.js +150 -0
  33. package/lib/commonjs/components/index.js +192 -1
  34. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  35. package/lib/commonjs/icons/registry.js +1 -1
  36. package/lib/commonjs/utils/index.js +7 -0
  37. package/lib/commonjs/utils/number-utils.js +57 -0
  38. package/lib/module/components/AccordionCheckbox/AccordionCheckbox.js +233 -0
  39. package/lib/module/components/BrandChip/BrandChip.js +143 -0
  40. package/lib/module/components/CardAdvisory/CardAdvisory.js +2 -2
  41. package/lib/module/components/CardBankAccount/CardBankAccount.js +208 -0
  42. package/lib/module/components/CardFinancialCondition/CardFinancialCondition.js +207 -0
  43. package/lib/module/components/CardInsight/CardInsight.js +161 -0
  44. package/lib/module/components/Carousel/Carousel.js +9 -7
  45. package/lib/module/components/CheckboxGroup/CheckboxGroup.js +62 -0
  46. package/lib/module/components/CheckboxItem/CheckboxItem.js +119 -0
  47. package/lib/module/components/CircularProgressBar/CircularProgressBar.js +56 -9
  48. package/lib/module/components/CoverageBarComparison/CoverageBarComparison.js +266 -0
  49. package/lib/module/components/CoverageRing/CoverageRing.js +136 -0
  50. package/lib/module/components/DonutChart/DonutChart.js +303 -0
  51. package/lib/module/components/DonutChartSummary/DonutChartSummary.js +150 -0
  52. package/lib/module/components/HoldingsCard/HoldingsCard.js +2 -2
  53. package/lib/module/components/InstitutionBadge/InstitutionBadge.js +127 -0
  54. package/lib/module/components/LinearMeter/LinearMeter.js +9 -28
  55. package/lib/module/components/LinearProgress/LinearProgress.js +63 -0
  56. package/lib/module/components/MetricLegendItem/MetricLegendItem.js +90 -0
  57. package/lib/module/components/MonthlyStatusGrid/MonthlyStatusGrid.js +281 -0
  58. package/lib/module/components/OTP/OTP.js +381 -38
  59. package/lib/module/components/ProductOverview/ProductOverview.js +142 -0
  60. package/lib/module/components/Radio/Radio.js +188 -0
  61. package/lib/module/components/RadioButton/RadioButton.js +20 -185
  62. package/lib/module/components/RangeTrack/RangeTrack.js +263 -0
  63. package/lib/module/components/SavingsGoalSummary/SavingsGoalSummary.js +175 -0
  64. package/lib/module/components/SegmentedTrack/SegmentedTrack.js +166 -0
  65. package/lib/module/components/StatGroup/StatGroup.js +123 -0
  66. package/lib/module/components/StatItem/StatItem.js +66 -36
  67. package/lib/module/components/StrengthIndicator/StrengthIndicator.js +152 -0
  68. package/lib/module/components/SummaryTile/SummaryTile.js +145 -0
  69. package/lib/module/components/index.js +28 -1
  70. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  71. package/lib/module/icons/registry.js +1 -1
  72. package/lib/module/utils/index.js +2 -1
  73. package/lib/module/utils/number-utils.js +53 -0
  74. package/lib/typescript/src/components/AccordionCheckbox/AccordionCheckbox.d.ts +71 -0
  75. package/lib/typescript/src/components/BrandChip/BrandChip.d.ts +43 -0
  76. package/lib/typescript/src/components/CardBankAccount/CardBankAccount.d.ts +79 -0
  77. package/lib/typescript/src/components/CardFinancialCondition/CardFinancialCondition.d.ts +50 -0
  78. package/lib/typescript/src/components/CardInsight/CardInsight.d.ts +48 -0
  79. package/lib/typescript/src/components/CheckboxGroup/CheckboxGroup.d.ts +41 -0
  80. package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +56 -0
  81. package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +11 -1
  82. package/lib/typescript/src/components/CoverageBarComparison/CoverageBarComparison.d.ts +105 -0
  83. package/lib/typescript/src/components/CoverageRing/CoverageRing.d.ts +90 -0
  84. package/lib/typescript/src/components/DonutChart/DonutChart.d.ts +117 -0
  85. package/lib/typescript/src/components/DonutChartSummary/DonutChartSummary.d.ts +103 -0
  86. package/lib/typescript/src/components/InstitutionBadge/InstitutionBadge.d.ts +30 -0
  87. package/lib/typescript/src/components/LinearProgress/LinearProgress.d.ts +17 -0
  88. package/lib/typescript/src/components/MetricLegendItem/MetricLegendItem.d.ts +37 -0
  89. package/lib/typescript/src/components/MonthlyStatusGrid/MonthlyStatusGrid.d.ts +119 -0
  90. package/lib/typescript/src/components/OTP/OTP.d.ts +88 -2
  91. package/lib/typescript/src/components/ProductOverview/ProductOverview.d.ts +39 -0
  92. package/lib/typescript/src/components/Radio/Radio.d.ts +30 -0
  93. package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +20 -28
  94. package/lib/typescript/src/components/RangeTrack/RangeTrack.d.ts +173 -0
  95. package/lib/typescript/src/components/SavingsGoalSummary/SavingsGoalSummary.d.ts +95 -0
  96. package/lib/typescript/src/components/SegmentedTrack/SegmentedTrack.d.ts +108 -0
  97. package/lib/typescript/src/components/StatGroup/StatGroup.d.ts +45 -0
  98. package/lib/typescript/src/components/StatItem/StatItem.d.ts +24 -7
  99. package/lib/typescript/src/components/StrengthIndicator/StrengthIndicator.d.ts +58 -0
  100. package/lib/typescript/src/components/SummaryTile/SummaryTile.d.ts +60 -0
  101. package/lib/typescript/src/components/index.d.ts +29 -2
  102. package/lib/typescript/src/icons/registry.d.ts +1 -1
  103. package/lib/typescript/src/utils/index.d.ts +1 -0
  104. package/lib/typescript/src/utils/number-utils.d.ts +29 -0
  105. package/package.json +1 -1
  106. package/src/components/AccordionCheckbox/AccordionCheckbox.tsx +323 -0
  107. package/src/components/BrandChip/BrandChip.tsx +235 -0
  108. package/src/components/CardAdvisory/CardAdvisory.tsx +2 -2
  109. package/src/components/CardBankAccount/CardBankAccount.tsx +295 -0
  110. package/src/components/CardFinancialCondition/CardFinancialCondition.tsx +366 -0
  111. package/src/components/CardInsight/CardInsight.tsx +239 -0
  112. package/src/components/Carousel/Carousel.tsx +14 -6
  113. package/src/components/CheckboxGroup/CheckboxGroup.tsx +86 -0
  114. package/src/components/CheckboxItem/CheckboxItem.tsx +174 -0
  115. package/src/components/CircularProgressBar/CircularProgressBar.tsx +74 -9
  116. package/src/components/CoverageBarComparison/CoverageBarComparison.tsx +378 -0
  117. package/src/components/CoverageRing/CoverageRing.tsx +225 -0
  118. package/src/components/DonutChart/DonutChart.tsx +503 -0
  119. package/src/components/DonutChartSummary/DonutChartSummary.tsx +256 -0
  120. package/src/components/HoldingsCard/HoldingsCard.tsx +2 -2
  121. package/src/components/InstitutionBadge/InstitutionBadge.tsx +216 -0
  122. package/src/components/LinearMeter/LinearMeter.tsx +9 -39
  123. package/src/components/LinearProgress/LinearProgress.tsx +92 -0
  124. package/src/components/MetricLegendItem/MetricLegendItem.tsx +167 -0
  125. package/src/components/MonthlyStatusGrid/MonthlyStatusGrid.tsx +438 -0
  126. package/src/components/OTP/OTP.tsx +476 -29
  127. package/src/components/ProductOverview/ProductOverview.tsx +236 -0
  128. package/src/components/Radio/Radio.tsx +227 -0
  129. package/src/components/RadioButton/RadioButton.tsx +23 -225
  130. package/src/components/RangeTrack/RangeTrack.tsx +394 -0
  131. package/src/components/SavingsGoalSummary/SavingsGoalSummary.tsx +269 -0
  132. package/src/components/SegmentedTrack/SegmentedTrack.tsx +268 -0
  133. package/src/components/StatGroup/StatGroup.tsx +169 -0
  134. package/src/components/StatItem/StatItem.tsx +117 -40
  135. package/src/components/StrengthIndicator/StrengthIndicator.tsx +205 -0
  136. package/src/components/SummaryTile/SummaryTile.tsx +251 -0
  137. package/src/components/index.ts +39 -2
  138. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  139. package/src/icons/registry.ts +1 -1
  140. package/src/utils/index.ts +1 -0
  141. package/src/utils/number-utils.ts +60 -0
@@ -0,0 +1,236 @@
1
+ import React from 'react'
2
+ import {
3
+ View,
4
+ Text,
5
+ type StyleProp,
6
+ type ViewStyle,
7
+ type TextStyle,
8
+ type ImageSourcePropType,
9
+ } from 'react-native'
10
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
11
+ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils'
12
+ import Image from '../Image/Image'
13
+ import ProductLabel from '../ProductLabel/ProductLabel'
14
+
15
+ export type ProductOverviewStat = {
16
+ /** The large prominent value shown on top, e.g. "995", "3%". */
17
+ value: string
18
+ /** The descriptive label rendered beneath the value, e.g. "Purity". */
19
+ label: string
20
+ }
21
+
22
+ export type ProductOverviewProps = {
23
+ /** Hero image source rendered at the top of the card. */
24
+ imageSource?: ImageSourcePropType | string
25
+ /** Aspect ratio for the hero image. Defaults to the Figma frame (288:170). */
26
+ imageRatio?: number
27
+ /** Avatar source for the inline ProductLabel chip. */
28
+ labelImageSource?: ImageSourcePropType | string
29
+ /** Text shown next to the avatar in the ProductLabel chip. */
30
+ label?: string
31
+ /** The large product name, e.g. "0.5g Gold Coin". */
32
+ productName?: string
33
+ /** The short multi-line description below the product name. */
34
+ description?: string
35
+ /**
36
+ * Stats rendered in the bottom row. Each stat shows a large value above a
37
+ * smaller label. Pass an empty array (or `null`) to hide the row.
38
+ */
39
+ stats?: ProductOverviewStat[]
40
+ /** Design token modes for theming (e.g. `{ 'Color Mode': 'Light' }`). */
41
+ modes?: Record<string, any>
42
+ /** Container style override. */
43
+ style?: StyleProp<ViewStyle>
44
+ /**
45
+ * Custom slot rendered between the description and the stats row, useful
46
+ * for badges, callouts, etc. Receives the same `modes` as the parent.
47
+ */
48
+ children?: React.ReactNode
49
+ }
50
+
51
+ const DEFAULT_STATS: ProductOverviewStat[] = [
52
+ { value: '995', label: 'Purity' },
53
+ { value: '3%', label: 'GST' },
54
+ ]
55
+
56
+ const ProductOverview = ({
57
+ imageSource,
58
+ imageRatio = 288 / 170,
59
+ labelImageSource,
60
+ label = 'Gold',
61
+ productName = '0.5g Gold Coin',
62
+ description = 'Your gold is insured from our vault to you. If lost or damaged, we’ll replace it.',
63
+ stats = DEFAULT_STATS,
64
+ modes = EMPTY_MODES,
65
+ style,
66
+ children,
67
+ }: ProductOverviewProps) => {
68
+ const padding = (getVariableByName('productOverview/padding', modes) as number | null) ?? 24
69
+ const gap = (getVariableByName('productOverview/gap', modes) as number | null) ?? 12
70
+ const background =
71
+ (getVariableByName('productOverview/background', modes) as string | null) ?? '#ffffff'
72
+
73
+ const productNameColor =
74
+ (getVariableByName('productOverview/productName/color', modes) as string | null) ??
75
+ '#0d0d0f'
76
+ const productNameFontFamily =
77
+ (getVariableByName('productOverview/productName/fontFamily', modes) as string | null) ??
78
+ 'JioType Var'
79
+ const productNameFontSize =
80
+ (getVariableByName('productOverview/productName/fontSize', modes) as number | null) ?? 26
81
+ const productNameFontWeight =
82
+ getVariableByName('productOverview/productName/fontWeight', modes) ?? 900
83
+ const productNameLineHeight =
84
+ (getVariableByName('productOverview/productName/lineHeight', modes) as number | null) ?? 26
85
+
86
+ const descriptionColor =
87
+ (getVariableByName('productOverview/description/color', modes) as string | null) ??
88
+ '#1a1c1f'
89
+ const descriptionFontFamily =
90
+ (getVariableByName('productOverview/description/fontFamily', modes) as string | null) ??
91
+ 'JioType Var'
92
+ const descriptionFontSize =
93
+ (getVariableByName('productOverview/description/fontSize', modes) as number | null) ?? 14
94
+ const descriptionFontWeight =
95
+ getVariableByName('productOverview/description/fontWeight', modes) ?? 500
96
+ const descriptionLineHeight =
97
+ (getVariableByName('productOverview/description/lineHeight', modes) as number | null) ??
98
+ 18.2
99
+
100
+ const statGap = (getVariableByName('productOverview/stat/gap', modes) as number | null) ?? 2
101
+ const statValueColor =
102
+ (getVariableByName('productOverview/stat/value/color', modes) as string | null) ??
103
+ '#141414'
104
+ const statValueFontFamily =
105
+ (getVariableByName('productOverview/stat/value/fontFamily', modes) as string | null) ??
106
+ 'JioType Var'
107
+ const statValueFontSize =
108
+ (getVariableByName('productOverview/stat/value/fontSize', modes) as number | null) ?? 20
109
+ const statValueFontWeight =
110
+ getVariableByName('productOverview/stat/value/fontWeight', modes) ?? 900
111
+ const statValueLineHeight =
112
+ (getVariableByName('productOverview/stat/value/lineHeight', modes) as number | null) ?? 20
113
+
114
+ const statLabelColor = productNameColor
115
+ const statLabelFontFamily =
116
+ (getVariableByName('productOverview/stat/label/fontFamily', modes) as string | null) ??
117
+ 'JioType Var'
118
+ const statLabelFontSize =
119
+ (getVariableByName('productOverview/stat/label/fontSize', modes) as number | null) ?? 12
120
+ const statLabelFontWeight =
121
+ getVariableByName('productOverview/stat/label/fontWeight', modes) ?? 400
122
+ const statLabelLineHeight =
123
+ (getVariableByName('productOverview/stat/label/lineHeight', modes) as number | null) ??
124
+ 15.6
125
+
126
+ const productNameStyle: TextStyle = {
127
+ color: productNameColor,
128
+ fontFamily: productNameFontFamily,
129
+ fontSize: productNameFontSize,
130
+ fontWeight: String(productNameFontWeight) as TextStyle['fontWeight'],
131
+ lineHeight: productNameLineHeight,
132
+ textAlign: 'center',
133
+ }
134
+
135
+ const descriptionStyle: TextStyle = {
136
+ color: descriptionColor,
137
+ fontFamily: descriptionFontFamily,
138
+ fontSize: descriptionFontSize,
139
+ fontWeight: String(descriptionFontWeight) as TextStyle['fontWeight'],
140
+ lineHeight: descriptionLineHeight,
141
+ textAlign: 'center',
142
+ }
143
+
144
+ const statValueStyle: TextStyle = {
145
+ color: statValueColor,
146
+ fontFamily: statValueFontFamily,
147
+ fontSize: statValueFontSize,
148
+ fontWeight: String(statValueFontWeight) as TextStyle['fontWeight'],
149
+ lineHeight: statValueLineHeight,
150
+ }
151
+
152
+ const statLabelStyle: TextStyle = {
153
+ color: statLabelColor,
154
+ fontFamily: statLabelFontFamily,
155
+ fontSize: statLabelFontSize,
156
+ fontWeight: String(statLabelFontWeight) as TextStyle['fontWeight'],
157
+ lineHeight: statLabelLineHeight,
158
+ textAlign: 'center',
159
+ }
160
+
161
+ const showStats = Array.isArray(stats) && stats.length > 0
162
+
163
+ return (
164
+ <View
165
+ style={[
166
+ {
167
+ backgroundColor: background,
168
+ padding,
169
+ gap,
170
+ alignItems: 'center',
171
+ width: '100%',
172
+ },
173
+ style,
174
+ ]}
175
+ >
176
+ {imageSource != null && (
177
+ <Image
178
+ imageSource={imageSource}
179
+ ratio={imageRatio}
180
+ resizeMode="contain"
181
+ accessibilityElementsHidden
182
+ importantForAccessibility="no"
183
+ />
184
+ )}
185
+
186
+ <ProductLabel
187
+ label={label}
188
+ {...(labelImageSource != null && { imageSource: labelImageSource })}
189
+ modes={modes}
190
+ />
191
+
192
+ {productName ? (
193
+ <Text style={productNameStyle} accessibilityRole="header">
194
+ {productName}
195
+ </Text>
196
+ ) : null}
197
+
198
+ {description ? <Text style={descriptionStyle}>{description}</Text> : null}
199
+
200
+ {children ? <>{cloneChildrenWithModes(children, modes)}</> : null}
201
+
202
+ {showStats && (
203
+ <View
204
+ style={{
205
+ flexDirection: 'row',
206
+ alignItems: 'center',
207
+ justifyContent: 'space-between',
208
+ width: '100%',
209
+ }}
210
+ >
211
+ {stats.map((stat, index) => (
212
+ <View
213
+ key={`${stat.label}-${index}`}
214
+ style={{
215
+ flex: 1,
216
+ minWidth: 0,
217
+ alignItems: 'center',
218
+ gap: statGap,
219
+ overflow: 'hidden',
220
+ }}
221
+ >
222
+ <Text style={statValueStyle} numberOfLines={1}>
223
+ {stat.value}
224
+ </Text>
225
+ <Text style={statLabelStyle} numberOfLines={1}>
226
+ {stat.label}
227
+ </Text>
228
+ </View>
229
+ ))}
230
+ </View>
231
+ )}
232
+ </View>
233
+ )
234
+ }
235
+
236
+ export default ProductOverview
@@ -0,0 +1,227 @@
1
+ import React, { useMemo, useState } from 'react'
2
+ import {
3
+ Pressable,
4
+ View,
5
+ StyleSheet,
6
+ Platform,
7
+ ViewStyle,
8
+ DimensionValue,
9
+ StyleProp,
10
+ } from 'react-native'
11
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
12
+ import { EMPTY_MODES } from '../../utils/react-utils'
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Props
16
+ // ---------------------------------------------------------------------------
17
+
18
+ export interface RadioProps {
19
+ /**
20
+ * Whether the radio is selected.
21
+ */
22
+ selected?: boolean
23
+ /**
24
+ * Whether the radio is disabled.
25
+ */
26
+ disabled?: boolean
27
+ /**
28
+ * Function to call when the radio is pressed.
29
+ */
30
+ onPress?: () => void
31
+ /**
32
+ * Modes object for design-token resolution.
33
+ */
34
+ modes?: Record<string, any>
35
+ /**
36
+ * Custom style for the radio container.
37
+ */
38
+ style?: StyleProp<ViewStyle>
39
+ /**
40
+ * Test ID for testing.
41
+ */
42
+ testID?: string
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Radio
47
+ // ---------------------------------------------------------------------------
48
+
49
+ export function Radio({
50
+ selected = false,
51
+ disabled = false,
52
+ onPress,
53
+ modes = EMPTY_MODES,
54
+ style,
55
+ testID,
56
+ }: RadioProps) {
57
+ // ---- Refs & State ----
58
+ const [hovered, setHovered] = useState(false)
59
+ const [focused, setFocused] = useState(false)
60
+ const [pressed, setPressed] = useState(false)
61
+
62
+ // ---- Dimensions ----
63
+ const widthStr = getVariableByName('radio/width', modes) || '18'
64
+ const heightStr = getVariableByName('radio/height', modes) || '18'
65
+ const selectorSizeStr = getVariableByName('radio/selector/size', modes) || '10'
66
+
67
+ const width = parseFloat(widthStr?.toString() || '18')
68
+ const height = parseFloat(heightStr?.toString() || '18')
69
+ const selectorSize = parseFloat(selectorSizeStr?.toString() || '10')
70
+
71
+ // ---- State Logic ----
72
+ // Priority: Disabled -> Focused -> Hover/Pressed -> Idle
73
+ // Note: Design treats Active (Pressed) similar to Selected for some styles,
74
+ // but usually in Radios, Pressed is a transient state.
75
+ // We will map:
76
+ // - Disabled -> 'disabled'
77
+ // - Focused -> 'focus'
78
+ // - Hovered -> 'hover'
79
+ // - Idle -> 'idle'
80
+
81
+ // We handle `selected` as a separate dimension derived from state.
82
+
83
+ let visualState = 'idle'
84
+ if (disabled) {
85
+ visualState = 'disabled'
86
+ } else if (focused) {
87
+ visualState = 'focus'
88
+ } else if (hovered || pressed) {
89
+ visualState = 'hover'
90
+ }
91
+
92
+ // Construct token paths based on state + selected
93
+ let prefix = `radio/${visualState}`
94
+ if (visualState === 'idle' && selected) {
95
+ prefix = `radio/selected`
96
+ } else if (selected) {
97
+ prefix = `radio/${visualState}Selected`
98
+ }
99
+
100
+ // ---- Colors & Border ----
101
+
102
+ const resolveColor = (path: string, fallback: string) => {
103
+ return getVariableByName(path, modes) || fallback
104
+ }
105
+
106
+ // Background Color
107
+ let bgColorVar = `${prefix}/background/color`
108
+ // Fix for disabledSelected weirdness if needed
109
+ if (visualState === 'disabled' && selected) {
110
+ // Check specific path from dump: `radio/disabledSelected/background`
111
+ if (!getVariableByName(`${prefix}/background/color`, modes)) {
112
+ bgColorVar = `${prefix}/background`
113
+ }
114
+ }
115
+
116
+ // Border Color
117
+ let borderColorVar = `${prefix}/border/color`
118
+
119
+ // Border Width
120
+ let borderWidthVar = `${prefix}/border/size`
121
+ // Fix for huge path: `radio/disabled/radio/disabled/border/size`
122
+ if (visualState === 'disabled' && !selected) {
123
+ if (getVariableByName('radio/disabled/radio/disabled/border/size', modes)) {
124
+ borderWidthVar = 'radio/disabled/radio/disabled/border/size'
125
+ }
126
+ }
127
+
128
+ // Selector Color
129
+ let selectorBgVar = `${prefix}/selector/background/color`
130
+ if (!selected) {
131
+ selectorBgVar = 'transparent'
132
+ }
133
+
134
+ // Shadows (Glow)
135
+ let shadowSizeVar = `${prefix}/boxShadow/size`
136
+ let shadowColorVar = `${prefix}/shadow/color`
137
+
138
+ // Resolve Values
139
+ const backgroundColor = resolveColor(bgColorVar, 'transparent')
140
+ const borderColor = resolveColor(borderColorVar, 'transparent')
141
+ const borderWidth = parseFloat(getVariableByName(borderWidthVar, modes)?.toString() || '1')
142
+ const selectorColor = resolveColor(selectorBgVar, 'transparent')
143
+
144
+ const shadowSize = parseFloat(getVariableByName(shadowSizeVar, modes)?.toString() || '0')
145
+ const shadowColor = resolveColor(shadowColorVar, 'transparent')
146
+
147
+ // Styles
148
+ const containerStyle: any = {
149
+ width,
150
+ height,
151
+ borderRadius: width / 2, // 9999px -> circle
152
+ borderWidth,
153
+ borderColor,
154
+ backgroundColor,
155
+ justifyContent: 'center',
156
+ alignItems: 'center',
157
+ // Web shadow (ring)
158
+ ...(Platform.OS === 'web' && shadowSize > 0 ? {
159
+ boxShadow: `0px 0px 0px ${shadowSize}px ${shadowColor}`,
160
+ } : {}),
161
+ }
162
+
163
+ const selectorStyle: ViewStyle = {
164
+ width: selectorSize,
165
+ height: selectorSize,
166
+ borderRadius: selectorSize / 2,
167
+ backgroundColor: selectorColor,
168
+ }
169
+
170
+ // Dummy block for token extraction (static analysis)
171
+ if (false as boolean) {
172
+ getVariableByName('radio/idle/background/color')
173
+ getVariableByName('radio/idle/border/color')
174
+ getVariableByName('radio/selector/size')
175
+ getVariableByName('radio/width')
176
+ getVariableByName('radio/height')
177
+ getVariableByName('radio/background/color')
178
+ getVariableByName('radio/hover/background/color')
179
+ getVariableByName('radio/hover/border/color')
180
+ getVariableByName('radio/hover/boxShadow/size')
181
+ getVariableByName('radio/hover/shadow/color')
182
+ getVariableByName('radio/selected/background/color')
183
+ getVariableByName('radio/selected/border/color')
184
+ getVariableByName('radio/selected/selector/background/color')
185
+ getVariableByName('radio/hoverSelected/background/color')
186
+ getVariableByName('radio/hoverSelected/border/color')
187
+ getVariableByName('radio/hoverSelected/boxShadow/size')
188
+ getVariableByName('radio/hoverSelected/shadow/color')
189
+ getVariableByName('radio/hoverSelected/selector/background/color')
190
+ getVariableByName('radio/focus/background/color')
191
+ getVariableByName('radio/focus/border/color')
192
+ getVariableByName('radio/focus/border/size')
193
+ getVariableByName('radio/focus/boxShadow/size')
194
+ getVariableByName('radio/focus/shadow/color')
195
+ getVariableByName('radio/focusSelected/background/color')
196
+ getVariableByName('radio/focusSelected/selector/background/color')
197
+ getVariableByName('radio/focusSelected/border/size')
198
+ getVariableByName('radio/disabled/radio/disabled/border/size')
199
+ getVariableByName('radio/disabled/background/color')
200
+ getVariableByName('radio/disabled/border/color')
201
+ getVariableByName('radio/disabledSelected/selector/background/color')
202
+ getVariableByName('radio/disabledSelected/background')
203
+ getVariableByName('radio/disabledSelected/border/color')
204
+ }
205
+
206
+ return (
207
+ <Pressable
208
+ testID={testID}
209
+ disabled={disabled}
210
+ onPress={onPress}
211
+ onHoverIn={() => setHovered(true)}
212
+ onHoverOut={() => setHovered(false)}
213
+ onFocus={() => setFocused(true)}
214
+ onBlur={() => setFocused(false)}
215
+ onPressIn={() => setPressed(true)}
216
+ onPressOut={() => setPressed(false)}
217
+ style={({ pressed: isPressed }) => [
218
+ containerStyle,
219
+ style,
220
+ ]}
221
+ >
222
+ <View style={selectorStyle} />
223
+ </Pressable>
224
+ )
225
+ }
226
+
227
+ export default Radio