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,303 @@
1
+ "use strict";
2
+
3
+ import React from 'react';
4
+ import { StyleSheet, Text, View } from 'react-native';
5
+ import Svg, { Circle } from 'react-native-svg';
6
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
7
+ import { useTokens } from '../../design-tokens/JFSThemeProvider';
8
+ import { EMPTY_MODES, flattenChildren } from '../../utils/react-utils';
9
+
10
+ /**
11
+ * Per-segment data definition for the data-driven `segments` prop.
12
+ *
13
+ * Each segment renders one arc on the donut ring. `value` controls the
14
+ * arc's angular share (its weight relative to the sum of all values).
15
+ * Use `modes` per segment to override the default `Appearance / DataViz`
16
+ * mode (the parent injects per-index defaults of `Senary`, `Primary`,
17
+ * `Secondary`, `Tertiary`, `Quaternary`, `Quinary`, then cycles).
18
+ */
19
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
20
+ const DEFAULT_APPEARANCE_CYCLE = ['Senary', 'Primary', 'Secondary', 'Tertiary', 'Quaternary', 'Quinary'];
21
+ const DEFAULT_SEGMENTS = [{
22
+ value: 1
23
+ }, {
24
+ value: 1
25
+ }, {
26
+ value: 1
27
+ }, {
28
+ value: 1
29
+ }, {
30
+ value: 1
31
+ }, {
32
+ value: 1
33
+ }];
34
+ const STROKE_WIDTH_RATIO = 18 / 194;
35
+ const toNumber = (value, fallback) => {
36
+ if (typeof value === 'number') {
37
+ return Number.isFinite(value) ? value : fallback;
38
+ }
39
+ if (typeof value === 'string') {
40
+ const parsed = Number(value);
41
+ return Number.isFinite(parsed) ? parsed : fallback;
42
+ }
43
+ return fallback;
44
+ };
45
+ const toFontWeight = (value, fallback) => {
46
+ if (typeof value === 'number') {
47
+ return String(value);
48
+ }
49
+ if (typeof value === 'string') {
50
+ return value;
51
+ }
52
+ return fallback;
53
+ };
54
+
55
+ /**
56
+ * Compute the default `Appearance / DataViz` mode for a segment at `index`.
57
+ * Cycles through {@link DEFAULT_APPEARANCE_CYCLE} so any number of segments
58
+ * gets a sensible default (Senary, Primary, Secondary, Tertiary,
59
+ * Quaternary, Quinary, then repeats).
60
+ */
61
+ function defaultAppearanceFor(index) {
62
+ return DEFAULT_APPEARANCE_CYCLE[index % DEFAULT_APPEARANCE_CYCLE.length];
63
+ }
64
+
65
+ /**
66
+ * Resolve a single segment's stroke color through the design tokens. Honors
67
+ * any explicit `color` override, then falls back to `dataViz/bg` for the
68
+ * supplied `modes`, then to the Figma reference purple.
69
+ */
70
+ function resolveSegmentColor(color, modes) {
71
+ if (color) return color;
72
+ return getVariableByName('dataViz/bg', modes) ?? '#5d00b5';
73
+ }
74
+
75
+ /**
76
+ * Coerce a slot child into a `DonutChartSegmentData` so the rendering path
77
+ * is uniform. Reads `value`, `color`, `modes` and `accessibilityLabel`
78
+ * from the child's props and ignores anything else.
79
+ */
80
+ function segmentDataFromChild(child, fallbackKey) {
81
+ const props = child.props ?? {};
82
+ return {
83
+ key: child.key ?? fallbackKey,
84
+ value: toNumber(props.value, 1),
85
+ color: typeof props.color === 'string' ? props.color : undefined,
86
+ modes: props.modes,
87
+ accessibilityLabel: props.accessibilityLabel
88
+ };
89
+ }
90
+ /**
91
+ * Inert helper used purely to author donut segments declaratively as
92
+ * `DonutChart.Segment` slot children. The parent reads its props and
93
+ * renders the actual SVG arcs — this component never renders anything by
94
+ * itself.
95
+ */
96
+ function DonutChartSegment(_) {
97
+ return null;
98
+ }
99
+
100
+ /**
101
+ * `DonutChart` renders a circular ring split into categorical segments.
102
+ * It is the segmented counterpart of `CircularProgressBar`: there is **no
103
+ * track background** behind the segments — the colored ring *is* the
104
+ * data, and each slice is a sibling category rather than a directional or
105
+ * temporal value.
106
+ *
107
+ * The default 6-segment layout receives per-index `Appearance / DataViz`
108
+ * defaults (segment 1 → `Senary`, 2 → `Primary`, 3 → `Secondary`, 4 →
109
+ * `Tertiary`, 5 → `Quaternary`, 6 → `Quinary`) so the wheel reads as one
110
+ * concept split into six color-coded categories. Override `modes` per
111
+ * segment to remix appearances, or set `Emphasis / DataViz` on the
112
+ * parent `modes` to dim or brighten the whole wheel at once.
113
+ *
114
+ * @component
115
+ * @param {DonutChartProps} props
116
+ */
117
+ function DonutChart({
118
+ segments,
119
+ value,
120
+ label,
121
+ children,
122
+ size = 194,
123
+ strokeWidth: strokeWidthProp,
124
+ gap = 0,
125
+ modes: propModes = EMPTY_MODES,
126
+ style,
127
+ valueStyle,
128
+ labelStyle,
129
+ accessibilityLabel
130
+ }) {
131
+ const {
132
+ modes: globalModes
133
+ } = useTokens();
134
+ const modes = {
135
+ ...globalModes,
136
+ ...propModes
137
+ };
138
+ const strokeWidth = Math.max(1, toNumber(strokeWidthProp, Math.max(1, size * STROKE_WIDTH_RATIO)));
139
+ const radius = Math.max(0, (size - strokeWidth) / 2);
140
+ const center = size / 2;
141
+ const circumference = 2 * Math.PI * radius;
142
+ const gapLength = Math.max(0, gap) / 360 * circumference;
143
+ const slotChildren = flattenChildren(children).filter(child => /*#__PURE__*/React.isValidElement(child));
144
+ const slotSegmentChildren = slotChildren.filter(child => child.type === DonutChartSegment);
145
+ let resolvedSegments;
146
+ if (slotSegmentChildren.length > 0) {
147
+ resolvedSegments = slotSegmentChildren.map((child, index) => segmentDataFromChild(child, `segment-${index}`));
148
+ } else if (segments && segments.length > 0) {
149
+ resolvedSegments = segments;
150
+ } else {
151
+ resolvedSegments = DEFAULT_SEGMENTS;
152
+ }
153
+ const totalValue = resolvedSegments.reduce((sum, segment) => sum + Math.max(0, toNumber(segment.value, 0)), 0);
154
+ const hasData = totalValue > 0 && resolvedSegments.length > 0;
155
+ const arcs = hasData ? buildArcs({
156
+ segments: resolvedSegments,
157
+ totalValue,
158
+ circumference,
159
+ gapLength,
160
+ modes
161
+ }) : [];
162
+ const customCenterChildren = slotChildren.filter(child => child.type !== DonutChartSegment);
163
+ const hasChildrenSlot = customCenterChildren.length > 0;
164
+ const showValueLabel = !hasChildrenSlot && shouldRenderText(value);
165
+ const showLabel = !hasChildrenSlot && shouldRenderText(label);
166
+ const valueColor = getVariableByName('donutChart/value/color', modes) ?? getVariableByName('value/fg', modes) ?? '#000000';
167
+ const valueFontFamily = getVariableByName('donutChart/value/fontFamily', modes) ?? getVariableByName('value/fontFamily', modes) ?? 'JioType Var';
168
+ const valueFontSize = toNumber(getVariableByName('donutChart/value/fontSize', modes) ?? getVariableByName('value/fontSize', modes), 29);
169
+ const valueLineHeight = toNumber(getVariableByName('donutChart/value/lineHeight', modes) ?? getVariableByName('value/lineHeight', modes), 29);
170
+ const valueFontWeight = toFontWeight(getVariableByName('donutChart/value/fontWeight', modes) ?? getVariableByName('value/fontWeight', modes), '900');
171
+ const labelColor = getVariableByName('donutChart/label/color', modes) ?? getVariableByName('label/fg', modes) ?? '#000000';
172
+ const labelFontFamily = getVariableByName('donutChart/label/fontFamily', modes) ?? getVariableByName('label/fontFamily', modes) ?? 'JioType Var';
173
+ const labelFontSize = toNumber(getVariableByName('donutChart/label/fontSize', modes) ?? getVariableByName('label/fontSize', modes), 12);
174
+ const labelLineHeight = toNumber(getVariableByName('donutChart/label/lineHeight', modes) ?? getVariableByName('label/lineHeight', modes), 15.6);
175
+ const labelFontWeight = toFontWeight(getVariableByName('donutChart/label/fontWeight', modes) ?? getVariableByName('label/fontWeight', modes), '400');
176
+ const textWrapGap = toNumber(getVariableByName('donutChart/textWrap/gap', modes) ?? getVariableByName('textWrap/gap', modes), 7);
177
+ const containerStyle = {
178
+ alignItems: 'center',
179
+ height: size,
180
+ justifyContent: 'center',
181
+ position: 'relative',
182
+ width: size
183
+ };
184
+ return /*#__PURE__*/_jsxs(View, {
185
+ accessibilityRole: "image",
186
+ accessibilityLabel: accessibilityLabel,
187
+ style: [containerStyle, style],
188
+ children: [/*#__PURE__*/_jsx(Svg, {
189
+ width: size,
190
+ height: size,
191
+ viewBox: `0 0 ${size} ${size}`,
192
+ children: arcs.map(arc => /*#__PURE__*/_jsx(Circle, {
193
+ cx: center,
194
+ cy: center,
195
+ r: radius,
196
+ stroke: arc.color,
197
+ strokeWidth: strokeWidth,
198
+ fill: "none",
199
+ strokeDasharray: arc.dashArray,
200
+ strokeDashoffset: arc.dashOffset,
201
+ strokeLinecap: "butt",
202
+ rotation: "-90",
203
+ originX: center,
204
+ originY: center
205
+ }, arc.key))
206
+ }), hasChildrenSlot ? /*#__PURE__*/_jsx(View, {
207
+ pointerEvents: "box-none",
208
+ style: StyleSheet.absoluteFill,
209
+ children: /*#__PURE__*/_jsx(View, {
210
+ style: styles.centerSlot,
211
+ children: customCenterChildren
212
+ })
213
+ }) : showValueLabel || showLabel ? /*#__PURE__*/_jsxs(View, {
214
+ pointerEvents: "none",
215
+ style: [styles.centerSlot, {
216
+ gap: textWrapGap
217
+ }],
218
+ children: [showValueLabel ? /*#__PURE__*/_jsx(Text, {
219
+ style: [{
220
+ color: valueColor,
221
+ fontFamily: valueFontFamily,
222
+ fontSize: valueFontSize,
223
+ lineHeight: valueLineHeight,
224
+ fontWeight: valueFontWeight,
225
+ textAlign: 'center'
226
+ }, valueStyle],
227
+ numberOfLines: 1,
228
+ children: value
229
+ }) : null, showLabel ? /*#__PURE__*/_jsx(Text, {
230
+ style: [{
231
+ color: labelColor,
232
+ fontFamily: labelFontFamily,
233
+ fontSize: labelFontSize,
234
+ lineHeight: labelLineHeight,
235
+ fontWeight: labelFontWeight,
236
+ textAlign: 'center'
237
+ }, labelStyle],
238
+ numberOfLines: 1,
239
+ children: label
240
+ }) : null]
241
+ }) : null]
242
+ });
243
+ }
244
+ function shouldRenderText(node) {
245
+ return node !== undefined && node !== null && node !== false && node !== '';
246
+ }
247
+ /**
248
+ * Convert the resolved segment list into a list of stroke-dasharray /
249
+ * stroke-dashoffset descriptors that can be drawn as SVG `Circle`s. Each
250
+ * arc's length is its share of `circumference`, and its rotational offset
251
+ * is the cumulative offset of all preceding segments. Visual gaps are
252
+ * applied symmetrically — half a gap is shaved from each end of the arc
253
+ * so the visible separation between two segments equals one full `gap`.
254
+ */
255
+ function buildArcs({
256
+ segments,
257
+ totalValue,
258
+ circumference,
259
+ gapLength,
260
+ modes
261
+ }) {
262
+ const arcs = [];
263
+ const halfGap = gapLength / 2;
264
+ let cumulativeOffset = 0;
265
+ segments.forEach((segment, index) => {
266
+ const safeValue = Math.max(0, toNumber(segment.value, 0));
267
+ if (safeValue <= 0) {
268
+ return;
269
+ }
270
+ const arcLength = safeValue / totalValue * circumference;
271
+ const drawnLength = Math.max(0, arcLength - gapLength);
272
+ const segmentModes = {
273
+ ...modes,
274
+ 'Appearance / DataViz': defaultAppearanceFor(index),
275
+ ...(segment.modes || {})
276
+ };
277
+ const color = resolveSegmentColor(segment.color, segmentModes);
278
+ const dashArray = `${drawnLength} ${circumference - drawnLength}`;
279
+ const dashOffset = -cumulativeOffset - halfGap;
280
+ arcs.push({
281
+ key: segment.key ?? `segment-${index}`,
282
+ color,
283
+ dashArray,
284
+ dashOffset
285
+ });
286
+ cumulativeOffset += arcLength;
287
+ });
288
+ return arcs;
289
+ }
290
+ const styles = StyleSheet.create({
291
+ centerSlot: {
292
+ alignItems: 'center',
293
+ bottom: 0,
294
+ justifyContent: 'center',
295
+ left: 0,
296
+ position: 'absolute',
297
+ right: 0,
298
+ top: 0
299
+ }
300
+ });
301
+ DonutChart.Segment = DonutChartSegment;
302
+ export { DonutChartSegment };
303
+ export default DonutChart;
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+
3
+ import React from 'react';
4
+ import { View } from 'react-native';
5
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
+ import { useTokens } from '../../design-tokens/JFSThemeProvider';
7
+ import { EMPTY_MODES } from '../../utils/react-utils';
8
+ import DonutChart from '../DonutChart/DonutChart';
9
+ import MetricLegendItem from '../MetricLegendItem/MetricLegendItem';
10
+
11
+ /**
12
+ * One row of the `DonutChartSummary`. Each item drives BOTH a donut
13
+ * segment and the matching legend row, so the segment and indicator
14
+ * always share the same color by construction.
15
+ */
16
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
+ const DEFAULT_APPEARANCE_CYCLE = ['Senary', 'Primary', 'Secondary', 'Tertiary', 'Quaternary', 'Quinary'];
18
+ const DEFAULT_ITEMS = [{
19
+ label: 'Equity',
20
+ value: 40,
21
+ displayValue: '40%'
22
+ }, {
23
+ label: 'Recommended coverage',
24
+ value: 25,
25
+ displayValue: '25%'
26
+ }, {
27
+ label: 'Additional benefits',
28
+ value: 20,
29
+ displayValue: '20%'
30
+ }, {
31
+ label: 'Cost analysis',
32
+ value: 15,
33
+ displayValue: '15%'
34
+ }];
35
+ const defaultAppearanceFor = index => DEFAULT_APPEARANCE_CYCLE[index % DEFAULT_APPEARANCE_CYCLE.length];
36
+
37
+ /**
38
+ * Resolve the shared color for an item. Honors any explicit `color`
39
+ * override, then falls back to `dataViz/bg` for the merged mode set,
40
+ * then to the Figma reference purple.
41
+ */
42
+ function resolveItemColor(parentModes, item, index) {
43
+ if (item.color) return item.color;
44
+ const itemModes = {
45
+ ...parentModes,
46
+ 'Appearance / DataViz': defaultAppearanceFor(index),
47
+ ...(item.modes || {})
48
+ };
49
+ return getVariableByName('dataViz/bg', itemModes) ?? '#5d00b5';
50
+ }
51
+
52
+ /**
53
+ * `DonutChartSummary` pairs a `DonutChart` with a vertical list of
54
+ * `MetricLegendItem` rows. The component takes a single `items` array,
55
+ * so the number of legend rows is locked in step with the number of
56
+ * donut segments by construction — every segment has exactly one
57
+ * legend row, and they share the same color through the same
58
+ * `Appearance / DataViz` cascade as the standalone `DonutChart`.
59
+ *
60
+ * The default 4-item layout receives per-index `Appearance / DataViz`
61
+ * defaults (item 1 → `Senary`, 2 → `Primary`, 3 → `Secondary`, 4 →
62
+ * `Tertiary`, then cycles). Override `modes` per item to remix, or set
63
+ * `Emphasis / DataViz` on the parent `modes` to dim or brighten the
64
+ * whole component at once.
65
+ *
66
+ * @component
67
+ * @param {DonutChartSummaryProps} props
68
+ */
69
+ function DonutChartSummary({
70
+ items,
71
+ formatValue,
72
+ centerValue = '₹51,230',
73
+ centerLabel = 'Total invested',
74
+ donutCenter,
75
+ donutSize = 194,
76
+ donutStrokeWidth,
77
+ donutGap = 0,
78
+ modes: propModes = EMPTY_MODES,
79
+ style,
80
+ legendStyle,
81
+ accessibilityLabel
82
+ }) {
83
+ const {
84
+ modes: globalModes
85
+ } = useTokens();
86
+ const modes = {
87
+ ...globalModes,
88
+ ...propModes
89
+ };
90
+ const gap = getVariableByName('donutChartSummary/gap', modes) ?? 16;
91
+ const legendGap = getVariableByName('donutChartSummary/legend/gap', modes) ?? 8;
92
+ const resolvedItems = items && items.length > 0 ? items : DEFAULT_ITEMS;
93
+ const segments = resolvedItems.map((item, index) => ({
94
+ key: item.key ?? `segment-${index}`,
95
+ value: item.value,
96
+ color: resolveItemColor(modes, item, index),
97
+ accessibilityLabel: item.accessibilityLabel
98
+ }));
99
+ const showCustomCenter = donutCenter !== undefined && donutCenter !== null;
100
+ return /*#__PURE__*/_jsxs(View, {
101
+ accessibilityRole: "summary",
102
+ accessibilityLabel: accessibilityLabel,
103
+ style: [{
104
+ width: '100%',
105
+ alignItems: 'center',
106
+ gap
107
+ }, style],
108
+ children: [/*#__PURE__*/_jsx(DonutChart, {
109
+ size: donutSize,
110
+ ...(donutStrokeWidth !== undefined && {
111
+ strokeWidth: donutStrokeWidth
112
+ }),
113
+ gap: donutGap,
114
+ segments: segments,
115
+ modes: modes,
116
+ ...(!showCustomCenter && {
117
+ value: centerValue,
118
+ label: centerLabel
119
+ }),
120
+ children: showCustomCenter ? donutCenter : null
121
+ }), /*#__PURE__*/_jsx(View, {
122
+ style: [{
123
+ width: '100%',
124
+ gap: legendGap
125
+ }, legendStyle],
126
+ children: resolvedItems.map((item, index) => /*#__PURE__*/_jsx(MetricLegendItem, {
127
+ label: item.label,
128
+ value: resolveLegendValue(item, formatValue),
129
+ indicatorColor: resolveItemColor(modes, item, index),
130
+ modes: modes
131
+ }, item.key ?? `legend-${index}`))
132
+ })]
133
+ });
134
+ }
135
+
136
+ /**
137
+ * Resolve what to render in the legend row's right-side slot. The
138
+ * order of precedence is:
139
+ * 1. `item.displayValue` if explicitly provided (including `null` /
140
+ * `false`, which the underlying `MetricLegendItem` treats as
141
+ * "hide the value slot").
142
+ * 2. `formatValue(item.value)` when a parent-level formatter exists.
143
+ * 3. `undefined` — the value slot is hidden.
144
+ */
145
+ function resolveLegendValue(item, formatValue) {
146
+ if (item.displayValue !== undefined) return item.displayValue;
147
+ if (formatValue) return formatValue(item.value);
148
+ return undefined;
149
+ }
150
+ export default DonutChartSummary;
@@ -3,7 +3,7 @@
3
3
  import React from 'react';
4
4
  import { View, Text, Pressable, Image } from 'react-native';
5
5
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
- import RadioButton from '../RadioButton/RadioButton';
6
+ import Radio from '../Radio/Radio';
7
7
  import { EMPTY_MODES } from '../../utils/react-utils';
8
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
9
  const GRID_COLUMNS = 3;
@@ -123,7 +123,7 @@ export default function HoldingsCard({
123
123
  }
124
124
  }), /*#__PURE__*/_jsx(View, {
125
125
  pointerEvents: "none",
126
- children: /*#__PURE__*/_jsx(RadioButton, {
126
+ children: /*#__PURE__*/_jsx(Radio, {
127
127
  selected: selected,
128
128
  disabled: disabled,
129
129
  modes: modes
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+
3
+ import React, { useMemo } from 'react';
4
+ import { Text, View } from 'react-native';
5
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
+ import { useTokens } from '../../design-tokens/JFSThemeProvider';
7
+ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
8
+ import MediaSource from '../../utils/MediaSource';
9
+
10
+ // Default avatar asset (shared with the Avatar component) so that
11
+ // InstitutionBadge has a sensible visual fallback when no `source` is
12
+ // provided in stories or playgrounds.
13
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
+ const DEFAULT_AVATAR_IMAGE = require('../Avatar/31595e70c4181263f9971590224b12934b280c9b.png');
15
+ const toNumber = (value, fallback) => {
16
+ if (typeof value === 'number') {
17
+ return Number.isFinite(value) ? value : fallback;
18
+ }
19
+ if (typeof value === 'string') {
20
+ const parsed = Number(value);
21
+ return Number.isFinite(parsed) ? parsed : fallback;
22
+ }
23
+ return fallback;
24
+ };
25
+ const toFontWeight = (value, fallback) => {
26
+ if (typeof value === 'number') {
27
+ return String(value);
28
+ }
29
+ if (typeof value === 'string') {
30
+ return value;
31
+ }
32
+ return fallback;
33
+ };
34
+ function resolveInstitutionBadgeTokens(modes) {
35
+ const gap = toNumber(getVariableByName('institutionBadge/gap', modes), 8);
36
+ const foreground = getVariableByName('institutionBadge/foreground', modes) || '#080d1a';
37
+ const fontSize = toNumber(getVariableByName('institutionBadge/fontSize', modes), 14);
38
+ const fontFamily = getVariableByName('institutionBadge/fontFamily', modes) || 'JioType Var';
39
+ const lineHeight = toNumber(getVariableByName('institutionBadge/lineHeight', modes), 18);
40
+ const fontWeight = toFontWeight(getVariableByName('institutionBadge/fontWeight', modes), '500');
41
+
42
+ // Avatar wrapper styled from shared `avatar/*` tokens so the badge stays
43
+ // visually consistent with other Avatar-bearing components.
44
+ const avatarSize = toNumber(getVariableByName('avatar/size', modes), 36);
45
+ const avatarRadiusRaw = toNumber(getVariableByName('avatar/radius', modes), 9999);
46
+ // 9999 is the design-token sentinel for "perfect circle".
47
+ const avatarRadius = avatarRadiusRaw === 9999 ? avatarSize / 2 : avatarRadiusRaw;
48
+ const avatarBorderColor = getVariableByName('avatar/border/color', modes) || 'rgba(255,255,255,0)';
49
+ const avatarBorderSize = toNumber(getVariableByName('avatar/border/size', modes), 1);
50
+ return {
51
+ containerStyle: {
52
+ alignItems: 'center',
53
+ flexDirection: 'row',
54
+ gap
55
+ },
56
+ avatarStyle: {
57
+ width: avatarSize,
58
+ height: avatarSize,
59
+ borderRadius: avatarRadius,
60
+ borderWidth: avatarBorderSize,
61
+ borderColor: avatarBorderColor,
62
+ overflow: 'hidden'
63
+ },
64
+ avatarSize,
65
+ labelStyle: {
66
+ color: foreground,
67
+ fontFamily,
68
+ fontSize,
69
+ fontWeight,
70
+ lineHeight
71
+ }
72
+ };
73
+ }
74
+
75
+ // Default Avatar Size = M (36px in tokens) — matches the Figma badge. Callers
76
+ // can override via `modes` if needed.
77
+ const DEFAULT_AVATAR_MODE = 'M';
78
+ function InstitutionBadge({
79
+ label = 'State Bank of India',
80
+ source,
81
+ avatarSlot,
82
+ modes: propModes = EMPTY_MODES,
83
+ style,
84
+ labelStyle,
85
+ accessibilityLabel,
86
+ ...rest
87
+ }) {
88
+ const {
89
+ modes: globalModes
90
+ } = useTokens();
91
+ const modes = useMemo(() => globalModes === EMPTY_MODES && propModes === EMPTY_MODES ? EMPTY_MODES : {
92
+ ...globalModes,
93
+ ...propModes
94
+ }, [globalModes, propModes]);
95
+ const avatarModes = useMemo(() => ({
96
+ 'Avatar Size': DEFAULT_AVATAR_MODE,
97
+ ...modes
98
+ }), [modes]);
99
+ const tokens = useMemo(() => resolveInstitutionBadgeTokens(avatarModes), [avatarModes]);
100
+ const processedAvatarSlot = useMemo(() => {
101
+ if (!avatarSlot) return null;
102
+ const processed = cloneChildrenWithModes(React.Children.toArray(avatarSlot), avatarModes);
103
+ return processed.length === 1 ? processed[0] : processed;
104
+ }, [avatarSlot, avatarModes]);
105
+ const resolvedSource = source ?? DEFAULT_AVATAR_IMAGE;
106
+ const defaultAccessibilityLabel = accessibilityLabel ?? label;
107
+ return /*#__PURE__*/_jsxs(View, {
108
+ accessibilityLabel: defaultAccessibilityLabel,
109
+ style: [tokens.containerStyle, style],
110
+ ...rest,
111
+ children: [processedAvatarSlot || /*#__PURE__*/_jsx(View, {
112
+ style: tokens.avatarStyle,
113
+ children: /*#__PURE__*/_jsx(MediaSource, {
114
+ source: resolvedSource,
115
+ size: tokens.avatarSize,
116
+ resizeMode: "cover",
117
+ accessibilityElementsHidden: true,
118
+ importantForAccessibility: "no"
119
+ })
120
+ }), /*#__PURE__*/_jsx(Text, {
121
+ style: [tokens.labelStyle, labelStyle],
122
+ numberOfLines: 1,
123
+ children: label
124
+ })]
125
+ });
126
+ }
127
+ export default /*#__PURE__*/React.memo(InstitutionBadge);
@@ -5,6 +5,7 @@ import { View, Text } from 'react-native';
5
5
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
6
  import { EMPTY_MODES } from '../../utils/react-utils';
7
7
  import MoneyValue from '../MoneyValue/MoneyValue';
8
+ import LinearProgress from '../LinearProgress/LinearProgress';
8
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
10
  const LinearMeterLabel = ({
10
11
  children,
@@ -41,14 +42,6 @@ const LinearMeter = ({
41
42
  ...rest
42
43
  }) => {
43
44
  const gap = getVariableByName('linearMeter/gap', modes);
44
- // Track tokens
45
- const trackBg = getVariableByName('linearMeter/track/background', modes);
46
- const trackHeight = getVariableByName('linearMeter/track/height', modes);
47
- const trackRadius = getVariableByName('linearMeter/track/radius', modes);
48
-
49
- // Indicator tokens
50
- const indicatorBg = getVariableByName('linearMeter/indicator/background', modes);
51
- const indicatorRadius = getVariableByName('linearMeter/indicator/radius', modes);
52
45
 
53
46
  // Wrap tokens
54
47
  const wrapGap = getVariableByName('linearMeter/wrap/gap', modes);
@@ -86,10 +79,6 @@ const LinearMeter = ({
86
79
  })]
87
80
  });
88
81
  const content = children ? childrenWithModes : defaultContent;
89
-
90
- // Calculate width percentage
91
- const clampedValue = Math.min(Math.max(value, 0), 1);
92
- const widthPercent = `${clampedValue * 100}%`;
93
82
  return /*#__PURE__*/_jsxs(View, {
94
83
  style: [{
95
84
  flexDirection: 'row',
@@ -97,22 +86,14 @@ const LinearMeter = ({
97
86
  gap
98
87
  }, style],
99
88
  ...rest,
100
- children: [/*#__PURE__*/_jsx(View, {
101
- style: [{
102
- flex: 1,
103
- height: trackHeight,
104
- backgroundColor: trackBg,
105
- borderRadius: trackRadius,
106
- overflow: 'hidden' // Ensure indicator stays inside
107
- }, trackStyle],
108
- children: /*#__PURE__*/_jsx(View, {
109
- style: [{
110
- width: widthPercent,
111
- height: '100%',
112
- backgroundColor: indicatorBg,
113
- borderRadius: indicatorRadius
114
- }, indicatorStyle]
115
- })
89
+ children: [/*#__PURE__*/_jsx(LinearProgress, {
90
+ value: value,
91
+ modes: modes,
92
+ style: {
93
+ flex: 1
94
+ },
95
+ trackStyle: trackStyle,
96
+ indicatorStyle: indicatorStyle
116
97
  }), /*#__PURE__*/_jsx(View, {
117
98
  style: {
118
99
  flexDirection: 'row',