jfs-components 0.0.72 → 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 (116) hide show
  1. package/CHANGELOG.md +11 -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/CardBankAccount/CardBankAccount.js +213 -0
  5. package/lib/commonjs/components/CardInsight/CardInsight.js +166 -0
  6. package/lib/commonjs/components/CheckboxGroup/CheckboxGroup.js +67 -0
  7. package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +125 -0
  8. package/lib/commonjs/components/CircularProgressBar/CircularProgressBar.js +56 -9
  9. package/lib/commonjs/components/CoverageBarComparison/CoverageBarComparison.js +272 -0
  10. package/lib/commonjs/components/CoverageRing/CoverageRing.js +141 -0
  11. package/lib/commonjs/components/DonutChart/DonutChart.js +309 -0
  12. package/lib/commonjs/components/DonutChartSummary/DonutChartSummary.js +155 -0
  13. package/lib/commonjs/components/LinearMeter/LinearMeter.js +9 -28
  14. package/lib/commonjs/components/LinearProgress/LinearProgress.js +68 -0
  15. package/lib/commonjs/components/MetricLegendItem/MetricLegendItem.js +95 -0
  16. package/lib/commonjs/components/MonthlyStatusGrid/MonthlyStatusGrid.js +286 -0
  17. package/lib/commonjs/components/OTP/OTP.js +381 -37
  18. package/lib/commonjs/components/ProductOverview/ProductOverview.js +147 -0
  19. package/lib/commonjs/components/RangeTrack/RangeTrack.js +269 -0
  20. package/lib/commonjs/components/SavingsGoalSummary/SavingsGoalSummary.js +181 -0
  21. package/lib/commonjs/components/SegmentedTrack/SegmentedTrack.js +171 -0
  22. package/lib/commonjs/components/StatGroup/StatGroup.js +128 -0
  23. package/lib/commonjs/components/StatItem/StatItem.js +65 -35
  24. package/lib/commonjs/components/StrengthIndicator/StrengthIndicator.js +157 -0
  25. package/lib/commonjs/components/SummaryTile/SummaryTile.js +150 -0
  26. package/lib/commonjs/components/index.js +171 -1
  27. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  28. package/lib/commonjs/icons/registry.js +1 -1
  29. package/lib/commonjs/utils/index.js +7 -0
  30. package/lib/commonjs/utils/number-utils.js +57 -0
  31. package/lib/module/components/AccordionCheckbox/AccordionCheckbox.js +233 -0
  32. package/lib/module/components/BrandChip/BrandChip.js +143 -0
  33. package/lib/module/components/CardBankAccount/CardBankAccount.js +208 -0
  34. package/lib/module/components/CardInsight/CardInsight.js +161 -0
  35. package/lib/module/components/CheckboxGroup/CheckboxGroup.js +62 -0
  36. package/lib/module/components/CheckboxItem/CheckboxItem.js +119 -0
  37. package/lib/module/components/CircularProgressBar/CircularProgressBar.js +56 -9
  38. package/lib/module/components/CoverageBarComparison/CoverageBarComparison.js +266 -0
  39. package/lib/module/components/CoverageRing/CoverageRing.js +136 -0
  40. package/lib/module/components/DonutChart/DonutChart.js +303 -0
  41. package/lib/module/components/DonutChartSummary/DonutChartSummary.js +150 -0
  42. package/lib/module/components/LinearMeter/LinearMeter.js +9 -28
  43. package/lib/module/components/LinearProgress/LinearProgress.js +63 -0
  44. package/lib/module/components/MetricLegendItem/MetricLegendItem.js +90 -0
  45. package/lib/module/components/MonthlyStatusGrid/MonthlyStatusGrid.js +281 -0
  46. package/lib/module/components/OTP/OTP.js +381 -38
  47. package/lib/module/components/ProductOverview/ProductOverview.js +142 -0
  48. package/lib/module/components/RangeTrack/RangeTrack.js +263 -0
  49. package/lib/module/components/SavingsGoalSummary/SavingsGoalSummary.js +175 -0
  50. package/lib/module/components/SegmentedTrack/SegmentedTrack.js +166 -0
  51. package/lib/module/components/StatGroup/StatGroup.js +123 -0
  52. package/lib/module/components/StatItem/StatItem.js +66 -36
  53. package/lib/module/components/StrengthIndicator/StrengthIndicator.js +152 -0
  54. package/lib/module/components/SummaryTile/SummaryTile.js +145 -0
  55. package/lib/module/components/index.js +21 -1
  56. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  57. package/lib/module/icons/registry.js +1 -1
  58. package/lib/module/utils/index.js +2 -1
  59. package/lib/module/utils/number-utils.js +53 -0
  60. package/lib/typescript/src/components/AccordionCheckbox/AccordionCheckbox.d.ts +71 -0
  61. package/lib/typescript/src/components/BrandChip/BrandChip.d.ts +43 -0
  62. package/lib/typescript/src/components/CardBankAccount/CardBankAccount.d.ts +79 -0
  63. package/lib/typescript/src/components/CardInsight/CardInsight.d.ts +48 -0
  64. package/lib/typescript/src/components/CheckboxGroup/CheckboxGroup.d.ts +41 -0
  65. package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +56 -0
  66. package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +11 -1
  67. package/lib/typescript/src/components/CoverageBarComparison/CoverageBarComparison.d.ts +105 -0
  68. package/lib/typescript/src/components/CoverageRing/CoverageRing.d.ts +90 -0
  69. package/lib/typescript/src/components/DonutChart/DonutChart.d.ts +117 -0
  70. package/lib/typescript/src/components/DonutChartSummary/DonutChartSummary.d.ts +103 -0
  71. package/lib/typescript/src/components/LinearProgress/LinearProgress.d.ts +17 -0
  72. package/lib/typescript/src/components/MetricLegendItem/MetricLegendItem.d.ts +37 -0
  73. package/lib/typescript/src/components/MonthlyStatusGrid/MonthlyStatusGrid.d.ts +119 -0
  74. package/lib/typescript/src/components/OTP/OTP.d.ts +88 -2
  75. package/lib/typescript/src/components/ProductOverview/ProductOverview.d.ts +39 -0
  76. package/lib/typescript/src/components/RangeTrack/RangeTrack.d.ts +173 -0
  77. package/lib/typescript/src/components/SavingsGoalSummary/SavingsGoalSummary.d.ts +95 -0
  78. package/lib/typescript/src/components/SegmentedTrack/SegmentedTrack.d.ts +108 -0
  79. package/lib/typescript/src/components/StatGroup/StatGroup.d.ts +45 -0
  80. package/lib/typescript/src/components/StatItem/StatItem.d.ts +24 -7
  81. package/lib/typescript/src/components/StrengthIndicator/StrengthIndicator.d.ts +58 -0
  82. package/lib/typescript/src/components/SummaryTile/SummaryTile.d.ts +60 -0
  83. package/lib/typescript/src/components/index.d.ts +22 -2
  84. package/lib/typescript/src/icons/registry.d.ts +1 -1
  85. package/lib/typescript/src/utils/index.d.ts +1 -0
  86. package/lib/typescript/src/utils/number-utils.d.ts +29 -0
  87. package/package.json +1 -1
  88. package/src/components/AccordionCheckbox/AccordionCheckbox.tsx +323 -0
  89. package/src/components/BrandChip/BrandChip.tsx +235 -0
  90. package/src/components/CardBankAccount/CardBankAccount.tsx +295 -0
  91. package/src/components/CardInsight/CardInsight.tsx +239 -0
  92. package/src/components/CheckboxGroup/CheckboxGroup.tsx +86 -0
  93. package/src/components/CheckboxItem/CheckboxItem.tsx +174 -0
  94. package/src/components/CircularProgressBar/CircularProgressBar.tsx +74 -9
  95. package/src/components/CoverageBarComparison/CoverageBarComparison.tsx +378 -0
  96. package/src/components/CoverageRing/CoverageRing.tsx +225 -0
  97. package/src/components/DonutChart/DonutChart.tsx +503 -0
  98. package/src/components/DonutChartSummary/DonutChartSummary.tsx +256 -0
  99. package/src/components/LinearMeter/LinearMeter.tsx +9 -39
  100. package/src/components/LinearProgress/LinearProgress.tsx +92 -0
  101. package/src/components/MetricLegendItem/MetricLegendItem.tsx +167 -0
  102. package/src/components/MonthlyStatusGrid/MonthlyStatusGrid.tsx +438 -0
  103. package/src/components/OTP/OTP.tsx +476 -29
  104. package/src/components/ProductOverview/ProductOverview.tsx +236 -0
  105. package/src/components/RangeTrack/RangeTrack.tsx +394 -0
  106. package/src/components/SavingsGoalSummary/SavingsGoalSummary.tsx +269 -0
  107. package/src/components/SegmentedTrack/SegmentedTrack.tsx +268 -0
  108. package/src/components/StatGroup/StatGroup.tsx +169 -0
  109. package/src/components/StatItem/StatItem.tsx +117 -40
  110. package/src/components/StrengthIndicator/StrengthIndicator.tsx +205 -0
  111. package/src/components/SummaryTile/SummaryTile.tsx +251 -0
  112. package/src/components/index.ts +32 -2
  113. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  114. package/src/icons/registry.ts +1 -1
  115. package/src/utils/index.ts +1 -0
  116. package/src/utils/number-utils.ts +60 -0
@@ -0,0 +1,272 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _reactNativeSvg = _interopRequireWildcard(require("react-native-svg"));
10
+ var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
11
+ var _JFSThemeProvider = require("../../design-tokens/JFSThemeProvider");
12
+ var _reactUtils = require("../../utils/react-utils");
13
+ var _MetricLegendItem = _interopRequireDefault(require("../MetricLegendItem/MetricLegendItem"));
14
+ var _jsxRuntime = require("react/jsx-runtime");
15
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
16
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
+ /**
18
+ * One entry in the {@link CoverageBarComparisonProps.bars} array.
19
+ *
20
+ * Each entry carries **both** the bar's data and its legend label, so the
21
+ * number of bars is intrinsically equal to the number of legend items.
22
+ * There is no separate `legends` prop — that is by design.
23
+ */
24
+
25
+ /**
26
+ * Default per-index `Emphasis / DataViz` modes applied when the caller does
27
+ * not provide its own override. Ascends Low → Medium → High so the natural
28
+ * "Current vs Recommended" two-bar story matches the Figma reference (left
29
+ * = lighter / Low, right = darker / High). Cycles for >3 bars.
30
+ */
31
+ const DEFAULT_EMPHASIS_CYCLE = ['Low', 'Medium', 'High'];
32
+ function defaultEmphasisFor(index, total) {
33
+ if (total <= 1) {
34
+ return 'High';
35
+ }
36
+ if (total === 2) {
37
+ return index === 0 ? 'Low' : 'High';
38
+ }
39
+ return DEFAULT_EMPHASIS_CYCLE[index % DEFAULT_EMPHASIS_CYCLE.length];
40
+ }
41
+ const DEFAULT_BARS = [{
42
+ value: 40,
43
+ label: '₹1L (40%)',
44
+ legend: 'Current coverage'
45
+ }, {
46
+ value: 100,
47
+ label: '₹2.5L (100%)',
48
+ legend: 'Recommended coverage'
49
+ }];
50
+ const toNumber = (value, fallback) => {
51
+ if (typeof value === 'number') {
52
+ return Number.isFinite(value) ? value : fallback;
53
+ }
54
+ if (typeof value === 'string') {
55
+ const parsed = Number(value);
56
+ return Number.isFinite(parsed) ? parsed : fallback;
57
+ }
58
+ return fallback;
59
+ };
60
+ const toFontWeight = (value, fallback) => {
61
+ if (typeof value === 'number') {
62
+ return String(value);
63
+ }
64
+ if (typeof value === 'string') {
65
+ return value;
66
+ }
67
+ return fallback;
68
+ };
69
+ /**
70
+ * Bar shape rendered with `react-native-svg`. The wrapper measures its width
71
+ * via `onLayout` so the SVG can use concrete dimensions (RN SVG does not
72
+ * resolve percentage shape attributes when the parent SVG itself has a
73
+ * percentage `width`).
74
+ */
75
+ function BarShape({
76
+ height,
77
+ color,
78
+ radius
79
+ }) {
80
+ const [width, setWidth] = _react.default.useState(0);
81
+ const handleLayout = _react.default.useCallback(event => {
82
+ const next = event.nativeEvent.layout.width;
83
+ setWidth(prev => prev === next ? prev : next);
84
+ }, []);
85
+ if (height <= 0) {
86
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
87
+ onLayout: handleLayout,
88
+ style: {
89
+ width: '100%',
90
+ height: 0
91
+ }
92
+ });
93
+ }
94
+ const safeRadius = Math.max(0, Math.min(radius, width / 2, height / 2));
95
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
96
+ onLayout: handleLayout,
97
+ style: {
98
+ width: '100%',
99
+ height,
100
+ minWidth: 1
101
+ },
102
+ children: width > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.default, {
103
+ width: width,
104
+ height: height,
105
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.Rect, {
106
+ x: 0,
107
+ y: 0,
108
+ width: width,
109
+ height: height,
110
+ rx: safeRadius,
111
+ ry: safeRadius,
112
+ fill: color
113
+ })
114
+ }) : null
115
+ });
116
+ }
117
+
118
+ /**
119
+ * `CoverageBarComparison` renders a small vertical-bar chart that compares
120
+ * 2+ values side by side, with a legend row directly below where each item
121
+ * is intrinsically tied to one bar.
122
+ *
123
+ * Cohesiveness guarantees:
124
+ * - The legend row is **derived** from the same `bars` prop, so the count
125
+ * and order can never desynchronise from the chart.
126
+ * - Each bar's color, its tokenized `Emphasis / DataViz` mode and its
127
+ * legend indicator dot are the same value.
128
+ *
129
+ * Bars are drawn with `react-native-svg` (`<Rect>` with `rx`), and the
130
+ * fonts/spacing/colors are sourced from the Figma `valueBar/*` and
131
+ * `coverageBarComparison/*` tokens.
132
+ *
133
+ * @component
134
+ */
135
+ function CoverageBarComparison({
136
+ bars = DEFAULT_BARS,
137
+ max,
138
+ height = 100,
139
+ legendGap = 12,
140
+ modes: propModes = _reactUtils.EMPTY_MODES,
141
+ style,
142
+ chartStyle,
143
+ legendStyle,
144
+ labelStyle,
145
+ accessibilityLabel
146
+ }) {
147
+ const {
148
+ modes: globalModes
149
+ } = (0, _JFSThemeProvider.useTokens)();
150
+ const modes = _react.default.useMemo(() => ({
151
+ ...globalModes,
152
+ ...propModes
153
+ }), [globalModes, propModes]);
154
+ const wrapGap = toNumber((0, _figmaVariablesResolver.getVariableByName)('coverageBarComparison/wrap/gap', modes), 6);
155
+ const valueBarGap = toNumber((0, _figmaVariablesResolver.getVariableByName)('valueBar/gap', modes), 4);
156
+ const barRadius = toNumber((0, _figmaVariablesResolver.getVariableByName)('valueBar/bar/radius', modes), 10);
157
+ const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('valueBar/fontFamily', modes) ?? 'JioType Var';
158
+ const fontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('valueBar/fontSize', modes), 12);
159
+ const lineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('valueBar/lineHeight', modes), 16);
160
+ const fontWeight = toFontWeight((0, _figmaVariablesResolver.getVariableByName)('valueBar/fontWeight', modes), '700');
161
+ const foreground = (0, _figmaVariablesResolver.getVariableByName)('valueBar/foreground', modes) ?? '#000000';
162
+ const labelHeight = lineHeight;
163
+ const barAreaHeight = Math.max(0, height - labelHeight - valueBarGap);
164
+ const total = bars.length;
165
+ const computedMax = max ?? bars.reduce((acc, bar) => Math.max(acc, bar.value), 0);
166
+ const safeMax = computedMax > 0 ? computedMax : 1;
167
+ const resolvedBars = _react.default.useMemo(() => bars.map((bar, index) => {
168
+ const barModes = {
169
+ ...modes,
170
+ 'Emphasis / DataViz': defaultEmphasisFor(index, total),
171
+ ...(bar.modes || {})
172
+ };
173
+ const ratio = Math.max(0, Math.min(1, bar.value / safeMax));
174
+ const tokenColor = (0, _figmaVariablesResolver.getVariableByName)('valueBar/bar/background', barModes) ?? '#c9b7ff';
175
+ const bgColor = bar.color ?? tokenColor;
176
+ return {
177
+ original: bar,
178
+ index,
179
+ barModes,
180
+ ratio,
181
+ bgColor
182
+ };
183
+ }), [bars, modes, safeMax, total]);
184
+ const computedLabelStyle = {
185
+ color: foreground,
186
+ fontFamily,
187
+ fontSize,
188
+ lineHeight,
189
+ fontWeight,
190
+ textAlign: 'center'
191
+ };
192
+ const defaultAccessibilityLabel = accessibilityLabel ?? `Comparison of ${total} bar${total === 1 ? '' : 's'}: ` + bars.map((bar, i) => {
193
+ const legend = typeof bar.legend === 'string' ? bar.legend : `bar ${i + 1}`;
194
+ return `${legend} ${bar.value}`;
195
+ }).join(', ');
196
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
197
+ accessibilityLabel: defaultAccessibilityLabel,
198
+ style: [{
199
+ width: '100%'
200
+ }, style],
201
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
202
+ accessibilityRole: "image",
203
+ style: [{
204
+ flexDirection: 'row',
205
+ alignItems: 'flex-end',
206
+ height,
207
+ gap: wrapGap,
208
+ width: '100%'
209
+ }, chartStyle],
210
+ children: resolvedBars.map(({
211
+ original,
212
+ index,
213
+ ratio,
214
+ bgColor
215
+ }) => {
216
+ const barHeightPx = Math.max(0, barAreaHeight * ratio);
217
+ const valueBarTotalHeight = labelHeight + valueBarGap + barHeightPx;
218
+ const hasLabel = original.label !== undefined && original.label !== null && original.label !== false;
219
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
220
+ accessibilityLabel: original.accessibilityLabel,
221
+ style: {
222
+ flex: 1,
223
+ flexDirection: 'column',
224
+ alignItems: 'stretch',
225
+ justifyContent: 'flex-end',
226
+ height: valueBarTotalHeight,
227
+ gap: valueBarGap,
228
+ minWidth: 1
229
+ },
230
+ children: [hasLabel ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
231
+ numberOfLines: 1,
232
+ style: [computedLabelStyle, labelStyle],
233
+ children: original.label
234
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(BarShape, {
235
+ height: barHeightPx,
236
+ color: bgColor,
237
+ radius: barRadius
238
+ })]
239
+ }, original.key ?? `bar-${index}`);
240
+ })
241
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
242
+ style: [{
243
+ marginTop: legendGap,
244
+ flexDirection: 'row',
245
+ alignItems: 'center',
246
+ justifyContent: 'space-between',
247
+ width: '100%'
248
+ }, legendStyle],
249
+ children: resolvedBars.map(({
250
+ original,
251
+ index,
252
+ barModes,
253
+ bgColor
254
+ }) => {
255
+ const indicatorOverride = original.color ? bgColor : undefined;
256
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_MetricLegendItem.default, {
257
+ label: original.legend,
258
+ value: original.legendValue,
259
+ modes: barModes,
260
+ ...(indicatorOverride !== undefined ? {
261
+ indicatorColor: indicatorOverride
262
+ } : {}),
263
+ style: {
264
+ flex: 1,
265
+ minWidth: 0
266
+ }
267
+ }, original.key ?? `legend-${index}`);
268
+ })
269
+ })]
270
+ });
271
+ }
272
+ var _default = exports.default = CoverageBarComparison;
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
10
+ var _JFSThemeProvider = require("../../design-tokens/JFSThemeProvider");
11
+ var _reactUtils = require("../../utils/react-utils");
12
+ var _Button = _interopRequireDefault(require("../Button/Button"));
13
+ var _CircularProgressBar = _interopRequireDefault(require("../CircularProgressBar/CircularProgressBar"));
14
+ var _jsxRuntime = require("react/jsx-runtime");
15
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
+ const toNumber = (value, fallback) => {
17
+ if (typeof value === 'number') {
18
+ return Number.isFinite(value) ? value : fallback;
19
+ }
20
+ if (typeof value === 'string') {
21
+ const parsed = Number(value);
22
+ return Number.isFinite(parsed) ? parsed : fallback;
23
+ }
24
+ return fallback;
25
+ };
26
+ const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
27
+
28
+ // The Figma "Coverage Ring" pairs a `circularProgressBar Size: M` ring with a
29
+ // `Button / Size: S` Secondary button. Locking these in as component defaults
30
+ // keeps the visual identical to the Figma reference when the caller supplies
31
+ // only their own brand/theme modes — caller modes are still merged on top so
32
+ // any of these can be overridden per-instance.
33
+ const COMPONENT_DEFAULT_MODES = Object.freeze({
34
+ 'circularProgressBar Size': 'M',
35
+ 'Button / Size': 'S'
36
+ });
37
+
38
+ // Match the Figma source: regardless of the overall AppearanceBrand/Emphasis
39
+ // the caller picks (used to colour the ring), the Coverage Ring CTA renders
40
+ // with the Secondary brand at Medium emphasis — which resolves to the
41
+ // pale-lavender background (`#dbcfff`) + deep-purple foreground (`#5d00b5`)
42
+ // shown in Figma. Mirrors the pattern used by `CardFinancialCondition.tsx`.
43
+ const BUTTON_FORCED_BRAND = Object.freeze({
44
+ AppearanceBrand: 'Secondary',
45
+ Emphasis: 'Medium'
46
+ });
47
+
48
+ /**
49
+ * `CoverageRing` renders a single-purpose insight: how many items out of a
50
+ * total have been covered (e.g. "4 of 7 benefits availed"), paired with a
51
+ * call-to-action button below the ring.
52
+ *
53
+ * It composes the existing {@link CircularProgressBar} (in its `M` size so
54
+ * there is room for a caption + value inside the ring) and {@link Button}
55
+ * (in its `S`, Secondary brand variant) — both driven by the same design
56
+ * tokens used by the rest of the design system.
57
+ *
58
+ * The ring fill is derived from `value / total`, so the percentage, the
59
+ * displayed `"{value} of {total}"` label and the support caption stay in
60
+ * sync automatically. There is no separate `progress` prop.
61
+ *
62
+ * @component
63
+ */
64
+ function CoverageRing({
65
+ value = 4,
66
+ total = 7,
67
+ supportText = 'Benefits availed',
68
+ valueLabel,
69
+ actionLabel = 'Learn more',
70
+ onActionPress,
71
+ actionProps,
72
+ action,
73
+ children,
74
+ modes: propModes = _reactUtils.EMPTY_MODES,
75
+ style,
76
+ supportTextStyle,
77
+ valueStyle,
78
+ accessibilityLabel,
79
+ ...rest
80
+ }) {
81
+ const {
82
+ modes: globalModes
83
+ } = (0, _JFSThemeProvider.useTokens)();
84
+
85
+ // Merge order matches the rest of the design system (see `Gauge.tsx`):
86
+ // 1. Component-level defaults — the lowest-priority safety net.
87
+ // 2. Global modes from `JFSThemeProvider` — app-wide theme.
88
+ // 3. Caller-supplied `propModes` — highest priority, overrides everything.
89
+ const modes = _react.default.useMemo(() => ({
90
+ ...COMPONENT_DEFAULT_MODES,
91
+ ...globalModes,
92
+ ...propModes
93
+ }), [propModes, globalModes]);
94
+
95
+ // Force the Secondary brand on the button only; the ring stays on whatever
96
+ // brand the caller selected. Done via a forcedModes layer so callers can't
97
+ // accidentally bring back a Primary-coloured CTA through their own modes.
98
+ const buttonModes = _react.default.useMemo(() => ({
99
+ ...modes,
100
+ ...BUTTON_FORCED_BRAND
101
+ }), [modes]);
102
+ const safeTotal = total > 0 ? total : 0;
103
+ const clampedValue = clamp(value, 0, safeTotal);
104
+ const progressPercent = safeTotal > 0 ? clampedValue / safeTotal * 100 : 0;
105
+ const computedValueLabel = valueLabel ?? `${clampedValue} of ${safeTotal}`;
106
+ const gap = toNumber((0, _figmaVariablesResolver.getVariableByName)('coverageRing/gap', modes), 16);
107
+ const containerStyle = {
108
+ alignItems: 'center',
109
+ gap
110
+ };
111
+
112
+ // Custom action slot resolution order: explicit `action` prop wins over
113
+ // `children`. We pass `modes` down via cloneChildrenWithModes so any
114
+ // token-driven child (e.g. a custom `<Button>`) inherits the parent theme.
115
+ const customAction = action ?? children;
116
+ const hasCustomAction = customAction !== undefined && customAction !== null;
117
+ const defaultAccessibilityLabel = accessibilityLabel ?? `${supportText}, ${computedValueLabel}`;
118
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
119
+ accessibilityLabel: defaultAccessibilityLabel,
120
+ style: [containerStyle, style],
121
+ ...rest,
122
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_CircularProgressBar.default, {
123
+ state: "Active",
124
+ value: progressPercent,
125
+ valueLabel: computedValueLabel,
126
+ supportText: supportText,
127
+ supportTextStyle: supportTextStyle,
128
+ valueStyle: valueStyle,
129
+ modes: modes,
130
+ accessibilityLabel: `${supportText}, ${computedValueLabel}`
131
+ }), hasCustomAction ? (0, _reactUtils.cloneChildrenWithModes)(customAction, buttonModes) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
132
+ label: actionLabel,
133
+ modes: buttonModes,
134
+ ...(onActionPress !== undefined ? {
135
+ onPress: onActionPress
136
+ } : {}),
137
+ ...actionProps
138
+ })]
139
+ });
140
+ }
141
+ var _default = exports.default = CoverageRing;