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,503 @@
1
+ import React from 'react'
2
+ import {
3
+ StyleSheet,
4
+ Text,
5
+ View,
6
+ type StyleProp,
7
+ type TextStyle,
8
+ type ViewStyle,
9
+ } from 'react-native'
10
+ import Svg, { Circle } from 'react-native-svg'
11
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
12
+ import { useTokens } from '../../design-tokens/JFSThemeProvider'
13
+ import { EMPTY_MODES, flattenChildren } from '../../utils/react-utils'
14
+
15
+ /**
16
+ * Per-segment data definition for the data-driven `segments` prop.
17
+ *
18
+ * Each segment renders one arc on the donut ring. `value` controls the
19
+ * arc's angular share (its weight relative to the sum of all values).
20
+ * Use `modes` per segment to override the default `Appearance / DataViz`
21
+ * mode (the parent injects per-index defaults of `Senary`, `Primary`,
22
+ * `Secondary`, `Tertiary`, `Quaternary`, `Quinary`, then cycles).
23
+ */
24
+ export type DonutChartSegmentData = {
25
+ /** Stable React key. */
26
+ key?: React.Key
27
+ /** Segment's angular weight (relative to the sum of all segment values). */
28
+ value: number
29
+ /** Hard-override the segment color. Bypasses `dataViz/bg` token resolution. */
30
+ color?: string
31
+ /**
32
+ * Per-segment design token mode overrides. Merged on top of parent
33
+ * `modes` and the per-index `Appearance / DataViz` defaults.
34
+ */
35
+ modes?: Record<string, any>
36
+ /** Per-segment accessibility label. */
37
+ accessibilityLabel?: string
38
+ }
39
+
40
+ export type DonutChartProps = {
41
+ /**
42
+ * Data-driven segment list. Used when no `children` slot is provided.
43
+ * Defaults to six equal segments themed across the
44
+ * `Appearance / DataViz` palette.
45
+ */
46
+ segments?: DonutChartSegmentData[]
47
+ /**
48
+ * Optional center value text (large/bold). Pass `null`/`false` to hide.
49
+ * Accepts a string or any `ReactNode` for full control.
50
+ */
51
+ value?: React.ReactNode
52
+ /**
53
+ * Optional center label text (smaller, beneath the value). Pass
54
+ * `null`/`false` to hide. Accepts a string or any `ReactNode`.
55
+ */
56
+ label?: React.ReactNode
57
+ /**
58
+ * Slot for custom center content. When provided, it replaces the
59
+ * default `value`/`label` text wrap entirely.
60
+ */
61
+ children?: React.ReactNode
62
+ /** Outer diameter in px. Defaults to 194 (matching the Figma reference). */
63
+ size?: number
64
+ /**
65
+ * Ring stroke width in px. Defaults to 18% of `size` so the donut keeps
66
+ * the same visual proportions across sizes.
67
+ */
68
+ strokeWidth?: number
69
+ /**
70
+ * Visual gap between adjacent segments expressed in degrees of arc.
71
+ * Defaults to 0 (segments touch). Use small values (1–4°) for
72
+ * separation when many segments are present.
73
+ */
74
+ gap?: number
75
+ /** Design token modes for theming (e.g. `{ 'Color Mode': 'Light' }`). */
76
+ modes?: Record<string, any>
77
+ /** Override container styles. */
78
+ style?: StyleProp<ViewStyle>
79
+ /** Override default value text styles. */
80
+ valueStyle?: StyleProp<TextStyle>
81
+ /** Override default label text styles. */
82
+ labelStyle?: StyleProp<TextStyle>
83
+ /** Accessibility label announced for the whole donut chart. */
84
+ accessibilityLabel?: string
85
+ }
86
+
87
+ const DEFAULT_APPEARANCE_CYCLE = [
88
+ 'Senary',
89
+ 'Primary',
90
+ 'Secondary',
91
+ 'Tertiary',
92
+ 'Quaternary',
93
+ 'Quinary',
94
+ ] as const
95
+
96
+ const DEFAULT_SEGMENTS: DonutChartSegmentData[] = [
97
+ { value: 1 },
98
+ { value: 1 },
99
+ { value: 1 },
100
+ { value: 1 },
101
+ { value: 1 },
102
+ { value: 1 },
103
+ ]
104
+
105
+ const STROKE_WIDTH_RATIO = 18 / 194
106
+
107
+ const toNumber = (value: unknown, fallback: number) => {
108
+ if (typeof value === 'number') {
109
+ return Number.isFinite(value) ? value : fallback
110
+ }
111
+ if (typeof value === 'string') {
112
+ const parsed = Number(value)
113
+ return Number.isFinite(parsed) ? parsed : fallback
114
+ }
115
+ return fallback
116
+ }
117
+
118
+ const toFontWeight = (value: unknown, fallback: TextStyle['fontWeight']) => {
119
+ if (typeof value === 'number') {
120
+ return String(value) as TextStyle['fontWeight']
121
+ }
122
+ if (typeof value === 'string') {
123
+ return value as TextStyle['fontWeight']
124
+ }
125
+ return fallback
126
+ }
127
+
128
+ /**
129
+ * Compute the default `Appearance / DataViz` mode for a segment at `index`.
130
+ * Cycles through {@link DEFAULT_APPEARANCE_CYCLE} so any number of segments
131
+ * gets a sensible default (Senary, Primary, Secondary, Tertiary,
132
+ * Quaternary, Quinary, then repeats).
133
+ */
134
+ function defaultAppearanceFor(index: number) {
135
+ return DEFAULT_APPEARANCE_CYCLE[index % DEFAULT_APPEARANCE_CYCLE.length]
136
+ }
137
+
138
+ /**
139
+ * Resolve a single segment's stroke color through the design tokens. Honors
140
+ * any explicit `color` override, then falls back to `dataViz/bg` for the
141
+ * supplied `modes`, then to the Figma reference purple.
142
+ */
143
+ function resolveSegmentColor(
144
+ color: string | undefined,
145
+ modes: Record<string, any>
146
+ ): string {
147
+ if (color) return color
148
+ return ((getVariableByName('dataViz/bg', modes) as string | null) ?? '#5d00b5')
149
+ }
150
+
151
+ /**
152
+ * Coerce a slot child into a `DonutChartSegmentData` so the rendering path
153
+ * is uniform. Reads `value`, `color`, `modes` and `accessibilityLabel`
154
+ * from the child's props and ignores anything else.
155
+ */
156
+ function segmentDataFromChild(
157
+ child: React.ReactElement,
158
+ fallbackKey: React.Key
159
+ ): DonutChartSegmentData {
160
+ const props = (child.props as any) ?? {}
161
+ return {
162
+ key: child.key ?? fallbackKey,
163
+ value: toNumber(props.value, 1),
164
+ color: typeof props.color === 'string' ? props.color : undefined,
165
+ modes: props.modes,
166
+ accessibilityLabel: props.accessibilityLabel,
167
+ }
168
+ }
169
+
170
+ type DonutChartSegmentProps = {
171
+ /** Angular weight relative to the sum of sibling segment values. */
172
+ value: number
173
+ /** Hard-override segment color (bypasses tokens). */
174
+ color?: string
175
+ /**
176
+ * Design token modes for the segment. Merged with parent `modes` and
177
+ * the per-index `Appearance / DataViz` defaults.
178
+ */
179
+ modes?: Record<string, any>
180
+ /** Per-segment accessibility label. */
181
+ accessibilityLabel?: string
182
+ }
183
+
184
+ /**
185
+ * Inert helper used purely to author donut segments declaratively as
186
+ * `DonutChart.Segment` slot children. The parent reads its props and
187
+ * renders the actual SVG arcs — this component never renders anything by
188
+ * itself.
189
+ */
190
+ function DonutChartSegment(_: DonutChartSegmentProps) {
191
+ return null
192
+ }
193
+
194
+ /**
195
+ * `DonutChart` renders a circular ring split into categorical segments.
196
+ * It is the segmented counterpart of `CircularProgressBar`: there is **no
197
+ * track background** behind the segments — the colored ring *is* the
198
+ * data, and each slice is a sibling category rather than a directional or
199
+ * temporal value.
200
+ *
201
+ * The default 6-segment layout receives per-index `Appearance / DataViz`
202
+ * defaults (segment 1 → `Senary`, 2 → `Primary`, 3 → `Secondary`, 4 →
203
+ * `Tertiary`, 5 → `Quaternary`, 6 → `Quinary`) so the wheel reads as one
204
+ * concept split into six color-coded categories. Override `modes` per
205
+ * segment to remix appearances, or set `Emphasis / DataViz` on the
206
+ * parent `modes` to dim or brighten the whole wheel at once.
207
+ *
208
+ * @component
209
+ * @param {DonutChartProps} props
210
+ */
211
+ function DonutChart({
212
+ segments,
213
+ value,
214
+ label,
215
+ children,
216
+ size = 194,
217
+ strokeWidth: strokeWidthProp,
218
+ gap = 0,
219
+ modes: propModes = EMPTY_MODES,
220
+ style,
221
+ valueStyle,
222
+ labelStyle,
223
+ accessibilityLabel,
224
+ }: DonutChartProps) {
225
+ const { modes: globalModes } = useTokens()
226
+ const modes = { ...globalModes, ...propModes }
227
+
228
+ const strokeWidth = Math.max(
229
+ 1,
230
+ toNumber(strokeWidthProp, Math.max(1, size * STROKE_WIDTH_RATIO))
231
+ )
232
+ const radius = Math.max(0, (size - strokeWidth) / 2)
233
+ const center = size / 2
234
+ const circumference = 2 * Math.PI * radius
235
+ const gapLength = (Math.max(0, gap) / 360) * circumference
236
+
237
+ const slotChildren = flattenChildren(children).filter((child) =>
238
+ React.isValidElement(child)
239
+ ) as React.ReactElement[]
240
+
241
+ const slotSegmentChildren = slotChildren.filter(
242
+ (child) => child.type === DonutChartSegment
243
+ )
244
+
245
+ let resolvedSegments: DonutChartSegmentData[]
246
+ if (slotSegmentChildren.length > 0) {
247
+ resolvedSegments = slotSegmentChildren.map((child, index) =>
248
+ segmentDataFromChild(child, `segment-${index}`)
249
+ )
250
+ } else if (segments && segments.length > 0) {
251
+ resolvedSegments = segments
252
+ } else {
253
+ resolvedSegments = DEFAULT_SEGMENTS
254
+ }
255
+
256
+ const totalValue = resolvedSegments.reduce(
257
+ (sum, segment) => sum + Math.max(0, toNumber(segment.value, 0)),
258
+ 0
259
+ )
260
+ const hasData = totalValue > 0 && resolvedSegments.length > 0
261
+
262
+ const arcs = hasData
263
+ ? buildArcs({
264
+ segments: resolvedSegments,
265
+ totalValue,
266
+ circumference,
267
+ gapLength,
268
+ modes,
269
+ })
270
+ : []
271
+
272
+ const customCenterChildren = slotChildren.filter(
273
+ (child) => child.type !== DonutChartSegment
274
+ )
275
+ const hasChildrenSlot = customCenterChildren.length > 0
276
+
277
+ const showValueLabel = !hasChildrenSlot && shouldRenderText(value)
278
+ const showLabel = !hasChildrenSlot && shouldRenderText(label)
279
+
280
+ const valueColor =
281
+ (getVariableByName('donutChart/value/color', modes) as string | null) ??
282
+ (getVariableByName('value/fg', modes) as string | null) ??
283
+ '#000000'
284
+ const valueFontFamily =
285
+ (getVariableByName('donutChart/value/fontFamily', modes) as string | null) ??
286
+ (getVariableByName('value/fontFamily', modes) as string | null) ??
287
+ 'JioType Var'
288
+ const valueFontSize = toNumber(
289
+ getVariableByName('donutChart/value/fontSize', modes) ??
290
+ getVariableByName('value/fontSize', modes),
291
+ 29
292
+ )
293
+ const valueLineHeight = toNumber(
294
+ getVariableByName('donutChart/value/lineHeight', modes) ??
295
+ getVariableByName('value/lineHeight', modes),
296
+ 29
297
+ )
298
+ const valueFontWeight = toFontWeight(
299
+ getVariableByName('donutChart/value/fontWeight', modes) ??
300
+ getVariableByName('value/fontWeight', modes),
301
+ '900'
302
+ )
303
+
304
+ const labelColor =
305
+ (getVariableByName('donutChart/label/color', modes) as string | null) ??
306
+ (getVariableByName('label/fg', modes) as string | null) ??
307
+ '#000000'
308
+ const labelFontFamily =
309
+ (getVariableByName('donutChart/label/fontFamily', modes) as string | null) ??
310
+ (getVariableByName('label/fontFamily', modes) as string | null) ??
311
+ 'JioType Var'
312
+ const labelFontSize = toNumber(
313
+ getVariableByName('donutChart/label/fontSize', modes) ??
314
+ getVariableByName('label/fontSize', modes),
315
+ 12
316
+ )
317
+ const labelLineHeight = toNumber(
318
+ getVariableByName('donutChart/label/lineHeight', modes) ??
319
+ getVariableByName('label/lineHeight', modes),
320
+ 15.6
321
+ )
322
+ const labelFontWeight = toFontWeight(
323
+ getVariableByName('donutChart/label/fontWeight', modes) ??
324
+ getVariableByName('label/fontWeight', modes),
325
+ '400'
326
+ )
327
+
328
+ const textWrapGap = toNumber(
329
+ getVariableByName('donutChart/textWrap/gap', modes) ??
330
+ getVariableByName('textWrap/gap', modes),
331
+ 7
332
+ )
333
+
334
+ const containerStyle: ViewStyle = {
335
+ alignItems: 'center',
336
+ height: size,
337
+ justifyContent: 'center',
338
+ position: 'relative',
339
+ width: size,
340
+ }
341
+
342
+ return (
343
+ <View
344
+ accessibilityRole="image"
345
+ accessibilityLabel={accessibilityLabel}
346
+ style={[containerStyle, style]}
347
+ >
348
+ <Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
349
+ {arcs.map((arc) => (
350
+ <Circle
351
+ key={arc.key}
352
+ cx={center}
353
+ cy={center}
354
+ r={radius}
355
+ stroke={arc.color}
356
+ strokeWidth={strokeWidth}
357
+ fill="none"
358
+ strokeDasharray={arc.dashArray}
359
+ strokeDashoffset={arc.dashOffset}
360
+ strokeLinecap="butt"
361
+ rotation="-90"
362
+ originX={center}
363
+ originY={center}
364
+ />
365
+ ))}
366
+ </Svg>
367
+
368
+ {hasChildrenSlot ? (
369
+ <View pointerEvents="box-none" style={StyleSheet.absoluteFill as ViewStyle}>
370
+ <View style={styles.centerSlot}>{customCenterChildren}</View>
371
+ </View>
372
+ ) : showValueLabel || showLabel ? (
373
+ <View
374
+ pointerEvents="none"
375
+ style={[styles.centerSlot, { gap: textWrapGap }]}
376
+ >
377
+ {showValueLabel ? (
378
+ <Text
379
+ style={[
380
+ {
381
+ color: valueColor,
382
+ fontFamily: valueFontFamily,
383
+ fontSize: valueFontSize,
384
+ lineHeight: valueLineHeight,
385
+ fontWeight: valueFontWeight,
386
+ textAlign: 'center',
387
+ },
388
+ valueStyle,
389
+ ]}
390
+ numberOfLines={1}
391
+ >
392
+ {value}
393
+ </Text>
394
+ ) : null}
395
+ {showLabel ? (
396
+ <Text
397
+ style={[
398
+ {
399
+ color: labelColor,
400
+ fontFamily: labelFontFamily,
401
+ fontSize: labelFontSize,
402
+ lineHeight: labelLineHeight,
403
+ fontWeight: labelFontWeight,
404
+ textAlign: 'center',
405
+ },
406
+ labelStyle,
407
+ ]}
408
+ numberOfLines={1}
409
+ >
410
+ {label}
411
+ </Text>
412
+ ) : null}
413
+ </View>
414
+ ) : null}
415
+ </View>
416
+ )
417
+ }
418
+
419
+ function shouldRenderText(node: React.ReactNode): boolean {
420
+ return node !== undefined && node !== null && node !== false && node !== ''
421
+ }
422
+
423
+ type ArcDescriptor = {
424
+ key: React.Key
425
+ color: string
426
+ dashArray: string
427
+ dashOffset: number
428
+ }
429
+
430
+ /**
431
+ * Convert the resolved segment list into a list of stroke-dasharray /
432
+ * stroke-dashoffset descriptors that can be drawn as SVG `Circle`s. Each
433
+ * arc's length is its share of `circumference`, and its rotational offset
434
+ * is the cumulative offset of all preceding segments. Visual gaps are
435
+ * applied symmetrically — half a gap is shaved from each end of the arc
436
+ * so the visible separation between two segments equals one full `gap`.
437
+ */
438
+ function buildArcs({
439
+ segments,
440
+ totalValue,
441
+ circumference,
442
+ gapLength,
443
+ modes,
444
+ }: {
445
+ segments: DonutChartSegmentData[]
446
+ totalValue: number
447
+ circumference: number
448
+ gapLength: number
449
+ modes: Record<string, any>
450
+ }): ArcDescriptor[] {
451
+ const arcs: ArcDescriptor[] = []
452
+ const halfGap = gapLength / 2
453
+ let cumulativeOffset = 0
454
+
455
+ segments.forEach((segment, index) => {
456
+ const safeValue = Math.max(0, toNumber(segment.value, 0))
457
+ if (safeValue <= 0) {
458
+ return
459
+ }
460
+
461
+ const arcLength = (safeValue / totalValue) * circumference
462
+ const drawnLength = Math.max(0, arcLength - gapLength)
463
+ const segmentModes = {
464
+ ...modes,
465
+ 'Appearance / DataViz': defaultAppearanceFor(index),
466
+ ...(segment.modes || {}),
467
+ }
468
+ const color = resolveSegmentColor(segment.color, segmentModes)
469
+
470
+ const dashArray = `${drawnLength} ${circumference - drawnLength}`
471
+ const dashOffset = -cumulativeOffset - halfGap
472
+
473
+ arcs.push({
474
+ key: segment.key ?? `segment-${index}`,
475
+ color,
476
+ dashArray,
477
+ dashOffset,
478
+ })
479
+
480
+ cumulativeOffset += arcLength
481
+ })
482
+
483
+ return arcs
484
+ }
485
+
486
+ const styles = StyleSheet.create({
487
+ centerSlot: {
488
+ alignItems: 'center',
489
+ bottom: 0,
490
+ justifyContent: 'center',
491
+ left: 0,
492
+ position: 'absolute',
493
+ right: 0,
494
+ top: 0,
495
+ },
496
+ })
497
+
498
+ DonutChart.Segment = DonutChartSegment
499
+
500
+ export { DonutChartSegment }
501
+ export type { DonutChartSegmentProps }
502
+
503
+ export default DonutChart