jfs-components 0.1.36 → 0.1.48

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 (47) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/lib/commonjs/components/Accordion/Accordion.js +16 -3
  3. package/lib/commonjs/components/AppBar/AppBar.js +118 -93
  4. package/lib/commonjs/components/CompareTable/CompareTable.js +98 -33
  5. package/lib/commonjs/components/FormField/FormField.js +9 -1
  6. package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +4 -14
  7. package/lib/commonjs/components/HelloJioInput/HelloJioInput.js +20 -4
  8. package/lib/commonjs/components/PageHero/PageHero.js +3 -0
  9. package/lib/commonjs/components/ProductMerchandisingCard/ProductMerchandisingCard.js +50 -30
  10. package/lib/commonjs/components/ValueBackMetric/ValueBackMetric.js +117 -89
  11. package/lib/commonjs/components/index.js +7 -1
  12. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -42204
  13. package/lib/commonjs/icons/registry.js +1 -1
  14. package/lib/module/components/Accordion/Accordion.js +16 -3
  15. package/lib/module/components/AppBar/AppBar.js +116 -93
  16. package/lib/module/components/CompareTable/CompareTable.js +99 -34
  17. package/lib/module/components/FormField/FormField.js +9 -1
  18. package/lib/module/components/FullscreenModal/FullscreenModal.js +4 -14
  19. package/lib/module/components/HelloJioInput/HelloJioInput.js +20 -4
  20. package/lib/module/components/PageHero/PageHero.js +3 -0
  21. package/lib/module/components/ProductMerchandisingCard/ProductMerchandisingCard.js +50 -30
  22. package/lib/module/components/ValueBackMetric/ValueBackMetric.js +120 -93
  23. package/lib/module/components/index.js +1 -1
  24. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -42204
  25. package/lib/module/icons/registry.js +1 -1
  26. package/lib/typescript/src/components/Accordion/Accordion.d.ts +14 -1
  27. package/lib/typescript/src/components/AppBar/AppBar.d.ts +46 -14
  28. package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +29 -12
  29. package/lib/typescript/src/components/HelloJioInput/HelloJioInput.d.ts +6 -4
  30. package/lib/typescript/src/components/PageHero/PageHero.d.ts +1 -0
  31. package/lib/typescript/src/components/ProductMerchandisingCard/ProductMerchandisingCard.d.ts +8 -1
  32. package/lib/typescript/src/components/ValueBackMetric/ValueBackMetric.d.ts +27 -16
  33. package/lib/typescript/src/components/index.d.ts +1 -1
  34. package/lib/typescript/src/icons/registry.d.ts +1 -1
  35. package/package.json +1 -1
  36. package/src/components/Accordion/Accordion.tsx +61 -33
  37. package/src/components/AppBar/AppBar.tsx +154 -124
  38. package/src/components/CompareTable/CompareTable.tsx +138 -52
  39. package/src/components/FormField/FormField.tsx +7 -0
  40. package/src/components/FullscreenModal/FullscreenModal.tsx +5 -14
  41. package/src/components/HelloJioInput/HelloJioInput.tsx +31 -12
  42. package/src/components/PageHero/PageHero.tsx +5 -0
  43. package/src/components/ProductMerchandisingCard/ProductMerchandisingCard.tsx +63 -30
  44. package/src/components/ValueBackMetric/ValueBackMetric.tsx +178 -104
  45. package/src/components/index.ts +6 -1
  46. package/src/design-tokens/Coin Variables-variables-full.json +1 -42204
  47. package/src/icons/registry.ts +1 -1
@@ -57,7 +57,15 @@ const STATE_FALLBACKS = {
57
57
  iconColor: '#24262b'
58
58
  },
59
59
  IdleJioPlus: {
60
- background: '#f5f5f5',
60
+ // Glass fill. In Figma the IdleJioPlus surface resolves
61
+ // `helloJioInput/background` under the `Page type = JioPlus` variable mode,
62
+ // which yields a TRANSLUCENT light fill (not the opaque `#f5f5f5` the
63
+ // MainPage mode gives). Painted over the backdrop blur, an opaque value
64
+ // would read as a solid bar and hide the blur entirely — so, matching the
65
+ // translucent glass fallbacks the sibling GlassFill consumers use
66
+ // (FavoriteToggle `#ffffff33`, NumberPagination `rgba(141,141,141,0.4)`),
67
+ // this fallback carries alpha so the frost tints the blurred content.
68
+ background: '#f5f5f566',
61
69
  borderColor: '#f5f5f5',
62
70
  labelColor: '#545961',
63
71
  iconColor: '#545961'
@@ -94,7 +102,11 @@ function resolveHelloJioInputTokens(modes, visualState) {
94
102
  // Glass keeps the container transparent so GlassFill shows through.
95
103
  backgroundColor: useGlass ? 'transparent' : background,
96
104
  borderColor,
97
- borderWidth: strokeWidth,
105
+ // The Figma IdleJioPlus variant is a borderless frosted pill — it has no
106
+ // `border/color`/`strokeWidth` tokens. Drawing the Idle border color here
107
+ // would leave a faint light ring the design doesn't have, so drop the
108
+ // border entirely in the glass state.
109
+ borderWidth: useGlass ? 0 : strokeWidth,
98
110
  borderStyle: 'solid',
99
111
  borderRadius,
100
112
  paddingLeft,
@@ -201,11 +213,15 @@ const HelloJioInput = /*#__PURE__*/(0, _react.forwardRef)(function HelloJioInput
201
213
  setIsFocused(false);
202
214
  onBlur?.(e);
203
215
  }, [onBlur]);
204
- const leadingElement = leading ?? (leadingIconName ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
216
+
217
+ // Symmetric with `trailing`: an explicit `null` hides the leading icon
218
+ // (the design's `startSlot={false}`); `undefined` falls back to the
219
+ // default brand icon.
220
+ const leadingElement = leading === undefined ? leadingIconName ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
205
221
  name: leadingIconName,
206
222
  size: tokens.iconSize,
207
223
  color: tokens.iconColor
208
- }) : null);
224
+ }) : null : leading;
209
225
  const processedLeading = leadingElement ? (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(leadingElement), modes) : null;
210
226
  const defaultTrailing = /*#__PURE__*/(0, _jsxRuntime.jsx)(_IconButton.default, {
211
227
  iconName: sendIconName,
@@ -39,6 +39,7 @@ const MEDIA_FALLBACK = 117;
39
39
  * eyebrow="Upgrade to JioFinance+"
40
40
  * headline="Resume earning cashback, extra points, and 1% gold"
41
41
  * supportingText="₹999/year · ₹0 until 2027"
42
+ * body="₹999/year · ₹0 until 2027"
42
43
  * buttonLabel="Renew for free"
43
44
  * onButtonPress={() => navigate('Upgrade')}
44
45
  * modes={{ 'Page type': 'JioPlus' }}
@@ -71,6 +72,7 @@ function PageHero({
71
72
  ...propModes
72
73
  }), [globalModes, propModes]);
73
74
  const gap = Number((0, _figmaVariablesResolver.getVariableByName)('PageHero/gap', modes));
75
+ const paddingHorizontal = Number((0, _figmaVariablesResolver.getVariableByName)('PageHero/padding/horizontal', modes));
74
76
  const textWrapGap = Number((0, _figmaVariablesResolver.getVariableByName)('PageHero/textWrap/gap', modes));
75
77
 
76
78
  // Media slot box — Figma frame 4540:7845 ("Video"), sized by the
@@ -115,6 +117,7 @@ function PageHero({
115
117
  flexDirection: 'column',
116
118
  alignItems: 'center',
117
119
  gap,
120
+ paddingHorizontal,
118
121
  width: '100%'
119
122
  };
120
123
  const textWrapStyle = {
@@ -48,6 +48,7 @@ function ProductMerchandisingCard({
48
48
  title = 'Title',
49
49
  subtitle = 'Subtitle',
50
50
  badgeLabel,
51
+ badgeIcon,
51
52
  badge,
52
53
  showAvatar = true,
53
54
  avatarSource,
@@ -102,29 +103,33 @@ function ProductMerchandisingCard({
102
103
  }), [modes]);
103
104
  const headerBadge = badge ?? (badgeLabel != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Badge.default, {
104
105
  label: badgeLabel,
106
+ leading: badgeIcon,
105
107
  modes: badgeModes
106
108
  }) : null);
107
109
  const headerAvatar = showAvatar ? avatar ?? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Avatar.default, {
108
110
  style: avatarSource ? 'Image' : avatarMonogram ? 'Monogram' : 'Image',
109
- imageSource: avatarSource,
110
- monogram: avatarMonogram,
111
+ ...(avatarSource != null ? {
112
+ imageSource: avatarSource
113
+ } : {}),
114
+ ...(avatarMonogram != null ? {
115
+ monogram: avatarMonogram
116
+ } : {}),
111
117
  modes: avatarModes
112
118
  }) : null;
113
119
 
114
- // The footer "special badge" is now the shared <Badge type="glass">. The
115
- // hardcoded literals that defined its look (warm frosted overlay, gentle
116
- // 5px blur, 6/2 padding, 4px radius, weight-500 white label) are preserved
117
- // 1:1 via the glass overrides + `style`/`labelStyle`, and the optional
118
- // `specialBadgeIcon` maps to Badge's `leading` slot — so every existing
119
- // instantiation renders byte-for-byte the same as before.
120
+ // The footer "special badge" is the shared <Badge type="glass">. Overlay +
121
+ // blur come from `badge/glass/*` + `blur/minimal`; box/gap from
122
+ // `productMerchandisingcard/specialbadge/*`; label color/weight from the
123
+ // glass badge tokens (Figma: white / 600). Optional `specialBadgeIcon`
124
+ // maps to Badge's `leading` slot.
120
125
  const footerSpecialBadge = specialBadge ?? (specialBadgeLabel != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Badge.default, {
121
126
  type: "glass",
122
127
  label: specialBadgeLabel,
123
128
  leading: specialBadgeIcon,
124
- glassOverlayColor: SPECIAL_BADGE_BG,
125
- glassIntensity: SPECIAL_BADGE_INTENSITY,
129
+ glassOverlayColor: tokens.specialBadgeBg,
130
+ glassIntensity: tokens.specialBadgeIntensity,
126
131
  modes: modes,
127
- style: styles.specialBadge,
132
+ style: tokens.specialBadge,
128
133
  labelStyle: tokens.specialBadgeText,
129
134
  ...(disableTruncation != null ? {
130
135
  disableTruncation
@@ -142,7 +147,9 @@ function ProductMerchandisingCard({
142
147
  const footerCta = cta ?? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
143
148
  label: ctaLabel,
144
149
  modes: ctaModes,
145
- onPress: onCtaPress
150
+ ...(onCtaPress != null ? {
151
+ onPress: onCtaPress
152
+ } : {})
146
153
  });
147
154
  const content = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
148
155
  children: [imageSource != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Image.default, {
@@ -303,12 +310,6 @@ function ProductMerchandisingCard({
303
310
  // the ramp (the strongest point); the upper part fades to fully clear. Kept
304
311
  // intentionally small to match the design's subtle "minimal" blur.
305
312
  const BLUR_INTENSITY_FACTOR = 1.0;
306
-
307
- // Special badge ("frosted" pill) — values are literals in the Figma node
308
- // (no dedicated tokens): bg rgba(255,245,229,0.15), 5px backdrop blur.
309
- const SPECIAL_BADGE_BG = 'rgba(255,245,229,0.15)';
310
- const SPECIAL_BADGE_INTENSITY = 17; // ~5px CSS blur / ~2 native blurAmount
311
-
312
313
  function asNum(raw, fallback) {
313
314
  const n = typeof raw === 'number' ? raw : parseFloat(raw);
314
315
  return Number.isFinite(n) ? n : fallback;
@@ -328,6 +329,7 @@ function resolveTokens(modes) {
328
329
  const footerPadH = asNum((0, _figmaVariablesResolver.getVariableByName)('productMerchandisingcard/footer/padding/horizontal', modes), 16);
329
330
  const footerPadV = asNum((0, _figmaVariablesResolver.getVariableByName)('productMerchandisingcard/footer/padding/vertical', modes), 16);
330
331
  const textGap = asNum((0, _figmaVariablesResolver.getVariableByName)('productMerchandisingcard/footer/text/gap', modes), 2);
332
+ const textWrapMinWidth = asNum((0, _figmaVariablesResolver.getVariableByName)('productMerchandisingcard/footer/textWrap/minWidth', modes), 100);
331
333
  const blurRadius = asNum((0, _figmaVariablesResolver.getVariableByName)('blur/minimal', modes), 29);
332
334
  const titleColor = asStr((0, _figmaVariablesResolver.getVariableByName)('productMerchandisingcard/footer/title/font/color', modes), '#ffffff');
333
335
  const titleSize = asNum((0, _figmaVariablesResolver.getVariableByName)('productMerchandisingcard/footer/title/fontsize', modes), 14);
@@ -339,7 +341,20 @@ function resolveTokens(modes) {
339
341
  const subtitleWeight = asStr((0, _figmaVariablesResolver.getVariableByName)('productMerchandisingcard/footer/subtitle/fontweight', modes), '400');
340
342
  const sbSize = asNum((0, _figmaVariablesResolver.getVariableByName)('productMerchandisingcard/specialbadge/text/fontsize', modes), 12);
341
343
  const sbFamily = asStr((0, _figmaVariablesResolver.getVariableByName)('productMerchandisingcard/specialbadge/text/fontfamily', modes), 'JioType Var');
342
- const sbWeight = asStr((0, _figmaVariablesResolver.getVariableByName)('productMerchandisingcard/specialbadge/text/fontweight', modes), '500');
344
+ // No dedicated `specialbadge/text/fontweight` in the collection — Figma
345
+ // binds the glass badge label to `badge/label/fontWeight` (600).
346
+ const sbWeight = asStr((0, _figmaVariablesResolver.getVariableByName)('badge/label/fontWeight', modes), '600');
347
+ const sbPadH = asNum((0, _figmaVariablesResolver.getVariableByName)('productMerchandisingcard/specialbadge/padding/horizontal', modes), 6);
348
+ const sbGap = asNum((0, _figmaVariablesResolver.getVariableByName)('productMerchandisingcard/specialbadge/gap', modes), 4);
349
+ const sbPadV = asNum((0, _figmaVariablesResolver.getVariableByName)('badge/padding/vertical', modes), 4);
350
+ const sbRadius = asNum((0, _figmaVariablesResolver.getVariableByName)('badge/radius', modes), 4);
351
+ const sbLabelColor = asStr((0, _figmaVariablesResolver.getVariableByName)('badge/glass/color/label', modes), '#ffffff');
352
+
353
+ // The special badge is a `type="glass"` Badge: its overlay + blur come
354
+ // straight from the shared badge-glass tokens the design references
355
+ // (`badge/glass/color/background` and the `glass/minimal` → `blur/minimal`
356
+ // effect), so it frosts at the same strength as the footer.
357
+ const specialBadgeBg = asStr((0, _figmaVariablesResolver.getVariableByName)('badge/glass/color/background', modes), 'rgba(255,255,255,0.15)');
343
358
 
344
359
  // SVG `<FeGaussianBlur stdDeviation>` is resolution-independent and renders
345
360
  // an identical Gaussian on iOS, Android and web — so unlike the old
@@ -351,6 +366,10 @@ function resolveTokens(modes) {
351
366
  radius,
352
367
  blurIntensity: Math.max(0, Math.min(100, Math.round(blurRadius * BLUR_INTENSITY_FACTOR))),
353
368
  imageBlurStdDeviation,
369
+ specialBadgeBg,
370
+ // Same `blur/minimal`-derived intensity the footer glass uses, so the
371
+ // special badge frosts consistently with the surface behind it.
372
+ specialBadgeIntensity: Math.max(0, Math.min(100, Math.round(blurRadius * BLUR_INTENSITY_FACTOR))),
354
373
  header: {
355
374
  flexDirection: 'row',
356
375
  alignItems: 'flex-start',
@@ -368,6 +387,9 @@ function resolveTokens(modes) {
368
387
  },
369
388
  textWrap: {
370
389
  flex: 1,
390
+ // The Figma footer pins the text column to a minimum width so a long
391
+ // CTA can never squeeze the title/subtitle away (`footer/textWrap/minWidth`).
392
+ minWidth: textWrapMinWidth,
371
393
  gap: textGap,
372
394
  justifyContent: 'center'
373
395
  },
@@ -387,8 +409,16 @@ function resolveTokens(modes) {
387
409
  lineHeight: Math.round(subtitleSize * 1.2),
388
410
  includeFontPadding: false
389
411
  },
412
+ // Pin the special-badge box to the card collection tokens so a parent
413
+ // `Badge Size` mode cannot inflate/deflate this pill away from Figma.
414
+ specialBadge: {
415
+ paddingHorizontal: sbPadH,
416
+ paddingVertical: sbPadV,
417
+ borderRadius: sbRadius,
418
+ gap: sbGap
419
+ },
390
420
  specialBadgeText: {
391
- color: '#ffffff',
421
+ color: sbLabelColor,
392
422
  fontSize: sbSize,
393
423
  fontFamily: sbFamily,
394
424
  fontWeight: sbWeight,
@@ -414,16 +444,6 @@ const styles = _reactNative.StyleSheet.create({
414
444
  // the intended fade strength.
415
445
  opacity: 0.9
416
446
  },
417
- // Overrides layered on top of <Badge type="glass">. Badge already supplies
418
- // the row layout + 4px gap (when a leading icon is present), `overflow:
419
- // 'hidden'`, `alignSelf: 'flex-start'` and `alignItems: 'center'`; these
420
- // pin the special badge's literal Figma box (6/2 padding, 4px radius)
421
- // regardless of the badge size tokens resolved by the active modes.
422
- specialBadge: {
423
- paddingHorizontal: 6,
424
- paddingVertical: 2,
425
- borderRadius: 4
426
- },
427
447
  pressed: {
428
448
  opacity: 0.92
429
449
  }
@@ -4,62 +4,97 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _react = _interopRequireDefault(require("react"));
7
+ var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactNative = require("react-native");
9
9
  var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
10
10
  var _reactUtils = require("../../utils/react-utils");
11
11
  var _Icon = _interopRequireDefault(require("../Icon/Icon"));
12
- var _Link = _interopRequireDefault(require("../Link/Link"));
12
+ var _Text = _interopRequireDefault(require("../Text/Text"));
13
13
  var _jsxRuntime = require("react/jsx-runtime");
14
14
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
15
16
  const TITLE_LINE_HEIGHT_RATIO = 1.2;
16
17
  const VALUE_LINE_HEIGHT_RATIO = 1.2;
17
18
  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
19
  const CARD_MIN_HEIGHT = 82;
37
20
  const ICON_SIZE = 18;
21
+ const TEXT_WRAP_GAP = 4;
22
+
38
23
  // 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.
24
+ // shared Icon pulls padding from those tokens, so zero it explicitly.
41
25
  const ICON_PADDING_RESET = {
42
26
  paddingLeft: 0,
43
27
  paddingRight: 0,
44
28
  paddingTop: 0,
45
29
  paddingBottom: 0
46
30
  };
31
+ // The `valueBackMetric/*` layout + typography tokens are bound on the Figma
32
+ // node but are NOT yet in the committed variables snapshot
33
+ // (`npm run tokens -- find valueBackMetric` → no matches). Colours and the
34
+ // header-row gap do exist as `metricdata/*` / `icon/*` tokens. Until the
35
+ // export is re-synced, `getVariableByName` returns undefined for the
36
+ // valueBackMetric names and the Figma-node fallbacks below take over.
37
+ function resolveTokens(modes) {
38
+ const titleColor = (0, _figmaVariablesResolver.getVariableByName)('metricdata/title/color', modes) ?? '#000000';
39
+ const valueColor = (0, _figmaVariablesResolver.getVariableByName)('metricdata/value/color', modes) ?? '#000000';
40
+ const captionColor = (0, _figmaVariablesResolver.getVariableByName)('metricdata/caption/color', modes) ?? '#777777';
41
+ const titleFontFamily = (0, _figmaVariablesResolver.getVariableByName)('valueBackMetric/title/fontfamily', modes) ?? 'JioType Var';
42
+ const titleFontSize = (0, _figmaVariablesResolver.getVariableByName)('valueBackMetric/title/fontsize', modes) ?? 12;
43
+ const titleFontWeight = String((0, _figmaVariablesResolver.getVariableByName)('valueBackMetric/title/fontweight', modes) ?? 500);
44
+ const valueFontFamily = (0, _figmaVariablesResolver.getVariableByName)('valueBackMetric/value/fontfamily', modes) ?? 'JioType Var';
45
+ const valueFontSize = (0, _figmaVariablesResolver.getVariableByName)('valueBackMetric/value/fontsize', modes) ?? 16;
46
+ // Figma binds the value face to JioType Medium; there is no dedicated
47
+ // valueBackMetric/value/fontweight token yet, so fall back to 500.
48
+ const valueFontWeight = '500';
49
+ const captionFontFamily = (0, _figmaVariablesResolver.getVariableByName)('valueBackMetric/caption/fontfamily', modes) ?? 'JioType Var';
50
+ const captionFontSize = (0, _figmaVariablesResolver.getVariableByName)('valueBackMetric/caption/fontsize', modes) ?? 12;
51
+ const captionFontWeight = String((0, _figmaVariablesResolver.getVariableByName)('valueBackMetric/caption/fontweight', modes) ?? 400);
52
+ const gap = (0, _figmaVariablesResolver.getVariableByName)('valueBackMetric/gap', modes) ?? 4;
53
+ const valueWrapGap = (0, _figmaVariablesResolver.getVariableByName)('metricdata/valueWrap/gap', modes) ?? 4;
54
+ // Design padding is 0 — do NOT fall back to metricdata/padding (10).
55
+ const paddingHorizontal = (0, _figmaVariablesResolver.getVariableByName)('valueBackMetric/padding/horizontal', modes) ?? 0;
56
+ const paddingVertical = (0, _figmaVariablesResolver.getVariableByName)('valueBackMetric/padding/vertical', modes) ?? 0;
57
+ const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('valueBackMetric/background/color', modes) ?? '#ffffff';
58
+ const iconColor = (0, _figmaVariablesResolver.getVariableByName)('icon/color', modes) ?? '#ad8444';
59
+ const iconSize = (0, _figmaVariablesResolver.getVariableByName)('icon/size', modes) ?? ICON_SIZE;
60
+ return {
61
+ titleColor,
62
+ titleFontFamily,
63
+ titleFontSize,
64
+ titleFontWeight,
65
+ valueColor,
66
+ valueFontFamily,
67
+ valueFontSize,
68
+ valueFontWeight,
69
+ captionColor,
70
+ captionFontFamily,
71
+ captionFontSize,
72
+ captionFontWeight,
73
+ gap,
74
+ valueWrapGap,
75
+ paddingHorizontal,
76
+ paddingVertical,
77
+ backgroundColor,
78
+ iconColor,
79
+ iconSize
80
+ };
81
+ }
82
+
47
83
  /**
48
84
  * ValueBackMetric — a compact, left-aligned "value back" card.
49
85
  *
50
86
  * 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.
87
+ * `value`, a muted `caption`, and an optional bottom CTA (`linkLabel` / `link`
88
+ * slot, e.g. "Earn"). `value`, `caption` and the CTA each render only when
89
+ * provided; the header (icon + title) always shows.
54
90
  *
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.
91
+ * Colours resolve from the `metricdata/*` tokens; the header glyph from the
92
+ * `icon/*` tokens (default 18px gold); the CTA from the shared {@link Text}
93
+ * with `Text Appearance: Secondary` + `Text Sizes: Small` seeded so it renders
94
+ * purple at 12px like the design (caller `modes` win). Layout/typography
95
+ * follow the Figma `valueBackMetric/*` variables not yet in the committed
96
+ * token snapshot, so they resolve through `getVariableByName` with Figma-node
97
+ * fallbacks.
63
98
  *
64
99
  * @example
65
100
  * ```tsx
@@ -79,6 +114,7 @@ function ValueBackMetric({
79
114
  value,
80
115
  caption,
81
116
  linkLabel,
117
+ link,
82
118
  onLinkPress,
83
119
  onPress,
84
120
  disabled,
@@ -89,82 +125,81 @@ function ValueBackMetric({
89
125
  captionStyle,
90
126
  accessibilityLabel
91
127
  }) {
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(() => ({
128
+ const tokens = (0, _react.useMemo)(() => resolveTokens(modes), [modes]);
129
+
130
+ // Figma Code-Connects the CTA to Text (not Link): purple, no underline.
131
+ // Seed Secondary + Small so it matches the design; caller modes win.
132
+ const linkModes = (0, _react.useMemo)(() => ({
100
133
  'Text Appearance': 'Secondary',
101
134
  'Text Sizes': 'Small',
102
135
  ...modes
103
136
  }), [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
137
  const containerStyle = {
116
138
  flexDirection: 'column',
117
139
  alignItems: 'flex-start',
118
140
  justifyContent: 'center',
119
- gap,
120
- paddingHorizontal,
121
- paddingVertical,
141
+ gap: tokens.gap,
142
+ paddingHorizontal: tokens.paddingHorizontal,
143
+ paddingVertical: tokens.paddingVertical,
122
144
  minHeight: CARD_MIN_HEIGHT,
123
- backgroundColor: CARD_BACKGROUND
145
+ backgroundColor: tokens.backgroundColor
124
146
  };
125
147
  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
148
+ color: tokens.titleColor,
149
+ fontFamily: tokens.titleFontFamily,
150
+ fontSize: tokens.titleFontSize,
151
+ fontWeight: tokens.titleFontWeight,
152
+ lineHeight: tokens.titleFontSize * TITLE_LINE_HEIGHT_RATIO
131
153
  };
132
154
  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
155
+ color: tokens.valueColor,
156
+ fontFamily: tokens.valueFontFamily,
157
+ fontSize: tokens.valueFontSize,
158
+ fontWeight: tokens.valueFontWeight,
159
+ lineHeight: tokens.valueFontSize * VALUE_LINE_HEIGHT_RATIO
138
160
  };
139
161
  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
162
+ color: tokens.captionColor,
163
+ fontFamily: tokens.captionFontFamily,
164
+ fontSize: tokens.captionFontSize,
165
+ fontWeight: tokens.captionFontWeight,
166
+ lineHeight: tokens.captionFontSize * CAPTION_LINE_HEIGHT_RATIO
145
167
  };
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
168
  const iconNode = icon == null ? null : typeof icon === 'string' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
153
169
  iconName: icon,
154
- size: iconSize,
155
- color: iconColor,
170
+ size: tokens.iconSize,
171
+ color: tokens.iconColor,
156
172
  modes: modes,
157
173
  style: ICON_PADDING_RESET
158
174
  }) : (0, _reactUtils.cloneChildrenWithModes)(icon, modes);
159
175
  const hasValue = value !== undefined && value !== null && value !== '';
160
176
  const hasCaption = caption !== undefined && caption !== '';
177
+ const hasLinkSlot = link !== undefined && link !== null && link !== false;
178
+ const hasLinkLabel = linkLabel !== undefined && linkLabel !== '';
161
179
  const resolvedLabel = accessibilityLabel ?? ([title, typeof value === 'string' ? value : undefined, caption, linkLabel].filter(Boolean).join(', ') || undefined);
180
+
181
+ // Seed Secondary + Small into the CTA slot too, so a bare `<Text text="Earn" />`
182
+ // matches the design without the consumer re-seeding. Child-level modes still win.
183
+ const ctaNode = hasLinkSlot ? (0, _reactUtils.cloneChildrenWithModes)(link, linkModes) : hasLinkLabel ? onLinkPress ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
184
+ onPress: onLinkPress,
185
+ accessibilityRole: "link",
186
+ accessibilityLabel: linkLabel,
187
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
188
+ text: linkLabel,
189
+ textAlign: "Left",
190
+ modes: linkModes
191
+ })
192
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
193
+ text: linkLabel,
194
+ textAlign: "Left",
195
+ modes: linkModes
196
+ }) : null;
162
197
  const content = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
163
198
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
164
199
  style: {
165
200
  flexDirection: 'row',
166
201
  alignItems: 'center',
167
- gap: valueWrapGap
202
+ gap: tokens.valueWrapGap
168
203
  },
169
204
  children: [iconNode, title !== undefined && title !== '' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
170
205
  style: [titleTextStyle, titleStyle],
@@ -174,7 +209,7 @@ function ValueBackMetric({
174
209
  style: {
175
210
  flexDirection: 'column',
176
211
  alignItems: 'flex-start',
177
- gap: 4
212
+ gap: TEXT_WRAP_GAP
178
213
  },
179
214
  children: [hasValue ? typeof value === 'string' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
180
215
  style: [valueTextStyle, valueStyle],
@@ -183,14 +218,7 @@ function ValueBackMetric({
183
218
  style: [captionTextStyle, captionStyle],
184
219
  children: caption
185
220
  }) : 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]
221
+ }) : null, ctaNode]
194
222
  });
195
223
  if (onPress) {
196
224
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
@@ -489,6 +489,12 @@ Object.defineProperty(exports, "InstitutionBadge", {
489
489
  return _InstitutionBadge.default;
490
490
  }
491
491
  });
492
+ Object.defineProperty(exports, "JioDot", {
493
+ enumerable: true,
494
+ get: function () {
495
+ return _AppBar.JioDot;
496
+ }
497
+ });
492
498
  Object.defineProperty(exports, "LazyList", {
493
499
  enumerable: true,
494
500
  get: function () {
@@ -1062,7 +1068,7 @@ Object.defineProperty(exports, "useToast", {
1062
1068
  var _AccountCard = _interopRequireDefault(require("./AccountCard/AccountCard"));
1063
1069
  var _ActionFooter = _interopRequireDefault(require("./ActionFooter/ActionFooter"));
1064
1070
  var _Attached = _interopRequireDefault(require("./Attached/Attached"));
1065
- var _AppBar = _interopRequireDefault(require("./AppBar/AppBar"));
1071
+ var _AppBar = _interopRequireWildcard(require("./AppBar/AppBar"));
1066
1072
  var _Avatar = _interopRequireDefault(require("./Avatar/Avatar"));
1067
1073
  var _AvatarGroup = _interopRequireDefault(require("./AvatarGroup/AvatarGroup"));
1068
1074
  var _Badge = _interopRequireDefault(require("./Badge/Badge"));