jfs-components 0.0.69 → 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.
- package/CHANGELOG.md +20 -0
- package/lib/commonjs/components/CardAdvisory/CardAdvisory.js +203 -0
- package/lib/commonjs/components/CardCTA/CardCTA.js +198 -16
- 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/ListGroup/ListGroup.js +3 -1
- package/lib/commonjs/components/MediaCard/GlassFill.js +62 -0
- package/lib/commonjs/components/MediaCard/GlassFill.web.js +48 -0
- package/lib/commonjs/components/MediaCard/MediaCard.js +28 -31
- package/lib/commonjs/components/Nudge/Nudge.js +179 -87
- package/lib/commonjs/components/index.js +35 -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/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/ListGroup/ListGroup.js +3 -1
- package/lib/module/components/MediaCard/GlassFill.js +57 -0
- package/lib/module/components/MediaCard/GlassFill.web.js +43 -0
- package/lib/module/components/MediaCard/MediaCard.js +29 -32
- package/lib/module/components/Nudge/Nudge.js +178 -87
- package/lib/module/components/index.js +5 -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/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/MediaCard/GlassFill.d.ts +47 -0
- package/lib/typescript/src/components/MediaCard/GlassFill.web.d.ts +20 -0
- package/lib/typescript/src/components/MediaCard/MediaCard.d.ts +17 -13
- package/lib/typescript/src/components/Nudge/Nudge.d.ts +14 -11
- package/lib/typescript/src/components/index.d.ts +6 -1
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +3 -2
- package/src/components/CardAdvisory/CardAdvisory.tsx +283 -0
- package/src/components/CardCTA/CardCTA.tsx +236 -13
- 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/ListGroup/ListGroup.tsx +3 -1
- package/src/components/MediaCard/GlassFill.tsx +89 -0
- package/src/components/MediaCard/GlassFill.web.tsx +53 -0
- package/src/components/MediaCard/MediaCard.tsx +29 -48
- package/src/components/Nudge/Nudge.tsx +222 -82
- package/src/components/index.ts +6 -1
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- package/src/icons/registry.ts +1 -1
|
@@ -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);
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { StyleSheet, View, Text } from 'react-native';
|
|
5
|
+
import Svg, { Path } from 'react-native-svg';
|
|
6
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
7
|
+
import { useTokens } from '../../design-tokens/JFSThemeProvider';
|
|
8
|
+
import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
|
|
9
|
+
import SupportText from '../SupportText/SupportText';
|
|
10
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
11
|
+
const clamp = (value, min, max) => {
|
|
12
|
+
if (max <= min) {
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
return Math.min(1, Math.max(0, (value - min) / (max - min)));
|
|
16
|
+
};
|
|
17
|
+
const toNumber = (value, fallback) => {
|
|
18
|
+
if (typeof value === 'number') {
|
|
19
|
+
return Number.isFinite(value) ? value : fallback;
|
|
20
|
+
}
|
|
21
|
+
if (typeof value === 'string') {
|
|
22
|
+
const parsed = Number(value);
|
|
23
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
24
|
+
}
|
|
25
|
+
return fallback;
|
|
26
|
+
};
|
|
27
|
+
const toFontWeight = (value, fallback) => {
|
|
28
|
+
if (typeof value === 'number') {
|
|
29
|
+
return String(value);
|
|
30
|
+
}
|
|
31
|
+
if (typeof value === 'string') {
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
return fallback;
|
|
35
|
+
};
|
|
36
|
+
const getStrokeColor = (style, fallback) => {
|
|
37
|
+
const flattened = StyleSheet.flatten(style);
|
|
38
|
+
return typeof flattened.backgroundColor === 'string' ? flattened.backgroundColor : fallback;
|
|
39
|
+
};
|
|
40
|
+
function Gauge({
|
|
41
|
+
value = 84,
|
|
42
|
+
min = 0,
|
|
43
|
+
max = 100,
|
|
44
|
+
valueLabel,
|
|
45
|
+
title = 'Your score is based on strong data',
|
|
46
|
+
caption = 'Add more details for even better accuracy',
|
|
47
|
+
supportText = 'Support Text',
|
|
48
|
+
supportTextStatus = 'Success',
|
|
49
|
+
showTitle = true,
|
|
50
|
+
showCaption = true,
|
|
51
|
+
showSupportText = true,
|
|
52
|
+
modes: propModes = EMPTY_MODES,
|
|
53
|
+
children,
|
|
54
|
+
style,
|
|
55
|
+
arcStyle,
|
|
56
|
+
readoutStyle,
|
|
57
|
+
titleStyle,
|
|
58
|
+
valueStyle,
|
|
59
|
+
captionStyle,
|
|
60
|
+
trackStyle,
|
|
61
|
+
progressStyle,
|
|
62
|
+
accessibilityLabel,
|
|
63
|
+
...rest
|
|
64
|
+
}) {
|
|
65
|
+
const {
|
|
66
|
+
modes: globalModes
|
|
67
|
+
} = useTokens();
|
|
68
|
+
const modes = {
|
|
69
|
+
...globalModes,
|
|
70
|
+
...propModes
|
|
71
|
+
};
|
|
72
|
+
const supportTextModes = {
|
|
73
|
+
...modes,
|
|
74
|
+
Status: supportTextStatus
|
|
75
|
+
};
|
|
76
|
+
const gap = toNumber(getVariableByName('gauge/gap', modes), 30);
|
|
77
|
+
const padding = toNumber(getVariableByName('gauge/padding', modes), 16);
|
|
78
|
+
const width = toNumber(getVariableByName('gauge/width', modes), 299);
|
|
79
|
+
const arcWidth = toNumber(getVariableByName('gauge/arc/width', modes), 267);
|
|
80
|
+
const arcHeight = arcWidth * (186 / 328);
|
|
81
|
+
const trackSize = toNumber(getVariableByName('gauge/track/size', modes), 244);
|
|
82
|
+
const trackInnerRadius = toNumber(getVariableByName('gauge/track/radius', modes), 99);
|
|
83
|
+
const progressInnerRadius = toNumber(getVariableByName('gauge/progress/radius', modes), 99);
|
|
84
|
+
const innerRadius = Math.min(trackInnerRadius, progressInnerRadius);
|
|
85
|
+
const strokeWidth = Math.max(1, trackSize / 2 - innerRadius);
|
|
86
|
+
const radius = innerRadius + strokeWidth / 2;
|
|
87
|
+
const centerX = arcWidth / 2;
|
|
88
|
+
const centerY = Math.min(arcHeight - strokeWidth / 2, radius + strokeWidth / 2);
|
|
89
|
+
const startX = centerX - radius;
|
|
90
|
+
const endX = centerX + radius;
|
|
91
|
+
const arcPath = `M ${startX} ${centerY} A ${radius} ${radius} 0 0 1 ${endX} ${centerY}`;
|
|
92
|
+
const arcLength = Math.PI * radius;
|
|
93
|
+
const progress = clamp(value, min, max);
|
|
94
|
+
const titleColor = getVariableByName('gauge/title/color', modes) || '#0c0d10';
|
|
95
|
+
const titleFontSize = toNumber(getVariableByName('gauge/title/fontSize', modes), 23);
|
|
96
|
+
const titleLineHeight = toNumber(getVariableByName('gauge/title/lineHeight', modes), 23);
|
|
97
|
+
const titleFontFamily = getVariableByName('gauge/title/fontFamily', modes) || 'JioType Var';
|
|
98
|
+
const titleFontWeight = toFontWeight(getVariableByName('gauge/title/fontWeight', modes), '900');
|
|
99
|
+
const titleHeight = toNumber(getVariableByName('gauge/title/height', modes), 46);
|
|
100
|
+
const readoutGap = toNumber(getVariableByName('gauge/readout/gap', modes), 8);
|
|
101
|
+
const readoutPadding = toNumber(getVariableByName('gauge/readout/padding', modes), 8);
|
|
102
|
+
const valueColor = getVariableByName('gauge/readout/value/color', modes) || '#0c0d10';
|
|
103
|
+
const valueFontSize = toNumber(getVariableByName('gauge/readout/value/fontSize', modes), 36);
|
|
104
|
+
const valueLineHeight = toNumber(getVariableByName('gauge/readout/value/lineHeight', modes), 36);
|
|
105
|
+
const valueFontFamily = getVariableByName('gauge/readout/value/fontFamily', modes) || 'JioType Var';
|
|
106
|
+
const valueFontWeight = toFontWeight(getVariableByName('gauge/readout/value/fontWeight', modes), '800');
|
|
107
|
+
const captionColor = getVariableByName('gauge/caption/color', modes) || '#0c0d10';
|
|
108
|
+
const captionFontSize = toNumber(getVariableByName('gauge/caption/fontSize', modes), 14);
|
|
109
|
+
const captionLineHeight = toNumber(getVariableByName('gauge/caption/lineHeight', modes), 18);
|
|
110
|
+
const captionFontFamily = getVariableByName('gauge/caption/fontFamily', modes) || 'JioType Var';
|
|
111
|
+
const captionFontWeight = toFontWeight(getVariableByName('gauge/caption/fontWeight', modes), '500');
|
|
112
|
+
const trackColor = getStrokeColor(trackStyle, getVariableByName('gauge/track/color', modes) || '#f5f5f6');
|
|
113
|
+
const progressColor = getStrokeColor(progressStyle, getVariableByName('gauge/progress/color', modes) || '#25ab21');
|
|
114
|
+
const containerStyle = {
|
|
115
|
+
alignItems: 'center',
|
|
116
|
+
gap,
|
|
117
|
+
padding,
|
|
118
|
+
width
|
|
119
|
+
};
|
|
120
|
+
const computedTitleStyle = {
|
|
121
|
+
color: titleColor,
|
|
122
|
+
fontFamily: titleFontFamily,
|
|
123
|
+
fontSize: titleFontSize,
|
|
124
|
+
fontWeight: titleFontWeight,
|
|
125
|
+
lineHeight: titleLineHeight,
|
|
126
|
+
minHeight: titleHeight,
|
|
127
|
+
textAlign: 'center',
|
|
128
|
+
width: '100%'
|
|
129
|
+
};
|
|
130
|
+
const computedArcStyle = {
|
|
131
|
+
height: arcHeight,
|
|
132
|
+
overflow: 'hidden',
|
|
133
|
+
position: 'relative',
|
|
134
|
+
width: '100%'
|
|
135
|
+
};
|
|
136
|
+
const computedReadoutStyle = {
|
|
137
|
+
alignItems: 'center',
|
|
138
|
+
gap: readoutGap,
|
|
139
|
+
left: 0,
|
|
140
|
+
padding: readoutPadding,
|
|
141
|
+
position: 'absolute',
|
|
142
|
+
right: 0,
|
|
143
|
+
top: arcHeight * 0.42
|
|
144
|
+
};
|
|
145
|
+
const computedValueStyle = {
|
|
146
|
+
color: valueColor,
|
|
147
|
+
fontFamily: valueFontFamily,
|
|
148
|
+
fontSize: valueFontSize,
|
|
149
|
+
fontWeight: valueFontWeight,
|
|
150
|
+
lineHeight: valueLineHeight,
|
|
151
|
+
textAlign: 'center'
|
|
152
|
+
};
|
|
153
|
+
const computedCaptionStyle = {
|
|
154
|
+
color: captionColor,
|
|
155
|
+
fontFamily: captionFontFamily,
|
|
156
|
+
fontSize: captionFontSize,
|
|
157
|
+
fontWeight: captionFontWeight,
|
|
158
|
+
lineHeight: captionLineHeight,
|
|
159
|
+
textAlign: 'center',
|
|
160
|
+
width: '100%'
|
|
161
|
+
};
|
|
162
|
+
const defaultValueLabel = valueLabel ?? String(Math.round(value));
|
|
163
|
+
const defaultAccessibilityLabel = accessibilityLabel ?? `${title}. ${defaultValueLabel} out of ${max}. ${supportText}. ${caption}`;
|
|
164
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
165
|
+
accessibilityRole: "progressbar",
|
|
166
|
+
accessibilityLabel: defaultAccessibilityLabel,
|
|
167
|
+
accessibilityValue: {
|
|
168
|
+
min,
|
|
169
|
+
max,
|
|
170
|
+
now: value
|
|
171
|
+
},
|
|
172
|
+
style: [containerStyle, style],
|
|
173
|
+
...rest,
|
|
174
|
+
children: [showTitle ? /*#__PURE__*/_jsx(Text, {
|
|
175
|
+
style: [computedTitleStyle, titleStyle],
|
|
176
|
+
children: title
|
|
177
|
+
}) : null, /*#__PURE__*/_jsxs(View, {
|
|
178
|
+
style: [computedArcStyle, arcStyle],
|
|
179
|
+
children: [/*#__PURE__*/_jsxs(Svg, {
|
|
180
|
+
width: "100%",
|
|
181
|
+
height: arcHeight,
|
|
182
|
+
viewBox: `0 0 ${arcWidth} ${arcHeight}`,
|
|
183
|
+
children: [/*#__PURE__*/_jsx(Path, {
|
|
184
|
+
d: arcPath,
|
|
185
|
+
stroke: trackColor,
|
|
186
|
+
strokeWidth: strokeWidth,
|
|
187
|
+
strokeLinecap: "butt",
|
|
188
|
+
fill: "none"
|
|
189
|
+
}), /*#__PURE__*/_jsx(Path, {
|
|
190
|
+
d: arcPath,
|
|
191
|
+
stroke: progressColor,
|
|
192
|
+
strokeWidth: strokeWidth,
|
|
193
|
+
strokeLinecap: "butt",
|
|
194
|
+
fill: "none",
|
|
195
|
+
strokeDasharray: `${arcLength} ${arcLength}`,
|
|
196
|
+
strokeDashoffset: arcLength * (1 - progress)
|
|
197
|
+
})]
|
|
198
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
199
|
+
style: [computedReadoutStyle, readoutStyle],
|
|
200
|
+
children: children ? cloneChildrenWithModes(children, modes) : /*#__PURE__*/_jsxs(_Fragment, {
|
|
201
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
202
|
+
style: [computedValueStyle, valueStyle],
|
|
203
|
+
children: defaultValueLabel
|
|
204
|
+
}), showSupportText ? /*#__PURE__*/_jsx(SupportText, {
|
|
205
|
+
label: supportText,
|
|
206
|
+
status: supportTextStatus,
|
|
207
|
+
modes: supportTextModes
|
|
208
|
+
}) : null]
|
|
209
|
+
})
|
|
210
|
+
})]
|
|
211
|
+
}), showCaption ? /*#__PURE__*/_jsx(Text, {
|
|
212
|
+
style: [computedCaptionStyle, captionStyle],
|
|
213
|
+
children: caption
|
|
214
|
+
}) : null]
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
export default Gauge;
|
|
@@ -47,7 +47,9 @@ function ListGroup({
|
|
|
47
47
|
}) {
|
|
48
48
|
// Resolve container tokens
|
|
49
49
|
const backgroundColor = getVariableByName('listGroup/background', modes) || 'rgba(255,255,255,0)';
|
|
50
|
-
|
|
50
|
+
// The current exported token aliases a missing Figma variable. Keep the
|
|
51
|
+
// transparent fallback without logging the missing alias on every render.
|
|
52
|
+
const borderColor = 'rgba(255,255,255,0)';
|
|
51
53
|
const borderWidth = getVariableByName('listGroup/borderWidth', modes) || 0;
|
|
52
54
|
const gap = getVariableByName('listGroup/gap', modes) || 12;
|
|
53
55
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { View, StyleSheet, Platform } from 'react-native';
|
|
5
|
+
import { BlurView } from '@react-native-community/blur';
|
|
6
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
+
const DEFAULT_FALLBACK_DARK = '#1414174a';
|
|
8
|
+
const DEFAULT_FALLBACK_LIGHT = '#ffffff66';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Glass / frosted surface for native (iOS + Android).
|
|
12
|
+
*
|
|
13
|
+
* Why this lives in its own platform-split file:
|
|
14
|
+
* - `@react-native-community/blur` is a native-only module. Importing it on
|
|
15
|
+
* web throws because the JS shim references native components that aren't
|
|
16
|
+
* registered there. By using Metro's platform-extension resolution
|
|
17
|
+
* (`GlassFill.tsx` for native, `GlassFill.web.tsx` for web), we keep the
|
|
18
|
+
* web bundle free of any native-only imports.
|
|
19
|
+
* - Centralizes the `intensity` (0–100) -> `blurAmount` (0–32) mapping so
|
|
20
|
+
* callers can keep the Figma token semantics they already know.
|
|
21
|
+
*
|
|
22
|
+
* On iOS this is a real `UIVisualEffectView` (true OS-level live blur).
|
|
23
|
+
* On Android this uses the community blur view (RealtimeBlurView). On devices
|
|
24
|
+
* where realtime blur is unavailable, `reducedTransparencyFallbackColor` (and
|
|
25
|
+
* the explicit `overlayColor`) ensure the surface still renders as a
|
|
26
|
+
* translucent tinted scrim instead of disappearing.
|
|
27
|
+
*/
|
|
28
|
+
function GlassFill({
|
|
29
|
+
tint = 'dark',
|
|
30
|
+
intensity = 50,
|
|
31
|
+
overlayColor,
|
|
32
|
+
style
|
|
33
|
+
}) {
|
|
34
|
+
const blurType = tint === 'light' ? 'light' : 'dark';
|
|
35
|
+
const blurAmount = Math.max(0, Math.min(32, Math.round(intensity * 0.32)));
|
|
36
|
+
const fallbackColor = overlayColor ?? (tint === 'light' ? DEFAULT_FALLBACK_LIGHT : DEFAULT_FALLBACK_DARK);
|
|
37
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
38
|
+
style: [StyleSheet.absoluteFill, style],
|
|
39
|
+
pointerEvents: "none",
|
|
40
|
+
children: [/*#__PURE__*/_jsx(BlurView, {
|
|
41
|
+
style: StyleSheet.absoluteFill,
|
|
42
|
+
blurType: blurType,
|
|
43
|
+
blurAmount: blurAmount,
|
|
44
|
+
reducedTransparencyFallbackColor: fallbackColor
|
|
45
|
+
}), overlayColor != null ? /*#__PURE__*/_jsx(View, {
|
|
46
|
+
style: [StyleSheet.absoluteFill, {
|
|
47
|
+
backgroundColor: overlayColor
|
|
48
|
+
}]
|
|
49
|
+
}) : null, Platform.OS === 'android' ? /*#__PURE__*/_jsx(View, {
|
|
50
|
+
style: [StyleSheet.absoluteFill, {
|
|
51
|
+
backgroundColor: 'rgba(255,255,255,0.03)',
|
|
52
|
+
opacity: 0.6
|
|
53
|
+
}]
|
|
54
|
+
}) : null]
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
export default GlassFill;
|