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.
- 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/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/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/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 +1 -1
- 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/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,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
|
|
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: '
|
|
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
|
-
}),
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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;
|