jfs-components 0.0.70 → 0.0.71

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 (43) hide show
  1. package/lib/commonjs/components/CardAdvisory/CardAdvisory.js +203 -0
  2. package/lib/commonjs/components/CardCTA/CardCTA.js +198 -16
  3. package/lib/commonjs/components/CircularProgressBar/CircularProgressBar.js +147 -0
  4. package/lib/commonjs/components/CircularProgressBarDoted/CircularProgressBarDoted.js +258 -0
  5. package/lib/commonjs/components/CircularRating/CircularRating.js +161 -0
  6. package/lib/commonjs/components/Gauge/Gauge.js +223 -0
  7. package/lib/commonjs/components/ListGroup/ListGroup.js +3 -1
  8. package/lib/commonjs/components/Nudge/Nudge.js +179 -87
  9. package/lib/commonjs/components/index.js +35 -0
  10. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  11. package/lib/commonjs/icons/registry.js +1 -1
  12. package/lib/module/components/CardAdvisory/CardAdvisory.js +197 -0
  13. package/lib/module/components/CardCTA/CardCTA.js +199 -17
  14. package/lib/module/components/CircularProgressBar/CircularProgressBar.js +141 -0
  15. package/lib/module/components/CircularProgressBarDoted/CircularProgressBarDoted.js +253 -0
  16. package/lib/module/components/CircularRating/CircularRating.js +155 -0
  17. package/lib/module/components/Gauge/Gauge.js +217 -0
  18. package/lib/module/components/ListGroup/ListGroup.js +3 -1
  19. package/lib/module/components/Nudge/Nudge.js +178 -87
  20. package/lib/module/components/index.js +5 -0
  21. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  22. package/lib/module/icons/registry.js +1 -1
  23. package/lib/typescript/src/components/CardAdvisory/CardAdvisory.d.ts +49 -0
  24. package/lib/typescript/src/components/CardCTA/CardCTA.d.ts +16 -1
  25. package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +27 -0
  26. package/lib/typescript/src/components/CircularProgressBarDoted/CircularProgressBarDoted.d.ts +48 -0
  27. package/lib/typescript/src/components/CircularRating/CircularRating.d.ts +49 -0
  28. package/lib/typescript/src/components/Gauge/Gauge.d.ts +53 -0
  29. package/lib/typescript/src/components/Nudge/Nudge.d.ts +14 -11
  30. package/lib/typescript/src/components/index.d.ts +6 -1
  31. package/lib/typescript/src/icons/registry.d.ts +1 -1
  32. package/package.json +1 -1
  33. package/src/components/CardAdvisory/CardAdvisory.tsx +283 -0
  34. package/src/components/CardCTA/CardCTA.tsx +236 -13
  35. package/src/components/CircularProgressBar/CircularProgressBar.tsx +190 -0
  36. package/src/components/CircularProgressBarDoted/CircularProgressBarDoted.tsx +357 -0
  37. package/src/components/CircularRating/CircularRating.tsx +241 -0
  38. package/src/components/Gauge/Gauge.tsx +303 -0
  39. package/src/components/ListGroup/ListGroup.tsx +3 -1
  40. package/src/components/Nudge/Nudge.tsx +222 -82
  41. package/src/components/index.ts +6 -1
  42. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  43. package/src/icons/registry.ts +1 -1
@@ -0,0 +1,197 @@
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 Icon from '../../icons/Icon';
8
+ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
9
+ import CircularProgressBar from '../CircularProgressBar/CircularProgressBar';
10
+ import Nudge from '../Nudge/Nudge';
11
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
+ const toNumber = (value, fallback) => {
13
+ if (typeof value === 'number') {
14
+ return Number.isFinite(value) ? value : fallback;
15
+ }
16
+ if (typeof value === 'string') {
17
+ const parsed = Number(value);
18
+ return Number.isFinite(parsed) ? parsed : fallback;
19
+ }
20
+ return fallback;
21
+ };
22
+ const toFontWeight = (value, fallback) => {
23
+ if (typeof value === 'number') {
24
+ return String(value);
25
+ }
26
+ if (typeof value === 'string') {
27
+ return value;
28
+ }
29
+ return fallback;
30
+ };
31
+ function resolveCardAdvisoryTokens(modes) {
32
+ const width = toNumber(getVariableByName('cardAdvisory/width', modes), 360);
33
+ const gap = toNumber(getVariableByName('cardAdvisory/gap', modes), 16);
34
+ const paddingHorizontal = toNumber(getVariableByName('cardAdvisory/padding/horizontal', modes), 16);
35
+ const paddingVertical = toNumber(getVariableByName('cardAdvisory/padding/vertical', modes), 12);
36
+ const radius = toNumber(getVariableByName('cardAdvisory/radius', modes), 0);
37
+ const background = getVariableByName('cardAdvisory/background', modes) || '#ffffff';
38
+ const mainContentGap = toNumber(getVariableByName('cardAdvisory/mainContent/gap', modes), 16);
39
+ const contentGap = toNumber(getVariableByName('cardAdvisory/content/gap', modes), 6);
40
+ const headerGap = toNumber(getVariableByName('cardAdvisory/header/gap', modes), 8);
41
+ const titleColor = getVariableByName('cardAdvisory/title/foreground', modes) || '#0d0d0f';
42
+ const titleFontSize = toNumber(getVariableByName('cardAdvisory/title/fontSize', modes), 26);
43
+ const titleFontFamily = getVariableByName('cardAdvisory/title/fontFamily', modes) || 'JioType Var';
44
+ const titleLineHeight = toNumber(getVariableByName('cardAdvisory/title/lineHeight', modes), 26);
45
+ const titleFontWeight = toFontWeight(getVariableByName('cardAdvisory/title/fontWeight', modes), '900');
46
+ const titleDescenderAllowance = Math.ceil(titleFontSize * 0.16);
47
+ const descriptionColor = getVariableByName('cardAdvisory/description/foreground', modes) || '#24262b';
48
+ const descriptionFontSize = toNumber(getVariableByName('cardAdvisory/description/fontSize', modes), 12);
49
+ const descriptionFontFamily = getVariableByName('cardAdvisory/description/fontFamily', modes) || 'JioType Var';
50
+ const descriptionLineHeight = toNumber(getVariableByName('cardAdvisory/description/lineHeight', modes), 16);
51
+ const descriptionFontWeight = toFontWeight(getVariableByName('cardAdvisory/description/fontWeight', modes), '500');
52
+ return {
53
+ containerStyle: {
54
+ alignItems: 'flex-start',
55
+ backgroundColor: background,
56
+ borderRadius: radius,
57
+ gap,
58
+ overflow: 'hidden',
59
+ paddingHorizontal,
60
+ paddingVertical,
61
+ width
62
+ },
63
+ mainContentStyle: {
64
+ alignItems: 'flex-start',
65
+ flexDirection: 'row',
66
+ gap: mainContentGap,
67
+ width: '100%'
68
+ },
69
+ contentStyle: {
70
+ alignItems: 'flex-start',
71
+ flex: 1,
72
+ gap: contentGap,
73
+ minWidth: 1
74
+ },
75
+ headerStyle: {
76
+ alignItems: 'center',
77
+ flexDirection: 'row',
78
+ gap: headerGap,
79
+ width: '100%'
80
+ },
81
+ titleStyle: {
82
+ color: titleColor,
83
+ fontFamily: titleFontFamily,
84
+ fontSize: titleFontSize,
85
+ fontWeight: titleFontWeight,
86
+ lineHeight: titleLineHeight,
87
+ marginBottom: -titleDescenderAllowance,
88
+ paddingBottom: titleDescenderAllowance
89
+ },
90
+ descriptionStyle: {
91
+ color: descriptionColor,
92
+ fontFamily: descriptionFontFamily,
93
+ fontSize: descriptionFontSize,
94
+ fontWeight: descriptionFontWeight,
95
+ lineHeight: descriptionLineHeight,
96
+ width: '100%'
97
+ },
98
+ iconColor: getVariableByName('cardAdvisory/icon/color', modes) || '#1a1c1f',
99
+ iconSize: toNumber(getVariableByName('cardAdvisory/icon/size', modes), 18)
100
+ };
101
+ }
102
+ function CardAdvisory({
103
+ title = 'Spending',
104
+ description = 'Track your spending habits and stay within your budget.',
105
+ value = 70,
106
+ valueLabel,
107
+ showInfoIcon = true,
108
+ showNudge = true,
109
+ nudgeBody = 'Data confidence is low, add more accounts for better insights.',
110
+ nudgeButtonLabel = 'Button',
111
+ onPressNudgeButton,
112
+ titleEndSlot,
113
+ progressSlot,
114
+ nudgeSlot,
115
+ modes: propModes = EMPTY_MODES,
116
+ style,
117
+ mainContentStyle,
118
+ titleStyle,
119
+ descriptionStyle,
120
+ progressStyle,
121
+ nudgeStyle,
122
+ accessibilityLabel,
123
+ ...rest
124
+ }) {
125
+ const {
126
+ modes: globalModes
127
+ } = useTokens();
128
+ const modes = useMemo(() => globalModes === EMPTY_MODES && propModes === EMPTY_MODES ? EMPTY_MODES : {
129
+ ...globalModes,
130
+ ...propModes
131
+ }, [globalModes, propModes]);
132
+ const tokens = useMemo(() => resolveCardAdvisoryTokens(modes), [modes]);
133
+ const processedTitleEndSlot = useMemo(() => {
134
+ if (!titleEndSlot) return null;
135
+ const processed = cloneChildrenWithModes(React.Children.toArray(titleEndSlot), modes);
136
+ return processed.length === 1 ? processed[0] : processed;
137
+ }, [titleEndSlot, modes]);
138
+ const processedProgressSlot = useMemo(() => {
139
+ if (!progressSlot) return null;
140
+ const processed = cloneChildrenWithModes(React.Children.toArray(progressSlot), modes);
141
+ return processed.length === 1 ? processed[0] : processed;
142
+ }, [progressSlot, modes]);
143
+ const processedNudgeSlot = useMemo(() => {
144
+ if (!nudgeSlot) return null;
145
+ const processed = cloneChildrenWithModes(React.Children.toArray(nudgeSlot), modes);
146
+ return processed.length === 1 ? processed[0] : processed;
147
+ }, [nudgeSlot, modes]);
148
+ const defaultAccessibilityLabel = accessibilityLabel ?? `${title}. ${description}. ${Math.round(value)} out of 100. ${nudgeBody}`;
149
+ return /*#__PURE__*/_jsxs(View, {
150
+ accessibilityLabel: defaultAccessibilityLabel,
151
+ style: [tokens.containerStyle, style],
152
+ ...rest,
153
+ children: [/*#__PURE__*/_jsxs(View, {
154
+ style: [tokens.mainContentStyle, mainContentStyle],
155
+ children: [/*#__PURE__*/_jsxs(View, {
156
+ style: tokens.contentStyle,
157
+ children: [/*#__PURE__*/_jsxs(View, {
158
+ style: tokens.headerStyle,
159
+ children: [/*#__PURE__*/_jsx(Text, {
160
+ numberOfLines: 1,
161
+ style: [tokens.titleStyle, titleStyle],
162
+ children: title
163
+ }), processedTitleEndSlot || (showInfoIcon ? /*#__PURE__*/_jsx(Icon, {
164
+ name: "ic_info",
165
+ size: tokens.iconSize,
166
+ color: tokens.iconColor,
167
+ accessibilityElementsHidden: true,
168
+ importantForAccessibility: "no"
169
+ }) : null)]
170
+ }), /*#__PURE__*/_jsx(Text, {
171
+ style: [tokens.descriptionStyle, descriptionStyle],
172
+ children: description
173
+ })]
174
+ }), processedProgressSlot || /*#__PURE__*/_jsx(CircularProgressBar, {
175
+ state: "Active",
176
+ value: value,
177
+ modes: modes,
178
+ style: progressStyle,
179
+ ...(valueLabel ? {
180
+ valueLabel
181
+ } : {})
182
+ })]
183
+ }), showNudge ? processedNudgeSlot || /*#__PURE__*/_jsx(Nudge, {
184
+ type: "inline-compact",
185
+ body: nudgeBody,
186
+ buttonLabel: nudgeButtonLabel,
187
+ modes: modes,
188
+ style: [{
189
+ width: '100%'
190
+ }, nudgeStyle],
191
+ ...(onPressNudgeButton ? {
192
+ onPressButton: onPressNudgeButton
193
+ } : {})
194
+ }) : null]
195
+ });
196
+ }
197
+ export default /*#__PURE__*/React.memo(CardAdvisory);
@@ -2,21 +2,43 @@
2
2
 
3
3
  import React from 'react';
4
4
  import { View, Text } from 'react-native';
5
- import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
5
+ import { findVariablesByPattern, getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
6
  import { useTokens } from '../../design-tokens/JFSThemeProvider';
7
7
  import { cloneChildrenWithModes, EMPTY_MODES } from '../../utils/react-utils';
8
8
  import IconCapsule from '../IconCapsule/IconCapsule';
9
9
  import Button from '../Button/Button';
10
+ import Badge from '../Badge/Badge';
11
+ import ButtonGroup from '../ButtonGroup/ButtonGroup';
12
+ import IconButton from '../IconButton/IconButton';
10
13
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
+ const optionalTokenAvailability = new Map();
15
+ function getOptionalVariableByName(name, modes, fallback) {
16
+ let isAvailable = optionalTokenAvailability.get(name);
17
+ if (isAvailable === undefined) {
18
+ isAvailable = findVariablesByPattern(name).some(variable => variable.name === name);
19
+ optionalTokenAvailability.set(name, isAvailable);
20
+ }
21
+ if (!isAvailable) {
22
+ return fallback;
23
+ }
24
+ return getVariableByName(name, modes) ?? fallback;
25
+ }
11
26
  function CardCTA({
27
+ type = 'cta',
12
28
  title = 'If you have 1 line',
13
29
  body = 'Then you can have up to 3 lines in the subtext as well. This is for demonstration.',
14
30
  iconName = 'ic_upi_number',
15
- buttonLabel = 'Button',
31
+ buttonLabel,
16
32
  onPressButton,
33
+ ratingLabel = '+28 Rating',
34
+ showRatingActions = true,
35
+ onPressLike,
36
+ onPressDislike,
17
37
  modes: propModes = EMPTY_MODES,
18
38
  iconSlot,
19
39
  buttonSlot,
40
+ ratingBadgeSlot,
41
+ ratingActionsSlot,
20
42
  style
21
43
  }) {
22
44
  const {
@@ -26,6 +48,7 @@ function CardCTA({
26
48
  ...globalModes,
27
49
  ...propModes
28
50
  };
51
+ const isRating = type === 'rating';
29
52
  const background = getVariableByName('cardCTA/background', modes) || '#ffffff';
30
53
  const radius = getVariableByName('cardCTA/radius', modes) || 12;
31
54
  const borderSize = getVariableByName('cardCTA/border/size', modes) || 1;
@@ -48,13 +71,48 @@ function CardCTA({
48
71
  const bodyLineHeight = getVariableByName('cardCTA/body/lineHeight', modes) || 12;
49
72
  const bodyFontWeightRaw = getVariableByName('cardCTA/body/fontWeight', modes) || 400;
50
73
  const bodyFontWeight = typeof bodyFontWeightRaw === 'number' ? bodyFontWeightRaw.toString() : bodyFontWeightRaw;
74
+ const ratingContentGap = getOptionalVariableByName('cardCTA/rating/content/gap', modes, 12);
75
+ const ratingContentPaddingH = getOptionalVariableByName('cardCTA/rating/content/padding/horizontal', modes, 16);
76
+ const ratingContentPaddingV = getOptionalVariableByName('cardCTA/rating/content/padding/vertical', modes, 12);
77
+ const ratingFooterPaddingH = getOptionalVariableByName('cardCTA/rating/footer/horizontal', modes, 16);
78
+ const ratingFooterPaddingTop = getOptionalVariableByName('cardCTA/rating/footer/top', modes, 0);
79
+ const ratingFooterPaddingBottom = getOptionalVariableByName('cardCTA/rating/footer/bottom', modes, 12);
80
+ const buttonModes = {
81
+ ...modes,
82
+ AppearanceBrand: 'Secondary',
83
+ 'Button / Size': 'S'
84
+ };
85
+ const iconButtonModes = {
86
+ 'Button / Size': 'S',
87
+ 'Emphasis': 'Low',
88
+ 'AppearanceBrand': 'Neutral',
89
+ ...modes
90
+ };
91
+ const effectiveButtonLabel = buttonLabel ?? (isRating ? 'Save' : 'Button');
92
+ const nonWrappingButtonLabel = effectiveButtonLabel.replace(/\s/g, '\u00A0');
93
+ const [measuredButtonLabelWidth, setMeasuredButtonLabelWidth] = React.useState(null);
94
+ const buttonPaddingH = getVariableByName('button/padding/horizontal', buttonModes) || 20;
95
+ const buttonBorderSize = getVariableByName('button/border/size', buttonModes) ?? 1;
96
+ const measuredButtonWidth = measuredButtonLabelWidth === null ? undefined : Math.ceil(measuredButtonLabelWidth + buttonPaddingH * 2 + buttonBorderSize * 2);
97
+ const handleButtonLabelTextLayout = React.useCallback(event => {
98
+ const lines = event?.nativeEvent?.lines;
99
+ if (!Array.isArray(lines) || lines.length === 0) return;
100
+ const nextWidth = Math.ceil(lines.reduce((sum, line) => sum + (typeof line?.width === 'number' ? line.width : 0), 0));
101
+ if (nextWidth <= 0) return;
102
+ setMeasuredButtonLabelWidth(currentWidth => {
103
+ if (currentWidth !== null && Math.abs(currentWidth - nextWidth) < 1) {
104
+ return currentWidth;
105
+ }
106
+ return nextWidth;
107
+ });
108
+ }, []);
51
109
  const containerStyle = {
52
110
  backgroundColor: background,
53
111
  borderRadius: radius,
54
112
  borderWidth: borderSize,
55
113
  borderColor,
56
- flexDirection: 'row',
57
- overflow: 'hidden'
114
+ flexDirection: isRating ? 'column' : 'row',
115
+ overflow: 'visible'
58
116
  };
59
117
 
60
118
  // NOTE: `minWidth: 0` + explicit `flexShrink: 1` are required on native.
@@ -71,7 +129,9 @@ function CardCTA({
71
129
  paddingVertical: leftPaddingV,
72
130
  gap: leftGap,
73
131
  alignItems: 'flex-start',
74
- justifyContent: 'center'
132
+ justifyContent: 'center',
133
+ overflow: 'visible',
134
+ zIndex: 1
75
135
  };
76
136
 
77
137
  // NOTE: rightWrap must NOT shrink on native. On Android (Yoga), the default
@@ -100,6 +160,29 @@ function CardCTA({
100
160
  alignSelf: 'stretch',
101
161
  minWidth: 0
102
162
  };
163
+
164
+ // Keep text shrink/wrap behavior on the left column, but let the CTA keep
165
+ // its own intrinsic width. On native, Yoga otherwise measures the Button
166
+ // with the left column's available width and the single-line label
167
+ // truncates even when the Button itself has no width/maxWidth constraint.
168
+ const buttonWrapStyle = {
169
+ alignSelf: 'flex-start',
170
+ flexGrow: 0,
171
+ flexShrink: 0,
172
+ flexBasis: 'auto',
173
+ overflow: 'visible',
174
+ zIndex: 1
175
+ };
176
+ const buttonStyle = {
177
+ alignSelf: 'flex-start',
178
+ flexGrow: 0,
179
+ flexShrink: 0,
180
+ flexBasis: 'auto',
181
+ overflow: 'visible',
182
+ ...(measuredButtonWidth !== undefined ? {
183
+ width: measuredButtonWidth
184
+ } : {})
185
+ };
103
186
  const titleStyle = {
104
187
  color: titleColor,
105
188
  fontFamily: titleFontFamily,
@@ -114,6 +197,104 @@ function CardCTA({
114
197
  lineHeight: bodyLineHeight,
115
198
  fontWeight: bodyFontWeight
116
199
  };
200
+ const ratingContentStyle = {
201
+ paddingHorizontal: ratingContentPaddingH,
202
+ paddingVertical: ratingContentPaddingV,
203
+ gap: ratingContentGap,
204
+ alignItems: 'flex-start'
205
+ };
206
+ const ratingFooterStyle = {
207
+ flexDirection: 'row',
208
+ alignItems: 'flex-start',
209
+ justifyContent: 'space-between',
210
+ paddingHorizontal: ratingFooterPaddingH,
211
+ paddingTop: ratingFooterPaddingTop,
212
+ paddingBottom: ratingFooterPaddingBottom,
213
+ overflow: 'visible'
214
+ };
215
+ const buttonLabelStyle = {
216
+ flexGrow: 0,
217
+ flexShrink: 0,
218
+ flexWrap: 'nowrap'
219
+ };
220
+
221
+ // Keep the rating CTA on an overflow-visible, non-shrinking path. The
222
+ // footer's row width stays fixed by the card, but Yoga must not use that
223
+ // width to shrink or clip the button label.
224
+ const ratingButtonWrapStyle = {
225
+ flexGrow: 0,
226
+ flexShrink: 0,
227
+ flexBasis: 'auto',
228
+ alignItems: 'flex-start',
229
+ overflow: 'visible'
230
+ };
231
+ const ratingButtonStyle = {
232
+ alignSelf: 'flex-start',
233
+ flexGrow: 0,
234
+ flexShrink: 0,
235
+ flexBasis: 'auto',
236
+ overflow: 'visible',
237
+ ...(measuredButtonWidth !== undefined ? {
238
+ width: measuredButtonWidth
239
+ } : {})
240
+ };
241
+ const ratingButtonLabelStyle = {
242
+ flexGrow: 0,
243
+ flexShrink: 0,
244
+ flexWrap: 'nowrap'
245
+ };
246
+ if (isRating) {
247
+ return /*#__PURE__*/_jsxs(View, {
248
+ style: [containerStyle, style],
249
+ children: [/*#__PURE__*/_jsxs(View, {
250
+ style: ratingContentStyle,
251
+ children: [ratingBadgeSlot ? cloneChildrenWithModes(ratingBadgeSlot, modes) : /*#__PURE__*/_jsx(Badge, {
252
+ label: ratingLabel,
253
+ modes: modes
254
+ }), /*#__PURE__*/_jsxs(View, {
255
+ style: textWrapStyle,
256
+ children: [/*#__PURE__*/_jsx(Text, {
257
+ style: titleStyle,
258
+ children: title
259
+ }), /*#__PURE__*/_jsx(Text, {
260
+ style: bodyStyle,
261
+ children: body
262
+ })]
263
+ })]
264
+ }), /*#__PURE__*/_jsxs(View, {
265
+ style: ratingFooterStyle,
266
+ children: [/*#__PURE__*/_jsx(View, {
267
+ style: ratingButtonWrapStyle,
268
+ children: buttonSlot ? cloneChildrenWithModes(buttonSlot, buttonModes) : /*#__PURE__*/_jsx(Button, {
269
+ label: effectiveButtonLabel,
270
+ onPress: onPressButton || (() => {}),
271
+ modes: buttonModes,
272
+ style: ratingButtonStyle,
273
+ renderContent: labelStyles => /*#__PURE__*/_jsx(Text, {
274
+ onTextLayout: handleButtonLabelTextLayout,
275
+ style: [labelStyles, ratingButtonLabelStyle],
276
+ children: nonWrappingButtonLabel
277
+ })
278
+ })
279
+ }), showRatingActions ? ratingActionsSlot ? cloneChildrenWithModes(ratingActionsSlot, iconButtonModes) : /*#__PURE__*/_jsxs(ButtonGroup, {
280
+ modes: iconButtonModes,
281
+ children: [/*#__PURE__*/_jsx(IconButton, {
282
+ iconName: "ic_like",
283
+ accessibilityLabel: "Like",
284
+ ...(onPressLike ? {
285
+ onPress: onPressLike
286
+ } : {})
287
+ }), /*#__PURE__*/_jsx(IconButton, {
288
+ iconName: "ic_dislike",
289
+ accessibilityLabel: "Dislike",
290
+ ...(onPressDislike ? {
291
+ onPress: onPressDislike
292
+ } : {})
293
+ })]
294
+ }) : null]
295
+ })]
296
+ });
297
+ }
117
298
  return /*#__PURE__*/_jsxs(View, {
118
299
  style: [containerStyle, style],
119
300
  children: [/*#__PURE__*/_jsxs(View, {
@@ -127,18 +308,19 @@ function CardCTA({
127
308
  style: bodyStyle,
128
309
  children: body
129
310
  })]
130
- }), buttonSlot ? cloneChildrenWithModes(buttonSlot, {
131
- ...modes,
132
- AppearanceBrand: 'Secondary',
133
- 'Button / Size': 'S'
134
- }) : /*#__PURE__*/_jsx(Button, {
135
- label: buttonLabel,
136
- onPress: onPressButton || (() => {}),
137
- modes: {
138
- ...modes,
139
- AppearanceBrand: 'Secondary',
140
- 'Button / Size': 'S'
141
- }
311
+ }), /*#__PURE__*/_jsx(View, {
312
+ style: buttonWrapStyle,
313
+ children: buttonSlot ? cloneChildrenWithModes(buttonSlot, buttonModes) : /*#__PURE__*/_jsx(Button, {
314
+ label: effectiveButtonLabel,
315
+ onPress: onPressButton || (() => {}),
316
+ modes: buttonModes,
317
+ style: buttonStyle,
318
+ renderContent: labelStyles => /*#__PURE__*/_jsx(Text, {
319
+ onTextLayout: handleButtonLabelTextLayout,
320
+ style: [labelStyles, buttonLabelStyle],
321
+ children: nonWrappingButtonLabel
322
+ })
323
+ })
142
324
  })]
143
325
  }), /*#__PURE__*/_jsx(View, {
144
326
  style: rightWrapStyle,
@@ -0,0 +1,141 @@
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 } from '../../utils/react-utils';
9
+ import { IconMinus } from '../../icons/components/IconMinus';
10
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
+ const STROKE_WIDTH_RATIO = 8 / 60;
12
+ const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
13
+ const toNumber = (value, fallback) => {
14
+ if (typeof value === 'number') {
15
+ return Number.isFinite(value) ? value : fallback;
16
+ }
17
+ if (typeof value === 'string') {
18
+ const parsed = Number(value);
19
+ return Number.isFinite(parsed) ? parsed : fallback;
20
+ }
21
+ return fallback;
22
+ };
23
+ const toFontWeight = (value, fallback) => {
24
+ if (typeof value === 'number') {
25
+ return String(value);
26
+ }
27
+ if (typeof value === 'string') {
28
+ return value;
29
+ }
30
+ return fallback;
31
+ };
32
+ const getStrokeColor = (style, fallback) => {
33
+ const flattened = StyleSheet.flatten(style);
34
+ return typeof flattened.backgroundColor === 'string' ? flattened.backgroundColor : fallback;
35
+ };
36
+ function CircularProgressBar({
37
+ value = 70,
38
+ state = 'Inactive',
39
+ valueLabel,
40
+ modes: propModes = EMPTY_MODES,
41
+ style,
42
+ trackStyle,
43
+ progressStyle,
44
+ valueStyle,
45
+ accessibilityLabel,
46
+ ...rest
47
+ }) {
48
+ const {
49
+ modes: globalModes
50
+ } = useTokens();
51
+ const modes = {
52
+ ...globalModes,
53
+ ...propModes
54
+ };
55
+ const isActive = state === true || state === 'Active';
56
+ const normalizedValue = clamp(value, 0, 100);
57
+ const size = toNumber(getVariableByName('circularProgressBar/size', modes), 60);
58
+ const strokeWidth = Math.max(1, size * STROKE_WIDTH_RATIO);
59
+ const radius = Math.max(0, (size - strokeWidth) / 2);
60
+ const center = size / 2;
61
+ const circumference = 2 * Math.PI * radius;
62
+ const trackColor = getStrokeColor(trackStyle, getVariableByName('circularProgressBar/track/color', modes) || '#ebebed');
63
+ const progressColor = getStrokeColor(progressStyle, getVariableByName('circularProgressBar/progress/color', modes) || '#25ab21');
64
+ const iconColor = getVariableByName('circularProgressBar/icon/color', modes) || '#666666';
65
+ const iconSize = toNumber(getVariableByName('circularProgressBar/icon/size', modes), 24);
66
+ const foreground = getVariableByName('circularProgressBar/foreground', modes) || '#0d0d0f';
67
+ const fontSize = toNumber(getVariableByName('circularProgressBar/fontSize', modes), 18);
68
+ const fontFamily = getVariableByName('circularProgressBar/fontFamily', modes) || 'JioType Var';
69
+ const lineHeight = toNumber(getVariableByName('circularProgressBar/lineHeight', modes), 21);
70
+ const fontWeight = toFontWeight(getVariableByName('circularProgressBar/fontWeight', modes), '700');
71
+ const computedContainerStyle = {
72
+ alignItems: 'center',
73
+ height: size,
74
+ justifyContent: 'center',
75
+ position: 'relative',
76
+ width: size
77
+ };
78
+ const computedValueStyle = {
79
+ color: foreground,
80
+ fontFamily,
81
+ fontSize,
82
+ fontWeight,
83
+ lineHeight,
84
+ position: 'absolute',
85
+ textAlign: 'center'
86
+ };
87
+ const iconStyle = {
88
+ left: (size - iconSize) / 2,
89
+ position: 'absolute',
90
+ top: (size - iconSize) / 2
91
+ };
92
+ const displayValue = valueLabel ?? String(Math.round(normalizedValue));
93
+ const defaultAccessibilityLabel = accessibilityLabel ?? (isActive ? `${displayValue} out of 100` : 'Inactive progress');
94
+ return /*#__PURE__*/_jsxs(View, {
95
+ accessibilityRole: "progressbar",
96
+ accessibilityLabel: defaultAccessibilityLabel,
97
+ accessibilityValue: {
98
+ min: 0,
99
+ max: 100,
100
+ now: normalizedValue
101
+ },
102
+ style: [computedContainerStyle, style],
103
+ ...rest,
104
+ children: [/*#__PURE__*/_jsxs(Svg, {
105
+ width: size,
106
+ height: size,
107
+ viewBox: `0 0 ${size} ${size}`,
108
+ children: [/*#__PURE__*/_jsx(Circle, {
109
+ cx: center,
110
+ cy: center,
111
+ r: radius,
112
+ stroke: trackColor,
113
+ strokeWidth: strokeWidth,
114
+ fill: "none"
115
+ }), isActive ? /*#__PURE__*/_jsx(Circle, {
116
+ cx: center,
117
+ cy: center,
118
+ r: radius,
119
+ stroke: progressColor,
120
+ strokeWidth: strokeWidth,
121
+ strokeLinecap: "round",
122
+ fill: "none",
123
+ strokeDasharray: `${circumference} ${circumference}`,
124
+ strokeDashoffset: circumference * (1 - normalizedValue / 100),
125
+ rotation: "-90",
126
+ originX: center,
127
+ originY: center
128
+ }) : null]
129
+ }), isActive ? /*#__PURE__*/_jsx(Text, {
130
+ style: [computedValueStyle, valueStyle],
131
+ children: displayValue
132
+ }) : /*#__PURE__*/_jsx(IconMinus, {
133
+ width: iconSize,
134
+ height: iconSize,
135
+ fill: iconColor,
136
+ color: iconColor,
137
+ style: iconStyle
138
+ })]
139
+ });
140
+ }
141
+ export default CircularProgressBar;