jfs-components 0.0.73 → 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.
- package/CHANGELOG.md +115 -6
- package/lib/commonjs/components/AccountCard/AccountCard.js +247 -0
- package/lib/commonjs/components/ActionFooter/ActionFooter.js +147 -82
- package/lib/commonjs/components/AppBar/AppBar.js +17 -11
- package/lib/commonjs/components/Avatar/Avatar.js +20 -0
- package/lib/commonjs/components/Badge/Badge.js +23 -0
- package/lib/commonjs/components/Button/Button.js +37 -0
- package/lib/commonjs/components/CardBankAccount/CardBankAccount.js +18 -2
- package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +40 -25
- package/lib/commonjs/components/Dropdown/Dropdown.js +214 -0
- package/lib/commonjs/components/DropdownInput/DropdownInput.js +542 -0
- package/lib/commonjs/components/FormField/FormField.js +328 -178
- package/lib/commonjs/components/IconButton/IconButton.js +20 -0
- package/lib/commonjs/components/Image/Image.js +26 -1
- package/lib/commonjs/components/LottieIntroBlock/LottieIntroBlock.js +150 -0
- package/lib/commonjs/components/LottiePlayer/LottiePlayer.js +116 -0
- package/lib/commonjs/components/LottiePlayer/LottiePlayer.web.js +82 -0
- package/lib/commonjs/components/LottiePlayer/loadNativeLottieView.js +74 -0
- package/lib/commonjs/components/LottiePlayer/loadWebLottieView.js +50 -0
- package/lib/commonjs/components/PageHero/PageHero.js +189 -0
- package/lib/commonjs/components/PoweredByLabel/PoweredByLabel.js +135 -0
- package/lib/commonjs/components/PoweredByLabel/finvu.png +0 -0
- package/lib/commonjs/components/RechargeCard/RechargeCard.js +32 -17
- package/lib/commonjs/components/Text/Text.js +40 -3
- package/lib/commonjs/components/Tooltip/Tooltip.js +34 -27
- package/lib/commonjs/components/index.js +67 -0
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/icons/Icon.js +16 -0
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/commonjs/index.js +12 -0
- package/lib/commonjs/skeleton/Skeleton.js +234 -0
- package/lib/commonjs/skeleton/SkeletonGroup.js +140 -0
- package/lib/commonjs/skeleton/index.js +58 -0
- package/lib/commonjs/skeleton/shimmer-tokens.js +189 -0
- package/lib/commonjs/skeleton/useReducedMotion.js +64 -0
- package/lib/module/components/AccountCard/AccountCard.js +241 -0
- package/lib/module/components/ActionFooter/ActionFooter.js +146 -82
- package/lib/module/components/AppBar/AppBar.js +17 -11
- package/lib/module/components/Avatar/Avatar.js +19 -0
- package/lib/module/components/Badge/Badge.js +23 -0
- package/lib/module/components/Button/Button.js +37 -0
- package/lib/module/components/CardBankAccount/CardBankAccount.js +17 -2
- package/lib/module/components/CheckboxItem/CheckboxItem.js +41 -26
- package/lib/module/components/Dropdown/Dropdown.js +206 -0
- package/lib/module/components/DropdownInput/DropdownInput.js +536 -0
- package/lib/module/components/FormField/FormField.js +330 -180
- package/lib/module/components/IconButton/IconButton.js +20 -0
- package/lib/module/components/Image/Image.js +25 -1
- package/lib/module/components/LottieIntroBlock/LottieIntroBlock.js +144 -0
- package/lib/module/components/LottiePlayer/LottiePlayer.js +111 -0
- package/lib/module/components/LottiePlayer/LottiePlayer.web.js +77 -0
- package/lib/module/components/LottiePlayer/loadNativeLottieView.js +69 -0
- package/lib/module/components/LottiePlayer/loadWebLottieView.js +45 -0
- package/lib/module/components/PageHero/PageHero.js +183 -0
- package/lib/module/components/PoweredByLabel/PoweredByLabel.js +130 -0
- package/lib/module/components/PoweredByLabel/finvu.png +0 -0
- package/lib/module/components/RechargeCard/RechargeCard.js +33 -17
- package/lib/module/components/Text/Text.js +40 -3
- package/lib/module/components/Tooltip/Tooltip.js +34 -27
- package/lib/module/components/index.js +8 -1
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/icons/Icon.js +16 -0
- package/lib/module/icons/registry.js +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/skeleton/Skeleton.js +229 -0
- package/lib/module/skeleton/SkeletonGroup.js +133 -0
- package/lib/module/skeleton/index.js +6 -0
- package/lib/module/skeleton/shimmer-tokens.js +181 -0
- package/lib/module/skeleton/useReducedMotion.js +61 -0
- package/lib/typescript/src/components/AccountCard/AccountCard.d.ts +81 -0
- package/lib/typescript/src/components/ActionFooter/ActionFooter.d.ts +26 -21
- package/lib/typescript/src/components/Avatar/Avatar.d.ts +7 -1
- package/lib/typescript/src/components/Badge/Badge.d.ts +7 -1
- package/lib/typescript/src/components/Button/Button.d.ts +8 -1
- package/lib/typescript/src/components/CardBankAccount/CardBankAccount.d.ts +9 -2
- package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +18 -2
- package/lib/typescript/src/components/Dropdown/Dropdown.d.ts +62 -0
- package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +107 -0
- package/lib/typescript/src/components/FormField/FormField.d.ts +76 -19
- package/lib/typescript/src/components/IconButton/IconButton.d.ts +7 -1
- package/lib/typescript/src/components/Image/Image.d.ts +8 -1
- package/lib/typescript/src/components/LottieIntroBlock/LottieIntroBlock.d.ts +58 -0
- package/lib/typescript/src/components/LottiePlayer/LottiePlayer.d.ts +85 -0
- package/lib/typescript/src/components/LottiePlayer/LottiePlayer.web.d.ts +28 -0
- package/lib/typescript/src/components/LottiePlayer/loadNativeLottieView.d.ts +11 -0
- package/lib/typescript/src/components/LottiePlayer/loadWebLottieView.d.ts +11 -0
- package/lib/typescript/src/components/PageHero/PageHero.d.ts +79 -0
- package/lib/typescript/src/components/PoweredByLabel/PoweredByLabel.d.ts +70 -0
- package/lib/typescript/src/components/Text/Text.d.ts +31 -2
- package/lib/typescript/src/components/Tooltip/Tooltip.d.ts +13 -2
- package/lib/typescript/src/components/index.d.ts +8 -1
- package/lib/typescript/src/icons/Icon.d.ts +7 -1
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/skeleton/Skeleton.d.ts +60 -0
- package/lib/typescript/src/skeleton/SkeletonGroup.d.ts +78 -0
- package/lib/typescript/src/skeleton/index.d.ts +5 -0
- package/lib/typescript/src/skeleton/shimmer-tokens.d.ts +160 -0
- package/lib/typescript/src/skeleton/useReducedMotion.d.ts +15 -0
- package/package.json +11 -3
- package/src/components/AccountCard/AccountCard.tsx +376 -0
- package/src/components/ActionFooter/ActionFooter.tsx +152 -86
- package/src/components/AppBar/AppBar.tsx +25 -14
- package/src/components/Avatar/Avatar.tsx +26 -0
- package/src/components/Badge/Badge.tsx +27 -0
- package/src/components/Button/Button.tsx +40 -0
- package/src/components/CardBankAccount/CardBankAccount.tsx +29 -3
- package/src/components/CheckboxItem/CheckboxItem.tsx +65 -30
- package/src/components/Dropdown/Dropdown.tsx +331 -0
- package/src/components/DropdownInput/DropdownInput.tsx +819 -0
- package/src/components/FormField/FormField.tsx +542 -215
- package/src/components/IconButton/IconButton.tsx +27 -0
- package/src/components/Image/Image.tsx +25 -0
- package/src/components/LottieIntroBlock/LottieIntroBlock.tsx +202 -0
- package/src/components/LottiePlayer/LottiePlayer.tsx +145 -0
- package/src/components/LottiePlayer/LottiePlayer.web.tsx +94 -0
- package/src/components/LottiePlayer/loadNativeLottieView.tsx +87 -0
- package/src/components/LottiePlayer/loadWebLottieView.tsx +64 -0
- package/src/components/PageHero/PageHero.tsx +257 -0
- package/src/components/PoweredByLabel/PoweredByLabel.tsx +221 -0
- package/src/components/PoweredByLabel/finvu.png +0 -0
- package/src/components/RechargeCard/RechargeCard.tsx +32 -24
- package/src/components/Text/Text.tsx +78 -3
- package/src/components/Tooltip/Tooltip.tsx +50 -25
- package/src/components/index.ts +16 -1
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- package/src/icons/Icon.tsx +17 -0
- package/src/icons/registry.ts +1 -1
- package/src/index.ts +1 -0
- package/src/skeleton/Skeleton.tsx +298 -0
- package/src/skeleton/SkeletonGroup.tsx +193 -0
- package/src/skeleton/index.ts +10 -0
- package/src/skeleton/shimmer-tokens.ts +221 -0
- package/src/skeleton/useReducedMotion.ts +72 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React, { useMemo } from 'react';
|
|
4
|
+
import { View, Text } from 'react-native';
|
|
5
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
+
import { useTokens } from '../../design-tokens/JFSThemeProvider';
|
|
7
|
+
import Button from '../Button/Button';
|
|
8
|
+
import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
|
|
9
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
const DEFAULT_MEDIA_SIZE = 117;
|
|
11
|
+
/**
|
|
12
|
+
* LottieIntroBlock displays a centered onboarding/intro block composed of a
|
|
13
|
+
* media slot (typically a Lottie animation or illustration) above a title,
|
|
14
|
+
* an optional supportive paragraph, and an optional action button.
|
|
15
|
+
*
|
|
16
|
+
* All visual values are resolved from Figma design tokens via
|
|
17
|
+
* `getVariableByName`. Slots cascade the active `modes` to their children
|
|
18
|
+
* through `cloneChildrenWithModes`.
|
|
19
|
+
*
|
|
20
|
+
* @component
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* <LottieIntroBlock
|
|
24
|
+
* title="Let's get to know how your financial health is doing"
|
|
25
|
+
* supportText="From assets to taxes, stay on top of everything in one simple view."
|
|
26
|
+
* buttonLabel="Get started"
|
|
27
|
+
* onButtonPress={() => navigate('NextScreen')}
|
|
28
|
+
* media={<MyLottiePlayer source={animationSource} />}
|
|
29
|
+
* />
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function LottieIntroBlock({
|
|
33
|
+
title = "Let's get to know how your financial health is doing",
|
|
34
|
+
showSupportText = true,
|
|
35
|
+
supportText = 'From assets to taxes, stay on top of everything in one simple view.',
|
|
36
|
+
showButton = true,
|
|
37
|
+
buttonLabel = 'Button',
|
|
38
|
+
onButtonPress,
|
|
39
|
+
media,
|
|
40
|
+
buttonSlot,
|
|
41
|
+
modes: propModes = EMPTY_MODES,
|
|
42
|
+
style,
|
|
43
|
+
testID
|
|
44
|
+
}) {
|
|
45
|
+
const {
|
|
46
|
+
modes: globalModes
|
|
47
|
+
} = useTokens();
|
|
48
|
+
const modes = useMemo(() => ({
|
|
49
|
+
...globalModes,
|
|
50
|
+
...propModes
|
|
51
|
+
}), [globalModes, propModes]);
|
|
52
|
+
|
|
53
|
+
// Container
|
|
54
|
+
const gap = Number(getVariableByName('lottieIntroBlock/gap', modes)) || 36;
|
|
55
|
+
const paddingHorizontal = Number(getVariableByName('lottieIntroBlock/padding/horizontal', modes)) || 0;
|
|
56
|
+
const paddingVertical = Number(getVariableByName('lottieIntroBlock/padding/vertical', modes)) || 16;
|
|
57
|
+
|
|
58
|
+
// Text wrap
|
|
59
|
+
const textWrapGap = Number(getVariableByName('lottieIntroBlock/textWrap/gap', modes)) || 16;
|
|
60
|
+
|
|
61
|
+
// Title
|
|
62
|
+
const titleColor = getVariableByName('lottieIntroBlock/title/foreground', modes) || '#0d0d0f';
|
|
63
|
+
const titleFontSize = Number(getVariableByName('lottieIntroBlock/title/fontSize', modes)) || 23;
|
|
64
|
+
const titleFontFamily = getVariableByName('lottieIntroBlock/title/fontFamily', modes) || 'System';
|
|
65
|
+
const titleLineHeight = Number(getVariableByName('lottieIntroBlock/title/lineHeight', modes)) || 23;
|
|
66
|
+
const titleFontWeight = getVariableByName('lottieIntroBlock/title/fontWeight', modes) || 900;
|
|
67
|
+
|
|
68
|
+
// Support text
|
|
69
|
+
const supportColor = getVariableByName('lottieIntroBlock/supportText/foreground', modes) || '#0d0d0f';
|
|
70
|
+
const supportFontSize = Number(getVariableByName('lottieIntroBlock/supportText/fontSize', modes)) || 14;
|
|
71
|
+
const supportFontFamily = getVariableByName('lottieIntroBlock/supportText/fontFamily', modes) || 'System';
|
|
72
|
+
const supportLineHeight = Number(getVariableByName('lottieIntroBlock/supportText/lineHeight', modes)) || 18;
|
|
73
|
+
const supportFontWeight = getVariableByName('lottieIntroBlock/supportText/fontWeight', modes) || 400;
|
|
74
|
+
const containerStyle = {
|
|
75
|
+
flexDirection: 'column',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
paddingHorizontal,
|
|
78
|
+
paddingVertical,
|
|
79
|
+
gap
|
|
80
|
+
};
|
|
81
|
+
const textWrapStyle = {
|
|
82
|
+
flexDirection: 'column',
|
|
83
|
+
alignItems: 'center',
|
|
84
|
+
gap: textWrapGap,
|
|
85
|
+
width: '100%'
|
|
86
|
+
};
|
|
87
|
+
const titleStyle = {
|
|
88
|
+
color: titleColor,
|
|
89
|
+
fontSize: titleFontSize,
|
|
90
|
+
fontFamily: titleFontFamily,
|
|
91
|
+
lineHeight: titleLineHeight,
|
|
92
|
+
fontWeight: String(titleFontWeight),
|
|
93
|
+
textAlign: 'center'
|
|
94
|
+
};
|
|
95
|
+
const supportTextStyle = {
|
|
96
|
+
color: supportColor,
|
|
97
|
+
fontSize: supportFontSize,
|
|
98
|
+
fontFamily: supportFontFamily,
|
|
99
|
+
lineHeight: supportLineHeight,
|
|
100
|
+
fontWeight: String(supportFontWeight),
|
|
101
|
+
textAlign: 'center'
|
|
102
|
+
};
|
|
103
|
+
const mediaContent = useMemo(() => {
|
|
104
|
+
if (media === undefined || media === null) {
|
|
105
|
+
return /*#__PURE__*/_jsx(View, {
|
|
106
|
+
style: {
|
|
107
|
+
width: DEFAULT_MEDIA_SIZE,
|
|
108
|
+
height: DEFAULT_MEDIA_SIZE
|
|
109
|
+
},
|
|
110
|
+
accessibilityElementsHidden: true,
|
|
111
|
+
importantForAccessibility: "no-hide-descendants"
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return cloneChildrenWithModes(media, modes);
|
|
115
|
+
}, [media, modes]);
|
|
116
|
+
const buttonContent = useMemo(() => {
|
|
117
|
+
if (buttonSlot !== undefined && buttonSlot !== null) {
|
|
118
|
+
return cloneChildrenWithModes(buttonSlot, modes);
|
|
119
|
+
}
|
|
120
|
+
if (!showButton) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return /*#__PURE__*/_jsx(Button, {
|
|
124
|
+
label: buttonLabel,
|
|
125
|
+
onPress: onButtonPress,
|
|
126
|
+
modes: modes
|
|
127
|
+
});
|
|
128
|
+
}, [buttonSlot, showButton, buttonLabel, onButtonPress, modes]);
|
|
129
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
130
|
+
style: [containerStyle, style],
|
|
131
|
+
testID: testID,
|
|
132
|
+
children: [mediaContent, /*#__PURE__*/_jsxs(View, {
|
|
133
|
+
style: textWrapStyle,
|
|
134
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
135
|
+
style: titleStyle,
|
|
136
|
+
children: title
|
|
137
|
+
}), showSupportText && supportText ? /*#__PURE__*/_jsx(Text, {
|
|
138
|
+
style: supportTextStyle,
|
|
139
|
+
children: supportText
|
|
140
|
+
}) : null, buttonContent]
|
|
141
|
+
})]
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
export default LottieIntroBlock;
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React, { useMemo } from 'react';
|
|
4
|
+
import { View, Text } from 'react-native';
|
|
5
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
+
import { useTokens } from '../../design-tokens/JFSThemeProvider';
|
|
7
|
+
import Button from '../Button/Button';
|
|
8
|
+
import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
|
|
9
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
/**
|
|
11
|
+
* PageHero displays a centered hero block typically used at the top of a page
|
|
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.
|
|
16
|
+
*
|
|
17
|
+
* All visual values are resolved from Figma design tokens via
|
|
18
|
+
* `getVariableByName`. Slots cascade the active `modes` to their children
|
|
19
|
+
* through `cloneChildrenWithModes`.
|
|
20
|
+
*
|
|
21
|
+
* @component
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* <PageHero
|
|
25
|
+
* eyebrow="Upgrade to JioFinance+"
|
|
26
|
+
* headline="Resume earning cashback, extra points, and 1% gold"
|
|
27
|
+
* supportingText="₹999/year · ₹0 until 2027"
|
|
28
|
+
* buttonLabel="Renew for free"
|
|
29
|
+
* onButtonPress={() => navigate('Upgrade')}
|
|
30
|
+
* media={
|
|
31
|
+
* <Image
|
|
32
|
+
* imageSource={require('./assets/upgrade.png')}
|
|
33
|
+
* width={117}
|
|
34
|
+
* height={117}
|
|
35
|
+
* />
|
|
36
|
+
* }
|
|
37
|
+
* />
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
function PageHero({
|
|
41
|
+
eyebrow = 'Upgrade to JioFinance+',
|
|
42
|
+
headline = 'Resume earning cashback, extra points, and 1% gold',
|
|
43
|
+
supportingText = '₹999/year · ₹0 until 2027',
|
|
44
|
+
showSupportingText = true,
|
|
45
|
+
buttonLabel = 'Renew for free',
|
|
46
|
+
onButtonPress,
|
|
47
|
+
showButton = true,
|
|
48
|
+
buttonSlot,
|
|
49
|
+
media,
|
|
50
|
+
modes: propModes = EMPTY_MODES,
|
|
51
|
+
style,
|
|
52
|
+
testID
|
|
53
|
+
}) {
|
|
54
|
+
const {
|
|
55
|
+
modes: globalModes
|
|
56
|
+
} = useTokens();
|
|
57
|
+
const modes = useMemo(() => ({
|
|
58
|
+
...globalModes,
|
|
59
|
+
...propModes
|
|
60
|
+
}), [globalModes, propModes]);
|
|
61
|
+
const gap = Number(getVariableByName('PageHero/gap', modes)) || 16;
|
|
62
|
+
const paddingHorizontal = Number(getVariableByName('PageHero/padding/horizontal', modes)) || 0;
|
|
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;
|
|
70
|
+
const eyebrowColor = getVariableByName('PageHero/eyebrow/color', modes) || '#ffffff';
|
|
71
|
+
const eyebrowFontFamily = getVariableByName('PageHero/eyebrow/fontFamily', modes) || 'System';
|
|
72
|
+
const eyebrowFontSize = Number(getVariableByName('PageHero/eyebrow/fontSize', modes)) || 18;
|
|
73
|
+
const eyebrowFontWeight = getVariableByName('PageHero/eyebrow/fontWeight', modes) || 700;
|
|
74
|
+
const eyebrowLineHeight = Number(getVariableByName('PageHero/eyebrow/lineHeight', modes)) || 20;
|
|
75
|
+
const headlineColor = getVariableByName('PageHero/headline/color', modes) || '#ffffff';
|
|
76
|
+
const headlineFontFamily = getVariableByName('PageHero/headline/fontFamily', modes) || 'System';
|
|
77
|
+
const headlineFontSize = Number(getVariableByName('PageHero/headline/fontSize', modes)) || 29;
|
|
78
|
+
const headlineFontWeight = getVariableByName('PageHero/headline/fontWeight', modes) || 900;
|
|
79
|
+
const headlineLineHeight = Number(getVariableByName('PageHero/headline/lineHeight', modes)) || 29;
|
|
80
|
+
|
|
81
|
+
// Only `lineHeight` is tokenized for the supporting text in the Figma source.
|
|
82
|
+
// Color, font size and weight are inline literals in the design (12px medium
|
|
83
|
+
// white) — we mirror that here so the visual stays faithful when no token
|
|
84
|
+
// exists.
|
|
85
|
+
const supportingTextLineHeight = Number(getVariableByName('PageHero/supportingText/lineHeight', modes)) || 16;
|
|
86
|
+
const containerStyle = {
|
|
87
|
+
flexDirection: 'column',
|
|
88
|
+
alignItems: 'center',
|
|
89
|
+
paddingHorizontal,
|
|
90
|
+
gap,
|
|
91
|
+
width: '100%'
|
|
92
|
+
};
|
|
93
|
+
const textWrapStyle = {
|
|
94
|
+
flexDirection: 'column',
|
|
95
|
+
alignItems: 'center',
|
|
96
|
+
gap: textWrapGap,
|
|
97
|
+
width: '100%'
|
|
98
|
+
};
|
|
99
|
+
const eyebrowStyle = {
|
|
100
|
+
color: eyebrowColor,
|
|
101
|
+
fontFamily: eyebrowFontFamily,
|
|
102
|
+
fontSize: eyebrowFontSize,
|
|
103
|
+
fontWeight: String(eyebrowFontWeight),
|
|
104
|
+
lineHeight: eyebrowLineHeight,
|
|
105
|
+
textAlign: 'center'
|
|
106
|
+
};
|
|
107
|
+
const headlineStyle = {
|
|
108
|
+
color: headlineColor,
|
|
109
|
+
fontFamily: headlineFontFamily,
|
|
110
|
+
fontSize: headlineFontSize,
|
|
111
|
+
fontWeight: String(headlineFontWeight),
|
|
112
|
+
lineHeight: headlineLineHeight,
|
|
113
|
+
textAlign: 'center',
|
|
114
|
+
width: '100%'
|
|
115
|
+
};
|
|
116
|
+
const supportingTextStyle = {
|
|
117
|
+
color: '#ffffff',
|
|
118
|
+
fontFamily: 'System',
|
|
119
|
+
fontSize: 12,
|
|
120
|
+
fontWeight: '500',
|
|
121
|
+
lineHeight: supportingTextLineHeight,
|
|
122
|
+
textAlign: 'center'
|
|
123
|
+
};
|
|
124
|
+
const buttonWrapStyle = {
|
|
125
|
+
width: '100%'
|
|
126
|
+
};
|
|
127
|
+
const buttonContent = useMemo(() => {
|
|
128
|
+
if (buttonSlot !== undefined && buttonSlot !== null) {
|
|
129
|
+
return cloneChildrenWithModes(buttonSlot, modes);
|
|
130
|
+
}
|
|
131
|
+
if (!showButton) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return /*#__PURE__*/_jsx(Button, {
|
|
135
|
+
label: buttonLabel,
|
|
136
|
+
onPress: onButtonPress,
|
|
137
|
+
modes: modes,
|
|
138
|
+
style: buttonWrapStyle
|
|
139
|
+
});
|
|
140
|
+
// buttonWrapStyle is a literal object created above; it is intentionally
|
|
141
|
+
// omitted from deps because its identity changes on every render but its
|
|
142
|
+
// shape never does.
|
|
143
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
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]);
|
|
165
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
166
|
+
style: [containerStyle, style],
|
|
167
|
+
testID: testID,
|
|
168
|
+
children: [mediaContent, /*#__PURE__*/_jsxs(View, {
|
|
169
|
+
style: textWrapStyle,
|
|
170
|
+
children: [eyebrow ? /*#__PURE__*/_jsx(Text, {
|
|
171
|
+
style: eyebrowStyle,
|
|
172
|
+
children: eyebrow
|
|
173
|
+
}) : null, headline ? /*#__PURE__*/_jsx(Text, {
|
|
174
|
+
style: headlineStyle,
|
|
175
|
+
children: headline
|
|
176
|
+
}) : null]
|
|
177
|
+
}), showSupportingText && supportingText ? /*#__PURE__*/_jsx(Text, {
|
|
178
|
+
style: supportingTextStyle,
|
|
179
|
+
children: supportingText
|
|
180
|
+
}) : null, buttonContent]
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
export default PageHero;
|