jfs-components 0.0.72 → 0.0.74

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 (158) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/lib/commonjs/components/AccordionCheckbox/AccordionCheckbox.js +239 -0
  3. package/lib/commonjs/components/AccountCard/AccountCard.js +247 -0
  4. package/lib/commonjs/components/AppBar/AppBar.js +17 -11
  5. package/lib/commonjs/components/BrandChip/BrandChip.js +149 -0
  6. package/lib/commonjs/components/CardBankAccount/CardBankAccount.js +229 -0
  7. package/lib/commonjs/components/CardInsight/CardInsight.js +166 -0
  8. package/lib/commonjs/components/CheckboxGroup/CheckboxGroup.js +67 -0
  9. package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +140 -0
  10. package/lib/commonjs/components/CircularProgressBar/CircularProgressBar.js +56 -9
  11. package/lib/commonjs/components/CoverageBarComparison/CoverageBarComparison.js +272 -0
  12. package/lib/commonjs/components/CoverageRing/CoverageRing.js +141 -0
  13. package/lib/commonjs/components/DonutChart/DonutChart.js +309 -0
  14. package/lib/commonjs/components/DonutChartSummary/DonutChartSummary.js +155 -0
  15. package/lib/commonjs/components/Dropdown/Dropdown.js +214 -0
  16. package/lib/commonjs/components/DropdownInput/DropdownInput.js +542 -0
  17. package/lib/commonjs/components/FormField/FormField.js +328 -178
  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/LottieIntroBlock/LottieIntroBlock.js +150 -0
  21. package/lib/commonjs/components/MetricLegendItem/MetricLegendItem.js +95 -0
  22. package/lib/commonjs/components/MonthlyStatusGrid/MonthlyStatusGrid.js +286 -0
  23. package/lib/commonjs/components/OTP/OTP.js +381 -37
  24. package/lib/commonjs/components/PageHero/PageHero.js +153 -0
  25. package/lib/commonjs/components/PoweredByLabel/PoweredByLabel.js +135 -0
  26. package/lib/commonjs/components/PoweredByLabel/finvu.png +0 -0
  27. package/lib/commonjs/components/ProductOverview/ProductOverview.js +147 -0
  28. package/lib/commonjs/components/RangeTrack/RangeTrack.js +269 -0
  29. package/lib/commonjs/components/SavingsGoalSummary/SavingsGoalSummary.js +181 -0
  30. package/lib/commonjs/components/SegmentedTrack/SegmentedTrack.js +171 -0
  31. package/lib/commonjs/components/StatGroup/StatGroup.js +128 -0
  32. package/lib/commonjs/components/StatItem/StatItem.js +65 -35
  33. package/lib/commonjs/components/StrengthIndicator/StrengthIndicator.js +157 -0
  34. package/lib/commonjs/components/SummaryTile/SummaryTile.js +150 -0
  35. package/lib/commonjs/components/Text/Text.js +9 -2
  36. package/lib/commonjs/components/Tooltip/Tooltip.js +34 -27
  37. package/lib/commonjs/components/index.js +231 -1
  38. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  39. package/lib/commonjs/icons/registry.js +1 -1
  40. package/lib/commonjs/utils/index.js +7 -0
  41. package/lib/commonjs/utils/number-utils.js +57 -0
  42. package/lib/module/components/AccordionCheckbox/AccordionCheckbox.js +233 -0
  43. package/lib/module/components/AccountCard/AccountCard.js +241 -0
  44. package/lib/module/components/AppBar/AppBar.js +17 -11
  45. package/lib/module/components/BrandChip/BrandChip.js +143 -0
  46. package/lib/module/components/CardBankAccount/CardBankAccount.js +223 -0
  47. package/lib/module/components/CardInsight/CardInsight.js +161 -0
  48. package/lib/module/components/CheckboxGroup/CheckboxGroup.js +62 -0
  49. package/lib/module/components/CheckboxItem/CheckboxItem.js +134 -0
  50. package/lib/module/components/CircularProgressBar/CircularProgressBar.js +56 -9
  51. package/lib/module/components/CoverageBarComparison/CoverageBarComparison.js +266 -0
  52. package/lib/module/components/CoverageRing/CoverageRing.js +136 -0
  53. package/lib/module/components/DonutChart/DonutChart.js +303 -0
  54. package/lib/module/components/DonutChartSummary/DonutChartSummary.js +150 -0
  55. package/lib/module/components/Dropdown/Dropdown.js +206 -0
  56. package/lib/module/components/DropdownInput/DropdownInput.js +536 -0
  57. package/lib/module/components/FormField/FormField.js +330 -180
  58. package/lib/module/components/LinearMeter/LinearMeter.js +9 -28
  59. package/lib/module/components/LinearProgress/LinearProgress.js +63 -0
  60. package/lib/module/components/LottieIntroBlock/LottieIntroBlock.js +144 -0
  61. package/lib/module/components/MetricLegendItem/MetricLegendItem.js +90 -0
  62. package/lib/module/components/MonthlyStatusGrid/MonthlyStatusGrid.js +281 -0
  63. package/lib/module/components/OTP/OTP.js +381 -38
  64. package/lib/module/components/PageHero/PageHero.js +147 -0
  65. package/lib/module/components/PoweredByLabel/PoweredByLabel.js +130 -0
  66. package/lib/module/components/PoweredByLabel/finvu.png +0 -0
  67. package/lib/module/components/ProductOverview/ProductOverview.js +142 -0
  68. package/lib/module/components/RangeTrack/RangeTrack.js +263 -0
  69. package/lib/module/components/SavingsGoalSummary/SavingsGoalSummary.js +175 -0
  70. package/lib/module/components/SegmentedTrack/SegmentedTrack.js +166 -0
  71. package/lib/module/components/StatGroup/StatGroup.js +123 -0
  72. package/lib/module/components/StatItem/StatItem.js +66 -36
  73. package/lib/module/components/StrengthIndicator/StrengthIndicator.js +152 -0
  74. package/lib/module/components/SummaryTile/SummaryTile.js +145 -0
  75. package/lib/module/components/Text/Text.js +9 -2
  76. package/lib/module/components/Tooltip/Tooltip.js +34 -27
  77. package/lib/module/components/index.js +28 -2
  78. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  79. package/lib/module/icons/registry.js +1 -1
  80. package/lib/module/utils/index.js +2 -1
  81. package/lib/module/utils/number-utils.js +53 -0
  82. package/lib/typescript/src/components/AccordionCheckbox/AccordionCheckbox.d.ts +71 -0
  83. package/lib/typescript/src/components/AccountCard/AccountCard.d.ts +81 -0
  84. package/lib/typescript/src/components/BrandChip/BrandChip.d.ts +43 -0
  85. package/lib/typescript/src/components/CardBankAccount/CardBankAccount.d.ts +86 -0
  86. package/lib/typescript/src/components/CardInsight/CardInsight.d.ts +48 -0
  87. package/lib/typescript/src/components/CheckboxGroup/CheckboxGroup.d.ts +41 -0
  88. package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +72 -0
  89. package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +11 -1
  90. package/lib/typescript/src/components/CoverageBarComparison/CoverageBarComparison.d.ts +105 -0
  91. package/lib/typescript/src/components/CoverageRing/CoverageRing.d.ts +90 -0
  92. package/lib/typescript/src/components/DonutChart/DonutChart.d.ts +117 -0
  93. package/lib/typescript/src/components/DonutChartSummary/DonutChartSummary.d.ts +103 -0
  94. package/lib/typescript/src/components/Dropdown/Dropdown.d.ts +62 -0
  95. package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +107 -0
  96. package/lib/typescript/src/components/FormField/FormField.d.ts +76 -19
  97. package/lib/typescript/src/components/LinearProgress/LinearProgress.d.ts +17 -0
  98. package/lib/typescript/src/components/LottieIntroBlock/LottieIntroBlock.d.ts +58 -0
  99. package/lib/typescript/src/components/MetricLegendItem/MetricLegendItem.d.ts +37 -0
  100. package/lib/typescript/src/components/MonthlyStatusGrid/MonthlyStatusGrid.d.ts +119 -0
  101. package/lib/typescript/src/components/OTP/OTP.d.ts +88 -2
  102. package/lib/typescript/src/components/PageHero/PageHero.d.ts +53 -0
  103. package/lib/typescript/src/components/PoweredByLabel/PoweredByLabel.d.ts +70 -0
  104. package/lib/typescript/src/components/ProductOverview/ProductOverview.d.ts +39 -0
  105. package/lib/typescript/src/components/RangeTrack/RangeTrack.d.ts +173 -0
  106. package/lib/typescript/src/components/SavingsGoalSummary/SavingsGoalSummary.d.ts +95 -0
  107. package/lib/typescript/src/components/SegmentedTrack/SegmentedTrack.d.ts +108 -0
  108. package/lib/typescript/src/components/StatGroup/StatGroup.d.ts +45 -0
  109. package/lib/typescript/src/components/StatItem/StatItem.d.ts +24 -7
  110. package/lib/typescript/src/components/StrengthIndicator/StrengthIndicator.d.ts +58 -0
  111. package/lib/typescript/src/components/SummaryTile/SummaryTile.d.ts +60 -0
  112. package/lib/typescript/src/components/Text/Text.d.ts +12 -2
  113. package/lib/typescript/src/components/Tooltip/Tooltip.d.ts +13 -2
  114. package/lib/typescript/src/components/index.d.ts +29 -3
  115. package/lib/typescript/src/icons/registry.d.ts +1 -1
  116. package/lib/typescript/src/utils/index.d.ts +1 -0
  117. package/lib/typescript/src/utils/number-utils.d.ts +29 -0
  118. package/package.json +1 -3
  119. package/src/components/AccordionCheckbox/AccordionCheckbox.tsx +323 -0
  120. package/src/components/AccountCard/AccountCard.tsx +376 -0
  121. package/src/components/AppBar/AppBar.tsx +25 -14
  122. package/src/components/BrandChip/BrandChip.tsx +235 -0
  123. package/src/components/CardBankAccount/CardBankAccount.tsx +321 -0
  124. package/src/components/CardInsight/CardInsight.tsx +239 -0
  125. package/src/components/CheckboxGroup/CheckboxGroup.tsx +86 -0
  126. package/src/components/CheckboxItem/CheckboxItem.tsx +209 -0
  127. package/src/components/CircularProgressBar/CircularProgressBar.tsx +74 -9
  128. package/src/components/CoverageBarComparison/CoverageBarComparison.tsx +378 -0
  129. package/src/components/CoverageRing/CoverageRing.tsx +225 -0
  130. package/src/components/DonutChart/DonutChart.tsx +503 -0
  131. package/src/components/DonutChartSummary/DonutChartSummary.tsx +256 -0
  132. package/src/components/Dropdown/Dropdown.tsx +331 -0
  133. package/src/components/DropdownInput/DropdownInput.tsx +819 -0
  134. package/src/components/FormField/FormField.tsx +542 -215
  135. package/src/components/LinearMeter/LinearMeter.tsx +9 -39
  136. package/src/components/LinearProgress/LinearProgress.tsx +92 -0
  137. package/src/components/LottieIntroBlock/LottieIntroBlock.tsx +202 -0
  138. package/src/components/MetricLegendItem/MetricLegendItem.tsx +167 -0
  139. package/src/components/MonthlyStatusGrid/MonthlyStatusGrid.tsx +438 -0
  140. package/src/components/OTP/OTP.tsx +476 -29
  141. package/src/components/PageHero/PageHero.tsx +200 -0
  142. package/src/components/PoweredByLabel/PoweredByLabel.tsx +221 -0
  143. package/src/components/PoweredByLabel/finvu.png +0 -0
  144. package/src/components/ProductOverview/ProductOverview.tsx +236 -0
  145. package/src/components/RangeTrack/RangeTrack.tsx +394 -0
  146. package/src/components/SavingsGoalSummary/SavingsGoalSummary.tsx +269 -0
  147. package/src/components/SegmentedTrack/SegmentedTrack.tsx +268 -0
  148. package/src/components/StatGroup/StatGroup.tsx +169 -0
  149. package/src/components/StatItem/StatItem.tsx +117 -40
  150. package/src/components/StrengthIndicator/StrengthIndicator.tsx +205 -0
  151. package/src/components/SummaryTile/SummaryTile.tsx +251 -0
  152. package/src/components/Text/Text.tsx +24 -3
  153. package/src/components/Tooltip/Tooltip.tsx +50 -25
  154. package/src/components/index.ts +47 -3
  155. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  156. package/src/icons/registry.ts +1 -1
  157. package/src/utils/index.ts +1 -0
  158. 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;
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+
3
+ import React, { useCallback, useMemo, useState } from 'react';
4
+ import { Platform, Pressable, ScrollView, 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 Icon from '../../icons/Icon';
9
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
+ const IS_WEB = Platform.OS === 'web';
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // DropdownItem
14
+ // ---------------------------------------------------------------------------
15
+
16
+ function useDropdownItemTokens(modes) {
17
+ return useMemo(() => {
18
+ // The `dropdownItem/background` token aliases through the
19
+ // `Dropdown Item State` collection (Idle | Selected), so we resolve
20
+ // both possibilities up-front and pick at render time.
21
+ const idleBackground = getVariableByName('dropdownItem/background', {
22
+ ...modes,
23
+ 'Dropdown Item State': 'Idle'
24
+ }) || '#ffffff';
25
+ const selectedBackground = getVariableByName('dropdownItem/background', {
26
+ ...modes,
27
+ 'Dropdown Item State': 'Selected'
28
+ }) || '#f5f5f5';
29
+ const foreground = getVariableByName('dropdownItem/foreground', modes) || '#000000';
30
+ const fontFamily = getVariableByName('dropdownItem/fontFamily', modes) || 'JioType Var';
31
+ const fontSize = parseInt(getVariableByName('dropdownItem/fontSize', modes), 10) || 16;
32
+ const fontWeight = getVariableByName('dropdownItem/fontWeight', modes) || '400';
33
+ const lineHeight = parseInt(getVariableByName('dropdownItem/lineHeight', modes), 10) || 19;
34
+ const gap = parseInt(getVariableByName('dropdownItem/gap', modes), 10) || 8;
35
+ const paddingHorizontal = parseInt(getVariableByName('dropdownItem/padding/horizontal', modes), 10) || 12;
36
+ const paddingVertical = parseInt(getVariableByName('dropdownItem/padding/vertical', modes), 10) || 12;
37
+ return {
38
+ idleBackground,
39
+ selectedBackground,
40
+ foreground,
41
+ fontFamily,
42
+ fontSize,
43
+ fontWeight,
44
+ lineHeight,
45
+ gap,
46
+ paddingHorizontal,
47
+ paddingVertical
48
+ };
49
+ }, [modes]);
50
+ }
51
+ export function DropdownItem({
52
+ label,
53
+ value = null,
54
+ selected = false,
55
+ disabled = false,
56
+ leading,
57
+ trailing,
58
+ onPress,
59
+ children,
60
+ modes: propModes = EMPTY_MODES,
61
+ style,
62
+ labelStyle,
63
+ accessibilityLabel,
64
+ accessibilityHint
65
+ }) {
66
+ const {
67
+ modes: globalModes
68
+ } = useTokens();
69
+ const modes = useMemo(() => ({
70
+ ...globalModes,
71
+ ...propModes
72
+ }), [globalModes, propModes]);
73
+ const tokens = useDropdownItemTokens(modes);
74
+ const [isHovered, setIsHovered] = useState(false);
75
+ const handlePress = useCallback(() => {
76
+ if (disabled) return;
77
+ onPress?.(value);
78
+ }, [disabled, onPress, value]);
79
+ const containerStyle = useCallback(({
80
+ pressed
81
+ }) => {
82
+ const showSelected = pressed || isHovered && IS_WEB || selected;
83
+ const base = {
84
+ flexDirection: 'row',
85
+ alignItems: 'center',
86
+ gap: tokens.gap,
87
+ paddingHorizontal: tokens.paddingHorizontal,
88
+ paddingVertical: tokens.paddingVertical,
89
+ backgroundColor: showSelected ? tokens.selectedBackground : tokens.idleBackground,
90
+ opacity: disabled ? 0.4 : 1,
91
+ width: '100%'
92
+ };
93
+ return [base, style];
94
+ }, [tokens.gap, tokens.paddingHorizontal, tokens.paddingVertical, tokens.idleBackground, tokens.selectedBackground, isHovered, selected, disabled, style]);
95
+ const textStyle = {
96
+ color: tokens.foreground,
97
+ fontFamily: tokens.fontFamily,
98
+ fontSize: tokens.fontSize,
99
+ fontWeight: tokens.fontWeight,
100
+ lineHeight: tokens.lineHeight,
101
+ flexShrink: 1
102
+ };
103
+ const processedLeading = leading ? cloneChildrenWithModes(React.Children.toArray(leading), modes) : null;
104
+ const customTrailing = trailing ? cloneChildrenWithModes(React.Children.toArray(trailing), modes) : null;
105
+ const showDefaultCheck = !trailing && selected;
106
+ const fallbackA11yLabel = accessibilityLabel || (typeof label === 'string' ? label : 'Dropdown item');
107
+ const a11yProps = {
108
+ accessibilityRole: 'menuitem',
109
+ accessibilityLabel: fallbackA11yLabel,
110
+ accessibilityState: {
111
+ selected,
112
+ disabled
113
+ }
114
+ };
115
+ if (accessibilityHint) {
116
+ a11yProps.accessibilityHint = accessibilityHint;
117
+ }
118
+ const handleHoverIn = useCallback(() => {
119
+ if (IS_WEB && !disabled) setIsHovered(true);
120
+ }, [disabled]);
121
+ const handleHoverOut = useCallback(() => {
122
+ if (IS_WEB) setIsHovered(false);
123
+ }, []);
124
+ return /*#__PURE__*/_jsxs(Pressable, {
125
+ onPress: handlePress,
126
+ disabled: disabled,
127
+ onHoverIn: handleHoverIn,
128
+ onHoverOut: handleHoverOut,
129
+ style: containerStyle,
130
+ ...a11yProps,
131
+ children: [processedLeading, children != null ? children : /*#__PURE__*/_jsx(Text, {
132
+ style: [textStyle, labelStyle],
133
+ numberOfLines: 1,
134
+ children: label
135
+ }), customTrailing, showDefaultCheck && /*#__PURE__*/_jsx(Icon, {
136
+ name: "ic_confirm",
137
+ size: 16,
138
+ color: tokens.foreground
139
+ })]
140
+ });
141
+ }
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // Dropdown (popup surface)
145
+ // ---------------------------------------------------------------------------
146
+
147
+ /**
148
+ * `Dropdown` is the visual surface (popup) that contains a list of
149
+ * `DropdownItem`s. It is responsible for the background, rounded corners,
150
+ * elevation/shadow, and clipping. Use it standalone for menu UIs, or rely on
151
+ * `DropdownInput` which composes it into a form-field experience.
152
+ */
153
+ export function Dropdown({
154
+ children,
155
+ maxHeight,
156
+ modes: propModes = EMPTY_MODES,
157
+ style,
158
+ accessibilityLabel
159
+ }) {
160
+ const {
161
+ modes: globalModes
162
+ } = useTokens();
163
+ const modes = useMemo(() => ({
164
+ ...globalModes,
165
+ ...propModes
166
+ }), [globalModes, propModes]);
167
+ const radius = parseInt(getVariableByName('dropdown/radius', modes), 10) || 8;
168
+ const background = getVariableByName('dropdown/background', modes) || '#ffffff';
169
+ const shadowColor = getVariableByName('dropdown/shadow/color', modes) || 'rgba(0, 0, 0, 0.08)';
170
+ const shadowOffsetX = parseInt(getVariableByName('dropdown/shadow/offsetX', modes), 10) || 0;
171
+ const shadowOffsetY = parseInt(getVariableByName('dropdown/shadow/offsetY', modes), 10) || 4;
172
+ const shadowBlur = parseInt(getVariableByName('dropdown/shadow/blur', modes), 10) || 16;
173
+ const containerStyle = {
174
+ backgroundColor: background,
175
+ borderRadius: radius,
176
+ overflow: 'hidden',
177
+ shadowColor,
178
+ shadowOffset: {
179
+ width: shadowOffsetX,
180
+ height: shadowOffsetY
181
+ },
182
+ shadowOpacity: 1,
183
+ shadowRadius: shadowBlur / 2,
184
+ elevation: 4
185
+ };
186
+ const content = /*#__PURE__*/_jsx(View, {
187
+ style: {
188
+ flexDirection: 'column'
189
+ },
190
+ children: cloneChildrenWithModes(children, modes)
191
+ });
192
+ return /*#__PURE__*/_jsx(View, {
193
+ style: [containerStyle, style],
194
+ accessibilityRole: "menu",
195
+ accessibilityLabel: accessibilityLabel || 'Dropdown menu',
196
+ children: maxHeight != null ? /*#__PURE__*/_jsx(ScrollView, {
197
+ style: {
198
+ maxHeight
199
+ },
200
+ showsVerticalScrollIndicator: true,
201
+ keyboardShouldPersistTaps: "handled",
202
+ children: content
203
+ }) : content
204
+ });
205
+ }
206
+ export default Dropdown;