jfs-components 0.1.2 → 0.1.8

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 (107) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/lib/commonjs/components/AmountInput/AmountInput.js +8 -5
  3. package/lib/commonjs/components/BenefitCard/BenefitCard.js +231 -0
  4. package/lib/commonjs/components/CcCard/CcCard.js +470 -0
  5. package/lib/commonjs/components/Checkbox/Checkbox.js +4 -3
  6. package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +4 -3
  7. package/lib/commonjs/components/CompareTable/CompareTable.js +372 -0
  8. package/lib/commonjs/components/ComparisonBar/ComparisonBar.js +266 -0
  9. package/lib/commonjs/components/DropdownInput/DropdownInput.js +35 -3
  10. package/lib/commonjs/components/FormField/FormField.js +4 -3
  11. package/lib/commonjs/components/InputSearch/InputSearch.js +6 -4
  12. package/lib/commonjs/components/NoteInput/NoteInput.js +6 -5
  13. package/lib/commonjs/components/PdpCcCard/PdpCcCard.js +273 -0
  14. package/lib/commonjs/components/ProductMerchandisingCard/GlassFill.js +263 -0
  15. package/lib/commonjs/components/ProductMerchandisingCard/GlassFill.web.js +116 -0
  16. package/lib/commonjs/components/ProductMerchandisingCard/ProductMerchandisingCard.js +353 -0
  17. package/lib/commonjs/components/ProjectionMarker/ProjectionMarker.js +161 -0
  18. package/lib/commonjs/components/Radio/Radio.js +5 -5
  19. package/lib/commonjs/components/Slider/Slider.js +473 -0
  20. package/lib/commonjs/components/TextInput/TextInput.js +13 -8
  21. package/lib/commonjs/components/TextSegment/TextSegment.js +118 -0
  22. package/lib/commonjs/components/index.js +63 -0
  23. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  24. package/lib/commonjs/design-tokens/figma-modes.generated.js +38 -9
  25. package/lib/commonjs/icons/registry.js +1 -1
  26. package/lib/commonjs/utils/react-utils.js +22 -0
  27. package/lib/module/components/AmountInput/AmountInput.js +6 -4
  28. package/lib/module/components/BenefitCard/BenefitCard.js +225 -0
  29. package/lib/module/components/CcCard/CcCard.js +464 -0
  30. package/lib/module/components/Checkbox/Checkbox.js +5 -4
  31. package/lib/module/components/CheckboxItem/CheckboxItem.js +5 -4
  32. package/lib/module/components/CompareTable/CompareTable.js +367 -0
  33. package/lib/module/components/ComparisonBar/ComparisonBar.js +260 -0
  34. package/lib/module/components/DropdownInput/DropdownInput.js +36 -4
  35. package/lib/module/components/FormField/FormField.js +5 -4
  36. package/lib/module/components/InputSearch/InputSearch.js +6 -4
  37. package/lib/module/components/NoteInput/NoteInput.js +7 -6
  38. package/lib/module/components/PdpCcCard/PdpCcCard.js +267 -0
  39. package/lib/module/components/ProductMerchandisingCard/GlassFill.js +257 -0
  40. package/lib/module/components/ProductMerchandisingCard/GlassFill.web.js +111 -0
  41. package/lib/module/components/ProductMerchandisingCard/ProductMerchandisingCard.js +347 -0
  42. package/lib/module/components/ProjectionMarker/ProjectionMarker.js +156 -0
  43. package/lib/module/components/Radio/Radio.js +5 -4
  44. package/lib/module/components/Slider/Slider.js +468 -0
  45. package/lib/module/components/TextInput/TextInput.js +15 -10
  46. package/lib/module/components/TextSegment/TextSegment.js +113 -0
  47. package/lib/module/components/index.js +9 -0
  48. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  49. package/lib/module/design-tokens/figma-modes.generated.js +38 -9
  50. package/lib/module/icons/registry.js +1 -1
  51. package/lib/module/utils/react-utils.js +21 -0
  52. package/lib/typescript/src/components/AmountInput/AmountInput.d.ts +3 -2
  53. package/lib/typescript/src/components/BenefitCard/BenefitCard.d.ts +93 -0
  54. package/lib/typescript/src/components/CcCard/CcCard.d.ts +137 -0
  55. package/lib/typescript/src/components/Checkbox/Checkbox.d.ts +3 -2
  56. package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +2 -2
  57. package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +88 -0
  58. package/lib/typescript/src/components/ComparisonBar/ComparisonBar.d.ts +118 -0
  59. package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +20 -1
  60. package/lib/typescript/src/components/FormField/FormField.d.ts +2 -2
  61. package/lib/typescript/src/components/InputSearch/InputSearch.d.ts +23 -2
  62. package/lib/typescript/src/components/NoteInput/NoteInput.d.ts +19 -2
  63. package/lib/typescript/src/components/PdpCcCard/PdpCcCard.d.ts +84 -0
  64. package/lib/typescript/src/components/ProductMerchandisingCard/GlassFill.d.ts +56 -0
  65. package/lib/typescript/src/components/ProductMerchandisingCard/GlassFill.web.d.ts +27 -0
  66. package/lib/typescript/src/components/ProductMerchandisingCard/ProductMerchandisingCard.d.ts +81 -0
  67. package/lib/typescript/src/components/ProjectionMarker/ProjectionMarker.d.ts +82 -0
  68. package/lib/typescript/src/components/Radio/Radio.d.ts +3 -2
  69. package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +2 -2
  70. package/lib/typescript/src/components/Slider/Slider.d.ts +99 -0
  71. package/lib/typescript/src/components/TextInput/TextInput.d.ts +9 -29
  72. package/lib/typescript/src/components/TextSegment/TextSegment.d.ts +100 -0
  73. package/lib/typescript/src/components/index.d.ts +10 -1
  74. package/lib/typescript/src/design-tokens/figma-modes.generated.d.ts +22 -2
  75. package/lib/typescript/src/icons/registry.d.ts +1 -1
  76. package/lib/typescript/src/utils/react-utils.d.ts +10 -0
  77. package/package.json +2 -1
  78. package/src/components/AmountInput/AmountInput.tsx +7 -5
  79. package/src/components/BenefitCard/BenefitCard.tsx +309 -0
  80. package/src/components/CcCard/CcCard.tsx +598 -0
  81. package/src/components/Checkbox/Checkbox.tsx +5 -4
  82. package/src/components/CheckboxItem/CheckboxItem.tsx +5 -4
  83. package/src/components/CompareTable/CompareTable.tsx +477 -0
  84. package/src/components/ComparisonBar/ComparisonBar.tsx +356 -0
  85. package/src/components/DropdownInput/DropdownInput.tsx +55 -3
  86. package/src/components/FormField/FormField.tsx +5 -4
  87. package/src/components/InputSearch/InputSearch.tsx +8 -5
  88. package/src/components/NoteInput/NoteInput.tsx +8 -6
  89. package/src/components/PdpCcCard/PdpCcCard.tsx +356 -0
  90. package/src/components/ProductMerchandisingCard/GlassFill.tsx +276 -0
  91. package/src/components/ProductMerchandisingCard/GlassFill.web.tsx +127 -0
  92. package/src/components/ProductMerchandisingCard/ProductMerchandisingCard.tsx +423 -0
  93. package/src/components/ProjectionMarker/ProjectionMarker.tsx +277 -0
  94. package/src/components/Radio/Radio.tsx +5 -4
  95. package/src/components/Slider/Slider.tsx +628 -0
  96. package/src/components/TextInput/TextInput.tsx +15 -11
  97. package/src/components/TextSegment/TextSegment.tsx +166 -0
  98. package/src/components/index.ts +10 -1
  99. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  100. package/src/design-tokens/figma-modes.generated.ts +38 -9
  101. package/src/icons/registry.ts +1 -1
  102. package/src/utils/react-utils.ts +23 -0
  103. package/lib/typescript/scripts/extract-component-tokens.d.ts +0 -9
  104. package/lib/typescript/scripts/generate-component-docs.d.ts +0 -9
  105. package/lib/typescript/scripts/generate-icon-registry.d.ts +0 -3
  106. package/lib/typescript/scripts/generate-mode-types.d.ts +0 -2
  107. package/lib/typescript/scripts/retype-modes.d.cts +0 -2
@@ -0,0 +1,347 @@
1
+ "use strict";
2
+
3
+ import React, { useId, useMemo } from 'react';
4
+ import { View, Text, Pressable, StyleSheet } from 'react-native';
5
+ import Svg, { Defs, LinearGradient, Stop, Rect } from 'react-native-svg';
6
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
7
+ import { EMPTY_MODES } from '../../utils/react-utils';
8
+ import Avatar from '../Avatar/Avatar';
9
+ import Badge from '../Badge/Badge';
10
+ import Button from '../Button/Button';
11
+ import Image from '../Image/Image';
12
+ import GlassFill from './GlassFill';
13
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
14
+ /**
15
+ * ProductMerchandisingCard — a full-bleed image card (Figma node 5277:317)
16
+ * with a top header (avatar + badge) and a bottom **glass footer** that
17
+ * combines a real native background blur with a transparent→black gradient
18
+ * scrim for legible text over any image.
19
+ *
20
+ * Glass footer implementation (works on iOS, Android and Web):
21
+ * - **iOS / Android:** `<GlassFill>` wraps `@react-native-community/blur`'s
22
+ * `BlurView` — iOS gets a real `UIVisualEffectView` (live OS blur),
23
+ * Android gets the community `RealtimeBlurView` with a tinted scrim
24
+ * fallback. The native-only module is isolated in `GlassFill.tsx`.
25
+ * - **Web:** the platform-extension `GlassFill.web.tsx` renders a translucent
26
+ * `View` with `backdrop-filter: blur()` — Metro picks the right file so the
27
+ * web bundle never imports the native blur module.
28
+ * - The gradient scrim is drawn with `react-native-svg` (one renderer for all
29
+ * platforms) so the fade is identical everywhere.
30
+ *
31
+ * The blur strength is driven by the Figma `blur/minimal` token, mapped to the
32
+ * `GlassFill` 0–100 intensity scale the same way `MediaCard` does.
33
+ */
34
+ function ProductMerchandisingCard({
35
+ imageSource,
36
+ title = 'Title',
37
+ subtitle = 'Subtitle',
38
+ badgeLabel,
39
+ badge,
40
+ showAvatar = true,
41
+ avatarSource,
42
+ avatarMonogram,
43
+ avatar,
44
+ specialBadgeLabel,
45
+ specialBadgeIcon,
46
+ specialBadge,
47
+ ctaLabel = 'CTA',
48
+ onCtaPress,
49
+ cta,
50
+ onPress,
51
+ height = 223,
52
+ modes = EMPTY_MODES,
53
+ style,
54
+ accessibilityLabel
55
+ }) {
56
+ // Unique gradient id so multiple cards on one (web) document never collide.
57
+ const rawId = useId();
58
+ const scrimId = `pmc-footer-scrim-${rawId.replace(/[^a-zA-Z0-9_-]/g, '')}`;
59
+ const tokens = useMemo(() => resolveTokens(modes), [modes]);
60
+ const containerStyle = {
61
+ height,
62
+ borderRadius: tokens.radius,
63
+ overflow: 'hidden',
64
+ position: 'relative',
65
+ flexDirection: 'column',
66
+ justifyContent: 'space-between'
67
+ };
68
+
69
+ // Header avatar defaults to the design's 36px (Avatar Size = M). A
70
+ // consumer-supplied `modes['Avatar Size']` still wins via spread order.
71
+ const avatarModes = useMemo(() => ({
72
+ 'Avatar Size': 'M',
73
+ ...modes
74
+ }), [modes]);
75
+
76
+ // The Figma node renders the header badge with the brand "Secondary"
77
+ // appearance at "Medium" emphasis (lilac fill + dark label). These are set
78
+ // as defaults so the card matches the design out of the box; a
79
+ // consumer-supplied `modes` value still wins via spread order.
80
+ const badgeModes = useMemo(() => ({
81
+ AppearanceBrand: 'Secondary',
82
+ Emphasis: 'Medium',
83
+ ...modes
84
+ }), [modes]);
85
+ const headerBadge = badge ?? (badgeLabel != null ? /*#__PURE__*/_jsx(Badge, {
86
+ label: badgeLabel,
87
+ modes: badgeModes
88
+ }) : null);
89
+ const headerAvatar = showAvatar ? avatar ?? /*#__PURE__*/_jsx(Avatar, {
90
+ style: avatarSource ? 'Image' : avatarMonogram ? 'Monogram' : 'Image',
91
+ imageSource: avatarSource,
92
+ monogram: avatarMonogram,
93
+ modes: avatarModes
94
+ }) : null;
95
+ const footerSpecialBadge = specialBadge ?? (specialBadgeLabel != null ? /*#__PURE__*/_jsxs(View, {
96
+ style: styles.specialBadge,
97
+ children: [/*#__PURE__*/_jsx(GlassFill, {
98
+ tint: "light",
99
+ intensity: SPECIAL_BADGE_INTENSITY,
100
+ overlayColor: SPECIAL_BADGE_BG,
101
+ style: styles.specialBadgeFill
102
+ }), specialBadgeIcon != null ? /*#__PURE__*/_jsx(View, {
103
+ style: styles.specialBadgeIcon,
104
+ children: specialBadgeIcon
105
+ }) : null, /*#__PURE__*/_jsx(Text, {
106
+ style: tokens.specialBadgeText,
107
+ numberOfLines: 1,
108
+ children: specialBadgeLabel
109
+ })]
110
+ }) : null);
111
+
112
+ // The Figma CTA uses the brand "Secondary" appearance (purple fill, white
113
+ // label) at the small size. Defaults match the design; consumer `modes`
114
+ // win via spread order.
115
+ const ctaModes = useMemo(() => ({
116
+ 'Button / Size': 'S',
117
+ AppearanceBrand: 'Secondary',
118
+ ...modes
119
+ }), [modes]);
120
+ const footerCta = cta ?? /*#__PURE__*/_jsx(Button, {
121
+ label: ctaLabel,
122
+ modes: ctaModes,
123
+ onPress: onCtaPress
124
+ });
125
+ const content = /*#__PURE__*/_jsxs(_Fragment, {
126
+ children: [imageSource != null ? /*#__PURE__*/_jsx(Image, {
127
+ imageSource: imageSource,
128
+ style: StyleSheet.absoluteFillObject,
129
+ width: "100%",
130
+ height: height,
131
+ resizeMode: "cover",
132
+ accessibilityElementsHidden: true,
133
+ importantForAccessibility: "no"
134
+ }) : null, /*#__PURE__*/_jsxs(View, {
135
+ style: tokens.header,
136
+ pointerEvents: "box-none",
137
+ children: [/*#__PURE__*/_jsx(View, {
138
+ style: styles.avatarContainer,
139
+ children: headerAvatar
140
+ }), headerBadge]
141
+ }), /*#__PURE__*/_jsxs(View, {
142
+ style: tokens.footer,
143
+ pointerEvents: "box-none",
144
+ children: [/*#__PURE__*/_jsx(GlassFill, {
145
+ tint: "dark",
146
+ intensity: tokens.blurIntensity,
147
+ progressive: true
148
+ }), /*#__PURE__*/_jsx(View, {
149
+ style: [StyleSheet.absoluteFill, styles.scrim],
150
+ pointerEvents: "none",
151
+ children: /*#__PURE__*/_jsxs(Svg, {
152
+ width: "100%",
153
+ height: "100%",
154
+ children: [/*#__PURE__*/_jsx(Defs, {
155
+ children: /*#__PURE__*/_jsxs(LinearGradient, {
156
+ id: scrimId,
157
+ x1: "0",
158
+ y1: "0",
159
+ x2: "0",
160
+ y2: "1",
161
+ children: [/*#__PURE__*/_jsx(Stop, {
162
+ offset: "0.018",
163
+ stopColor: "#000000",
164
+ stopOpacity: 0
165
+ }), /*#__PURE__*/_jsx(Stop, {
166
+ offset: "0.974",
167
+ stopColor: "#000000",
168
+ stopOpacity: 1
169
+ })]
170
+ })
171
+ }), /*#__PURE__*/_jsx(Rect, {
172
+ x: "0",
173
+ y: "0",
174
+ width: "100%",
175
+ height: "100%",
176
+ fill: `url(#${scrimId})`
177
+ })]
178
+ })
179
+ }), footerSpecialBadge, /*#__PURE__*/_jsxs(View, {
180
+ style: styles.footerRow,
181
+ children: [/*#__PURE__*/_jsxs(View, {
182
+ style: tokens.textWrap,
183
+ children: [/*#__PURE__*/_jsx(Text, {
184
+ style: tokens.title,
185
+ numberOfLines: 1,
186
+ children: title
187
+ }), subtitle != null ? /*#__PURE__*/_jsx(Text, {
188
+ style: tokens.subtitle,
189
+ numberOfLines: 1,
190
+ children: subtitle
191
+ }) : null]
192
+ }), footerCta]
193
+ })]
194
+ })]
195
+ });
196
+ if (onPress) {
197
+ return /*#__PURE__*/_jsx(Pressable, {
198
+ style: ({
199
+ pressed
200
+ }) => [containerStyle, pressed ? styles.pressed : null, style],
201
+ accessibilityRole: "button",
202
+ accessibilityLabel: accessibilityLabel ?? title,
203
+ onPress: onPress,
204
+ children: content
205
+ });
206
+ }
207
+ return /*#__PURE__*/_jsx(View, {
208
+ style: [containerStyle, style],
209
+ accessibilityLabel: accessibilityLabel,
210
+ children: content
211
+ });
212
+ }
213
+
214
+ // ---------------------------------------------------------------------------
215
+ // Tokens / static styles
216
+ // ---------------------------------------------------------------------------
217
+
218
+ // Figma footer backdrop-blur token (`blur/minimal`). The footer uses a
219
+ // *progressive* blur, so this maps the token to the intensity at the BOTTOM of
220
+ // the ramp (the strongest point); the upper part fades to fully clear. Kept
221
+ // intentionally small to match the design's subtle "minimal" blur.
222
+ const BLUR_INTENSITY_FACTOR = 1.0;
223
+
224
+ // Special badge ("frosted" pill) — values are literals in the Figma node
225
+ // (no dedicated tokens): bg rgba(255,245,229,0.15), 5px backdrop blur.
226
+ const SPECIAL_BADGE_BG = 'rgba(255,245,229,0.15)';
227
+ const SPECIAL_BADGE_INTENSITY = 17; // ~5px CSS blur / ~2 native blurAmount
228
+
229
+ function asNum(raw, fallback) {
230
+ const n = typeof raw === 'number' ? raw : parseFloat(raw);
231
+ return Number.isFinite(n) ? n : fallback;
232
+ }
233
+ function asStr(raw, fallback) {
234
+ return raw != null ? String(raw) : fallback;
235
+ }
236
+ function resolveTokens(modes) {
237
+ // NOTE: token names are passed as string literals DIRECTLY to
238
+ // getVariableByName so the `extract-component-tokens` script can statically
239
+ // collect them for the generated docs. Do not refactor these into a helper
240
+ // that receives the name as a variable.
241
+ const radius = asNum(getVariableByName('productMerchandisingcard/radius', modes), 12);
242
+ const headerPadH = asNum(getVariableByName('productMerchandisingcard/header/padding/horizontal', modes), 12);
243
+ const headerPadV = asNum(getVariableByName('productMerchandisingcard/header/padding/vertical', modes), 12);
244
+ const footerGap = asNum(getVariableByName('productMerchandisingcard/footer/gap', modes), 8);
245
+ const footerPadH = asNum(getVariableByName('productMerchandisingcard/footer/padding/horizontal', modes), 16);
246
+ const footerPadV = asNum(getVariableByName('productMerchandisingcard/footer/padding/vertical', modes), 16);
247
+ const textGap = asNum(getVariableByName('productMerchandisingcard/footer/text/gap', modes), 2);
248
+ const blurRadius = asNum(getVariableByName('blur/minimal', modes), 29);
249
+ const titleColor = asStr(getVariableByName('productMerchandisingcard/footer/title/font/color', modes), '#ffffff');
250
+ const titleSize = asNum(getVariableByName('productMerchandisingcard/footer/title/fontsize', modes), 14);
251
+ const titleFamily = asStr(getVariableByName('productMerchandisingcard/footer/title/fontfamily', modes), 'JioType Var');
252
+ const titleWeight = asStr(getVariableByName('productMerchandisingcard/footer/title/fontweight', modes), '700');
253
+ const subtitleColor = asStr(getVariableByName('productMerchandisingcard/footer/subtitle/font/color', modes), '#c5bcb5');
254
+ const subtitleSize = asNum(getVariableByName('productMerchandisingcard/footer/subtitle/fontsize', modes), 12);
255
+ const subtitleFamily = asStr(getVariableByName('productMerchandisingcard/footer/subtitle/fontfamily', modes), 'JioType Var');
256
+ const subtitleWeight = asStr(getVariableByName('productMerchandisingcard/footer/subtitle/fontweight', modes), '400');
257
+ const sbSize = asNum(getVariableByName('productMerchandisingcard/specialbadge/text/fontsize', modes), 12);
258
+ const sbFamily = asStr(getVariableByName('productMerchandisingcard/specialbadge/text/fontfamily', modes), 'JioType Var');
259
+ const sbWeight = asStr(getVariableByName('productMerchandisingcard/specialbadge/text/fontweight', modes), '500');
260
+ return {
261
+ radius,
262
+ blurIntensity: Math.max(0, Math.min(100, Math.round(blurRadius * BLUR_INTENSITY_FACTOR))),
263
+ header: {
264
+ flexDirection: 'row',
265
+ alignItems: 'flex-start',
266
+ justifyContent: 'space-between',
267
+ paddingHorizontal: headerPadH,
268
+ paddingVertical: headerPadV,
269
+ zIndex: 1
270
+ },
271
+ footer: {
272
+ paddingHorizontal: footerPadH,
273
+ paddingVertical: footerPadV,
274
+ gap: footerGap,
275
+ overflow: 'hidden',
276
+ zIndex: 2
277
+ },
278
+ textWrap: {
279
+ flex: 1,
280
+ gap: textGap,
281
+ justifyContent: 'center'
282
+ },
283
+ title: {
284
+ color: titleColor,
285
+ fontSize: titleSize,
286
+ fontFamily: titleFamily,
287
+ fontWeight: titleWeight,
288
+ lineHeight: Math.round(titleSize * 1.2),
289
+ includeFontPadding: false
290
+ },
291
+ subtitle: {
292
+ color: subtitleColor,
293
+ fontSize: subtitleSize,
294
+ fontFamily: subtitleFamily,
295
+ fontWeight: subtitleWeight,
296
+ lineHeight: Math.round(subtitleSize * 1.2),
297
+ includeFontPadding: false
298
+ },
299
+ specialBadgeText: {
300
+ color: '#ffffff',
301
+ fontSize: sbSize,
302
+ fontFamily: sbFamily,
303
+ fontWeight: sbWeight,
304
+ lineHeight: Math.round(sbSize * 1.2),
305
+ includeFontPadding: false
306
+ }
307
+ };
308
+ }
309
+ const styles = StyleSheet.create({
310
+ avatarContainer: {
311
+ flexShrink: 1,
312
+ justifyContent: 'center'
313
+ },
314
+ footerRow: {
315
+ flexDirection: 'row',
316
+ alignItems: 'center',
317
+ justifyContent: 'space-between',
318
+ gap: 8
319
+ },
320
+ scrim: {
321
+ // The footer node sits at 90% opacity in Figma; applying it to the
322
+ // scrim (not the content) keeps the title/CTA crisp while preserving
323
+ // the intended fade strength.
324
+ opacity: 0.9
325
+ },
326
+ specialBadge: {
327
+ flexDirection: 'row',
328
+ alignItems: 'center',
329
+ gap: 4,
330
+ paddingHorizontal: 6,
331
+ paddingVertical: 2,
332
+ borderRadius: 4,
333
+ overflow: 'hidden',
334
+ alignSelf: 'flex-start'
335
+ },
336
+ specialBadgeFill: {
337
+ borderRadius: 4
338
+ },
339
+ specialBadgeIcon: {
340
+ justifyContent: 'center',
341
+ alignItems: 'center'
342
+ },
343
+ pressed: {
344
+ opacity: 0.92
345
+ }
346
+ });
347
+ export default ProductMerchandisingCard;
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+
3
+ import React from 'react';
4
+ import { View, Text } from 'react-native';
5
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
+ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
7
+ import Divider from '../Divider/Divider';
8
+
9
+ /**
10
+ * A value rendered at either end of the marker. A `string`/`number` is rendered
11
+ * with the built-in title typography; any other `ReactNode` (e.g. a
12
+ * `MoneyValue`) is rendered as-is so consumers can fully control presentation.
13
+ */
14
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
+ const isTextValue = value => typeof value === 'string' || typeof value === 'number';
16
+
17
+ /**
18
+ * ProjectionMarker renders a start value and an end value connected to a
19
+ * centered badge that summarizes the projection (e.g. a tenure and interest
20
+ * rate). It mirrors the Figma `projectionMarker` component and is commonly used
21
+ * inside "Potential returns" style cards.
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * <ProjectionMarker
26
+ * startValue="₹1,00,000"
27
+ * endValue="₹1,25,101"
28
+ * duration="1Y 10M"
29
+ * rate="@ 8.25 %"
30
+ * />
31
+ * ```
32
+ *
33
+ * @example Custom end values via MoneyValue
34
+ * ```tsx
35
+ * <ProjectionMarker
36
+ * startValue={<MoneyValue value="1,00,000" />}
37
+ * endValue={<MoneyValue value="1,25,101" />}
38
+ * duration="1Y 10M"
39
+ * rate="@ 8.25 %"
40
+ * />
41
+ * ```
42
+ */
43
+ function ProjectionMarker({
44
+ startValue = '₹1,00,000',
45
+ endValue = '₹1,25,101',
46
+ duration = '1Y 10M',
47
+ rate = '@ 8.25 %',
48
+ showConnectors = true,
49
+ modes = EMPTY_MODES,
50
+ style,
51
+ startValueStyle,
52
+ endValueStyle,
53
+ badgeStyle,
54
+ durationStyle,
55
+ rateStyle,
56
+ accessibilityLabel
57
+ }) {
58
+ const titleColor = getVariableByName('projectionMarker/title/color', modes) ?? '#000000';
59
+ const titleFontFamily = getVariableByName('projectionMarker/title/fontfamily', modes) ?? 'JioType Var';
60
+ const titleFontSize = getVariableByName('projectionMarker/title/fontsize', modes) ?? 14;
61
+ const titleFontWeight = String(getVariableByName('projectionMarker/title/fontweight', modes) ?? 600);
62
+ const durationColor = getVariableByName('projectionMarker/duration/color', modes) ?? '#000000';
63
+ const durationFontFamily = getVariableByName('projectionMarker/duration/fontfamily', modes) ?? 'JioType Var';
64
+ const durationFontSize = getVariableByName('projectionMarker/duration/fontsize', modes) ?? 12;
65
+ const durationFontWeight = String(getVariableByName('projectionMarker/duration/fontweight', modes) ?? 500);
66
+ const rateColor = getVariableByName('projectionMarker/rate/color', modes) ?? '#6b6b6b';
67
+ const rateFontFamily = getVariableByName('metricdata/rate/fontfamily', modes) ?? 'JioType Var';
68
+ const rateFontSize = getVariableByName('metricdata/rate/fontsize', modes) ?? 11;
69
+ const rateFontWeight = String(getVariableByName('metricdata/rate/fontweight', modes) ?? 400);
70
+ const badgeBg = getVariableByName('projectionMarker/durationWrap/bg/color', modes) ?? '#f5f5f5';
71
+ const badgeBorderColor = getVariableByName('projectionMarker/durationWrap/border/color', modes) ?? '#ebebed';
72
+ const badgeBorderWidth = getVariableByName('metricdata/durationWrap/stroke', modes) ?? 1;
73
+ const badgeRadius = getVariableByName('metricdata/durationWrap/radius', modes) ?? 8;
74
+ const badgePaddingHorizontal = getVariableByName('metricdata/durationWrap/padding/horizontal', modes) ?? 16;
75
+ const badgePaddingVertical = getVariableByName('metricdata/durationWrap/padding/vertical', modes) ?? 8;
76
+ const titleTextStyle = {
77
+ color: titleColor,
78
+ fontFamily: titleFontFamily,
79
+ fontSize: titleFontSize,
80
+ fontWeight: titleFontWeight,
81
+ lineHeight: titleFontSize * 1.3
82
+ };
83
+ const renderEndValue = (value, overrideStyle) => {
84
+ if (value === null || value === undefined) return null;
85
+ if (isTextValue(value)) {
86
+ return /*#__PURE__*/_jsx(Text, {
87
+ style: [titleTextStyle, overrideStyle],
88
+ numberOfLines: 1,
89
+ children: value
90
+ });
91
+ }
92
+ return cloneChildrenWithModes(value, modes);
93
+ };
94
+ return /*#__PURE__*/_jsxs(View, {
95
+ style: [styles.container, style],
96
+ accessibilityRole: "summary",
97
+ accessibilityLabel: accessibilityLabel,
98
+ children: [renderEndValue(startValue, startValueStyle), showConnectors && /*#__PURE__*/_jsx(Divider, {
99
+ direction: "horizontal",
100
+ modes: modes,
101
+ style: styles.connector
102
+ }), /*#__PURE__*/_jsxs(View, {
103
+ style: [styles.badge, {
104
+ backgroundColor: badgeBg,
105
+ borderColor: badgeBorderColor,
106
+ borderWidth: badgeBorderWidth,
107
+ borderRadius: badgeRadius,
108
+ paddingHorizontal: badgePaddingHorizontal,
109
+ paddingVertical: badgePaddingVertical
110
+ }, badgeStyle],
111
+ children: [duration !== null && duration !== undefined ? isTextValue(duration) ? /*#__PURE__*/_jsx(Text, {
112
+ style: [{
113
+ color: durationColor,
114
+ fontFamily: durationFontFamily,
115
+ fontSize: durationFontSize,
116
+ fontWeight: durationFontWeight,
117
+ lineHeight: durationFontSize * 1.2,
118
+ textAlign: 'center'
119
+ }, durationStyle],
120
+ children: duration
121
+ }) : cloneChildrenWithModes(duration, modes) : null, rate !== null && rate !== undefined ? isTextValue(rate) ? /*#__PURE__*/_jsx(Text, {
122
+ style: [{
123
+ color: rateColor,
124
+ fontFamily: rateFontFamily,
125
+ fontSize: rateFontSize,
126
+ fontWeight: rateFontWeight,
127
+ lineHeight: rateFontSize * 1.3,
128
+ textAlign: 'center'
129
+ }, rateStyle],
130
+ children: rate
131
+ }) : cloneChildrenWithModes(rate, modes) : null]
132
+ }), showConnectors && /*#__PURE__*/_jsx(Divider, {
133
+ direction: "horizontal",
134
+ modes: modes,
135
+ style: styles.connector
136
+ }), renderEndValue(endValue, endValueStyle)]
137
+ });
138
+ }
139
+ const styles = {
140
+ container: {
141
+ flexDirection: 'row',
142
+ alignItems: 'center',
143
+ width: '100%'
144
+ },
145
+ connector: {
146
+ flex: 1,
147
+ alignSelf: 'center',
148
+ marginHorizontal: 16
149
+ },
150
+ badge: {
151
+ flexDirection: 'column',
152
+ alignItems: 'center',
153
+ justifyContent: 'center'
154
+ }
155
+ };
156
+ export default ProjectionMarker;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- import React, { useState } from 'react';
3
+ import React, { forwardRef, useState } from 'react';
4
4
  import { Pressable, View, Platform } from 'react-native';
5
5
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
6
  import { EMPTY_MODES } from '../../utils/react-utils';
@@ -13,14 +13,14 @@ import { jsx as _jsx } from "react/jsx-runtime";
13
13
  // Radio
14
14
  // ---------------------------------------------------------------------------
15
15
 
16
- export function Radio({
16
+ export const Radio = /*#__PURE__*/forwardRef(function Radio({
17
17
  selected = false,
18
18
  disabled = false,
19
19
  onPress,
20
20
  modes = EMPTY_MODES,
21
21
  style,
22
22
  testID
23
- }) {
23
+ }, ref) {
24
24
  // ---- Refs & State ----
25
25
  const [hovered, setHovered] = useState(false);
26
26
  const [focused, setFocused] = useState(false);
@@ -168,6 +168,7 @@ export function Radio({
168
168
  getVariableByName('radio/disabledSelected/border/color');
169
169
  }
170
170
  return /*#__PURE__*/_jsx(Pressable, {
171
+ ref: ref,
171
172
  testID: testID,
172
173
  disabled: disabled,
173
174
  onPress: onPress,
@@ -184,5 +185,5 @@ export function Radio({
184
185
  style: selectorStyle
185
186
  })
186
187
  });
187
- }
188
+ });
188
189
  export default Radio;