jfs-components 0.0.44 → 0.0.45
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/lib/commonjs/components/AmountInput/AmountInput.js +82 -0
- package/lib/commonjs/components/AmountInput/index.js +13 -0
- package/lib/commonjs/components/Button/Button.js +31 -28
- package/lib/commonjs/components/CardProviderInfo/CardProviderInfo.js +76 -0
- package/lib/commonjs/components/EmptyState/EmptyState.js +2 -1
- package/lib/commonjs/components/OTP/OTP.js +242 -0
- package/lib/commonjs/components/PortfolioHero/PortfolioHero.js +78 -0
- package/lib/commonjs/components/ProductLabel/ProductLabel.js +50 -0
- package/lib/commonjs/components/ProgressBadge/ProgressBadge.js +130 -0
- package/lib/commonjs/components/ProgressBadge/index.js +25 -0
- package/lib/commonjs/components/StatItem/StatItem.js +61 -0
- package/lib/commonjs/components/SwappableAmount/SwappableAmount.js +71 -0
- package/lib/commonjs/components/Text/Text.js +38 -0
- package/lib/commonjs/components/Toggle/Toggle.js +102 -0
- package/lib/commonjs/components/index.js +63 -0
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -0
- package/lib/commonjs/design-tokens/figma-variables-resolver.js +1 -1
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/AmountInput/AmountInput.js +77 -0
- package/lib/module/components/AmountInput/index.js +3 -0
- package/lib/module/components/Button/Button.js +31 -28
- package/lib/module/components/CardProviderInfo/CardProviderInfo.js +71 -0
- package/lib/module/components/EmptyState/EmptyState.js +2 -1
- package/lib/module/components/OTP/OTP.js +236 -0
- package/lib/module/components/PortfolioHero/PortfolioHero.js +73 -0
- package/lib/module/components/ProductLabel/ProductLabel.js +45 -0
- package/lib/module/components/ProgressBadge/ProgressBadge.js +125 -0
- package/lib/module/components/ProgressBadge/index.js +4 -0
- package/lib/module/components/StatItem/StatItem.js +56 -0
- package/lib/module/components/SwappableAmount/SwappableAmount.js +66 -0
- package/lib/module/components/Text/Text.js +33 -0
- package/lib/module/components/Toggle/Toggle.js +97 -0
- package/lib/module/components/index.js +10 -1
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -0
- package/lib/module/design-tokens/figma-variables-resolver.js +1 -1
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/AmountInput/AmountInput.d.ts +23 -0
- package/lib/typescript/src/components/AmountInput/index.d.ts +3 -0
- package/lib/typescript/src/components/CardProviderInfo/CardProviderInfo.d.ts +24 -0
- package/lib/typescript/src/components/EmptyState/EmptyState.d.ts +6 -1
- package/lib/typescript/src/components/OTP/OTP.d.ts +36 -0
- package/lib/typescript/src/components/PortfolioHero/PortfolioHero.d.ts +21 -0
- package/lib/typescript/src/components/ProductLabel/ProductLabel.d.ts +14 -0
- package/lib/typescript/src/components/ProgressBadge/ProgressBadge.d.ts +36 -0
- package/lib/typescript/src/components/ProgressBadge/index.d.ts +3 -0
- package/lib/typescript/src/components/StatItem/StatItem.d.ts +21 -0
- package/lib/typescript/src/components/SwappableAmount/SwappableAmount.d.ts +22 -0
- package/lib/typescript/src/components/Text/Text.d.ts +14 -0
- package/lib/typescript/src/components/Toggle/Toggle.d.ts +29 -0
- package/lib/typescript/src/components/index.d.ts +9 -0
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/AmountInput/AmountInput.tsx +81 -0
- package/src/components/AmountInput/index.ts +2 -0
- package/src/components/Button/Button.tsx +27 -20
- package/src/components/CardProviderInfo/CardProviderInfo.tsx +81 -0
- package/src/components/EmptyState/EmptyState.tsx +7 -1
- package/src/components/OTP/OTP.tsx +275 -0
- package/src/components/PortfolioHero/PortfolioHero.tsx +91 -0
- package/src/components/ProductLabel/ProductLabel.tsx +58 -0
- package/src/components/ProgressBadge/ProgressBadge.tsx +172 -0
- package/src/components/ProgressBadge/index.ts +2 -0
- package/src/components/StatItem/StatItem.tsx +71 -0
- package/src/components/SwappableAmount/SwappableAmount.tsx +92 -0
- package/src/components/Text/Text.tsx +48 -0
- package/src/components/Toggle/Toggle.tsx +122 -0
- package/src/components/index.ts +9 -0
- package/src/design-tokens/Coin Variables-variables-full.json +1 -0
- package/src/design-tokens/figma-variables-resolver.ts +1 -1
- package/src/icons/registry.ts +1 -1
- package/lib/commonjs/design-tokens/JFS Variables-variables-full.json +0 -1
- package/lib/module/design-tokens/JFS Variables-variables-full.json +0 -1
- package/src/design-tokens/JFS Variables-variables-full.json +0 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React 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 MoneyValue from '../MoneyValue/MoneyValue';
|
|
8
|
+
import NoteInput from '../NoteInput/NoteInput';
|
|
9
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
/**
|
|
11
|
+
* AmountInput component that combines MoneyValue and NoteInput from Figma design.
|
|
12
|
+
*
|
|
13
|
+
* @component
|
|
14
|
+
*/
|
|
15
|
+
export default function AmountInput({
|
|
16
|
+
moneyValueSlot,
|
|
17
|
+
noteInputSlot,
|
|
18
|
+
modes: propModes = {},
|
|
19
|
+
style
|
|
20
|
+
}) {
|
|
21
|
+
const {
|
|
22
|
+
modes: globalModes
|
|
23
|
+
} = useTokens();
|
|
24
|
+
const modes = {
|
|
25
|
+
...globalModes,
|
|
26
|
+
...propModes
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Resolve tokens
|
|
30
|
+
const gap = Number(getVariableByName('amountInput/gap', modes)) || 16;
|
|
31
|
+
const paddingHorizontal = Number(getVariableByName('amountInput/padding/horizontal', modes)) || 0;
|
|
32
|
+
const paddingVertical = Number(getVariableByName('amountInput/padding/vertical', modes)) || 0;
|
|
33
|
+
const containerStyle = {
|
|
34
|
+
flexDirection: 'column',
|
|
35
|
+
alignItems: 'center',
|
|
36
|
+
gap,
|
|
37
|
+
paddingHorizontal,
|
|
38
|
+
paddingVertical,
|
|
39
|
+
...style
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Handle MoneyValue Slot
|
|
43
|
+
const renderMoneyValueSlot = () => {
|
|
44
|
+
if (/*#__PURE__*/React.isValidElement(moneyValueSlot)) {
|
|
45
|
+
return /*#__PURE__*/React.cloneElement(moneyValueSlot, {
|
|
46
|
+
modes
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// Default fallback if no slot prop is provided
|
|
50
|
+
if (!moneyValueSlot) {
|
|
51
|
+
return /*#__PURE__*/_jsx(MoneyValue, {
|
|
52
|
+
modes: modes
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return moneyValueSlot;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Handle NoteInput Slot
|
|
59
|
+
const renderNoteInputSlot = () => {
|
|
60
|
+
if (/*#__PURE__*/React.isValidElement(noteInputSlot)) {
|
|
61
|
+
return /*#__PURE__*/React.cloneElement(noteInputSlot, {
|
|
62
|
+
modes
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Default fallback if no slot prop is provided
|
|
66
|
+
if (!noteInputSlot) {
|
|
67
|
+
return /*#__PURE__*/_jsx(NoteInput, {
|
|
68
|
+
modes: modes
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return noteInputSlot;
|
|
72
|
+
};
|
|
73
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
74
|
+
style: containerStyle,
|
|
75
|
+
children: [renderMoneyValueSlot(), renderNoteInputSlot()]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -59,8 +59,28 @@ function Button({
|
|
|
59
59
|
const textColor = getVariableByName('button/foreground', modes) || '#0f0d0a';
|
|
60
60
|
const iconColor = getVariableByName('button/icon/color', modes) ?? textColor;
|
|
61
61
|
const iconSize = getVariableByName('button/icon/size', modes) ?? 18;
|
|
62
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
63
|
+
const [isPressed, setIsPressed] = useState(false);
|
|
64
|
+
const hoverModes = {
|
|
65
|
+
...modes,
|
|
66
|
+
"Button / State": "Hover"
|
|
67
|
+
};
|
|
68
|
+
const hoverBg = getVariableByName('button/background', hoverModes) || backgroundColor;
|
|
69
|
+
const hoverBorderColor = getVariableByName('button/border/color', hoverModes) || borderColor;
|
|
70
|
+
const hoverTextColor = getVariableByName('button/foreground', hoverModes) || textColor;
|
|
71
|
+
const hoverIconColor = getVariableByName('button/icon/color', hoverModes) ?? hoverTextColor;
|
|
72
|
+
const pressedModes = {
|
|
73
|
+
...modes,
|
|
74
|
+
"Button / State": "Pressed"
|
|
75
|
+
};
|
|
76
|
+
const pressedBg = getVariableByName('button/background', pressedModes) || backgroundColor;
|
|
77
|
+
const pressedBorderColor = getVariableByName('button/border/color', pressedModes) || borderColor;
|
|
78
|
+
const pressedTextColor = getVariableByName('button/foreground', pressedModes) || textColor;
|
|
79
|
+
const pressedIconColor = getVariableByName('button/icon/color', pressedModes) ?? pressedTextColor;
|
|
80
|
+
const activeTextColor = isPressed && !disabled ? pressedTextColor : isHovered && !disabled ? hoverTextColor : textColor;
|
|
81
|
+
const activeIconColor = isPressed && !disabled ? pressedIconColor : isHovered && !disabled ? hoverIconColor : iconColor;
|
|
62
82
|
const baseLabelTextStyle = {
|
|
63
|
-
color:
|
|
83
|
+
color: activeTextColor,
|
|
64
84
|
fontFamily,
|
|
65
85
|
fontWeight,
|
|
66
86
|
fontSize,
|
|
@@ -114,22 +134,13 @@ function Button({
|
|
|
114
134
|
accessibilityLabel: defaultAccessibilityLabel,
|
|
115
135
|
webAccessibilityProps
|
|
116
136
|
});
|
|
117
|
-
|
|
118
|
-
// Interaction states (placeholders for visuals; tweak later)
|
|
119
|
-
const [isFocused, setIsFocused] = useState(false);
|
|
120
|
-
const [isHovered, setIsHovered] = useState(false);
|
|
121
|
-
const pressedStyle = {
|
|
122
|
-
transform: [{
|
|
123
|
-
scale: 0.995
|
|
124
|
-
}],
|
|
125
|
-
backgroundColor: '#b88940'
|
|
126
|
-
};
|
|
127
|
-
const focusStyle = {
|
|
128
|
-
borderWidth: 1,
|
|
129
|
-
borderColor: '#222'
|
|
130
|
-
};
|
|
131
137
|
const hoverStyle = {
|
|
132
|
-
|
|
138
|
+
backgroundColor: hoverBg,
|
|
139
|
+
borderColor: hoverBorderColor
|
|
140
|
+
};
|
|
141
|
+
const pressedStyle = {
|
|
142
|
+
backgroundColor: pressedBg,
|
|
143
|
+
borderColor: pressedBorderColor
|
|
133
144
|
};
|
|
134
145
|
if (__DEV__) {
|
|
135
146
|
if (children && labelStyle) {
|
|
@@ -154,21 +165,13 @@ function Button({
|
|
|
154
165
|
onPress
|
|
155
166
|
} : {}),
|
|
156
167
|
onPressIn: e => {
|
|
157
|
-
;
|
|
168
|
+
setIsPressed(true);
|
|
158
169
|
rest?.onPressIn?.(e);
|
|
159
170
|
},
|
|
160
171
|
onPressOut: e => {
|
|
161
|
-
;
|
|
172
|
+
setIsPressed(false);
|
|
162
173
|
rest?.onPressOut?.(e);
|
|
163
174
|
},
|
|
164
|
-
onFocus: e => {
|
|
165
|
-
setIsFocused(true);
|
|
166
|
-
rest?.onFocus?.(e);
|
|
167
|
-
},
|
|
168
|
-
onBlur: e => {
|
|
169
|
-
setIsFocused(false);
|
|
170
|
-
rest?.onBlur?.(e);
|
|
171
|
-
},
|
|
172
175
|
onHoverIn: e => {
|
|
173
176
|
setIsHovered(true);
|
|
174
177
|
rest?.onHoverIn?.(e);
|
|
@@ -179,7 +182,7 @@ function Button({
|
|
|
179
182
|
},
|
|
180
183
|
style: ({
|
|
181
184
|
pressed
|
|
182
|
-
}) => [containerBaseStyle,
|
|
185
|
+
}) => [containerBaseStyle, isHovered && !disabled ? hoverStyle : null, (pressed || isPressed) && !disabled ? pressedStyle : null, style],
|
|
183
186
|
...webProps,
|
|
184
187
|
children: [leading ? /*#__PURE__*/_jsx(View, {
|
|
185
188
|
style: leadingAccessoryStyle,
|
|
@@ -189,7 +192,7 @@ function Button({
|
|
|
189
192
|
children: /*#__PURE__*/_jsx(Icon, {
|
|
190
193
|
name: icon,
|
|
191
194
|
size: iconSize,
|
|
192
|
-
color:
|
|
195
|
+
color: activeIconColor,
|
|
193
196
|
accessibilityElementsHidden: true,
|
|
194
197
|
importantForAccessibility: "no"
|
|
195
198
|
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { View } from 'react-native';
|
|
5
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
+
import ProductLabel from '../ProductLabel/ProductLabel';
|
|
7
|
+
import { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
8
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
+
/**
|
|
10
|
+
* CardProviderInfo displays a product header (ProductLabel) followed by a
|
|
11
|
+
* 2-column grid of children (typically StatItem instances).
|
|
12
|
+
*
|
|
13
|
+
* @component
|
|
14
|
+
* @param {CardProviderInfoProps} props
|
|
15
|
+
*/
|
|
16
|
+
function CardProviderInfo({
|
|
17
|
+
label = 'Gold',
|
|
18
|
+
imageSource,
|
|
19
|
+
children,
|
|
20
|
+
modes = {},
|
|
21
|
+
style
|
|
22
|
+
}) {
|
|
23
|
+
const background = getVariableByName('card/providerInfo/background', modes) ?? '#fef4e5';
|
|
24
|
+
const border = getVariableByName('card/providerInfo/border', modes) ?? '#fef4e5';
|
|
25
|
+
const borderWidthVal = getVariableByName('card/providerInfo/borderWidth', modes) ?? 1;
|
|
26
|
+
const padding = getVariableByName('card/providerInfo/padding', modes) ?? 20;
|
|
27
|
+
const gap = getVariableByName('card/providerInfo/gap', modes) ?? 20;
|
|
28
|
+
const radius = getVariableByName('card/providerInfo/radius', modes) ?? 16;
|
|
29
|
+
const gridGap = getVariableByName('card/providerInfo/grid/gap', modes) ?? 8;
|
|
30
|
+
const containerStyle = {
|
|
31
|
+
backgroundColor: background,
|
|
32
|
+
borderColor: border,
|
|
33
|
+
borderWidth: borderWidthVal,
|
|
34
|
+
borderStyle: 'solid',
|
|
35
|
+
padding: padding,
|
|
36
|
+
gap: gap,
|
|
37
|
+
borderRadius: radius
|
|
38
|
+
};
|
|
39
|
+
const gridGapNum = gridGap;
|
|
40
|
+
const clonedChildren = children ? cloneChildrenWithModes(children, modes) : [];
|
|
41
|
+
const childArray = React.Children.toArray(clonedChildren);
|
|
42
|
+
const rows = [];
|
|
43
|
+
for (let i = 0; i < childArray.length; i += 2) {
|
|
44
|
+
rows.push(childArray.slice(i, i + 2));
|
|
45
|
+
}
|
|
46
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
47
|
+
style: [containerStyle, style],
|
|
48
|
+
children: [/*#__PURE__*/_jsx(ProductLabel, {
|
|
49
|
+
label: label,
|
|
50
|
+
imageSource: imageSource,
|
|
51
|
+
modes: modes
|
|
52
|
+
}), childArray.length > 0 && /*#__PURE__*/_jsx(View, {
|
|
53
|
+
style: {
|
|
54
|
+
rowGap: gridGapNum
|
|
55
|
+
},
|
|
56
|
+
children: rows.map((row, i) => /*#__PURE__*/_jsx(View, {
|
|
57
|
+
style: {
|
|
58
|
+
flexDirection: 'row',
|
|
59
|
+
columnGap: gridGapNum
|
|
60
|
+
},
|
|
61
|
+
children: row.map((child, j) => /*#__PURE__*/_jsx(View, {
|
|
62
|
+
style: {
|
|
63
|
+
flex: 1
|
|
64
|
+
},
|
|
65
|
+
children: child
|
|
66
|
+
}, j))
|
|
67
|
+
}, i))
|
|
68
|
+
})]
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
export default CardProviderInfo;
|
|
@@ -15,6 +15,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
15
15
|
function EmptyState({
|
|
16
16
|
title = "No payments to show",
|
|
17
17
|
description = "Start by paying bills, recharge or your friends",
|
|
18
|
+
showDescription = true,
|
|
18
19
|
iconSlot,
|
|
19
20
|
buttonSlot,
|
|
20
21
|
modes: propModes = {},
|
|
@@ -110,7 +111,7 @@ function EmptyState({
|
|
|
110
111
|
children: [iconContent, /*#__PURE__*/_jsx(Text, {
|
|
111
112
|
style: titleStyle,
|
|
112
113
|
children: title
|
|
113
|
-
}), description ? /*#__PURE__*/_jsx(Text, {
|
|
114
|
+
}), showDescription && description ? /*#__PURE__*/_jsx(Text, {
|
|
114
115
|
style: bodyStyle,
|
|
115
116
|
children: description
|
|
116
117
|
}) : null]
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React, { useState, useRef, useCallback, useEffect } from 'react';
|
|
4
|
+
import { View, Text, TextInput as RNTextInput, Pressable, Animated } from 'react-native';
|
|
5
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
+
import { useTokens } from '../../design-tokens/JFSThemeProvider';
|
|
7
|
+
import SupportText from '../SupportText/SupportText';
|
|
8
|
+
import { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
9
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
10
|
+
const DIGITS_ONLY = /^\d*$/;
|
|
11
|
+
function OTP({
|
|
12
|
+
length = 6,
|
|
13
|
+
value: controlledValue,
|
|
14
|
+
defaultValue = '',
|
|
15
|
+
onChange,
|
|
16
|
+
onValueChange,
|
|
17
|
+
onComplete,
|
|
18
|
+
isDisabled = false,
|
|
19
|
+
isInvalid = false,
|
|
20
|
+
allowedPattern = DIGITS_ONLY,
|
|
21
|
+
autoFocus = false,
|
|
22
|
+
modes: propModes = {},
|
|
23
|
+
style,
|
|
24
|
+
supportText,
|
|
25
|
+
supportTextStatus
|
|
26
|
+
}) {
|
|
27
|
+
const {
|
|
28
|
+
modes: globalModes
|
|
29
|
+
} = useTokens();
|
|
30
|
+
const modes = {
|
|
31
|
+
...globalModes,
|
|
32
|
+
...propModes
|
|
33
|
+
};
|
|
34
|
+
const isControlled = controlledValue !== undefined;
|
|
35
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
36
|
+
const currentValue = isControlled ? controlledValue : internalValue;
|
|
37
|
+
const inputRef = useRef(null);
|
|
38
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
39
|
+
const caretAnim = useRef(new Animated.Value(1)).current;
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!isFocused) return;
|
|
42
|
+
const blink = Animated.loop(Animated.sequence([Animated.timing(caretAnim, {
|
|
43
|
+
toValue: 0,
|
|
44
|
+
duration: 400,
|
|
45
|
+
useNativeDriver: true
|
|
46
|
+
}), Animated.timing(caretAnim, {
|
|
47
|
+
toValue: 1,
|
|
48
|
+
duration: 400,
|
|
49
|
+
useNativeDriver: true
|
|
50
|
+
})]));
|
|
51
|
+
blink.start();
|
|
52
|
+
return () => blink.stop();
|
|
53
|
+
}, [isFocused, caretAnim]);
|
|
54
|
+
const prevCompleteRef = useRef(false);
|
|
55
|
+
const setValue = useCallback(next => {
|
|
56
|
+
const clamped = next.slice(0, length);
|
|
57
|
+
if (!isControlled) setInternalValue(clamped);
|
|
58
|
+
onChange?.(clamped);
|
|
59
|
+
onValueChange?.(clamped);
|
|
60
|
+
if (clamped.length === length && !prevCompleteRef.current) {
|
|
61
|
+
prevCompleteRef.current = true;
|
|
62
|
+
onComplete?.(clamped);
|
|
63
|
+
}
|
|
64
|
+
if (clamped.length < length) {
|
|
65
|
+
prevCompleteRef.current = false;
|
|
66
|
+
}
|
|
67
|
+
}, [length, isControlled, onChange, onValueChange, onComplete]);
|
|
68
|
+
const handleChangeText = useCallback(text => {
|
|
69
|
+
if (isDisabled) return;
|
|
70
|
+
if (!allowedPattern.test(text)) return;
|
|
71
|
+
setValue(text);
|
|
72
|
+
}, [isDisabled, allowedPattern, setValue]);
|
|
73
|
+
const handlePress = useCallback(() => {
|
|
74
|
+
if (isDisabled) return;
|
|
75
|
+
inputRef.current?.focus();
|
|
76
|
+
}, [isDisabled]);
|
|
77
|
+
|
|
78
|
+
// --- Token resolution (state-independent tokens resolved once) ---
|
|
79
|
+
const otpGap = Number(getVariableByName('otp/gap', modes)) || 36;
|
|
80
|
+
const otpPaddingH = Number(getVariableByName('otp/padding/horizontal', modes)) || 8;
|
|
81
|
+
const otpPaddingV = Number(getVariableByName('otp/padding/vertical', modes)) || 8;
|
|
82
|
+
const slotWidth = Number(getVariableByName('pinSlot/width', modes)) || 48;
|
|
83
|
+
const slotGap = Number(getVariableByName('pinSlot/gap', modes)) || 8;
|
|
84
|
+
// digit/color has no state variants in Figma — resolved once from the Output collection
|
|
85
|
+
const digitColor = getVariableByName('pinSlot/digit/color', modes) || '#000000';
|
|
86
|
+
const digitFontSize = Number(getVariableByName('pinSlot/digit/fontSize', modes)) || 24;
|
|
87
|
+
const digitFontFamily = getVariableByName('pinSlot/digit/fontFamily', modes) || 'JioType Var';
|
|
88
|
+
const digitLineHeight = Number(getVariableByName('pinSlot/digit/lineHeight', modes)) || 29;
|
|
89
|
+
const digitFontWeight = getVariableByName('pinSlot/digit/fontWeight', modes) || '500';
|
|
90
|
+
const underlineHeight = Number(getVariableByName('pinSlot/underline/height', modes)) || 2;
|
|
91
|
+
const underlineRadius = Number(getVariableByName('pinSlot/underline/radius', modes)) || 1;
|
|
92
|
+
|
|
93
|
+
// --- State-driven slot modes ---
|
|
94
|
+
// Collection name in Figma is "Input/PINSlot States" (double space before States).
|
|
95
|
+
// Only PinSlot/underline/color (capital P/S) lives in this collection with Idle/Active/Error modes.
|
|
96
|
+
// isInvalid takes priority over active focus; the component maps semantic state → token mode
|
|
97
|
+
// internally so consumers never need to know the collection key name.
|
|
98
|
+
const getSlotModes = isActiveSlot => {
|
|
99
|
+
if (isInvalid) return {
|
|
100
|
+
...modes,
|
|
101
|
+
'Input/PINSlot States': 'Error'
|
|
102
|
+
};
|
|
103
|
+
if (isActiveSlot && isFocused) return {
|
|
104
|
+
...modes,
|
|
105
|
+
'Input/PINSlot States': 'Active'
|
|
106
|
+
};
|
|
107
|
+
return {
|
|
108
|
+
...modes,
|
|
109
|
+
'Input/PINSlot States': 'Idle'
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// --- Styles ---
|
|
114
|
+
const containerStyle = {
|
|
115
|
+
flexDirection: 'column',
|
|
116
|
+
alignItems: 'flex-end',
|
|
117
|
+
gap: otpGap,
|
|
118
|
+
paddingHorizontal: otpPaddingH,
|
|
119
|
+
paddingVertical: otpPaddingV
|
|
120
|
+
};
|
|
121
|
+
const slotWrapStyle = {
|
|
122
|
+
flexDirection: 'row',
|
|
123
|
+
gap: 8,
|
|
124
|
+
alignItems: 'flex-start',
|
|
125
|
+
alignSelf: 'stretch'
|
|
126
|
+
};
|
|
127
|
+
const renderSlot = index => {
|
|
128
|
+
const char = currentValue[index];
|
|
129
|
+
const isActiveSlot = index === currentValue.length && currentValue.length < length;
|
|
130
|
+
const isFilled = char !== undefined;
|
|
131
|
+
|
|
132
|
+
// Underline color is the only state-sensitive token (lives in "Input/PINSlot States" collection).
|
|
133
|
+
// Note: token name is "PinSlot/underline/color" (capital P/S) — different from the static
|
|
134
|
+
// "pinSlot/underline/color" in the Output collection.
|
|
135
|
+
const slotModes = getSlotModes(isActiveSlot);
|
|
136
|
+
const underlineColor = getVariableByName('PinSlot/underline/color', slotModes) || '#303338';
|
|
137
|
+
const slotStyle = {
|
|
138
|
+
width: slotWidth,
|
|
139
|
+
flexDirection: 'column',
|
|
140
|
+
alignItems: 'center',
|
|
141
|
+
justifyContent: 'flex-end',
|
|
142
|
+
gap: slotGap
|
|
143
|
+
};
|
|
144
|
+
const digitStyle = {
|
|
145
|
+
fontFamily: digitFontFamily,
|
|
146
|
+
fontWeight: digitFontWeight,
|
|
147
|
+
fontSize: digitFontSize,
|
|
148
|
+
lineHeight: digitLineHeight,
|
|
149
|
+
color: digitColor,
|
|
150
|
+
textAlign: 'center',
|
|
151
|
+
minWidth: '100%'
|
|
152
|
+
};
|
|
153
|
+
const underlineStyle = {
|
|
154
|
+
width: slotWidth,
|
|
155
|
+
height: underlineHeight,
|
|
156
|
+
borderRadius: underlineRadius,
|
|
157
|
+
backgroundColor: underlineColor
|
|
158
|
+
};
|
|
159
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
160
|
+
style: slotStyle,
|
|
161
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
162
|
+
style: {
|
|
163
|
+
minHeight: digitLineHeight,
|
|
164
|
+
justifyContent: 'center',
|
|
165
|
+
alignItems: 'center',
|
|
166
|
+
width: '100%'
|
|
167
|
+
},
|
|
168
|
+
children: isFilled ? /*#__PURE__*/_jsx(Text, {
|
|
169
|
+
style: digitStyle,
|
|
170
|
+
children: char
|
|
171
|
+
}) : isActiveSlot && isFocused ? /*#__PURE__*/_jsx(Animated.View, {
|
|
172
|
+
style: {
|
|
173
|
+
width: 2,
|
|
174
|
+
height: digitFontSize,
|
|
175
|
+
backgroundColor: digitColor,
|
|
176
|
+
opacity: caretAnim
|
|
177
|
+
}
|
|
178
|
+
}) : /*#__PURE__*/_jsx(Text, {
|
|
179
|
+
style: [digitStyle, {
|
|
180
|
+
color: 'transparent'
|
|
181
|
+
}],
|
|
182
|
+
children: '\u00A0'
|
|
183
|
+
})
|
|
184
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
185
|
+
style: underlineStyle
|
|
186
|
+
})]
|
|
187
|
+
}, index);
|
|
188
|
+
};
|
|
189
|
+
const renderSupportText = () => {
|
|
190
|
+
if (!supportText) return null;
|
|
191
|
+
if (typeof supportText === 'string') {
|
|
192
|
+
return /*#__PURE__*/_jsx(SupportText, {
|
|
193
|
+
label: supportText,
|
|
194
|
+
status: supportTextStatus ?? (isInvalid ? 'Error' : 'Neutral'),
|
|
195
|
+
modes: modes
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return /*#__PURE__*/_jsx(_Fragment, {
|
|
199
|
+
children: cloneChildrenWithModes(React.Children.toArray(supportText), modes)
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
return /*#__PURE__*/_jsxs(Pressable, {
|
|
203
|
+
style: [containerStyle, isDisabled && {
|
|
204
|
+
opacity: 0.4
|
|
205
|
+
}, style],
|
|
206
|
+
onPress: handlePress,
|
|
207
|
+
disabled: isDisabled,
|
|
208
|
+
accessibilityRole: "none",
|
|
209
|
+
children: [/*#__PURE__*/_jsx(RNTextInput, {
|
|
210
|
+
ref: inputRef,
|
|
211
|
+
value: currentValue,
|
|
212
|
+
onChangeText: handleChangeText,
|
|
213
|
+
maxLength: length,
|
|
214
|
+
keyboardType: "number-pad",
|
|
215
|
+
autoFocus: autoFocus,
|
|
216
|
+
editable: !isDisabled,
|
|
217
|
+
onFocus: () => setIsFocused(true),
|
|
218
|
+
onBlur: () => setIsFocused(false),
|
|
219
|
+
caretHidden: true,
|
|
220
|
+
style: {
|
|
221
|
+
position: 'absolute',
|
|
222
|
+
width: 1,
|
|
223
|
+
height: 1,
|
|
224
|
+
opacity: 0
|
|
225
|
+
},
|
|
226
|
+
accessibilityLabel: `OTP input, ${length} digits`,
|
|
227
|
+
accessibilityHint: "Enter your verification code"
|
|
228
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
229
|
+
style: slotWrapStyle,
|
|
230
|
+
children: Array.from({
|
|
231
|
+
length
|
|
232
|
+
}, (_, i) => renderSlot(i))
|
|
233
|
+
}), renderSupportText()]
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
export default OTP;
|
|
@@ -0,0 +1,73 @@
|
|
|
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 { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
7
|
+
import Avatar from '../Avatar/Avatar';
|
|
8
|
+
import MoneyValue from '../MoneyValue/MoneyValue';
|
|
9
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
function PortfolioHero({
|
|
11
|
+
label = 'Gold',
|
|
12
|
+
imageSource,
|
|
13
|
+
value = '12,000.11',
|
|
14
|
+
currency = '₹',
|
|
15
|
+
modes = {
|
|
16
|
+
Context3: 'Balance'
|
|
17
|
+
},
|
|
18
|
+
children,
|
|
19
|
+
style
|
|
20
|
+
}) {
|
|
21
|
+
const gap = getVariableByName('portfolioHero/gap', modes) ?? 16;
|
|
22
|
+
const padding = getVariableByName('portfolioHero/padding', modes) ?? 8;
|
|
23
|
+
const slotWrapGap = getVariableByName('portfolioHero/slotWrap/gap', modes) ?? 8;
|
|
24
|
+
const containerStyle = {
|
|
25
|
+
flexDirection: 'column',
|
|
26
|
+
alignItems: 'center',
|
|
27
|
+
justifyContent: 'center',
|
|
28
|
+
gap: gap,
|
|
29
|
+
padding: padding
|
|
30
|
+
};
|
|
31
|
+
const productLabelStyle = {
|
|
32
|
+
flexDirection: 'row',
|
|
33
|
+
alignItems: 'center',
|
|
34
|
+
gap: 8
|
|
35
|
+
};
|
|
36
|
+
const labelTextStyle = {
|
|
37
|
+
fontFamily: 'System',
|
|
38
|
+
fontWeight: '700',
|
|
39
|
+
fontSize: 16,
|
|
40
|
+
lineHeight: 16 * 1.3,
|
|
41
|
+
color: '#0d0d0f',
|
|
42
|
+
textAlign: 'center'
|
|
43
|
+
};
|
|
44
|
+
const slotWrapStyle = {
|
|
45
|
+
flexDirection: 'column',
|
|
46
|
+
alignItems: 'center',
|
|
47
|
+
gap: slotWrapGap
|
|
48
|
+
};
|
|
49
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
50
|
+
style: [containerStyle, style],
|
|
51
|
+
children: [/*#__PURE__*/_jsxs(View, {
|
|
52
|
+
style: productLabelStyle,
|
|
53
|
+
children: [/*#__PURE__*/_jsx(Avatar, {
|
|
54
|
+
style: "Image",
|
|
55
|
+
...(imageSource != null && {
|
|
56
|
+
imageSource
|
|
57
|
+
}),
|
|
58
|
+
modes: modes
|
|
59
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
60
|
+
style: labelTextStyle,
|
|
61
|
+
children: label
|
|
62
|
+
})]
|
|
63
|
+
}), /*#__PURE__*/_jsx(MoneyValue, {
|
|
64
|
+
value: value,
|
|
65
|
+
currency: currency,
|
|
66
|
+
modes: modes
|
|
67
|
+
}), children && /*#__PURE__*/_jsx(View, {
|
|
68
|
+
style: slotWrapStyle,
|
|
69
|
+
children: cloneChildrenWithModes(children, modes)
|
|
70
|
+
})]
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export default PortfolioHero;
|
|
@@ -0,0 +1,45 @@
|
|
|
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 Avatar from '../Avatar/Avatar';
|
|
7
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
+
function ProductLabel({
|
|
9
|
+
label = 'Gold',
|
|
10
|
+
imageSource,
|
|
11
|
+
modes = {},
|
|
12
|
+
style
|
|
13
|
+
}) {
|
|
14
|
+
const gap = getVariableByName('productLabel/gap', modes) ?? 8;
|
|
15
|
+
const textColor = getVariableByName('productLabel/text/color', modes) ?? '#000000';
|
|
16
|
+
const textFontSize = getVariableByName('productLabel/text/fontSize', modes) ?? 16;
|
|
17
|
+
const textFontFamily = getVariableByName('productLabel/text/fontFamily', modes) ?? 'JioType Var';
|
|
18
|
+
const textFontWeight = getVariableByName('productLabel/text/fontWeight', modes) ?? 700;
|
|
19
|
+
const textLineHeight = getVariableByName('productLabel/text/lineHeight', modes) ?? 21;
|
|
20
|
+
const containerStyle = {
|
|
21
|
+
flexDirection: 'row',
|
|
22
|
+
alignItems: 'center',
|
|
23
|
+
gap: gap
|
|
24
|
+
};
|
|
25
|
+
const labelTextStyle = {
|
|
26
|
+
color: textColor,
|
|
27
|
+
fontSize: textFontSize,
|
|
28
|
+
fontFamily: textFontFamily,
|
|
29
|
+
fontWeight: String(textFontWeight),
|
|
30
|
+
lineHeight: textLineHeight,
|
|
31
|
+
textAlign: 'center'
|
|
32
|
+
};
|
|
33
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
34
|
+
style: [containerStyle, style],
|
|
35
|
+
children: [/*#__PURE__*/_jsx(Avatar, {
|
|
36
|
+
style: "Image",
|
|
37
|
+
imageSource: imageSource,
|
|
38
|
+
modes: modes
|
|
39
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
40
|
+
style: labelTextStyle,
|
|
41
|
+
children: label
|
|
42
|
+
})]
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export default ProductLabel;
|