jfs-components 0.0.70 → 0.0.72
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.
- package/CHANGELOG.md +49 -0
- package/lib/commonjs/components/CardAdvisory/CardAdvisory.js +203 -0
- package/lib/commonjs/components/CardCTA/CardCTA.js +198 -16
- package/lib/commonjs/components/CardFinancialCondition/CardFinancialCondition.js +213 -0
- package/lib/commonjs/components/Carousel/Carousel.js +9 -7
- package/lib/commonjs/components/CircularProgressBar/CircularProgressBar.js +147 -0
- package/lib/commonjs/components/CircularProgressBarDoted/CircularProgressBarDoted.js +258 -0
- package/lib/commonjs/components/CircularRating/CircularRating.js +161 -0
- package/lib/commonjs/components/Gauge/Gauge.js +223 -0
- package/lib/commonjs/components/HoldingsCard/HoldingsCard.js +2 -2
- package/lib/commonjs/components/InstitutionBadge/InstitutionBadge.js +132 -0
- package/lib/commonjs/components/ListGroup/ListGroup.js +3 -1
- package/lib/commonjs/components/Nudge/Nudge.js +179 -87
- package/lib/commonjs/components/Radio/Radio.js +194 -0
- package/lib/commonjs/components/RadioButton/RadioButton.js +21 -188
- package/lib/commonjs/components/index.js +56 -0
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/CardAdvisory/CardAdvisory.js +197 -0
- package/lib/module/components/CardCTA/CardCTA.js +199 -17
- package/lib/module/components/CardFinancialCondition/CardFinancialCondition.js +207 -0
- package/lib/module/components/Carousel/Carousel.js +9 -7
- package/lib/module/components/CircularProgressBar/CircularProgressBar.js +141 -0
- package/lib/module/components/CircularProgressBarDoted/CircularProgressBarDoted.js +253 -0
- package/lib/module/components/CircularRating/CircularRating.js +155 -0
- package/lib/module/components/Gauge/Gauge.js +217 -0
- package/lib/module/components/HoldingsCard/HoldingsCard.js +2 -2
- package/lib/module/components/InstitutionBadge/InstitutionBadge.js +127 -0
- package/lib/module/components/ListGroup/ListGroup.js +3 -1
- package/lib/module/components/Nudge/Nudge.js +178 -87
- package/lib/module/components/Radio/Radio.js +188 -0
- package/lib/module/components/RadioButton/RadioButton.js +20 -185
- package/lib/module/components/index.js +12 -0
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/CardAdvisory/CardAdvisory.d.ts +49 -0
- package/lib/typescript/src/components/CardCTA/CardCTA.d.ts +16 -1
- package/lib/typescript/src/components/CardFinancialCondition/CardFinancialCondition.d.ts +50 -0
- package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +27 -0
- package/lib/typescript/src/components/CircularProgressBarDoted/CircularProgressBarDoted.d.ts +48 -0
- package/lib/typescript/src/components/CircularRating/CircularRating.d.ts +49 -0
- package/lib/typescript/src/components/Gauge/Gauge.d.ts +53 -0
- package/lib/typescript/src/components/InstitutionBadge/InstitutionBadge.d.ts +30 -0
- package/lib/typescript/src/components/Nudge/Nudge.d.ts +14 -11
- package/lib/typescript/src/components/Radio/Radio.d.ts +30 -0
- package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +20 -28
- package/lib/typescript/src/components/index.d.ts +13 -1
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/CardAdvisory/CardAdvisory.tsx +283 -0
- package/src/components/CardCTA/CardCTA.tsx +236 -13
- package/src/components/CardFinancialCondition/CardFinancialCondition.tsx +366 -0
- package/src/components/Carousel/Carousel.tsx +14 -6
- package/src/components/CircularProgressBar/CircularProgressBar.tsx +190 -0
- package/src/components/CircularProgressBarDoted/CircularProgressBarDoted.tsx +357 -0
- package/src/components/CircularRating/CircularRating.tsx +241 -0
- package/src/components/Gauge/Gauge.tsx +303 -0
- package/src/components/HoldingsCard/HoldingsCard.tsx +2 -2
- package/src/components/InstitutionBadge/InstitutionBadge.tsx +216 -0
- package/src/components/ListGroup/ListGroup.tsx +3 -1
- package/src/components/Nudge/Nudge.tsx +222 -82
- package/src/components/Radio/Radio.tsx +227 -0
- package/src/components/RadioButton/RadioButton.tsx +23 -225
- package/src/components/index.ts +13 -1
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- package/src/icons/registry.ts +1 -1
|
@@ -43,6 +43,8 @@ export function Carousel({
|
|
|
43
43
|
const gap = gapProp ?? tokenGap;
|
|
44
44
|
const containerPaddingH = parseFloat(getVariableByName('carousel/padding/horizontal', modes) || '0');
|
|
45
45
|
const containerPaddingV = parseFloat(getVariableByName('carousel/padding/vertical', modes) || '0');
|
|
46
|
+
// Outer container max height per Figma (`carousel/maxHeight`).
|
|
47
|
+
const maxHeight = parseFloat(getVariableByName('carousel/maxHeight', modes) || '280');
|
|
46
48
|
// Spacing between the cards row and the pagination dots uses `carousel/gap`.
|
|
47
49
|
const paginationOffset = gap;
|
|
48
50
|
|
|
@@ -154,7 +156,8 @@ export function Carousel({
|
|
|
154
156
|
|
|
155
157
|
// ---- Render ----
|
|
156
158
|
const outerStyle = {
|
|
157
|
-
paddingVertical: containerPaddingV
|
|
159
|
+
paddingVertical: containerPaddingV,
|
|
160
|
+
maxHeight
|
|
158
161
|
};
|
|
159
162
|
const contentContainerStyle = {
|
|
160
163
|
paddingHorizontal: containerPaddingH * 2,
|
|
@@ -262,13 +265,12 @@ export function Pagination({
|
|
|
262
265
|
const modes = propModes || ctxModes || {};
|
|
263
266
|
|
|
264
267
|
// Token resolution for dots — matches Figma tokens
|
|
265
|
-
// (carousel/pagination/gap, carousel/pagination/indicator/{
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
const dotActiveWidth = 16;
|
|
268
|
+
// (carousel/pagination/gap, carousel/pagination/indicator/{activeColor,inactiveColor,radius,size,activeWidth}).
|
|
269
|
+
const dotSize = parseFloat(getVariableByName('carousel/pagination/indicator/size', modes) || '6');
|
|
270
|
+
const dotActiveWidth = parseFloat(getVariableByName('carousel/pagination/indicator/activeWidth', modes) || '16');
|
|
269
271
|
const dotGap = parseFloat(getVariableByName('carousel/pagination/gap', modes) || '4');
|
|
270
|
-
const dotColor = getVariableByName('carousel/pagination/indicator/
|
|
271
|
-
const dotActiveColor = getVariableByName('carousel/pagination/indicator/
|
|
272
|
+
const dotColor = getVariableByName('carousel/pagination/indicator/inactiveColor', modes) || 'rgba(0,0,0,0.3)';
|
|
273
|
+
const dotActiveColor = getVariableByName('carousel/pagination/indicator/activeColor', modes) || '#170d0a';
|
|
272
274
|
const dotRadius = parseFloat(getVariableByName('carousel/pagination/indicator/radius', modes) || '9999');
|
|
273
275
|
const containerStyle = {
|
|
274
276
|
flexDirection: 'row',
|
|
@@ -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;
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React, { useMemo, useState } from 'react';
|
|
4
|
+
import { Pressable, StyleSheet, 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 { IconChevronright } from '../../icons/components/IconChevronright';
|
|
9
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
const DEFAULT_DOT_COUNT = 24;
|
|
11
|
+
const START_ANGLE_DEGREES = -90;
|
|
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 getBackgroundColor = (style, fallback) => {
|
|
33
|
+
const flattened = StyleSheet.flatten(style);
|
|
34
|
+
return typeof flattened.backgroundColor === 'string' ? flattened.backgroundColor : fallback;
|
|
35
|
+
};
|
|
36
|
+
function CircularProgressBarDoted({
|
|
37
|
+
value = 72,
|
|
38
|
+
dotCount = DEFAULT_DOT_COUNT,
|
|
39
|
+
label = 'Rating',
|
|
40
|
+
tierLabel = 'Doing great',
|
|
41
|
+
showChevron = true,
|
|
42
|
+
onPress,
|
|
43
|
+
onTierPress,
|
|
44
|
+
modes: propModes = EMPTY_MODES,
|
|
45
|
+
children,
|
|
46
|
+
style,
|
|
47
|
+
ringStyle,
|
|
48
|
+
trackDotStyle,
|
|
49
|
+
progressDotStyle,
|
|
50
|
+
contentStyle,
|
|
51
|
+
scoreTierStyle,
|
|
52
|
+
scoreTrendStyle,
|
|
53
|
+
labelStyle,
|
|
54
|
+
scoreLabelStyle,
|
|
55
|
+
tierLabelStyle,
|
|
56
|
+
accessibilityLabel,
|
|
57
|
+
onLayout,
|
|
58
|
+
...rest
|
|
59
|
+
}) {
|
|
60
|
+
const {
|
|
61
|
+
modes: globalModes
|
|
62
|
+
} = useTokens();
|
|
63
|
+
const modes = {
|
|
64
|
+
...globalModes,
|
|
65
|
+
...propModes
|
|
66
|
+
};
|
|
67
|
+
const [layoutSize, setLayoutSize] = useState(0);
|
|
68
|
+
const normalizedValue = clamp(value, 0, 100);
|
|
69
|
+
const resolvedDotCount = Math.max(1, Math.floor(dotCount));
|
|
70
|
+
const activeDots = normalizedValue <= 0 ? 0 : Math.ceil(normalizedValue / 100 * resolvedDotCount);
|
|
71
|
+
const dotShadowSize = toNumber(getVariableByName('circularProgressBarDoted/dot/shadow/size', modes), 6);
|
|
72
|
+
const baseDotSize = toNumber(getVariableByName('circularProgressBarDoted/dot/size', modes), 6);
|
|
73
|
+
const dotSize = baseDotSize + dotShadowSize;
|
|
74
|
+
const outerDotSize = dotSize;
|
|
75
|
+
const ringSize = layoutSize;
|
|
76
|
+
const ringRadius = Math.max(0, (ringSize - outerDotSize) / 2);
|
|
77
|
+
const trackDotColor = getBackgroundColor(trackDotStyle, getVariableByName('circularProgressBarDoted/trackDot/bg', modes) || '#ebebed');
|
|
78
|
+
const progressDotColor = getBackgroundColor(progressDotStyle, getVariableByName('circularProgressBarDoted/progressDot/bg', modes) || '#25ab21');
|
|
79
|
+
const contentGap = toNumber(getVariableByName('circularProgressBarDoted/gap', modes), 12);
|
|
80
|
+
const scoreTierGap = toNumber(getVariableByName('circularProgressBarDoted/scoreTier/gap', modes), 6);
|
|
81
|
+
const scoreTierWidth = toNumber(getVariableByName('circularProgressBarDoted/scoreTier/width', modes), 116);
|
|
82
|
+
const scoreTrendGap = toNumber(getVariableByName('circularProgressBarDoted/scoreTrend/gap', modes), 2);
|
|
83
|
+
const scoreTrendHeight = toNumber(getVariableByName('circularProgressBarDoted/scoreTrend/height', modes), 24);
|
|
84
|
+
const typographyFontFamily = getVariableByName('Typography/Font Family', modes) || 'JioType Var';
|
|
85
|
+
const labelColor = getVariableByName('circularProgressBarDoted/label/color', modes) || '#080d1a';
|
|
86
|
+
const labelFontSize = toNumber(getVariableByName('Typography/Size/Body/XS', modes), 12);
|
|
87
|
+
const labelFontWeight = toFontWeight(getVariableByName('Typography/Font Weight/Body Low (Regular)', modes), '400');
|
|
88
|
+
const scoreColor = getVariableByName('circularProgressBarDoted/scoreLabel/color', modes) || '#080d1a';
|
|
89
|
+
const scoreFontSize = toNumber(getVariableByName('circularProgressBarDoted/scoreLabel/fontSize', modes), 56);
|
|
90
|
+
const scoreFontFamily = getVariableByName('circularProgressBarDoted/scoreLabel/fontFamily', modes) || 'JioType Var';
|
|
91
|
+
const scoreLineHeight = toNumber(getVariableByName('circularProgressBarDoted/scoreLabel/lineHeight', modes), 56);
|
|
92
|
+
const scoreFontWeight = toFontWeight(getVariableByName('circularProgressBarDoted/scoreLabel/fontWeight', modes), '900');
|
|
93
|
+
const tierColor = getVariableByName('circularProgressBarDoted/tierLabel/color', modes) || '#080d1a';
|
|
94
|
+
const tierFontSize = toNumber(getVariableByName('Typography/Size/Body/M', modes), 16);
|
|
95
|
+
const tierFontWeight = toFontWeight(getVariableByName('Typography/Font Weight/Body High', modes), '700');
|
|
96
|
+
const iconColor = getVariableByName('circularProgressBarDoted/icon/color', modes) || '#303338';
|
|
97
|
+
const iconSize = toNumber(getVariableByName('circularProgressBarDoted/icon/size', modes), 24);
|
|
98
|
+
const dots = useMemo(() => Array.from({
|
|
99
|
+
length: resolvedDotCount
|
|
100
|
+
}, (_, index) => {
|
|
101
|
+
const angle = (360 / resolvedDotCount * index + START_ANGLE_DEGREES) * (Math.PI / 180);
|
|
102
|
+
const center = ringSize / 2;
|
|
103
|
+
return {
|
|
104
|
+
isActive: index < activeDots,
|
|
105
|
+
left: center + ringRadius * Math.cos(angle) - outerDotSize / 2,
|
|
106
|
+
top: center + ringRadius * Math.sin(angle) - outerDotSize / 2
|
|
107
|
+
};
|
|
108
|
+
}), [activeDots, outerDotSize, resolvedDotCount, ringRadius, ringSize]);
|
|
109
|
+
const containerStyle = {
|
|
110
|
+
alignItems: 'center',
|
|
111
|
+
alignSelf: 'stretch',
|
|
112
|
+
height: '100%',
|
|
113
|
+
justifyContent: 'center',
|
|
114
|
+
position: 'relative',
|
|
115
|
+
width: '100%'
|
|
116
|
+
};
|
|
117
|
+
const computedRingStyle = {
|
|
118
|
+
height: ringSize,
|
|
119
|
+
position: 'absolute',
|
|
120
|
+
width: ringSize
|
|
121
|
+
};
|
|
122
|
+
const dotOuterStyle = {
|
|
123
|
+
alignItems: 'center',
|
|
124
|
+
height: outerDotSize,
|
|
125
|
+
justifyContent: 'center',
|
|
126
|
+
position: 'absolute',
|
|
127
|
+
width: outerDotSize
|
|
128
|
+
};
|
|
129
|
+
const dotInnerStyle = {
|
|
130
|
+
borderRadius: dotSize / 2,
|
|
131
|
+
height: dotSize,
|
|
132
|
+
width: dotSize
|
|
133
|
+
};
|
|
134
|
+
const computedContentStyle = {
|
|
135
|
+
alignItems: 'center',
|
|
136
|
+
gap: contentGap,
|
|
137
|
+
justifyContent: 'center',
|
|
138
|
+
minWidth: scoreTierWidth
|
|
139
|
+
};
|
|
140
|
+
const computedScoreTierStyle = {
|
|
141
|
+
alignItems: 'center',
|
|
142
|
+
gap: scoreTierGap,
|
|
143
|
+
justifyContent: 'center',
|
|
144
|
+
width: '100%'
|
|
145
|
+
};
|
|
146
|
+
const computedScoreTrendStyle = {
|
|
147
|
+
alignItems: 'center',
|
|
148
|
+
flexDirection: 'row',
|
|
149
|
+
gap: scoreTrendGap,
|
|
150
|
+
height: scoreTrendHeight,
|
|
151
|
+
justifyContent: 'center',
|
|
152
|
+
minWidth: scoreTierWidth
|
|
153
|
+
};
|
|
154
|
+
const computedLabelStyle = {
|
|
155
|
+
color: labelColor,
|
|
156
|
+
fontFamily: typographyFontFamily,
|
|
157
|
+
fontSize: labelFontSize,
|
|
158
|
+
fontWeight: labelFontWeight,
|
|
159
|
+
lineHeight: labelFontSize * 1.3,
|
|
160
|
+
textAlign: 'center'
|
|
161
|
+
};
|
|
162
|
+
const computedScoreLabelStyle = {
|
|
163
|
+
color: scoreColor,
|
|
164
|
+
fontFamily: scoreFontFamily,
|
|
165
|
+
fontSize: scoreFontSize,
|
|
166
|
+
fontWeight: scoreFontWeight,
|
|
167
|
+
lineHeight: scoreLineHeight,
|
|
168
|
+
textAlign: 'center'
|
|
169
|
+
};
|
|
170
|
+
const computedTierLabelStyle = {
|
|
171
|
+
color: tierColor,
|
|
172
|
+
fontFamily: typographyFontFamily,
|
|
173
|
+
fontSize: tierFontSize,
|
|
174
|
+
fontWeight: tierFontWeight,
|
|
175
|
+
lineHeight: tierFontSize * 1.3,
|
|
176
|
+
textAlign: 'center'
|
|
177
|
+
};
|
|
178
|
+
const resolvedScoreLabel = String(Math.round(normalizedValue));
|
|
179
|
+
const defaultAccessibilityLabel = accessibilityLabel ?? `${label}. ${resolvedScoreLabel} out of 100. ${tierLabel}`;
|
|
180
|
+
const handleLayout = event => {
|
|
181
|
+
onLayout?.(event);
|
|
182
|
+
const {
|
|
183
|
+
width,
|
|
184
|
+
height
|
|
185
|
+
} = event.nativeEvent.layout;
|
|
186
|
+
const nextSize = Math.max(0, Math.min(width, height));
|
|
187
|
+
setLayoutSize(currentSize => currentSize === nextSize ? currentSize : nextSize);
|
|
188
|
+
};
|
|
189
|
+
const trendContent = /*#__PURE__*/_jsxs(_Fragment, {
|
|
190
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
191
|
+
numberOfLines: 1,
|
|
192
|
+
style: [computedTierLabelStyle, tierLabelStyle],
|
|
193
|
+
children: tierLabel
|
|
194
|
+
}), showChevron ? /*#__PURE__*/_jsx(IconChevronright, {
|
|
195
|
+
width: iconSize,
|
|
196
|
+
height: iconSize,
|
|
197
|
+
fill: iconColor,
|
|
198
|
+
color: iconColor
|
|
199
|
+
}) : null]
|
|
200
|
+
});
|
|
201
|
+
return /*#__PURE__*/_jsxs(Pressable, {
|
|
202
|
+
accessibilityRole: "progressbar",
|
|
203
|
+
accessibilityLabel: defaultAccessibilityLabel,
|
|
204
|
+
accessibilityValue: {
|
|
205
|
+
min: 0,
|
|
206
|
+
max: 100,
|
|
207
|
+
now: normalizedValue
|
|
208
|
+
},
|
|
209
|
+
disabled: !onPress,
|
|
210
|
+
onLayout: handleLayout,
|
|
211
|
+
onPress: onPress,
|
|
212
|
+
style: [containerStyle, style],
|
|
213
|
+
...rest,
|
|
214
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
215
|
+
pointerEvents: "none",
|
|
216
|
+
style: [computedRingStyle, ringStyle],
|
|
217
|
+
children: dots.map((dot, index) => /*#__PURE__*/_jsx(View, {
|
|
218
|
+
style: [dotOuterStyle, {
|
|
219
|
+
left: dot.left,
|
|
220
|
+
top: dot.top
|
|
221
|
+
}],
|
|
222
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
223
|
+
style: [dotInnerStyle, {
|
|
224
|
+
backgroundColor: dot.isActive ? progressDotColor : trackDotColor
|
|
225
|
+
}, dot.isActive ? progressDotStyle : trackDotStyle]
|
|
226
|
+
})
|
|
227
|
+
}, index))
|
|
228
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
229
|
+
style: [computedContentStyle, contentStyle],
|
|
230
|
+
children: children ? cloneChildrenWithModes(children, modes) : /*#__PURE__*/_jsxs(_Fragment, {
|
|
231
|
+
children: [/*#__PURE__*/_jsxs(View, {
|
|
232
|
+
style: [computedScoreTierStyle, scoreTierStyle],
|
|
233
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
234
|
+
style: [computedLabelStyle, labelStyle],
|
|
235
|
+
children: label
|
|
236
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
237
|
+
style: [computedScoreLabelStyle, scoreLabelStyle],
|
|
238
|
+
children: resolvedScoreLabel
|
|
239
|
+
})]
|
|
240
|
+
}), onTierPress ? /*#__PURE__*/_jsx(Pressable, {
|
|
241
|
+
accessibilityRole: "button",
|
|
242
|
+
onPress: onTierPress,
|
|
243
|
+
style: [computedScoreTrendStyle, scoreTrendStyle],
|
|
244
|
+
children: trendContent
|
|
245
|
+
}) : /*#__PURE__*/_jsx(View, {
|
|
246
|
+
style: [computedScoreTrendStyle, scoreTrendStyle],
|
|
247
|
+
children: trendContent
|
|
248
|
+
})]
|
|
249
|
+
})
|
|
250
|
+
})]
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
export default CircularProgressBarDoted;
|
|
@@ -0,0 +1,155 @@
|
|
|
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 CircularProgressBarDoted from '../CircularProgressBarDoted/CircularProgressBarDoted';
|
|
10
|
+
import Nudge from '../Nudge/Nudge';
|
|
11
|
+
import { jsx as _jsx, Fragment as _Fragment, 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 resolveCircularRatingTokens(modes) {
|
|
32
|
+
const gap = toNumber(getVariableByName('circularRating/gap', modes), 32);
|
|
33
|
+
const padding = toNumber(getVariableByName('circularRating/padding', modes), 10);
|
|
34
|
+
const footerGap = toNumber(getVariableByName('circularRating/footer/gap', modes), 4);
|
|
35
|
+
const footerHeight = toNumber(getVariableByName('circularRating/footer/height', modes), 16);
|
|
36
|
+
const footerTextColor = getVariableByName('circularRating/footer/text/color', modes) || '#0d0d0f';
|
|
37
|
+
const footerTextFontSize = toNumber(getVariableByName('circularRating/footer/text/fontSize', modes), 12);
|
|
38
|
+
const footerTextFontFamily = getVariableByName('circularRating/footer/text/fontFamily', modes) || 'JioType Var';
|
|
39
|
+
const footerTextLineHeight = toNumber(getVariableByName('circularRating/footer/text/lineHeight', modes), 16);
|
|
40
|
+
const footerTextFontWeight = toFontWeight(getVariableByName('circularRating/footer/text/fontWeight', modes), '400');
|
|
41
|
+
return {
|
|
42
|
+
containerStyle: {
|
|
43
|
+
alignItems: 'flex-start',
|
|
44
|
+
gap,
|
|
45
|
+
justifyContent: 'center',
|
|
46
|
+
padding
|
|
47
|
+
},
|
|
48
|
+
ratingStyle: {
|
|
49
|
+
height: 320,
|
|
50
|
+
width: 320
|
|
51
|
+
},
|
|
52
|
+
footerStyle: {
|
|
53
|
+
alignItems: 'center',
|
|
54
|
+
flexDirection: 'row',
|
|
55
|
+
gap: footerGap,
|
|
56
|
+
height: footerHeight,
|
|
57
|
+
justifyContent: 'center',
|
|
58
|
+
width: '100%'
|
|
59
|
+
},
|
|
60
|
+
footerTextStyle: {
|
|
61
|
+
color: footerTextColor,
|
|
62
|
+
fontFamily: footerTextFontFamily,
|
|
63
|
+
fontSize: footerTextFontSize,
|
|
64
|
+
fontWeight: footerTextFontWeight,
|
|
65
|
+
lineHeight: footerTextLineHeight,
|
|
66
|
+
textAlign: 'center'
|
|
67
|
+
},
|
|
68
|
+
footerIconColor: getVariableByName('circularRating/footer/icon/color', modes) || '#1a1c1f',
|
|
69
|
+
footerIconSize: toNumber(getVariableByName('circularRating/footer/icon/size', modes), 16)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function CircularRating({
|
|
73
|
+
value = 72,
|
|
74
|
+
dotCount = 24,
|
|
75
|
+
label = 'Rating',
|
|
76
|
+
tierLabel = 'Doing great',
|
|
77
|
+
footerText = 'Updated on 1 March',
|
|
78
|
+
showFooterIcon = true,
|
|
79
|
+
showNudge = true,
|
|
80
|
+
nudgeBody = 'Split this transaction into installments',
|
|
81
|
+
nudgeButtonLabel = 'Button',
|
|
82
|
+
onPressNudgeButton,
|
|
83
|
+
onTierPress,
|
|
84
|
+
footerSlot,
|
|
85
|
+
nudgeSlot,
|
|
86
|
+
modes: propModes = EMPTY_MODES,
|
|
87
|
+
style,
|
|
88
|
+
ratingStyle,
|
|
89
|
+
footerStyle,
|
|
90
|
+
footerTextStyle,
|
|
91
|
+
nudgeStyle,
|
|
92
|
+
accessibilityLabel,
|
|
93
|
+
...rest
|
|
94
|
+
}) {
|
|
95
|
+
const {
|
|
96
|
+
modes: globalModes
|
|
97
|
+
} = useTokens();
|
|
98
|
+
const modes = useMemo(() => globalModes === EMPTY_MODES && propModes === EMPTY_MODES ? EMPTY_MODES : {
|
|
99
|
+
...globalModes,
|
|
100
|
+
...propModes
|
|
101
|
+
}, [globalModes, propModes]);
|
|
102
|
+
const tokens = useMemo(() => resolveCircularRatingTokens(modes), [modes]);
|
|
103
|
+
const processedFooterSlot = useMemo(() => {
|
|
104
|
+
if (!footerSlot) return null;
|
|
105
|
+
const processed = cloneChildrenWithModes(React.Children.toArray(footerSlot), modes);
|
|
106
|
+
return processed.length === 1 ? processed[0] : processed;
|
|
107
|
+
}, [footerSlot, modes]);
|
|
108
|
+
const processedNudgeSlot = useMemo(() => {
|
|
109
|
+
if (!nudgeSlot) return null;
|
|
110
|
+
const processed = cloneChildrenWithModes(React.Children.toArray(nudgeSlot), modes);
|
|
111
|
+
return processed.length === 1 ? processed[0] : processed;
|
|
112
|
+
}, [nudgeSlot, modes]);
|
|
113
|
+
const defaultAccessibilityLabel = accessibilityLabel ?? `${label}. ${Math.round(value)} out of 100. ${tierLabel}. ${footerText}`;
|
|
114
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
115
|
+
accessibilityLabel: defaultAccessibilityLabel,
|
|
116
|
+
style: [tokens.containerStyle, style],
|
|
117
|
+
...rest,
|
|
118
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
119
|
+
style: [tokens.ratingStyle, ratingStyle],
|
|
120
|
+
children: /*#__PURE__*/_jsx(CircularProgressBarDoted, {
|
|
121
|
+
value: value,
|
|
122
|
+
dotCount: dotCount,
|
|
123
|
+
label: label,
|
|
124
|
+
tierLabel: tierLabel,
|
|
125
|
+
onTierPress: onTierPress,
|
|
126
|
+
modes: modes
|
|
127
|
+
})
|
|
128
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
129
|
+
style: [tokens.footerStyle, footerStyle],
|
|
130
|
+
children: processedFooterSlot || /*#__PURE__*/_jsxs(_Fragment, {
|
|
131
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
132
|
+
numberOfLines: 1,
|
|
133
|
+
style: [tokens.footerTextStyle, footerTextStyle],
|
|
134
|
+
children: footerText
|
|
135
|
+
}), showFooterIcon ? /*#__PURE__*/_jsx(Icon, {
|
|
136
|
+
name: "ic_info",
|
|
137
|
+
size: tokens.footerIconSize,
|
|
138
|
+
color: tokens.footerIconColor,
|
|
139
|
+
accessibilityElementsHidden: true,
|
|
140
|
+
importantForAccessibility: "no"
|
|
141
|
+
}) : null]
|
|
142
|
+
})
|
|
143
|
+
}), showNudge ? processedNudgeSlot || /*#__PURE__*/_jsx(Nudge, {
|
|
144
|
+
type: "inline-compact",
|
|
145
|
+
body: nudgeBody,
|
|
146
|
+
buttonLabel: nudgeButtonLabel,
|
|
147
|
+
onPressButton: onPressNudgeButton,
|
|
148
|
+
modes: modes,
|
|
149
|
+
style: [{
|
|
150
|
+
width: 312
|
|
151
|
+
}, nudgeStyle]
|
|
152
|
+
}) : null]
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
export default /*#__PURE__*/React.memo(CircularRating);
|