jfs-components 0.0.70 → 0.0.72
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 +49 -0
- package/lib/commonjs/components/CardAdvisory/CardAdvisory.js +203 -0
- package/lib/commonjs/components/CardCTA/CardCTA.js +198 -16
- package/lib/commonjs/components/CardFinancialCondition/CardFinancialCondition.js +213 -0
- package/lib/commonjs/components/Carousel/Carousel.js +9 -7
- package/lib/commonjs/components/CircularProgressBar/CircularProgressBar.js +147 -0
- package/lib/commonjs/components/CircularProgressBarDoted/CircularProgressBarDoted.js +258 -0
- package/lib/commonjs/components/CircularRating/CircularRating.js +161 -0
- package/lib/commonjs/components/Gauge/Gauge.js +223 -0
- package/lib/commonjs/components/HoldingsCard/HoldingsCard.js +2 -2
- package/lib/commonjs/components/InstitutionBadge/InstitutionBadge.js +132 -0
- package/lib/commonjs/components/ListGroup/ListGroup.js +3 -1
- package/lib/commonjs/components/Nudge/Nudge.js +179 -87
- package/lib/commonjs/components/Radio/Radio.js +194 -0
- package/lib/commonjs/components/RadioButton/RadioButton.js +21 -188
- package/lib/commonjs/components/index.js +56 -0
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/CardAdvisory/CardAdvisory.js +197 -0
- package/lib/module/components/CardCTA/CardCTA.js +199 -17
- package/lib/module/components/CardFinancialCondition/CardFinancialCondition.js +207 -0
- package/lib/module/components/Carousel/Carousel.js +9 -7
- package/lib/module/components/CircularProgressBar/CircularProgressBar.js +141 -0
- package/lib/module/components/CircularProgressBarDoted/CircularProgressBarDoted.js +253 -0
- package/lib/module/components/CircularRating/CircularRating.js +155 -0
- package/lib/module/components/Gauge/Gauge.js +217 -0
- package/lib/module/components/HoldingsCard/HoldingsCard.js +2 -2
- package/lib/module/components/InstitutionBadge/InstitutionBadge.js +127 -0
- package/lib/module/components/ListGroup/ListGroup.js +3 -1
- package/lib/module/components/Nudge/Nudge.js +178 -87
- package/lib/module/components/Radio/Radio.js +188 -0
- package/lib/module/components/RadioButton/RadioButton.js +20 -185
- package/lib/module/components/index.js +12 -0
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/CardAdvisory/CardAdvisory.d.ts +49 -0
- package/lib/typescript/src/components/CardCTA/CardCTA.d.ts +16 -1
- package/lib/typescript/src/components/CardFinancialCondition/CardFinancialCondition.d.ts +50 -0
- package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +27 -0
- package/lib/typescript/src/components/CircularProgressBarDoted/CircularProgressBarDoted.d.ts +48 -0
- package/lib/typescript/src/components/CircularRating/CircularRating.d.ts +49 -0
- package/lib/typescript/src/components/Gauge/Gauge.d.ts +53 -0
- package/lib/typescript/src/components/InstitutionBadge/InstitutionBadge.d.ts +30 -0
- package/lib/typescript/src/components/Nudge/Nudge.d.ts +14 -11
- 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/index.d.ts +13 -1
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/CardAdvisory/CardAdvisory.tsx +283 -0
- package/src/components/CardCTA/CardCTA.tsx +236 -13
- package/src/components/CardFinancialCondition/CardFinancialCondition.tsx +366 -0
- package/src/components/Carousel/Carousel.tsx +14 -6
- package/src/components/CircularProgressBar/CircularProgressBar.tsx +190 -0
- package/src/components/CircularProgressBarDoted/CircularProgressBarDoted.tsx +357 -0
- package/src/components/CircularRating/CircularRating.tsx +241 -0
- package/src/components/Gauge/Gauge.tsx +303 -0
- package/src/components/HoldingsCard/HoldingsCard.tsx +2 -2
- package/src/components/InstitutionBadge/InstitutionBadge.tsx +216 -0
- package/src/components/ListGroup/ListGroup.tsx +3 -1
- package/src/components/Nudge/Nudge.tsx +222 -82
- package/src/components/Radio/Radio.tsx +227 -0
- package/src/components/RadioButton/RadioButton.tsx +23 -225
- package/src/components/index.ts +13 -1
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- package/src/icons/registry.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,55 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## [0.0.72] - 2026-05-04
|
|
8
|
+
|
|
9
|
+
High-level summary (one line per point):
|
|
10
|
+
|
|
11
|
+
- **`RadioButton` renamed to `Radio`** — naming aligned with the rest of the library; the old `RadioButton` import is preserved as a deprecated alias so consumer code keeps working without changes.
|
|
12
|
+
- **`Carousel` pagination is now fully token-driven** — dot size, active width, and active/inactive colors come from design tokens (`carousel/pagination/indicator/{size,activeWidth,activeColor,inactiveColor}`) instead of hardcoded values, with token names normalized to camelCase.
|
|
13
|
+
- **`Carousel` outer container height is now controlled by a token** — new `carousel/maxHeight` token enforces the Figma-aligned max height of the carousel viewport.
|
|
14
|
+
- **`HoldingsCard` migrated to `Radio`** — internal-only swap, no API change for consumers.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- `Carousel.Pagination` now reads `carousel/pagination/indicator/size`, `carousel/pagination/indicator/activeWidth`, `carousel/pagination/indicator/activeColor`, and `carousel/pagination/indicator/inactiveColor` from design tokens. Previous lowercase names (`activecolor`, `inactivecolor`) and hardcoded dot dimensions (6×6 / 16×6) are replaced.
|
|
19
|
+
- `Carousel` outer container respects a new `carousel/maxHeight` token (defaults to `280`) applied as `maxHeight` on the outer view.
|
|
20
|
+
- `HoldingsCard` now imports the new `Radio` component instead of `RadioButton` (no behavioral change).
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- New `Radio` component (`src/components/Radio/Radio.tsx`) with its own stories and MDX docs. Exported from the package barrel as `Radio` / `RadioProps`.
|
|
25
|
+
|
|
26
|
+
### Deprecated
|
|
27
|
+
|
|
28
|
+
- `RadioButton` is deprecated. It is kept as a thin re-export of `Radio` for backward compatibility and will be removed in a future major release. Migrate to `import { Radio } from 'jfs-components'`.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## [0.0.71] - 2026-05-04
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **`CardAdvisory`** — new card component with stories and MDX docs.
|
|
37
|
+
- **`CardFinancialCondition`** — new card component with stories and MDX docs.
|
|
38
|
+
- **`CircularProgressBar`** — new circular progress component with stories and MDX docs.
|
|
39
|
+
- **`CircularProgressBarDoted`** — new dotted variant of the circular progress component with stories and MDX docs.
|
|
40
|
+
- **`CircularRating`** — new circular rating display component with stories and MDX docs.
|
|
41
|
+
- **`Gauge`** — new gauge component with stories and MDX docs.
|
|
42
|
+
- **`InstitutionBadge`** — new institution badge component with stories and MDX docs.
|
|
43
|
+
- All new components are exported from the package barrel with their public types.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- **`Nudge`** — significant rewrite (props, layout, and token usage refreshed); story file simplified.
|
|
48
|
+
- **`CardCTA`** — expanded API and visual variants; story coverage extended.
|
|
49
|
+
- **`MediaCard`** — minor doc/story refresh; no breaking changes.
|
|
50
|
+
- **`ListGroup`** — small adjustments to internal layout; no API change.
|
|
51
|
+
- Refreshed component token metadata and the icon registry; updated `Coin Variables` design-token JSON.
|
|
52
|
+
- Storybook plumbing: added `.storybook/storybook-test-mock.js` and registered it in `.storybook/main.js` to stabilize Storybook test runs.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
7
56
|
## [0.0.70] - 2026-04-23
|
|
8
57
|
|
|
9
58
|
### Fixed
|
|
@@ -0,0 +1,203 @@
|
|
|
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 _JFSThemeProvider = require("../../design-tokens/JFSThemeProvider");
|
|
11
|
+
var _Icon = _interopRequireDefault(require("../../icons/Icon"));
|
|
12
|
+
var _reactUtils = require("../../utils/react-utils");
|
|
13
|
+
var _CircularProgressBar = _interopRequireDefault(require("../CircularProgressBar/CircularProgressBar"));
|
|
14
|
+
var _Nudge = _interopRequireDefault(require("../Nudge/Nudge"));
|
|
15
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
16
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
|
+
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); }
|
|
18
|
+
const toNumber = (value, fallback) => {
|
|
19
|
+
if (typeof value === 'number') {
|
|
20
|
+
return Number.isFinite(value) ? value : fallback;
|
|
21
|
+
}
|
|
22
|
+
if (typeof value === 'string') {
|
|
23
|
+
const parsed = Number(value);
|
|
24
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
25
|
+
}
|
|
26
|
+
return fallback;
|
|
27
|
+
};
|
|
28
|
+
const toFontWeight = (value, fallback) => {
|
|
29
|
+
if (typeof value === 'number') {
|
|
30
|
+
return String(value);
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === 'string') {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
return fallback;
|
|
36
|
+
};
|
|
37
|
+
function resolveCardAdvisoryTokens(modes) {
|
|
38
|
+
const width = toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/width', modes), 360);
|
|
39
|
+
const gap = toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/gap', modes), 16);
|
|
40
|
+
const paddingHorizontal = toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/padding/horizontal', modes), 0);
|
|
41
|
+
const paddingVertical = toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/padding/vertical', modes), 0);
|
|
42
|
+
const radius = toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/radius', modes), 0);
|
|
43
|
+
const background = (0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/background', modes) || '#ffffff';
|
|
44
|
+
const mainContentGap = toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/mainContent/gap', modes), 16);
|
|
45
|
+
const contentGap = toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/content/gap', modes), 6);
|
|
46
|
+
const headerGap = toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/header/gap', modes), 8);
|
|
47
|
+
const titleColor = (0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/title/foreground', modes) || '#0d0d0f';
|
|
48
|
+
const titleFontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/title/fontSize', modes), 26);
|
|
49
|
+
const titleFontFamily = (0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/title/fontFamily', modes) || 'JioType Var';
|
|
50
|
+
const titleLineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/title/lineHeight', modes), 26);
|
|
51
|
+
const titleFontWeight = toFontWeight((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/title/fontWeight', modes), '900');
|
|
52
|
+
const titleDescenderAllowance = Math.ceil(titleFontSize * 0.16);
|
|
53
|
+
const descriptionColor = (0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/description/foreground', modes) || '#24262b';
|
|
54
|
+
const descriptionFontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/description/fontSize', modes), 12);
|
|
55
|
+
const descriptionFontFamily = (0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/description/fontFamily', modes) || 'JioType Var';
|
|
56
|
+
const descriptionLineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/description/lineHeight', modes), 16);
|
|
57
|
+
const descriptionFontWeight = toFontWeight((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/description/fontWeight', modes), '500');
|
|
58
|
+
return {
|
|
59
|
+
containerStyle: {
|
|
60
|
+
alignItems: 'flex-start',
|
|
61
|
+
backgroundColor: background,
|
|
62
|
+
borderRadius: radius,
|
|
63
|
+
gap,
|
|
64
|
+
overflow: 'hidden',
|
|
65
|
+
paddingHorizontal,
|
|
66
|
+
paddingVertical,
|
|
67
|
+
width
|
|
68
|
+
},
|
|
69
|
+
mainContentStyle: {
|
|
70
|
+
alignItems: 'flex-start',
|
|
71
|
+
flexDirection: 'row',
|
|
72
|
+
gap: mainContentGap,
|
|
73
|
+
width: '100%'
|
|
74
|
+
},
|
|
75
|
+
contentStyle: {
|
|
76
|
+
alignItems: 'flex-start',
|
|
77
|
+
flex: 1,
|
|
78
|
+
gap: contentGap,
|
|
79
|
+
minWidth: 1
|
|
80
|
+
},
|
|
81
|
+
headerStyle: {
|
|
82
|
+
alignItems: 'center',
|
|
83
|
+
flexDirection: 'row',
|
|
84
|
+
gap: headerGap,
|
|
85
|
+
width: '100%'
|
|
86
|
+
},
|
|
87
|
+
titleStyle: {
|
|
88
|
+
color: titleColor,
|
|
89
|
+
fontFamily: titleFontFamily,
|
|
90
|
+
fontSize: titleFontSize,
|
|
91
|
+
fontWeight: titleFontWeight,
|
|
92
|
+
lineHeight: titleLineHeight,
|
|
93
|
+
marginBottom: -titleDescenderAllowance,
|
|
94
|
+
paddingBottom: titleDescenderAllowance
|
|
95
|
+
},
|
|
96
|
+
descriptionStyle: {
|
|
97
|
+
color: descriptionColor,
|
|
98
|
+
fontFamily: descriptionFontFamily,
|
|
99
|
+
fontSize: descriptionFontSize,
|
|
100
|
+
fontWeight: descriptionFontWeight,
|
|
101
|
+
lineHeight: descriptionLineHeight,
|
|
102
|
+
width: '100%'
|
|
103
|
+
},
|
|
104
|
+
iconColor: (0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/icon/color', modes) || '#1a1c1f',
|
|
105
|
+
iconSize: toNumber((0, _figmaVariablesResolver.getVariableByName)('cardAdvisory/icon/size', modes), 18)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function CardAdvisory({
|
|
109
|
+
title = 'Spending',
|
|
110
|
+
description = 'Track your spending habits and stay within your budget.',
|
|
111
|
+
value = 70,
|
|
112
|
+
valueLabel,
|
|
113
|
+
showInfoIcon = true,
|
|
114
|
+
showNudge = true,
|
|
115
|
+
nudgeBody = 'Data confidence is low, add more accounts for better insights.',
|
|
116
|
+
nudgeButtonLabel = 'Button',
|
|
117
|
+
onPressNudgeButton,
|
|
118
|
+
titleEndSlot,
|
|
119
|
+
progressSlot,
|
|
120
|
+
nudgeSlot,
|
|
121
|
+
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
122
|
+
style,
|
|
123
|
+
mainContentStyle,
|
|
124
|
+
titleStyle,
|
|
125
|
+
descriptionStyle,
|
|
126
|
+
progressStyle,
|
|
127
|
+
nudgeStyle,
|
|
128
|
+
accessibilityLabel,
|
|
129
|
+
...rest
|
|
130
|
+
}) {
|
|
131
|
+
const {
|
|
132
|
+
modes: globalModes
|
|
133
|
+
} = (0, _JFSThemeProvider.useTokens)();
|
|
134
|
+
const modes = (0, _react.useMemo)(() => globalModes === _reactUtils.EMPTY_MODES && propModes === _reactUtils.EMPTY_MODES ? _reactUtils.EMPTY_MODES : {
|
|
135
|
+
...globalModes,
|
|
136
|
+
...propModes
|
|
137
|
+
}, [globalModes, propModes]);
|
|
138
|
+
const tokens = (0, _react.useMemo)(() => resolveCardAdvisoryTokens(modes), [modes]);
|
|
139
|
+
const processedTitleEndSlot = (0, _react.useMemo)(() => {
|
|
140
|
+
if (!titleEndSlot) return null;
|
|
141
|
+
const processed = (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(titleEndSlot), modes);
|
|
142
|
+
return processed.length === 1 ? processed[0] : processed;
|
|
143
|
+
}, [titleEndSlot, modes]);
|
|
144
|
+
const processedProgressSlot = (0, _react.useMemo)(() => {
|
|
145
|
+
if (!progressSlot) return null;
|
|
146
|
+
const processed = (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(progressSlot), modes);
|
|
147
|
+
return processed.length === 1 ? processed[0] : processed;
|
|
148
|
+
}, [progressSlot, modes]);
|
|
149
|
+
const processedNudgeSlot = (0, _react.useMemo)(() => {
|
|
150
|
+
if (!nudgeSlot) return null;
|
|
151
|
+
const processed = (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(nudgeSlot), modes);
|
|
152
|
+
return processed.length === 1 ? processed[0] : processed;
|
|
153
|
+
}, [nudgeSlot, modes]);
|
|
154
|
+
const defaultAccessibilityLabel = accessibilityLabel ?? `${title}. ${description}. ${Math.round(value)} out of 100. ${nudgeBody}`;
|
|
155
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
156
|
+
accessibilityLabel: defaultAccessibilityLabel,
|
|
157
|
+
style: [tokens.containerStyle, style],
|
|
158
|
+
...rest,
|
|
159
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
160
|
+
style: [tokens.mainContentStyle, mainContentStyle],
|
|
161
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
162
|
+
style: tokens.contentStyle,
|
|
163
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
164
|
+
style: tokens.headerStyle,
|
|
165
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
166
|
+
numberOfLines: 1,
|
|
167
|
+
style: [tokens.titleStyle, titleStyle],
|
|
168
|
+
children: title
|
|
169
|
+
}), processedTitleEndSlot || (showInfoIcon ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
|
|
170
|
+
name: "ic_info",
|
|
171
|
+
size: tokens.iconSize,
|
|
172
|
+
color: tokens.iconColor,
|
|
173
|
+
accessibilityElementsHidden: true,
|
|
174
|
+
importantForAccessibility: "no"
|
|
175
|
+
}) : null)]
|
|
176
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
177
|
+
style: [tokens.descriptionStyle, descriptionStyle],
|
|
178
|
+
children: description
|
|
179
|
+
})]
|
|
180
|
+
}), processedProgressSlot || /*#__PURE__*/(0, _jsxRuntime.jsx)(_CircularProgressBar.default, {
|
|
181
|
+
state: "Active",
|
|
182
|
+
value: value,
|
|
183
|
+
modes: modes,
|
|
184
|
+
style: progressStyle,
|
|
185
|
+
...(valueLabel ? {
|
|
186
|
+
valueLabel
|
|
187
|
+
} : {})
|
|
188
|
+
})]
|
|
189
|
+
}), showNudge ? processedNudgeSlot || /*#__PURE__*/(0, _jsxRuntime.jsx)(_Nudge.default, {
|
|
190
|
+
type: "inline-compact",
|
|
191
|
+
body: nudgeBody,
|
|
192
|
+
buttonLabel: nudgeButtonLabel,
|
|
193
|
+
modes: modes,
|
|
194
|
+
style: [{
|
|
195
|
+
width: '100%'
|
|
196
|
+
}, nudgeStyle],
|
|
197
|
+
...(onPressNudgeButton ? {
|
|
198
|
+
onPressButton: onPressNudgeButton
|
|
199
|
+
} : {})
|
|
200
|
+
}) : null]
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
var _default = exports.default = /*#__PURE__*/_react.default.memo(CardAdvisory);
|
|
@@ -11,17 +11,39 @@ var _JFSThemeProvider = require("../../design-tokens/JFSThemeProvider");
|
|
|
11
11
|
var _reactUtils = require("../../utils/react-utils");
|
|
12
12
|
var _IconCapsule = _interopRequireDefault(require("../IconCapsule/IconCapsule"));
|
|
13
13
|
var _Button = _interopRequireDefault(require("../Button/Button"));
|
|
14
|
+
var _Badge = _interopRequireDefault(require("../Badge/Badge"));
|
|
15
|
+
var _ButtonGroup = _interopRequireDefault(require("../ButtonGroup/ButtonGroup"));
|
|
16
|
+
var _IconButton = _interopRequireDefault(require("../IconButton/IconButton"));
|
|
14
17
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
18
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
19
|
+
const optionalTokenAvailability = new Map();
|
|
20
|
+
function getOptionalVariableByName(name, modes, fallback) {
|
|
21
|
+
let isAvailable = optionalTokenAvailability.get(name);
|
|
22
|
+
if (isAvailable === undefined) {
|
|
23
|
+
isAvailable = (0, _figmaVariablesResolver.findVariablesByPattern)(name).some(variable => variable.name === name);
|
|
24
|
+
optionalTokenAvailability.set(name, isAvailable);
|
|
25
|
+
}
|
|
26
|
+
if (!isAvailable) {
|
|
27
|
+
return fallback;
|
|
28
|
+
}
|
|
29
|
+
return (0, _figmaVariablesResolver.getVariableByName)(name, modes) ?? fallback;
|
|
30
|
+
}
|
|
16
31
|
function CardCTA({
|
|
32
|
+
type = 'cta',
|
|
17
33
|
title = 'If you have 1 line',
|
|
18
34
|
body = 'Then you can have up to 3 lines in the subtext as well. This is for demonstration.',
|
|
19
35
|
iconName = 'ic_upi_number',
|
|
20
|
-
buttonLabel
|
|
36
|
+
buttonLabel,
|
|
21
37
|
onPressButton,
|
|
38
|
+
ratingLabel = '+28 Rating',
|
|
39
|
+
showRatingActions = true,
|
|
40
|
+
onPressLike,
|
|
41
|
+
onPressDislike,
|
|
22
42
|
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
23
43
|
iconSlot,
|
|
24
44
|
buttonSlot,
|
|
45
|
+
ratingBadgeSlot,
|
|
46
|
+
ratingActionsSlot,
|
|
25
47
|
style
|
|
26
48
|
}) {
|
|
27
49
|
const {
|
|
@@ -31,6 +53,7 @@ function CardCTA({
|
|
|
31
53
|
...globalModes,
|
|
32
54
|
...propModes
|
|
33
55
|
};
|
|
56
|
+
const isRating = type === 'rating';
|
|
34
57
|
const background = (0, _figmaVariablesResolver.getVariableByName)('cardCTA/background', modes) || '#ffffff';
|
|
35
58
|
const radius = (0, _figmaVariablesResolver.getVariableByName)('cardCTA/radius', modes) || 12;
|
|
36
59
|
const borderSize = (0, _figmaVariablesResolver.getVariableByName)('cardCTA/border/size', modes) || 1;
|
|
@@ -53,13 +76,48 @@ function CardCTA({
|
|
|
53
76
|
const bodyLineHeight = (0, _figmaVariablesResolver.getVariableByName)('cardCTA/body/lineHeight', modes) || 12;
|
|
54
77
|
const bodyFontWeightRaw = (0, _figmaVariablesResolver.getVariableByName)('cardCTA/body/fontWeight', modes) || 400;
|
|
55
78
|
const bodyFontWeight = typeof bodyFontWeightRaw === 'number' ? bodyFontWeightRaw.toString() : bodyFontWeightRaw;
|
|
79
|
+
const ratingContentGap = getOptionalVariableByName('cardCTA/rating/content/gap', modes, 12);
|
|
80
|
+
const ratingContentPaddingH = getOptionalVariableByName('cardCTA/rating/content/padding/horizontal', modes, 16);
|
|
81
|
+
const ratingContentPaddingV = getOptionalVariableByName('cardCTA/rating/content/padding/vertical', modes, 12);
|
|
82
|
+
const ratingFooterPaddingH = getOptionalVariableByName('cardCTA/rating/footer/horizontal', modes, 16);
|
|
83
|
+
const ratingFooterPaddingTop = getOptionalVariableByName('cardCTA/rating/footer/top', modes, 0);
|
|
84
|
+
const ratingFooterPaddingBottom = getOptionalVariableByName('cardCTA/rating/footer/bottom', modes, 12);
|
|
85
|
+
const buttonModes = {
|
|
86
|
+
...modes,
|
|
87
|
+
AppearanceBrand: 'Secondary',
|
|
88
|
+
'Button / Size': 'S'
|
|
89
|
+
};
|
|
90
|
+
const iconButtonModes = {
|
|
91
|
+
'Button / Size': 'S',
|
|
92
|
+
'Emphasis': 'Low',
|
|
93
|
+
'AppearanceBrand': 'Neutral',
|
|
94
|
+
...modes
|
|
95
|
+
};
|
|
96
|
+
const effectiveButtonLabel = buttonLabel ?? (isRating ? 'Save' : 'Button');
|
|
97
|
+
const nonWrappingButtonLabel = effectiveButtonLabel.replace(/\s/g, '\u00A0');
|
|
98
|
+
const [measuredButtonLabelWidth, setMeasuredButtonLabelWidth] = _react.default.useState(null);
|
|
99
|
+
const buttonPaddingH = (0, _figmaVariablesResolver.getVariableByName)('button/padding/horizontal', buttonModes) || 20;
|
|
100
|
+
const buttonBorderSize = (0, _figmaVariablesResolver.getVariableByName)('button/border/size', buttonModes) ?? 1;
|
|
101
|
+
const measuredButtonWidth = measuredButtonLabelWidth === null ? undefined : Math.ceil(measuredButtonLabelWidth + buttonPaddingH * 2 + buttonBorderSize * 2);
|
|
102
|
+
const handleButtonLabelTextLayout = _react.default.useCallback(event => {
|
|
103
|
+
const lines = event?.nativeEvent?.lines;
|
|
104
|
+
if (!Array.isArray(lines) || lines.length === 0) return;
|
|
105
|
+
const nextWidth = Math.ceil(lines.reduce((sum, line) => sum + (typeof line?.width === 'number' ? line.width : 0), 0));
|
|
106
|
+
if (nextWidth <= 0) return;
|
|
107
|
+
setMeasuredButtonLabelWidth(currentWidth => {
|
|
108
|
+
if (currentWidth !== null && Math.abs(currentWidth - nextWidth) < 1) {
|
|
109
|
+
return currentWidth;
|
|
110
|
+
}
|
|
111
|
+
return nextWidth;
|
|
112
|
+
});
|
|
113
|
+
}, []);
|
|
56
114
|
const containerStyle = {
|
|
57
115
|
backgroundColor: background,
|
|
58
116
|
borderRadius: radius,
|
|
59
117
|
borderWidth: borderSize,
|
|
60
118
|
borderColor,
|
|
61
|
-
flexDirection: 'row',
|
|
62
|
-
overflow: '
|
|
119
|
+
flexDirection: isRating ? 'column' : 'row',
|
|
120
|
+
overflow: 'visible'
|
|
63
121
|
};
|
|
64
122
|
|
|
65
123
|
// NOTE: `minWidth: 0` + explicit `flexShrink: 1` are required on native.
|
|
@@ -76,7 +134,9 @@ function CardCTA({
|
|
|
76
134
|
paddingVertical: leftPaddingV,
|
|
77
135
|
gap: leftGap,
|
|
78
136
|
alignItems: 'flex-start',
|
|
79
|
-
justifyContent: 'center'
|
|
137
|
+
justifyContent: 'center',
|
|
138
|
+
overflow: 'visible',
|
|
139
|
+
zIndex: 1
|
|
80
140
|
};
|
|
81
141
|
|
|
82
142
|
// NOTE: rightWrap must NOT shrink on native. On Android (Yoga), the default
|
|
@@ -105,6 +165,29 @@ function CardCTA({
|
|
|
105
165
|
alignSelf: 'stretch',
|
|
106
166
|
minWidth: 0
|
|
107
167
|
};
|
|
168
|
+
|
|
169
|
+
// Keep text shrink/wrap behavior on the left column, but let the CTA keep
|
|
170
|
+
// its own intrinsic width. On native, Yoga otherwise measures the Button
|
|
171
|
+
// with the left column's available width and the single-line label
|
|
172
|
+
// truncates even when the Button itself has no width/maxWidth constraint.
|
|
173
|
+
const buttonWrapStyle = {
|
|
174
|
+
alignSelf: 'flex-start',
|
|
175
|
+
flexGrow: 0,
|
|
176
|
+
flexShrink: 0,
|
|
177
|
+
flexBasis: 'auto',
|
|
178
|
+
overflow: 'visible',
|
|
179
|
+
zIndex: 1
|
|
180
|
+
};
|
|
181
|
+
const buttonStyle = {
|
|
182
|
+
alignSelf: 'flex-start',
|
|
183
|
+
flexGrow: 0,
|
|
184
|
+
flexShrink: 0,
|
|
185
|
+
flexBasis: 'auto',
|
|
186
|
+
overflow: 'visible',
|
|
187
|
+
...(measuredButtonWidth !== undefined ? {
|
|
188
|
+
width: measuredButtonWidth
|
|
189
|
+
} : {})
|
|
190
|
+
};
|
|
108
191
|
const titleStyle = {
|
|
109
192
|
color: titleColor,
|
|
110
193
|
fontFamily: titleFontFamily,
|
|
@@ -119,6 +202,104 @@ function CardCTA({
|
|
|
119
202
|
lineHeight: bodyLineHeight,
|
|
120
203
|
fontWeight: bodyFontWeight
|
|
121
204
|
};
|
|
205
|
+
const ratingContentStyle = {
|
|
206
|
+
paddingHorizontal: ratingContentPaddingH,
|
|
207
|
+
paddingVertical: ratingContentPaddingV,
|
|
208
|
+
gap: ratingContentGap,
|
|
209
|
+
alignItems: 'flex-start'
|
|
210
|
+
};
|
|
211
|
+
const ratingFooterStyle = {
|
|
212
|
+
flexDirection: 'row',
|
|
213
|
+
alignItems: 'flex-start',
|
|
214
|
+
justifyContent: 'space-between',
|
|
215
|
+
paddingHorizontal: ratingFooterPaddingH,
|
|
216
|
+
paddingTop: ratingFooterPaddingTop,
|
|
217
|
+
paddingBottom: ratingFooterPaddingBottom,
|
|
218
|
+
overflow: 'visible'
|
|
219
|
+
};
|
|
220
|
+
const buttonLabelStyle = {
|
|
221
|
+
flexGrow: 0,
|
|
222
|
+
flexShrink: 0,
|
|
223
|
+
flexWrap: 'nowrap'
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// Keep the rating CTA on an overflow-visible, non-shrinking path. The
|
|
227
|
+
// footer's row width stays fixed by the card, but Yoga must not use that
|
|
228
|
+
// width to shrink or clip the button label.
|
|
229
|
+
const ratingButtonWrapStyle = {
|
|
230
|
+
flexGrow: 0,
|
|
231
|
+
flexShrink: 0,
|
|
232
|
+
flexBasis: 'auto',
|
|
233
|
+
alignItems: 'flex-start',
|
|
234
|
+
overflow: 'visible'
|
|
235
|
+
};
|
|
236
|
+
const ratingButtonStyle = {
|
|
237
|
+
alignSelf: 'flex-start',
|
|
238
|
+
flexGrow: 0,
|
|
239
|
+
flexShrink: 0,
|
|
240
|
+
flexBasis: 'auto',
|
|
241
|
+
overflow: 'visible',
|
|
242
|
+
...(measuredButtonWidth !== undefined ? {
|
|
243
|
+
width: measuredButtonWidth
|
|
244
|
+
} : {})
|
|
245
|
+
};
|
|
246
|
+
const ratingButtonLabelStyle = {
|
|
247
|
+
flexGrow: 0,
|
|
248
|
+
flexShrink: 0,
|
|
249
|
+
flexWrap: 'nowrap'
|
|
250
|
+
};
|
|
251
|
+
if (isRating) {
|
|
252
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
253
|
+
style: [containerStyle, style],
|
|
254
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
255
|
+
style: ratingContentStyle,
|
|
256
|
+
children: [ratingBadgeSlot ? (0, _reactUtils.cloneChildrenWithModes)(ratingBadgeSlot, modes) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Badge.default, {
|
|
257
|
+
label: ratingLabel,
|
|
258
|
+
modes: modes
|
|
259
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
260
|
+
style: textWrapStyle,
|
|
261
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
262
|
+
style: titleStyle,
|
|
263
|
+
children: title
|
|
264
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
265
|
+
style: bodyStyle,
|
|
266
|
+
children: body
|
|
267
|
+
})]
|
|
268
|
+
})]
|
|
269
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
270
|
+
style: ratingFooterStyle,
|
|
271
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
272
|
+
style: ratingButtonWrapStyle,
|
|
273
|
+
children: buttonSlot ? (0, _reactUtils.cloneChildrenWithModes)(buttonSlot, buttonModes) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
|
|
274
|
+
label: effectiveButtonLabel,
|
|
275
|
+
onPress: onPressButton || (() => {}),
|
|
276
|
+
modes: buttonModes,
|
|
277
|
+
style: ratingButtonStyle,
|
|
278
|
+
renderContent: labelStyles => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
279
|
+
onTextLayout: handleButtonLabelTextLayout,
|
|
280
|
+
style: [labelStyles, ratingButtonLabelStyle],
|
|
281
|
+
children: nonWrappingButtonLabel
|
|
282
|
+
})
|
|
283
|
+
})
|
|
284
|
+
}), showRatingActions ? ratingActionsSlot ? (0, _reactUtils.cloneChildrenWithModes)(ratingActionsSlot, iconButtonModes) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_ButtonGroup.default, {
|
|
285
|
+
modes: iconButtonModes,
|
|
286
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_IconButton.default, {
|
|
287
|
+
iconName: "ic_like",
|
|
288
|
+
accessibilityLabel: "Like",
|
|
289
|
+
...(onPressLike ? {
|
|
290
|
+
onPress: onPressLike
|
|
291
|
+
} : {})
|
|
292
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_IconButton.default, {
|
|
293
|
+
iconName: "ic_dislike",
|
|
294
|
+
accessibilityLabel: "Dislike",
|
|
295
|
+
...(onPressDislike ? {
|
|
296
|
+
onPress: onPressDislike
|
|
297
|
+
} : {})
|
|
298
|
+
})]
|
|
299
|
+
}) : null]
|
|
300
|
+
})]
|
|
301
|
+
});
|
|
302
|
+
}
|
|
122
303
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
123
304
|
style: [containerStyle, style],
|
|
124
305
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
@@ -132,18 +313,19 @@ function CardCTA({
|
|
|
132
313
|
style: bodyStyle,
|
|
133
314
|
children: body
|
|
134
315
|
})]
|
|
135
|
-
}),
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
316
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
317
|
+
style: buttonWrapStyle,
|
|
318
|
+
children: buttonSlot ? (0, _reactUtils.cloneChildrenWithModes)(buttonSlot, buttonModes) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
|
|
319
|
+
label: effectiveButtonLabel,
|
|
320
|
+
onPress: onPressButton || (() => {}),
|
|
321
|
+
modes: buttonModes,
|
|
322
|
+
style: buttonStyle,
|
|
323
|
+
renderContent: labelStyles => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
324
|
+
onTextLayout: handleButtonLabelTextLayout,
|
|
325
|
+
style: [labelStyles, buttonLabelStyle],
|
|
326
|
+
children: nonWrappingButtonLabel
|
|
327
|
+
})
|
|
328
|
+
})
|
|
147
329
|
})]
|
|
148
330
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
149
331
|
style: rightWrapStyle,
|