jfs-components 0.0.71 → 0.0.72

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 (35) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/lib/commonjs/components/CardAdvisory/CardAdvisory.js +2 -2
  3. package/lib/commonjs/components/CardFinancialCondition/CardFinancialCondition.js +213 -0
  4. package/lib/commonjs/components/Carousel/Carousel.js +9 -7
  5. package/lib/commonjs/components/HoldingsCard/HoldingsCard.js +2 -2
  6. package/lib/commonjs/components/InstitutionBadge/InstitutionBadge.js +132 -0
  7. package/lib/commonjs/components/Radio/Radio.js +194 -0
  8. package/lib/commonjs/components/RadioButton/RadioButton.js +21 -188
  9. package/lib/commonjs/components/index.js +21 -0
  10. package/lib/commonjs/icons/registry.js +1 -1
  11. package/lib/module/components/CardAdvisory/CardAdvisory.js +2 -2
  12. package/lib/module/components/CardFinancialCondition/CardFinancialCondition.js +207 -0
  13. package/lib/module/components/Carousel/Carousel.js +9 -7
  14. package/lib/module/components/HoldingsCard/HoldingsCard.js +2 -2
  15. package/lib/module/components/InstitutionBadge/InstitutionBadge.js +127 -0
  16. package/lib/module/components/Radio/Radio.js +188 -0
  17. package/lib/module/components/RadioButton/RadioButton.js +20 -185
  18. package/lib/module/components/index.js +7 -0
  19. package/lib/module/icons/registry.js +1 -1
  20. package/lib/typescript/src/components/CardFinancialCondition/CardFinancialCondition.d.ts +50 -0
  21. package/lib/typescript/src/components/InstitutionBadge/InstitutionBadge.d.ts +30 -0
  22. package/lib/typescript/src/components/Radio/Radio.d.ts +30 -0
  23. package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +20 -28
  24. package/lib/typescript/src/components/index.d.ts +7 -0
  25. package/lib/typescript/src/icons/registry.d.ts +1 -1
  26. package/package.json +1 -1
  27. package/src/components/CardAdvisory/CardAdvisory.tsx +2 -2
  28. package/src/components/CardFinancialCondition/CardFinancialCondition.tsx +366 -0
  29. package/src/components/Carousel/Carousel.tsx +14 -6
  30. package/src/components/HoldingsCard/HoldingsCard.tsx +2 -2
  31. package/src/components/InstitutionBadge/InstitutionBadge.tsx +216 -0
  32. package/src/components/Radio/Radio.tsx +227 -0
  33. package/src/components/RadioButton/RadioButton.tsx +23 -225
  34. package/src/components/index.ts +7 -0
  35. package/src/icons/registry.ts +1 -1
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ import { View, type StyleProp, type TextStyle, type ViewStyle } from 'react-native';
3
+ import { type CircularProgressBarProps } from '../CircularProgressBar/CircularProgressBar';
4
+ type CardFinancialConditionBaseProps = Omit<React.ComponentProps<typeof View>, 'children' | 'style'>;
5
+ export type CardFinancialConditionProps = CardFinancialConditionBaseProps & {
6
+ /** Title text shown in the header. */
7
+ title?: string;
8
+ /** Body / description shown below the title. */
9
+ body?: string;
10
+ /** Progress score for the circular indicator. Forwarded to CircularProgressBar. */
11
+ value?: number;
12
+ /** Visual state of the circular indicator. Forwarded to CircularProgressBar. */
13
+ progressState?: CircularProgressBarProps['state'];
14
+ /** Optional formatted value label for the active state. */
15
+ valueLabel?: string;
16
+ /** Show the bottom advisory nudge. */
17
+ showNudge?: boolean;
18
+ /** Body text for the default nudge. Use `\n` to add a second line. */
19
+ nudgeBody?: string;
20
+ /** Show the bottom divider above the action button. */
21
+ showDivider?: boolean;
22
+ /** Show the action button at the bottom of the card. */
23
+ showButton?: boolean;
24
+ /** Label for the default action button. */
25
+ buttonLabel?: string;
26
+ /** Callback for the default action button press. */
27
+ onPressButton?: () => void;
28
+ /** Slot replacing the default circular progress bar. Receives `modes` recursively. */
29
+ progressSlot?: React.ReactNode;
30
+ /** Slot replacing the default Nudge. Receives `modes` recursively. */
31
+ nudgeSlot?: React.ReactNode;
32
+ /** Slot replacing the default action button. Receives `modes` recursively. */
33
+ buttonSlot?: React.ReactNode;
34
+ /** Design token modes forwarded to token lookups and child components. */
35
+ modes?: Record<string, any>;
36
+ /** Container style override. */
37
+ style?: StyleProp<ViewStyle>;
38
+ /** Header row style override. */
39
+ headerStyle?: StyleProp<ViewStyle>;
40
+ /** Title text style override. */
41
+ titleStyle?: StyleProp<TextStyle>;
42
+ /** Body text style override. */
43
+ bodyStyle?: StyleProp<TextStyle>;
44
+ /** Accessibility label for the whole card. */
45
+ accessibilityLabel?: string;
46
+ };
47
+ declare function CardFinancialCondition({ title, body, value, progressState, valueLabel, showNudge, nudgeBody, showDivider, showButton, buttonLabel, onPressButton, progressSlot, nudgeSlot, buttonSlot, modes: propModes, style, headerStyle, titleStyle, bodyStyle, accessibilityLabel, ...rest }: CardFinancialConditionProps): import("react/jsx-runtime").JSX.Element;
48
+ declare const _default: React.MemoExoticComponent<typeof CardFinancialCondition>;
49
+ export default _default;
50
+ //# sourceMappingURL=CardFinancialCondition.d.ts.map
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { View, type StyleProp, type TextStyle, type ViewStyle } from 'react-native';
3
+ import { type UnifiedSource } from '../../utils/MediaSource';
4
+ type InstitutionBadgeBaseProps = Omit<React.ComponentProps<typeof View>, 'children' | 'style'>;
5
+ export type InstitutionBadgeProps = InstitutionBadgeBaseProps & {
6
+ /** Visible label for the institution (e.g. bank name). */
7
+ label?: string;
8
+ /**
9
+ * Unified avatar source. Accepts a remote URI (raster or `.svg`), an
10
+ * inline SVG XML string, a `require()` asset, an SVG React component,
11
+ * or an already-rendered React element. Smart-detects raster vs SVG so
12
+ * the same prop works on iOS, Android and web. See {@link UnifiedSource}.
13
+ * Ignored when `avatarSlot` is provided.
14
+ */
15
+ source?: UnifiedSource;
16
+ /** Slot replacing the default Avatar (e.g. for monogram avatars). Receives `modes` recursively. */
17
+ avatarSlot?: React.ReactNode;
18
+ /** Design token modes forwarded to token lookups and the Avatar slot. */
19
+ modes?: Record<string, any>;
20
+ /** Container style override. */
21
+ style?: StyleProp<ViewStyle>;
22
+ /** Label style override. */
23
+ labelStyle?: StyleProp<TextStyle>;
24
+ /** Accessibility label. Defaults to `label`. */
25
+ accessibilityLabel?: string;
26
+ };
27
+ declare function InstitutionBadge({ label, source, avatarSlot, modes: propModes, style, labelStyle, accessibilityLabel, ...rest }: InstitutionBadgeProps): import("react/jsx-runtime").JSX.Element;
28
+ declare const _default: React.MemoExoticComponent<typeof InstitutionBadge>;
29
+ export default _default;
30
+ //# sourceMappingURL=InstitutionBadge.d.ts.map
@@ -0,0 +1,30 @@
1
+ import { ViewStyle, StyleProp } from 'react-native';
2
+ export interface RadioProps {
3
+ /**
4
+ * Whether the radio is selected.
5
+ */
6
+ selected?: boolean;
7
+ /**
8
+ * Whether the radio is disabled.
9
+ */
10
+ disabled?: boolean;
11
+ /**
12
+ * Function to call when the radio is pressed.
13
+ */
14
+ onPress?: () => void;
15
+ /**
16
+ * Modes object for design-token resolution.
17
+ */
18
+ modes?: Record<string, any>;
19
+ /**
20
+ * Custom style for the radio container.
21
+ */
22
+ style?: StyleProp<ViewStyle>;
23
+ /**
24
+ * Test ID for testing.
25
+ */
26
+ testID?: string;
27
+ }
28
+ export declare function Radio({ selected, disabled, onPress, modes, style, testID, }: RadioProps): import("react/jsx-runtime").JSX.Element;
29
+ export default Radio;
30
+ //# sourceMappingURL=Radio.d.ts.map
@@ -1,30 +1,22 @@
1
- import { ViewStyle, StyleProp } from 'react-native';
2
- export interface RadioButtonProps {
3
- /**
4
- * Whether the radio button is selected.
5
- */
6
- selected?: boolean;
7
- /**
8
- * Whether the radio button is disabled.
9
- */
10
- disabled?: boolean;
11
- /**
12
- * Function to call when the radio button is pressed.
13
- */
14
- onPress?: () => void;
15
- /**
16
- * Modes object for design-token resolution.
17
- */
18
- modes?: Record<string, any>;
19
- /**
20
- * Custom style for the radio button container.
21
- */
22
- style?: StyleProp<ViewStyle>;
23
- /**
24
- * Test ID for testing.
25
- */
26
- testID?: string;
27
- }
28
- export declare function RadioButton({ selected, disabled, onPress, modes, style, testID, }: RadioButtonProps): import("react/jsx-runtime").JSX.Element;
1
+ /**
2
+ * @deprecated `RadioButton` has been renamed to `Radio`.
3
+ *
4
+ * This file is kept as a backward-compatibility shim for teams that may be
5
+ * importing `RadioButton` directly from this deep path:
6
+ *
7
+ * import RadioButton from 'jfs-components/src/components/RadioButton/RadioButton'
8
+ * import { RadioButton, RadioButtonProps } from '...'
9
+ *
10
+ * The recommended public import is now:
11
+ *
12
+ * import { Radio, type RadioProps } from 'jfs-components'
13
+ *
14
+ * Going forward, this component is called `Radio`. This shim only re-exports
15
+ * the new `Radio` component under the old `RadioButton` names; please migrate
16
+ * existing usages to `Radio` at your earliest convenience.
17
+ */
18
+ import { Radio, type RadioProps } from '../Radio/Radio';
19
+ export type RadioButtonProps = RadioProps;
20
+ export declare const RadioButton: typeof Radio;
29
21
  export default RadioButton;
30
22
  //# sourceMappingURL=RadioButton.d.ts.map
@@ -12,6 +12,7 @@ export { default as Carousel } from './Carousel/Carousel';
12
12
  export type { CarouselProps, CarouselItemProps, PaginationProps } from './Carousel/Carousel';
13
13
  export { default as Checkbox, type CheckboxProps } from './Checkbox/Checkbox';
14
14
  export { default as CardFeedback, type CardFeedbackProps } from './CardFeedback/CardFeedback';
15
+ export { default as CardFinancialCondition, type CardFinancialConditionProps } from './CardFinancialCondition/CardFinancialCondition';
15
16
  export { default as Disclaimer } from './Disclaimer/Disclaimer';
16
17
  export { default as Divider, type DividerProps, type DividerDirection } from './Divider/Divider';
17
18
  export { default as Drawer } from './Drawer/Drawer';
@@ -65,8 +66,14 @@ export { default as ButtonGroup, type ButtonGroupProps } from './ButtonGroup/But
65
66
  export { default as CardProviderInfo, type CardProviderInfoProps } from './CardProviderInfo/CardProviderInfo';
66
67
  export { default as ChipSelect, type ChipSelectProps } from './ChipSelect/ChipSelect';
67
68
  export { default as InputSearch, type InputSearchProps } from './InputSearch/InputSearch';
69
+ export { default as InstitutionBadge, type InstitutionBadgeProps } from './InstitutionBadge/InstitutionBadge';
68
70
  export { default as SupportText, type SupportTextProps } from './SupportText/SupportText';
69
71
  export { default as SupportTextIcon, type SupportTextIconProps, type SupportTextStatus } from './SupportText/SupportTextIcon';
72
+ export { default as Radio, type RadioProps } from './Radio/Radio';
73
+ /**
74
+ * @deprecated Use `Radio` instead. `RadioButton` is kept as an alias for
75
+ * backward compatibility and will be removed in a future major release.
76
+ */
70
77
  export { default as RadioButton, type RadioButtonProps } from './RadioButton/RadioButton';
71
78
  export { default as RechargeCard, type RechargeCardProps } from './RechargeCard/RechargeCard';
72
79
  export { default as Tabs, type TabsProps } from './Tabs/Tabs';
@@ -4,7 +4,7 @@
4
4
  * Auto-generated from SVG files in src/icons/
5
5
  * DO NOT EDIT MANUALLY - Run "npm run icons:generate" to regenerate
6
6
  *
7
- * Generated: 2026-04-30T09:22:52.462Z
7
+ * Generated: 2026-05-04T12:27:11.660Z
8
8
  */
9
9
  export declare const iconRegistry: Record<string, {
10
10
  path: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jfs-components",
3
- "version": "0.0.71",
3
+ "version": "0.0.72",
4
4
  "description": "React Native Jio Finance Components Library",
5
5
  "author": "sunshuaiqi@gmail.com",
6
6
  "license": "MIT",
@@ -97,8 +97,8 @@ const toFontWeight = (value: unknown, fallback: TextStyle['fontWeight']) => {
97
97
  function resolveCardAdvisoryTokens(modes: Record<string, any>): CardAdvisoryTokens {
98
98
  const width = toNumber(getVariableByName('cardAdvisory/width', modes), 360)
99
99
  const gap = toNumber(getVariableByName('cardAdvisory/gap', modes), 16)
100
- const paddingHorizontal = toNumber(getVariableByName('cardAdvisory/padding/horizontal', modes), 16)
101
- const paddingVertical = toNumber(getVariableByName('cardAdvisory/padding/vertical', modes), 12)
100
+ const paddingHorizontal = toNumber(getVariableByName('cardAdvisory/padding/horizontal', modes), 0)
101
+ const paddingVertical = toNumber(getVariableByName('cardAdvisory/padding/vertical', modes), 0)
102
102
  const radius = toNumber(getVariableByName('cardAdvisory/radius', modes), 0)
103
103
  const background = getVariableByName('cardAdvisory/background', modes) || '#ffffff'
104
104
 
@@ -0,0 +1,366 @@
1
+ import React, { useMemo } from 'react'
2
+ import {
3
+ Text,
4
+ View,
5
+ type StyleProp,
6
+ type TextStyle,
7
+ type ViewStyle,
8
+ } from 'react-native'
9
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
10
+ import { useTokens } from '../../design-tokens/JFSThemeProvider'
11
+ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils'
12
+ import Button from '../Button/Button'
13
+ import CircularProgressBar, {
14
+ type CircularProgressBarProps,
15
+ } from '../CircularProgressBar/CircularProgressBar'
16
+ import Divider from '../Divider/Divider'
17
+ import Nudge from '../Nudge/Nudge'
18
+
19
+ type CardFinancialConditionBaseProps = Omit<
20
+ React.ComponentProps<typeof View>,
21
+ 'children' | 'style'
22
+ >
23
+
24
+ export type CardFinancialConditionProps = CardFinancialConditionBaseProps & {
25
+ /** Title text shown in the header. */
26
+ title?: string
27
+ /** Body / description shown below the title. */
28
+ body?: string
29
+ /** Progress score for the circular indicator. Forwarded to CircularProgressBar. */
30
+ value?: number
31
+ /** Visual state of the circular indicator. Forwarded to CircularProgressBar. */
32
+ progressState?: CircularProgressBarProps['state']
33
+ /** Optional formatted value label for the active state. */
34
+ valueLabel?: string
35
+ /** Show the bottom advisory nudge. */
36
+ showNudge?: boolean
37
+ /** Body text for the default nudge. Use `\n` to add a second line. */
38
+ nudgeBody?: string
39
+ /** Show the bottom divider above the action button. */
40
+ showDivider?: boolean
41
+ /** Show the action button at the bottom of the card. */
42
+ showButton?: boolean
43
+ /** Label for the default action button. */
44
+ buttonLabel?: string
45
+ /** Callback for the default action button press. */
46
+ onPressButton?: () => void
47
+ /** Slot replacing the default circular progress bar. Receives `modes` recursively. */
48
+ progressSlot?: React.ReactNode
49
+ /** Slot replacing the default Nudge. Receives `modes` recursively. */
50
+ nudgeSlot?: React.ReactNode
51
+ /** Slot replacing the default action button. Receives `modes` recursively. */
52
+ buttonSlot?: React.ReactNode
53
+ /** Design token modes forwarded to token lookups and child components. */
54
+ modes?: Record<string, any>
55
+ /** Container style override. */
56
+ style?: StyleProp<ViewStyle>
57
+ /** Header row style override. */
58
+ headerStyle?: StyleProp<ViewStyle>
59
+ /** Title text style override. */
60
+ titleStyle?: StyleProp<TextStyle>
61
+ /** Body text style override. */
62
+ bodyStyle?: StyleProp<TextStyle>
63
+ /** Accessibility label for the whole card. */
64
+ accessibilityLabel?: string
65
+ }
66
+
67
+ interface CardFinancialConditionTokens {
68
+ containerStyle: ViewStyle
69
+ headerStyle: ViewStyle
70
+ textContentStyle: ViewStyle
71
+ titleStyle: TextStyle
72
+ bodyStyle: TextStyle
73
+ nudgeBodyStyle: TextStyle
74
+ buttonWrapStyle: ViewStyle
75
+ }
76
+
77
+ const toNumber = (value: unknown, fallback: number) => {
78
+ if (typeof value === 'number') {
79
+ return Number.isFinite(value) ? value : fallback
80
+ }
81
+
82
+ if (typeof value === 'string') {
83
+ const parsed = Number(value)
84
+ return Number.isFinite(parsed) ? parsed : fallback
85
+ }
86
+
87
+ return fallback
88
+ }
89
+
90
+ const toFontWeight = (value: unknown, fallback: TextStyle['fontWeight']) => {
91
+ if (typeof value === 'number') {
92
+ return String(value) as TextStyle['fontWeight']
93
+ }
94
+
95
+ if (typeof value === 'string') {
96
+ return value as TextStyle['fontWeight']
97
+ }
98
+
99
+ return fallback
100
+ }
101
+
102
+ function resolveCardFinancialConditionTokens(
103
+ modes: Record<string, any>
104
+ ): CardFinancialConditionTokens {
105
+ const background =
106
+ (getVariableByName('financialConditionCard/background', modes) as string) ||
107
+ '#ffffff'
108
+ const radius = toNumber(
109
+ getVariableByName('financialConditionCard/radius', modes),
110
+ 16
111
+ )
112
+ const paddingH = toNumber(
113
+ getVariableByName('financialConditionCard/padding/horizontal', modes),
114
+ 16
115
+ )
116
+ const paddingV = toNumber(
117
+ getVariableByName('financialConditionCard/padding/vertical', modes),
118
+ 16
119
+ )
120
+ const gap = toNumber(
121
+ getVariableByName('financialConditionCard/gap', modes),
122
+ 8
123
+ )
124
+
125
+ const headerGap = toNumber(
126
+ getVariableByName('financialConditionCard/header/gap', modes),
127
+ 16
128
+ )
129
+ const textContentGap = toNumber(
130
+ getVariableByName('financialConditionCard/textContent/gap', modes),
131
+ 8
132
+ )
133
+
134
+ const titleColor =
135
+ (getVariableByName('financialConditionCard/title/color', modes) as string) ||
136
+ '#0c0d10'
137
+ const titleFontSize = toNumber(
138
+ getVariableByName('financialConditionCard/title/fontSize', modes),
139
+ 16
140
+ )
141
+ const titleFontFamily =
142
+ (getVariableByName(
143
+ 'financialConditionCard/title/fontFamily',
144
+ modes
145
+ ) as string) || 'JioType Var'
146
+ const titleLineHeight = toNumber(
147
+ getVariableByName('financialConditionCard/title/lineHeight', modes),
148
+ 18
149
+ )
150
+ const titleFontWeight = toFontWeight(
151
+ getVariableByName('financialConditionCard/title/fontWeight', modes),
152
+ '700'
153
+ )
154
+
155
+ const bodyColor =
156
+ (getVariableByName('financialConditionCard/body/color', modes) as string) ||
157
+ '#0c0d10'
158
+ const bodyFontSize = toNumber(
159
+ getVariableByName('financialConditionCard/body/fontSize', modes),
160
+ 14
161
+ )
162
+ const bodyFontFamily =
163
+ (getVariableByName(
164
+ 'financialConditionCard/body/fontFamily',
165
+ modes
166
+ ) as string) || 'JioType Var'
167
+ const bodyLineHeight = toNumber(
168
+ getVariableByName('financialConditionCard/body/lineHeight', modes),
169
+ 17
170
+ )
171
+ const bodyFontWeight = toFontWeight(
172
+ getVariableByName('financialConditionCard/body/fontWeight', modes),
173
+ '400'
174
+ )
175
+
176
+ const nudgeBodyColor =
177
+ (getVariableByName('nudge/body/color', modes) as string) || '#1a1c1f'
178
+ const nudgeBodyFontSize = toNumber(
179
+ getVariableByName('nudge/body/fontSize', modes),
180
+ 12
181
+ )
182
+ const nudgeBodyFontFamily =
183
+ (getVariableByName('nudge/body/fontFamily', modes) as string) || 'JioType Var'
184
+ const nudgeBodyLineHeight = toNumber(
185
+ getVariableByName('nudge/body/lineHeight', modes),
186
+ 16
187
+ )
188
+ const nudgeBodyFontWeight = toFontWeight(
189
+ getVariableByName('nudge/body/fontWeight', modes),
190
+ '500'
191
+ )
192
+
193
+ return {
194
+ containerStyle: {
195
+ alignItems: 'flex-start',
196
+ backgroundColor: background,
197
+ borderRadius: radius,
198
+ gap,
199
+ justifyContent: 'center',
200
+ overflow: 'hidden',
201
+ paddingHorizontal: paddingH,
202
+ paddingVertical: paddingV,
203
+ },
204
+ headerStyle: {
205
+ alignItems: 'center',
206
+ flexDirection: 'row',
207
+ gap: headerGap,
208
+ width: '100%',
209
+ },
210
+ textContentStyle: {
211
+ alignItems: 'flex-start',
212
+ flex: 1,
213
+ gap: textContentGap,
214
+ minWidth: 1,
215
+ },
216
+ titleStyle: {
217
+ color: titleColor,
218
+ fontFamily: titleFontFamily,
219
+ fontSize: titleFontSize,
220
+ fontWeight: titleFontWeight,
221
+ lineHeight: titleLineHeight,
222
+ },
223
+ bodyStyle: {
224
+ color: bodyColor,
225
+ fontFamily: bodyFontFamily,
226
+ fontSize: bodyFontSize,
227
+ fontWeight: bodyFontWeight,
228
+ lineHeight: bodyLineHeight,
229
+ },
230
+ nudgeBodyStyle: {
231
+ color: nudgeBodyColor,
232
+ fontFamily: nudgeBodyFontFamily,
233
+ fontSize: nudgeBodyFontSize,
234
+ fontWeight: nudgeBodyFontWeight,
235
+ lineHeight: nudgeBodyLineHeight,
236
+ },
237
+ buttonWrapStyle: {
238
+ alignSelf: 'stretch',
239
+ alignItems: 'flex-start',
240
+ },
241
+ }
242
+ }
243
+
244
+ function CardFinancialCondition({
245
+ title = 'Protection',
246
+ body = 'Check your coverage and gaps',
247
+ value = 0,
248
+ progressState = 'Inactive',
249
+ valueLabel,
250
+ showNudge = true,
251
+ nudgeBody = 'There may be gaps in your health or life cover\nAdd coverage to stay financially secure',
252
+ showDivider = true,
253
+ showButton = true,
254
+ buttonLabel = 'View Details',
255
+ onPressButton,
256
+ progressSlot,
257
+ nudgeSlot,
258
+ buttonSlot,
259
+ modes: propModes = EMPTY_MODES,
260
+ style,
261
+ headerStyle,
262
+ titleStyle,
263
+ bodyStyle,
264
+ accessibilityLabel,
265
+ ...rest
266
+ }: CardFinancialConditionProps) {
267
+ const { modes: globalModes } = useTokens()
268
+ const modes = useMemo(
269
+ () =>
270
+ globalModes === EMPTY_MODES && propModes === EMPTY_MODES
271
+ ? EMPTY_MODES
272
+ : { ...globalModes, ...propModes },
273
+ [globalModes, propModes]
274
+ )
275
+
276
+ const tokens = useMemo(
277
+ () => resolveCardFinancialConditionTokens(modes),
278
+ [modes]
279
+ )
280
+
281
+ // Match Figma: the action button is the primary call-to-action and uses the
282
+ // Secondary brand (purple) regardless of the card's overall AppearanceBrand,
283
+ // while Nudge / CircularProgressBar still follow the user-supplied modes.
284
+ const buttonModes = useMemo(
285
+ () => ({ ...modes, AppearanceBrand: 'Secondary' }),
286
+ [modes]
287
+ )
288
+
289
+ const processedProgressSlot = useMemo(() => {
290
+ if (!progressSlot) return null
291
+ const processed = cloneChildrenWithModes(
292
+ React.Children.toArray(progressSlot),
293
+ modes
294
+ )
295
+ return processed.length === 1 ? processed[0] : processed
296
+ }, [progressSlot, modes])
297
+
298
+ const processedNudgeSlot = useMemo(() => {
299
+ if (!nudgeSlot) return null
300
+ const processed = cloneChildrenWithModes(
301
+ React.Children.toArray(nudgeSlot),
302
+ modes
303
+ )
304
+ return processed.length === 1 ? processed[0] : processed
305
+ }, [nudgeSlot, modes])
306
+
307
+ const processedButtonSlot = useMemo(() => {
308
+ if (!buttonSlot) return null
309
+ const processed = cloneChildrenWithModes(
310
+ React.Children.toArray(buttonSlot),
311
+ buttonModes
312
+ )
313
+ return processed.length === 1 ? processed[0] : processed
314
+ }, [buttonSlot, buttonModes])
315
+
316
+ const defaultAccessibilityLabel =
317
+ accessibilityLabel ?? `${title}. ${body}.`
318
+
319
+ return (
320
+ <View
321
+ accessibilityLabel={defaultAccessibilityLabel}
322
+ style={[tokens.containerStyle, style]}
323
+ {...rest}
324
+ >
325
+ <View style={[tokens.headerStyle, headerStyle]}>
326
+ <View style={tokens.textContentStyle}>
327
+ <Text style={[tokens.titleStyle, titleStyle]}>{title}</Text>
328
+ <Text style={[tokens.bodyStyle, bodyStyle]}>{body}</Text>
329
+ </View>
330
+
331
+ {processedProgressSlot || (
332
+ <CircularProgressBar
333
+ state={progressState}
334
+ value={value}
335
+ modes={modes}
336
+ {...(valueLabel ? { valueLabel } : {})}
337
+ />
338
+ )}
339
+ </View>
340
+
341
+ {showNudge ? (
342
+ processedNudgeSlot || (
343
+ <Nudge type="inline-compact" modes={modes} style={{ width: '100%' }}>
344
+ <Text style={tokens.nudgeBodyStyle}>{nudgeBody}</Text>
345
+ </Nudge>
346
+ )
347
+ ) : null}
348
+
349
+ {showDivider ? <Divider modes={modes} /> : null}
350
+
351
+ {showButton ? (
352
+ <View style={tokens.buttonWrapStyle}>
353
+ {processedButtonSlot || (
354
+ <Button
355
+ label={buttonLabel}
356
+ modes={buttonModes}
357
+ {...(onPressButton ? { onPress: onPressButton } : {})}
358
+ />
359
+ )}
360
+ </View>
361
+ ) : null}
362
+ </View>
363
+ )
364
+ }
365
+
366
+ export default React.memo(CardFinancialCondition)
@@ -102,6 +102,10 @@ export function Carousel({
102
102
  const containerPaddingV = parseFloat(
103
103
  getVariableByName('carousel/padding/vertical', modes) || '0',
104
104
  )
105
+ // Outer container max height per Figma (`carousel/maxHeight`).
106
+ const maxHeight = parseFloat(
107
+ getVariableByName('carousel/maxHeight', modes) || '280',
108
+ )
105
109
  // Spacing between the cards row and the pagination dots uses `carousel/gap`.
106
110
  const paginationOffset = gap
107
111
 
@@ -251,6 +255,7 @@ export function Carousel({
251
255
  // ---- Render ----
252
256
  const outerStyle: ViewStyle = {
253
257
  paddingVertical: containerPaddingV,
258
+ maxHeight,
254
259
  }
255
260
 
256
261
  const contentContainerStyle: ViewStyle = {
@@ -364,21 +369,24 @@ export function Pagination({ modes: propModes, style }: PaginationProps) {
364
369
  const modes = propModes || ctxModes || {}
365
370
 
366
371
  // Token resolution for dots — matches Figma tokens
367
- // (carousel/pagination/gap, carousel/pagination/indicator/{activecolor,inactivecolor,radius}).
368
- // Dot dimensions are fixed per Figma spec: inactive 6x6, active 16x6.
369
- const dotSize = 6
370
- const dotActiveWidth = 16
372
+ // (carousel/pagination/gap, carousel/pagination/indicator/{activeColor,inactiveColor,radius,size,activeWidth}).
373
+ const dotSize = parseFloat(
374
+ getVariableByName('carousel/pagination/indicator/size', modes) || '6',
375
+ )
376
+ const dotActiveWidth = parseFloat(
377
+ getVariableByName('carousel/pagination/indicator/activeWidth', modes) || '16',
378
+ )
371
379
  const dotGap = parseFloat(
372
380
  getVariableByName('carousel/pagination/gap', modes) || '4',
373
381
  )
374
382
  const dotColor =
375
383
  (getVariableByName(
376
- 'carousel/pagination/indicator/inactivecolor',
384
+ 'carousel/pagination/indicator/inactiveColor',
377
385
  modes,
378
386
  ) as string) || 'rgba(0,0,0,0.3)'
379
387
  const dotActiveColor =
380
388
  (getVariableByName(
381
- 'carousel/pagination/indicator/activecolor',
389
+ 'carousel/pagination/indicator/activeColor',
382
390
  modes,
383
391
  ) as string) || '#170d0a'
384
392
  const dotRadius = parseFloat(
@@ -9,7 +9,7 @@ import {
9
9
  type ImageSourcePropType,
10
10
  } from 'react-native'
11
11
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
12
- import RadioButton from '../RadioButton/RadioButton'
12
+ import Radio from '../Radio/Radio'
13
13
  import { EMPTY_MODES } from '../../utils/react-utils'
14
14
 
15
15
  export interface HoldingsCardDetailItem {
@@ -198,7 +198,7 @@ export default function HoldingsCard({
198
198
 
199
199
  {/* Pointer events disabled — the whole card is the pressable target */}
200
200
  <View pointerEvents="none">
201
- <RadioButton
201
+ <Radio
202
202
  selected={selected}
203
203
  disabled={disabled}
204
204
  modes={modes}