jfs-components 0.1.30 → 0.1.33
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 +29 -0
- package/D2C.md +118 -0
- package/lib/commonjs/components/CategoryCard/CategoryCard.js +264 -0
- package/lib/commonjs/components/CompareTable/CompareTable.js +140 -84
- package/lib/commonjs/components/ContentSheet/ContentSheet.js +28 -5
- package/lib/commonjs/components/CounterBadge/CounterBadge.js +78 -0
- package/lib/commonjs/components/FavoriteToggle/FavoriteToggle.js +180 -0
- package/lib/commonjs/components/HelloJioInput/HelloJioInput.js +287 -0
- package/lib/commonjs/components/ValueBackMetric/ValueBackMetric.js +219 -0
- package/lib/commonjs/components/index.js +35 -0
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +40095 -1
- package/lib/commonjs/design-tokens/figma-modes.generated.js +3 -0
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/CategoryCard/CategoryCard.js +258 -0
- package/lib/module/components/CompareTable/CompareTable.js +140 -84
- package/lib/module/components/ContentSheet/ContentSheet.js +29 -6
- package/lib/module/components/CounterBadge/CounterBadge.js +73 -0
- package/lib/module/components/FavoriteToggle/FavoriteToggle.js +174 -0
- package/lib/module/components/HelloJioInput/HelloJioInput.js +281 -0
- package/lib/module/components/ValueBackMetric/ValueBackMetric.js +214 -0
- package/lib/module/components/index.js +6 -1
- package/lib/module/design-tokens/Coin Variables-variables-full.json +40095 -1
- package/lib/module/design-tokens/figma-modes.generated.js +3 -0
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/CategoryCard/CategoryCard.d.ts +72 -0
- package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +16 -1
- package/lib/typescript/src/components/ContentSheet/ContentSheet.d.ts +7 -1
- package/lib/typescript/src/components/CounterBadge/CounterBadge.d.ts +19 -0
- package/lib/typescript/src/components/FavoriteToggle/FavoriteToggle.d.ts +66 -0
- package/lib/typescript/src/components/HelloJioInput/HelloJioInput.d.ts +111 -0
- package/lib/typescript/src/components/ValueBackMetric/ValueBackMetric.d.ts +86 -0
- package/lib/typescript/src/components/index.d.ts +5 -0
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +7 -1
- package/scripts/extract-figma-modes.js +359 -0
- package/src/components/CategoryCard/CategoryCard.tsx +404 -0
- package/src/components/CompareTable/CompareTable.tsx +201 -101
- package/src/components/ContentSheet/ContentSheet.tsx +40 -11
- package/src/components/CounterBadge/CounterBadge.tsx +95 -0
- package/src/components/FavoriteToggle/FavoriteToggle.tsx +243 -0
- package/src/components/HelloJioInput/HelloJioInput.tsx +513 -0
- package/src/components/ValueBackMetric/ValueBackMetric.tsx +298 -0
- package/src/components/index.ts +5 -0
- package/src/design-tokens/Coin Variables-variables-full.json +40095 -1
- package/src/design-tokens/figma-modes.generated.ts +3 -0
- package/src/icons/registry.ts +1 -1
|
@@ -0,0 +1,287 @@
|
|
|
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 _GlassFill = _interopRequireDefault(require("../../utils/GlassFill/GlassFill"));
|
|
14
|
+
var _IconButton = _interopRequireDefault(require("../IconButton/IconButton"));
|
|
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 IS_WEB = _reactNative.Platform.OS === 'web';
|
|
19
|
+
const STATE_COLLECTION = 'HelloJioInput State';
|
|
20
|
+
|
|
21
|
+
/** Default IconButton modes matching the Figma send control. */
|
|
22
|
+
const DEFAULT_SEND_BUTTON_MODES = Object.freeze({
|
|
23
|
+
AppearanceBrand: 'Primary',
|
|
24
|
+
'Button / Size': 'S'
|
|
25
|
+
});
|
|
26
|
+
const toNumber = (value, fallback) => {
|
|
27
|
+
if (typeof value === 'number') {
|
|
28
|
+
return Number.isFinite(value) ? value : fallback;
|
|
29
|
+
}
|
|
30
|
+
if (typeof value === 'string') {
|
|
31
|
+
const parsed = Number(value);
|
|
32
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
33
|
+
}
|
|
34
|
+
return fallback;
|
|
35
|
+
};
|
|
36
|
+
const toFontWeight = (value, fallback) => {
|
|
37
|
+
if (typeof value === 'number') return String(value);
|
|
38
|
+
if (typeof value === 'string') return value;
|
|
39
|
+
return fallback;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Fallback values from the Figma `HelloJioInput` component when local token
|
|
43
|
+
* JSON has not been synced yet. Mode-driven `getVariableByName` wins when
|
|
44
|
+
* tokens resolve.
|
|
45
|
+
*/
|
|
46
|
+
const STATE_FALLBACKS = {
|
|
47
|
+
Idle: {
|
|
48
|
+
background: '#f5f5f5',
|
|
49
|
+
borderColor: '#f5f5f5',
|
|
50
|
+
labelColor: '#545961',
|
|
51
|
+
iconColor: '#545961'
|
|
52
|
+
},
|
|
53
|
+
Active: {
|
|
54
|
+
background: '#ffffff',
|
|
55
|
+
borderColor: '#b5b5b5',
|
|
56
|
+
labelColor: '#24262b',
|
|
57
|
+
iconColor: '#24262b'
|
|
58
|
+
},
|
|
59
|
+
IdleJioPlus: {
|
|
60
|
+
background: '#f5f5f5',
|
|
61
|
+
borderColor: '#f5f5f5',
|
|
62
|
+
labelColor: '#545961',
|
|
63
|
+
iconColor: '#545961'
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
function resolveHelloJioInputTokens(modes, visualState) {
|
|
67
|
+
const fallback = STATE_FALLBACKS[visualState];
|
|
68
|
+
const background = (0, _figmaVariablesResolver.getVariableByName)('helloJioInput/background', modes) ?? fallback.background;
|
|
69
|
+
const borderColor = (0, _figmaVariablesResolver.getVariableByName)('helloJioInput/border/color', modes) ?? fallback.borderColor;
|
|
70
|
+
const strokeWidth = toNumber((0, _figmaVariablesResolver.getVariableByName)('helloJioInput/strokeWidth', modes), 1);
|
|
71
|
+
const gap = toNumber((0, _figmaVariablesResolver.getVariableByName)('helloJioInput/gap', modes), 8);
|
|
72
|
+
const paddingLeft = toNumber((0, _figmaVariablesResolver.getVariableByName)('helloJioInput/padding/left', modes), 12);
|
|
73
|
+
const paddingRight = toNumber((0, _figmaVariablesResolver.getVariableByName)('helloJioInput/padding/right', modes), 4);
|
|
74
|
+
const paddingTop = toNumber((0, _figmaVariablesResolver.getVariableByName)('helloJioInput/padding/top', modes), 4);
|
|
75
|
+
const paddingBottom = toNumber((0, _figmaVariablesResolver.getVariableByName)('helloJioInput/padding/bottom', modes), 4);
|
|
76
|
+
const height = toNumber((0, _figmaVariablesResolver.getVariableByName)('helloJioInput/height', modes), 36);
|
|
77
|
+
const radiusRaw = toNumber((0, _figmaVariablesResolver.getVariableByName)('helloJioInput/radius', modes), 99999);
|
|
78
|
+
const borderRadius = radiusRaw >= 9999 ? 99999 : radiusRaw;
|
|
79
|
+
const labelColor = (0, _figmaVariablesResolver.getVariableByName)('helloJioInput/label/color', modes) ?? fallback.labelColor;
|
|
80
|
+
const fontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('helloJioInput/fontSize', modes), 14);
|
|
81
|
+
const lineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('helloJioInput/lineHeight', modes), 18);
|
|
82
|
+
const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('helloJioInput/fontFamily', modes) ?? 'JioType Var';
|
|
83
|
+
const fontWeight = toFontWeight((0, _figmaVariablesResolver.getVariableByName)('helloJioInput/fontWeight', modes), '400');
|
|
84
|
+
const iconColor = (0, _figmaVariablesResolver.getVariableByName)('helloJioInput/icon/color', modes) ?? fallback.iconColor;
|
|
85
|
+
const iconSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('textInput/icon/size', modes) ?? (0, _figmaVariablesResolver.getVariableByName)('helloJioInput/icon/size', modes), 18);
|
|
86
|
+
const blurMinimal = toNumber((0, _figmaVariablesResolver.getVariableByName)('blur/minimal', modes), 29);
|
|
87
|
+
const blurIntensity = Math.max(0, Math.min(100, Math.round(blurMinimal)));
|
|
88
|
+
const useGlass = visualState === 'IdleJioPlus';
|
|
89
|
+
return {
|
|
90
|
+
containerStyle: {
|
|
91
|
+
position: 'relative',
|
|
92
|
+
flexDirection: 'row',
|
|
93
|
+
alignItems: 'center',
|
|
94
|
+
// Glass keeps the container transparent so GlassFill shows through.
|
|
95
|
+
backgroundColor: useGlass ? 'transparent' : background,
|
|
96
|
+
borderColor,
|
|
97
|
+
borderWidth: strokeWidth,
|
|
98
|
+
borderStyle: 'solid',
|
|
99
|
+
borderRadius,
|
|
100
|
+
paddingLeft,
|
|
101
|
+
paddingRight,
|
|
102
|
+
paddingTop,
|
|
103
|
+
paddingBottom,
|
|
104
|
+
gap,
|
|
105
|
+
height,
|
|
106
|
+
overflow: 'hidden',
|
|
107
|
+
width: '100%'
|
|
108
|
+
},
|
|
109
|
+
inputStyle: {
|
|
110
|
+
flex: 1,
|
|
111
|
+
color: labelColor,
|
|
112
|
+
fontSize,
|
|
113
|
+
fontFamily,
|
|
114
|
+
fontWeight,
|
|
115
|
+
// iOS single-line TextInput + explicit lineHeight sits below center —
|
|
116
|
+
// same quirk TextInput works around. Omit on iOS.
|
|
117
|
+
...(_reactNative.Platform.OS === 'ios' ? null : {
|
|
118
|
+
lineHeight
|
|
119
|
+
}),
|
|
120
|
+
padding: 0,
|
|
121
|
+
margin: 0,
|
|
122
|
+
minHeight: lineHeight,
|
|
123
|
+
outlineStyle: 'none',
|
|
124
|
+
outlineWidth: 0,
|
|
125
|
+
outlineColor: 'transparent'
|
|
126
|
+
},
|
|
127
|
+
iconColor,
|
|
128
|
+
iconSize,
|
|
129
|
+
blurIntensity,
|
|
130
|
+
useGlass
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* `HelloJioInput` is the pill-shaped HelloJio prompt field: leading brand
|
|
136
|
+
* icon, single-line text input, and a gold send `IconButton` on the right.
|
|
137
|
+
*
|
|
138
|
+
* Visual state is derived automatically:
|
|
139
|
+
* - **Idle** — gray fill, gray label (default)
|
|
140
|
+
* - **Active** — white fill + border while focused
|
|
141
|
+
* - **IdleJioPlus** — frosted glass when `jioPlus` and unfocused
|
|
142
|
+
*
|
|
143
|
+
* @component
|
|
144
|
+
*/
|
|
145
|
+
const HelloJioInput = /*#__PURE__*/(0, _react.forwardRef)(function HelloJioInput({
|
|
146
|
+
placeholder = 'Ask me anything',
|
|
147
|
+
value,
|
|
148
|
+
defaultValue = '',
|
|
149
|
+
onChangeText,
|
|
150
|
+
onSubmit,
|
|
151
|
+
leadingIconName = 'ic_hellojio',
|
|
152
|
+
leading,
|
|
153
|
+
trailing,
|
|
154
|
+
sendIconName = 'ic_send_message',
|
|
155
|
+
jioPlus = false,
|
|
156
|
+
disabled = false,
|
|
157
|
+
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
158
|
+
style,
|
|
159
|
+
inputStyle,
|
|
160
|
+
accessibilityLabel,
|
|
161
|
+
accessibilityHint,
|
|
162
|
+
testID,
|
|
163
|
+
onFocus,
|
|
164
|
+
onBlur,
|
|
165
|
+
returnKeyType = 'send',
|
|
166
|
+
...rest
|
|
167
|
+
}, ref) {
|
|
168
|
+
const {
|
|
169
|
+
modes: globalModes
|
|
170
|
+
} = (0, _JFSThemeProvider.useTokens)();
|
|
171
|
+
const inputRef = (0, _react.useRef)(null);
|
|
172
|
+
const [isFocused, setIsFocused] = (0, _react.useState)(false);
|
|
173
|
+
const [isHovered, setIsHovered] = (0, _react.useState)(false);
|
|
174
|
+
const [uncontrolledValue, setUncontrolledValue] = (0, _react.useState)(defaultValue);
|
|
175
|
+
const isControlled = value !== undefined;
|
|
176
|
+
const currentValue = isControlled ? value : uncontrolledValue;
|
|
177
|
+
const visualState = isFocused ? 'Active' : jioPlus ? 'IdleJioPlus' : 'Idle';
|
|
178
|
+
const modes = (0, _react.useMemo)(() => ({
|
|
179
|
+
...globalModes,
|
|
180
|
+
...propModes,
|
|
181
|
+
[STATE_COLLECTION]: visualState
|
|
182
|
+
}), [globalModes, propModes, visualState]);
|
|
183
|
+
const sendButtonModes = (0, _react.useMemo)(() => ({
|
|
184
|
+
...DEFAULT_SEND_BUTTON_MODES,
|
|
185
|
+
...modes
|
|
186
|
+
}), [modes]);
|
|
187
|
+
const tokens = (0, _react.useMemo)(() => resolveHelloJioInputTokens(modes, visualState), [modes, visualState]);
|
|
188
|
+
const handleChangeText = (0, _react.useCallback)(text => {
|
|
189
|
+
if (!isControlled) setUncontrolledValue(text);
|
|
190
|
+
onChangeText?.(text);
|
|
191
|
+
}, [isControlled, onChangeText]);
|
|
192
|
+
const handleSubmit = (0, _react.useCallback)(() => {
|
|
193
|
+
if (disabled) return;
|
|
194
|
+
onSubmit?.(currentValue);
|
|
195
|
+
}, [disabled, onSubmit, currentValue]);
|
|
196
|
+
const handleFocus = (0, _react.useCallback)(e => {
|
|
197
|
+
setIsFocused(true);
|
|
198
|
+
onFocus?.(e);
|
|
199
|
+
}, [onFocus]);
|
|
200
|
+
const handleBlur = (0, _react.useCallback)(e => {
|
|
201
|
+
setIsFocused(false);
|
|
202
|
+
onBlur?.(e);
|
|
203
|
+
}, [onBlur]);
|
|
204
|
+
const leadingElement = leading ?? (leadingIconName ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
|
|
205
|
+
name: leadingIconName,
|
|
206
|
+
size: tokens.iconSize,
|
|
207
|
+
color: tokens.iconColor
|
|
208
|
+
}) : null);
|
|
209
|
+
const processedLeading = leadingElement ? (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(leadingElement), modes) : null;
|
|
210
|
+
const defaultTrailing = /*#__PURE__*/(0, _jsxRuntime.jsx)(_IconButton.default, {
|
|
211
|
+
iconName: sendIconName,
|
|
212
|
+
modes: sendButtonModes,
|
|
213
|
+
onPress: handleSubmit,
|
|
214
|
+
disabled: disabled,
|
|
215
|
+
accessibilityLabel: "Send"
|
|
216
|
+
});
|
|
217
|
+
const trailingElement = trailing === undefined ? defaultTrailing : trailing === null ? null : trailing;
|
|
218
|
+
const processedTrailing = trailingElement ? (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(trailingElement), modes) : null;
|
|
219
|
+
const a11yLabel = accessibilityLabel ?? (placeholder || 'HelloJio input');
|
|
220
|
+
const hoverStyle = isHovered && !disabled && !isFocused ? {
|
|
221
|
+
opacity: 0.95
|
|
222
|
+
} : {};
|
|
223
|
+
const containerStyleArray = [tokens.containerStyle, hoverStyle, disabled ? {
|
|
224
|
+
opacity: 0.5
|
|
225
|
+
} : null, style];
|
|
226
|
+
const glassOverlay = (0, _figmaVariablesResolver.getVariableByName)('helloJioInput/background', modes) ?? STATE_FALLBACKS.IdleJioPlus.background;
|
|
227
|
+
const inner = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
228
|
+
children: [tokens.useGlass ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_GlassFill.default, {
|
|
229
|
+
tint: "light",
|
|
230
|
+
intensity: tokens.blurIntensity,
|
|
231
|
+
overlayColor: glassOverlay,
|
|
232
|
+
androidTintWash: false
|
|
233
|
+
}) : null, processedLeading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
234
|
+
accessibilityElementsHidden: true,
|
|
235
|
+
importantForAccessibility: "no",
|
|
236
|
+
style: SLOT_CENTER,
|
|
237
|
+
children: processedLeading
|
|
238
|
+
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
|
|
239
|
+
ref: (0, _reactUtils.mergeRefs)(inputRef, ref),
|
|
240
|
+
accessibilityLabel: a11yLabel,
|
|
241
|
+
accessibilityHint: accessibilityHint,
|
|
242
|
+
placeholder: isFocused ? '' : placeholder,
|
|
243
|
+
placeholderTextColor: tokens.inputStyle.color,
|
|
244
|
+
value: currentValue,
|
|
245
|
+
onChangeText: handleChangeText,
|
|
246
|
+
onFocus: handleFocus,
|
|
247
|
+
onBlur: handleBlur,
|
|
248
|
+
onSubmitEditing: handleSubmit,
|
|
249
|
+
returnKeyType: returnKeyType,
|
|
250
|
+
editable: !disabled,
|
|
251
|
+
style: [tokens.inputStyle, inputStyle],
|
|
252
|
+
testID: testID,
|
|
253
|
+
...rest
|
|
254
|
+
}), processedTrailing ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View
|
|
255
|
+
// Keep the send button in the a11y tree — it has its own label.
|
|
256
|
+
, {
|
|
257
|
+
style: SLOT_CENTER,
|
|
258
|
+
children: processedTrailing
|
|
259
|
+
}) : null]
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Same Android focus rule as TextInput: never wrap native TextInput in
|
|
263
|
+
// Pressable on native (steals the first tap). Web keeps Pressable for
|
|
264
|
+
// hover + click-anywhere-to-focus.
|
|
265
|
+
if (IS_WEB) {
|
|
266
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
267
|
+
style: containerStyleArray,
|
|
268
|
+
onHoverIn: () => setIsHovered(true),
|
|
269
|
+
onHoverOut: () => setIsHovered(false),
|
|
270
|
+
onPress: () => {
|
|
271
|
+
if (!disabled) inputRef.current?.focus();
|
|
272
|
+
},
|
|
273
|
+
accessible: false,
|
|
274
|
+
disabled: disabled,
|
|
275
|
+
children: inner
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
279
|
+
style: containerStyleArray,
|
|
280
|
+
children: inner
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
const SLOT_CENTER = {
|
|
284
|
+
justifyContent: 'center',
|
|
285
|
+
alignItems: 'center'
|
|
286
|
+
};
|
|
287
|
+
var _default = exports.default = /*#__PURE__*/_react.default.memo(HelloJioInput);
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireDefault(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
10
|
+
var _reactUtils = require("../../utils/react-utils");
|
|
11
|
+
var _Icon = _interopRequireDefault(require("../Icon/Icon"));
|
|
12
|
+
var _Link = _interopRequireDefault(require("../Link/Link"));
|
|
13
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
const TITLE_LINE_HEIGHT_RATIO = 1.2;
|
|
16
|
+
const VALUE_LINE_HEIGHT_RATIO = 1.2;
|
|
17
|
+
const CAPTION_LINE_HEIGHT_RATIO = 1.5;
|
|
18
|
+
|
|
19
|
+
// The Figma node binds the header-icon size and the per-part font sizes/weights
|
|
20
|
+
// to `valueBackMetric/*` variables, but those tokens are NOT in the committed
|
|
21
|
+
// variables snapshot (`npm run tokens -- find valueBackMetric` → no matches).
|
|
22
|
+
// They also intentionally differ from the `metricdata/*` typography defaults
|
|
23
|
+
// (e.g. `metricdata/value` resolves to 20px/700, whereas this component's value
|
|
24
|
+
// is 16px/500). So the sizes/weights below are encoded as literal constants
|
|
25
|
+
// matching the Figma design exactly. Colours, gaps and padding DO exist as real
|
|
26
|
+
// `metricdata/*` tokens (and match the design), so those resolve through
|
|
27
|
+
// `getVariableByName` and stay themeable.
|
|
28
|
+
const TITLE_FONT_SIZE = 12;
|
|
29
|
+
const TITLE_FONT_WEIGHT = '500';
|
|
30
|
+
const VALUE_FONT_SIZE = 16;
|
|
31
|
+
const VALUE_FONT_WEIGHT = '500';
|
|
32
|
+
const CAPTION_FONT_SIZE = 12;
|
|
33
|
+
const CAPTION_FONT_WEIGHT = '400';
|
|
34
|
+
const FONT_FAMILY = 'JioType Var';
|
|
35
|
+
const CARD_BACKGROUND = '#ffffff';
|
|
36
|
+
const CARD_MIN_HEIGHT = 82;
|
|
37
|
+
const ICON_SIZE = 18;
|
|
38
|
+
// Figma renders the header glyph flush (all `icon/padding/*` tokens are 0); the
|
|
39
|
+
// shared Icon pulls padding from those tokens, so mirror MetricData and zero it
|
|
40
|
+
// explicitly to keep the icon flush against the title.
|
|
41
|
+
const ICON_PADDING_RESET = {
|
|
42
|
+
paddingLeft: 0,
|
|
43
|
+
paddingRight: 0,
|
|
44
|
+
paddingTop: 0,
|
|
45
|
+
paddingBottom: 0
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* ValueBackMetric — a compact, left-aligned "value back" card.
|
|
49
|
+
*
|
|
50
|
+
* Stacks a branded header row (icon + `title`, e.g. "JioPoints"), a prominent
|
|
51
|
+
* `value`, a muted `caption`, and an optional bottom `linkLabel` (e.g. "Earn").
|
|
52
|
+
* `value`, `caption` and `linkLabel` each render only when provided; the header
|
|
53
|
+
* (icon + title) always shows.
|
|
54
|
+
*
|
|
55
|
+
* Colours, gaps and padding resolve from the `metricdata/*` tokens; the header
|
|
56
|
+
* glyph from the `icon/*` tokens (default 18px gold); the link entirely from
|
|
57
|
+
* the `link/*` + `text/foreground` tokens via the shared {@link Link}, with
|
|
58
|
+
* `Text Appearance: Secondary` and `Text Sizes: Small` seeded so it renders
|
|
59
|
+
* purple at 12px like the design (caller `modes` win). The per-part font
|
|
60
|
+
* sizes/weights come from the Figma `valueBackMetric/*` variables, which are
|
|
61
|
+
* not yet in the committed token snapshot, so they are encoded as constants
|
|
62
|
+
* matching the design.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```tsx
|
|
66
|
+
* <ValueBackMetric
|
|
67
|
+
* title="JioPoints"
|
|
68
|
+
* value="Value"
|
|
69
|
+
* caption="Earn 10 point with UPI"
|
|
70
|
+
* linkLabel="Earn"
|
|
71
|
+
* onPress={() => navigation.navigate('ValueBack')}
|
|
72
|
+
* onLinkPress={() => navigation.navigate('EarnPoints')}
|
|
73
|
+
* />
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
function ValueBackMetric({
|
|
77
|
+
icon = 'ic_rupee_coin',
|
|
78
|
+
title = 'JioPoints',
|
|
79
|
+
value,
|
|
80
|
+
caption,
|
|
81
|
+
linkLabel,
|
|
82
|
+
onLinkPress,
|
|
83
|
+
onPress,
|
|
84
|
+
disabled,
|
|
85
|
+
modes = _reactUtils.EMPTY_MODES,
|
|
86
|
+
style,
|
|
87
|
+
titleStyle,
|
|
88
|
+
valueStyle,
|
|
89
|
+
captionStyle,
|
|
90
|
+
accessibilityLabel
|
|
91
|
+
}) {
|
|
92
|
+
const titleColor = (0, _figmaVariablesResolver.getVariableByName)('metricdata/title/color', modes) ?? '#000000';
|
|
93
|
+
const valueColor = (0, _figmaVariablesResolver.getVariableByName)('metricdata/value/color', modes) ?? '#000000';
|
|
94
|
+
const captionColor = (0, _figmaVariablesResolver.getVariableByName)('metricdata/caption/color', modes) ?? '#777777';
|
|
95
|
+
// The design's link is purple (`text/foreground` under `Text Appearance:
|
|
96
|
+
// Secondary`) at 12px (`link/fontSize` under `Text Sizes: Small`), while the
|
|
97
|
+
// header icon keeps the default gold `icon/color`. Seed only the link's
|
|
98
|
+
// modes; caller-supplied modes win.
|
|
99
|
+
const linkModes = _react.default.useMemo(() => ({
|
|
100
|
+
'Text Appearance': 'Secondary',
|
|
101
|
+
'Text Sizes': 'Small',
|
|
102
|
+
...modes
|
|
103
|
+
}), [modes]);
|
|
104
|
+
|
|
105
|
+
// The header glyph is token-driven: `icon/color` (gold by default, retinted
|
|
106
|
+
// via `AppearanceBrand`) and `icon/size` (18px). Passing them explicitly — as
|
|
107
|
+
// MetricData does — makes the glyph deterministic instead of relying on the
|
|
108
|
+
// shared Icon's internal token lookups.
|
|
109
|
+
const iconColor = (0, _figmaVariablesResolver.getVariableByName)('icon/color', modes) ?? '#ad8444';
|
|
110
|
+
const iconSize = (0, _figmaVariablesResolver.getVariableByName)('icon/size', modes) ?? ICON_SIZE;
|
|
111
|
+
const gap = (0, _figmaVariablesResolver.getVariableByName)('metricdata/gap', modes) ?? 4;
|
|
112
|
+
const valueWrapGap = (0, _figmaVariablesResolver.getVariableByName)('metricdata/valueWrap/gap', modes) ?? 4;
|
|
113
|
+
const paddingHorizontal = (0, _figmaVariablesResolver.getVariableByName)('metricdata/padding/horizontal', modes) ?? 10;
|
|
114
|
+
const paddingVertical = (0, _figmaVariablesResolver.getVariableByName)('metricdata/padding/vertical', modes) ?? 10;
|
|
115
|
+
const containerStyle = {
|
|
116
|
+
flexDirection: 'column',
|
|
117
|
+
alignItems: 'flex-start',
|
|
118
|
+
justifyContent: 'center',
|
|
119
|
+
gap,
|
|
120
|
+
paddingHorizontal,
|
|
121
|
+
paddingVertical,
|
|
122
|
+
minHeight: CARD_MIN_HEIGHT,
|
|
123
|
+
backgroundColor: CARD_BACKGROUND
|
|
124
|
+
};
|
|
125
|
+
const titleTextStyle = {
|
|
126
|
+
color: titleColor,
|
|
127
|
+
fontFamily: FONT_FAMILY,
|
|
128
|
+
fontSize: TITLE_FONT_SIZE,
|
|
129
|
+
fontWeight: TITLE_FONT_WEIGHT,
|
|
130
|
+
lineHeight: TITLE_FONT_SIZE * TITLE_LINE_HEIGHT_RATIO
|
|
131
|
+
};
|
|
132
|
+
const valueTextStyle = {
|
|
133
|
+
color: valueColor,
|
|
134
|
+
fontFamily: FONT_FAMILY,
|
|
135
|
+
fontSize: VALUE_FONT_SIZE,
|
|
136
|
+
fontWeight: VALUE_FONT_WEIGHT,
|
|
137
|
+
lineHeight: VALUE_FONT_SIZE * VALUE_LINE_HEIGHT_RATIO
|
|
138
|
+
};
|
|
139
|
+
const captionTextStyle = {
|
|
140
|
+
color: captionColor,
|
|
141
|
+
fontFamily: FONT_FAMILY,
|
|
142
|
+
fontSize: CAPTION_FONT_SIZE,
|
|
143
|
+
fontWeight: CAPTION_FONT_WEIGHT,
|
|
144
|
+
lineHeight: CAPTION_FONT_SIZE * CAPTION_LINE_HEIGHT_RATIO
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// Mirrors the Figma swap slot, whose preferred instances are `Icon` and
|
|
148
|
+
// `Image`. A registry name renders the token-driven `Icon` (18px, gold,
|
|
149
|
+
// flush — all from the `icon/*` tokens); a custom node (`<Icon/>`,
|
|
150
|
+
// `<Image/>`, anything) renders as-is with `modes` cascaded into it, so an
|
|
151
|
+
// Icon instance follows the icon tokens while an Image keeps its own sizing.
|
|
152
|
+
const iconNode = icon == null ? null : typeof icon === 'string' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
|
|
153
|
+
iconName: icon,
|
|
154
|
+
size: iconSize,
|
|
155
|
+
color: iconColor,
|
|
156
|
+
modes: modes,
|
|
157
|
+
style: ICON_PADDING_RESET
|
|
158
|
+
}) : (0, _reactUtils.cloneChildrenWithModes)(icon, modes);
|
|
159
|
+
const hasValue = value !== undefined && value !== null && value !== '';
|
|
160
|
+
const hasCaption = caption !== undefined && caption !== '';
|
|
161
|
+
const resolvedLabel = accessibilityLabel ?? ([title, typeof value === 'string' ? value : undefined, caption, linkLabel].filter(Boolean).join(', ') || undefined);
|
|
162
|
+
const content = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
163
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
164
|
+
style: {
|
|
165
|
+
flexDirection: 'row',
|
|
166
|
+
alignItems: 'center',
|
|
167
|
+
gap: valueWrapGap
|
|
168
|
+
},
|
|
169
|
+
children: [iconNode, title !== undefined && title !== '' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
170
|
+
style: [titleTextStyle, titleStyle],
|
|
171
|
+
children: title
|
|
172
|
+
}) : null]
|
|
173
|
+
}), hasValue || hasCaption ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
174
|
+
style: {
|
|
175
|
+
flexDirection: 'column',
|
|
176
|
+
alignItems: 'flex-start',
|
|
177
|
+
gap: 4
|
|
178
|
+
},
|
|
179
|
+
children: [hasValue ? typeof value === 'string' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
180
|
+
style: [valueTextStyle, valueStyle],
|
|
181
|
+
children: value
|
|
182
|
+
}) : value : null, hasCaption ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
183
|
+
style: [captionTextStyle, captionStyle],
|
|
184
|
+
children: caption
|
|
185
|
+
}) : null]
|
|
186
|
+
}) : null, linkLabel ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Link.default, {
|
|
187
|
+
text: linkLabel,
|
|
188
|
+
autolayout: "Hug",
|
|
189
|
+
modes: linkModes,
|
|
190
|
+
...(onLinkPress ? {
|
|
191
|
+
onPress: onLinkPress
|
|
192
|
+
} : null)
|
|
193
|
+
}) : null]
|
|
194
|
+
});
|
|
195
|
+
if (onPress) {
|
|
196
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
197
|
+
style: [containerStyle, style],
|
|
198
|
+
onPress: onPress,
|
|
199
|
+
disabled: disabled,
|
|
200
|
+
accessibilityRole: "button",
|
|
201
|
+
...(resolvedLabel !== undefined ? {
|
|
202
|
+
accessibilityLabel: resolvedLabel
|
|
203
|
+
} : null),
|
|
204
|
+
accessibilityState: {
|
|
205
|
+
disabled: !!disabled
|
|
206
|
+
},
|
|
207
|
+
children: content
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
211
|
+
style: [containerStyle, style],
|
|
212
|
+
accessible: true,
|
|
213
|
+
...(resolvedLabel !== undefined ? {
|
|
214
|
+
accessibilityLabel: resolvedLabel
|
|
215
|
+
} : null),
|
|
216
|
+
children: content
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
var _default = exports.default = /*#__PURE__*/_react.default.memo(ValueBackMetric);
|
|
@@ -207,6 +207,12 @@ Object.defineProperty(exports, "CarouselCardAccounts", {
|
|
|
207
207
|
return _CarouselCardAccounts.default;
|
|
208
208
|
}
|
|
209
209
|
});
|
|
210
|
+
Object.defineProperty(exports, "CategoryCard", {
|
|
211
|
+
enumerable: true,
|
|
212
|
+
get: function () {
|
|
213
|
+
return _CategoryCard.default;
|
|
214
|
+
}
|
|
215
|
+
});
|
|
210
216
|
Object.defineProperty(exports, "CcCard", {
|
|
211
217
|
enumerable: true,
|
|
212
218
|
get: function () {
|
|
@@ -285,6 +291,12 @@ Object.defineProperty(exports, "ContentSheet", {
|
|
|
285
291
|
return _ContentSheet.default;
|
|
286
292
|
}
|
|
287
293
|
});
|
|
294
|
+
Object.defineProperty(exports, "CounterBadge", {
|
|
295
|
+
enumerable: true,
|
|
296
|
+
get: function () {
|
|
297
|
+
return _CounterBadge.default;
|
|
298
|
+
}
|
|
299
|
+
});
|
|
288
300
|
Object.defineProperty(exports, "CoverageBarComparison", {
|
|
289
301
|
enumerable: true,
|
|
290
302
|
get: function () {
|
|
@@ -369,6 +381,12 @@ Object.defineProperty(exports, "ExpandableCheckbox", {
|
|
|
369
381
|
return _ExpandableCheckbox.default;
|
|
370
382
|
}
|
|
371
383
|
});
|
|
384
|
+
Object.defineProperty(exports, "FavoriteToggle", {
|
|
385
|
+
enumerable: true,
|
|
386
|
+
get: function () {
|
|
387
|
+
return _FavoriteToggle.default;
|
|
388
|
+
}
|
|
389
|
+
});
|
|
372
390
|
Object.defineProperty(exports, "FilterBar", {
|
|
373
391
|
enumerable: true,
|
|
374
392
|
get: function () {
|
|
@@ -417,6 +435,12 @@ Object.defineProperty(exports, "HStack", {
|
|
|
417
435
|
return _HStack.default;
|
|
418
436
|
}
|
|
419
437
|
});
|
|
438
|
+
Object.defineProperty(exports, "HelloJioInput", {
|
|
439
|
+
enumerable: true,
|
|
440
|
+
get: function () {
|
|
441
|
+
return _HelloJioInput.default;
|
|
442
|
+
}
|
|
443
|
+
});
|
|
420
444
|
Object.defineProperty(exports, "HeroSection", {
|
|
421
445
|
enumerable: true,
|
|
422
446
|
get: function () {
|
|
@@ -987,6 +1011,12 @@ Object.defineProperty(exports, "VStack", {
|
|
|
987
1011
|
return _VStack.default;
|
|
988
1012
|
}
|
|
989
1013
|
});
|
|
1014
|
+
Object.defineProperty(exports, "ValueBackMetric", {
|
|
1015
|
+
enumerable: true,
|
|
1016
|
+
get: function () {
|
|
1017
|
+
return _ValueBackMetric.default;
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
990
1020
|
Object.defineProperty(exports, "addToast", {
|
|
991
1021
|
enumerable: true,
|
|
992
1022
|
get: function () {
|
|
@@ -1053,6 +1083,7 @@ var _CardFeedback = _interopRequireDefault(require("./CardFeedback/CardFeedback"
|
|
|
1053
1083
|
var _CardFinancialCondition = _interopRequireDefault(require("./CardFinancialCondition/CardFinancialCondition"));
|
|
1054
1084
|
var _CardInsight = _interopRequireDefault(require("./CardInsight/CardInsight"));
|
|
1055
1085
|
var _CcCard = _interopRequireDefault(require("./CcCard/CcCard"));
|
|
1086
|
+
var _CategoryCard = _interopRequireDefault(require("./CategoryCard/CategoryCard"));
|
|
1056
1087
|
var _Disclaimer = _interopRequireDefault(require("./Disclaimer/Disclaimer"));
|
|
1057
1088
|
var _Divider = _interopRequireDefault(require("./Divider/Divider"));
|
|
1058
1089
|
var _Drawer = _interopRequireDefault(require("./Drawer/Drawer"));
|
|
@@ -1079,6 +1110,7 @@ var _AllocationComparisonChart = _interopRequireDefault(require("./AllocationCom
|
|
|
1079
1110
|
var _MonthlyStatusGrid = _interopRequireWildcard(require("./MonthlyStatusGrid/MonthlyStatusGrid"));
|
|
1080
1111
|
var _Gauge = _interopRequireDefault(require("./Gauge/Gauge"));
|
|
1081
1112
|
var _HeroSection = _interopRequireDefault(require("./HeroSection/HeroSection"));
|
|
1113
|
+
var _HelloJioInput = _interopRequireDefault(require("./HelloJioInput/HelloJioInput"));
|
|
1082
1114
|
var _HoldingsCard = _interopRequireDefault(require("./HoldingsCard/HoldingsCard"));
|
|
1083
1115
|
var _HStack = _interopRequireDefault(require("./HStack/HStack"));
|
|
1084
1116
|
var _Icon = _interopRequireDefault(require("./Icon/Icon"));
|
|
@@ -1182,5 +1214,8 @@ var _SegmentedControl = _interopRequireDefault(require("./SegmentedControl/Segme
|
|
|
1182
1214
|
var _Toggle = _interopRequireDefault(require("./Toggle/Toggle"));
|
|
1183
1215
|
var _AutoplayControl = _interopRequireDefault(require("./AutoplayControl/AutoplayControl"));
|
|
1184
1216
|
var _NumberPagination = _interopRequireDefault(require("./NumberPagination/NumberPagination"));
|
|
1217
|
+
var _CounterBadge = _interopRequireDefault(require("./CounterBadge/CounterBadge"));
|
|
1218
|
+
var _FavoriteToggle = _interopRequireDefault(require("./FavoriteToggle/FavoriteToggle"));
|
|
1219
|
+
var _ValueBackMetric = _interopRequireDefault(require("./ValueBackMetric/ValueBackMetric"));
|
|
1185
1220
|
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); }
|
|
1186
1221
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|