jfs-components 0.0.71 → 0.0.73
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 +60 -0
- package/lib/commonjs/components/AccordionCheckbox/AccordionCheckbox.js +239 -0
- package/lib/commonjs/components/BrandChip/BrandChip.js +149 -0
- package/lib/commonjs/components/CardAdvisory/CardAdvisory.js +2 -2
- package/lib/commonjs/components/CardBankAccount/CardBankAccount.js +213 -0
- package/lib/commonjs/components/CardFinancialCondition/CardFinancialCondition.js +213 -0
- package/lib/commonjs/components/CardInsight/CardInsight.js +166 -0
- package/lib/commonjs/components/Carousel/Carousel.js +9 -7
- package/lib/commonjs/components/CheckboxGroup/CheckboxGroup.js +67 -0
- package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +125 -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/HoldingsCard/HoldingsCard.js +2 -2
- package/lib/commonjs/components/InstitutionBadge/InstitutionBadge.js +132 -0
- package/lib/commonjs/components/LinearMeter/LinearMeter.js +9 -28
- package/lib/commonjs/components/LinearProgress/LinearProgress.js +68 -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/ProductOverview/ProductOverview.js +147 -0
- package/lib/commonjs/components/Radio/Radio.js +194 -0
- package/lib/commonjs/components/RadioButton/RadioButton.js +21 -188
- 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/index.js +192 -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/BrandChip/BrandChip.js +143 -0
- package/lib/module/components/CardAdvisory/CardAdvisory.js +2 -2
- package/lib/module/components/CardBankAccount/CardBankAccount.js +208 -0
- package/lib/module/components/CardFinancialCondition/CardFinancialCondition.js +207 -0
- package/lib/module/components/CardInsight/CardInsight.js +161 -0
- package/lib/module/components/Carousel/Carousel.js +9 -7
- package/lib/module/components/CheckboxGroup/CheckboxGroup.js +62 -0
- package/lib/module/components/CheckboxItem/CheckboxItem.js +119 -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/HoldingsCard/HoldingsCard.js +2 -2
- package/lib/module/components/InstitutionBadge/InstitutionBadge.js +127 -0
- package/lib/module/components/LinearMeter/LinearMeter.js +9 -28
- package/lib/module/components/LinearProgress/LinearProgress.js +63 -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/ProductOverview/ProductOverview.js +142 -0
- package/lib/module/components/Radio/Radio.js +188 -0
- package/lib/module/components/RadioButton/RadioButton.js +20 -185
- 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/index.js +28 -1
- 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/BrandChip/BrandChip.d.ts +43 -0
- package/lib/typescript/src/components/CardBankAccount/CardBankAccount.d.ts +79 -0
- package/lib/typescript/src/components/CardFinancialCondition/CardFinancialCondition.d.ts +50 -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 +56 -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/InstitutionBadge/InstitutionBadge.d.ts +30 -0
- package/lib/typescript/src/components/LinearProgress/LinearProgress.d.ts +17 -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/ProductOverview/ProductOverview.d.ts +39 -0
- package/lib/typescript/src/components/Radio/Radio.d.ts +30 -0
- package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +20 -28
- package/lib/typescript/src/components/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/index.d.ts +29 -2
- 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 -1
- package/src/components/AccordionCheckbox/AccordionCheckbox.tsx +323 -0
- package/src/components/BrandChip/BrandChip.tsx +235 -0
- package/src/components/CardAdvisory/CardAdvisory.tsx +2 -2
- package/src/components/CardBankAccount/CardBankAccount.tsx +295 -0
- package/src/components/CardFinancialCondition/CardFinancialCondition.tsx +366 -0
- package/src/components/CardInsight/CardInsight.tsx +239 -0
- package/src/components/Carousel/Carousel.tsx +14 -6
- package/src/components/CheckboxGroup/CheckboxGroup.tsx +86 -0
- package/src/components/CheckboxItem/CheckboxItem.tsx +174 -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/HoldingsCard/HoldingsCard.tsx +2 -2
- package/src/components/InstitutionBadge/InstitutionBadge.tsx +216 -0
- package/src/components/LinearMeter/LinearMeter.tsx +9 -39
- package/src/components/LinearProgress/LinearProgress.tsx +92 -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/ProductOverview/ProductOverview.tsx +236 -0
- package/src/components/Radio/Radio.tsx +227 -0
- package/src/components/RadioButton/RadioButton.tsx +23 -225
- 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/index.ts +39 -2
- 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,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { View } from 'react-native';
|
|
5
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
+
import { EMPTY_MODES } from '../../utils/react-utils';
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
const LinearProgress = ({
|
|
9
|
+
value = 0,
|
|
10
|
+
modes = EMPTY_MODES,
|
|
11
|
+
style,
|
|
12
|
+
trackStyle,
|
|
13
|
+
indicatorStyle,
|
|
14
|
+
...rest
|
|
15
|
+
}) => {
|
|
16
|
+
// The track and the progress indicator are intentionally rendered at
|
|
17
|
+
// different emphasis levels by default: the track sits in the
|
|
18
|
+
// background as a low-emphasis surface, while the progress indicator
|
|
19
|
+
// is the high-emphasis foreground. Defaults are placed *before* the
|
|
20
|
+
// user-provided modes spread, so callers can still override
|
|
21
|
+
// `Emphasis / DataViz` (or any other mode) via the `modes` prop.
|
|
22
|
+
const trackModes = {
|
|
23
|
+
'Emphasis': 'Low',
|
|
24
|
+
...modes
|
|
25
|
+
};
|
|
26
|
+
const progressModes = {
|
|
27
|
+
'Emphasis': 'High',
|
|
28
|
+
...modes
|
|
29
|
+
};
|
|
30
|
+
const trackHeight = getVariableByName('linearProgress/track/height', trackModes) ?? 8;
|
|
31
|
+
const trackRadius = getVariableByName('linearProgress/track/radius', trackModes) ?? 999;
|
|
32
|
+
const trackBg = getVariableByName('linearProgress/track/background', trackModes) ?? '#ede7ff';
|
|
33
|
+
const indicatorHeight = getVariableByName('linearProgress/indicator/height', progressModes) ?? 8;
|
|
34
|
+
const indicatorRadius = getVariableByName('linearProgress/indicator/radius', progressModes) ?? 999;
|
|
35
|
+
const indicatorBg = getVariableByName('linearProgress/indicator/background', progressModes) ?? '#5d00b5';
|
|
36
|
+
const clampedValue = Math.min(Math.max(value, 0), 1);
|
|
37
|
+
const widthPercent = `${clampedValue * 100}%`;
|
|
38
|
+
return /*#__PURE__*/_jsx(View, {
|
|
39
|
+
style: [{
|
|
40
|
+
height: trackHeight,
|
|
41
|
+
backgroundColor: trackBg,
|
|
42
|
+
borderRadius: trackRadius,
|
|
43
|
+
overflow: 'hidden',
|
|
44
|
+
width: '100%'
|
|
45
|
+
}, style, trackStyle],
|
|
46
|
+
accessibilityRole: "progressbar",
|
|
47
|
+
accessibilityValue: {
|
|
48
|
+
min: 0,
|
|
49
|
+
max: 1,
|
|
50
|
+
now: clampedValue
|
|
51
|
+
},
|
|
52
|
+
...rest,
|
|
53
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
54
|
+
style: [{
|
|
55
|
+
width: widthPercent,
|
|
56
|
+
height: indicatorHeight,
|
|
57
|
+
backgroundColor: indicatorBg,
|
|
58
|
+
borderRadius: indicatorRadius
|
|
59
|
+
}, indicatorStyle]
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
export default LinearProgress;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { View, Text } from 'react-native';
|
|
5
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
+
import { EMPTY_MODES } from '../../utils/react-utils';
|
|
7
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
+
/**
|
|
9
|
+
* MetricLegendItem renders a horizontal row consisting of a small colored
|
|
10
|
+
* indicator dot, a label and an optional value. It is used in chart legends
|
|
11
|
+
* and similar metric callouts.
|
|
12
|
+
*
|
|
13
|
+
* @component
|
|
14
|
+
* @param {MetricLegendItemProps} props
|
|
15
|
+
*/
|
|
16
|
+
function MetricLegendItem({
|
|
17
|
+
label = 'Current (4 months)',
|
|
18
|
+
value,
|
|
19
|
+
indicatorColor,
|
|
20
|
+
modes = EMPTY_MODES,
|
|
21
|
+
style,
|
|
22
|
+
indicatorStyle,
|
|
23
|
+
labelStyle,
|
|
24
|
+
valueStyle
|
|
25
|
+
}) {
|
|
26
|
+
const gap = getVariableByName('metricLegendItem/gap', modes) ?? 4;
|
|
27
|
+
const textWrapGap = getVariableByName('metricLegendItem/textWrap/gap', modes) ?? 4;
|
|
28
|
+
const indicatorSize = getVariableByName('metricLegendItem/indicator/size', modes) ?? 8;
|
|
29
|
+
const indicatorRadius = getVariableByName('metricLegendItem/indicator/radius', modes) ?? 999;
|
|
30
|
+
const indicatorBg = indicatorColor ?? getVariableByName('metricLegendItem/indicator/bg', modes) ?? '#f7ab21';
|
|
31
|
+
const labelColor = getVariableByName('metricLegendItem/label/color', modes) ?? '#000000';
|
|
32
|
+
const labelFontFamily = getVariableByName('metricLegendItem/label/fontFamily', modes) ?? 'JioType Var';
|
|
33
|
+
const labelFontSize = getVariableByName('metricLegendItem/label/fontSize', modes) ?? 12;
|
|
34
|
+
const labelLineHeight = getVariableByName('metricLegendItem/label/lineHeight', modes) ?? 16;
|
|
35
|
+
const labelFontWeightRaw = getVariableByName('metricLegendItem/label/fontWeight', modes) ?? 400;
|
|
36
|
+
const labelFontWeight = String(labelFontWeightRaw);
|
|
37
|
+
const valueColor = getVariableByName('metricLegendItem/value/color', modes) ?? '#000000';
|
|
38
|
+
const valueFontFamily = getVariableByName('metricLegendItem/value/fontFamily', modes) ?? 'JioType Var';
|
|
39
|
+
const valueFontSize = getVariableByName('metricLegendItem/value/fontSize', modes) ?? 12;
|
|
40
|
+
const valueLineHeight = getVariableByName('metricLegendItem/value/lineHeight', modes) ?? 16;
|
|
41
|
+
const valueFontWeightRaw = getVariableByName('metricLegendItem/value/fontWeight', modes) ?? 500;
|
|
42
|
+
const valueFontWeight = String(valueFontWeightRaw);
|
|
43
|
+
const showValue = value !== undefined && value !== null && value !== false;
|
|
44
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
45
|
+
style: [{
|
|
46
|
+
flexDirection: 'row',
|
|
47
|
+
alignItems: 'center',
|
|
48
|
+
gap
|
|
49
|
+
}, style],
|
|
50
|
+
accessibilityRole: "text",
|
|
51
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
52
|
+
style: [{
|
|
53
|
+
width: indicatorSize,
|
|
54
|
+
height: indicatorSize,
|
|
55
|
+
borderRadius: indicatorRadius,
|
|
56
|
+
backgroundColor: indicatorBg
|
|
57
|
+
}, indicatorStyle]
|
|
58
|
+
}), /*#__PURE__*/_jsxs(View, {
|
|
59
|
+
style: {
|
|
60
|
+
flex: 1,
|
|
61
|
+
flexDirection: 'row',
|
|
62
|
+
alignItems: 'center',
|
|
63
|
+
gap: textWrapGap,
|
|
64
|
+
minWidth: 0
|
|
65
|
+
},
|
|
66
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
67
|
+
style: [{
|
|
68
|
+
flex: 1,
|
|
69
|
+
color: labelColor,
|
|
70
|
+
fontFamily: labelFontFamily,
|
|
71
|
+
fontSize: labelFontSize,
|
|
72
|
+
lineHeight: labelLineHeight,
|
|
73
|
+
fontWeight: labelFontWeight
|
|
74
|
+
}, labelStyle],
|
|
75
|
+
children: label
|
|
76
|
+
}), showValue && /*#__PURE__*/_jsx(Text, {
|
|
77
|
+
style: [{
|
|
78
|
+
color: valueColor,
|
|
79
|
+
fontFamily: valueFontFamily,
|
|
80
|
+
fontSize: valueFontSize,
|
|
81
|
+
lineHeight: valueLineHeight,
|
|
82
|
+
fontWeight: valueFontWeight
|
|
83
|
+
}, valueStyle],
|
|
84
|
+
numberOfLines: 1,
|
|
85
|
+
children: value
|
|
86
|
+
})]
|
|
87
|
+
})]
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
export default MetricLegendItem;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { View, Text } from 'react-native';
|
|
5
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
+
import { useTokens } from '../../design-tokens/JFSThemeProvider';
|
|
7
|
+
import { EMPTY_MODES } from '../../utils/react-utils';
|
|
8
|
+
import MetricLegendItem from '../MetricLegendItem/MetricLegendItem';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The three semantic states a calendar glyph can be in. Maps 1:1 to the
|
|
12
|
+
* Figma `Calendar Glyph State` collection modes (`Idle`, `notSaved`,
|
|
13
|
+
* `saved`) and drives both the glyph fill/foreground tokens and the
|
|
14
|
+
* matching legend dot.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** One month entry in the grid. */
|
|
18
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
|
+
const DEFAULT_MONTH_LABELS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
20
|
+
const DEFAULT_LEGEND_LABELS = Object.freeze({
|
|
21
|
+
Idle: 'No data',
|
|
22
|
+
notSaved: 'Not saved',
|
|
23
|
+
saved: 'Saved'
|
|
24
|
+
});
|
|
25
|
+
const DEFAULT_LEGEND_STATUSES = ['Idle', 'notSaved', 'saved'];
|
|
26
|
+
const DEFAULT_MONTHS = DEFAULT_MONTH_LABELS.map(label => ({
|
|
27
|
+
label,
|
|
28
|
+
status: 'Idle'
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Default tokens used as the safe fallbacks when token resolution returns
|
|
33
|
+
* `null` (e.g. modes are missing or the resolver hasn't been initialized).
|
|
34
|
+
* They mirror the Figma source values for the `Calendar Glyph State` modes.
|
|
35
|
+
*/
|
|
36
|
+
const FALLBACK_BG = {
|
|
37
|
+
Idle: '#e0e0e3',
|
|
38
|
+
notSaved: '#b84fbd',
|
|
39
|
+
saved: '#5d00b5'
|
|
40
|
+
};
|
|
41
|
+
const FALLBACK_FG = {
|
|
42
|
+
Idle: '#000000',
|
|
43
|
+
notSaved: '#ffffff',
|
|
44
|
+
saved: '#ffffff'
|
|
45
|
+
};
|
|
46
|
+
const toNumber = (value, fallback) => {
|
|
47
|
+
if (typeof value === 'number') {
|
|
48
|
+
return Number.isFinite(value) ? value : fallback;
|
|
49
|
+
}
|
|
50
|
+
if (typeof value === 'string') {
|
|
51
|
+
const parsed = Number(value);
|
|
52
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
53
|
+
}
|
|
54
|
+
return fallback;
|
|
55
|
+
};
|
|
56
|
+
const toFontWeight = (value, fallback) => {
|
|
57
|
+
if (typeof value === 'number') {
|
|
58
|
+
return String(value);
|
|
59
|
+
}
|
|
60
|
+
if (typeof value === 'string') {
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
return fallback;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Single calendar-month glyph: a small pill with a status-driven background
|
|
67
|
+
* and foreground, and a centered month label. Exposed for callers who want
|
|
68
|
+
* to compose glyphs themselves; most consumers should use
|
|
69
|
+
* {@link MonthlyStatusGrid} instead, which keeps the count, layout and
|
|
70
|
+
* legend in sync automatically.
|
|
71
|
+
*/
|
|
72
|
+
function CalendarGlyph({
|
|
73
|
+
label = 'Jan',
|
|
74
|
+
status = 'Idle',
|
|
75
|
+
modes: propModes = EMPTY_MODES,
|
|
76
|
+
style,
|
|
77
|
+
labelStyle,
|
|
78
|
+
accessibilityLabel
|
|
79
|
+
}) {
|
|
80
|
+
const {
|
|
81
|
+
modes: globalModes
|
|
82
|
+
} = useTokens();
|
|
83
|
+
const modes = React.useMemo(() => ({
|
|
84
|
+
...globalModes,
|
|
85
|
+
...propModes,
|
|
86
|
+
'Calendar Glyph State': status
|
|
87
|
+
}), [globalModes, propModes, status]);
|
|
88
|
+
const width = toNumber(getVariableByName('calendarGlyph/width', modes), 46);
|
|
89
|
+
const height = toNumber(getVariableByName('calendarGlyph/height', modes), 46);
|
|
90
|
+
const radius = toNumber(getVariableByName('calendarGlyph/radius', modes), 29);
|
|
91
|
+
const paddingTop = toNumber(getVariableByName('calendarGlyph/padding/top', modes), 16);
|
|
92
|
+
const paddingBottom = toNumber(getVariableByName('calendarGlyph/padding/bottom', modes), 16);
|
|
93
|
+
const paddingLeft = toNumber(getVariableByName('calendarGlyph/padding/left', modes), 13);
|
|
94
|
+
const paddingRight = toNumber(getVariableByName('calendarGlyph/padding/right', modes), 12);
|
|
95
|
+
const gap = toNumber(getVariableByName('calendarGlyph/gap', modes), 0);
|
|
96
|
+
const background = getVariableByName('calendarGlyph/background', modes) ?? FALLBACK_BG[status];
|
|
97
|
+
const foreground = getVariableByName('calendarGlyph/foreground', modes) ?? FALLBACK_FG[status];
|
|
98
|
+
const fontFamily = getVariableByName('calendarGlyph/fontFamily', modes) ?? 'JioType Var';
|
|
99
|
+
const fontSize = toNumber(getVariableByName('calendarGlyph/fontSize', modes), 12);
|
|
100
|
+
const lineHeight = toNumber(getVariableByName('calendarGlyph/lineHeight', modes), 14);
|
|
101
|
+
const fontWeight = toFontWeight(getVariableByName('calendarGlyph/fontWeight', modes), '500');
|
|
102
|
+
const defaultAccessibilityLabel = accessibilityLabel ?? `${typeof label === 'string' ? label : 'Month'}, ${DEFAULT_LEGEND_LABELS[status].toLowerCase()}`;
|
|
103
|
+
return /*#__PURE__*/_jsx(View, {
|
|
104
|
+
accessibilityLabel: defaultAccessibilityLabel,
|
|
105
|
+
style: [{
|
|
106
|
+
// Use min* so the pill is always at least the Figma size, but
|
|
107
|
+
// grows naturally when the label is wider (e.g. localized
|
|
108
|
+
// months). Avoids the ellipsis-truncation that happens when a
|
|
109
|
+
// 3-letter label barely overflows the strict inner width.
|
|
110
|
+
minWidth: width,
|
|
111
|
+
minHeight: height,
|
|
112
|
+
borderRadius: radius,
|
|
113
|
+
backgroundColor: background,
|
|
114
|
+
paddingTop,
|
|
115
|
+
paddingBottom,
|
|
116
|
+
paddingLeft,
|
|
117
|
+
paddingRight,
|
|
118
|
+
alignItems: 'center',
|
|
119
|
+
justifyContent: 'center',
|
|
120
|
+
gap
|
|
121
|
+
}, style],
|
|
122
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
123
|
+
numberOfLines: 1,
|
|
124
|
+
style: [{
|
|
125
|
+
color: foreground,
|
|
126
|
+
fontFamily,
|
|
127
|
+
fontSize,
|
|
128
|
+
lineHeight,
|
|
129
|
+
fontWeight,
|
|
130
|
+
textAlign: 'center'
|
|
131
|
+
}, labelStyle],
|
|
132
|
+
children: label
|
|
133
|
+
})
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Resolve the canonical color pair for a status. Used by both the glyph and
|
|
139
|
+
* the matching legend item so they can never visually drift apart.
|
|
140
|
+
*/
|
|
141
|
+
function resolveStatusColors(status, modes) {
|
|
142
|
+
const statusModes = {
|
|
143
|
+
...modes,
|
|
144
|
+
'Calendar Glyph State': status
|
|
145
|
+
};
|
|
146
|
+
const bg = getVariableByName('calendarGlyph/background', statusModes) ?? FALLBACK_BG[status];
|
|
147
|
+
const fg = getVariableByName('calendarGlyph/foreground', statusModes) ?? FALLBACK_FG[status];
|
|
148
|
+
return {
|
|
149
|
+
bg,
|
|
150
|
+
fg,
|
|
151
|
+
statusModes
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* `MonthlyStatusGrid` shows a year (or any contiguous range of months) as a
|
|
157
|
+
* grid of small pill-shaped calendar glyphs, each colored by its semantic
|
|
158
|
+
* status (`Idle` = "No data", `notSaved` = "Not saved", `saved` = "Saved").
|
|
159
|
+
* A legend below the grid explains the colors.
|
|
160
|
+
*
|
|
161
|
+
* Cohesiveness guarantees:
|
|
162
|
+
* - The number of glyphs is **always** `months.length`. There is no way to
|
|
163
|
+
* render a glyph that has no underlying status.
|
|
164
|
+
* - The legend dot color for a status is resolved through the **same**
|
|
165
|
+
* `calendarGlyph/background` token + `Calendar Glyph State` mode as the
|
|
166
|
+
* glyph itself, so the visual mapping cannot drift.
|
|
167
|
+
*
|
|
168
|
+
* The chart is rendered with plain RN primitives (`View` + `Text`); SVG is
|
|
169
|
+
* unnecessary here because the glyph shape is a simple rounded rectangle.
|
|
170
|
+
*
|
|
171
|
+
* @component
|
|
172
|
+
*/
|
|
173
|
+
function MonthlyStatusGrid({
|
|
174
|
+
months = DEFAULT_MONTHS,
|
|
175
|
+
columns = 4,
|
|
176
|
+
legend,
|
|
177
|
+
legendStatuses = DEFAULT_LEGEND_STATUSES,
|
|
178
|
+
modes: propModes = EMPTY_MODES,
|
|
179
|
+
style,
|
|
180
|
+
monthsStyle,
|
|
181
|
+
rowStyle,
|
|
182
|
+
glyphStyle,
|
|
183
|
+
labelStyle,
|
|
184
|
+
legendStyle,
|
|
185
|
+
accessibilityLabel
|
|
186
|
+
}) {
|
|
187
|
+
const {
|
|
188
|
+
modes: globalModes
|
|
189
|
+
} = useTokens();
|
|
190
|
+
const modes = React.useMemo(() => ({
|
|
191
|
+
...globalModes,
|
|
192
|
+
...propModes
|
|
193
|
+
}), [globalModes, propModes]);
|
|
194
|
+
const gridGap = toNumber(getVariableByName('monthlyStatusGrid/gap', modes), 16);
|
|
195
|
+
const rowGap = toNumber(getVariableByName('monthlyStatusGrid/months/gap', modes), 12);
|
|
196
|
+
const safeColumns = Math.max(1, Math.floor(columns));
|
|
197
|
+
|
|
198
|
+
// Group months into rows of `safeColumns`. Render-time only; cheap.
|
|
199
|
+
const rows = React.useMemo(() => {
|
|
200
|
+
const out = [];
|
|
201
|
+
for (let i = 0; i < months.length; i += safeColumns) {
|
|
202
|
+
out.push(months.slice(i, i + safeColumns));
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}, [months, safeColumns]);
|
|
206
|
+
const showLegend = legend !== false;
|
|
207
|
+
const legendOverrides = legend && typeof legend === 'object' ? legend : {};
|
|
208
|
+
const defaultAccessibilityLabel = accessibilityLabel ?? `Monthly status grid: ${months.length} month${months.length === 1 ? '' : 's'}`;
|
|
209
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
210
|
+
accessibilityLabel: defaultAccessibilityLabel,
|
|
211
|
+
style: [{
|
|
212
|
+
width: '100%',
|
|
213
|
+
gap: gridGap,
|
|
214
|
+
alignItems: 'stretch',
|
|
215
|
+
justifyContent: 'center'
|
|
216
|
+
}, style],
|
|
217
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
218
|
+
style: [{
|
|
219
|
+
width: '100%',
|
|
220
|
+
gap: rowGap,
|
|
221
|
+
alignItems: 'stretch'
|
|
222
|
+
}, monthsStyle],
|
|
223
|
+
children: rows.map((row, rowIndex) => /*#__PURE__*/_jsx(View, {
|
|
224
|
+
style: [{
|
|
225
|
+
flexDirection: 'row',
|
|
226
|
+
alignItems: 'center',
|
|
227
|
+
justifyContent: 'space-between',
|
|
228
|
+
width: '100%'
|
|
229
|
+
}, rowStyle],
|
|
230
|
+
children: row.map((month, colIndex) => {
|
|
231
|
+
const absoluteIndex = rowIndex * safeColumns + colIndex;
|
|
232
|
+
const fallbackLabel = DEFAULT_MONTH_LABELS[absoluteIndex % DEFAULT_MONTH_LABELS.length];
|
|
233
|
+
const label = month.label ?? fallbackLabel;
|
|
234
|
+
const glyphModes = month.modes ? {
|
|
235
|
+
...modes,
|
|
236
|
+
...month.modes
|
|
237
|
+
} : modes;
|
|
238
|
+
return /*#__PURE__*/_jsx(CalendarGlyph, {
|
|
239
|
+
label: label,
|
|
240
|
+
status: month.status,
|
|
241
|
+
modes: glyphModes,
|
|
242
|
+
...(glyphStyle !== undefined ? {
|
|
243
|
+
style: glyphStyle
|
|
244
|
+
} : {}),
|
|
245
|
+
...(labelStyle !== undefined ? {
|
|
246
|
+
labelStyle
|
|
247
|
+
} : {}),
|
|
248
|
+
...(month.accessibilityLabel !== undefined ? {
|
|
249
|
+
accessibilityLabel: month.accessibilityLabel
|
|
250
|
+
} : {})
|
|
251
|
+
}, month.key ?? `glyph-${absoluteIndex}`);
|
|
252
|
+
})
|
|
253
|
+
}, `row-${rowIndex}`))
|
|
254
|
+
}), showLegend ? /*#__PURE__*/_jsx(View, {
|
|
255
|
+
style: [{
|
|
256
|
+
flexDirection: 'row',
|
|
257
|
+
alignItems: 'center',
|
|
258
|
+
justifyContent: 'space-between',
|
|
259
|
+
width: '100%'
|
|
260
|
+
}, legendStyle],
|
|
261
|
+
children: legendStatuses.map(status => {
|
|
262
|
+
const {
|
|
263
|
+
bg,
|
|
264
|
+
statusModes
|
|
265
|
+
} = resolveStatusColors(status, modes);
|
|
266
|
+
const label = legendOverrides[status] ?? DEFAULT_LEGEND_LABELS[status];
|
|
267
|
+
return /*#__PURE__*/_jsx(MetricLegendItem, {
|
|
268
|
+
label: label,
|
|
269
|
+
indicatorColor: bg,
|
|
270
|
+
modes: statusModes,
|
|
271
|
+
style: {
|
|
272
|
+
flex: 1,
|
|
273
|
+
minWidth: 0
|
|
274
|
+
}
|
|
275
|
+
}, `legend-${status}`);
|
|
276
|
+
})
|
|
277
|
+
}) : null]
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
export { CalendarGlyph };
|
|
281
|
+
export default MonthlyStatusGrid;
|