jfs-components 0.0.62 → 0.0.64
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 +59 -0
- package/lib/commonjs/components/Accordion/Accordion.js +1 -1
- package/lib/commonjs/components/ActionFooter/ActionFooter.js +1 -1
- package/lib/commonjs/components/ActionTile/ActionTile.js +2 -1
- package/lib/commonjs/components/AmountInput/AmountInput.js +2 -1
- package/lib/commonjs/components/AppBar/AppBar.js +1 -1
- package/lib/commonjs/components/Avatar/Avatar.js +184 -162
- package/lib/commonjs/components/AvatarGroup/AvatarGroup.js +1 -1
- package/lib/commonjs/components/Badge/Badge.js +2 -1
- package/lib/commonjs/components/Balance/Balance.js +2 -1
- package/lib/commonjs/components/BottomNav/BottomNav.js +2 -1
- package/lib/commonjs/components/BottomNavItem/BottomNavItem.js +106 -86
- package/lib/commonjs/components/Button/Button.js +190 -93
- package/lib/commonjs/components/ButtonGroup/ButtonGroup.js +1 -1
- package/lib/commonjs/components/Card/Card.js +2 -1
- package/lib/commonjs/components/CardCTA/CardCTA.js +1 -1
- package/lib/commonjs/components/CardProviderInfo/CardProviderInfo.js +1 -1
- package/lib/commonjs/components/Carousel/Carousel.js +3 -2
- package/lib/commonjs/components/Checkbox/Checkbox.js +2 -1
- package/lib/commonjs/components/ChipGroup/ChipGroup.js +1 -1
- package/lib/commonjs/components/ChipSelect/ChipSelect.js +2 -1
- package/lib/commonjs/components/DebitCard/DebitCard.js +1 -1
- package/lib/commonjs/components/Disclaimer/Disclaimer.js +2 -1
- package/lib/commonjs/components/Divider/Divider.js +2 -1
- package/lib/commonjs/components/Drawer/Drawer.js +109 -48
- package/lib/commonjs/components/EmptyState/EmptyState.js +2 -1
- package/lib/commonjs/components/FilterBar/FilterBar.js +1 -1
- package/lib/commonjs/components/Form/Form.js +2 -1
- package/lib/commonjs/components/FormField/FormField.js +3 -2
- package/lib/commonjs/components/HStack/HStack.js +1 -1
- package/lib/commonjs/components/HoldingsCard/HoldingsCard.js +2 -1
- package/lib/commonjs/components/IconButton/IconButton.js +118 -128
- package/lib/commonjs/components/IconCapsule/IconCapsule.js +61 -57
- package/lib/commonjs/components/InputSearch/InputSearch.js +7 -3
- package/lib/commonjs/components/LazyList/LazyList.js +1 -1
- package/lib/commonjs/components/LinearMeter/LinearMeter.js +3 -2
- package/lib/commonjs/components/ListGroup/ListGroup.js +1 -1
- package/lib/commonjs/components/ListItem/ListItem.js +190 -142
- package/lib/commonjs/components/MediaCard/MediaCard.js +3 -3
- package/lib/commonjs/components/MerchantProfile/MerchantProfile.js +2 -1
- package/lib/commonjs/components/MoneyValue/MoneyValue.js +2 -1
- package/lib/commonjs/components/NavArrow/NavArrow.js +82 -59
- package/lib/commonjs/components/NoteInput/NoteInput.js +2 -1
- package/lib/commonjs/components/Nudge/Nudge.js +1 -1
- package/lib/commonjs/components/Numpad/Numpad.js +2 -1
- package/lib/commonjs/components/OTP/OTP.js +1 -1
- package/lib/commonjs/components/PaymentFeedback/PaymentFeedback.js +2 -1
- package/lib/commonjs/components/Popup/Popup.js +2 -1
- package/lib/commonjs/components/ProductLabel/ProductLabel.js +2 -1
- package/lib/commonjs/components/ProgressBadge/ProgressBadge.js +2 -1
- package/lib/commonjs/components/RadioButton/RadioButton.js +2 -1
- package/lib/commonjs/components/RechargeCard/RechargeCard.js +2 -1
- package/lib/commonjs/components/Screen/Screen.js +1 -1
- package/lib/commonjs/components/Section/Section.js +500 -166
- package/lib/commonjs/components/SegmentedControl/SegmentedControl.js +3 -2
- package/lib/commonjs/components/StatItem/StatItem.js +2 -1
- package/lib/commonjs/components/StatusHero/StatusHero.js +2 -1
- package/lib/commonjs/components/Stepper/Step.js +2 -1
- package/lib/commonjs/components/Stepper/StepLabel.js +2 -1
- package/lib/commonjs/components/Stepper/Stepper.js +2 -1
- package/lib/commonjs/components/SupportText/SupportText.js +2 -1
- package/lib/commonjs/components/SupportText/SupportTextIcon.js +2 -1
- package/lib/commonjs/components/SwappableAmount/SwappableAmount.js +2 -1
- package/lib/commonjs/components/Tabs/TabItem.js +2 -1
- package/lib/commonjs/components/Tabs/Tabs.js +2 -1
- package/lib/commonjs/components/Text/Text.js +2 -1
- package/lib/commonjs/components/TextInput/TextInput.js +2 -2
- package/lib/commonjs/components/ThreadHero/ThreadHero.js +2 -1
- package/lib/commonjs/components/Title/Title.js +2 -1
- package/lib/commonjs/components/Toast/Toast.js +2 -1
- package/lib/commonjs/components/Toggle/Toggle.js +2 -1
- package/lib/commonjs/components/Tooltip/Tooltip.js +2 -1
- package/lib/commonjs/components/TransactionBubble/TransactionBubble.js +1 -1
- package/lib/commonjs/components/TransactionDetails/TransactionDetails.js +2 -2
- package/lib/commonjs/components/TransactionStatus/TransactionStatus.js +3 -2
- package/lib/commonjs/components/UpiHandle/UpiHandle.js +144 -110
- package/lib/commonjs/components/VStack/VStack.js +1 -1
- package/lib/commonjs/design-tokens/figma-variables-resolver.js +21 -3
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/commonjs/utils/react-utils.js +17 -0
- package/lib/module/components/Accordion/Accordion.js +2 -2
- package/lib/module/components/ActionFooter/ActionFooter.js +2 -2
- package/lib/module/components/ActionTile/ActionTile.js +2 -1
- package/lib/module/components/AmountInput/AmountInput.js +2 -1
- package/lib/module/components/AppBar/AppBar.js +2 -2
- package/lib/module/components/Avatar/Avatar.js +184 -162
- package/lib/module/components/AvatarGroup/AvatarGroup.js +2 -2
- package/lib/module/components/Badge/Badge.js +2 -1
- package/lib/module/components/Balance/Balance.js +2 -1
- package/lib/module/components/BottomNav/BottomNav.js +2 -1
- package/lib/module/components/BottomNavItem/BottomNavItem.js +108 -88
- package/lib/module/components/Button/Button.js +192 -95
- package/lib/module/components/ButtonGroup/ButtonGroup.js +2 -2
- package/lib/module/components/Card/Card.js +2 -1
- package/lib/module/components/CardCTA/CardCTA.js +2 -2
- package/lib/module/components/CardProviderInfo/CardProviderInfo.js +2 -2
- package/lib/module/components/Carousel/Carousel.js +3 -2
- package/lib/module/components/Checkbox/Checkbox.js +2 -1
- package/lib/module/components/ChipGroup/ChipGroup.js +2 -2
- package/lib/module/components/ChipSelect/ChipSelect.js +2 -1
- package/lib/module/components/DebitCard/DebitCard.js +2 -2
- package/lib/module/components/Disclaimer/Disclaimer.js +2 -1
- package/lib/module/components/Divider/Divider.js +2 -1
- package/lib/module/components/Drawer/Drawer.js +109 -48
- package/lib/module/components/EmptyState/EmptyState.js +2 -1
- package/lib/module/components/FilterBar/FilterBar.js +2 -2
- package/lib/module/components/Form/Form.js +2 -1
- package/lib/module/components/FormField/FormField.js +3 -2
- package/lib/module/components/HStack/HStack.js +2 -2
- package/lib/module/components/HoldingsCard/HoldingsCard.js +2 -1
- package/lib/module/components/IconButton/IconButton.js +120 -130
- package/lib/module/components/IconCapsule/IconCapsule.js +60 -57
- package/lib/module/components/InputSearch/InputSearch.js +7 -3
- package/lib/module/components/LazyList/LazyList.js +2 -2
- package/lib/module/components/LinearMeter/LinearMeter.js +3 -2
- package/lib/module/components/ListGroup/ListGroup.js +2 -2
- package/lib/module/components/ListItem/ListItem.js +194 -146
- package/lib/module/components/MediaCard/MediaCard.js +4 -2
- package/lib/module/components/MerchantProfile/MerchantProfile.js +2 -1
- package/lib/module/components/MoneyValue/MoneyValue.js +2 -1
- package/lib/module/components/NavArrow/NavArrow.js +82 -58
- package/lib/module/components/NoteInput/NoteInput.js +2 -1
- package/lib/module/components/Nudge/Nudge.js +2 -2
- package/lib/module/components/Numpad/Numpad.js +2 -1
- package/lib/module/components/OTP/OTP.js +2 -2
- package/lib/module/components/PaymentFeedback/PaymentFeedback.js +2 -1
- package/lib/module/components/Popup/Popup.js +2 -1
- package/lib/module/components/ProductLabel/ProductLabel.js +2 -1
- package/lib/module/components/ProgressBadge/ProgressBadge.js +2 -1
- package/lib/module/components/RadioButton/RadioButton.js +2 -1
- package/lib/module/components/RechargeCard/RechargeCard.js +2 -1
- package/lib/module/components/Screen/Screen.js +2 -2
- package/lib/module/components/Section/Section.js +503 -169
- package/lib/module/components/SegmentedControl/SegmentedControl.js +3 -2
- package/lib/module/components/StatItem/StatItem.js +2 -1
- package/lib/module/components/StatusHero/StatusHero.js +2 -1
- package/lib/module/components/Stepper/Step.js +2 -1
- package/lib/module/components/Stepper/StepLabel.js +2 -1
- package/lib/module/components/Stepper/Stepper.js +2 -1
- package/lib/module/components/SupportText/SupportText.js +2 -1
- package/lib/module/components/SupportText/SupportTextIcon.js +2 -1
- package/lib/module/components/SwappableAmount/SwappableAmount.js +2 -1
- package/lib/module/components/Tabs/TabItem.js +2 -1
- package/lib/module/components/Tabs/Tabs.js +2 -1
- package/lib/module/components/Text/Text.js +2 -1
- package/lib/module/components/TextInput/TextInput.js +3 -3
- package/lib/module/components/ThreadHero/ThreadHero.js +2 -1
- package/lib/module/components/Title/Title.js +2 -1
- package/lib/module/components/Toast/Toast.js +2 -1
- package/lib/module/components/Toggle/Toggle.js +2 -1
- package/lib/module/components/Tooltip/Tooltip.js +2 -1
- package/lib/module/components/TransactionBubble/TransactionBubble.js +2 -2
- package/lib/module/components/TransactionDetails/TransactionDetails.js +3 -3
- package/lib/module/components/TransactionStatus/TransactionStatus.js +3 -2
- package/lib/module/components/UpiHandle/UpiHandle.js +147 -113
- package/lib/module/components/VStack/VStack.js +2 -2
- package/lib/module/design-tokens/figma-variables-resolver.js +21 -3
- package/lib/module/icons/registry.js +1 -1
- package/lib/module/utils/react-utils.js +16 -0
- package/lib/typescript/src/components/Avatar/Avatar.d.ts +11 -17
- package/lib/typescript/src/components/BottomNavItem/BottomNavItem.d.ts +12 -8
- package/lib/typescript/src/components/Button/Button.d.ts +18 -1
- package/lib/typescript/src/components/IconButton/IconButton.d.ts +12 -29
- package/lib/typescript/src/components/IconCapsule/IconCapsule.d.ts +10 -18
- package/lib/typescript/src/components/InputSearch/InputSearch.d.ts +8 -3
- package/lib/typescript/src/components/ListItem/ListItem.d.ts +14 -1
- package/lib/typescript/src/components/NavArrow/NavArrow.d.ts +12 -11
- package/lib/typescript/src/components/Section/Section.d.ts +43 -48
- package/lib/typescript/src/components/UpiHandle/UpiHandle.d.ts +13 -12
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/lib/typescript/src/utils/react-utils.d.ts +15 -0
- package/package.json +4 -6
- package/src/components/Accordion/Accordion.tsx +2 -2
- package/src/components/ActionFooter/ActionFooter.tsx +2 -2
- package/src/components/ActionTile/ActionTile.tsx +2 -1
- package/src/components/AmountInput/AmountInput.tsx +2 -1
- package/src/components/AppBar/AppBar.tsx +2 -2
- package/src/components/Avatar/Avatar.tsx +229 -158
- package/src/components/AvatarGroup/AvatarGroup.tsx +2 -2
- package/src/components/Badge/Badge.tsx +2 -1
- package/src/components/Balance/Balance.tsx +2 -1
- package/src/components/BottomNav/BottomNav.tsx +2 -1
- package/src/components/BottomNavItem/BottomNavItem.tsx +159 -88
- package/src/components/Button/Button.tsx +228 -101
- package/src/components/ButtonGroup/ButtonGroup.tsx +2 -2
- package/src/components/Card/Card.tsx +2 -1
- package/src/components/CardCTA/CardCTA.tsx +2 -2
- package/src/components/CardProviderInfo/CardProviderInfo.tsx +2 -2
- package/src/components/Carousel/Carousel.tsx +3 -2
- package/src/components/Checkbox/Checkbox.tsx +2 -1
- package/src/components/ChipGroup/ChipGroup.tsx +2 -2
- package/src/components/ChipSelect/ChipSelect.tsx +2 -1
- package/src/components/DebitCard/DebitCard.tsx +2 -2
- package/src/components/Disclaimer/Disclaimer.tsx +2 -1
- package/src/components/Divider/Divider.tsx +2 -1
- package/src/components/Drawer/Drawer.tsx +124 -58
- package/src/components/EmptyState/EmptyState.tsx +2 -1
- package/src/components/FilterBar/FilterBar.tsx +2 -2
- package/src/components/Form/Form.tsx +2 -1
- package/src/components/FormField/FormField.tsx +3 -2
- package/src/components/HStack/HStack.tsx +2 -2
- package/src/components/HoldingsCard/HoldingsCard.tsx +2 -1
- package/src/components/IconButton/IconButton.tsx +154 -126
- package/src/components/IconCapsule/IconCapsule.tsx +73 -54
- package/src/components/InputSearch/InputSearch.tsx +19 -5
- package/src/components/LazyList/LazyList.tsx +2 -2
- package/src/components/LinearMeter/LinearMeter.tsx +3 -2
- package/src/components/ListGroup/ListGroup.tsx +2 -2
- package/src/components/ListItem/ListItem.tsx +257 -187
- package/src/components/MediaCard/MediaCard.tsx +2 -1
- package/src/components/MerchantProfile/MerchantProfile.tsx +2 -1
- package/src/components/MoneyValue/MoneyValue.tsx +2 -1
- package/src/components/NavArrow/NavArrow.tsx +91 -58
- package/src/components/NoteInput/NoteInput.tsx +2 -1
- package/src/components/Nudge/Nudge.tsx +2 -2
- package/src/components/Numpad/Numpad.tsx +2 -1
- package/src/components/OTP/OTP.tsx +2 -2
- package/src/components/PaymentFeedback/PaymentFeedback.tsx +2 -1
- package/src/components/Popup/Popup.tsx +2 -1
- package/src/components/ProductLabel/ProductLabel.tsx +2 -1
- package/src/components/ProgressBadge/ProgressBadge.tsx +2 -2
- package/src/components/RadioButton/RadioButton.tsx +2 -1
- package/src/components/RechargeCard/RechargeCard.tsx +2 -1
- package/src/components/Screen/Screen.tsx +2 -2
- package/src/components/Section/Section.tsx +672 -176
- package/src/components/SegmentedControl/SegmentedControl.tsx +3 -2
- package/src/components/StatItem/StatItem.tsx +2 -1
- package/src/components/StatusHero/StatusHero.tsx +2 -1
- package/src/components/Stepper/Step.tsx +2 -1
- package/src/components/Stepper/StepLabel.tsx +2 -1
- package/src/components/Stepper/Stepper.tsx +2 -1
- package/src/components/SupportText/SupportText.tsx +2 -1
- package/src/components/SupportText/SupportTextIcon.tsx +2 -1
- package/src/components/SwappableAmount/SwappableAmount.tsx +2 -1
- package/src/components/Tabs/TabItem.tsx +2 -1
- package/src/components/Tabs/Tabs.tsx +2 -1
- package/src/components/Text/Text.tsx +2 -1
- package/src/components/TextInput/TextInput.tsx +3 -3
- package/src/components/ThreadHero/ThreadHero.tsx +2 -1
- package/src/components/Title/Title.tsx +2 -1
- package/src/components/Toast/Toast.tsx +2 -1
- package/src/components/Toggle/Toggle.tsx +2 -1
- package/src/components/Tooltip/Tooltip.tsx +2 -1
- package/src/components/TransactionBubble/TransactionBubble.tsx +2 -2
- package/src/components/TransactionDetails/TransactionDetails.tsx +3 -3
- package/src/components/TransactionStatus/TransactionStatus.tsx +3 -2
- package/src/components/UpiHandle/UpiHandle.tsx +193 -125
- package/src/components/VStack/VStack.tsx +2 -2
- package/src/design-tokens/figma-variables-resolver.ts +21 -3
- package/src/icons/registry.ts +1 -1
- package/src/utils/react-utils.ts +16 -0
- package/lib/typescript/App.d.ts +0 -2
- package/lib/typescript/index.d.ts +0 -2
- package/lib/typescript/metro.config.d.ts +0 -78
- package/lib/typescript/react-native.config.d.ts +0 -4
|
@@ -1,18 +1,115 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import {
|
|
1
|
+
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
Pressable,
|
|
4
|
+
View,
|
|
5
|
+
Image,
|
|
6
|
+
Text,
|
|
7
|
+
Platform,
|
|
8
|
+
type ImageSourcePropType,
|
|
9
|
+
type ViewStyle,
|
|
10
|
+
type TextStyle,
|
|
11
|
+
type ImageStyle,
|
|
12
|
+
type PressableStateCallbackType,
|
|
13
|
+
type StyleProp,
|
|
14
|
+
} from 'react-native'
|
|
3
15
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
16
|
+
import { EMPTY_MODES } from '../../utils/react-utils'
|
|
4
17
|
|
|
5
18
|
const avatarImage = require('./31595e70c4181263f9971590224b12934b280c9b.png')
|
|
6
19
|
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Module-scope constants — never re-allocated per render.
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
const IS_WEB = Platform.OS === 'web'
|
|
25
|
+
const IS_IOS = Platform.OS === 'ios'
|
|
26
|
+
const PRESS_DELAY = IS_IOS ? 130 : 0
|
|
27
|
+
|
|
28
|
+
// Pressed visual is applied through Pressable's style callback so the host
|
|
29
|
+
// view updates without scheduling a React render.
|
|
30
|
+
const pressedOverlayStyle: ViewStyle = { transform: [{ scale: 0.98 }] }
|
|
31
|
+
const focusOverlayStyle: ViewStyle = { borderColor: '#222', borderWidth: 1 }
|
|
32
|
+
|
|
33
|
+
const imageContainerStyle: ViewStyle = {
|
|
34
|
+
width: '100%',
|
|
35
|
+
height: '100%',
|
|
36
|
+
overflow: 'hidden',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const imageBaseStyle: ImageStyle = {
|
|
40
|
+
width: '100%',
|
|
41
|
+
height: '100%',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const monogramContainerStyle: ViewStyle = {
|
|
45
|
+
width: '100%',
|
|
46
|
+
height: '100%',
|
|
47
|
+
justifyContent: 'center',
|
|
48
|
+
alignItems: 'center',
|
|
49
|
+
paddingVertical: 7,
|
|
50
|
+
paddingHorizontal: 5,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Token resolution — collapsed into one useMemo per (modes, style) tuple.
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
interface AvatarTokens {
|
|
58
|
+
containerStyle: ViewStyle;
|
|
59
|
+
imageContainerStyle: ViewStyle;
|
|
60
|
+
imageStyle: ImageStyle;
|
|
61
|
+
monogramTextStyle: TextStyle;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function resolveAvatarTokens(modes: Record<string, any>, isMonogram: boolean): AvatarTokens {
|
|
65
|
+
const size = (getVariableByName('avatar/size', modes) || 29) as number
|
|
66
|
+
const radiusRaw = (getVariableByName('avatar/radius', modes) || 9999) as number
|
|
67
|
+
const backgroundColor = getVariableByName('avatar/background', modes) || '#dbcfff'
|
|
68
|
+
const borderColor = getVariableByName('avatar/border/color', modes) || 'rgba(255,255,255,0)'
|
|
69
|
+
const borderSize = (getVariableByName('avatar/border/size', modes) || 1) as number
|
|
70
|
+
const labelColor = getVariableByName('avatar/label/color', modes) || '#5c00b5'
|
|
71
|
+
const labelFontSize = (getVariableByName('avatar/label/fontSize', modes) || 12) as number
|
|
72
|
+
const labelLineHeight = (getVariableByName('avatar/label/lineHeight', modes) || 14) as number
|
|
73
|
+
const labelFontWeightRaw = getVariableByName('avatar/label/fontWeight', modes) || 500
|
|
74
|
+
const labelFontWeight =
|
|
75
|
+
typeof labelFontWeightRaw === 'number' ? labelFontWeightRaw.toString() : labelFontWeightRaw
|
|
76
|
+
const labelFontFamily = (getVariableByName('avatar/label/fontFamily', modes) || 'Inter') as string
|
|
77
|
+
|
|
78
|
+
// 9999 is the design-token sentinel for "perfect circle"
|
|
79
|
+
const borderRadius = radiusRaw === 9999 ? size / 2 : radiusRaw
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
containerStyle: {
|
|
83
|
+
width: size,
|
|
84
|
+
height: size,
|
|
85
|
+
borderRadius,
|
|
86
|
+
borderWidth: borderSize,
|
|
87
|
+
borderColor: borderColor as string,
|
|
88
|
+
overflow: 'hidden',
|
|
89
|
+
...(isMonogram ? { backgroundColor: backgroundColor as string } : {}),
|
|
90
|
+
},
|
|
91
|
+
imageContainerStyle: {
|
|
92
|
+
...imageContainerStyle,
|
|
93
|
+
borderRadius,
|
|
94
|
+
},
|
|
95
|
+
imageStyle: {
|
|
96
|
+
...imageBaseStyle,
|
|
97
|
+
borderRadius,
|
|
98
|
+
},
|
|
99
|
+
monogramTextStyle: {
|
|
100
|
+
fontSize: labelFontSize,
|
|
101
|
+
lineHeight: labelLineHeight,
|
|
102
|
+
fontWeight: labelFontWeight as TextStyle['fontWeight'],
|
|
103
|
+
fontFamily: labelFontFamily,
|
|
104
|
+
color: labelColor as string,
|
|
105
|
+
textAlign: 'center',
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
7
110
|
/**
|
|
8
111
|
* Avatar component that displays either an image or a monogram.
|
|
9
|
-
*
|
|
10
|
-
* This component supports two styles:
|
|
11
|
-
* - Image: Displays a user's profile picture
|
|
12
|
-
* - Monogram: Displays user initials in a circular background
|
|
13
|
-
*
|
|
14
|
-
* All styling values are resolved from Figma design tokens using the provided modes.
|
|
15
|
-
*
|
|
112
|
+
*
|
|
16
113
|
* @component
|
|
17
114
|
* @param {Object} props - Component props
|
|
18
115
|
* @param {string} [props.monogram="MS"] - The initials to display when style is "Monogram"
|
|
@@ -20,19 +117,18 @@ const avatarImage = require('./31595e70c4181263f9971590224b12934b280c9b.png')
|
|
|
20
117
|
* @param {Object} [props.modes={}] - Mode configuration for design tokens (e.g., {"Avatar Size": "M"})
|
|
21
118
|
* @param {string} [props.imageSource] - Optional image source for Image style (defaults to built-in image)
|
|
22
119
|
* @param {string} [props.accessibilityLabel] - Accessibility label for screen readers. If not provided, uses monogram or "User avatar"
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* ```
|
|
120
|
+
*
|
|
121
|
+
* Performance notes:
|
|
122
|
+
* - All token reads are folded into a single `useMemo([modes, isMonogram])`.
|
|
123
|
+
* - Press visual goes through Pressable's `({ pressed })` style callback so
|
|
124
|
+
* a scroll-cancelled touch never schedules a React render. iOS gets
|
|
125
|
+
* `unstable_pressDelay={130}` for additional safety inside scrollables.
|
|
126
|
+
* - Focus state stays in React (it's a sustained visual) but the setter is
|
|
127
|
+
* gated to web only, where focus events actually fire.
|
|
32
128
|
*/
|
|
33
129
|
export type AvatarProps = {
|
|
34
130
|
monogram?: string;
|
|
35
|
-
style?:
|
|
131
|
+
style?: 'Image' | 'Monogram';
|
|
36
132
|
modes?: Record<string, any>;
|
|
37
133
|
imageSource?: ImageSourcePropType | string;
|
|
38
134
|
accessibilityLabel?: string;
|
|
@@ -41,169 +137,144 @@ export type AvatarProps = {
|
|
|
41
137
|
} & Omit<React.ComponentProps<typeof View>, 'style' | 'accessibilityRole' | 'accessibilityLabel'>;
|
|
42
138
|
|
|
43
139
|
function Avatar({
|
|
44
|
-
monogram =
|
|
45
|
-
style =
|
|
46
|
-
modes =
|
|
140
|
+
monogram = 'MS',
|
|
141
|
+
style = 'Image',
|
|
142
|
+
modes = EMPTY_MODES,
|
|
47
143
|
imageSource,
|
|
48
|
-
accessibilityLabel
|
|
144
|
+
// accessibilityLabel is accepted on the type for API back-compat but the
|
|
145
|
+
// component intentionally renders `accessibilityLabel={undefined}` on the
|
|
146
|
+
// wrapper (the inner Text/Image carry the label instead).
|
|
147
|
+
accessibilityLabel: _accessibilityLabel,
|
|
49
148
|
...rest
|
|
50
149
|
}: AvatarProps) {
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
const radius = getVariableByName('avatar/radius', modes) || 9999
|
|
54
|
-
const backgroundColor = getVariableByName('avatar/background', modes) || '#dbcfff'
|
|
55
|
-
const borderColor = getVariableByName('avatar/border/color', modes) || 'rgba(255,255,255,0)'
|
|
56
|
-
const borderSize = getVariableByName('avatar/border/size', modes) || 1
|
|
57
|
-
const labelColor = getVariableByName('avatar/label/color', modes) || '#5c00b5'
|
|
58
|
-
const labelFontSize = getVariableByName('avatar/label/fontSize', modes) || 12
|
|
59
|
-
const labelLineHeight = getVariableByName('avatar/label/lineHeight', modes) || 14
|
|
60
|
-
const labelFontWeight = getVariableByName('avatar/label/fontWeight', modes) || 500
|
|
61
|
-
const labelFontFamily = getVariableByName('avatar/label/fontFamily', modes) || 'Inter'
|
|
62
|
-
|
|
63
|
-
// Convert radius to React Native format (if 9999, use size/2 for perfect circle)
|
|
64
|
-
const borderRadius = radius === 9999 ? size / 2 : radius
|
|
65
|
-
|
|
66
|
-
// Container style
|
|
67
|
-
const containerStyle: ViewStyle = {
|
|
68
|
-
width: size,
|
|
69
|
-
height: size,
|
|
70
|
-
borderRadius: borderRadius,
|
|
71
|
-
borderWidth: borderSize,
|
|
72
|
-
borderColor: borderColor,
|
|
73
|
-
overflow: 'hidden',
|
|
74
|
-
...(style === 'Monogram' ? { backgroundColor: backgroundColor } : {})
|
|
75
|
-
}
|
|
150
|
+
const isMonogram = style === 'Monogram'
|
|
151
|
+
const tokens = useMemo(() => resolveAvatarTokens(modes, isMonogram), [modes, isMonogram])
|
|
76
152
|
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
borderRadius: borderRadius,
|
|
82
|
-
overflow: 'hidden',
|
|
83
|
-
}
|
|
153
|
+
// Focus is a sustained visible state — keep mirroring on web; gate the
|
|
154
|
+
// setter so it never fires on native (where focus events don't fire on
|
|
155
|
+
// these elements anyway).
|
|
156
|
+
const [isFocused, setIsFocused] = useState(false)
|
|
84
157
|
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
158
|
+
// Mirror user handlers in a ref so our wrappers can stay referentially
|
|
159
|
+
// stable across renders.
|
|
160
|
+
const userHandlersRef = useRef<{
|
|
161
|
+
onPressIn?: (e: any) => void
|
|
162
|
+
onPressOut?: (e: any) => void
|
|
163
|
+
onFocus?: (e: any) => void
|
|
164
|
+
onBlur?: (e: any) => void
|
|
165
|
+
}>({})
|
|
166
|
+
userHandlersRef.current.onPressIn = (rest as any)?.onPressIn
|
|
167
|
+
userHandlersRef.current.onPressOut = (rest as any)?.onPressOut
|
|
168
|
+
userHandlersRef.current.onFocus = (rest as any)?.onFocus
|
|
169
|
+
userHandlersRef.current.onBlur = (rest as any)?.onBlur
|
|
91
170
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
171
|
+
const handlePressIn = useCallback((e: any) => {
|
|
172
|
+
userHandlersRef.current.onPressIn?.(e)
|
|
173
|
+
}, [])
|
|
174
|
+
const handlePressOut = useCallback((e: any) => {
|
|
175
|
+
userHandlersRef.current.onPressOut?.(e)
|
|
176
|
+
}, [])
|
|
177
|
+
const handleFocus = useCallback((e: any) => {
|
|
178
|
+
if (IS_WEB) setIsFocused(true)
|
|
179
|
+
userHandlersRef.current.onFocus?.(e)
|
|
180
|
+
}, [])
|
|
181
|
+
const handleBlur = useCallback((e: any) => {
|
|
182
|
+
if (IS_WEB) setIsFocused(false)
|
|
183
|
+
userHandlersRef.current.onBlur?.(e)
|
|
184
|
+
}, [])
|
|
101
185
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
186
|
+
const pressableStyle = useCallback(
|
|
187
|
+
({ pressed }: PressableStateCallbackType): StyleProp<ViewStyle> => [
|
|
188
|
+
tokens.containerStyle,
|
|
189
|
+
pressed ? pressedOverlayStyle : null,
|
|
190
|
+
isFocused ? focusOverlayStyle : null,
|
|
191
|
+
],
|
|
192
|
+
[tokens.containerStyle, isFocused]
|
|
193
|
+
)
|
|
111
194
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
195
|
+
const staticContainerStyle = useMemo<StyleProp<ViewStyle>>(
|
|
196
|
+
() => [tokens.containerStyle, isFocused ? focusOverlayStyle : null],
|
|
197
|
+
[tokens.containerStyle, isFocused]
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
// The inner content varies; everything else (wrapper, handlers, style) is shared.
|
|
201
|
+
const innerContent = isMonogram ? (
|
|
202
|
+
<View style={monogramContainerStyle}>
|
|
203
|
+
<Text
|
|
204
|
+
style={tokens.monogramTextStyle}
|
|
205
|
+
accessibilityElementsHidden={true}
|
|
206
|
+
importantForAccessibility="no"
|
|
207
|
+
>
|
|
208
|
+
{monogram}
|
|
209
|
+
</Text>
|
|
210
|
+
</View>
|
|
211
|
+
) : (
|
|
212
|
+
<ImageContent
|
|
213
|
+
tokens={tokens}
|
|
214
|
+
imageSource={imageSource}
|
|
215
|
+
/>
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
const isPressable = !!(rest as any)?.onPress
|
|
219
|
+
|
|
220
|
+
if (isPressable) {
|
|
123
221
|
return (
|
|
124
|
-
<
|
|
125
|
-
style={
|
|
222
|
+
<Pressable
|
|
223
|
+
style={pressableStyle}
|
|
126
224
|
accessibilityRole="image"
|
|
127
225
|
accessibilityLabel={undefined}
|
|
128
|
-
onPressIn={
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
setIsPressed(false)
|
|
134
|
-
; (rest as any)?.onPressOut?.(e)
|
|
135
|
-
}}
|
|
136
|
-
onFocus={(e: any) => {
|
|
137
|
-
setIsFocused(true)
|
|
138
|
-
; (rest as any)?.onFocus?.(e)
|
|
139
|
-
}}
|
|
140
|
-
onBlur={(e: any) => {
|
|
141
|
-
setIsFocused(false)
|
|
142
|
-
; (rest as any)?.onBlur?.(e)
|
|
143
|
-
}}
|
|
226
|
+
onPressIn={handlePressIn}
|
|
227
|
+
onPressOut={handlePressOut}
|
|
228
|
+
onFocus={handleFocus}
|
|
229
|
+
onBlur={handleBlur}
|
|
230
|
+
unstable_pressDelay={PRESS_DELAY}
|
|
144
231
|
{...rest}
|
|
145
232
|
>
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
style={monogramTextStyle}
|
|
149
|
-
accessibilityElementsHidden={true}
|
|
150
|
-
importantForAccessibility="no"
|
|
151
|
-
>
|
|
152
|
-
{monogram}
|
|
153
|
-
</Text>
|
|
154
|
-
</View>
|
|
155
|
-
</Wrapper>
|
|
233
|
+
{innerContent}
|
|
234
|
+
</Pressable>
|
|
156
235
|
)
|
|
157
236
|
}
|
|
158
237
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
238
|
+
return (
|
|
239
|
+
<View
|
|
240
|
+
style={staticContainerStyle}
|
|
241
|
+
accessibilityRole="image"
|
|
242
|
+
accessibilityLabel={undefined}
|
|
243
|
+
{...rest}
|
|
244
|
+
>
|
|
245
|
+
{innerContent}
|
|
246
|
+
</View>
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Memoize the image normalization so a string URL that doesn't change
|
|
251
|
+
// doesn't keep producing a new `{ uri }` object every render.
|
|
252
|
+
function ImageContent({
|
|
253
|
+
tokens,
|
|
254
|
+
imageSource,
|
|
255
|
+
}: {
|
|
256
|
+
tokens: AvatarTokens;
|
|
257
|
+
imageSource?: ImageSourcePropType | string;
|
|
258
|
+
}) {
|
|
259
|
+
const normalizedImageSource = useMemo(() => {
|
|
162
260
|
if (!imageSource) return avatarImage
|
|
163
261
|
if (typeof imageSource === 'string') {
|
|
164
|
-
// If it's a string, treat it as a URL
|
|
165
262
|
return { uri: imageSource }
|
|
166
263
|
}
|
|
167
|
-
// Otherwise, use it as-is (could be require() result or { uri: ... } object)
|
|
168
264
|
return imageSource
|
|
169
265
|
}, [imageSource])
|
|
170
266
|
|
|
171
|
-
const Wrapper: any = (rest as any)?.onPress ? Pressable : View
|
|
172
267
|
return (
|
|
173
|
-
<
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
setIsPressed(false)
|
|
183
|
-
; (rest as any)?.onPressOut?.(e)
|
|
184
|
-
}}
|
|
185
|
-
onFocus={(e: any) => {
|
|
186
|
-
setIsFocused(true)
|
|
187
|
-
; (rest as any)?.onFocus?.(e)
|
|
188
|
-
}}
|
|
189
|
-
onBlur={(e: any) => {
|
|
190
|
-
setIsFocused(false)
|
|
191
|
-
; (rest as any)?.onBlur?.(e)
|
|
192
|
-
}}
|
|
193
|
-
{...rest}
|
|
194
|
-
>
|
|
195
|
-
<View style={imageContainerStyle}>
|
|
196
|
-
<Image
|
|
197
|
-
source={normalizedImageSource}
|
|
198
|
-
resizeMode="cover"
|
|
199
|
-
style={imageStyle}
|
|
200
|
-
accessibilityElementsHidden={true}
|
|
201
|
-
importantForAccessibility="no"
|
|
202
|
-
/>
|
|
203
|
-
</View>
|
|
204
|
-
</Wrapper>
|
|
268
|
+
<View style={tokens.imageContainerStyle}>
|
|
269
|
+
<Image
|
|
270
|
+
source={normalizedImageSource}
|
|
271
|
+
resizeMode="cover"
|
|
272
|
+
style={tokens.imageStyle}
|
|
273
|
+
accessibilityElementsHidden={true}
|
|
274
|
+
importantForAccessibility="no"
|
|
275
|
+
/>
|
|
276
|
+
</View>
|
|
205
277
|
)
|
|
206
278
|
}
|
|
207
279
|
|
|
208
|
-
export default Avatar
|
|
209
|
-
|
|
280
|
+
export default React.memo(Avatar)
|
|
@@ -3,7 +3,7 @@ import { View, Platform, type ViewStyle } from 'react-native';
|
|
|
3
3
|
import MaskedView from '@react-native-masked-view/masked-view';
|
|
4
4
|
import Svg, { Path } from 'react-native-svg';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
-
import { flattenChildren } from '../../utils/react-utils';
|
|
6
|
+
import { EMPTY_MODES, flattenChildren } from '../../utils/react-utils';
|
|
7
7
|
|
|
8
8
|
type AvatarGroupProps = {
|
|
9
9
|
modes?: Record<string, any>;
|
|
@@ -11,7 +11,7 @@ type AvatarGroupProps = {
|
|
|
11
11
|
style?: ViewStyle;
|
|
12
12
|
} & React.ComponentProps<typeof View>;
|
|
13
13
|
|
|
14
|
-
function AvatarGroup({ modes =
|
|
14
|
+
function AvatarGroup({ modes = EMPTY_MODES, children, style, ...rest }: AvatarGroupProps) {
|
|
15
15
|
const gap = getVariableByName('avatarGroup/gap', modes) || 0;
|
|
16
16
|
|
|
17
17
|
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type TextStyle,
|
|
8
8
|
} from 'react-native'
|
|
9
9
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
10
|
+
import { EMPTY_MODES } from '../../utils/react-utils'
|
|
10
11
|
|
|
11
12
|
type BadgeProps = {
|
|
12
13
|
/** Visible label text shown inside the badge */
|
|
@@ -22,7 +23,7 @@ type BadgeProps = {
|
|
|
22
23
|
|
|
23
24
|
function Badge({
|
|
24
25
|
label = 'Label',
|
|
25
|
-
modes =
|
|
26
|
+
modes = EMPTY_MODES,
|
|
26
27
|
onPress,
|
|
27
28
|
accessibilityLabel,
|
|
28
29
|
style,
|
|
@@ -2,6 +2,7 @@ import React from 'react'
|
|
|
2
2
|
import { View, Text, type StyleProp, type ViewStyle, type TextStyle } from 'react-native'
|
|
3
3
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
4
4
|
import MoneyValue from '../MoneyValue/MoneyValue'
|
|
5
|
+
import { EMPTY_MODES } from '../../utils/react-utils'
|
|
5
6
|
|
|
6
7
|
export type BalanceProps = {
|
|
7
8
|
/**
|
|
@@ -47,7 +48,7 @@ function Balance({
|
|
|
47
48
|
title = "Total owed to people",
|
|
48
49
|
amount = "500",
|
|
49
50
|
currency = "₹",
|
|
50
|
-
modes =
|
|
51
|
+
modes = EMPTY_MODES,
|
|
51
52
|
style,
|
|
52
53
|
children,
|
|
53
54
|
}: BalanceProps) {
|
|
@@ -3,6 +3,7 @@ import { View, type StyleProp, type ViewStyle } from 'react-native'
|
|
|
3
3
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
4
4
|
import { useTokens } from '../../design-tokens/JFSThemeProvider'
|
|
5
5
|
import BottomNavItem from '../BottomNavItem/BottomNavItem'
|
|
6
|
+
import { EMPTY_MODES } from '../../utils/react-utils'
|
|
6
7
|
|
|
7
8
|
type BottomNavProps = {
|
|
8
9
|
/**
|
|
@@ -41,7 +42,7 @@ function BottomNav({
|
|
|
41
42
|
value,
|
|
42
43
|
onChange,
|
|
43
44
|
children,
|
|
44
|
-
modes: propModes =
|
|
45
|
+
modes: propModes = EMPTY_MODES,
|
|
45
46
|
style,
|
|
46
47
|
accessibilityLabel = undefined,
|
|
47
48
|
accessibilityHint,
|