jfs-components 0.0.61 → 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 +2 -3
- 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/JFSThemeProvider.js +2 -38
- 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 +26 -3
- 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 +3 -4
- 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/JFSThemeProvider.js +2 -35
- 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 +25 -3
- 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/design-tokens/JFSThemeProvider.d.ts +0 -15
- 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 +4 -5
- 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/JFSThemeProvider.tsx +1 -37
- package/src/design-tokens/figma-variables-resolver.ts +21 -3
- package/src/icons/registry.ts +1 -1
- package/src/utils/react-utils.ts +29 -3
- 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,27 +1,72 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { useState } from 'react';
|
|
4
|
-
import { Pressable, Text, View } from 'react-native';
|
|
3
|
+
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { Platform, Pressable, Text, View } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import { useTokens } from '../../design-tokens/JFSThemeProvider';
|
|
7
7
|
import Icon from '../../icons/Icon';
|
|
8
8
|
import { usePressableWebSupport } from '../../utils/web-platform-utils';
|
|
9
|
+
import { EMPTY_MODES } from '../../utils/react-utils';
|
|
9
10
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
const IS_WEB = Platform.OS === 'web';
|
|
12
|
+
const IS_IOS = Platform.OS === 'ios';
|
|
13
|
+
const PRESS_DELAY = IS_IOS ? 130 : 0;
|
|
14
|
+
const pressedOverlayStyle = {
|
|
15
|
+
opacity: 0.7
|
|
16
|
+
};
|
|
17
|
+
const hoverOverlayStyle = {
|
|
18
|
+
opacity: 0.85
|
|
19
|
+
};
|
|
20
|
+
const focusOverlayStyle = {
|
|
21
|
+
borderBottomWidth: 2,
|
|
22
|
+
borderBottomColor: '#222'
|
|
23
|
+
};
|
|
24
|
+
function resolveBottomNavItemTokens(modes, disabled, iconColorOverride, iconSizeOverride) {
|
|
25
|
+
const gap = getVariableByName('bottomNavItem/gap', modes) ?? 6;
|
|
26
|
+
const fontFamily = getVariableByName('bottomNavItem/fontFamily', modes) || 'JioType_Var:Medium';
|
|
27
|
+
const fontWeightRaw = getVariableByName('bottomNavItem/fontWeight', modes) ?? 500;
|
|
28
|
+
const fontWeight = typeof fontWeightRaw === 'number' ? fontWeightRaw.toString() : fontWeightRaw;
|
|
29
|
+
const fontSize = getVariableByName('bottomNavItem/fontSize', modes) ?? 11;
|
|
30
|
+
const lineHeight = getVariableByName('bottomNavItem/lineHeight', modes) ?? 14;
|
|
31
|
+
const labelColor = getVariableByName('bottomNavItem/label/color', modes) || '#0d0d0f';
|
|
32
|
+
const resolvedIconColor = iconColorOverride || getVariableByName('bottomNavItem/icon/color', modes) || '#ad8545';
|
|
33
|
+
const resolvedIconSize = iconSizeOverride ?? getVariableByName('bottomNavItem/icon/size', modes) ?? 24;
|
|
34
|
+
return {
|
|
35
|
+
baseContainerStyle: {
|
|
36
|
+
alignItems: 'center',
|
|
37
|
+
opacity: disabled ? 0.5 : 1
|
|
38
|
+
},
|
|
39
|
+
textStyle: {
|
|
40
|
+
color: labelColor,
|
|
41
|
+
fontFamily,
|
|
42
|
+
fontWeight: fontWeight,
|
|
43
|
+
fontSize,
|
|
44
|
+
lineHeight,
|
|
45
|
+
textAlign: 'center',
|
|
46
|
+
marginTop: gap
|
|
47
|
+
},
|
|
48
|
+
iconColor: resolvedIconColor,
|
|
49
|
+
iconSize: resolvedIconSize
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
10
53
|
/**
|
|
11
54
|
* Bottom navigation item with icon and label stacked vertically.
|
|
12
55
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
56
|
+
* Performance notes:
|
|
57
|
+
* - Token reads collapsed into a single `useMemo([modes, disabled, iconColor, iconSize])`.
|
|
58
|
+
* - Press visual via Pressable's `({ pressed })` style callback.
|
|
59
|
+
* - Hover and focus state are mirrored on web only (gated setters).
|
|
60
|
+
* - The previous version had no-op `onPressIn`/`onPressOut` handlers that
|
|
61
|
+
* forwarded to user callbacks but did nothing else — they were still
|
|
62
|
+
* creating fresh closures every render and forcing Pressable to re-bind.
|
|
63
|
+
* Now stable via a ref-backed wrapper.
|
|
64
|
+
* - Wrapped in `React.memo`.
|
|
20
65
|
*/
|
|
21
66
|
function BottomNavItem({
|
|
22
67
|
iconName = 'ic_home',
|
|
23
68
|
label = 'Home',
|
|
24
|
-
modes: propModes =
|
|
69
|
+
modes: propModes = EMPTY_MODES,
|
|
25
70
|
onPress,
|
|
26
71
|
disabled = false,
|
|
27
72
|
style,
|
|
@@ -37,54 +82,21 @@ function BottomNavItem({
|
|
|
37
82
|
const {
|
|
38
83
|
modes: globalModes
|
|
39
84
|
} = useTokens();
|
|
40
|
-
const modes = {
|
|
85
|
+
const modes = useMemo(() => globalModes === EMPTY_MODES && propModes === EMPTY_MODES ? EMPTY_MODES : {
|
|
41
86
|
...globalModes,
|
|
42
87
|
...propModes
|
|
43
|
-
};
|
|
88
|
+
}, [globalModes, propModes]);
|
|
89
|
+
const tokens = useMemo(() => resolveBottomNavItemTokens(modes, disabled, iconColorOverride, iconSizeOverride), [modes, disabled, iconColorOverride, iconSizeOverride]);
|
|
44
90
|
const [isFocused, setIsFocused] = useState(false);
|
|
45
91
|
const [isHovered, setIsHovered] = useState(false);
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const hoverStyle = {
|
|
54
|
-
opacity: 0.85
|
|
55
|
-
};
|
|
56
|
-
// Resolve spacing and typography
|
|
57
|
-
const gap = getVariableByName('bottomNavItem/gap', modes) ?? 6;
|
|
58
|
-
const fontFamily = getVariableByName('bottomNavItem/fontFamily', modes) || 'JioType_Var:Medium';
|
|
59
|
-
const fontWeightRaw = getVariableByName('bottomNavItem/fontWeight', modes) ?? 500;
|
|
60
|
-
const fontWeight = typeof fontWeightRaw === 'number' ? fontWeightRaw.toString() : fontWeightRaw;
|
|
61
|
-
const fontSize = getVariableByName('bottomNavItem/fontSize', modes) ?? 11;
|
|
62
|
-
const lineHeight = getVariableByName('bottomNavItem/lineHeight', modes) ?? 14;
|
|
63
|
-
|
|
64
|
-
// Resolve Label Color
|
|
65
|
-
const labelColor = getVariableByName('bottomNavItem/label/color', modes) || '#0d0d0f';
|
|
66
|
-
|
|
67
|
-
// Resolve icon appearance
|
|
68
|
-
const resolvedIconColor = iconColorOverride || getVariableByName('bottomNavItem/icon/color', modes) || '#ad8545';
|
|
69
|
-
const resolvedIconSize = iconSizeOverride ?? getVariableByName('bottomNavItem/icon/size', modes) ?? 24;
|
|
70
|
-
const baseContainerStyle = {
|
|
71
|
-
alignItems: 'center',
|
|
72
|
-
opacity: disabled ? 0.5 : 1
|
|
73
|
-
};
|
|
74
|
-
const textStyle = {
|
|
75
|
-
color: labelColor,
|
|
76
|
-
fontFamily,
|
|
77
|
-
fontWeight,
|
|
78
|
-
fontSize,
|
|
79
|
-
lineHeight,
|
|
80
|
-
textAlign: 'center',
|
|
81
|
-
marginTop: gap
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// Use provided accessibilityLabel or fall back to label
|
|
92
|
+
const userHandlersRef = useRef({});
|
|
93
|
+
userHandlersRef.current.onPressIn = rest?.onPressIn;
|
|
94
|
+
userHandlersRef.current.onPressOut = rest?.onPressOut;
|
|
95
|
+
userHandlersRef.current.onFocus = rest?.onFocus;
|
|
96
|
+
userHandlersRef.current.onBlur = rest?.onBlur;
|
|
97
|
+
userHandlersRef.current.onHoverIn = rest?.onHoverIn;
|
|
98
|
+
userHandlersRef.current.onHoverOut = rest?.onHoverOut;
|
|
85
99
|
const defaultAccessibilityLabel = accessibilityLabel || label;
|
|
86
|
-
|
|
87
|
-
// Get web platform support props (only used when onPress is defined)
|
|
88
100
|
const webProps = usePressableWebSupport({
|
|
89
101
|
restProps: rest,
|
|
90
102
|
onPress: disabled ? undefined : onPress,
|
|
@@ -92,23 +104,50 @@ function BottomNavItem({
|
|
|
92
104
|
accessibilityLabel: defaultAccessibilityLabel,
|
|
93
105
|
webAccessibilityProps
|
|
94
106
|
});
|
|
95
|
-
const
|
|
107
|
+
const composedTextStyle = useMemo(() => labelStyle ? [tokens.textStyle, labelStyle] : tokens.textStyle, [tokens.textStyle, labelStyle]);
|
|
108
|
+
const renderContent = /*#__PURE__*/_jsxs(_Fragment, {
|
|
96
109
|
children: [/*#__PURE__*/_jsx(Icon, {
|
|
97
110
|
name: iconName,
|
|
98
|
-
size:
|
|
99
|
-
color:
|
|
111
|
+
size: tokens.iconSize,
|
|
112
|
+
color: tokens.iconColor,
|
|
100
113
|
accessibilityElementsHidden: true,
|
|
101
114
|
importantForAccessibility: "no"
|
|
102
115
|
}), /*#__PURE__*/_jsx(Text, {
|
|
103
|
-
style:
|
|
116
|
+
style: composedTextStyle,
|
|
104
117
|
accessibilityElementsHidden: true,
|
|
105
118
|
importantForAccessibility: "no",
|
|
106
119
|
children: label
|
|
107
120
|
})]
|
|
108
121
|
});
|
|
122
|
+
const staticContainerStyle = useMemo(() => style ? [tokens.baseContainerStyle, style] : tokens.baseContainerStyle, [tokens.baseContainerStyle, style]);
|
|
123
|
+
const handlePressIn = useCallback(e => {
|
|
124
|
+
userHandlersRef.current.onPressIn?.(e);
|
|
125
|
+
}, []);
|
|
126
|
+
const handlePressOut = useCallback(e => {
|
|
127
|
+
userHandlersRef.current.onPressOut?.(e);
|
|
128
|
+
}, []);
|
|
129
|
+
const handleFocus = useCallback(e => {
|
|
130
|
+
if (IS_WEB) setIsFocused(true);
|
|
131
|
+
userHandlersRef.current.onFocus?.(e);
|
|
132
|
+
}, []);
|
|
133
|
+
const handleBlur = useCallback(e => {
|
|
134
|
+
if (IS_WEB) setIsFocused(false);
|
|
135
|
+
userHandlersRef.current.onBlur?.(e);
|
|
136
|
+
}, []);
|
|
137
|
+
const handleHoverIn = useCallback(e => {
|
|
138
|
+
if (IS_WEB) setIsHovered(true);
|
|
139
|
+
userHandlersRef.current.onHoverIn?.(e);
|
|
140
|
+
}, []);
|
|
141
|
+
const handleHoverOut = useCallback(e => {
|
|
142
|
+
if (IS_WEB) setIsHovered(false);
|
|
143
|
+
userHandlersRef.current.onHoverOut?.(e);
|
|
144
|
+
}, []);
|
|
145
|
+
const pressableStyle = useCallback(({
|
|
146
|
+
pressed
|
|
147
|
+
}) => [tokens.baseContainerStyle, style, pressed && !disabled ? pressedOverlayStyle : null, isHovered && !disabled ? hoverOverlayStyle : null, isFocused && !disabled ? focusOverlayStyle : null], [tokens.baseContainerStyle, style, isHovered, isFocused, disabled]);
|
|
109
148
|
if (!onPress) {
|
|
110
149
|
return /*#__PURE__*/_jsx(View, {
|
|
111
|
-
style:
|
|
150
|
+
style: staticContainerStyle,
|
|
112
151
|
accessibilityRole: "tab",
|
|
113
152
|
accessibilityLabel: undefined,
|
|
114
153
|
accessibilityHint: accessibilityHint,
|
|
@@ -117,7 +156,7 @@ function BottomNavItem({
|
|
|
117
156
|
...accessibilityState
|
|
118
157
|
},
|
|
119
158
|
...rest,
|
|
120
|
-
children: renderContent
|
|
159
|
+
children: renderContent
|
|
121
160
|
});
|
|
122
161
|
}
|
|
123
162
|
return /*#__PURE__*/_jsx(Pressable, {
|
|
@@ -131,35 +170,16 @@ function BottomNavItem({
|
|
|
131
170
|
},
|
|
132
171
|
onPress: onPress,
|
|
133
172
|
disabled: disabled,
|
|
134
|
-
onPressIn:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
onFocus: e => {
|
|
143
|
-
setIsFocused(true);
|
|
144
|
-
rest?.onFocus?.(e);
|
|
145
|
-
},
|
|
146
|
-
onBlur: e => {
|
|
147
|
-
setIsFocused(false);
|
|
148
|
-
rest?.onBlur?.(e);
|
|
149
|
-
},
|
|
150
|
-
onHoverIn: e => {
|
|
151
|
-
setIsHovered(true);
|
|
152
|
-
rest?.onHoverIn?.(e);
|
|
153
|
-
},
|
|
154
|
-
onHoverOut: e => {
|
|
155
|
-
setIsHovered(false);
|
|
156
|
-
rest?.onHoverOut?.(e);
|
|
157
|
-
},
|
|
158
|
-
style: ({
|
|
159
|
-
pressed
|
|
160
|
-
}) => [baseContainerStyle, style, pressed && !disabled ? pressedStyle : null, isHovered && !disabled ? hoverStyle : null, isFocused && !disabled ? focusStyle : null],
|
|
173
|
+
onPressIn: handlePressIn,
|
|
174
|
+
onPressOut: handlePressOut,
|
|
175
|
+
onFocus: handleFocus,
|
|
176
|
+
onBlur: handleBlur,
|
|
177
|
+
onHoverIn: handleHoverIn,
|
|
178
|
+
onHoverOut: handleHoverOut,
|
|
179
|
+
unstable_pressDelay: PRESS_DELAY,
|
|
180
|
+
style: pressableStyle,
|
|
161
181
|
...webProps,
|
|
162
|
-
children: renderContent
|
|
182
|
+
children: renderContent
|
|
163
183
|
});
|
|
164
184
|
}
|
|
165
|
-
export default BottomNavItem;
|
|
185
|
+
export default /*#__PURE__*/React.memo(BottomNavItem);
|
|
@@ -1,11 +1,121 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { useMemo, useState } from 'react';
|
|
4
|
-
import { Pressable, Text, View } from 'react-native';
|
|
3
|
+
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { Platform, Pressable, Text, View } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import { usePressableWebSupport } from '../../utils/web-platform-utils';
|
|
7
|
+
import { EMPTY_MODES } from '../../utils/react-utils';
|
|
7
8
|
import Icon from '../../icons/Icon';
|
|
8
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Module-scope constants — never re-allocated per render.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
const IS_WEB = Platform.OS === 'web';
|
|
15
|
+
const IS_IOS = Platform.OS === 'ios';
|
|
16
|
+
|
|
17
|
+
// iOS Pressable inside a ScrollView fires onPressIn on touchstart by default,
|
|
18
|
+
// which causes a visible "press" flash that snaps back when ScrollView claims
|
|
19
|
+
// the gesture. Delaying press by ~130ms lets the scroll responder cancel the
|
|
20
|
+
// gesture before the pressed visual is ever applied.
|
|
21
|
+
//
|
|
22
|
+
// Android uses the responder system + the touchable cancellation pipeline and
|
|
23
|
+
// does not need an extra delay; setting it to 0 keeps tap latency identical
|
|
24
|
+
// to today's behavior on Android.
|
|
25
|
+
const PRESS_DELAY = IS_IOS ? 130 : 0;
|
|
26
|
+
const containerLayoutStyle = {
|
|
27
|
+
flexDirection: 'row',
|
|
28
|
+
alignItems: 'center',
|
|
29
|
+
justifyContent: 'center'
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Token resolution
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
//
|
|
36
|
+
// Memoizing all token reads in a single useMemo keyed on (modes, disabled)
|
|
37
|
+
// turns the previous ~21 cache lookups + arithmetic per render into a single
|
|
38
|
+
// resolve per (modes, disabled) tuple. With `EMPTY_MODES` as the default,
|
|
39
|
+
// the most common path (no modes prop) is resolved once for the whole app.
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
function resolveButtonTokens(modes, disabled) {
|
|
43
|
+
const backgroundColor = getVariableByName('button/background', modes) || '#cfa159';
|
|
44
|
+
const borderColor = getVariableByName('button/border/color', modes) || 'rgba(255,255,255,0)';
|
|
45
|
+
const borderWidth = getVariableByName('button/border/size', modes);
|
|
46
|
+
const radius = getVariableByName('button/radius', modes) || 9999;
|
|
47
|
+
const paddingHorizontal = getVariableByName('button/padding/horizontal', modes) || 20;
|
|
48
|
+
const paddingVertical = getVariableByName('button/padding/vertical', modes) || 12;
|
|
49
|
+
const gap = getVariableByName('button/gap', modes) || 8;
|
|
50
|
+
const fontFamily = getVariableByName('button/fontFamily', modes) || 'System';
|
|
51
|
+
const fontWeightValue = getVariableByName('button/fontWeight', modes) || 700;
|
|
52
|
+
const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue;
|
|
53
|
+
const lineHeight = getVariableByName('button/lineHeight', modes) || 19;
|
|
54
|
+
const fontSize = getVariableByName('button/fontSize', modes) || 16;
|
|
55
|
+
const textColor = getVariableByName('button/foreground', modes) || '#0f0d0a';
|
|
56
|
+
const iconSize = getVariableByName('button/icon/size', modes) ?? 18;
|
|
57
|
+
|
|
58
|
+
// Pressed tokens are always resolved so the Pressable `style` callback can
|
|
59
|
+
// apply the pressed visual without scheduling a React render.
|
|
60
|
+
const pressedModes = {
|
|
61
|
+
...modes,
|
|
62
|
+
'Button / State': 'Pressed'
|
|
63
|
+
};
|
|
64
|
+
const pressedBg = getVariableByName('button/background', pressedModes) || backgroundColor;
|
|
65
|
+
const pressedBorderColor = getVariableByName('button/border/color', pressedModes) || borderColor;
|
|
66
|
+
const pressedTextColor = getVariableByName('button/foreground', pressedModes) || textColor;
|
|
67
|
+
|
|
68
|
+
// Hover tokens are only meaningful on web. Skipping them on native saves
|
|
69
|
+
// ~3 token lookups + an object spread per render on every Button.
|
|
70
|
+
let hoverContainer = null;
|
|
71
|
+
let hoverTextColor = null;
|
|
72
|
+
if (IS_WEB) {
|
|
73
|
+
const hoverModes = {
|
|
74
|
+
...modes,
|
|
75
|
+
'Button / State': 'Hover'
|
|
76
|
+
};
|
|
77
|
+
const hoverBg = getVariableByName('button/background', hoverModes) || backgroundColor;
|
|
78
|
+
const hoverBorderColor = getVariableByName('button/border/color', hoverModes) || borderColor;
|
|
79
|
+
hoverTextColor = getVariableByName('button/foreground', hoverModes) || textColor;
|
|
80
|
+
hoverContainer = {
|
|
81
|
+
backgroundColor: hoverBg,
|
|
82
|
+
borderColor: hoverBorderColor
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
container: {
|
|
87
|
+
...containerLayoutStyle,
|
|
88
|
+
paddingHorizontal: paddingHorizontal,
|
|
89
|
+
paddingVertical: paddingVertical,
|
|
90
|
+
borderRadius: radius,
|
|
91
|
+
borderWidth: borderWidth ?? 1,
|
|
92
|
+
borderColor: borderColor,
|
|
93
|
+
backgroundColor: backgroundColor,
|
|
94
|
+
gap: gap,
|
|
95
|
+
opacity: disabled ? 0.5 : 1
|
|
96
|
+
},
|
|
97
|
+
baseLabel: {
|
|
98
|
+
color: textColor,
|
|
99
|
+
fontFamily: fontFamily,
|
|
100
|
+
fontWeight: fontWeight,
|
|
101
|
+
fontSize: fontSize,
|
|
102
|
+
lineHeight: lineHeight
|
|
103
|
+
},
|
|
104
|
+
hoverContainer,
|
|
105
|
+
pressedContainer: {
|
|
106
|
+
backgroundColor: pressedBg,
|
|
107
|
+
borderColor: pressedBorderColor
|
|
108
|
+
},
|
|
109
|
+
hoverTextColor,
|
|
110
|
+
pressedTextColor,
|
|
111
|
+
baseIconColor: textColor,
|
|
112
|
+
hoverIconColor: hoverTextColor,
|
|
113
|
+
pressedIconColor: pressedTextColor,
|
|
114
|
+
iconSize,
|
|
115
|
+
accessoryOffset: gap / 2
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
9
119
|
/**
|
|
10
120
|
* Button component that maps directly to the Figma design using design tokens.
|
|
11
121
|
*
|
|
@@ -24,15 +134,31 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
24
134
|
* @param {string} [props.accessibilityLabel] - Accessibility label for screen readers. If not provided, uses label or children text
|
|
25
135
|
* @param {string} [props.accessibilityHint] - Additional accessibility hint for screen readers
|
|
26
136
|
* @param {Object} [props.accessibilityState] - Additional accessibility state information
|
|
137
|
+
*
|
|
138
|
+
* Performance notes:
|
|
139
|
+
* - The pressed visual (background/border swap) is applied through the
|
|
140
|
+
* Pressable `style` callback, NOT a mirrored React state. This avoids the
|
|
141
|
+
* flicker that scrolling parents previously caused: when a ScrollView
|
|
142
|
+
* cancels a touch the host view simply reverts its style without React
|
|
143
|
+
* rendering. On iOS we additionally use `unstable_pressDelay` so the
|
|
144
|
+
* pressed visual is never even applied during a scroll-cancelled touch.
|
|
145
|
+
* - Hover state is only mirrored into React state on web (where hover events
|
|
146
|
+
* fire). On native the setter call is gated out so the component never
|
|
147
|
+
* re-renders due to hover.
|
|
148
|
+
* - All design-token reads are folded into a single `useMemo` keyed on
|
|
149
|
+
* `(modes, disabled)`.
|
|
150
|
+
* - The component is wrapped in `React.memo`. Callers that want to take
|
|
151
|
+
* advantage of this should pass stable `modes` (the default `EMPTY_MODES`
|
|
152
|
+
* is already stable) and stable callback props.
|
|
27
153
|
*/
|
|
28
|
-
function
|
|
154
|
+
function ButtonImpl({
|
|
29
155
|
label = 'Button',
|
|
30
156
|
children,
|
|
31
157
|
renderContent,
|
|
32
158
|
leading,
|
|
33
159
|
trailing,
|
|
34
160
|
icon,
|
|
35
|
-
modes =
|
|
161
|
+
modes = EMPTY_MODES,
|
|
36
162
|
onPress,
|
|
37
163
|
disabled = false,
|
|
38
164
|
style,
|
|
@@ -43,85 +169,50 @@ function Button({
|
|
|
43
169
|
webAccessibilityProps,
|
|
44
170
|
...rest
|
|
45
171
|
}) {
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
const borderColor = getVariableByName('button/border/color', modes) || 'rgba(255,255,255,0)';
|
|
49
|
-
const borderWidth = getVariableByName('button/border/size', modes);
|
|
50
|
-
const radius = getVariableByName('button/radius', modes) || 9999;
|
|
51
|
-
const paddingHorizontal = getVariableByName('button/padding/horizontal', modes) || 20;
|
|
52
|
-
const paddingVertical = getVariableByName('button/padding/vertical', modes) || 12;
|
|
53
|
-
const gap = getVariableByName('button/gap', modes) || 8;
|
|
54
|
-
const fontFamily = getVariableByName('button/fontFamily', modes) || 'System';
|
|
55
|
-
const fontWeightValue = getVariableByName('button/fontWeight', modes) || 700;
|
|
56
|
-
const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue;
|
|
57
|
-
const lineHeight = getVariableByName('button/lineHeight', modes) || 19;
|
|
58
|
-
const fontSize = getVariableByName('button/fontSize', modes) || 16;
|
|
59
|
-
const textColor = getVariableByName('button/foreground', modes) || '#0f0d0a';
|
|
60
|
-
const iconColor = textColor;
|
|
61
|
-
const iconSize = getVariableByName('button/icon/size', modes) ?? 18;
|
|
172
|
+
// Hover state is web-only in practice; the setter is gated so native taps
|
|
173
|
+
// never schedule a re-render through this hook.
|
|
62
174
|
const [isHovered, setIsHovered] = useState(false);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
color: activeTextColor,
|
|
84
|
-
fontFamily,
|
|
85
|
-
fontWeight,
|
|
86
|
-
fontSize,
|
|
87
|
-
lineHeight
|
|
88
|
-
};
|
|
89
|
-
const mergedLabelTextStyle = [baseLabelTextStyle, labelStyle];
|
|
175
|
+
|
|
176
|
+
// Mirror user-supplied handlers in a ref so our wrapper handlers can stay
|
|
177
|
+
// referentially stable (otherwise Pressable would re-bind every render).
|
|
178
|
+
const userHandlersRef = useRef({});
|
|
179
|
+
userHandlersRef.current.onPressIn = rest?.onPressIn;
|
|
180
|
+
userHandlersRef.current.onPressOut = rest?.onPressOut;
|
|
181
|
+
userHandlersRef.current.onHoverIn = rest?.onHoverIn;
|
|
182
|
+
userHandlersRef.current.onHoverOut = rest?.onHoverOut;
|
|
183
|
+
const tokens = useMemo(() => resolveButtonTokens(modes, disabled), [modes, disabled]);
|
|
184
|
+
|
|
185
|
+
// Active label color: base by default; hover override (web-only) when hovered.
|
|
186
|
+
// Press color is intentionally NOT applied to the label on native — applying
|
|
187
|
+
// it would require a React render per touch and re-introduce the flicker.
|
|
188
|
+
const activeLabelStyle = useMemo(() => {
|
|
189
|
+
const hoverOverride = isHovered && !disabled && tokens.hoverTextColor ? {
|
|
190
|
+
color: tokens.hoverTextColor
|
|
191
|
+
} : null;
|
|
192
|
+
return [tokens.baseLabel, hoverOverride, labelStyle];
|
|
193
|
+
}, [tokens.baseLabel, tokens.hoverTextColor, isHovered, disabled, labelStyle]);
|
|
194
|
+
const activeIconColor = isHovered && !disabled && tokens.hoverIconColor ? tokens.hoverIconColor : tokens.baseIconColor;
|
|
90
195
|
let content;
|
|
91
196
|
if (renderContent) {
|
|
92
197
|
// Preferred extension point: caller fully controls the inner node but we
|
|
93
198
|
// still provide the token-derived text styles.
|
|
94
|
-
content = renderContent(
|
|
199
|
+
content = renderContent(activeLabelStyle);
|
|
95
200
|
} else if (children !== undefined && children !== null) {
|
|
96
201
|
// Full-content override: label and labelStyle are intentionally ignored.
|
|
97
202
|
content = children;
|
|
98
203
|
} else {
|
|
99
204
|
content = /*#__PURE__*/_jsx(Text, {
|
|
100
|
-
style:
|
|
205
|
+
style: activeLabelStyle,
|
|
101
206
|
numberOfLines: 1,
|
|
102
207
|
children: label
|
|
103
208
|
});
|
|
104
209
|
}
|
|
105
|
-
const containerBaseStyle = {
|
|
106
|
-
flexDirection: 'row',
|
|
107
|
-
alignItems: 'center',
|
|
108
|
-
justifyContent: 'center',
|
|
109
|
-
paddingHorizontal,
|
|
110
|
-
paddingVertical,
|
|
111
|
-
borderRadius: radius,
|
|
112
|
-
borderWidth: borderWidth ?? 1,
|
|
113
|
-
borderColor,
|
|
114
|
-
backgroundColor,
|
|
115
|
-
gap,
|
|
116
|
-
opacity: disabled ? 0.5 : 1
|
|
117
|
-
};
|
|
118
|
-
const accessoryOffset = gap / 2;
|
|
119
210
|
const leadingAccessoryStyle = useMemo(() => ({
|
|
120
|
-
marginRight: accessoryOffset
|
|
121
|
-
}), [accessoryOffset]);
|
|
211
|
+
marginRight: tokens.accessoryOffset
|
|
212
|
+
}), [tokens.accessoryOffset]);
|
|
122
213
|
const trailingAccessoryStyle = useMemo(() => ({
|
|
123
|
-
marginLeft: accessoryOffset
|
|
124
|
-
}), [accessoryOffset]);
|
|
214
|
+
marginLeft: tokens.accessoryOffset
|
|
215
|
+
}), [tokens.accessoryOffset]);
|
|
125
216
|
|
|
126
217
|
// Use provided accessibilityLabel, or fall back to label text
|
|
127
218
|
const defaultAccessibilityLabel = accessibilityLabel || (typeof label === 'string' ? label : undefined);
|
|
@@ -134,14 +225,32 @@ function Button({
|
|
|
134
225
|
accessibilityLabel: defaultAccessibilityLabel,
|
|
135
226
|
webAccessibilityProps
|
|
136
227
|
});
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
228
|
+
|
|
229
|
+
// Stable handler identities. The user's handlers are read through a ref so
|
|
230
|
+
// these wrappers do not need to be re-created when the user passes inline
|
|
231
|
+
// closures, which would defeat React.memo on Pressable.
|
|
232
|
+
const handlePressIn = useCallback(e => {
|
|
233
|
+
userHandlersRef.current.onPressIn?.(e);
|
|
234
|
+
}, []);
|
|
235
|
+
const handlePressOut = useCallback(e => {
|
|
236
|
+
userHandlersRef.current.onPressOut?.(e);
|
|
237
|
+
}, []);
|
|
238
|
+
const handleHoverIn = useCallback(e => {
|
|
239
|
+
if (IS_WEB) setIsHovered(true);
|
|
240
|
+
userHandlersRef.current.onHoverIn?.(e);
|
|
241
|
+
}, []);
|
|
242
|
+
const handleHoverOut = useCallback(e => {
|
|
243
|
+
if (IS_WEB) setIsHovered(false);
|
|
244
|
+
userHandlersRef.current.onHoverOut?.(e);
|
|
245
|
+
}, []);
|
|
246
|
+
|
|
247
|
+
// The Pressable style callback receives `pressed` from RN's gesture
|
|
248
|
+
// pipeline and applies the pressed visual to the host view directly,
|
|
249
|
+
// without going through React. This is the key to flicker-free behavior:
|
|
250
|
+
// a scroll-cancelled touch reverts the style on the native side only.
|
|
251
|
+
const styleCallback = useCallback(({
|
|
252
|
+
pressed
|
|
253
|
+
}) => [tokens.container, isHovered && !disabled ? tokens.hoverContainer : null, pressed && !disabled ? tokens.pressedContainer : null, style], [tokens.container, tokens.hoverContainer, tokens.pressedContainer, isHovered, disabled, style]);
|
|
145
254
|
if (__DEV__) {
|
|
146
255
|
if (children && labelStyle) {
|
|
147
256
|
console.warn('[Button] `children` was provided; `labelStyle` will be ignored. ' + 'Use `renderContent` if you need custom content that still reuses the label styles.');
|
|
@@ -164,25 +273,12 @@ function Button({
|
|
|
164
273
|
...(onPress !== undefined ? {
|
|
165
274
|
onPress
|
|
166
275
|
} : {}),
|
|
167
|
-
onPressIn:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
rest?.onPressOut?.(e);
|
|
174
|
-
},
|
|
175
|
-
onHoverIn: e => {
|
|
176
|
-
setIsHovered(true);
|
|
177
|
-
rest?.onHoverIn?.(e);
|
|
178
|
-
},
|
|
179
|
-
onHoverOut: e => {
|
|
180
|
-
setIsHovered(false);
|
|
181
|
-
rest?.onHoverOut?.(e);
|
|
182
|
-
},
|
|
183
|
-
style: ({
|
|
184
|
-
pressed
|
|
185
|
-
}) => [containerBaseStyle, isHovered && !disabled ? hoverStyle : null, (pressed || isPressed) && !disabled ? pressedStyle : null, style],
|
|
276
|
+
onPressIn: handlePressIn,
|
|
277
|
+
onPressOut: handlePressOut,
|
|
278
|
+
onHoverIn: handleHoverIn,
|
|
279
|
+
onHoverOut: handleHoverOut,
|
|
280
|
+
unstable_pressDelay: PRESS_DELAY,
|
|
281
|
+
style: styleCallback,
|
|
186
282
|
...webProps,
|
|
187
283
|
children: [leading ? /*#__PURE__*/_jsx(View, {
|
|
188
284
|
style: leadingAccessoryStyle,
|
|
@@ -191,7 +287,7 @@ function Button({
|
|
|
191
287
|
style: trailingAccessoryStyle,
|
|
192
288
|
children: /*#__PURE__*/_jsx(Icon, {
|
|
193
289
|
name: icon,
|
|
194
|
-
size: iconSize,
|
|
290
|
+
size: tokens.iconSize,
|
|
195
291
|
color: activeIconColor,
|
|
196
292
|
accessibilityElementsHidden: true,
|
|
197
293
|
importantForAccessibility: "no"
|
|
@@ -202,4 +298,5 @@ function Button({
|
|
|
202
298
|
}) : null]
|
|
203
299
|
});
|
|
204
300
|
}
|
|
301
|
+
const Button = /*#__PURE__*/React.memo(ButtonImpl);
|
|
205
302
|
export default Button;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { View } from 'react-native';
|
|
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
|
import IconButton from '../IconButton/IconButton';
|
|
8
8
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
9
|
/**
|
|
@@ -25,7 +25,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
25
25
|
*/
|
|
26
26
|
function ButtonGroup({
|
|
27
27
|
children,
|
|
28
|
-
modes =
|
|
28
|
+
modes = EMPTY_MODES,
|
|
29
29
|
style
|
|
30
30
|
}) {
|
|
31
31
|
// Resolve design tokens
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import React, { createContext, useContext, isValidElement, cloneElement } from 'react';
|
|
4
4
|
import { View, Text } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
+
import { EMPTY_MODES } from '../../utils/react-utils';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Context to share 'modes' with child components like Card.Title and Card.SupportText.
|
|
@@ -27,7 +28,7 @@ const CardContext = /*#__PURE__*/createContext({});
|
|
|
27
28
|
export function Card({
|
|
28
29
|
media,
|
|
29
30
|
children,
|
|
30
|
-
modes =
|
|
31
|
+
modes = EMPTY_MODES,
|
|
31
32
|
mediaAspectRatio = 154 / 116,
|
|
32
33
|
style
|
|
33
34
|
}) {
|
|
@@ -4,7 +4,7 @@ import React from 'react';
|
|
|
4
4
|
import { View, Text } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import { useTokens } from '../../design-tokens/JFSThemeProvider';
|
|
7
|
-
import { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
7
|
+
import { cloneChildrenWithModes, EMPTY_MODES } from '../../utils/react-utils';
|
|
8
8
|
import IconCapsule from '../IconCapsule/IconCapsule';
|
|
9
9
|
import Button from '../Button/Button';
|
|
10
10
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
@@ -14,7 +14,7 @@ function CardCTA({
|
|
|
14
14
|
iconName = 'ic_upi_number',
|
|
15
15
|
buttonLabel = 'Button',
|
|
16
16
|
onPressButton,
|
|
17
|
-
modes: propModes =
|
|
17
|
+
modes: propModes = EMPTY_MODES,
|
|
18
18
|
iconSlot,
|
|
19
19
|
buttonSlot,
|
|
20
20
|
style
|