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.
Files changed (158) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/lib/commonjs/components/AccordionCheckbox/AccordionCheckbox.js +239 -0
  3. package/lib/commonjs/components/AccountCard/AccountCard.js +247 -0
  4. package/lib/commonjs/components/AppBar/AppBar.js +17 -11
  5. package/lib/commonjs/components/BrandChip/BrandChip.js +149 -0
  6. package/lib/commonjs/components/CardBankAccount/CardBankAccount.js +229 -0
  7. package/lib/commonjs/components/CardInsight/CardInsight.js +166 -0
  8. package/lib/commonjs/components/CheckboxGroup/CheckboxGroup.js +67 -0
  9. package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +140 -0
  10. package/lib/commonjs/components/CircularProgressBar/CircularProgressBar.js +56 -9
  11. package/lib/commonjs/components/CoverageBarComparison/CoverageBarComparison.js +272 -0
  12. package/lib/commonjs/components/CoverageRing/CoverageRing.js +141 -0
  13. package/lib/commonjs/components/DonutChart/DonutChart.js +309 -0
  14. package/lib/commonjs/components/DonutChartSummary/DonutChartSummary.js +155 -0
  15. package/lib/commonjs/components/Dropdown/Dropdown.js +214 -0
  16. package/lib/commonjs/components/DropdownInput/DropdownInput.js +542 -0
  17. package/lib/commonjs/components/FormField/FormField.js +328 -178
  18. package/lib/commonjs/components/LinearMeter/LinearMeter.js +9 -28
  19. package/lib/commonjs/components/LinearProgress/LinearProgress.js +68 -0
  20. package/lib/commonjs/components/LottieIntroBlock/LottieIntroBlock.js +150 -0
  21. package/lib/commonjs/components/MetricLegendItem/MetricLegendItem.js +95 -0
  22. package/lib/commonjs/components/MonthlyStatusGrid/MonthlyStatusGrid.js +286 -0
  23. package/lib/commonjs/components/OTP/OTP.js +381 -37
  24. package/lib/commonjs/components/PageHero/PageHero.js +153 -0
  25. package/lib/commonjs/components/PoweredByLabel/PoweredByLabel.js +135 -0
  26. package/lib/commonjs/components/PoweredByLabel/finvu.png +0 -0
  27. package/lib/commonjs/components/ProductOverview/ProductOverview.js +147 -0
  28. package/lib/commonjs/components/RangeTrack/RangeTrack.js +269 -0
  29. package/lib/commonjs/components/SavingsGoalSummary/SavingsGoalSummary.js +181 -0
  30. package/lib/commonjs/components/SegmentedTrack/SegmentedTrack.js +171 -0
  31. package/lib/commonjs/components/StatGroup/StatGroup.js +128 -0
  32. package/lib/commonjs/components/StatItem/StatItem.js +65 -35
  33. package/lib/commonjs/components/StrengthIndicator/StrengthIndicator.js +157 -0
  34. package/lib/commonjs/components/SummaryTile/SummaryTile.js +150 -0
  35. package/lib/commonjs/components/Text/Text.js +9 -2
  36. package/lib/commonjs/components/Tooltip/Tooltip.js +34 -27
  37. package/lib/commonjs/components/index.js +231 -1
  38. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  39. package/lib/commonjs/icons/registry.js +1 -1
  40. package/lib/commonjs/utils/index.js +7 -0
  41. package/lib/commonjs/utils/number-utils.js +57 -0
  42. package/lib/module/components/AccordionCheckbox/AccordionCheckbox.js +233 -0
  43. package/lib/module/components/AccountCard/AccountCard.js +241 -0
  44. package/lib/module/components/AppBar/AppBar.js +17 -11
  45. package/lib/module/components/BrandChip/BrandChip.js +143 -0
  46. package/lib/module/components/CardBankAccount/CardBankAccount.js +223 -0
  47. package/lib/module/components/CardInsight/CardInsight.js +161 -0
  48. package/lib/module/components/CheckboxGroup/CheckboxGroup.js +62 -0
  49. package/lib/module/components/CheckboxItem/CheckboxItem.js +134 -0
  50. package/lib/module/components/CircularProgressBar/CircularProgressBar.js +56 -9
  51. package/lib/module/components/CoverageBarComparison/CoverageBarComparison.js +266 -0
  52. package/lib/module/components/CoverageRing/CoverageRing.js +136 -0
  53. package/lib/module/components/DonutChart/DonutChart.js +303 -0
  54. package/lib/module/components/DonutChartSummary/DonutChartSummary.js +150 -0
  55. package/lib/module/components/Dropdown/Dropdown.js +206 -0
  56. package/lib/module/components/DropdownInput/DropdownInput.js +536 -0
  57. package/lib/module/components/FormField/FormField.js +330 -180
  58. package/lib/module/components/LinearMeter/LinearMeter.js +9 -28
  59. package/lib/module/components/LinearProgress/LinearProgress.js +63 -0
  60. package/lib/module/components/LottieIntroBlock/LottieIntroBlock.js +144 -0
  61. package/lib/module/components/MetricLegendItem/MetricLegendItem.js +90 -0
  62. package/lib/module/components/MonthlyStatusGrid/MonthlyStatusGrid.js +281 -0
  63. package/lib/module/components/OTP/OTP.js +381 -38
  64. package/lib/module/components/PageHero/PageHero.js +147 -0
  65. package/lib/module/components/PoweredByLabel/PoweredByLabel.js +130 -0
  66. package/lib/module/components/PoweredByLabel/finvu.png +0 -0
  67. package/lib/module/components/ProductOverview/ProductOverview.js +142 -0
  68. package/lib/module/components/RangeTrack/RangeTrack.js +263 -0
  69. package/lib/module/components/SavingsGoalSummary/SavingsGoalSummary.js +175 -0
  70. package/lib/module/components/SegmentedTrack/SegmentedTrack.js +166 -0
  71. package/lib/module/components/StatGroup/StatGroup.js +123 -0
  72. package/lib/module/components/StatItem/StatItem.js +66 -36
  73. package/lib/module/components/StrengthIndicator/StrengthIndicator.js +152 -0
  74. package/lib/module/components/SummaryTile/SummaryTile.js +145 -0
  75. package/lib/module/components/Text/Text.js +9 -2
  76. package/lib/module/components/Tooltip/Tooltip.js +34 -27
  77. package/lib/module/components/index.js +28 -2
  78. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  79. package/lib/module/icons/registry.js +1 -1
  80. package/lib/module/utils/index.js +2 -1
  81. package/lib/module/utils/number-utils.js +53 -0
  82. package/lib/typescript/src/components/AccordionCheckbox/AccordionCheckbox.d.ts +71 -0
  83. package/lib/typescript/src/components/AccountCard/AccountCard.d.ts +81 -0
  84. package/lib/typescript/src/components/BrandChip/BrandChip.d.ts +43 -0
  85. package/lib/typescript/src/components/CardBankAccount/CardBankAccount.d.ts +86 -0
  86. package/lib/typescript/src/components/CardInsight/CardInsight.d.ts +48 -0
  87. package/lib/typescript/src/components/CheckboxGroup/CheckboxGroup.d.ts +41 -0
  88. package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +72 -0
  89. package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +11 -1
  90. package/lib/typescript/src/components/CoverageBarComparison/CoverageBarComparison.d.ts +105 -0
  91. package/lib/typescript/src/components/CoverageRing/CoverageRing.d.ts +90 -0
  92. package/lib/typescript/src/components/DonutChart/DonutChart.d.ts +117 -0
  93. package/lib/typescript/src/components/DonutChartSummary/DonutChartSummary.d.ts +103 -0
  94. package/lib/typescript/src/components/Dropdown/Dropdown.d.ts +62 -0
  95. package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +107 -0
  96. package/lib/typescript/src/components/FormField/FormField.d.ts +76 -19
  97. package/lib/typescript/src/components/LinearProgress/LinearProgress.d.ts +17 -0
  98. package/lib/typescript/src/components/LottieIntroBlock/LottieIntroBlock.d.ts +58 -0
  99. package/lib/typescript/src/components/MetricLegendItem/MetricLegendItem.d.ts +37 -0
  100. package/lib/typescript/src/components/MonthlyStatusGrid/MonthlyStatusGrid.d.ts +119 -0
  101. package/lib/typescript/src/components/OTP/OTP.d.ts +88 -2
  102. package/lib/typescript/src/components/PageHero/PageHero.d.ts +53 -0
  103. package/lib/typescript/src/components/PoweredByLabel/PoweredByLabel.d.ts +70 -0
  104. package/lib/typescript/src/components/ProductOverview/ProductOverview.d.ts +39 -0
  105. package/lib/typescript/src/components/RangeTrack/RangeTrack.d.ts +173 -0
  106. package/lib/typescript/src/components/SavingsGoalSummary/SavingsGoalSummary.d.ts +95 -0
  107. package/lib/typescript/src/components/SegmentedTrack/SegmentedTrack.d.ts +108 -0
  108. package/lib/typescript/src/components/StatGroup/StatGroup.d.ts +45 -0
  109. package/lib/typescript/src/components/StatItem/StatItem.d.ts +24 -7
  110. package/lib/typescript/src/components/StrengthIndicator/StrengthIndicator.d.ts +58 -0
  111. package/lib/typescript/src/components/SummaryTile/SummaryTile.d.ts +60 -0
  112. package/lib/typescript/src/components/Text/Text.d.ts +12 -2
  113. package/lib/typescript/src/components/Tooltip/Tooltip.d.ts +13 -2
  114. package/lib/typescript/src/components/index.d.ts +29 -3
  115. package/lib/typescript/src/icons/registry.d.ts +1 -1
  116. package/lib/typescript/src/utils/index.d.ts +1 -0
  117. package/lib/typescript/src/utils/number-utils.d.ts +29 -0
  118. package/package.json +1 -3
  119. package/src/components/AccordionCheckbox/AccordionCheckbox.tsx +323 -0
  120. package/src/components/AccountCard/AccountCard.tsx +376 -0
  121. package/src/components/AppBar/AppBar.tsx +25 -14
  122. package/src/components/BrandChip/BrandChip.tsx +235 -0
  123. package/src/components/CardBankAccount/CardBankAccount.tsx +321 -0
  124. package/src/components/CardInsight/CardInsight.tsx +239 -0
  125. package/src/components/CheckboxGroup/CheckboxGroup.tsx +86 -0
  126. package/src/components/CheckboxItem/CheckboxItem.tsx +209 -0
  127. package/src/components/CircularProgressBar/CircularProgressBar.tsx +74 -9
  128. package/src/components/CoverageBarComparison/CoverageBarComparison.tsx +378 -0
  129. package/src/components/CoverageRing/CoverageRing.tsx +225 -0
  130. package/src/components/DonutChart/DonutChart.tsx +503 -0
  131. package/src/components/DonutChartSummary/DonutChartSummary.tsx +256 -0
  132. package/src/components/Dropdown/Dropdown.tsx +331 -0
  133. package/src/components/DropdownInput/DropdownInput.tsx +819 -0
  134. package/src/components/FormField/FormField.tsx +542 -215
  135. package/src/components/LinearMeter/LinearMeter.tsx +9 -39
  136. package/src/components/LinearProgress/LinearProgress.tsx +92 -0
  137. package/src/components/LottieIntroBlock/LottieIntroBlock.tsx +202 -0
  138. package/src/components/MetricLegendItem/MetricLegendItem.tsx +167 -0
  139. package/src/components/MonthlyStatusGrid/MonthlyStatusGrid.tsx +438 -0
  140. package/src/components/OTP/OTP.tsx +476 -29
  141. package/src/components/PageHero/PageHero.tsx +200 -0
  142. package/src/components/PoweredByLabel/PoweredByLabel.tsx +221 -0
  143. package/src/components/PoweredByLabel/finvu.png +0 -0
  144. package/src/components/ProductOverview/ProductOverview.tsx +236 -0
  145. package/src/components/RangeTrack/RangeTrack.tsx +394 -0
  146. package/src/components/SavingsGoalSummary/SavingsGoalSummary.tsx +269 -0
  147. package/src/components/SegmentedTrack/SegmentedTrack.tsx +268 -0
  148. package/src/components/StatGroup/StatGroup.tsx +169 -0
  149. package/src/components/StatItem/StatItem.tsx +117 -40
  150. package/src/components/StrengthIndicator/StrengthIndicator.tsx +205 -0
  151. package/src/components/SummaryTile/SummaryTile.tsx +251 -0
  152. package/src/components/Text/Text.tsx +24 -3
  153. package/src/components/Tooltip/Tooltip.tsx +50 -25
  154. package/src/components/index.ts +47 -3
  155. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  156. package/src/icons/registry.ts +1 -1
  157. package/src/utils/index.ts +1 -0
  158. package/src/utils/number-utils.ts +60 -0
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+
3
+ import React, { useMemo } 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 Button from '../Button/Button';
8
+ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
9
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
+ const DEFAULT_MEDIA_SIZE = 117;
11
+ /**
12
+ * LottieIntroBlock displays a centered onboarding/intro block composed of a
13
+ * media slot (typically a Lottie animation or illustration) above a title,
14
+ * an optional supportive paragraph, and an optional action button.
15
+ *
16
+ * All visual values are resolved from Figma design tokens via
17
+ * `getVariableByName`. Slots cascade the active `modes` to their children
18
+ * through `cloneChildrenWithModes`.
19
+ *
20
+ * @component
21
+ * @example
22
+ * ```tsx
23
+ * <LottieIntroBlock
24
+ * title="Let's get to know how your financial health is doing"
25
+ * supportText="From assets to taxes, stay on top of everything in one simple view."
26
+ * buttonLabel="Get started"
27
+ * onButtonPress={() => navigate('NextScreen')}
28
+ * media={<MyLottiePlayer source={animationSource} />}
29
+ * />
30
+ * ```
31
+ */
32
+ function LottieIntroBlock({
33
+ title = "Let's get to know how your financial health is doing",
34
+ showSupportText = true,
35
+ supportText = 'From assets to taxes, stay on top of everything in one simple view.',
36
+ showButton = true,
37
+ buttonLabel = 'Button',
38
+ onButtonPress,
39
+ media,
40
+ buttonSlot,
41
+ modes: propModes = EMPTY_MODES,
42
+ style,
43
+ testID
44
+ }) {
45
+ const {
46
+ modes: globalModes
47
+ } = useTokens();
48
+ const modes = useMemo(() => ({
49
+ ...globalModes,
50
+ ...propModes
51
+ }), [globalModes, propModes]);
52
+
53
+ // Container
54
+ const gap = Number(getVariableByName('lottieIntroBlock/gap', modes)) || 36;
55
+ const paddingHorizontal = Number(getVariableByName('lottieIntroBlock/padding/horizontal', modes)) || 0;
56
+ const paddingVertical = Number(getVariableByName('lottieIntroBlock/padding/vertical', modes)) || 16;
57
+
58
+ // Text wrap
59
+ const textWrapGap = Number(getVariableByName('lottieIntroBlock/textWrap/gap', modes)) || 16;
60
+
61
+ // Title
62
+ const titleColor = getVariableByName('lottieIntroBlock/title/foreground', modes) || '#0d0d0f';
63
+ const titleFontSize = Number(getVariableByName('lottieIntroBlock/title/fontSize', modes)) || 23;
64
+ const titleFontFamily = getVariableByName('lottieIntroBlock/title/fontFamily', modes) || 'System';
65
+ const titleLineHeight = Number(getVariableByName('lottieIntroBlock/title/lineHeight', modes)) || 23;
66
+ const titleFontWeight = getVariableByName('lottieIntroBlock/title/fontWeight', modes) || 900;
67
+
68
+ // Support text
69
+ const supportColor = getVariableByName('lottieIntroBlock/supportText/foreground', modes) || '#0d0d0f';
70
+ const supportFontSize = Number(getVariableByName('lottieIntroBlock/supportText/fontSize', modes)) || 14;
71
+ const supportFontFamily = getVariableByName('lottieIntroBlock/supportText/fontFamily', modes) || 'System';
72
+ const supportLineHeight = Number(getVariableByName('lottieIntroBlock/supportText/lineHeight', modes)) || 18;
73
+ const supportFontWeight = getVariableByName('lottieIntroBlock/supportText/fontWeight', modes) || 400;
74
+ const containerStyle = {
75
+ flexDirection: 'column',
76
+ alignItems: 'center',
77
+ paddingHorizontal,
78
+ paddingVertical,
79
+ gap
80
+ };
81
+ const textWrapStyle = {
82
+ flexDirection: 'column',
83
+ alignItems: 'center',
84
+ gap: textWrapGap,
85
+ width: '100%'
86
+ };
87
+ const titleStyle = {
88
+ color: titleColor,
89
+ fontSize: titleFontSize,
90
+ fontFamily: titleFontFamily,
91
+ lineHeight: titleLineHeight,
92
+ fontWeight: String(titleFontWeight),
93
+ textAlign: 'center'
94
+ };
95
+ const supportTextStyle = {
96
+ color: supportColor,
97
+ fontSize: supportFontSize,
98
+ fontFamily: supportFontFamily,
99
+ lineHeight: supportLineHeight,
100
+ fontWeight: String(supportFontWeight),
101
+ textAlign: 'center'
102
+ };
103
+ const mediaContent = useMemo(() => {
104
+ if (media === undefined || media === null) {
105
+ return /*#__PURE__*/_jsx(View, {
106
+ style: {
107
+ width: DEFAULT_MEDIA_SIZE,
108
+ height: DEFAULT_MEDIA_SIZE
109
+ },
110
+ accessibilityElementsHidden: true,
111
+ importantForAccessibility: "no-hide-descendants"
112
+ });
113
+ }
114
+ return cloneChildrenWithModes(media, modes);
115
+ }, [media, modes]);
116
+ const buttonContent = useMemo(() => {
117
+ if (buttonSlot !== undefined && buttonSlot !== null) {
118
+ return cloneChildrenWithModes(buttonSlot, modes);
119
+ }
120
+ if (!showButton) {
121
+ return null;
122
+ }
123
+ return /*#__PURE__*/_jsx(Button, {
124
+ label: buttonLabel,
125
+ onPress: onButtonPress,
126
+ modes: modes
127
+ });
128
+ }, [buttonSlot, showButton, buttonLabel, onButtonPress, modes]);
129
+ return /*#__PURE__*/_jsxs(View, {
130
+ style: [containerStyle, style],
131
+ testID: testID,
132
+ children: [mediaContent, /*#__PURE__*/_jsxs(View, {
133
+ style: textWrapStyle,
134
+ children: [/*#__PURE__*/_jsx(Text, {
135
+ style: titleStyle,
136
+ children: title
137
+ }), showSupportText && supportText ? /*#__PURE__*/_jsx(Text, {
138
+ style: supportTextStyle,
139
+ children: supportText
140
+ }) : null, buttonContent]
141
+ })]
142
+ });
143
+ }
144
+ export default LottieIntroBlock;
@@ -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;