jfs-components 0.0.72 → 0.0.74
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 +28 -0
- package/lib/commonjs/components/AccordionCheckbox/AccordionCheckbox.js +239 -0
- package/lib/commonjs/components/AccountCard/AccountCard.js +247 -0
- package/lib/commonjs/components/AppBar/AppBar.js +17 -11
- package/lib/commonjs/components/BrandChip/BrandChip.js +149 -0
- package/lib/commonjs/components/CardBankAccount/CardBankAccount.js +229 -0
- package/lib/commonjs/components/CardInsight/CardInsight.js +166 -0
- package/lib/commonjs/components/CheckboxGroup/CheckboxGroup.js +67 -0
- package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +140 -0
- package/lib/commonjs/components/CircularProgressBar/CircularProgressBar.js +56 -9
- package/lib/commonjs/components/CoverageBarComparison/CoverageBarComparison.js +272 -0
- package/lib/commonjs/components/CoverageRing/CoverageRing.js +141 -0
- package/lib/commonjs/components/DonutChart/DonutChart.js +309 -0
- package/lib/commonjs/components/DonutChartSummary/DonutChartSummary.js +155 -0
- package/lib/commonjs/components/Dropdown/Dropdown.js +214 -0
- package/lib/commonjs/components/DropdownInput/DropdownInput.js +542 -0
- package/lib/commonjs/components/FormField/FormField.js +328 -178
- package/lib/commonjs/components/LinearMeter/LinearMeter.js +9 -28
- package/lib/commonjs/components/LinearProgress/LinearProgress.js +68 -0
- package/lib/commonjs/components/LottieIntroBlock/LottieIntroBlock.js +150 -0
- package/lib/commonjs/components/MetricLegendItem/MetricLegendItem.js +95 -0
- package/lib/commonjs/components/MonthlyStatusGrid/MonthlyStatusGrid.js +286 -0
- package/lib/commonjs/components/OTP/OTP.js +381 -37
- package/lib/commonjs/components/PageHero/PageHero.js +153 -0
- package/lib/commonjs/components/PoweredByLabel/PoweredByLabel.js +135 -0
- package/lib/commonjs/components/PoweredByLabel/finvu.png +0 -0
- package/lib/commonjs/components/ProductOverview/ProductOverview.js +147 -0
- package/lib/commonjs/components/RangeTrack/RangeTrack.js +269 -0
- package/lib/commonjs/components/SavingsGoalSummary/SavingsGoalSummary.js +181 -0
- package/lib/commonjs/components/SegmentedTrack/SegmentedTrack.js +171 -0
- package/lib/commonjs/components/StatGroup/StatGroup.js +128 -0
- package/lib/commonjs/components/StatItem/StatItem.js +65 -35
- package/lib/commonjs/components/StrengthIndicator/StrengthIndicator.js +157 -0
- package/lib/commonjs/components/SummaryTile/SummaryTile.js +150 -0
- package/lib/commonjs/components/Text/Text.js +9 -2
- package/lib/commonjs/components/Tooltip/Tooltip.js +34 -27
- package/lib/commonjs/components/index.js +231 -1
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/commonjs/utils/index.js +7 -0
- package/lib/commonjs/utils/number-utils.js +57 -0
- package/lib/module/components/AccordionCheckbox/AccordionCheckbox.js +233 -0
- package/lib/module/components/AccountCard/AccountCard.js +241 -0
- package/lib/module/components/AppBar/AppBar.js +17 -11
- package/lib/module/components/BrandChip/BrandChip.js +143 -0
- package/lib/module/components/CardBankAccount/CardBankAccount.js +223 -0
- package/lib/module/components/CardInsight/CardInsight.js +161 -0
- package/lib/module/components/CheckboxGroup/CheckboxGroup.js +62 -0
- package/lib/module/components/CheckboxItem/CheckboxItem.js +134 -0
- package/lib/module/components/CircularProgressBar/CircularProgressBar.js +56 -9
- package/lib/module/components/CoverageBarComparison/CoverageBarComparison.js +266 -0
- package/lib/module/components/CoverageRing/CoverageRing.js +136 -0
- package/lib/module/components/DonutChart/DonutChart.js +303 -0
- package/lib/module/components/DonutChartSummary/DonutChartSummary.js +150 -0
- package/lib/module/components/Dropdown/Dropdown.js +206 -0
- package/lib/module/components/DropdownInput/DropdownInput.js +536 -0
- package/lib/module/components/FormField/FormField.js +330 -180
- package/lib/module/components/LinearMeter/LinearMeter.js +9 -28
- package/lib/module/components/LinearProgress/LinearProgress.js +63 -0
- package/lib/module/components/LottieIntroBlock/LottieIntroBlock.js +144 -0
- package/lib/module/components/MetricLegendItem/MetricLegendItem.js +90 -0
- package/lib/module/components/MonthlyStatusGrid/MonthlyStatusGrid.js +281 -0
- package/lib/module/components/OTP/OTP.js +381 -38
- package/lib/module/components/PageHero/PageHero.js +147 -0
- package/lib/module/components/PoweredByLabel/PoweredByLabel.js +130 -0
- package/lib/module/components/PoweredByLabel/finvu.png +0 -0
- package/lib/module/components/ProductOverview/ProductOverview.js +142 -0
- package/lib/module/components/RangeTrack/RangeTrack.js +263 -0
- package/lib/module/components/SavingsGoalSummary/SavingsGoalSummary.js +175 -0
- package/lib/module/components/SegmentedTrack/SegmentedTrack.js +166 -0
- package/lib/module/components/StatGroup/StatGroup.js +123 -0
- package/lib/module/components/StatItem/StatItem.js +66 -36
- package/lib/module/components/StrengthIndicator/StrengthIndicator.js +152 -0
- package/lib/module/components/SummaryTile/SummaryTile.js +145 -0
- package/lib/module/components/Text/Text.js +9 -2
- package/lib/module/components/Tooltip/Tooltip.js +34 -27
- package/lib/module/components/index.js +28 -2
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/icons/registry.js +1 -1
- package/lib/module/utils/index.js +2 -1
- package/lib/module/utils/number-utils.js +53 -0
- package/lib/typescript/src/components/AccordionCheckbox/AccordionCheckbox.d.ts +71 -0
- package/lib/typescript/src/components/AccountCard/AccountCard.d.ts +81 -0
- package/lib/typescript/src/components/BrandChip/BrandChip.d.ts +43 -0
- package/lib/typescript/src/components/CardBankAccount/CardBankAccount.d.ts +86 -0
- package/lib/typescript/src/components/CardInsight/CardInsight.d.ts +48 -0
- package/lib/typescript/src/components/CheckboxGroup/CheckboxGroup.d.ts +41 -0
- package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +72 -0
- package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +11 -1
- package/lib/typescript/src/components/CoverageBarComparison/CoverageBarComparison.d.ts +105 -0
- package/lib/typescript/src/components/CoverageRing/CoverageRing.d.ts +90 -0
- package/lib/typescript/src/components/DonutChart/DonutChart.d.ts +117 -0
- package/lib/typescript/src/components/DonutChartSummary/DonutChartSummary.d.ts +103 -0
- package/lib/typescript/src/components/Dropdown/Dropdown.d.ts +62 -0
- package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +107 -0
- package/lib/typescript/src/components/FormField/FormField.d.ts +76 -19
- package/lib/typescript/src/components/LinearProgress/LinearProgress.d.ts +17 -0
- package/lib/typescript/src/components/LottieIntroBlock/LottieIntroBlock.d.ts +58 -0
- package/lib/typescript/src/components/MetricLegendItem/MetricLegendItem.d.ts +37 -0
- package/lib/typescript/src/components/MonthlyStatusGrid/MonthlyStatusGrid.d.ts +119 -0
- package/lib/typescript/src/components/OTP/OTP.d.ts +88 -2
- package/lib/typescript/src/components/PageHero/PageHero.d.ts +53 -0
- package/lib/typescript/src/components/PoweredByLabel/PoweredByLabel.d.ts +70 -0
- package/lib/typescript/src/components/ProductOverview/ProductOverview.d.ts +39 -0
- package/lib/typescript/src/components/RangeTrack/RangeTrack.d.ts +173 -0
- package/lib/typescript/src/components/SavingsGoalSummary/SavingsGoalSummary.d.ts +95 -0
- package/lib/typescript/src/components/SegmentedTrack/SegmentedTrack.d.ts +108 -0
- package/lib/typescript/src/components/StatGroup/StatGroup.d.ts +45 -0
- package/lib/typescript/src/components/StatItem/StatItem.d.ts +24 -7
- package/lib/typescript/src/components/StrengthIndicator/StrengthIndicator.d.ts +58 -0
- package/lib/typescript/src/components/SummaryTile/SummaryTile.d.ts +60 -0
- package/lib/typescript/src/components/Text/Text.d.ts +12 -2
- package/lib/typescript/src/components/Tooltip/Tooltip.d.ts +13 -2
- package/lib/typescript/src/components/index.d.ts +29 -3
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/lib/typescript/src/utils/index.d.ts +1 -0
- package/lib/typescript/src/utils/number-utils.d.ts +29 -0
- package/package.json +1 -3
- package/src/components/AccordionCheckbox/AccordionCheckbox.tsx +323 -0
- package/src/components/AccountCard/AccountCard.tsx +376 -0
- package/src/components/AppBar/AppBar.tsx +25 -14
- package/src/components/BrandChip/BrandChip.tsx +235 -0
- package/src/components/CardBankAccount/CardBankAccount.tsx +321 -0
- package/src/components/CardInsight/CardInsight.tsx +239 -0
- package/src/components/CheckboxGroup/CheckboxGroup.tsx +86 -0
- package/src/components/CheckboxItem/CheckboxItem.tsx +209 -0
- package/src/components/CircularProgressBar/CircularProgressBar.tsx +74 -9
- package/src/components/CoverageBarComparison/CoverageBarComparison.tsx +378 -0
- package/src/components/CoverageRing/CoverageRing.tsx +225 -0
- package/src/components/DonutChart/DonutChart.tsx +503 -0
- package/src/components/DonutChartSummary/DonutChartSummary.tsx +256 -0
- package/src/components/Dropdown/Dropdown.tsx +331 -0
- package/src/components/DropdownInput/DropdownInput.tsx +819 -0
- package/src/components/FormField/FormField.tsx +542 -215
- package/src/components/LinearMeter/LinearMeter.tsx +9 -39
- package/src/components/LinearProgress/LinearProgress.tsx +92 -0
- package/src/components/LottieIntroBlock/LottieIntroBlock.tsx +202 -0
- package/src/components/MetricLegendItem/MetricLegendItem.tsx +167 -0
- package/src/components/MonthlyStatusGrid/MonthlyStatusGrid.tsx +438 -0
- package/src/components/OTP/OTP.tsx +476 -29
- package/src/components/PageHero/PageHero.tsx +200 -0
- package/src/components/PoweredByLabel/PoweredByLabel.tsx +221 -0
- package/src/components/PoweredByLabel/finvu.png +0 -0
- package/src/components/ProductOverview/ProductOverview.tsx +236 -0
- package/src/components/RangeTrack/RangeTrack.tsx +394 -0
- package/src/components/SavingsGoalSummary/SavingsGoalSummary.tsx +269 -0
- package/src/components/SegmentedTrack/SegmentedTrack.tsx +268 -0
- package/src/components/StatGroup/StatGroup.tsx +169 -0
- package/src/components/StatItem/StatItem.tsx +117 -40
- package/src/components/StrengthIndicator/StrengthIndicator.tsx +205 -0
- package/src/components/SummaryTile/SummaryTile.tsx +251 -0
- package/src/components/Text/Text.tsx +24 -3
- package/src/components/Tooltip/Tooltip.tsx +50 -25
- package/src/components/index.ts +47 -3
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- package/src/icons/registry.ts +1 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/number-utils.ts +60 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
10
|
+
var _reactUtils = require("../../utils/react-utils");
|
|
11
|
+
var _Checkbox = _interopRequireDefault(require("../Checkbox/Checkbox"));
|
|
12
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
15
|
+
/**
|
|
16
|
+
* CheckboxItem composes a `Checkbox`, a label and an optional `endSlot` into a
|
|
17
|
+
* single horizontal pressable row. Pressing anywhere on the row (outside of the
|
|
18
|
+
* `endSlot`) toggles the checkbox, mirroring the typical native form pattern.
|
|
19
|
+
*
|
|
20
|
+
* Use the `control` prop to swap the checkbox between the leading (left, default)
|
|
21
|
+
* and trailing (right) edge of the row. The `endSlot` flips to the opposite edge.
|
|
22
|
+
*
|
|
23
|
+
* Mirrors the Figma "Checkbox Item" component and uses the `checkboxItem/*`
|
|
24
|
+
* design tokens for typography and spacing.
|
|
25
|
+
*
|
|
26
|
+
* @component
|
|
27
|
+
* @param {CheckboxItemProps} props
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* const [checked, setChecked] = useState(false)
|
|
32
|
+
*
|
|
33
|
+
* <CheckboxItem
|
|
34
|
+
* label="Fixed deposit • 0245"
|
|
35
|
+
* checked={checked}
|
|
36
|
+
* onValueChange={setChecked}
|
|
37
|
+
* control="leading"
|
|
38
|
+
* modes={{ 'Color Mode': 'Light' }}
|
|
39
|
+
* />
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
function CheckboxItem({
|
|
43
|
+
checked: controlledChecked,
|
|
44
|
+
defaultChecked = false,
|
|
45
|
+
onValueChange,
|
|
46
|
+
disabled = false,
|
|
47
|
+
label = 'Fixed deposit • 0245',
|
|
48
|
+
control = 'leading',
|
|
49
|
+
endSlot,
|
|
50
|
+
endSlotWidth = 80,
|
|
51
|
+
modes = _reactUtils.EMPTY_MODES,
|
|
52
|
+
style,
|
|
53
|
+
labelStyle,
|
|
54
|
+
accessibilityLabel
|
|
55
|
+
}) {
|
|
56
|
+
const isTrailing = control === 'trailing';
|
|
57
|
+
const isControlled = controlledChecked !== undefined;
|
|
58
|
+
const [internalChecked, setInternalChecked] = (0, _react.useState)(defaultChecked);
|
|
59
|
+
const isChecked = isControlled ? controlledChecked : internalChecked;
|
|
60
|
+
const handleToggle = (0, _react.useCallback)(() => {
|
|
61
|
+
if (disabled) return;
|
|
62
|
+
const next = !isChecked;
|
|
63
|
+
if (!isControlled) {
|
|
64
|
+
setInternalChecked(next);
|
|
65
|
+
}
|
|
66
|
+
onValueChange?.(next);
|
|
67
|
+
}, [disabled, isChecked, isControlled, onValueChange]);
|
|
68
|
+
const gap = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/gap', modes) ?? 8;
|
|
69
|
+
const paddingHorizontal = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/padding/horizontal', modes) ?? 0;
|
|
70
|
+
const paddingVertical = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/padding/vertical', modes) ?? 0;
|
|
71
|
+
const labelColor = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/foreground', modes) ?? '#1a1c1f';
|
|
72
|
+
const labelFontFamily = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/label/fontFamily', modes) ?? 'JioType Var';
|
|
73
|
+
const labelFontSize = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/label/fontSize', modes) ?? 14;
|
|
74
|
+
const labelLineHeight = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/label/lineHeight', modes) ?? 19;
|
|
75
|
+
const labelFontWeightRaw = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/label/fontWeight', modes) ?? 400;
|
|
76
|
+
const labelFontWeight = String(labelFontWeightRaw);
|
|
77
|
+
const containerStyle = {
|
|
78
|
+
flexDirection: 'row',
|
|
79
|
+
alignItems: 'center',
|
|
80
|
+
gap,
|
|
81
|
+
paddingHorizontal,
|
|
82
|
+
paddingVertical,
|
|
83
|
+
width: '100%'
|
|
84
|
+
};
|
|
85
|
+
const resolvedLabelStyle = {
|
|
86
|
+
flex: 1,
|
|
87
|
+
minWidth: 0,
|
|
88
|
+
color: labelColor,
|
|
89
|
+
fontFamily: labelFontFamily,
|
|
90
|
+
fontSize: labelFontSize,
|
|
91
|
+
lineHeight: labelLineHeight,
|
|
92
|
+
fontWeight: labelFontWeight
|
|
93
|
+
};
|
|
94
|
+
const a11yLabel = accessibilityLabel ?? (typeof label === 'string' ? label : undefined);
|
|
95
|
+
const checkboxNode = /*#__PURE__*/(0, _jsxRuntime.jsx)(_Checkbox.default, {
|
|
96
|
+
checked: isChecked,
|
|
97
|
+
disabled: disabled,
|
|
98
|
+
onValueChange: handleToggle,
|
|
99
|
+
modes: modes,
|
|
100
|
+
...(a11yLabel !== undefined ? {
|
|
101
|
+
accessibilityLabel: a11yLabel
|
|
102
|
+
} : {})
|
|
103
|
+
});
|
|
104
|
+
const labelNode = label != null && label !== false ? typeof label === 'string' || typeof label === 'number' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
105
|
+
style: [resolvedLabelStyle, labelStyle],
|
|
106
|
+
selectable: false,
|
|
107
|
+
children: label
|
|
108
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
109
|
+
style: {
|
|
110
|
+
flex: 1,
|
|
111
|
+
minWidth: 0
|
|
112
|
+
},
|
|
113
|
+
children: label
|
|
114
|
+
}) : null;
|
|
115
|
+
const endSlotNode = endSlot ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
116
|
+
style: {
|
|
117
|
+
width: endSlotWidth,
|
|
118
|
+
flexShrink: 0,
|
|
119
|
+
alignItems: 'stretch'
|
|
120
|
+
},
|
|
121
|
+
children: (0, _reactUtils.cloneChildrenWithModes)(endSlot, modes)
|
|
122
|
+
}) : null;
|
|
123
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
124
|
+
style: [containerStyle, style],
|
|
125
|
+
onPress: handleToggle,
|
|
126
|
+
disabled: disabled,
|
|
127
|
+
accessibilityRole: "checkbox",
|
|
128
|
+
accessibilityState: {
|
|
129
|
+
checked: isChecked,
|
|
130
|
+
disabled
|
|
131
|
+
},
|
|
132
|
+
accessibilityLabel: a11yLabel,
|
|
133
|
+
children: isTrailing ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
134
|
+
children: [endSlotNode, labelNode, checkboxNode]
|
|
135
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
136
|
+
children: [checkboxNode, labelNode, endSlotNode]
|
|
137
|
+
})
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
var _default = exports.default = CheckboxItem;
|
|
@@ -43,11 +43,13 @@ function CircularProgressBar({
|
|
|
43
43
|
value = 70,
|
|
44
44
|
state = 'Inactive',
|
|
45
45
|
valueLabel,
|
|
46
|
+
supportText,
|
|
46
47
|
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
47
48
|
style,
|
|
48
49
|
trackStyle,
|
|
49
50
|
progressStyle,
|
|
50
51
|
valueStyle,
|
|
52
|
+
supportTextStyle,
|
|
51
53
|
accessibilityLabel,
|
|
52
54
|
...rest
|
|
53
55
|
}) {
|
|
@@ -58,6 +60,17 @@ function CircularProgressBar({
|
|
|
58
60
|
...globalModes,
|
|
59
61
|
...propModes
|
|
60
62
|
};
|
|
63
|
+
// The Figma `circularProgressBar/track/color` variable aliases to the same
|
|
64
|
+
// chain as `progress/color`, so user-selected Brand modes collapse the
|
|
65
|
+
// track and progress to a single hue. The Figma source of truth always
|
|
66
|
+
// renders the track as a neutral gray, so we force Neutral/Medium when
|
|
67
|
+
// resolving the track color while keeping the user modes for everything
|
|
68
|
+
// else.
|
|
69
|
+
const trackModes = {
|
|
70
|
+
...modes,
|
|
71
|
+
'AppearanceBrand': 'Neutral',
|
|
72
|
+
'Emphasis': 'Medium'
|
|
73
|
+
};
|
|
61
74
|
const isActive = state === true || state === 'Active';
|
|
62
75
|
const normalizedValue = clamp(value, 0, 100);
|
|
63
76
|
const size = toNumber((0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/size', modes), 60);
|
|
@@ -65,15 +78,18 @@ function CircularProgressBar({
|
|
|
65
78
|
const radius = Math.max(0, (size - strokeWidth) / 2);
|
|
66
79
|
const center = size / 2;
|
|
67
80
|
const circumference = 2 * Math.PI * radius;
|
|
68
|
-
const trackColor = getStrokeColor(trackStyle, (0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/track/color',
|
|
81
|
+
const trackColor = getStrokeColor(trackStyle, (0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/track/color', trackModes) || '#ebebed');
|
|
69
82
|
const progressColor = getStrokeColor(progressStyle, (0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/progress/color', modes) || '#25ab21');
|
|
70
83
|
const iconColor = (0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/icon/color', modes) || '#666666';
|
|
71
84
|
const iconSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/icon/size', modes), 24);
|
|
72
85
|
const foreground = (0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/foreground', modes) || '#0d0d0f';
|
|
73
|
-
const fontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/fontSize', modes), 18);
|
|
86
|
+
const fontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/value/fontSize', modes), 18);
|
|
74
87
|
const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/fontFamily', modes) || 'JioType Var';
|
|
75
|
-
const lineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/lineHeight', modes), 21);
|
|
76
|
-
const fontWeight = toFontWeight((0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/fontWeight', modes), '700');
|
|
88
|
+
const lineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/value/lineHeight', modes), 21);
|
|
89
|
+
const fontWeight = toFontWeight((0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/value/fontWeight', modes), '700');
|
|
90
|
+
const supportFontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/supportText/fontSize', modes), 11);
|
|
91
|
+
const supportLineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/supportText/lineHeight', modes), 13);
|
|
92
|
+
const supportFontWeight = toFontWeight((0, _figmaVariablesResolver.getVariableByName)('circularProgressBar/supportText/fontWeight', modes), '500');
|
|
77
93
|
const computedContainerStyle = {
|
|
78
94
|
alignItems: 'center',
|
|
79
95
|
height: size,
|
|
@@ -81,13 +97,34 @@ function CircularProgressBar({
|
|
|
81
97
|
position: 'relative',
|
|
82
98
|
width: size
|
|
83
99
|
};
|
|
100
|
+
|
|
101
|
+
// The text stack (support text + value) is centered inside the ring using
|
|
102
|
+
// an absolutely-positioned column. Keeping the wrapper absolute (rather
|
|
103
|
+
// than the individual texts, as in earlier versions) lets multi-line stacks
|
|
104
|
+
// render correctly without sub-pixel misalignment.
|
|
105
|
+
const textStackStyle = {
|
|
106
|
+
alignItems: 'center',
|
|
107
|
+
justifyContent: 'center',
|
|
108
|
+
left: 0,
|
|
109
|
+
position: 'absolute',
|
|
110
|
+
right: 0,
|
|
111
|
+
top: 0,
|
|
112
|
+
bottom: 0
|
|
113
|
+
};
|
|
84
114
|
const computedValueStyle = {
|
|
85
115
|
color: foreground,
|
|
86
116
|
fontFamily,
|
|
87
117
|
fontSize,
|
|
88
118
|
fontWeight,
|
|
89
119
|
lineHeight,
|
|
90
|
-
|
|
120
|
+
textAlign: 'center'
|
|
121
|
+
};
|
|
122
|
+
const computedSupportTextStyle = {
|
|
123
|
+
color: foreground,
|
|
124
|
+
fontFamily,
|
|
125
|
+
fontSize: supportFontSize,
|
|
126
|
+
fontWeight: supportFontWeight,
|
|
127
|
+
lineHeight: supportLineHeight,
|
|
91
128
|
textAlign: 'center'
|
|
92
129
|
};
|
|
93
130
|
const iconStyle = {
|
|
@@ -96,7 +133,8 @@ function CircularProgressBar({
|
|
|
96
133
|
top: (size - iconSize) / 2
|
|
97
134
|
};
|
|
98
135
|
const displayValue = valueLabel ?? String(Math.round(normalizedValue));
|
|
99
|
-
const
|
|
136
|
+
const hasSupportText = typeof supportText === 'string' && supportText.length > 0;
|
|
137
|
+
const defaultAccessibilityLabel = accessibilityLabel ?? (isActive ? hasSupportText ? `${supportText}, ${displayValue} out of 100` : `${displayValue} out of 100` : 'Inactive progress');
|
|
100
138
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
101
139
|
accessibilityRole: "progressbar",
|
|
102
140
|
accessibilityLabel: defaultAccessibilityLabel,
|
|
@@ -132,9 +170,18 @@ function CircularProgressBar({
|
|
|
132
170
|
originX: center,
|
|
133
171
|
originY: center
|
|
134
172
|
}) : null]
|
|
135
|
-
}), isActive ? /*#__PURE__*/(0, _jsxRuntime.
|
|
136
|
-
style:
|
|
137
|
-
|
|
173
|
+
}), isActive ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
174
|
+
style: textStackStyle,
|
|
175
|
+
pointerEvents: "none",
|
|
176
|
+
children: [hasSupportText ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
177
|
+
style: [computedSupportTextStyle, supportTextStyle],
|
|
178
|
+
numberOfLines: 1,
|
|
179
|
+
children: supportText
|
|
180
|
+
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
181
|
+
style: [computedValueStyle, valueStyle],
|
|
182
|
+
numberOfLines: 1,
|
|
183
|
+
children: displayValue
|
|
184
|
+
})]
|
|
138
185
|
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_IconMinus.IconMinus, {
|
|
139
186
|
width: iconSize,
|
|
140
187
|
height: iconSize,
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireDefault(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _reactNativeSvg = _interopRequireWildcard(require("react-native-svg"));
|
|
10
|
+
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
11
|
+
var _JFSThemeProvider = require("../../design-tokens/JFSThemeProvider");
|
|
12
|
+
var _reactUtils = require("../../utils/react-utils");
|
|
13
|
+
var _MetricLegendItem = _interopRequireDefault(require("../MetricLegendItem/MetricLegendItem"));
|
|
14
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
16
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
|
+
/**
|
|
18
|
+
* One entry in the {@link CoverageBarComparisonProps.bars} array.
|
|
19
|
+
*
|
|
20
|
+
* Each entry carries **both** the bar's data and its legend label, so the
|
|
21
|
+
* number of bars is intrinsically equal to the number of legend items.
|
|
22
|
+
* There is no separate `legends` prop — that is by design.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Default per-index `Emphasis / DataViz` modes applied when the caller does
|
|
27
|
+
* not provide its own override. Ascends Low → Medium → High so the natural
|
|
28
|
+
* "Current vs Recommended" two-bar story matches the Figma reference (left
|
|
29
|
+
* = lighter / Low, right = darker / High). Cycles for >3 bars.
|
|
30
|
+
*/
|
|
31
|
+
const DEFAULT_EMPHASIS_CYCLE = ['Low', 'Medium', 'High'];
|
|
32
|
+
function defaultEmphasisFor(index, total) {
|
|
33
|
+
if (total <= 1) {
|
|
34
|
+
return 'High';
|
|
35
|
+
}
|
|
36
|
+
if (total === 2) {
|
|
37
|
+
return index === 0 ? 'Low' : 'High';
|
|
38
|
+
}
|
|
39
|
+
return DEFAULT_EMPHASIS_CYCLE[index % DEFAULT_EMPHASIS_CYCLE.length];
|
|
40
|
+
}
|
|
41
|
+
const DEFAULT_BARS = [{
|
|
42
|
+
value: 40,
|
|
43
|
+
label: '₹1L (40%)',
|
|
44
|
+
legend: 'Current coverage'
|
|
45
|
+
}, {
|
|
46
|
+
value: 100,
|
|
47
|
+
label: '₹2.5L (100%)',
|
|
48
|
+
legend: 'Recommended coverage'
|
|
49
|
+
}];
|
|
50
|
+
const toNumber = (value, fallback) => {
|
|
51
|
+
if (typeof value === 'number') {
|
|
52
|
+
return Number.isFinite(value) ? value : fallback;
|
|
53
|
+
}
|
|
54
|
+
if (typeof value === 'string') {
|
|
55
|
+
const parsed = Number(value);
|
|
56
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
57
|
+
}
|
|
58
|
+
return fallback;
|
|
59
|
+
};
|
|
60
|
+
const toFontWeight = (value, fallback) => {
|
|
61
|
+
if (typeof value === 'number') {
|
|
62
|
+
return String(value);
|
|
63
|
+
}
|
|
64
|
+
if (typeof value === 'string') {
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
return fallback;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Bar shape rendered with `react-native-svg`. The wrapper measures its width
|
|
71
|
+
* via `onLayout` so the SVG can use concrete dimensions (RN SVG does not
|
|
72
|
+
* resolve percentage shape attributes when the parent SVG itself has a
|
|
73
|
+
* percentage `width`).
|
|
74
|
+
*/
|
|
75
|
+
function BarShape({
|
|
76
|
+
height,
|
|
77
|
+
color,
|
|
78
|
+
radius
|
|
79
|
+
}) {
|
|
80
|
+
const [width, setWidth] = _react.default.useState(0);
|
|
81
|
+
const handleLayout = _react.default.useCallback(event => {
|
|
82
|
+
const next = event.nativeEvent.layout.width;
|
|
83
|
+
setWidth(prev => prev === next ? prev : next);
|
|
84
|
+
}, []);
|
|
85
|
+
if (height <= 0) {
|
|
86
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
87
|
+
onLayout: handleLayout,
|
|
88
|
+
style: {
|
|
89
|
+
width: '100%',
|
|
90
|
+
height: 0
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const safeRadius = Math.max(0, Math.min(radius, width / 2, height / 2));
|
|
95
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
96
|
+
onLayout: handleLayout,
|
|
97
|
+
style: {
|
|
98
|
+
width: '100%',
|
|
99
|
+
height,
|
|
100
|
+
minWidth: 1
|
|
101
|
+
},
|
|
102
|
+
children: width > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.default, {
|
|
103
|
+
width: width,
|
|
104
|
+
height: height,
|
|
105
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.Rect, {
|
|
106
|
+
x: 0,
|
|
107
|
+
y: 0,
|
|
108
|
+
width: width,
|
|
109
|
+
height: height,
|
|
110
|
+
rx: safeRadius,
|
|
111
|
+
ry: safeRadius,
|
|
112
|
+
fill: color
|
|
113
|
+
})
|
|
114
|
+
}) : null
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* `CoverageBarComparison` renders a small vertical-bar chart that compares
|
|
120
|
+
* 2+ values side by side, with a legend row directly below where each item
|
|
121
|
+
* is intrinsically tied to one bar.
|
|
122
|
+
*
|
|
123
|
+
* Cohesiveness guarantees:
|
|
124
|
+
* - The legend row is **derived** from the same `bars` prop, so the count
|
|
125
|
+
* and order can never desynchronise from the chart.
|
|
126
|
+
* - Each bar's color, its tokenized `Emphasis / DataViz` mode and its
|
|
127
|
+
* legend indicator dot are the same value.
|
|
128
|
+
*
|
|
129
|
+
* Bars are drawn with `react-native-svg` (`<Rect>` with `rx`), and the
|
|
130
|
+
* fonts/spacing/colors are sourced from the Figma `valueBar/*` and
|
|
131
|
+
* `coverageBarComparison/*` tokens.
|
|
132
|
+
*
|
|
133
|
+
* @component
|
|
134
|
+
*/
|
|
135
|
+
function CoverageBarComparison({
|
|
136
|
+
bars = DEFAULT_BARS,
|
|
137
|
+
max,
|
|
138
|
+
height = 100,
|
|
139
|
+
legendGap = 12,
|
|
140
|
+
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
141
|
+
style,
|
|
142
|
+
chartStyle,
|
|
143
|
+
legendStyle,
|
|
144
|
+
labelStyle,
|
|
145
|
+
accessibilityLabel
|
|
146
|
+
}) {
|
|
147
|
+
const {
|
|
148
|
+
modes: globalModes
|
|
149
|
+
} = (0, _JFSThemeProvider.useTokens)();
|
|
150
|
+
const modes = _react.default.useMemo(() => ({
|
|
151
|
+
...globalModes,
|
|
152
|
+
...propModes
|
|
153
|
+
}), [globalModes, propModes]);
|
|
154
|
+
const wrapGap = toNumber((0, _figmaVariablesResolver.getVariableByName)('coverageBarComparison/wrap/gap', modes), 6);
|
|
155
|
+
const valueBarGap = toNumber((0, _figmaVariablesResolver.getVariableByName)('valueBar/gap', modes), 4);
|
|
156
|
+
const barRadius = toNumber((0, _figmaVariablesResolver.getVariableByName)('valueBar/bar/radius', modes), 10);
|
|
157
|
+
const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('valueBar/fontFamily', modes) ?? 'JioType Var';
|
|
158
|
+
const fontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('valueBar/fontSize', modes), 12);
|
|
159
|
+
const lineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('valueBar/lineHeight', modes), 16);
|
|
160
|
+
const fontWeight = toFontWeight((0, _figmaVariablesResolver.getVariableByName)('valueBar/fontWeight', modes), '700');
|
|
161
|
+
const foreground = (0, _figmaVariablesResolver.getVariableByName)('valueBar/foreground', modes) ?? '#000000';
|
|
162
|
+
const labelHeight = lineHeight;
|
|
163
|
+
const barAreaHeight = Math.max(0, height - labelHeight - valueBarGap);
|
|
164
|
+
const total = bars.length;
|
|
165
|
+
const computedMax = max ?? bars.reduce((acc, bar) => Math.max(acc, bar.value), 0);
|
|
166
|
+
const safeMax = computedMax > 0 ? computedMax : 1;
|
|
167
|
+
const resolvedBars = _react.default.useMemo(() => bars.map((bar, index) => {
|
|
168
|
+
const barModes = {
|
|
169
|
+
...modes,
|
|
170
|
+
'Emphasis / DataViz': defaultEmphasisFor(index, total),
|
|
171
|
+
...(bar.modes || {})
|
|
172
|
+
};
|
|
173
|
+
const ratio = Math.max(0, Math.min(1, bar.value / safeMax));
|
|
174
|
+
const tokenColor = (0, _figmaVariablesResolver.getVariableByName)('valueBar/bar/background', barModes) ?? '#c9b7ff';
|
|
175
|
+
const bgColor = bar.color ?? tokenColor;
|
|
176
|
+
return {
|
|
177
|
+
original: bar,
|
|
178
|
+
index,
|
|
179
|
+
barModes,
|
|
180
|
+
ratio,
|
|
181
|
+
bgColor
|
|
182
|
+
};
|
|
183
|
+
}), [bars, modes, safeMax, total]);
|
|
184
|
+
const computedLabelStyle = {
|
|
185
|
+
color: foreground,
|
|
186
|
+
fontFamily,
|
|
187
|
+
fontSize,
|
|
188
|
+
lineHeight,
|
|
189
|
+
fontWeight,
|
|
190
|
+
textAlign: 'center'
|
|
191
|
+
};
|
|
192
|
+
const defaultAccessibilityLabel = accessibilityLabel ?? `Comparison of ${total} bar${total === 1 ? '' : 's'}: ` + bars.map((bar, i) => {
|
|
193
|
+
const legend = typeof bar.legend === 'string' ? bar.legend : `bar ${i + 1}`;
|
|
194
|
+
return `${legend} ${bar.value}`;
|
|
195
|
+
}).join(', ');
|
|
196
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
197
|
+
accessibilityLabel: defaultAccessibilityLabel,
|
|
198
|
+
style: [{
|
|
199
|
+
width: '100%'
|
|
200
|
+
}, style],
|
|
201
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
202
|
+
accessibilityRole: "image",
|
|
203
|
+
style: [{
|
|
204
|
+
flexDirection: 'row',
|
|
205
|
+
alignItems: 'flex-end',
|
|
206
|
+
height,
|
|
207
|
+
gap: wrapGap,
|
|
208
|
+
width: '100%'
|
|
209
|
+
}, chartStyle],
|
|
210
|
+
children: resolvedBars.map(({
|
|
211
|
+
original,
|
|
212
|
+
index,
|
|
213
|
+
ratio,
|
|
214
|
+
bgColor
|
|
215
|
+
}) => {
|
|
216
|
+
const barHeightPx = Math.max(0, barAreaHeight * ratio);
|
|
217
|
+
const valueBarTotalHeight = labelHeight + valueBarGap + barHeightPx;
|
|
218
|
+
const hasLabel = original.label !== undefined && original.label !== null && original.label !== false;
|
|
219
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
220
|
+
accessibilityLabel: original.accessibilityLabel,
|
|
221
|
+
style: {
|
|
222
|
+
flex: 1,
|
|
223
|
+
flexDirection: 'column',
|
|
224
|
+
alignItems: 'stretch',
|
|
225
|
+
justifyContent: 'flex-end',
|
|
226
|
+
height: valueBarTotalHeight,
|
|
227
|
+
gap: valueBarGap,
|
|
228
|
+
minWidth: 1
|
|
229
|
+
},
|
|
230
|
+
children: [hasLabel ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
231
|
+
numberOfLines: 1,
|
|
232
|
+
style: [computedLabelStyle, labelStyle],
|
|
233
|
+
children: original.label
|
|
234
|
+
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(BarShape, {
|
|
235
|
+
height: barHeightPx,
|
|
236
|
+
color: bgColor,
|
|
237
|
+
radius: barRadius
|
|
238
|
+
})]
|
|
239
|
+
}, original.key ?? `bar-${index}`);
|
|
240
|
+
})
|
|
241
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
242
|
+
style: [{
|
|
243
|
+
marginTop: legendGap,
|
|
244
|
+
flexDirection: 'row',
|
|
245
|
+
alignItems: 'center',
|
|
246
|
+
justifyContent: 'space-between',
|
|
247
|
+
width: '100%'
|
|
248
|
+
}, legendStyle],
|
|
249
|
+
children: resolvedBars.map(({
|
|
250
|
+
original,
|
|
251
|
+
index,
|
|
252
|
+
barModes,
|
|
253
|
+
bgColor
|
|
254
|
+
}) => {
|
|
255
|
+
const indicatorOverride = original.color ? bgColor : undefined;
|
|
256
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_MetricLegendItem.default, {
|
|
257
|
+
label: original.legend,
|
|
258
|
+
value: original.legendValue,
|
|
259
|
+
modes: barModes,
|
|
260
|
+
...(indicatorOverride !== undefined ? {
|
|
261
|
+
indicatorColor: indicatorOverride
|
|
262
|
+
} : {}),
|
|
263
|
+
style: {
|
|
264
|
+
flex: 1,
|
|
265
|
+
minWidth: 0
|
|
266
|
+
}
|
|
267
|
+
}, original.key ?? `legend-${index}`);
|
|
268
|
+
})
|
|
269
|
+
})]
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
var _default = exports.default = CoverageBarComparison;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireDefault(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
10
|
+
var _JFSThemeProvider = require("../../design-tokens/JFSThemeProvider");
|
|
11
|
+
var _reactUtils = require("../../utils/react-utils");
|
|
12
|
+
var _Button = _interopRequireDefault(require("../Button/Button"));
|
|
13
|
+
var _CircularProgressBar = _interopRequireDefault(require("../CircularProgressBar/CircularProgressBar"));
|
|
14
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
|
+
const toNumber = (value, fallback) => {
|
|
17
|
+
if (typeof value === 'number') {
|
|
18
|
+
return Number.isFinite(value) ? value : fallback;
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === 'string') {
|
|
21
|
+
const parsed = Number(value);
|
|
22
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
23
|
+
}
|
|
24
|
+
return fallback;
|
|
25
|
+
};
|
|
26
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
27
|
+
|
|
28
|
+
// The Figma "Coverage Ring" pairs a `circularProgressBar Size: M` ring with a
|
|
29
|
+
// `Button / Size: S` Secondary button. Locking these in as component defaults
|
|
30
|
+
// keeps the visual identical to the Figma reference when the caller supplies
|
|
31
|
+
// only their own brand/theme modes — caller modes are still merged on top so
|
|
32
|
+
// any of these can be overridden per-instance.
|
|
33
|
+
const COMPONENT_DEFAULT_MODES = Object.freeze({
|
|
34
|
+
'circularProgressBar Size': 'M',
|
|
35
|
+
'Button / Size': 'S'
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Match the Figma source: regardless of the overall AppearanceBrand/Emphasis
|
|
39
|
+
// the caller picks (used to colour the ring), the Coverage Ring CTA renders
|
|
40
|
+
// with the Secondary brand at Medium emphasis — which resolves to the
|
|
41
|
+
// pale-lavender background (`#dbcfff`) + deep-purple foreground (`#5d00b5`)
|
|
42
|
+
// shown in Figma. Mirrors the pattern used by `CardFinancialCondition.tsx`.
|
|
43
|
+
const BUTTON_FORCED_BRAND = Object.freeze({
|
|
44
|
+
AppearanceBrand: 'Secondary',
|
|
45
|
+
Emphasis: 'Medium'
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* `CoverageRing` renders a single-purpose insight: how many items out of a
|
|
50
|
+
* total have been covered (e.g. "4 of 7 benefits availed"), paired with a
|
|
51
|
+
* call-to-action button below the ring.
|
|
52
|
+
*
|
|
53
|
+
* It composes the existing {@link CircularProgressBar} (in its `M` size so
|
|
54
|
+
* there is room for a caption + value inside the ring) and {@link Button}
|
|
55
|
+
* (in its `S`, Secondary brand variant) — both driven by the same design
|
|
56
|
+
* tokens used by the rest of the design system.
|
|
57
|
+
*
|
|
58
|
+
* The ring fill is derived from `value / total`, so the percentage, the
|
|
59
|
+
* displayed `"{value} of {total}"` label and the support caption stay in
|
|
60
|
+
* sync automatically. There is no separate `progress` prop.
|
|
61
|
+
*
|
|
62
|
+
* @component
|
|
63
|
+
*/
|
|
64
|
+
function CoverageRing({
|
|
65
|
+
value = 4,
|
|
66
|
+
total = 7,
|
|
67
|
+
supportText = 'Benefits availed',
|
|
68
|
+
valueLabel,
|
|
69
|
+
actionLabel = 'Learn more',
|
|
70
|
+
onActionPress,
|
|
71
|
+
actionProps,
|
|
72
|
+
action,
|
|
73
|
+
children,
|
|
74
|
+
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
75
|
+
style,
|
|
76
|
+
supportTextStyle,
|
|
77
|
+
valueStyle,
|
|
78
|
+
accessibilityLabel,
|
|
79
|
+
...rest
|
|
80
|
+
}) {
|
|
81
|
+
const {
|
|
82
|
+
modes: globalModes
|
|
83
|
+
} = (0, _JFSThemeProvider.useTokens)();
|
|
84
|
+
|
|
85
|
+
// Merge order matches the rest of the design system (see `Gauge.tsx`):
|
|
86
|
+
// 1. Component-level defaults — the lowest-priority safety net.
|
|
87
|
+
// 2. Global modes from `JFSThemeProvider` — app-wide theme.
|
|
88
|
+
// 3. Caller-supplied `propModes` — highest priority, overrides everything.
|
|
89
|
+
const modes = _react.default.useMemo(() => ({
|
|
90
|
+
...COMPONENT_DEFAULT_MODES,
|
|
91
|
+
...globalModes,
|
|
92
|
+
...propModes
|
|
93
|
+
}), [propModes, globalModes]);
|
|
94
|
+
|
|
95
|
+
// Force the Secondary brand on the button only; the ring stays on whatever
|
|
96
|
+
// brand the caller selected. Done via a forcedModes layer so callers can't
|
|
97
|
+
// accidentally bring back a Primary-coloured CTA through their own modes.
|
|
98
|
+
const buttonModes = _react.default.useMemo(() => ({
|
|
99
|
+
...modes,
|
|
100
|
+
...BUTTON_FORCED_BRAND
|
|
101
|
+
}), [modes]);
|
|
102
|
+
const safeTotal = total > 0 ? total : 0;
|
|
103
|
+
const clampedValue = clamp(value, 0, safeTotal);
|
|
104
|
+
const progressPercent = safeTotal > 0 ? clampedValue / safeTotal * 100 : 0;
|
|
105
|
+
const computedValueLabel = valueLabel ?? `${clampedValue} of ${safeTotal}`;
|
|
106
|
+
const gap = toNumber((0, _figmaVariablesResolver.getVariableByName)('coverageRing/gap', modes), 16);
|
|
107
|
+
const containerStyle = {
|
|
108
|
+
alignItems: 'center',
|
|
109
|
+
gap
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Custom action slot resolution order: explicit `action` prop wins over
|
|
113
|
+
// `children`. We pass `modes` down via cloneChildrenWithModes so any
|
|
114
|
+
// token-driven child (e.g. a custom `<Button>`) inherits the parent theme.
|
|
115
|
+
const customAction = action ?? children;
|
|
116
|
+
const hasCustomAction = customAction !== undefined && customAction !== null;
|
|
117
|
+
const defaultAccessibilityLabel = accessibilityLabel ?? `${supportText}, ${computedValueLabel}`;
|
|
118
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
119
|
+
accessibilityLabel: defaultAccessibilityLabel,
|
|
120
|
+
style: [containerStyle, style],
|
|
121
|
+
...rest,
|
|
122
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_CircularProgressBar.default, {
|
|
123
|
+
state: "Active",
|
|
124
|
+
value: progressPercent,
|
|
125
|
+
valueLabel: computedValueLabel,
|
|
126
|
+
supportText: supportText,
|
|
127
|
+
supportTextStyle: supportTextStyle,
|
|
128
|
+
valueStyle: valueStyle,
|
|
129
|
+
modes: modes,
|
|
130
|
+
accessibilityLabel: `${supportText}, ${computedValueLabel}`
|
|
131
|
+
}), hasCustomAction ? (0, _reactUtils.cloneChildrenWithModes)(customAction, buttonModes) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
|
|
132
|
+
label: actionLabel,
|
|
133
|
+
modes: buttonModes,
|
|
134
|
+
...(onActionPress !== undefined ? {
|
|
135
|
+
onPress: onActionPress
|
|
136
|
+
} : {}),
|
|
137
|
+
...actionProps
|
|
138
|
+
})]
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
var _default = exports.default = CoverageRing;
|