jfs-components 0.0.62 → 0.0.63
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 +36 -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 +2 -1
- 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 +268 -156
- 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 +2 -1
- 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 +271 -159
- 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 +2 -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 +2 -1
- 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 +323 -167
- 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,13 +1,33 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
2
|
-
import {
|
|
1
|
+
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
Pressable,
|
|
4
|
+
View,
|
|
5
|
+
Text,
|
|
6
|
+
Image,
|
|
7
|
+
Platform,
|
|
8
|
+
type ViewStyle,
|
|
9
|
+
type TextStyle,
|
|
10
|
+
type ImageStyle,
|
|
11
|
+
type ImageSourcePropType,
|
|
12
|
+
type StyleProp,
|
|
13
|
+
type PressableStateCallbackType,
|
|
14
|
+
} from 'react-native'
|
|
3
15
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
4
16
|
import { useTokens } from '../../design-tokens/JFSThemeProvider'
|
|
17
|
+
import { EMPTY_MODES } from '../../utils/react-utils'
|
|
5
18
|
import Icon from '../../icons/Icon'
|
|
6
19
|
|
|
7
20
|
// Default static asset from the component folder.
|
|
8
21
|
// Consumers can override the image via the `avatarSource` prop if needed.
|
|
9
22
|
const DEFAULT_AVATAR_IMAGE = require('./Image.png')
|
|
10
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
|
+
const pressedOverlayStyle: ViewStyle = { transform: [{ scale: 0.98 }] }
|
|
29
|
+
const focusOverlayStyle: ViewStyle = { borderWidth: 1, borderColor: '#222' }
|
|
30
|
+
|
|
11
31
|
type UpiHandleProps = {
|
|
12
32
|
label?: string;
|
|
13
33
|
modes?: Record<string, any>;
|
|
@@ -21,160 +41,178 @@ type UpiHandleProps = {
|
|
|
21
41
|
disabled?: boolean;
|
|
22
42
|
} & Omit<React.ComponentProps<typeof View>, 'style' | 'accessibilityRole' | 'accessibilityLabel' | 'accessibilityHint'>;
|
|
23
43
|
|
|
44
|
+
interface UpiHandleTokens {
|
|
45
|
+
containerStyle: ViewStyle;
|
|
46
|
+
avatarStyle: ImageStyle;
|
|
47
|
+
labelStyle: TextStyle;
|
|
48
|
+
iconColor: string;
|
|
49
|
+
iconSize: number;
|
|
50
|
+
iconPlaceholderStyle: ViewStyle;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolveUpiHandleTokens(modes: Record<string, any>): UpiHandleTokens {
|
|
54
|
+
const backgroundColor = (getVariableByName('upiHandle/background', modes) || '#f5f5f5') as string
|
|
55
|
+
const radius = (getVariableByName('upiHandle/radius', modes) || 99999) as number
|
|
56
|
+
const paddingLeft = (getVariableByName('upiHandle/padding/left', modes) || 4) as number
|
|
57
|
+
const paddingRight = (getVariableByName('upiHandle/padding/right', modes) || 14) as number
|
|
58
|
+
const paddingVertical = (getVariableByName('upiHandle/padding/vertical', modes) || 3) as number
|
|
59
|
+
const gap = (getVariableByName('upiHandle/gap', modes) || 6) as number
|
|
60
|
+
|
|
61
|
+
const avatarSize = (getVariableByName('upiHandle/image/size', modes) || 23) as number
|
|
62
|
+
const avatarRadius = (getVariableByName('upiHandle/image/radius', modes) || 99999) as number
|
|
63
|
+
|
|
64
|
+
const labelColor = (getVariableByName('upiHandle/label/color', modes) || '#0d0d0f') as string
|
|
65
|
+
const labelFontSize = (getVariableByName('upiHandle/label/fontSize', modes) || 12) as number
|
|
66
|
+
const labelLineHeight = (getVariableByName('upiHandle/label/lineHeight', modes) || 23) as number
|
|
67
|
+
const labelFontFamily = (getVariableByName('upiHandle/label/fontFamily', modes) || 'System') as string
|
|
68
|
+
const labelFontWeightRaw = getVariableByName('upiHandle/label/fontWeight', modes) || 500
|
|
69
|
+
const labelFontWeight =
|
|
70
|
+
typeof labelFontWeightRaw === 'number' ? labelFontWeightRaw.toString() : labelFontWeightRaw
|
|
71
|
+
|
|
72
|
+
const iconColor = (getVariableByName('upiHandle/icon/color', modes) || '#0d0d0f') as string
|
|
73
|
+
const iconSize = (getVariableByName('upiHandle/icon/size', modes) || 12) as number
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
containerStyle: {
|
|
77
|
+
flexDirection: 'row',
|
|
78
|
+
alignItems: 'center',
|
|
79
|
+
justifyContent: 'center',
|
|
80
|
+
backgroundColor,
|
|
81
|
+
paddingLeft,
|
|
82
|
+
paddingRight,
|
|
83
|
+
paddingVertical,
|
|
84
|
+
borderRadius: radius,
|
|
85
|
+
gap,
|
|
86
|
+
},
|
|
87
|
+
avatarStyle: {
|
|
88
|
+
width: avatarSize,
|
|
89
|
+
height: avatarSize,
|
|
90
|
+
borderRadius: avatarRadius,
|
|
91
|
+
overflow: 'hidden',
|
|
92
|
+
},
|
|
93
|
+
labelStyle: {
|
|
94
|
+
color: labelColor,
|
|
95
|
+
fontSize: labelFontSize,
|
|
96
|
+
lineHeight: labelLineHeight,
|
|
97
|
+
fontFamily: labelFontFamily,
|
|
98
|
+
fontWeight: labelFontWeight as TextStyle['fontWeight'],
|
|
99
|
+
},
|
|
100
|
+
iconColor,
|
|
101
|
+
iconSize,
|
|
102
|
+
iconPlaceholderStyle: {
|
|
103
|
+
width: iconSize,
|
|
104
|
+
height: iconSize,
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
24
109
|
/**
|
|
25
110
|
* UpiHandle pill that mirrors the Figma "UPI Handle" component.
|
|
26
111
|
*
|
|
27
|
-
* Layout:
|
|
28
|
-
* - Circular image/avatar on the left
|
|
29
|
-
* - Label text in the center
|
|
30
|
-
* - Optional QR-code style icon on the right
|
|
31
|
-
*
|
|
32
|
-
* All visual styling is resolved from Figma variables via `getVariableByName`
|
|
33
|
-
* using a `modes` configuration object, matching the rest of this library.
|
|
34
|
-
*
|
|
35
112
|
* @component
|
|
36
113
|
* @param {Object} props
|
|
37
114
|
* @param {string} [props.label="Label"] - UPI handle text to display.
|
|
38
115
|
* @param {Object} [props.modes={}] - Modes object passed directly to `getVariableByName`.
|
|
39
116
|
* @param {boolean} [props.showIcon=true] - Toggles the trailing icon visibility.
|
|
40
|
-
* @param {string} [props.iconName='ic_scan_qr_code'] - Icon name from the actions set
|
|
117
|
+
* @param {string} [props.iconName='ic_scan_qr_code'] - Icon name from the actions set.
|
|
41
118
|
* @param {ImageSourcePropType} [props.avatarSource] - Optional custom image source for the avatar.
|
|
42
119
|
* @param {Function} [props.onClick] - Click/tap handler. Works as an alias for `onPress`.
|
|
43
|
-
* @param {string} [props.accessibilityLabel] - Accessibility label for screen readers
|
|
120
|
+
* @param {string} [props.accessibilityLabel] - Accessibility label for screen readers
|
|
44
121
|
* @param {string} [props.accessibilityHint] - Additional accessibility hint for screen readers
|
|
122
|
+
*
|
|
123
|
+
* Performance notes:
|
|
124
|
+
* - Token reads collapsed into a single `useMemo([modes])`.
|
|
125
|
+
* - Press visual goes through Pressable's `({ pressed })` style callback so
|
|
126
|
+
* a scroll-cancelled touch never schedules a React render. iOS gets
|
|
127
|
+
* `unstable_pressDelay={130}` for additional safety inside scrollables.
|
|
128
|
+
* - Focus state is mirrored on web only (gated setter).
|
|
129
|
+
* - Wrapped in `React.memo`.
|
|
45
130
|
*/
|
|
46
131
|
function UpiHandle({
|
|
47
132
|
label = 'Label',
|
|
48
|
-
modes: propModes =
|
|
133
|
+
modes: propModes = EMPTY_MODES,
|
|
49
134
|
showIcon = true,
|
|
50
135
|
iconName = 'ic_scan_qr_code',
|
|
51
136
|
avatarSource,
|
|
52
137
|
onPress,
|
|
53
138
|
onClick,
|
|
54
139
|
disabled,
|
|
55
|
-
accessibilityLabel
|
|
140
|
+
// accessibilityLabel is accepted on the type for API back-compat; the
|
|
141
|
+
// wrapper renders `accessibilityLabel={undefined}` because the inner Text
|
|
142
|
+
// already carries the label.
|
|
143
|
+
accessibilityLabel: _accessibilityLabel,
|
|
56
144
|
accessibilityHint,
|
|
57
145
|
...rest
|
|
58
146
|
}: UpiHandleProps) {
|
|
59
147
|
const { modes: globalModes } = useTokens()
|
|
60
|
-
const modes = { ...globalModes, ...propModes }
|
|
61
|
-
// Token‑driven container styling
|
|
62
|
-
const backgroundColor =
|
|
63
|
-
getVariableByName('upiHandle/background', modes) || '#f5f5f5'
|
|
64
|
-
const radius = getVariableByName('upiHandle/radius', modes) || 99999
|
|
65
|
-
const paddingLeft =
|
|
66
|
-
getVariableByName('upiHandle/padding/left', modes) || 4
|
|
67
|
-
const paddingRight =
|
|
68
|
-
getVariableByName('upiHandle/padding/right', modes) || 14
|
|
69
|
-
const paddingVertical =
|
|
70
|
-
getVariableByName('upiHandle/padding/vertical', modes) || 3
|
|
71
|
-
const gap = getVariableByName('upiHandle/gap', modes) || 6
|
|
72
|
-
|
|
73
|
-
// Avatar
|
|
74
|
-
const avatarSize =
|
|
75
|
-
getVariableByName('upiHandle/image/size', modes) || 23
|
|
76
|
-
const avatarRadius =
|
|
77
|
-
getVariableByName('upiHandle/image/radius', modes) || 99999
|
|
78
|
-
|
|
79
|
-
// Label typography
|
|
80
|
-
const labelColor =
|
|
81
|
-
getVariableByName('upiHandle/label/color', modes) || '#0d0d0f'
|
|
82
|
-
const labelFontSize =
|
|
83
|
-
getVariableByName('upiHandle/label/fontSize', modes) || 12
|
|
84
|
-
const labelLineHeight =
|
|
85
|
-
getVariableByName('upiHandle/label/lineHeight', modes) || 23
|
|
86
|
-
const labelFontFamily =
|
|
87
|
-
getVariableByName('upiHandle/label/fontFamily', modes) || 'System'
|
|
88
|
-
const labelFontWeightRaw =
|
|
89
|
-
getVariableByName('upiHandle/label/fontWeight', modes) || 500
|
|
90
|
-
const labelFontWeight =
|
|
91
|
-
typeof labelFontWeightRaw === 'number'
|
|
92
|
-
? labelFontWeightRaw.toString()
|
|
93
|
-
: labelFontWeightRaw
|
|
94
|
-
|
|
95
|
-
// Icon sizing
|
|
96
|
-
const iconColor =
|
|
97
|
-
getVariableByName('upiHandle/icon/color', modes) || '#0d0d0f'
|
|
98
|
-
const iconSize =
|
|
99
|
-
getVariableByName('upiHandle/icon/size', modes) || 12
|
|
100
|
-
|
|
101
|
-
const containerStyle: ViewStyle = {
|
|
102
|
-
flexDirection: 'row',
|
|
103
|
-
alignItems: 'center',
|
|
104
|
-
justifyContent: 'center',
|
|
105
|
-
backgroundColor,
|
|
106
|
-
paddingLeft,
|
|
107
|
-
paddingRight,
|
|
108
|
-
paddingVertical,
|
|
109
|
-
borderRadius: radius,
|
|
110
|
-
gap,
|
|
111
|
-
}
|
|
112
148
|
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
149
|
+
const modes = useMemo(
|
|
150
|
+
() => (globalModes === EMPTY_MODES && propModes === EMPTY_MODES
|
|
151
|
+
? EMPTY_MODES
|
|
152
|
+
: { ...globalModes, ...propModes }),
|
|
153
|
+
[globalModes, propModes]
|
|
154
|
+
)
|
|
119
155
|
|
|
120
|
-
const
|
|
121
|
-
color: labelColor,
|
|
122
|
-
fontSize: labelFontSize,
|
|
123
|
-
lineHeight: labelLineHeight,
|
|
124
|
-
fontFamily: labelFontFamily,
|
|
125
|
-
fontWeight: labelFontWeight,
|
|
126
|
-
}
|
|
156
|
+
const tokens = useMemo(() => resolveUpiHandleTokens(modes), [modes])
|
|
127
157
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
height: iconSize
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Use provided accessibilityLabel or fall back to label
|
|
134
|
-
const defaultAccessibilityLabel = accessibilityLabel || `UPI handle ${label}`
|
|
135
|
-
const [isPressed, setIsPressed] = useState(false)
|
|
158
|
+
// Focus is a sustained visible state (web-only). Setter is gated so it
|
|
159
|
+
// never fires on native.
|
|
136
160
|
const [isFocused, setIsFocused] = useState(false)
|
|
137
|
-
|
|
138
|
-
const
|
|
161
|
+
|
|
162
|
+
const userHandlersRef = useRef<{
|
|
163
|
+
onPressIn?: (e: any) => void
|
|
164
|
+
onPressOut?: (e: any) => void
|
|
165
|
+
onFocus?: (e: any) => void
|
|
166
|
+
onBlur?: (e: any) => void
|
|
167
|
+
}>({})
|
|
168
|
+
userHandlersRef.current.onPressIn = (rest as any)?.onPressIn
|
|
169
|
+
userHandlersRef.current.onPressOut = (rest as any)?.onPressOut
|
|
170
|
+
userHandlersRef.current.onFocus = (rest as any)?.onFocus
|
|
171
|
+
userHandlersRef.current.onBlur = (rest as any)?.onBlur
|
|
172
|
+
|
|
173
|
+
const handlePressIn = useCallback((e: any) => {
|
|
174
|
+
userHandlersRef.current.onPressIn?.(e)
|
|
175
|
+
}, [])
|
|
176
|
+
const handlePressOut = useCallback((e: any) => {
|
|
177
|
+
userHandlersRef.current.onPressOut?.(e)
|
|
178
|
+
}, [])
|
|
179
|
+
const handleFocus = useCallback((e: any) => {
|
|
180
|
+
if (IS_WEB) setIsFocused(true)
|
|
181
|
+
userHandlersRef.current.onFocus?.(e)
|
|
182
|
+
}, [])
|
|
183
|
+
const handleBlur = useCallback((e: any) => {
|
|
184
|
+
if (IS_WEB) setIsFocused(false)
|
|
185
|
+
userHandlersRef.current.onBlur?.(e)
|
|
186
|
+
}, [])
|
|
139
187
|
|
|
140
188
|
const handlePress = onPress || onClick
|
|
141
|
-
const
|
|
189
|
+
const isPressable = !!((rest as any)?.onPress || handlePress)
|
|
142
190
|
|
|
143
|
-
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
onFocus={(e: any) => {
|
|
160
|
-
setIsFocused(true)
|
|
161
|
-
; (rest as any)?.onFocus?.(e)
|
|
162
|
-
}}
|
|
163
|
-
onBlur={(e: any) => {
|
|
164
|
-
setIsFocused(false)
|
|
165
|
-
; (rest as any)?.onBlur?.(e)
|
|
166
|
-
}}
|
|
167
|
-
{...rest}
|
|
168
|
-
>
|
|
191
|
+
const pressableStyle = useCallback(
|
|
192
|
+
({ pressed }: PressableStateCallbackType): StyleProp<ViewStyle> => [
|
|
193
|
+
tokens.containerStyle,
|
|
194
|
+
pressed ? pressedOverlayStyle : null,
|
|
195
|
+
isFocused ? focusOverlayStyle : null,
|
|
196
|
+
],
|
|
197
|
+
[tokens.containerStyle, isFocused]
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
const staticContainerStyle = useMemo<StyleProp<ViewStyle>>(
|
|
201
|
+
() => [tokens.containerStyle, isFocused ? focusOverlayStyle : null],
|
|
202
|
+
[tokens.containerStyle, isFocused]
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
const innerContent = (
|
|
206
|
+
<>
|
|
169
207
|
<Image
|
|
170
208
|
source={avatarSource || DEFAULT_AVATAR_IMAGE}
|
|
171
|
-
style={
|
|
209
|
+
style={tokens.avatarStyle}
|
|
172
210
|
resizeMode="cover"
|
|
173
211
|
accessibilityElementsHidden={true}
|
|
174
212
|
importantForAccessibility="no"
|
|
175
213
|
/>
|
|
176
214
|
<Text
|
|
177
|
-
style={
|
|
215
|
+
style={tokens.labelStyle}
|
|
178
216
|
numberOfLines={1}
|
|
179
217
|
ellipsizeMode="tail"
|
|
180
218
|
accessibilityElementsHidden={true}
|
|
@@ -185,18 +223,48 @@ function UpiHandle({
|
|
|
185
223
|
{showIcon && (
|
|
186
224
|
<Icon
|
|
187
225
|
name={iconName}
|
|
188
|
-
size={iconSize}
|
|
189
|
-
color={iconColor}
|
|
190
|
-
style={iconPlaceholderStyle}
|
|
226
|
+
size={tokens.iconSize}
|
|
227
|
+
color={tokens.iconColor}
|
|
228
|
+
style={tokens.iconPlaceholderStyle}
|
|
191
229
|
accessibilityElementsHidden={true}
|
|
192
230
|
importantForAccessibility="no"
|
|
193
231
|
/>
|
|
194
232
|
)}
|
|
195
|
-
|
|
233
|
+
</>
|
|
196
234
|
)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export default UpiHandle
|
|
200
235
|
|
|
236
|
+
if (isPressable) {
|
|
237
|
+
return (
|
|
238
|
+
<Pressable
|
|
239
|
+
style={pressableStyle}
|
|
240
|
+
accessibilityRole="text"
|
|
241
|
+
accessibilityLabel={undefined}
|
|
242
|
+
{...(accessibilityHint !== undefined ? { accessibilityHint } : {})}
|
|
243
|
+
onPress={handlePress}
|
|
244
|
+
disabled={(rest as any)?.disabled ?? disabled}
|
|
245
|
+
onPressIn={handlePressIn}
|
|
246
|
+
onPressOut={handlePressOut}
|
|
247
|
+
onFocus={handleFocus}
|
|
248
|
+
onBlur={handleBlur}
|
|
249
|
+
unstable_pressDelay={PRESS_DELAY}
|
|
250
|
+
{...rest}
|
|
251
|
+
>
|
|
252
|
+
{innerContent}
|
|
253
|
+
</Pressable>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
201
256
|
|
|
257
|
+
return (
|
|
258
|
+
<View
|
|
259
|
+
style={staticContainerStyle}
|
|
260
|
+
accessibilityRole="text"
|
|
261
|
+
accessibilityLabel={undefined}
|
|
262
|
+
{...(accessibilityHint !== undefined ? { accessibilityHint } : {})}
|
|
263
|
+
{...rest}
|
|
264
|
+
>
|
|
265
|
+
{innerContent}
|
|
266
|
+
</View>
|
|
267
|
+
)
|
|
268
|
+
}
|
|
202
269
|
|
|
270
|
+
export default React.memo(UpiHandle)
|
|
@@ -2,7 +2,7 @@ import React from 'react'
|
|
|
2
2
|
import { View, StyleProp, ViewStyle, ViewProps } from 'react-native'
|
|
3
3
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
4
4
|
import { useTokens } from '../../design-tokens/JFSThemeProvider'
|
|
5
|
-
import { cloneChildrenWithModes } from '../../utils/react-utils'
|
|
5
|
+
import { cloneChildrenWithModes, EMPTY_MODES } from '../../utils/react-utils'
|
|
6
6
|
|
|
7
7
|
export interface VStackProps extends ViewProps {
|
|
8
8
|
/**
|
|
@@ -48,7 +48,7 @@ export const VStack = ({
|
|
|
48
48
|
wrap,
|
|
49
49
|
reverse = false,
|
|
50
50
|
as,
|
|
51
|
-
modes: propModes =
|
|
51
|
+
modes: propModes = EMPTY_MODES,
|
|
52
52
|
style,
|
|
53
53
|
...rest
|
|
54
54
|
}: VStackProps) => {
|
|
@@ -190,14 +190,32 @@ function resolveVariable(variableId, modesByCollectionName = {}) {
|
|
|
190
190
|
return value;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
// Per-object serialization cache. Most callers pass the same `modes` object
|
|
194
|
+
// many times in a single render (e.g. one Button does ~21 token lookups with
|
|
195
|
+
// the same `modes`); with a stable identity from the caller (see
|
|
196
|
+
// `EMPTY_MODES` in `utils/react-utils.ts`) this collapses to a single
|
|
197
|
+
// sort+join per modes object across the whole app.
|
|
198
|
+
const serializedModesCache = new WeakMap();
|
|
199
|
+
|
|
193
200
|
// Serialize modes object to create a stable cache key
|
|
194
201
|
function serializeModes(modes) {
|
|
195
|
-
if (!modes ||
|
|
202
|
+
if (!modes || typeof modes !== 'object') {
|
|
203
|
+
return '';
|
|
204
|
+
}
|
|
205
|
+
const cached = serializedModesCache.get(modes);
|
|
206
|
+
if (cached !== undefined) {
|
|
207
|
+
return cached;
|
|
208
|
+
}
|
|
209
|
+
const keys = Object.keys(modes);
|
|
210
|
+
if (keys.length === 0) {
|
|
211
|
+
serializedModesCache.set(modes, '');
|
|
196
212
|
return '';
|
|
197
213
|
}
|
|
198
214
|
// Sort keys for consistent serialization
|
|
199
|
-
|
|
200
|
-
|
|
215
|
+
keys.sort();
|
|
216
|
+
const result = keys.map(key => `${key}:${modes[key]}`).join('|');
|
|
217
|
+
serializedModesCache.set(modes, result);
|
|
218
|
+
return result;
|
|
201
219
|
}
|
|
202
220
|
|
|
203
221
|
// Get variable by name with dynamic mode resolution (optimized with O(1) lookup)
|
package/src/icons/registry.ts
CHANGED
package/src/utils/react-utils.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* A shared, frozen empty modes object.
|
|
5
|
+
*
|
|
6
|
+
* Components that accept a `modes` prop should use this as the default value
|
|
7
|
+
* instead of the inline `{}` literal. The literal allocates a new object on
|
|
8
|
+
* every render, which:
|
|
9
|
+
* 1. Forces `React.memo` shallow comparisons to fail.
|
|
10
|
+
* 2. Forces `useMemo`/`useEffect` deps that include `modes` to re-run.
|
|
11
|
+
* 3. Forces the design-token resolver to re-serialize the modes object on
|
|
12
|
+
* every call (see `serializeModes` in `figma-variables-resolver.ts`).
|
|
13
|
+
*
|
|
14
|
+
* Sharing a single frozen object across the tree makes all of those checks
|
|
15
|
+
* O(1) identity comparisons in the common "no modes provided" path.
|
|
16
|
+
*/
|
|
17
|
+
export const EMPTY_MODES: Readonly<Record<string, any>> = Object.freeze({});
|
|
18
|
+
|
|
3
19
|
/**
|
|
4
20
|
* Helper function to recursively clone children and pass modes prop to components that accept it.
|
|
5
21
|
* This ensures that all child components in slots receive the modes prop from the parent.
|
package/lib/typescript/App.d.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
export = config;
|
|
2
|
-
declare const config: import("expo/metro-config").MetroConfig & {
|
|
3
|
-
reporter: {
|
|
4
|
-
update(): void;
|
|
5
|
-
};
|
|
6
|
-
watchFolders: string[];
|
|
7
|
-
resolver: {
|
|
8
|
-
unstable_conditionsByPlatform: {
|
|
9
|
-
ios: string[];
|
|
10
|
-
android: string[];
|
|
11
|
-
web: string[];
|
|
12
|
-
};
|
|
13
|
-
resolverMainFields: string[];
|
|
14
|
-
platforms: string[];
|
|
15
|
-
assetExts: string[];
|
|
16
|
-
sourceExts: string[];
|
|
17
|
-
nodeModulesPaths: string[];
|
|
18
|
-
blockList: RegExp[];
|
|
19
|
-
};
|
|
20
|
-
cacheStores: import("@expo/metro-config/build/file-store").FileStore<any>[];
|
|
21
|
-
watcher: {
|
|
22
|
-
additionalExts: string[];
|
|
23
|
-
};
|
|
24
|
-
serializer: {
|
|
25
|
-
isThirdPartyModule(module: {
|
|
26
|
-
readonly path: string;
|
|
27
|
-
}): boolean;
|
|
28
|
-
createModuleIdFactory: () => (path: string, context?: {
|
|
29
|
-
platform: string;
|
|
30
|
-
environment?: string;
|
|
31
|
-
}) => number;
|
|
32
|
-
getModulesRunBeforeMainModule: () => string[];
|
|
33
|
-
getPolyfills: ({ platform }: {
|
|
34
|
-
platform?: null | string;
|
|
35
|
-
}) => any;
|
|
36
|
-
};
|
|
37
|
-
server: {
|
|
38
|
-
rewriteRequestUrl: (url: string) => string;
|
|
39
|
-
port: number;
|
|
40
|
-
unstable_serverRoot: string;
|
|
41
|
-
};
|
|
42
|
-
symbolicator: {
|
|
43
|
-
customizeFrame: ($$PARAM_0$$: {
|
|
44
|
-
readonly file?: null | string;
|
|
45
|
-
readonly lineNumber?: null | number;
|
|
46
|
-
readonly column?: null | number;
|
|
47
|
-
readonly methodName?: null | string;
|
|
48
|
-
}) => (null | undefined | {
|
|
49
|
-
readonly collapse?: boolean;
|
|
50
|
-
}) | Promise<null | undefined | {
|
|
51
|
-
readonly collapse?: boolean;
|
|
52
|
-
}>;
|
|
53
|
-
};
|
|
54
|
-
transformerPath: string;
|
|
55
|
-
transformer: {
|
|
56
|
-
unstable_renameRequire: false;
|
|
57
|
-
_expoRouterPath: string | undefined;
|
|
58
|
-
postcssHash: string | null;
|
|
59
|
-
browserslistHash: string | null;
|
|
60
|
-
sassVersion: string | null;
|
|
61
|
-
reanimatedVersion: string | null;
|
|
62
|
-
workletsVersion: string | null;
|
|
63
|
-
_expoRelativeProjectRoot: string;
|
|
64
|
-
unstable_allowRequireContext: true;
|
|
65
|
-
allowOptionalDependencies: true;
|
|
66
|
-
babelTransformerPath: string;
|
|
67
|
-
asyncRequireModulePath: string;
|
|
68
|
-
assetRegistryPath: string;
|
|
69
|
-
enableBabelRuntime: string | undefined;
|
|
70
|
-
getTransformOptions: () => Promise<{
|
|
71
|
-
transform: {
|
|
72
|
-
experimentalImportSupport: true;
|
|
73
|
-
inlineRequires: false;
|
|
74
|
-
};
|
|
75
|
-
}>;
|
|
76
|
-
};
|
|
77
|
-
};
|
|
78
|
-
//# sourceMappingURL=metro.config.d.ts.map
|