jfs-components 0.1.50 → 0.1.54

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 (93) hide show
  1. package/.cursor/D2C-FLOW.md +94 -0
  2. package/.cursor/D2C-SCREEN.md +119 -0
  3. package/.cursor/commands/D2C.md +101 -0
  4. package/CHANGELOG.md +18 -0
  5. package/lib/commonjs/components/ActionFooter/ActionFooter.js +319 -135
  6. package/lib/commonjs/components/Badge/Badge.js +20 -1
  7. package/lib/commonjs/components/Button/Button.js +139 -47
  8. package/lib/commonjs/components/ButtonGroup/ButtonGroup.js +1 -1
  9. package/lib/commonjs/components/Card/Card.js +151 -112
  10. package/lib/commonjs/components/Card/assets/gold-logo-mark.png +0 -0
  11. package/lib/commonjs/components/CategoryCard/CategoryCard.js +97 -63
  12. package/lib/commonjs/components/CategoryCard/assets/sample.png +0 -0
  13. package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +59 -10
  14. package/lib/commonjs/components/ExpandableCheckbox/ExpandableCheckbox.js +132 -49
  15. package/lib/commonjs/components/HelloJioInput/HelloJioInput.js +56 -14
  16. package/lib/commonjs/components/IconCapsule/IconCapsule.js +1 -1
  17. package/lib/commonjs/components/MetricData/MetricData.js +26 -13
  18. package/lib/commonjs/components/MoneyValue/MoneyValue.js +1 -1
  19. package/lib/commonjs/components/Nudge/Nudge.js +1 -1
  20. package/lib/commonjs/components/PdpCcCard/PdpCcCard.js +47 -91
  21. package/lib/commonjs/components/PlanComparisonCard/PlanComparisonCard.js +52 -45
  22. package/lib/commonjs/components/PortfolioHero/PortfolioHero.js +1 -1
  23. package/lib/commonjs/components/Slider/Slider.js +3 -3
  24. package/lib/commonjs/components/TextInput/TextInput.js +26 -8
  25. package/lib/commonjs/components/Tooltip/Tooltip.js +3 -5
  26. package/lib/commonjs/components/index.js +7 -1
  27. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  28. package/lib/commonjs/design-tokens/figma-modes.generated.js +13 -13
  29. package/lib/commonjs/icons/registry.js +1 -1
  30. package/lib/module/components/ActionFooter/ActionFooter.js +322 -138
  31. package/lib/module/components/Badge/Badge.js +20 -1
  32. package/lib/module/components/Button/Button.js +140 -48
  33. package/lib/module/components/ButtonGroup/ButtonGroup.js +1 -1
  34. package/lib/module/components/Card/Card.js +152 -114
  35. package/lib/module/components/Card/assets/gold-logo-mark.png +0 -0
  36. package/lib/module/components/CategoryCard/CategoryCard.js +97 -63
  37. package/lib/module/components/CategoryCard/assets/sample.png +0 -0
  38. package/lib/module/components/CheckboxItem/CheckboxItem.js +59 -10
  39. package/lib/module/components/ExpandableCheckbox/ExpandableCheckbox.js +135 -52
  40. package/lib/module/components/HelloJioInput/HelloJioInput.js +56 -14
  41. package/lib/module/components/IconCapsule/IconCapsule.js +1 -1
  42. package/lib/module/components/MetricData/MetricData.js +26 -14
  43. package/lib/module/components/MoneyValue/MoneyValue.js +1 -1
  44. package/lib/module/components/Nudge/Nudge.js +1 -1
  45. package/lib/module/components/PdpCcCard/PdpCcCard.js +48 -92
  46. package/lib/module/components/PlanComparisonCard/PlanComparisonCard.js +52 -45
  47. package/lib/module/components/PortfolioHero/PortfolioHero.js +1 -1
  48. package/lib/module/components/Slider/Slider.js +3 -3
  49. package/lib/module/components/TextInput/TextInput.js +27 -9
  50. package/lib/module/components/Tooltip/Tooltip.js +3 -5
  51. package/lib/module/components/index.js +1 -1
  52. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  53. package/lib/module/design-tokens/figma-modes.generated.js +13 -13
  54. package/lib/module/icons/registry.js +1 -1
  55. package/lib/typescript/src/components/ActionFooter/ActionFooter.d.ts +50 -21
  56. package/lib/typescript/src/components/Button/Button.d.ts +24 -1
  57. package/lib/typescript/src/components/ButtonGroup/ButtonGroup.d.ts +2 -2
  58. package/lib/typescript/src/components/Card/Card.d.ts +54 -25
  59. package/lib/typescript/src/components/CategoryCard/CategoryCard.d.ts +29 -12
  60. package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +27 -9
  61. package/lib/typescript/src/components/ExpandableCheckbox/ExpandableCheckbox.d.ts +37 -12
  62. package/lib/typescript/src/components/IconCapsule/IconCapsule.d.ts +1 -1
  63. package/lib/typescript/src/components/MetricData/MetricData.d.ts +7 -5
  64. package/lib/typescript/src/components/PdpCcCard/PdpCcCard.d.ts +16 -6
  65. package/lib/typescript/src/components/PlanComparisonCard/PlanComparisonCard.d.ts +9 -2
  66. package/lib/typescript/src/components/index.d.ts +2 -2
  67. package/lib/typescript/src/icons/registry.d.ts +1 -1
  68. package/package.json +4 -2
  69. package/src/components/ActionFooter/ActionFooter.tsx +375 -141
  70. package/src/components/Badge/Badge.tsx +21 -1
  71. package/src/components/Button/Button.tsx +192 -44
  72. package/src/components/ButtonGroup/ButtonGroup.tsx +2 -2
  73. package/src/components/Card/Card.tsx +370 -212
  74. package/src/components/Card/assets/gold-logo-mark.png +0 -0
  75. package/src/components/CategoryCard/CategoryCard.tsx +130 -84
  76. package/src/components/CategoryCard/assets/sample.png +0 -0
  77. package/src/components/CheckboxItem/CheckboxItem.tsx +72 -12
  78. package/src/components/ExpandableCheckbox/ExpandableCheckbox.tsx +186 -67
  79. package/src/components/HelloJioInput/HelloJioInput.tsx +58 -17
  80. package/src/components/IconCapsule/IconCapsule.tsx +1 -1
  81. package/src/components/MetricData/MetricData.tsx +39 -22
  82. package/src/components/MoneyValue/MoneyValue.tsx +1 -1
  83. package/src/components/Nudge/Nudge.tsx +1 -1
  84. package/src/components/PdpCcCard/PdpCcCard.tsx +41 -92
  85. package/src/components/PlanComparisonCard/PlanComparisonCard.tsx +73 -58
  86. package/src/components/PortfolioHero/PortfolioHero.tsx +1 -1
  87. package/src/components/Slider/Slider.tsx +3 -3
  88. package/src/components/TextInput/TextInput.tsx +26 -9
  89. package/src/components/Tooltip/Tooltip.tsx +3 -5
  90. package/src/components/index.ts +9 -2
  91. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  92. package/src/design-tokens/figma-modes.generated.ts +13 -13
  93. package/src/icons/registry.ts +1 -1
@@ -16,13 +16,21 @@ var _jsxRuntime = require("react/jsx-runtime");
16
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
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
18
  const IS_WEB = _reactNative.Platform.OS === 'web';
19
- const STATE_COLLECTION = 'HelloJioInput State';
19
+ /** Figma collection name — must match `figma-modes.generated.ts` exactly. */
20
+ const STATE_COLLECTION = 'Hello Jio Input State';
20
21
 
21
22
  /** Default IconButton modes matching the Figma send control. */
22
23
  const DEFAULT_SEND_BUTTON_MODES = Object.freeze({
23
24
  AppearanceBrand: 'Primary',
24
25
  'Button / Size': 'S'
25
26
  });
27
+
28
+ /**
29
+ * Figma `Hello Jio Input State` only exposes Idle | Active. The component-set
30
+ * variant IdleJioPlus is glass chrome on top of Idle tokens — never a mode
31
+ * value for this collection.
32
+ */
33
+
26
34
  const toNumber = (value, fallback) => {
27
35
  if (typeof value === 'number') {
28
36
  return Number.isFinite(value) ? value : fallback;
@@ -38,6 +46,29 @@ const toFontWeight = (value, fallback) => {
38
46
  if (typeof value === 'string') return value;
39
47
  return fallback;
40
48
  };
49
+
50
+ /** True when a CSS/RN color string already carries a non-opaque alpha channel. */
51
+ const hasAlphaChannel = color => {
52
+ const trimmed = color.trim();
53
+ if (/^#[0-9a-fA-F]{8}$/.test(trimmed)) {
54
+ const alpha = parseInt(trimmed.slice(7, 9), 16);
55
+ return alpha < 255;
56
+ }
57
+ if (/^#[0-9a-fA-F]{4}$/.test(trimmed)) {
58
+ const alphaNibble = parseInt(trimmed.slice(3, 4), 16);
59
+ return alphaNibble < 15;
60
+ }
61
+ const func = trimmed.match(/^(rgba|hsla)\(/i);
62
+ if (func) {
63
+ const parts = trimmed.slice(trimmed.indexOf('(') + 1, -1).split(',');
64
+ if (parts.length === 4) {
65
+ const alpha = Number(parts[3].trim());
66
+ return Number.isFinite(alpha) && alpha < 1;
67
+ }
68
+ }
69
+ return false;
70
+ };
71
+
41
72
  /**
42
73
  * Fallback values from the Figma `HelloJioInput` component when local token
43
74
  * JSON has not been synced yet. Mode-driven `getVariableByName` wins when
@@ -57,14 +88,11 @@ const STATE_FALLBACKS = {
57
88
  iconColor: '#24262b'
58
89
  },
59
90
  IdleJioPlus: {
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.
91
+ // Frosted IdleJioPlus fill. Figma paints `helloJioInput/background` over
92
+ // `glass/minimal` blur; the synced token is often opaque `#f5f5f5`, which
93
+ // would hide the blur. Match sibling GlassFill consumers (FavoriteToggle
94
+ // `#ffffff33`, NumberPagination `rgba(141,141,141,0.4)`) with alpha so the
95
+ // frost tints the blurred content.
68
96
  background: '#f5f5f566',
69
97
  borderColor: '#f5f5f5',
70
98
  labelColor: '#545961',
@@ -94,6 +122,11 @@ function resolveHelloJioInputTokens(modes, visualState) {
94
122
  const blurMinimal = toNumber((0, _figmaVariablesResolver.getVariableByName)('blur/minimal', modes), 29);
95
123
  const blurIntensity = Math.max(0, Math.min(100, Math.round(blurMinimal)));
96
124
  const useGlass = visualState === 'IdleJioPlus';
125
+
126
+ // Figma IdleJioPlus paints `helloJioInput/background` over `glass/minimal`
127
+ // blur. The exported token is often opaque `#f5f5f5` (alpha lost in sync),
128
+ // which would fully hide the blur — keep a translucent frost when needed.
129
+ const glassOverlay = useGlass ? hasAlphaChannel(background) ? background : STATE_FALLBACKS.IdleJioPlus.background : background;
97
130
  return {
98
131
  containerStyle: {
99
132
  position: 'relative',
@@ -139,7 +172,8 @@ function resolveHelloJioInputTokens(modes, visualState) {
139
172
  iconColor,
140
173
  iconSize,
141
174
  blurIntensity,
142
- useGlass
175
+ useGlass,
176
+ glassOverlay
143
177
  };
144
178
  }
145
179
 
@@ -186,12 +220,21 @@ const HelloJioInput = /*#__PURE__*/(0, _react.forwardRef)(function HelloJioInput
186
220
  const [uncontrolledValue, setUncontrolledValue] = (0, _react.useState)(defaultValue);
187
221
  const isControlled = value !== undefined;
188
222
  const currentValue = isControlled ? value : uncontrolledValue;
223
+
224
+ // Component-set visual vs token mode:
225
+ // - Idle / Active map 1:1 onto `Hello Jio Input State`
226
+ // - IdleJioPlus is Figma chrome (glass) on Idle tokens — never pass
227
+ // "IdleJioPlus" into the state collection (that mode does not exist)
189
228
  const visualState = isFocused ? 'Active' : jioPlus ? 'IdleJioPlus' : 'Idle';
229
+ const tokenState = isFocused ? 'Active' : 'Idle';
190
230
  const modes = (0, _react.useMemo)(() => ({
191
231
  ...globalModes,
192
232
  ...propModes,
193
- [STATE_COLLECTION]: visualState
194
- }), [globalModes, propModes, visualState]);
233
+ [STATE_COLLECTION]: tokenState
234
+ }), [globalModes, propModes, tokenState]);
235
+
236
+ // Defaults first; caller/`modes` may still override Size/Appearance.
237
+ // Custom chrome that must ignore parent modes belongs in `trailing`.
195
238
  const sendButtonModes = (0, _react.useMemo)(() => ({
196
239
  ...DEFAULT_SEND_BUTTON_MODES,
197
240
  ...modes
@@ -239,12 +282,11 @@ const HelloJioInput = /*#__PURE__*/(0, _react.forwardRef)(function HelloJioInput
239
282
  const containerStyleArray = [tokens.containerStyle, hoverStyle, disabled ? {
240
283
  opacity: 0.5
241
284
  } : null, style];
242
- const glassOverlay = (0, _figmaVariablesResolver.getVariableByName)('helloJioInput/background', modes) ?? STATE_FALLBACKS.IdleJioPlus.background;
243
285
  const inner = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
244
286
  children: [tokens.useGlass ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_GlassFill.default, {
245
287
  tint: "light",
246
288
  intensity: tokens.blurIntensity,
247
- overlayColor: glassOverlay,
289
+ overlayColor: tokens.glassOverlay,
248
290
  androidTintWash: false
249
291
  }) : null, processedLeading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
250
292
  accessibilityElementsHidden: true,
@@ -50,7 +50,7 @@ function resolveIconCapsuleTokens(modes) {
50
50
  * @param {Object} props - Component props
51
51
  * @param {string} [props.iconName="ic_card"] - The name of the icon to display from the icon registry
52
52
  * @param {UnifiedSource} [props.source] - Fallback source (remote URI, inline SVG XML, `require()` asset, SVG React component, or React element). Used when `iconName` is missing or unknown. Tinted with the mode-resolved icon color so it follows design tokens just like a built-in icon.
53
- * @param {Object} [props.modes={}] - Mode configuration for design tokens (e.g., {"Appearance": "Primary"})
53
+ * @param {Object} [props.modes={}] - Mode configuration for design tokens (e.g., {"AppearanceBrand": "Primary"})
54
54
  * @param {string} [props.accessibilityLabel] - Accessibility label for screen readers
55
55
  * @param {string} [props.accessibilityRole] - Accessibility role (defaults to "image" for decorative icons)
56
56
  *
@@ -4,15 +4,15 @@ 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
12
  var _jsxRuntime = require("react/jsx-runtime");
13
13
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
+ 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); }
14
15
  const LINE_HEIGHT_RATIO = 1.2;
15
- const ICON_SIZE = 18;
16
16
  // Figma renders the value-row icon with no surrounding padding; the shared Icon
17
17
  // component pulls padding from `icon/padding/*` tokens, so we zero it here.
18
18
  const ICON_PADDING_RESET = {
@@ -25,9 +25,10 @@ const ICON_PADDING_RESET = {
25
25
  * MetricData — a compact, centered metric block.
26
26
  *
27
27
  * Stacks a small `title`, a prominent `value` (with an optional leading
28
- * `icon`), and a muted `caption`. Typography, colours, gaps and padding all
29
- * resolve from the `metricdata/*` design tokens. `title`, `caption` and `icon`
30
- * are optional; only `value` is shown by default.
28
+ * `icon` in a `value wrap` row), and a muted `caption`. Typography, colours,
29
+ * gaps and padding resolve from the `metricdata/*` design tokens; the leading
30
+ * glyph uses `icon/*`. `title`, `caption` and `icon` are optional — omit
31
+ * `icon` to keep the legacy text-only layout.
31
32
  *
32
33
  * @example
33
34
  * ```tsx
@@ -47,10 +48,16 @@ function MetricData({
47
48
  captionStyle,
48
49
  accessibilityLabel
49
50
  }) {
51
+ // Figma's Metricdata instance seeds Secondary on the nested Icon so the
52
+ // default glyph is brand purple (`#5d00b5`). Caller `modes` still win.
53
+ const iconModes = (0, _react.useMemo)(() => ({
54
+ AppearanceBrand: 'Secondary',
55
+ ...modes
56
+ }), [modes]);
50
57
  const titleColor = (0, _figmaVariablesResolver.getVariableByName)('metricdata/title/color', modes) ?? '#000000';
51
58
  const titleFontFamily = (0, _figmaVariablesResolver.getVariableByName)('metricdata/title/fontfamily', modes) ?? 'JioType Var';
52
59
  const titleFontSize = (0, _figmaVariablesResolver.getVariableByName)('metricdata/title/fontsize', modes) ?? 12;
53
- const titleFontWeight = String((0, _figmaVariablesResolver.getVariableByName)('metricdata/title/fontweight', modes) ?? 400);
60
+ const titleFontWeight = String((0, _figmaVariablesResolver.getVariableByName)('metricdata/title/fontweight', modes) ?? 500);
54
61
  const valueColor = (0, _figmaVariablesResolver.getVariableByName)('metricdata/value/color', modes) ?? '#000000';
55
62
  const valueFontFamily = (0, _figmaVariablesResolver.getVariableByName)('metricdata/value/fontfamily', modes) ?? 'JioType Var';
56
63
  const valueFontSize = (0, _figmaVariablesResolver.getVariableByName)('metricdata/value/fontsize', modes) ?? 20;
@@ -58,11 +65,16 @@ function MetricData({
58
65
  const captionColor = (0, _figmaVariablesResolver.getVariableByName)('metricdata/caption/color', modes) ?? '#777777';
59
66
  const captionFontFamily = (0, _figmaVariablesResolver.getVariableByName)('metricdata/caption/fontfamily', modes) ?? 'JioType Var';
60
67
  const captionFontSize = (0, _figmaVariablesResolver.getVariableByName)('metricdata/caption/fontsize', modes) ?? 12;
61
- const captionFontWeight = String((0, _figmaVariablesResolver.getVariableByName)('metricdata/caption/fontweight', modes) ?? 500);
68
+ const captionFontWeight = String((0, _figmaVariablesResolver.getVariableByName)('metricdata/caption/fontweight', modes) ?? 400);
62
69
  const gap = (0, _figmaVariablesResolver.getVariableByName)('metricdata/gap', modes) ?? 4;
63
70
  const valueWrapGap = (0, _figmaVariablesResolver.getVariableByName)('metricdata/valueWrap/gap', modes) ?? 4;
64
71
  const paddingHorizontal = (0, _figmaVariablesResolver.getVariableByName)('metricdata/padding/horizontal', modes) ?? 10;
65
72
  const paddingVertical = (0, _figmaVariablesResolver.getVariableByName)('metricdata/padding/vertical', modes) ?? 10;
73
+
74
+ // Resolve via getVariableByName so extract-tokens picks up the new bindings;
75
+ // Icon reads the same tokens when color/size props are omitted.
76
+ const iconColor = (0, _figmaVariablesResolver.getVariableByName)('icon/color', iconModes) ?? '#5d00b5';
77
+ const iconSize = (0, _figmaVariablesResolver.getVariableByName)('icon/size', iconModes) ?? 18;
66
78
  const containerStyle = {
67
79
  flexDirection: 'column',
68
80
  alignItems: 'center',
@@ -97,11 +109,12 @@ function MetricData({
97
109
  };
98
110
  const iconNode = icon == null ? null : typeof icon === 'string' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
99
111
  iconName: icon,
100
- size: ICON_SIZE,
101
- color: valueColor,
102
- modes: modes,
112
+ size: iconSize,
113
+ color: iconColor,
114
+ modes: iconModes,
103
115
  style: ICON_PADDING_RESET
104
- }) : icon;
116
+ }) : (0, _reactUtils.cloneChildrenWithModes)(icon, iconModes);
117
+ const showValueWrap = iconNode != null || value !== undefined;
105
118
  const resolvedLabel = accessibilityLabel ?? ([title, typeof value === 'string' ? value : undefined, caption].filter(Boolean).join(', ') || undefined);
106
119
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
107
120
  style: [containerStyle, style],
@@ -112,7 +125,7 @@ function MetricData({
112
125
  children: [title !== undefined && title !== '' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
113
126
  style: [titleTextStyle, titleStyle],
114
127
  children: title
115
- }) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
128
+ }) : null, showValueWrap ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
116
129
  style: {
117
130
  flexDirection: 'row',
118
131
  alignItems: 'center',
@@ -123,7 +136,7 @@ function MetricData({
123
136
  style: [valueTextStyle, valueStyle],
124
137
  children: value
125
138
  }) : value]
126
- }), caption !== undefined && caption !== '' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
139
+ }) : null, caption !== undefined && caption !== '' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
127
140
  style: [captionTextStyle, captionStyle],
128
141
  children: caption
129
142
  }) : null]
@@ -181,7 +181,7 @@ function MoneyValue({
181
181
  }, [focused, cursorOpacity]);
182
182
 
183
183
  // Keyboard avoidance — lift the component when the keyboard would cover it.
184
- // Follows the same pattern as ActionFooter (uses Keyboard.addListener).
184
+ // Uses Keyboard.addListener + measureInWindow overlap math (field-local).
185
185
  // Resets when editing ends or the keyboard hides.
186
186
  const rootRef = (0, _react.useRef)(null);
187
187
  const keyboardLift = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
@@ -21,7 +21,7 @@ function toFontWeight(value, fallback) {
21
21
  }
22
22
  function resolveNudgeTokens(modes) {
23
23
  const background = (0, _figmaVariablesResolver.getVariableByName)('nudge/background', modes);
24
- const radius = (0, _figmaVariablesResolver.getVariableByName)('nudge/radius', modes);
24
+ const radius = (0, _figmaVariablesResolver.getVariableByName)('nudge/borderRadius', modes);
25
25
  const paddingH = (0, _figmaVariablesResolver.getVariableByName)('nudge/padding/horizontal', modes);
26
26
  const paddingV = (0, _figmaVariablesResolver.getVariableByName)('nudge/padding/vertical', modes);
27
27
  const gap = (0, _figmaVariablesResolver.getVariableByName)('nudge/gap', modes);
@@ -13,12 +13,13 @@ var _Divider = _interopRequireDefault(require("../Divider/Divider"));
13
13
  var _Button = _interopRequireDefault(require("../Button/Button"));
14
14
  var _Image = _interopRequireDefault(require("../Image/Image"));
15
15
  var _Icon = _interopRequireDefault(require("../Icon/Icon"));
16
+ var _MetricData = _interopRequireDefault(require("../MetricData/MetricData"));
16
17
  var _jsxRuntime = require("react/jsx-runtime");
17
18
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
18
19
  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); }
19
20
  /**
20
21
  * A single metric column inside the card's stats row: a small title, a bold
21
- * value and an optional muted caption.
22
+ * value (optionally preceded by an icon) and an optional muted caption.
22
23
  */
23
24
 
24
25
  /**
@@ -31,8 +32,9 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
31
32
  * `imageSource` for the default image or `media` for a full slot override.
32
33
  * - **Title** — a centered headline + subtitle rendered through the shared
33
34
  * {@link Title} component (`title/*`, `pageSubtitle/*` tokens).
34
- * - **Metrics** — a row of {@link PdpCcCardMetric} columns (title / value /
35
- * caption) separated by vertical `Divider`s (`metricdata/*` tokens).
35
+ * - **Metrics** — a row of {@link PdpCcCardMetric} columns composed via
36
+ * {@link MetricData} (title / optional icon + value / caption) separated by
37
+ * vertical `Divider`s (`metricdata/*`, `icon/*` tokens).
36
38
  * - **CTA** — a small tonal {@link Button} (`Button / Size: S`,
37
39
  * `AppearanceBrand: Secondary`, `Emphasis: Medium`) with a leading icon.
38
40
  *
@@ -84,12 +86,16 @@ function PdpCcCard({
84
86
  const ctaNode = button ? (0, _reactUtils.cloneChildrenWithModes)(button, ctaModes) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
85
87
  label: buttonLabel,
86
88
  modes: ctaModes,
87
- onPress: onButtonPress,
88
- leading: buttonIcon ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
89
- iconName: buttonIcon,
90
- size: tokens.buttonIconSize,
91
- color: buttonForeground
92
- }) : undefined
89
+ ...(onButtonPress !== undefined ? {
90
+ onPress: onButtonPress
91
+ } : null),
92
+ ...(buttonIcon ? {
93
+ leading: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
94
+ iconName: buttonIcon,
95
+ size: tokens.buttonIconSize,
96
+ color: buttonForeground
97
+ })
98
+ } : null)
93
99
  });
94
100
  const content = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
95
101
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
@@ -100,9 +106,15 @@ function PdpCcCard({
100
106
  subtitle: subtitle,
101
107
  textAlign: "Center",
102
108
  modes: modes,
103
- numberOfLines: numberOfLines,
104
- disableTruncation: disableTruncation,
105
- singleLine: singleLine
109
+ ...(numberOfLines !== undefined ? {
110
+ numberOfLines
111
+ } : null),
112
+ ...(disableTruncation !== undefined ? {
113
+ disableTruncation
114
+ } : null),
115
+ ...(singleLine !== undefined ? {
116
+ singleLine
117
+ } : null)
106
118
  }), metrics.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
107
119
  style: styles.metricsRow,
108
120
  children: metrics.map((metric, index) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.default.Fragment, {
@@ -110,9 +122,21 @@ function PdpCcCard({
110
122
  direction: "vertical",
111
123
  modes: modes,
112
124
  style: styles.metricDivider
113
- }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(Metricdata, {
114
- metric: metric,
115
- tokens: tokens
125
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_MetricData.default, {
126
+ ...(metric.title !== undefined ? {
127
+ title: metric.title
128
+ } : null),
129
+ ...(metric.value !== undefined ? {
130
+ value: metric.value
131
+ } : null),
132
+ ...(metric.caption !== undefined ? {
133
+ caption: metric.caption
134
+ } : null),
135
+ ...(metric.icon !== undefined ? {
136
+ icon: metric.icon
137
+ } : null),
138
+ modes: modes,
139
+ style: styles.metricCell
116
140
  })]
117
141
  }, index))
118
142
  }) : null, showButton ? ctaNode : null]
@@ -139,29 +163,6 @@ function PdpCcCard({
139
163
  });
140
164
  }
141
165
 
142
- // ---------------------------------------------------------------------------
143
- // Metricdata — internal metric column (Figma node 5352:256)
144
- // ---------------------------------------------------------------------------
145
-
146
- function Metricdata({
147
- metric,
148
- tokens
149
- }) {
150
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
151
- style: tokens.metric,
152
- children: [metric.title != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
153
- style: tokens.metricTitle,
154
- children: metric.title
155
- }) : null, metric.value != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
156
- style: tokens.metricValue,
157
- children: metric.value
158
- }) : null, metric.caption != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
159
- style: tokens.metricCaption,
160
- children: metric.caption
161
- }) : null]
162
- });
163
- }
164
-
165
166
  // ---------------------------------------------------------------------------
166
167
  // Tokens / static styles
167
168
  // ---------------------------------------------------------------------------
@@ -184,21 +185,6 @@ function resolveTokens(modes) {
184
185
  const gap = asNum((0, _figmaVariablesResolver.getVariableByName)('PDPcccard/gap', modes), 12);
185
186
  const imageRadius = asNum((0, _figmaVariablesResolver.getVariableByName)('image/radius', modes), 8);
186
187
  const buttonIconSize = asNum((0, _figmaVariablesResolver.getVariableByName)('button/iconSize', modes), 16);
187
- const metricGap = asNum((0, _figmaVariablesResolver.getVariableByName)('metricdata/gap', modes), 4);
188
- const metricPadH = asNum((0, _figmaVariablesResolver.getVariableByName)('metricdata/padding/horizontal', modes), 10);
189
- const metricPadV = asNum((0, _figmaVariablesResolver.getVariableByName)('metricdata/padding/vertical', modes), 10);
190
- const titleColor = asStr((0, _figmaVariablesResolver.getVariableByName)('metricdata/title/color', modes), '#000000');
191
- const titleSize = asNum((0, _figmaVariablesResolver.getVariableByName)('metricdata/title/fontsize', modes), 12);
192
- const titleFamily = asStr((0, _figmaVariablesResolver.getVariableByName)('metricdata/title/fontfamily', modes), 'JioType Var');
193
- const titleWeight = asStr((0, _figmaVariablesResolver.getVariableByName)('metricdata/title/fontweight', modes), '400');
194
- const valueColor = asStr((0, _figmaVariablesResolver.getVariableByName)('metricdata/value/color', modes), '#000000');
195
- const valueSize = asNum((0, _figmaVariablesResolver.getVariableByName)('metricdata/value/fontsize', modes), 20);
196
- const valueFamily = asStr((0, _figmaVariablesResolver.getVariableByName)('metricdata/value/fontfamily', modes), 'JioType Var');
197
- const valueWeight = asStr((0, _figmaVariablesResolver.getVariableByName)('metricdata/value/fontweight', modes), '700');
198
- const captionColor = asStr((0, _figmaVariablesResolver.getVariableByName)('metricdata/caption/color', modes), '#777777');
199
- const captionSize = asNum((0, _figmaVariablesResolver.getVariableByName)('metricdata/caption/fontsize', modes), 12);
200
- const captionFamily = asStr((0, _figmaVariablesResolver.getVariableByName)('metricdata/caption/fontfamily', modes), 'JioType Var');
201
- const captionWeight = asStr((0, _figmaVariablesResolver.getVariableByName)('metricdata/caption/fontweight', modes), '500');
202
188
  return {
203
189
  container: {
204
190
  backgroundColor: background,
@@ -210,52 +196,19 @@ function resolveTokens(modes) {
210
196
  justifyContent: 'center'
211
197
  },
212
198
  imageRadius,
213
- buttonIconSize,
214
- metric: {
215
- flex: 1,
216
- gap: metricGap,
217
- paddingHorizontal: metricPadH,
218
- paddingVertical: metricPadV,
219
- alignItems: 'center',
220
- justifyContent: 'center'
221
- },
222
- metricTitle: {
223
- color: titleColor,
224
- fontSize: titleSize,
225
- fontFamily: titleFamily,
226
- fontWeight: titleWeight,
227
- lineHeight: Math.round(titleSize * 1.2),
228
- textAlign: 'center',
229
- includeFontPadding: false
230
- },
231
- metricValue: {
232
- color: valueColor,
233
- fontSize: valueSize,
234
- fontFamily: valueFamily,
235
- fontWeight: valueWeight,
236
- lineHeight: Math.round(valueSize * 1.2),
237
- textAlign: 'center',
238
- includeFontPadding: false
239
- },
240
- metricCaption: {
241
- color: captionColor,
242
- fontSize: captionSize,
243
- fontFamily: captionFamily,
244
- fontWeight: captionWeight,
245
- lineHeight: Math.round(captionSize * 1.2),
246
- textAlign: 'center',
247
- includeFontPadding: false
248
- }
199
+ buttonIconSize
249
200
  };
250
201
  }
251
202
  const DEFAULT_METRICS = [{
252
203
  title: 'Title',
253
204
  value: 'Value',
254
- caption: 'caption'
205
+ caption: 'caption',
206
+ icon: 'ic_card'
255
207
  }, {
256
208
  title: 'Title',
257
209
  value: 'Value',
258
- caption: 'caption'
210
+ caption: 'caption',
211
+ icon: 'ic_card'
259
212
  }];
260
213
  const styles = _reactNative.StyleSheet.create({
261
214
  mediaSlot: {
@@ -268,6 +221,9 @@ const styles = _reactNative.StyleSheet.create({
268
221
  flexDirection: 'row',
269
222
  alignItems: 'stretch'
270
223
  },
224
+ metricCell: {
225
+ flex: 1
226
+ },
271
227
  metricDivider: {
272
228
  alignSelf: 'center',
273
229
  height: '70%'
@@ -44,6 +44,10 @@ const DEFAULT_ROWS = [{
44
44
  label: 'Bonus JioGold',
45
45
  showInfo: true,
46
46
  values: [false, '1%']
47
+ }, {
48
+ label: 'Bonus JioSilver',
49
+ showInfo: true,
50
+ values: [false, '1%']
47
51
  }];
48
52
 
49
53
  /** Keeps every text layer on a single line. */
@@ -68,13 +72,15 @@ const planColumnStyle = {
68
72
  * current plan against one or more alternative plans across a set of feature
69
73
  * rows. Implementation of Figma node `4498:2968` (`PlanComparisonCard`).
70
74
  *
71
- * Columns use a 1.8fr / 1fr flex ratio (label vs plan), matching the Figma grid.
75
+ * Structure: header (from `columns`) + Slot (default table from `rows`, or
76
+ * custom `children`). Columns use a 1.8fr / 1fr flex ratio (label vs plan).
72
77
  *
73
78
  * @component
74
79
  */
75
80
  function PlanComparisonCard({
76
81
  columns = DEFAULT_COLUMNS,
77
82
  rows = DEFAULT_ROWS,
83
+ children,
78
84
  modes = _reactUtils.EMPTY_MODES,
79
85
  style
80
86
  }) {
@@ -171,6 +177,50 @@ function PlanComparisonCard({
171
177
  }
172
178
  return (0, _reactUtils.cloneChildrenWithModes)(value, modes);
173
179
  };
180
+ const defaultTable = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
181
+ style: {
182
+ width: '100%',
183
+ backgroundColor: tableBackground,
184
+ borderWidth: tableBorderSize,
185
+ borderColor: tableBorderColor,
186
+ borderRadius: tableRadius,
187
+ overflow: 'hidden'
188
+ },
189
+ children: rows.map((row, rowIndex) => {
190
+ const isLast = rowIndex === rows.length - 1;
191
+ const showInfo = row.showInfo || row.onInfoPress != null;
192
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
193
+ style: [rowStyle, {
194
+ borderBottomWidth: isLast ? 0 : cellBorderSize,
195
+ borderBottomColor: cellBorderColor
196
+ }],
197
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
198
+ style: [labelColumnStyle, labelCellStyle],
199
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
200
+ style: labelTextStyle,
201
+ children: row.label
202
+ }), showInfo && (row.onInfoPress ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
203
+ onPress: row.onInfoPress,
204
+ accessibilityRole: "button",
205
+ accessibilityLabel: `More information about ${row.label}`,
206
+ hitSlop: 8,
207
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
208
+ name: "ic_info",
209
+ size: iconSize,
210
+ color: iconColor
211
+ })
212
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
213
+ name: "ic_info",
214
+ size: iconSize,
215
+ color: iconColor
216
+ }))]
217
+ }), columns.map((column, colIndex) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
218
+ style: [planColumnStyle, valueCellStyle],
219
+ children: renderValue(row.values?.[colIndex], `${rowIndex}-${colIndex}`)
220
+ }, column.label ?? colIndex))]
221
+ }, row.key ?? `${row.label}-${rowIndex}`);
222
+ })
223
+ });
174
224
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
175
225
  style: [{
176
226
  gap,
@@ -189,50 +239,7 @@ function PlanComparisonCard({
189
239
  children: column.label
190
240
  })
191
241
  }, column.label ?? index))]
192
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
193
- style: {
194
- width: '100%',
195
- backgroundColor: tableBackground,
196
- borderWidth: tableBorderSize,
197
- borderColor: tableBorderColor,
198
- borderRadius: tableRadius,
199
- overflow: 'hidden'
200
- },
201
- children: rows.map((row, rowIndex) => {
202
- const isLast = rowIndex === rows.length - 1;
203
- const showInfo = row.showInfo || row.onInfoPress != null;
204
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
205
- style: [rowStyle, {
206
- borderBottomWidth: isLast ? 0 : cellBorderSize,
207
- borderBottomColor: cellBorderColor
208
- }],
209
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
210
- style: [labelColumnStyle, labelCellStyle],
211
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
212
- style: labelTextStyle,
213
- children: row.label
214
- }), showInfo && (row.onInfoPress ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
215
- onPress: row.onInfoPress,
216
- accessibilityRole: "button",
217
- accessibilityLabel: `More information about ${row.label}`,
218
- hitSlop: 8,
219
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
220
- name: "ic_info",
221
- size: iconSize,
222
- color: iconColor
223
- })
224
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
225
- name: "ic_info",
226
- size: iconSize,
227
- color: iconColor
228
- }))]
229
- }), columns.map((column, colIndex) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
230
- style: [planColumnStyle, valueCellStyle],
231
- children: renderValue(row.values?.[colIndex], `${rowIndex}-${colIndex}`)
232
- }, column.label ?? colIndex))]
233
- }, row.key ?? `${row.label}-${rowIndex}`);
234
- })
235
- })]
242
+ }), children != null ? (0, _reactUtils.cloneChildrenWithModes)(children, modes) : defaultTable]
236
243
  });
237
244
  }
238
245
  var _default = exports.default = PlanComparisonCard;
@@ -18,7 +18,7 @@ function PortfolioHero({
18
18
  value = '12,000.11',
19
19
  currency = '₹',
20
20
  modes = {
21
- Context3: 'Balance'
21
+ Context3: 'Balance & Cards'
22
22
  },
23
23
  children,
24
24
  style
@@ -391,9 +391,9 @@ function resolveTokens(modes) {
391
391
  const tooltipBackground = asStr((0, _figmaVariablesResolver.getVariableByName)('tooltip/background', modes), '#0f0d0a');
392
392
  const tooltipPaddingH = asNum((0, _figmaVariablesResolver.getVariableByName)('tooltip/padding/horizontal', modes), 12);
393
393
  const tooltipPaddingV = asNum((0, _figmaVariablesResolver.getVariableByName)('tooltip/padding/vertical', modes), 8);
394
- // Not yet in Coin Variables export (Figma still binds bare local radius/maxWidth).
395
- const tooltipRadius = asNum((0, _figmaVariablesResolver.getVariableByName)('tooltip/radius', modes), 8);
396
- const tooltipMaxWidth = asNum((0, _figmaVariablesResolver.getVariableByName)('tooltip/maxWidth', modes), 280);
394
+ // Figma Tooltip binds bare local `radius` / `maxWidth` (not tooltip/*).
395
+ const tooltipRadius = Number((0, _figmaVariablesResolver.getVariableByName)('radius', modes));
396
+ const tooltipMaxWidth = Number((0, _figmaVariablesResolver.getVariableByName)('maxWidth', modes));
397
397
  const tipWidth = asNum((0, _figmaVariablesResolver.getVariableByName)('tooltip/tipItem/width', modes), 16);
398
398
  const tipHeight = asNum((0, _figmaVariablesResolver.getVariableByName)('tooltip/tipItem/height', modes), 8);
399
399
  const tooltipLabelColor = asStr((0, _figmaVariablesResolver.getVariableByName)('tooltip/label/color', modes), '#ffffff');