jfs-components 0.0.74 → 0.0.77

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 (92) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/lib/commonjs/components/ActionFooter/ActionFooter.js +147 -82
  3. package/lib/commonjs/components/Avatar/Avatar.js +20 -0
  4. package/lib/commonjs/components/Badge/Badge.js +23 -0
  5. package/lib/commonjs/components/Button/Button.js +37 -0
  6. package/lib/commonjs/components/IconButton/IconButton.js +20 -0
  7. package/lib/commonjs/components/Image/Image.js +26 -1
  8. package/lib/commonjs/components/LottiePlayer/LottiePlayer.js +116 -0
  9. package/lib/commonjs/components/LottiePlayer/LottiePlayer.web.js +82 -0
  10. package/lib/commonjs/components/LottiePlayer/loadNativeLottieView.js +74 -0
  11. package/lib/commonjs/components/LottiePlayer/loadWebLottieView.js +50 -0
  12. package/lib/commonjs/components/PageHero/PageHero.js +41 -5
  13. package/lib/commonjs/components/RechargeCard/RechargeCard.js +32 -17
  14. package/lib/commonjs/components/Text/Text.js +31 -1
  15. package/lib/commonjs/components/index.js +7 -0
  16. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  17. package/lib/commonjs/icons/Icon.js +16 -0
  18. package/lib/commonjs/icons/registry.js +1 -1
  19. package/lib/commonjs/index.js +12 -0
  20. package/lib/commonjs/skeleton/Skeleton.js +234 -0
  21. package/lib/commonjs/skeleton/SkeletonGroup.js +140 -0
  22. package/lib/commonjs/skeleton/index.js +58 -0
  23. package/lib/commonjs/skeleton/shimmer-tokens.js +189 -0
  24. package/lib/commonjs/skeleton/useReducedMotion.js +64 -0
  25. package/lib/module/components/ActionFooter/ActionFooter.js +146 -82
  26. package/lib/module/components/Avatar/Avatar.js +19 -0
  27. package/lib/module/components/Badge/Badge.js +23 -0
  28. package/lib/module/components/Button/Button.js +37 -0
  29. package/lib/module/components/IconButton/IconButton.js +20 -0
  30. package/lib/module/components/Image/Image.js +25 -1
  31. package/lib/module/components/LottiePlayer/LottiePlayer.js +111 -0
  32. package/lib/module/components/LottiePlayer/LottiePlayer.web.js +77 -0
  33. package/lib/module/components/LottiePlayer/loadNativeLottieView.js +69 -0
  34. package/lib/module/components/LottiePlayer/loadWebLottieView.js +45 -0
  35. package/lib/module/components/PageHero/PageHero.js +41 -5
  36. package/lib/module/components/RechargeCard/RechargeCard.js +33 -17
  37. package/lib/module/components/Text/Text.js +31 -1
  38. package/lib/module/components/index.js +1 -0
  39. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  40. package/lib/module/icons/Icon.js +16 -0
  41. package/lib/module/icons/registry.js +1 -1
  42. package/lib/module/index.js +2 -1
  43. package/lib/module/skeleton/Skeleton.js +229 -0
  44. package/lib/module/skeleton/SkeletonGroup.js +133 -0
  45. package/lib/module/skeleton/index.js +6 -0
  46. package/lib/module/skeleton/shimmer-tokens.js +181 -0
  47. package/lib/module/skeleton/useReducedMotion.js +61 -0
  48. package/lib/typescript/src/components/ActionFooter/ActionFooter.d.ts +26 -21
  49. package/lib/typescript/src/components/Avatar/Avatar.d.ts +7 -1
  50. package/lib/typescript/src/components/Badge/Badge.d.ts +7 -1
  51. package/lib/typescript/src/components/Button/Button.d.ts +8 -1
  52. package/lib/typescript/src/components/IconButton/IconButton.d.ts +7 -1
  53. package/lib/typescript/src/components/Image/Image.d.ts +8 -1
  54. package/lib/typescript/src/components/LottiePlayer/LottiePlayer.d.ts +85 -0
  55. package/lib/typescript/src/components/LottiePlayer/LottiePlayer.web.d.ts +28 -0
  56. package/lib/typescript/src/components/LottiePlayer/loadNativeLottieView.d.ts +11 -0
  57. package/lib/typescript/src/components/LottiePlayer/loadWebLottieView.d.ts +11 -0
  58. package/lib/typescript/src/components/PageHero/PageHero.d.ts +31 -5
  59. package/lib/typescript/src/components/Text/Text.d.ts +20 -1
  60. package/lib/typescript/src/components/index.d.ts +1 -0
  61. package/lib/typescript/src/icons/Icon.d.ts +7 -1
  62. package/lib/typescript/src/icons/registry.d.ts +1 -1
  63. package/lib/typescript/src/index.d.ts +1 -0
  64. package/lib/typescript/src/skeleton/Skeleton.d.ts +60 -0
  65. package/lib/typescript/src/skeleton/SkeletonGroup.d.ts +78 -0
  66. package/lib/typescript/src/skeleton/index.d.ts +5 -0
  67. package/lib/typescript/src/skeleton/shimmer-tokens.d.ts +160 -0
  68. package/lib/typescript/src/skeleton/useReducedMotion.d.ts +15 -0
  69. package/package.json +11 -1
  70. package/src/components/ActionFooter/ActionFooter.tsx +152 -86
  71. package/src/components/Avatar/Avatar.tsx +26 -0
  72. package/src/components/Badge/Badge.tsx +27 -0
  73. package/src/components/Button/Button.tsx +40 -0
  74. package/src/components/IconButton/IconButton.tsx +27 -0
  75. package/src/components/Image/Image.tsx +25 -0
  76. package/src/components/LottiePlayer/LottiePlayer.tsx +145 -0
  77. package/src/components/LottiePlayer/LottiePlayer.web.tsx +94 -0
  78. package/src/components/LottiePlayer/loadNativeLottieView.tsx +87 -0
  79. package/src/components/LottiePlayer/loadWebLottieView.tsx +64 -0
  80. package/src/components/PageHero/PageHero.tsx +61 -4
  81. package/src/components/RechargeCard/RechargeCard.tsx +32 -24
  82. package/src/components/Text/Text.tsx +54 -0
  83. package/src/components/index.ts +1 -0
  84. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  85. package/src/icons/Icon.tsx +17 -0
  86. package/src/icons/registry.ts +1 -1
  87. package/src/index.ts +1 -0
  88. package/src/skeleton/Skeleton.tsx +298 -0
  89. package/src/skeleton/SkeletonGroup.tsx +193 -0
  90. package/src/skeleton/index.ts +10 -0
  91. package/src/skeleton/shimmer-tokens.ts +221 -0
  92. package/src/skeleton/useReducedMotion.ts +72 -0
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+
3
+ import React, { useMemo } from 'react';
4
+ import { View } from 'react-native';
5
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
+ import { useTokens } from '../../design-tokens/JFSThemeProvider';
7
+ import { EMPTY_MODES } from '../../utils/react-utils';
8
+ import { getNativeLottieView } from './loadNativeLottieView';
9
+
10
+ /**
11
+ * A parsed Lottie animation. The JSON object you get from
12
+ * `require('./animation.json')` or `fetch().then(r => r.json())`. We keep the
13
+ * type intentionally loose because both `lottie-react-native` and `lottie-react`
14
+ * accept slightly different shapes — `LottiePlayer` narrows back to the
15
+ * platform-specific type internally.
16
+ */
17
+ import { jsx as _jsx } from "react/jsx-runtime";
18
+ const DEFAULT_SIZE = 117;
19
+ function resolveSize(size, modes) {
20
+ if (typeof size === 'number') return {
21
+ width: size,
22
+ height: size
23
+ };
24
+ if (size && typeof size === 'object') return size;
25
+ const width = Number(getVariableByName('media/width', modes)) || DEFAULT_SIZE;
26
+ const height = Number(getVariableByName('media/height', modes)) || DEFAULT_SIZE;
27
+ return {
28
+ width,
29
+ height
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Renders a Lottie animation using the consumer's installed
35
+ * `lottie-react-native` (native) or `lottie-react` (web) — both are declared
36
+ * as **optional peer dependencies** of `jfs-components`, so installing the
37
+ * library does not pull them in. Add the relevant package to your app only
38
+ * if you actually use `LottiePlayer`:
39
+ *
40
+ * ```sh
41
+ * # React Native (iOS / Android)
42
+ * npm install lottie-react-native
43
+ * cd ios && pod install
44
+ *
45
+ * # Web (or react-native-web)
46
+ * npm install lottie-react
47
+ * ```
48
+ *
49
+ * The web build (`LottiePlayer.web.tsx`) is picked automatically by Metro /
50
+ * webpack via platform extensions — same pattern as `MediaCard/GlassFill`.
51
+ *
52
+ * Token-driven sizing: when `size` is omitted, `LottiePlayer` reads
53
+ * `media/width` and `media/height` from the Figma variables resolver, so the
54
+ * animation matches the surrounding component's `Media / Output` mode
55
+ * automatically. This is the same sizing contract `PageHero` and
56
+ * `LottieIntroBlock` use for their `media` slots.
57
+ *
58
+ * @component
59
+ * @example
60
+ * ```tsx
61
+ * import animation from './assets/loader.json';
62
+ *
63
+ * <LottiePlayer source={animation} /> // 117 × 117 (default)
64
+ * <LottiePlayer source={animation} size={70} /> // 70 × 70
65
+ * <LottiePlayer source={animation} modes={{ 'Media / Output': 'S' }} /> // 20 × 20
66
+ * <PageHero media={<LottiePlayer source={animation} />} />
67
+ * ```
68
+ */
69
+ function LottiePlayer({
70
+ source,
71
+ size,
72
+ autoPlay = true,
73
+ loop = true,
74
+ modes: propModes = EMPTY_MODES,
75
+ style,
76
+ accessibilityLabel,
77
+ testID
78
+ }) {
79
+ const {
80
+ modes: globalModes
81
+ } = useTokens();
82
+ const modes = useMemo(() => globalModes === EMPTY_MODES && propModes === EMPTY_MODES ? EMPTY_MODES : {
83
+ ...globalModes,
84
+ ...propModes
85
+ }, [globalModes, propModes]);
86
+ const {
87
+ width,
88
+ height
89
+ } = useMemo(() => resolveSize(size, modes), [size, modes]);
90
+ const NativeLottieView = useMemo(() => getNativeLottieView(), []);
91
+ return /*#__PURE__*/_jsx(View, {
92
+ style: [{
93
+ width,
94
+ height
95
+ }, style],
96
+ testID: testID,
97
+ accessibilityLabel: accessibilityLabel,
98
+ accessibilityElementsHidden: accessibilityLabel ? undefined : true,
99
+ importantForAccessibility: accessibilityLabel ? 'auto' : 'no',
100
+ children: /*#__PURE__*/_jsx(NativeLottieView, {
101
+ source: source,
102
+ autoPlay: autoPlay,
103
+ loop: loop,
104
+ style: {
105
+ width: '100%',
106
+ height: '100%'
107
+ }
108
+ })
109
+ });
110
+ }
111
+ export default /*#__PURE__*/React.memo(LottiePlayer);
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+
3
+ import React, { useMemo } from 'react';
4
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
5
+ import { useTokens } from '../../design-tokens/JFSThemeProvider';
6
+ import { EMPTY_MODES } from '../../utils/react-utils';
7
+ import { getWebLottieView } from './loadWebLottieView';
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
+ const DEFAULT_SIZE = 117;
10
+ function resolveSize(size, modes) {
11
+ if (typeof size === 'number') return {
12
+ width: size,
13
+ height: size
14
+ };
15
+ if (size && typeof size === 'object') return size;
16
+ const width = Number(getVariableByName('media/width', modes)) || DEFAULT_SIZE;
17
+ const height = Number(getVariableByName('media/height', modes)) || DEFAULT_SIZE;
18
+ return {
19
+ width,
20
+ height
21
+ };
22
+ }
23
+
24
+ /**
25
+ * Web build of `LottiePlayer` — picked automatically by webpack /
26
+ * Metro-for-web via the `.web.tsx` platform extension. Uses `lottie-react`
27
+ * (which wraps `lottie-web`) and renders a plain DOM container.
28
+ *
29
+ * Public API mirrors `LottiePlayer.tsx` (native). See that file for the
30
+ * documented prop reference and usage patterns.
31
+ */
32
+ function LottiePlayer({
33
+ source,
34
+ size,
35
+ autoPlay = true,
36
+ loop = true,
37
+ modes: propModes = EMPTY_MODES,
38
+ style,
39
+ accessibilityLabel,
40
+ testID
41
+ }) {
42
+ const {
43
+ modes: globalModes
44
+ } = useTokens();
45
+ const modes = useMemo(() => globalModes === EMPTY_MODES && propModes === EMPTY_MODES ? EMPTY_MODES : {
46
+ ...globalModes,
47
+ ...propModes
48
+ }, [globalModes, propModes]);
49
+ const {
50
+ width,
51
+ height
52
+ } = useMemo(() => resolveSize(size, modes), [size, modes]);
53
+ const WebLottieView = useMemo(() => getWebLottieView(), []);
54
+ return /*#__PURE__*/_jsx("div", {
55
+ style: {
56
+ width,
57
+ height,
58
+ display: 'flex',
59
+ alignItems: 'center',
60
+ justifyContent: 'center',
61
+ ...style
62
+ },
63
+ "data-testid": testID,
64
+ "aria-label": accessibilityLabel,
65
+ "aria-hidden": accessibilityLabel ? undefined : true,
66
+ children: /*#__PURE__*/_jsx(WebLottieView, {
67
+ animationData: source,
68
+ autoplay: autoPlay,
69
+ loop: loop,
70
+ style: {
71
+ width: '100%',
72
+ height: '100%'
73
+ }
74
+ })
75
+ });
76
+ }
77
+ export default /*#__PURE__*/React.memo(LottiePlayer);
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+
3
+ import React from 'react';
4
+ import { Text, View } from 'react-native';
5
+
6
+ /** Props we forward to the underlying native Lottie view. */
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ const INSTALL_HINT = 'LottiePlayer requires lottie-react-native in your app.\n' + ' npm install lottie-react-native\n' + ' cd ios && pod install';
9
+
10
+ /**
11
+ * Metro resolves `require('lottie-react-native')` at bundle time even inside
12
+ * try/catch, which breaks apps that import `jfs-components` without having
13
+ * the optional peer installed. Splitting the module id into a runtime string
14
+ * keeps Metro from statically linking it — the native module is loaded only
15
+ * when present in the consumer's node_modules.
16
+ */
17
+ function resolveNativeLottieModuleName() {
18
+ return ['lottie', '-react', '-native'].join('');
19
+ }
20
+ function LottieUnavailableView({
21
+ style
22
+ }) {
23
+ if (__DEV__) {
24
+ return /*#__PURE__*/_jsx(View, {
25
+ style: [style, {
26
+ alignItems: 'center',
27
+ justifyContent: 'center',
28
+ backgroundColor: 'rgba(255, 196, 0, 0.12)',
29
+ borderWidth: 1,
30
+ borderColor: 'rgba(255, 196, 0, 0.45)',
31
+ borderRadius: 8,
32
+ padding: 8
33
+ }],
34
+ children: /*#__PURE__*/_jsx(Text, {
35
+ style: {
36
+ color: '#8a6d00',
37
+ fontSize: 11,
38
+ textAlign: 'center',
39
+ lineHeight: 15
40
+ },
41
+ children: INSTALL_HINT
42
+ })
43
+ });
44
+ }
45
+ return /*#__PURE__*/_jsx(View, {
46
+ style: style
47
+ });
48
+ }
49
+ function LottieUnavailable(props) {
50
+ React.useEffect(() => {
51
+ if (__DEV__) {
52
+ console.warn(`[jfs-components/LottiePlayer] ${INSTALL_HINT}`);
53
+ }
54
+ }, []);
55
+ return /*#__PURE__*/_jsx(LottieUnavailableView, {
56
+ style: props.style
57
+ });
58
+ }
59
+ let cachedView;
60
+ export function getNativeLottieView() {
61
+ if (cachedView !== undefined) return cachedView;
62
+ try {
63
+ const mod = require(resolveNativeLottieModuleName());
64
+ cachedView = mod.default ?? LottieUnavailable;
65
+ } catch {
66
+ cachedView = LottieUnavailable;
67
+ }
68
+ return cachedView;
69
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+
3
+ import React from 'react';
4
+
5
+ /** Props we forward to the underlying web Lottie view. */
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
+ const INSTALL_HINT = 'LottiePlayer requires lottie-react in your app.\n' + ' npm install lottie-react';
8
+ function resolveWebLottieModuleName() {
9
+ return ['lottie', '-react'].join('');
10
+ }
11
+ function LottieUnavailable(props) {
12
+ React.useEffect(() => {
13
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
14
+ console.warn(`[jfs-components/LottiePlayer] ${INSTALL_HINT}`);
15
+ }
16
+ }, []);
17
+ return /*#__PURE__*/_jsx("div", {
18
+ style: {
19
+ ...props.style,
20
+ display: 'flex',
21
+ alignItems: 'center',
22
+ justifyContent: 'center',
23
+ backgroundColor: 'rgba(255, 196, 0, 0.12)',
24
+ border: '1px solid rgba(255, 196, 0, 0.45)',
25
+ borderRadius: 8,
26
+ padding: 8,
27
+ color: '#8a6d00',
28
+ fontSize: 11,
29
+ textAlign: 'center',
30
+ lineHeight: '15px'
31
+ },
32
+ children: typeof __DEV__ !== 'undefined' && __DEV__ ? INSTALL_HINT : null
33
+ });
34
+ }
35
+ let cachedView;
36
+ export function getWebLottieView() {
37
+ if (cachedView !== undefined) return cachedView;
38
+ try {
39
+ const mod = require(resolveWebLottieModuleName());
40
+ cachedView = mod.default ?? LottieUnavailable;
41
+ } catch {
42
+ cachedView = LottieUnavailable;
43
+ }
44
+ return cachedView;
45
+ }
@@ -9,12 +9,14 @@ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
9
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  /**
11
11
  * PageHero displays a centered hero block typically used at the top of a page
12
- * or feature screen. It contains an eyebrow line, a large headline, an optional
13
- * supporting line (e.g. price/timeline), and an optional action button.
12
+ * or feature screen. It contains an optional media slot (illustration / image
13
+ * / Lottie / SVG / video — consumer's choice), an eyebrow line, a large
14
+ * headline, an optional supporting line (e.g. price / timeline), and an
15
+ * optional action button.
14
16
  *
15
17
  * All visual values are resolved from Figma design tokens via
16
- * `getVariableByName`. The button slot cascades the active `modes` to its
17
- * children through `cloneChildrenWithModes`.
18
+ * `getVariableByName`. Slots cascade the active `modes` to their children
19
+ * through `cloneChildrenWithModes`.
18
20
  *
19
21
  * @component
20
22
  * @example
@@ -25,6 +27,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
25
27
  * supportingText="₹999/year · ₹0 until 2027"
26
28
  * buttonLabel="Renew for free"
27
29
  * onButtonPress={() => navigate('Upgrade')}
30
+ * media={
31
+ * <Image
32
+ * imageSource={require('./assets/upgrade.png')}
33
+ * width={117}
34
+ * height={117}
35
+ * />
36
+ * }
28
37
  * />
29
38
  * ```
30
39
  */
@@ -37,6 +46,7 @@ function PageHero({
37
46
  onButtonPress,
38
47
  showButton = true,
39
48
  buttonSlot,
49
+ media,
40
50
  modes: propModes = EMPTY_MODES,
41
51
  style,
42
52
  testID
@@ -51,6 +61,12 @@ function PageHero({
51
61
  const gap = Number(getVariableByName('PageHero/gap', modes)) || 16;
52
62
  const paddingHorizontal = Number(getVariableByName('PageHero/padding/horizontal', modes)) || 0;
53
63
  const textWrapGap = Number(getVariableByName('PageHero/textWrap/gap', modes)) || 8;
64
+
65
+ // Media slot box — matches the 117×117 frame in Figma (node 4540:7845).
66
+ // Tokens fall back to 117 when not defined in the variables collection,
67
+ // so the layout stays stable on consumers that haven't tokenized this yet.
68
+ const mediaWidth = Number(getVariableByName('media/width', modes)) || 117;
69
+ const mediaHeight = Number(getVariableByName('media/height', modes)) || 117;
54
70
  const eyebrowColor = getVariableByName('PageHero/eyebrow/color', modes) || '#ffffff';
55
71
  const eyebrowFontFamily = getVariableByName('PageHero/eyebrow/fontFamily', modes) || 'System';
56
72
  const eyebrowFontSize = Number(getVariableByName('PageHero/eyebrow/fontSize', modes)) || 18;
@@ -126,10 +142,30 @@ function PageHero({
126
142
  // shape never does.
127
143
  // eslint-disable-next-line react-hooks/exhaustive-deps
128
144
  }, [buttonSlot, showButton, buttonLabel, onButtonPress, modes]);
145
+
146
+ // Sized container for the media slot. Always rendered when `media` is
147
+ // provided, so the slot has a predictable box (matches Figma frame
148
+ // 4540:7845 — 117×117 by default) even if the inner element omits its
149
+ // own width/height. `overflow: 'hidden'` mirrors the Figma frame's
150
+ // `clipsContent` so a slightly oversized illustration doesn't break
151
+ // the centered layout.
152
+ const mediaContent = useMemo(() => {
153
+ if (media === undefined || media === null) return null;
154
+ return /*#__PURE__*/_jsx(View, {
155
+ style: {
156
+ width: mediaWidth,
157
+ height: mediaHeight,
158
+ alignItems: 'center',
159
+ justifyContent: 'center',
160
+ overflow: 'hidden'
161
+ },
162
+ children: cloneChildrenWithModes(media, modes)
163
+ });
164
+ }, [media, mediaWidth, mediaHeight, modes]);
129
165
  return /*#__PURE__*/_jsxs(View, {
130
166
  style: [containerStyle, style],
131
167
  testID: testID,
132
- children: [/*#__PURE__*/_jsxs(View, {
168
+ children: [mediaContent, /*#__PURE__*/_jsxs(View, {
133
169
  style: textWrapStyle,
134
170
  children: [eyebrow ? /*#__PURE__*/_jsx(Text, {
135
171
  style: eyebrowStyle,
@@ -7,7 +7,23 @@ import AvatarGroup from '../AvatarGroup/AvatarGroup';
7
7
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
8
8
  import { EMPTY_MODES } from '../../utils/react-utils';
9
9
  import MoneyValue from '../MoneyValue/MoneyValue';
10
+
11
+ // Defaults applied to the inner ButtonGroup so the card matches Figma out of
12
+ // the box. They are spread *before* the caller's `modes` so any consumer can
13
+ // override an individual key (e.g. swap the size to "M").
10
14
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
+ const DEFAULT_BUTTON_GROUP_MODES = {
16
+ AppearanceBrand: 'Secondary',
17
+ 'Button / Size': 'S',
18
+ Emphasis: 'High'
19
+ };
20
+
21
+ // Defaults applied to the inner MoneyValue so the price renders at the
22
+ // 36 px / 900-weight scale defined for cards in Figma. Same merge rule as
23
+ // the button group — consumer `modes` override these.
24
+ const DEFAULT_MONEY_VALUE_MODES = {
25
+ Context3: 'Balance & Cards'
26
+ };
11
27
  /**
12
28
  * RechargeCard component implementation from Figma node 2235:937.
13
29
  * Displays a recharge plan with price, validity, data, and subscription details.
@@ -23,17 +39,19 @@ export default function RechargeCard({
23
39
  modes = EMPTY_MODES,
24
40
  style
25
41
  }) {
26
- // Container Tokens
27
- const backgroundColor = getVariableByName('rechargeCard/background', modes) || '#f6f3ff';
42
+ // Container Tokens (defaults mirror Figma node 2235:937).
43
+ const backgroundColor = getVariableByName('rechargeCard/background', modes) || '#ffffff';
28
44
  const paddingHorizontal = parseInt(getVariableByName('rechargeCard/padding/horizontal', modes) || 16, 10);
29
45
  const paddingVertical = parseInt(getVariableByName('rechargeCard/padding/vertical', modes) || 20, 10);
30
46
  const gap = parseInt(getVariableByName('rechargeCard/gap', modes) || 20, 10);
31
47
  const radius = parseInt(getVariableByName('rechargeCard/radius', modes) || 20, 10);
32
- const minWidth = parseInt(getVariableByName('rechargeCard/minWidth', modes) || 328, 10);
48
+ const minWidth = parseInt(getVariableByName('rechargeCard/minWidth', modes) || 312, 10);
49
+ const strokeWidth = parseInt(getVariableByName('rechargeCard/strokeWidth', modes) || 1, 10);
50
+ const strokeColor = getVariableByName('rechargeCard/stroke/color', modes) || '#ebebed';
33
51
 
34
52
  // Header Tokens
35
53
  const headerGap = parseInt(getVariableByName('rechargeCard/header/gap', modes) || 4, 10);
36
- const titleColor = getVariableByName('rechargeCard/title/color', modes) || '#13002d';
54
+ const titleColor = getVariableByName('rechargeCard/title/color', modes) || '#000000';
37
55
  const titleFontSize = parseInt(getVariableByName('rechargeCard/title/fontSize', modes) || 12, 10);
38
56
  const titleFontFamily = getVariableByName('rechargeCard/title/fontFamily', modes) || 'JioType Var';
39
57
  const titleLineHeight = parseInt(getVariableByName('rechargeCard/title/lineHeight', modes) || 14, 10);
@@ -44,30 +62,25 @@ export default function RechargeCard({
44
62
  const specItemGap = parseInt(getVariableByName('rechargeCard/specItem/gap', modes) || 4, 10);
45
63
 
46
64
  // Spec Label Tokens
47
- const specLabelColor = getVariableByName('rechargeCard/specItem/label/color', modes) || '#13002d';
65
+ const specLabelColor = getVariableByName('rechargeCard/specItem/label/color', modes) || '#000000';
48
66
  const specLabelFontSize = parseInt(getVariableByName('rechargeCard/specItem/label/fontSize', modes) || 12, 10);
49
67
  const specLabelFontFamily = getVariableByName('rechargeCard/specItem/label/fontFamily', modes) || 'JioType Var';
50
68
  const specLabelLineHeight = parseInt(getVariableByName('rechargeCard/specItem/label/lineHeight', modes) || 14, 10);
51
69
  const specLabelFontWeight = getVariableByName('rechargeCard/specItem/label/fontWeight', modes) || '500';
52
70
 
53
71
  // Spec Value Tokens
54
- const specValueColor = getVariableByName('rechargeCard/specItem/value/color', modes) || '#310064';
72
+ const specValueColor = getVariableByName('rechargeCard/specItem/value/color', modes) || '#000000';
55
73
  const specValueFontSize = parseInt(getVariableByName('rechargeCard/specItem/value/fontSize', modes) || 14, 10);
56
74
  const specValueFontFamily = getVariableByName('rechargeCard/specItem/value/fontFamily', modes) || 'JioType Var';
57
75
  const specValueLineHeight = parseInt(getVariableByName('rechargeCard/specItem/value/lineHeight', modes) || 17, 10);
58
76
  const specValueFontWeight = getVariableByName('rechargeCard/specItem/value/fontWeight', modes) || '500';
59
77
 
60
78
  // Disclaimer Tokens
61
- const disclaimerColor = getVariableByName('rechargeCard/disclaimer/color', modes) || '#22004a';
79
+ const disclaimerColor = getVariableByName('rechargeCard/disclaimer/color', modes) || '#000000';
62
80
  const disclaimerFontSize = parseInt(getVariableByName('rechargeCard/disclaimer/fontSize', modes) || 10, 10);
63
81
  const disclaimerFontFamily = getVariableByName('rechargeCard/disclaimer/fontFamily', modes) || 'JioType Var';
64
82
  const disclaimerLineHeight = parseInt(getVariableByName('rechargeCard/disclaimer/lineHeight', modes) || 13, 10);
65
83
  const disclaimerFontWeight = getVariableByName('rechargeCard/disclaimer/fontWeight', modes) || '400';
66
-
67
- // Button Group Tokens
68
- // Handled by ButtonGroup component directly
69
-
70
- // Helpers
71
84
  const resolveFontWeight = weight => typeof weight === 'number' ? weight.toString() : weight;
72
85
 
73
86
  // Pass modes to subscription children (e.g. AvatarGroup)
@@ -80,6 +93,8 @@ export default function RechargeCard({
80
93
  paddingVertical,
81
94
  gap,
82
95
  borderRadius: radius,
96
+ borderWidth: strokeWidth,
97
+ borderColor: strokeColor,
83
98
  minWidth,
84
99
  alignItems: 'flex-start'
85
100
  }, style],
@@ -100,7 +115,10 @@ export default function RechargeCard({
100
115
  }), /*#__PURE__*/_jsx(MoneyValue, {
101
116
  value: price,
102
117
  currency: "\u20B9",
103
- modes: modes
118
+ modes: {
119
+ ...DEFAULT_MONEY_VALUE_MODES,
120
+ ...modes
121
+ }
104
122
  })]
105
123
  }), /*#__PURE__*/_jsxs(View, {
106
124
  style: {
@@ -189,10 +207,8 @@ export default function RechargeCard({
189
207
  children: disclaimer
190
208
  }), /*#__PURE__*/_jsx(ButtonGroup, {
191
209
  modes: {
192
- ...modes,
193
- "Appearance.Brand": "Secondary",
194
- "Button / Size": "S",
195
- "Emphasis": "High"
210
+ ...DEFAULT_BUTTON_GROUP_MODES,
211
+ ...modes
196
212
  },
197
213
  children: actions
198
214
  })]
@@ -4,6 +4,8 @@ import React from 'react';
4
4
  import { Text as RNText } from 'react-native';
5
5
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
6
  import { EMPTY_MODES } from '../../utils/react-utils';
7
+ import Skeleton from '../../skeleton/Skeleton';
8
+ import { useSkeleton } from '../../skeleton/SkeletonGroup';
7
9
  import { jsx as _jsx } from "react/jsx-runtime";
8
10
  const TEXT_ALIGN_MAP = {
9
11
  Left: 'left',
@@ -15,7 +17,10 @@ function Text({
15
17
  textAlign = 'Left',
16
18
  modes = EMPTY_MODES,
17
19
  style,
18
- numberOfLines
20
+ numberOfLines,
21
+ loading,
22
+ skeletonWidth,
23
+ skeletonLines
19
24
  }) {
20
25
  const foreground = getVariableByName('text/foreground', modes) ?? '#000000';
21
26
  const fontFamily = getVariableByName('text/fontFamily', modes) ?? 'JioType';
@@ -23,6 +28,31 @@ function Text({
23
28
  const fontWeight = getVariableByName('text/fontWeight', modes) ?? '500';
24
29
  const lineHeight = getVariableByName('text/lineHeight', modes) ?? 20;
25
30
  const letterSpacing = getVariableByName('text/letterSpacing', modes) ?? -0.5;
31
+
32
+ // Skeleton short-circuit. The hook is always called (stable order); the
33
+ // surrounding group's `active` flag is the default, but an explicit
34
+ // `loading` prop overrides both directions.
35
+ const {
36
+ active: groupActive
37
+ } = useSkeleton();
38
+ const isLoading = loading ?? groupActive;
39
+ if (isLoading) {
40
+ const resolvedFontSize = fontSize;
41
+ const resolvedLineHeight = lineHeight;
42
+ const lines = Math.max(1, skeletonLines ?? numberOfLines ?? 1);
43
+ // Heuristic width based on the actual content length so the placeholder
44
+ // visually matches what's about to load. Capped to a reasonable lower
45
+ // bound so very short labels still produce a visible block.
46
+ const contentLength = typeof children === 'string' ? children.length : typeof text === 'string' ? text.length : 12;
47
+ const charWidth = resolvedFontSize * 0.55;
48
+ const computedWidth = Math.max(charWidth * 4, contentLength * charWidth);
49
+ return /*#__PURE__*/_jsx(Skeleton, {
50
+ kind: "text",
51
+ width: skeletonWidth ?? computedWidth,
52
+ height: resolvedLineHeight * lines,
53
+ modes: modes
54
+ });
55
+ }
26
56
  const textStyle = {
27
57
  color: foreground,
28
58
  fontFamily: fontFamily,
@@ -48,6 +48,7 @@ export { default as LinearMeter } from './LinearMeter/LinearMeter';
48
48
  export { default as LinearProgress } from './LinearProgress/LinearProgress';
49
49
  export { default as ListGroup } from './ListGroup/ListGroup';
50
50
  export { default as LottieIntroBlock } from './LottieIntroBlock/LottieIntroBlock';
51
+ export { default as LottiePlayer } from './LottiePlayer/LottiePlayer';
51
52
  export { default as ListItem } from './ListItem/ListItem';
52
53
  export { default as MediaCard } from './MediaCard/MediaCard';
53
54
  export { default as MerchantProfile } from './MerchantProfile/MerchantProfile';