jfs-components 0.1.51 → 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 (90) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/lib/commonjs/components/ActionFooter/ActionFooter.js +319 -135
  3. package/lib/commonjs/components/Badge/Badge.js +20 -1
  4. package/lib/commonjs/components/Button/Button.js +139 -47
  5. package/lib/commonjs/components/ButtonGroup/ButtonGroup.js +1 -1
  6. package/lib/commonjs/components/Card/Card.js +151 -112
  7. package/lib/commonjs/components/Card/assets/gold-logo-mark.png +0 -0
  8. package/lib/commonjs/components/CategoryCard/CategoryCard.js +97 -63
  9. package/lib/commonjs/components/CategoryCard/assets/sample.png +0 -0
  10. package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +59 -10
  11. package/lib/commonjs/components/ExpandableCheckbox/ExpandableCheckbox.js +132 -49
  12. package/lib/commonjs/components/HelloJioInput/HelloJioInput.js +56 -14
  13. package/lib/commonjs/components/IconCapsule/IconCapsule.js +1 -1
  14. package/lib/commonjs/components/MetricData/MetricData.js +26 -13
  15. package/lib/commonjs/components/MoneyValue/MoneyValue.js +1 -1
  16. package/lib/commonjs/components/Nudge/Nudge.js +1 -1
  17. package/lib/commonjs/components/PdpCcCard/PdpCcCard.js +47 -91
  18. package/lib/commonjs/components/PlanComparisonCard/PlanComparisonCard.js +52 -45
  19. package/lib/commonjs/components/PortfolioHero/PortfolioHero.js +1 -1
  20. package/lib/commonjs/components/Slider/Slider.js +3 -3
  21. package/lib/commonjs/components/TextInput/TextInput.js +26 -8
  22. package/lib/commonjs/components/Tooltip/Tooltip.js +3 -5
  23. package/lib/commonjs/components/index.js +7 -1
  24. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  25. package/lib/commonjs/design-tokens/figma-modes.generated.js +13 -13
  26. package/lib/commonjs/icons/registry.js +1 -1
  27. package/lib/module/components/ActionFooter/ActionFooter.js +322 -138
  28. package/lib/module/components/Badge/Badge.js +20 -1
  29. package/lib/module/components/Button/Button.js +140 -48
  30. package/lib/module/components/ButtonGroup/ButtonGroup.js +1 -1
  31. package/lib/module/components/Card/Card.js +152 -114
  32. package/lib/module/components/Card/assets/gold-logo-mark.png +0 -0
  33. package/lib/module/components/CategoryCard/CategoryCard.js +97 -63
  34. package/lib/module/components/CategoryCard/assets/sample.png +0 -0
  35. package/lib/module/components/CheckboxItem/CheckboxItem.js +59 -10
  36. package/lib/module/components/ExpandableCheckbox/ExpandableCheckbox.js +135 -52
  37. package/lib/module/components/HelloJioInput/HelloJioInput.js +56 -14
  38. package/lib/module/components/IconCapsule/IconCapsule.js +1 -1
  39. package/lib/module/components/MetricData/MetricData.js +26 -14
  40. package/lib/module/components/MoneyValue/MoneyValue.js +1 -1
  41. package/lib/module/components/Nudge/Nudge.js +1 -1
  42. package/lib/module/components/PdpCcCard/PdpCcCard.js +48 -92
  43. package/lib/module/components/PlanComparisonCard/PlanComparisonCard.js +52 -45
  44. package/lib/module/components/PortfolioHero/PortfolioHero.js +1 -1
  45. package/lib/module/components/Slider/Slider.js +3 -3
  46. package/lib/module/components/TextInput/TextInput.js +27 -9
  47. package/lib/module/components/Tooltip/Tooltip.js +3 -5
  48. package/lib/module/components/index.js +1 -1
  49. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  50. package/lib/module/design-tokens/figma-modes.generated.js +13 -13
  51. package/lib/module/icons/registry.js +1 -1
  52. package/lib/typescript/src/components/ActionFooter/ActionFooter.d.ts +50 -21
  53. package/lib/typescript/src/components/Button/Button.d.ts +24 -1
  54. package/lib/typescript/src/components/ButtonGroup/ButtonGroup.d.ts +2 -2
  55. package/lib/typescript/src/components/Card/Card.d.ts +54 -25
  56. package/lib/typescript/src/components/CategoryCard/CategoryCard.d.ts +29 -12
  57. package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +27 -9
  58. package/lib/typescript/src/components/ExpandableCheckbox/ExpandableCheckbox.d.ts +37 -12
  59. package/lib/typescript/src/components/IconCapsule/IconCapsule.d.ts +1 -1
  60. package/lib/typescript/src/components/MetricData/MetricData.d.ts +7 -5
  61. package/lib/typescript/src/components/PdpCcCard/PdpCcCard.d.ts +16 -6
  62. package/lib/typescript/src/components/PlanComparisonCard/PlanComparisonCard.d.ts +9 -2
  63. package/lib/typescript/src/components/index.d.ts +2 -2
  64. package/lib/typescript/src/icons/registry.d.ts +1 -1
  65. package/package.json +1 -1
  66. package/src/components/ActionFooter/ActionFooter.tsx +375 -141
  67. package/src/components/Badge/Badge.tsx +21 -1
  68. package/src/components/Button/Button.tsx +192 -44
  69. package/src/components/ButtonGroup/ButtonGroup.tsx +2 -2
  70. package/src/components/Card/Card.tsx +370 -212
  71. package/src/components/Card/assets/gold-logo-mark.png +0 -0
  72. package/src/components/CategoryCard/CategoryCard.tsx +130 -84
  73. package/src/components/CategoryCard/assets/sample.png +0 -0
  74. package/src/components/CheckboxItem/CheckboxItem.tsx +72 -12
  75. package/src/components/ExpandableCheckbox/ExpandableCheckbox.tsx +186 -67
  76. package/src/components/HelloJioInput/HelloJioInput.tsx +58 -17
  77. package/src/components/IconCapsule/IconCapsule.tsx +1 -1
  78. package/src/components/MetricData/MetricData.tsx +39 -22
  79. package/src/components/MoneyValue/MoneyValue.tsx +1 -1
  80. package/src/components/Nudge/Nudge.tsx +1 -1
  81. package/src/components/PdpCcCard/PdpCcCard.tsx +41 -92
  82. package/src/components/PlanComparisonCard/PlanComparisonCard.tsx +73 -58
  83. package/src/components/PortfolioHero/PortfolioHero.tsx +1 -1
  84. package/src/components/Slider/Slider.tsx +3 -3
  85. package/src/components/TextInput/TextInput.tsx +26 -9
  86. package/src/components/Tooltip/Tooltip.tsx +3 -5
  87. package/src/components/index.ts +9 -2
  88. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  89. package/src/design-tokens/figma-modes.generated.ts +13 -13
  90. package/src/icons/registry.ts +1 -1
@@ -9,12 +9,20 @@ var _reactNative = require("react-native");
9
9
  var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
10
10
  var _webPlatformUtils = require("../../utils/web-platform-utils");
11
11
  var _reactUtils = require("../../utils/react-utils");
12
+ var _GlassFill = _interopRequireDefault(require("../../utils/GlassFill/GlassFill"));
12
13
  var _Icon = _interopRequireDefault(require("../../icons/Icon"));
13
14
  var _Skeleton = _interopRequireDefault(require("../../skeleton/Skeleton"));
14
15
  var _SkeletonGroup = require("../../skeleton/SkeletonGroup");
15
16
  var _jsxRuntime = require("react/jsx-runtime");
16
17
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
18
  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
+ * Figma `Type` property on Button.
21
+ * - `'default'` — solid token-colored pill (hugs content)
22
+ * - `'glass'` — frosted translucent pill backed by {@link GlassFill}
23
+ * - `'fixed'` — solid pill constrained by `button/maxWidth`
24
+ */
25
+
18
26
  // ---------------------------------------------------------------------------
19
27
  // Module-scope constants — never re-allocated per render.
20
28
  // ---------------------------------------------------------------------------
@@ -58,28 +66,79 @@ const NO_WRAP_LABEL_STYLE = {
58
66
  // the most common path (no modes prop) is resolved once for the whole app.
59
67
  // ---------------------------------------------------------------------------
60
68
 
61
- function resolveButtonTokens(modes, disabled) {
62
- const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('button/background', modes);
63
- const borderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', modes);
64
- const borderWidth = (0, _figmaVariablesResolver.getVariableByName)('button/border/size', modes);
65
- const radius = (0, _figmaVariablesResolver.getVariableByName)('button/radius', modes);
66
- const paddingHorizontal = (0, _figmaVariablesResolver.getVariableByName)('button/padding/horizontal', modes);
67
- const paddingVertical = (0, _figmaVariablesResolver.getVariableByName)('button/padding/vertical', modes);
68
- const gap = (0, _figmaVariablesResolver.getVariableByName)('button/gap', modes);
69
- const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('button/fontFamily', modes);
70
- const fontWeightValue = (0, _figmaVariablesResolver.getVariableByName)('button/fontWeight', modes);
71
- const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue;
72
- const lineHeight = (0, _figmaVariablesResolver.getVariableByName)('button/lineHeight', modes);
73
- const fontSize = (0, _figmaVariablesResolver.getVariableByName)('button/fontSize', modes);
74
- const textColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', modes);
75
- const iconSize = (0, _figmaVariablesResolver.getVariableByName)('button/icon/size', modes) ?? 18;
69
+ const BUTTON_STATE_VALUES = new Set(['Idle', 'Hover', 'Pressed', 'Disabled']);
70
+ const SOLID_STATE_COLLECTION = 'Button / State';
71
+ const GLASS_STATE_COLLECTION = 'Button Glass State';
72
+ function resolveIsGlass(type, modes) {
73
+ return type === 'glass' || modes['Button type'] === 'glass';
74
+ }
76
75
 
77
- // Pressed tokens are always resolved so the Pressable `style` callback can
78
- // apply the pressed visual without scheduling a React render.
79
- const pressedModes = {
80
- ...modes,
81
- 'Button / State': 'Pressed'
76
+ /**
77
+ * Pick Idle/Hover/Pressed/Disabled from the active state collection, falling
78
+ * back to the sibling collection when callers pass the other key (e.g. glass
79
+ * buttons that still send `Button / State`). Never treats a collection name
80
+ * as a mode value.
81
+ */
82
+ function pickButtonInteractiveState(modes, isGlass) {
83
+ const primary = isGlass ? GLASS_STATE_COLLECTION : SOLID_STATE_COLLECTION;
84
+ const sibling = isGlass ? SOLID_STATE_COLLECTION : GLASS_STATE_COLLECTION;
85
+ const candidate = modes[primary] ?? modes[sibling];
86
+ if (typeof candidate === 'string' && BUTTON_STATE_VALUES.has(candidate)) {
87
+ return candidate;
88
+ }
89
+ return 'Idle';
90
+ }
91
+
92
+ /** Write a valid interactive state onto the correct collection for glass/solid. */
93
+ function withButtonInteractiveState(modes, isGlass, state) {
94
+ const collection = isGlass ? GLASS_STATE_COLLECTION : SOLID_STATE_COLLECTION;
95
+ const sibling = isGlass ? SOLID_STATE_COLLECTION : GLASS_STATE_COLLECTION;
96
+ const {
97
+ [sibling]: _dropSibling,
98
+ ...rest
99
+ } = modes;
100
+ return {
101
+ ...rest,
102
+ [collection]: state
82
103
  };
104
+ }
105
+ function resolveButtonTokens(modes, disabled, type) {
106
+ const isGlass = resolveIsGlass(type, modes);
107
+ const isFixed = type === 'fixed';
108
+
109
+ // Inject the Button type mode so token aliases (background/foreground) resolve
110
+ // to the glass palette. Callers can also drive glass solely via modes.
111
+ const effectiveModes = isGlass ? {
112
+ ...modes,
113
+ 'Button type': 'glass'
114
+ } : modes;
115
+
116
+ // Glass interactive states live on `Button Glass State`; solid on `Button / State`.
117
+ // Map Idle/Hover/Pressed/Disabled values through — never assign a collection name.
118
+ const idleState = disabled && isGlass ? 'Disabled' : pickButtonInteractiveState(effectiveModes, isGlass);
119
+ const idleModes = withButtonInteractiveState(effectiveModes, isGlass, idleState);
120
+ const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('button/background', idleModes);
121
+ const borderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', idleModes);
122
+ const borderWidth = (0, _figmaVariablesResolver.getVariableByName)('button/border/size', idleModes);
123
+ const radius = (0, _figmaVariablesResolver.getVariableByName)('button/radius', idleModes);
124
+ const paddingHorizontal = (0, _figmaVariablesResolver.getVariableByName)('button/padding/horizontal', idleModes);
125
+ const paddingVertical = (0, _figmaVariablesResolver.getVariableByName)('button/padding/vertical', idleModes);
126
+ const gap = (0, _figmaVariablesResolver.getVariableByName)('button/gap', idleModes);
127
+ const maxWidth = (0, _figmaVariablesResolver.getVariableByName)('button/maxWidth', idleModes);
128
+ const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('button/fontFamily', idleModes);
129
+ const fontWeightValue = (0, _figmaVariablesResolver.getVariableByName)('button/fontWeight', idleModes);
130
+ const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue;
131
+ const lineHeight = (0, _figmaVariablesResolver.getVariableByName)('button/lineHeight', idleModes);
132
+ const fontSize = (0, _figmaVariablesResolver.getVariableByName)('button/fontSize', idleModes);
133
+ const textColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', idleModes);
134
+ const iconSize = (0, _figmaVariablesResolver.getVariableByName)('button/icon/size', idleModes) ?? 18;
135
+
136
+ // Figma `blur/minimal` (px radius) -> GlassFill's 0–100 intensity scale.
137
+ const glassBlurIntensity = Math.max(0, Math.min(100, Math.round(Number((0, _figmaVariablesResolver.getVariableByName)('blur/minimal', idleModes) ?? 29))));
138
+
139
+ // Pressed tokens are always resolved so the Pressable `style` callback /
140
+ // glass overlay can apply the pressed visual without a modes recompute.
141
+ const pressedModes = withButtonInteractiveState(effectiveModes, isGlass, 'Pressed');
83
142
  const pressedBg = (0, _figmaVariablesResolver.getVariableByName)('button/background', pressedModes);
84
143
  const pressedBorderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', pressedModes);
85
144
  const pressedTextColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', pressedModes);
@@ -88,20 +147,22 @@ function resolveButtonTokens(modes, disabled) {
88
147
  // ~3 token lookups + an object spread per render on every Button.
89
148
  let hoverContainer = null;
90
149
  let hoverTextColor = null;
150
+ let glassOverlayHover = null;
91
151
  if (IS_WEB) {
92
- const hoverModes = {
93
- ...modes,
94
- 'Button / State': 'Hover'
95
- };
152
+ const hoverModes = withButtonInteractiveState(effectiveModes, isGlass, 'Hover');
96
153
  const hoverBg = (0, _figmaVariablesResolver.getVariableByName)('button/background', hoverModes);
97
154
  const hoverBorderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', hoverModes);
98
155
  hoverTextColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', hoverModes);
99
- hoverContainer = {
156
+ glassOverlayHover = hoverBg;
157
+ // Glass keeps the host transparent; hover only retints the GlassFill overlay.
158
+ hoverContainer = isGlass ? null : {
100
159
  backgroundColor: hoverBg,
101
160
  borderColor: hoverBorderColor
102
161
  };
103
162
  }
104
163
  return {
164
+ isGlass,
165
+ isFixed,
105
166
  container: {
106
167
  ...containerLayoutStyle,
107
168
  paddingHorizontal: paddingHorizontal,
@@ -109,9 +170,18 @@ function resolveButtonTokens(modes, disabled) {
109
170
  borderRadius: radius,
110
171
  borderWidth: borderWidth ?? 1,
111
172
  borderColor: borderColor,
112
- backgroundColor: backgroundColor,
173
+ // Glass: transparent host so GlassFill shows through. Fixed: clamp width.
174
+ backgroundColor: isGlass ? 'transparent' : backgroundColor,
113
175
  gap: gap,
114
- opacity: disabled ? 0.5 : 1
176
+ // Glass disabled uses Button Glass State=Disabled foreground tokens —
177
+ // don't also apply the solid-button opacity fade.
178
+ opacity: disabled && !isGlass ? 0.5 : 1,
179
+ ...(isGlass ? {
180
+ overflow: 'hidden'
181
+ } : null),
182
+ ...(isFixed && maxWidth != null ? {
183
+ maxWidth: maxWidth
184
+ } : null)
115
185
  },
116
186
  baseLabel: {
117
187
  color: textColor,
@@ -121,7 +191,7 @@ function resolveButtonTokens(modes, disabled) {
121
191
  lineHeight: lineHeight
122
192
  },
123
193
  hoverContainer,
124
- pressedContainer: {
194
+ pressedContainer: isGlass ? {} : {
125
195
  backgroundColor: pressedBg,
126
196
  borderColor: pressedBorderColor
127
197
  },
@@ -131,7 +201,11 @@ function resolveButtonTokens(modes, disabled) {
131
201
  hoverIconColor: hoverTextColor,
132
202
  pressedIconColor: pressedTextColor,
133
203
  iconSize,
134
- accessoryOffset: gap / 2
204
+ accessoryOffset: gap / 2,
205
+ glassOverlayIdle: backgroundColor,
206
+ glassOverlayHover,
207
+ glassOverlayPressed: pressedBg,
208
+ glassBlurIntensity
135
209
  };
136
210
  }
137
211
 
@@ -177,6 +251,9 @@ function ButtonImpl({
177
251
  leading,
178
252
  trailing,
179
253
  icon,
254
+ type = 'default',
255
+ glassOverlayColor,
256
+ glassIntensity,
180
257
  modes = _reactUtils.EMPTY_MODES,
181
258
  onPress,
182
259
  disabled = false,
@@ -201,7 +278,7 @@ function ButtonImpl({
201
278
  userHandlersRef.current.onPressOut = rest?.onPressOut;
202
279
  userHandlersRef.current.onHoverIn = rest?.onHoverIn;
203
280
  userHandlersRef.current.onHoverOut = rest?.onHoverOut;
204
- const tokens = (0, _react.useMemo)(() => resolveButtonTokens(modes, disabled), [modes, disabled]);
281
+ const tokens = (0, _react.useMemo)(() => resolveButtonTokens(modes, disabled, type), [modes, disabled, type]);
205
282
 
206
283
  // Skeleton context — read unconditionally so React's hook order stays
207
284
  // stable. The actual short-circuit return happens AFTER all remaining
@@ -315,7 +392,25 @@ function ButtonImpl({
315
392
  modes: modes
316
393
  });
317
394
  }
318
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
395
+ const body = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
396
+ children: [leading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
397
+ style: leadingAccessoryStyle,
398
+ children: leading
399
+ }) : null, content, icon ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
400
+ style: trailingAccessoryStyle,
401
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
402
+ name: icon,
403
+ size: tokens.iconSize,
404
+ color: activeIconColor,
405
+ accessibilityElementsHidden: true,
406
+ importantForAccessibility: "no"
407
+ })
408
+ }) : trailing ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
409
+ style: trailingAccessoryStyle,
410
+ children: trailing
411
+ }) : null]
412
+ });
413
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
319
414
  accessibilityRole: "button",
320
415
  accessibilityLabel: undefined,
321
416
  ...(accessibilityHint !== undefined ? {
@@ -336,22 +431,19 @@ function ButtonImpl({
336
431
  unstable_pressDelay: PRESS_DELAY,
337
432
  style: styleCallback,
338
433
  ...webProps,
339
- children: [leading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
340
- style: leadingAccessoryStyle,
341
- children: leading
342
- }) : null, content, icon ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
343
- style: trailingAccessoryStyle,
344
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
345
- name: icon,
346
- size: tokens.iconSize,
347
- color: activeIconColor,
348
- accessibilityElementsHidden: true,
349
- importantForAccessibility: "no"
350
- })
351
- }) : trailing ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
352
- style: trailingAccessoryStyle,
353
- children: trailing
354
- }) : null]
434
+ children: tokens.isGlass ? ({
435
+ pressed
436
+ }) => {
437
+ const overlayColor = glassOverlayColor ?? (pressed && !disabled ? tokens.glassOverlayPressed : isHovered && !disabled && tokens.glassOverlayHover ? tokens.glassOverlayHover : tokens.glassOverlayIdle);
438
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
439
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_GlassFill.default, {
440
+ tint: "light",
441
+ intensity: glassIntensity ?? tokens.glassBlurIntensity,
442
+ overlayColor: overlayColor,
443
+ androidTintWash: false
444
+ }), body]
445
+ });
446
+ } : body
355
447
  });
356
448
  }
357
449
  const Button = /*#__PURE__*/_react.default.memo(ButtonImpl);
@@ -22,7 +22,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
22
22
  * @component
23
23
  * @example
24
24
  * ```jsx
25
- * <ButtonGroup modes={{"Appearance": "light"}}>
25
+ * <ButtonGroup modes={{"Color Mode": "Light"}}>
26
26
  * <IconButton iconName="ic_qr_code" />
27
27
  * <Button label="Pay" />
28
28
  * </ButtonGroup>
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.CARD_MEDIA_ASPECT_RATIO = void 0;
6
7
  exports.Card = Card;
7
8
  exports.default = void 0;
8
9
  var _react = _interopRequireWildcard(require("react"));
@@ -12,117 +13,160 @@ var _reactUtils = require("../../utils/react-utils");
12
13
  var _jsxRuntime = require("react/jsx-runtime");
13
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
  /**
15
- * Context to share 'modes' with child components like Card.Title and Card.SupportText.
16
- * This ensures that nested components can resolve their tokens correctly without
17
- * needing explicit mode prop passing for every child.
16
+ * Context so `Card.Title` / `Card.SupportText` resolve tokens from the parent
17
+ * card's `modes` without every call site having to pass them explicitly.
18
18
  */
19
- const CardContext = /*#__PURE__*/(0, _react.createContext)({});
19
+ const CardContext = /*#__PURE__*/(0, _react.createContext)({
20
+ modes: _reactUtils.EMPTY_MODES
21
+ });
22
+
23
+ /** Canonical variant values. Maps 1:1 to Figma `Property 1`. */
24
+
25
+ /**
26
+ * Accepts both the canonical lowercase API and Figma's PascalCase property
27
+ * values so Code Connect / design tooling can pass either.
28
+ */
29
+
30
+ const normalizeVariant = value => {
31
+ if (value === 'slim' || value === 'Slim') return 'slim';
32
+ return 'default';
33
+ };
34
+ const toNumber = (value, fallback) => {
35
+ if (typeof value === 'number') {
36
+ return Number.isFinite(value) ? value : fallback;
37
+ }
38
+ if (typeof value === 'string') {
39
+ const parsed = Number(value);
40
+ return Number.isFinite(parsed) ? parsed : fallback;
41
+ }
42
+ return fallback;
43
+ };
44
+ const toFontWeight = (value, fallback) => {
45
+ if (typeof value === 'number') return String(value);
46
+ if (typeof value === 'string' && value.length > 0) {
47
+ return value;
48
+ }
49
+ return fallback;
50
+ };
51
+
20
52
  /**
21
- * Card component implementation from Figma node 765:6186.
22
- *
23
- * Supports a `media` slot (with aspect ratio) and a content area.
24
- * Supports an optional `header` slot (e.g. a brand logo), a `media` slot
25
- * (with aspect ratio) and a content area.
26
- * Usage:
53
+ * Figma `Ratio` default for the media slot (node 766:6258) — 154×116.
54
+ * Kept as a named constant so stories / docs stay aligned with the design.
55
+ */
56
+ const CARD_MEDIA_ASPECT_RATIO = exports.CARD_MEDIA_ASPECT_RATIO = 154 / 116;
57
+ function resolveCardTokens(modes, mediaAspectRatio) {
58
+ const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('card/background/color', modes) ?? '#ffffff';
59
+ const borderColor = (0, _figmaVariablesResolver.getVariableByName)('card/border/color', modes) ?? '#ffffff';
60
+ const borderWidth = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/strokeWidth', modes), 0.5);
61
+ const radius = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/radius', modes), 12);
62
+ // Figma root uses itemSpacing=4 (bound conceptually to card/gap). The
63
+ // component-set frames also use SPACE_BETWEEN + fixed heights for gallery
64
+ // layout — we hug content and apply gap only, so consumers aren't stuck
65
+ // with empty distributed space.
66
+ const gap = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/gap', modes), 4);
67
+ const paddingHorizontal = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/padding-horizontal', modes), 0);
68
+ const paddingVertical = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/padding-vertical', modes), 0);
69
+ const contentGap = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/content/gap', modes), 4);
70
+ const contentPaddingHorizontal = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/content/padding/horizontal', modes), 12);
71
+ const contentPaddingVertical = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/content/padding/vertical', modes), 12);
72
+ return {
73
+ containerStyle: {
74
+ backgroundColor,
75
+ borderColor,
76
+ borderWidth,
77
+ borderRadius: radius,
78
+ gap,
79
+ paddingHorizontal,
80
+ paddingVertical,
81
+ overflow: 'hidden'
82
+ },
83
+ // Header wrap binds the same content-padding tokens as the content wrap
84
+ // in Figma (12/12) — not a separate header token set.
85
+ headerWrapperStyle: {
86
+ width: '100%',
87
+ flexDirection: 'row',
88
+ alignItems: 'flex-start',
89
+ paddingHorizontal: contentPaddingHorizontal,
90
+ paddingVertical: contentPaddingVertical
91
+ },
92
+ mediaWrapperStyle: {
93
+ width: '100%',
94
+ aspectRatio: mediaAspectRatio,
95
+ overflow: 'hidden'
96
+ },
97
+ contentWrapperStyle: {
98
+ width: '100%',
99
+ gap: contentGap,
100
+ paddingHorizontal: contentPaddingHorizontal,
101
+ paddingVertical: contentPaddingVertical
102
+ }
103
+ };
104
+ }
105
+
106
+ /**
107
+ * `Card` — Figma node 5739:794 (`Property 1=Default` / `Property 1=Slim`).
108
+ *
109
+ * Slots: optional `header`, optional `media` (Default only), and `children`
110
+ * for the content stack. Typography subcomponents (`Card.Title`,
111
+ * `Card.SupportText`) read tokens via context.
112
+ *
113
+ * @example
27
114
  * ```tsx
28
- * <Card header={<GoldLogo />} media={<Image source={...} />} modes={modes}>
115
+ * <Card
116
+ * variant="default"
117
+ * header={<GoldLogo />}
118
+ * media={<Image imageSource={uri} ratio={154 / 116} />}
119
+ * modes={{ AppearanceBrand: 'Neutral' }}
120
+ * >
29
121
  * <Card.SupportText>Support text</Card.SupportText>
30
122
  * <Card.Title>Title</Card.Title>
31
123
  * <Card.SupportText>Support text</Card.SupportText>
32
124
  * </Card>
33
125
  * ```
126
+ *
127
+ * @component
34
128
  */
35
129
  function Card({
130
+ variant: variantProp = 'default',
36
131
  header,
37
132
  media,
38
133
  children,
39
- modes = _reactUtils.EMPTY_MODES,
40
- mediaAspectRatio = 154 / 116,
134
+ modes: propModes = _reactUtils.EMPTY_MODES,
135
+ mediaAspectRatio = CARD_MEDIA_ASPECT_RATIO,
41
136
  style,
42
137
  onPress,
43
138
  disabled,
44
139
  accessibilityLabel
45
140
  }) {
46
- // Resolve Card Container Tokens
47
- const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('card/background/color', modes);
48
- const borderColor = (0, _figmaVariablesResolver.getVariableByName)('card/border/color', modes);
49
- const borderWidth = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/strokeWidth', modes)); // Ensure number
50
- const radius = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/radius', modes));
51
- const gap = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/gap', modes));
52
- const paddingHorizontal = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/padding-horizontal', modes));
53
- const paddingVertical = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/padding-vertical', modes));
141
+ const variant = normalizeVariant(variantProp);
142
+ const modes = propModes ?? _reactUtils.EMPTY_MODES;
143
+ const tokens = (0, _react.useMemo)(() => resolveCardTokens(modes, mediaAspectRatio), [modes, mediaAspectRatio]);
54
144
 
55
- // Resolve Content Wrapper Tokens
56
- const contentGap = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/content/gap', modes));
57
- const contentPaddingHorizontal = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/content/padding/horizontal', modes));
58
- const contentPaddingVertical = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/content/padding/vertical', modes));
59
-
60
- // Clone media to pass modes if it's a valid element, facilitating nested style resolution
61
- const mediaWithModes = /*#__PURE__*/(0, _react.isValidElement)(media) ? /*#__PURE__*/(0, _react.cloneElement)(media, {
62
- modes: {
63
- ...media.props.modes,
64
- ...modes
65
- }
66
- }) : media;
67
-
68
- // Clone header to pass modes if it's a valid element
69
- const headerWithModes = /*#__PURE__*/(0, _react.isValidElement)(header) ? /*#__PURE__*/(0, _react.cloneElement)(header, {
70
- modes: {
71
- ...header.props.modes,
72
- ...modes
73
- }
74
- }) : header;
75
- const containerStyle = {
76
- backgroundColor,
77
- borderColor,
78
- borderWidth,
79
- borderRadius: radius,
80
- gap,
81
- paddingHorizontal,
82
- paddingVertical,
83
- overflow: 'hidden' // Ensure border radius clips content
84
- };
85
-
86
- // Header wrap uses fixed padding from Figma (no dedicated tokens defined).
87
- const headerWrapperStyle = {
88
- width: '100%',
89
- flexDirection: 'row',
90
- alignItems: 'flex-start',
91
- paddingHorizontal: 12,
92
- paddingVertical: 16
93
- };
94
- const mediaWrapperStyle = {
95
- width: '100%',
96
- aspectRatio: mediaAspectRatio,
97
- overflow: 'hidden'
98
- // Figma has 'bg-[#3a46f7]' on the Ratio definition, but that's a placeholder.
99
- // We assume the media content handles its own fill, or we can add a placeholder bg if needed.
100
- // For now, we leave it transparent or rely on media.
101
- };
102
- const contentWrapperStyle = {
103
- width: '100%',
104
- gap: contentGap,
105
- paddingHorizontal: contentPaddingHorizontal,
106
- paddingVertical: contentPaddingVertical
107
- };
145
+ // Slim suppresses media even if a consumer still passes it — matches Figma
146
+ // Property 1=Slim. Default keeps prior behavior: media renders when provided.
147
+ const showMedia = variant === 'default' && media != null;
148
+ const processedHeader = header != null ? (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(header), modes) : null;
149
+ const processedMedia = showMedia ? (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(media), modes) : null;
150
+ const processedChildren = (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(children ?? null), modes);
108
151
  const content = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
109
- children: [header && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
110
- style: headerWrapperStyle,
111
- children: headerWithModes
112
- }), media && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
113
- style: mediaWrapperStyle,
114
- children: mediaWithModes
115
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
116
- style: contentWrapperStyle,
117
- children: children
152
+ children: [processedHeader ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
153
+ style: tokens.headerWrapperStyle,
154
+ children: processedHeader
155
+ }) : null, processedMedia ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
156
+ style: tokens.mediaWrapperStyle,
157
+ children: processedMedia
158
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
159
+ style: tokens.contentWrapperStyle,
160
+ children: processedChildren
118
161
  })]
119
162
  });
163
+ const rootStyle = [tokens.containerStyle, style];
120
164
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(CardContext.Provider, {
121
165
  value: {
122
166
  modes
123
167
  },
124
168
  children: onPress ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
125
- style: [containerStyle, style],
169
+ style: rootStyle,
126
170
  onPress: onPress,
127
171
  disabled: disabled,
128
172
  accessibilityRole: "button",
@@ -132,7 +176,7 @@ function Card({
132
176
  },
133
177
  children: content
134
178
  }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
135
- style: [containerStyle, style],
179
+ style: rootStyle,
136
180
  accessibilityLabel: accessibilityLabel,
137
181
  children: content
138
182
  })
@@ -144,69 +188,64 @@ function Card({
144
188
  // ----------------------------------------------------------------------------
145
189
 
146
190
  /**
147
- * Title component for Card content.
148
- * Uses tokens: card/title/*
191
+ * Title tokens: `card/title/*`
149
192
  */
150
193
  function Title({
151
194
  children,
152
195
  style,
153
- modes: propModes
196
+ modes: propModes,
197
+ numberOfLines = 3
154
198
  }) {
155
199
  const context = (0, _react.useContext)(CardContext);
156
- const modes = propModes || context.modes || {};
157
- const color = (0, _figmaVariablesResolver.getVariableByName)('card/title/color', modes);
158
- const fontSize = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/title/fontSize', modes));
159
- const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('card/title/fontFamily', modes);
160
- const lineHeight = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/title/lineHeight', modes));
161
-
162
- // Handling the potential typo in Figma variables "fonwWeight" vs "fontWeight"
163
- // We check both keys to be safe.
164
- const rawFontWeight = (0, _figmaVariablesResolver.getVariableByName)('card/title/fontWeight', modes);
165
- const fontWeight = typeof rawFontWeight === 'number' ? rawFontWeight.toString() : rawFontWeight;
200
+ const modes = propModes ?? context.modes ?? _reactUtils.EMPTY_MODES;
201
+ const color = (0, _figmaVariablesResolver.getVariableByName)('card/title/color', modes) ?? '#000000';
202
+ const fontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/title/fontSize', modes), 16);
203
+ const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('card/title/fontFamily', modes) ?? 'JioType Var';
204
+ const lineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/title/lineHeight', modes), 18);
205
+ const fontWeight = toFontWeight((0, _figmaVariablesResolver.getVariableByName)('card/title/fontWeight', modes), '900');
166
206
  const textStyle = {
167
207
  color,
168
208
  fontSize,
169
209
  fontFamily,
170
210
  lineHeight,
171
- fontWeight: fontWeight
211
+ fontWeight
172
212
  };
173
213
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
174
214
  style: [textStyle, style],
215
+ numberOfLines: numberOfLines > 0 ? numberOfLines : undefined,
175
216
  children: children
176
217
  });
177
218
  }
178
219
 
179
220
  /**
180
- * SupportiveText component for Card content.
181
- * Uses tokens: card/supportive-text/*
221
+ * Supportive text tokens: `card/supportive-text/*`
182
222
  */
183
223
  function SupportText({
184
224
  children,
185
225
  style,
186
- modes: propModes
226
+ modes: propModes,
227
+ numberOfLines
187
228
  }) {
188
229
  const context = (0, _react.useContext)(CardContext);
189
- const modes = propModes || context.modes || {};
190
- const color = (0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/color', modes);
191
- const fontSize = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/fontSize', modes));
192
- const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/fontFamily', modes);
193
- const lineHeight = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/lineHeight', modes));
194
- const rawFontWeight = (0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/fontWeight', modes);
195
- const fontWeight = typeof rawFontWeight === 'number' ? rawFontWeight.toString() : rawFontWeight;
230
+ const modes = propModes ?? context.modes ?? _reactUtils.EMPTY_MODES;
231
+ const color = (0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/color', modes) ?? '#1a1c1f';
232
+ const fontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/fontSize', modes), 12);
233
+ const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/fontFamily', modes) ?? 'JioType Var';
234
+ const lineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/lineHeight', modes), 16);
235
+ const fontWeight = toFontWeight((0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/fontWeight', modes), '400');
196
236
  const textStyle = {
197
237
  color,
198
238
  fontSize,
199
239
  fontFamily,
200
240
  lineHeight,
201
- fontWeight: fontWeight
241
+ fontWeight
202
242
  };
203
243
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
204
244
  style: [textStyle, style],
245
+ numberOfLines: numberOfLines,
205
246
  children: children
206
247
  });
207
248
  }
208
-
209
- // Attach sub-components to Card
210
249
  Card.Title = Title;
211
250
  Card.SupportText = SupportText;
212
251
  var _default = exports.default = Card;