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.
- package/CHANGELOG.md +60 -0
- package/lib/commonjs/components/AccordionCheckbox/AccordionCheckbox.js +239 -0
- package/lib/commonjs/components/BrandChip/BrandChip.js +149 -0
- package/lib/commonjs/components/CardAdvisory/CardAdvisory.js +2 -2
- package/lib/commonjs/components/CardBankAccount/CardBankAccount.js +213 -0
- package/lib/commonjs/components/CardFinancialCondition/CardFinancialCondition.js +213 -0
- package/lib/commonjs/components/CardInsight/CardInsight.js +166 -0
- package/lib/commonjs/components/Carousel/Carousel.js +9 -7
- package/lib/commonjs/components/CheckboxGroup/CheckboxGroup.js +67 -0
- package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +125 -0
- package/lib/commonjs/components/CircularProgressBar/CircularProgressBar.js +56 -9
- package/lib/commonjs/components/CoverageBarComparison/CoverageBarComparison.js +272 -0
- package/lib/commonjs/components/CoverageRing/CoverageRing.js +141 -0
- package/lib/commonjs/components/DonutChart/DonutChart.js +309 -0
- package/lib/commonjs/components/DonutChartSummary/DonutChartSummary.js +155 -0
- package/lib/commonjs/components/HoldingsCard/HoldingsCard.js +2 -2
- package/lib/commonjs/components/InstitutionBadge/InstitutionBadge.js +132 -0
- package/lib/commonjs/components/LinearMeter/LinearMeter.js +9 -28
- package/lib/commonjs/components/LinearProgress/LinearProgress.js +68 -0
- package/lib/commonjs/components/MetricLegendItem/MetricLegendItem.js +95 -0
- package/lib/commonjs/components/MonthlyStatusGrid/MonthlyStatusGrid.js +286 -0
- package/lib/commonjs/components/OTP/OTP.js +381 -37
- package/lib/commonjs/components/ProductOverview/ProductOverview.js +147 -0
- package/lib/commonjs/components/Radio/Radio.js +194 -0
- package/lib/commonjs/components/RadioButton/RadioButton.js +21 -188
- package/lib/commonjs/components/RangeTrack/RangeTrack.js +269 -0
- package/lib/commonjs/components/SavingsGoalSummary/SavingsGoalSummary.js +181 -0
- package/lib/commonjs/components/SegmentedTrack/SegmentedTrack.js +171 -0
- package/lib/commonjs/components/StatGroup/StatGroup.js +128 -0
- package/lib/commonjs/components/StatItem/StatItem.js +65 -35
- package/lib/commonjs/components/StrengthIndicator/StrengthIndicator.js +157 -0
- package/lib/commonjs/components/SummaryTile/SummaryTile.js +150 -0
- package/lib/commonjs/components/index.js +192 -1
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/commonjs/utils/index.js +7 -0
- package/lib/commonjs/utils/number-utils.js +57 -0
- package/lib/module/components/AccordionCheckbox/AccordionCheckbox.js +233 -0
- package/lib/module/components/BrandChip/BrandChip.js +143 -0
- package/lib/module/components/CardAdvisory/CardAdvisory.js +2 -2
- package/lib/module/components/CardBankAccount/CardBankAccount.js +208 -0
- package/lib/module/components/CardFinancialCondition/CardFinancialCondition.js +207 -0
- package/lib/module/components/CardInsight/CardInsight.js +161 -0
- package/lib/module/components/Carousel/Carousel.js +9 -7
- package/lib/module/components/CheckboxGroup/CheckboxGroup.js +62 -0
- package/lib/module/components/CheckboxItem/CheckboxItem.js +119 -0
- package/lib/module/components/CircularProgressBar/CircularProgressBar.js +56 -9
- package/lib/module/components/CoverageBarComparison/CoverageBarComparison.js +266 -0
- package/lib/module/components/CoverageRing/CoverageRing.js +136 -0
- package/lib/module/components/DonutChart/DonutChart.js +303 -0
- package/lib/module/components/DonutChartSummary/DonutChartSummary.js +150 -0
- package/lib/module/components/HoldingsCard/HoldingsCard.js +2 -2
- package/lib/module/components/InstitutionBadge/InstitutionBadge.js +127 -0
- package/lib/module/components/LinearMeter/LinearMeter.js +9 -28
- package/lib/module/components/LinearProgress/LinearProgress.js +63 -0
- package/lib/module/components/MetricLegendItem/MetricLegendItem.js +90 -0
- package/lib/module/components/MonthlyStatusGrid/MonthlyStatusGrid.js +281 -0
- package/lib/module/components/OTP/OTP.js +381 -38
- package/lib/module/components/ProductOverview/ProductOverview.js +142 -0
- package/lib/module/components/Radio/Radio.js +188 -0
- package/lib/module/components/RadioButton/RadioButton.js +20 -185
- package/lib/module/components/RangeTrack/RangeTrack.js +263 -0
- package/lib/module/components/SavingsGoalSummary/SavingsGoalSummary.js +175 -0
- package/lib/module/components/SegmentedTrack/SegmentedTrack.js +166 -0
- package/lib/module/components/StatGroup/StatGroup.js +123 -0
- package/lib/module/components/StatItem/StatItem.js +66 -36
- package/lib/module/components/StrengthIndicator/StrengthIndicator.js +152 -0
- package/lib/module/components/SummaryTile/SummaryTile.js +145 -0
- package/lib/module/components/index.js +28 -1
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/icons/registry.js +1 -1
- package/lib/module/utils/index.js +2 -1
- package/lib/module/utils/number-utils.js +53 -0
- package/lib/typescript/src/components/AccordionCheckbox/AccordionCheckbox.d.ts +71 -0
- package/lib/typescript/src/components/BrandChip/BrandChip.d.ts +43 -0
- package/lib/typescript/src/components/CardBankAccount/CardBankAccount.d.ts +79 -0
- package/lib/typescript/src/components/CardFinancialCondition/CardFinancialCondition.d.ts +50 -0
- package/lib/typescript/src/components/CardInsight/CardInsight.d.ts +48 -0
- package/lib/typescript/src/components/CheckboxGroup/CheckboxGroup.d.ts +41 -0
- package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +56 -0
- package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +11 -1
- package/lib/typescript/src/components/CoverageBarComparison/CoverageBarComparison.d.ts +105 -0
- package/lib/typescript/src/components/CoverageRing/CoverageRing.d.ts +90 -0
- package/lib/typescript/src/components/DonutChart/DonutChart.d.ts +117 -0
- package/lib/typescript/src/components/DonutChartSummary/DonutChartSummary.d.ts +103 -0
- package/lib/typescript/src/components/InstitutionBadge/InstitutionBadge.d.ts +30 -0
- package/lib/typescript/src/components/LinearProgress/LinearProgress.d.ts +17 -0
- package/lib/typescript/src/components/MetricLegendItem/MetricLegendItem.d.ts +37 -0
- package/lib/typescript/src/components/MonthlyStatusGrid/MonthlyStatusGrid.d.ts +119 -0
- package/lib/typescript/src/components/OTP/OTP.d.ts +88 -2
- package/lib/typescript/src/components/ProductOverview/ProductOverview.d.ts +39 -0
- package/lib/typescript/src/components/Radio/Radio.d.ts +30 -0
- package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +20 -28
- package/lib/typescript/src/components/RangeTrack/RangeTrack.d.ts +173 -0
- package/lib/typescript/src/components/SavingsGoalSummary/SavingsGoalSummary.d.ts +95 -0
- package/lib/typescript/src/components/SegmentedTrack/SegmentedTrack.d.ts +108 -0
- package/lib/typescript/src/components/StatGroup/StatGroup.d.ts +45 -0
- package/lib/typescript/src/components/StatItem/StatItem.d.ts +24 -7
- package/lib/typescript/src/components/StrengthIndicator/StrengthIndicator.d.ts +58 -0
- package/lib/typescript/src/components/SummaryTile/SummaryTile.d.ts +60 -0
- package/lib/typescript/src/components/index.d.ts +29 -2
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/lib/typescript/src/utils/index.d.ts +1 -0
- package/lib/typescript/src/utils/number-utils.d.ts +29 -0
- package/package.json +1 -1
- package/src/components/AccordionCheckbox/AccordionCheckbox.tsx +323 -0
- package/src/components/BrandChip/BrandChip.tsx +235 -0
- package/src/components/CardAdvisory/CardAdvisory.tsx +2 -2
- package/src/components/CardBankAccount/CardBankAccount.tsx +295 -0
- package/src/components/CardFinancialCondition/CardFinancialCondition.tsx +366 -0
- package/src/components/CardInsight/CardInsight.tsx +239 -0
- package/src/components/Carousel/Carousel.tsx +14 -6
- package/src/components/CheckboxGroup/CheckboxGroup.tsx +86 -0
- package/src/components/CheckboxItem/CheckboxItem.tsx +174 -0
- package/src/components/CircularProgressBar/CircularProgressBar.tsx +74 -9
- package/src/components/CoverageBarComparison/CoverageBarComparison.tsx +378 -0
- package/src/components/CoverageRing/CoverageRing.tsx +225 -0
- package/src/components/DonutChart/DonutChart.tsx +503 -0
- package/src/components/DonutChartSummary/DonutChartSummary.tsx +256 -0
- package/src/components/HoldingsCard/HoldingsCard.tsx +2 -2
- package/src/components/InstitutionBadge/InstitutionBadge.tsx +216 -0
- package/src/components/LinearMeter/LinearMeter.tsx +9 -39
- package/src/components/LinearProgress/LinearProgress.tsx +92 -0
- package/src/components/MetricLegendItem/MetricLegendItem.tsx +167 -0
- package/src/components/MonthlyStatusGrid/MonthlyStatusGrid.tsx +438 -0
- package/src/components/OTP/OTP.tsx +476 -29
- package/src/components/ProductOverview/ProductOverview.tsx +236 -0
- package/src/components/Radio/Radio.tsx +227 -0
- package/src/components/RadioButton/RadioButton.tsx +23 -225
- package/src/components/RangeTrack/RangeTrack.tsx +394 -0
- package/src/components/SavingsGoalSummary/SavingsGoalSummary.tsx +269 -0
- package/src/components/SegmentedTrack/SegmentedTrack.tsx +268 -0
- package/src/components/StatGroup/StatGroup.tsx +169 -0
- package/src/components/StatItem/StatItem.tsx +117 -40
- package/src/components/StrengthIndicator/StrengthIndicator.tsx +205 -0
- package/src/components/SummaryTile/SummaryTile.tsx +251 -0
- package/src/components/index.ts +39 -2
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- package/src/icons/registry.ts +1 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/number-utils.ts +60 -0
|
@@ -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)
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
type StyleProp,
|
|
6
|
+
type ViewStyle,
|
|
7
|
+
type TextStyle,
|
|
8
|
+
} from 'react-native'
|
|
9
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
10
|
+
import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils'
|
|
11
|
+
import Badge from '../Badge/Badge'
|
|
12
|
+
import Divider from '../Divider/Divider'
|
|
13
|
+
import Nudge from '../Nudge/Nudge'
|
|
14
|
+
import SavingsGoalSummary from '../SavingsGoalSummary/SavingsGoalSummary'
|
|
15
|
+
|
|
16
|
+
export type CardInsightProps = {
|
|
17
|
+
/** Card title rendered in the header (left of the badge). */
|
|
18
|
+
title?: string
|
|
19
|
+
/** One-line explanation rendered under the title. */
|
|
20
|
+
subtitle?: string
|
|
21
|
+
/**
|
|
22
|
+
* Header badge. Pass a string to render the default `Badge` with that
|
|
23
|
+
* label, or a custom `ReactNode` for full control. Pass `false` or
|
|
24
|
+
* `null` to hide the badge entirely.
|
|
25
|
+
*/
|
|
26
|
+
badge?: React.ReactNode | string | false
|
|
27
|
+
/**
|
|
28
|
+
* Main content slot. Defaults to a `SavingsGoalSummary` to match the
|
|
29
|
+
* Figma reference. Pass any `ReactNode` to override.
|
|
30
|
+
*/
|
|
31
|
+
children?: React.ReactNode
|
|
32
|
+
/**
|
|
33
|
+
* Footer Nudge. Pass a `ReactNode` to override the default inline-compact
|
|
34
|
+
* `Nudge`, or `false`/`null` to hide it.
|
|
35
|
+
*/
|
|
36
|
+
footer?: React.ReactNode | false
|
|
37
|
+
/**
|
|
38
|
+
* Whether to render a `Divider` between the content slot and the footer.
|
|
39
|
+
* Defaults to `true` whenever a footer is shown. Pass `false` to hide it,
|
|
40
|
+
* or a custom `ReactNode` to override.
|
|
41
|
+
*/
|
|
42
|
+
divider?: React.ReactNode | boolean
|
|
43
|
+
/** Design token modes for theming (e.g. `{ 'Color Mode': 'Light' }`). */
|
|
44
|
+
modes?: Record<string, any>
|
|
45
|
+
/** Override container styles. */
|
|
46
|
+
style?: StyleProp<ViewStyle>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* `CardInsight` renders a single insight card composed of:
|
|
51
|
+
*
|
|
52
|
+
* 1. A header with a title, subtitle and a trailing `Badge`.
|
|
53
|
+
* 2. A flexible content slot — defaults to `SavingsGoalSummary`.
|
|
54
|
+
* 3. A `Divider` separating the slot from the footer.
|
|
55
|
+
* 4. A footer slot — defaults to an inline-compact `Nudge`.
|
|
56
|
+
*
|
|
57
|
+
* @component
|
|
58
|
+
* @param {CardInsightProps} props
|
|
59
|
+
*/
|
|
60
|
+
function CardInsight({
|
|
61
|
+
title = 'Emergency savings',
|
|
62
|
+
subtitle = 'One line explanation here.',
|
|
63
|
+
badge = 'Label',
|
|
64
|
+
children,
|
|
65
|
+
footer,
|
|
66
|
+
divider,
|
|
67
|
+
modes = EMPTY_MODES,
|
|
68
|
+
style,
|
|
69
|
+
}: CardInsightProps) {
|
|
70
|
+
const background =
|
|
71
|
+
(getVariableByName('cardInsight/background', modes) as string | null) ?? '#ffffff'
|
|
72
|
+
const radius =
|
|
73
|
+
(getVariableByName('cardInsight/radius', modes) as number | null) ?? 16
|
|
74
|
+
const paddingHorizontal =
|
|
75
|
+
(getVariableByName('cardInsight/padding/horizontal', modes) as number | null) ?? 12
|
|
76
|
+
const paddingVertical =
|
|
77
|
+
(getVariableByName('cardInsight/padding/vertical', modes) as number | null) ?? 16
|
|
78
|
+
const gap = (getVariableByName('cardInsight/gap', modes) as number | null) ?? 12
|
|
79
|
+
const contentGap =
|
|
80
|
+
(getVariableByName('cardInsight/contentGap', modes) as number | null) ?? 8
|
|
81
|
+
const width = (getVariableByName('cardInsight/width', modes) as number | null) ?? 356
|
|
82
|
+
const slotBackground =
|
|
83
|
+
(getVariableByName('cardInsight/slot/background', modes) as string | null) ?? '#ffffff'
|
|
84
|
+
|
|
85
|
+
const headerGap =
|
|
86
|
+
(getVariableByName('cardInsight/header/gap', modes) as number | null) ?? 8
|
|
87
|
+
const textGap =
|
|
88
|
+
(getVariableByName('cardInsight/text/gap', modes) as number | null) ?? 4
|
|
89
|
+
|
|
90
|
+
const titleColor =
|
|
91
|
+
(getVariableByName('cardInsight/title/color', modes) as string | null) ?? '#0d0d0f'
|
|
92
|
+
const titleFontFamily =
|
|
93
|
+
(getVariableByName('cardInsight/title/fontFamily', modes) as string | null) ??
|
|
94
|
+
'JioType Var'
|
|
95
|
+
const titleFontSize =
|
|
96
|
+
(getVariableByName('cardInsight/title/fontSize', modes) as number | null) ?? 16
|
|
97
|
+
const titleLineHeight =
|
|
98
|
+
(getVariableByName('cardInsight/title/lineHeight', modes) as number | null) ?? 17.6
|
|
99
|
+
const titleFontWeightRaw =
|
|
100
|
+
getVariableByName('cardInsight/title/fontWeight', modes) ?? 700
|
|
101
|
+
const titleFontWeight = String(titleFontWeightRaw) as TextStyle['fontWeight']
|
|
102
|
+
|
|
103
|
+
const bodyColor =
|
|
104
|
+
(getVariableByName('cardInsight/body/color', modes) as string | null) ?? '#0d0d0f'
|
|
105
|
+
const bodyFontFamily =
|
|
106
|
+
(getVariableByName('cardInsight/body/fontFamily', modes) as string | null) ??
|
|
107
|
+
'JioType Var'
|
|
108
|
+
const bodyFontSize =
|
|
109
|
+
(getVariableByName('cardInsight/body/fontSize', modes) as number | null) ?? 12
|
|
110
|
+
const bodyLineHeight =
|
|
111
|
+
(getVariableByName('cardInsight/body/lineHeight', modes) as number | null) ?? 15.6
|
|
112
|
+
const bodyFontWeightRaw =
|
|
113
|
+
getVariableByName('cardInsight/body/fontWeight', modes) ?? 400
|
|
114
|
+
const bodyFontWeight = String(bodyFontWeightRaw) as TextStyle['fontWeight']
|
|
115
|
+
|
|
116
|
+
const renderBadge = (): React.ReactNode => {
|
|
117
|
+
if (badge === false || badge === null || badge === undefined) return null
|
|
118
|
+
if (typeof badge === 'string') {
|
|
119
|
+
return <Badge label={badge} modes={modes} />
|
|
120
|
+
}
|
|
121
|
+
const processed = cloneChildrenWithModes(badge, modes)
|
|
122
|
+
return processed.length === 1 ? processed[0] : processed
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const renderSlot = (): React.ReactNode => {
|
|
126
|
+
if (!children) {
|
|
127
|
+
return <SavingsGoalSummary modes={modes} />
|
|
128
|
+
}
|
|
129
|
+
const processed = cloneChildrenWithModes(children, modes)
|
|
130
|
+
return processed.length === 1 ? processed[0] : processed
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const renderFooter = (): React.ReactNode => {
|
|
134
|
+
if (footer === false || footer === null) return null
|
|
135
|
+
if (footer === undefined) {
|
|
136
|
+
return (
|
|
137
|
+
<Nudge
|
|
138
|
+
type="inline-compact"
|
|
139
|
+
modes={{ AppearanceBrand: 'Neutral', Context: 'Nudge&Alert', ...modes }}
|
|
140
|
+
/>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
const processed = cloneChildrenWithModes(footer, modes)
|
|
144
|
+
return processed.length === 1 ? processed[0] : processed
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const renderDivider = (): React.ReactNode => {
|
|
148
|
+
if (divider === false || divider === null) return null
|
|
149
|
+
if (divider === undefined || divider === true) {
|
|
150
|
+
return <Divider modes={modes} />
|
|
151
|
+
}
|
|
152
|
+
const processed = cloneChildrenWithModes(divider, modes)
|
|
153
|
+
return processed.length === 1 ? processed[0] : processed
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const footerNode = renderFooter()
|
|
157
|
+
const showDivider = footerNode !== null && divider !== false && divider !== null
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<View
|
|
161
|
+
style={[
|
|
162
|
+
{
|
|
163
|
+
width,
|
|
164
|
+
backgroundColor: background,
|
|
165
|
+
borderRadius: radius,
|
|
166
|
+
paddingHorizontal,
|
|
167
|
+
paddingVertical,
|
|
168
|
+
gap,
|
|
169
|
+
overflow: 'hidden',
|
|
170
|
+
alignItems: 'stretch',
|
|
171
|
+
},
|
|
172
|
+
style,
|
|
173
|
+
]}
|
|
174
|
+
>
|
|
175
|
+
<View
|
|
176
|
+
style={{
|
|
177
|
+
flexDirection: 'row',
|
|
178
|
+
alignItems: 'flex-start',
|
|
179
|
+
gap: headerGap,
|
|
180
|
+
width: '100%',
|
|
181
|
+
}}
|
|
182
|
+
>
|
|
183
|
+
<View
|
|
184
|
+
style={{
|
|
185
|
+
flex: 1,
|
|
186
|
+
minWidth: 0,
|
|
187
|
+
gap: textGap,
|
|
188
|
+
alignItems: 'flex-start',
|
|
189
|
+
}}
|
|
190
|
+
>
|
|
191
|
+
<Text
|
|
192
|
+
style={{
|
|
193
|
+
color: titleColor,
|
|
194
|
+
fontFamily: titleFontFamily,
|
|
195
|
+
fontSize: titleFontSize,
|
|
196
|
+
lineHeight: titleLineHeight,
|
|
197
|
+
fontWeight: titleFontWeight,
|
|
198
|
+
width: '100%',
|
|
199
|
+
}}
|
|
200
|
+
>
|
|
201
|
+
{title}
|
|
202
|
+
</Text>
|
|
203
|
+
<Text
|
|
204
|
+
style={{
|
|
205
|
+
color: bodyColor,
|
|
206
|
+
fontFamily: bodyFontFamily,
|
|
207
|
+
fontSize: bodyFontSize,
|
|
208
|
+
lineHeight: bodyLineHeight,
|
|
209
|
+
fontWeight: bodyFontWeight,
|
|
210
|
+
width: '100%',
|
|
211
|
+
}}
|
|
212
|
+
>
|
|
213
|
+
{subtitle}
|
|
214
|
+
</Text>
|
|
215
|
+
</View>
|
|
216
|
+
|
|
217
|
+
{renderBadge()}
|
|
218
|
+
</View>
|
|
219
|
+
|
|
220
|
+
<View
|
|
221
|
+
style={{
|
|
222
|
+
width: '100%',
|
|
223
|
+
backgroundColor: slotBackground,
|
|
224
|
+
gap: contentGap,
|
|
225
|
+
overflow: 'hidden',
|
|
226
|
+
alignItems: 'stretch',
|
|
227
|
+
}}
|
|
228
|
+
>
|
|
229
|
+
{renderSlot()}
|
|
230
|
+
</View>
|
|
231
|
+
|
|
232
|
+
{showDivider ? renderDivider() : null}
|
|
233
|
+
|
|
234
|
+
{footerNode}
|
|
235
|
+
</View>
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export default CardInsight
|
|
@@ -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/{
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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/
|
|
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/
|
|
389
|
+
'carousel/pagination/indicator/activeColor',
|
|
382
390
|
modes,
|
|
383
391
|
) as string) || '#170d0a'
|
|
384
392
|
const dotRadius = parseFloat(
|